commit f5a6cfce9b70e440cfd3ded87c45807714ea5594 Author: Ragora Date: Fri Feb 1 15:45:22 2013 -0500 Initial commit. diff --git a/ChangeLog.txt b/ChangeLog.txt new file mode 100644 index 0000000..256be74 --- /dev/null +++ b/ChangeLog.txt @@ -0,0 +1,116 @@ +ACCM 1.4.0 [For Deflun :] ------------------------------ +* The turret override (disable / enable turrets) now works outside of purebuild. +* Emplacements can no longer be controlled via the command circuit. +* Fix an issue with doors not saving timeout when being re-textured. [Thanks, Deflun] +* Added power change modes to the light. [Request by Deflun] +* More tripwire modes for easier storm-making. +* Demon Mothers can now be spawned for the zombie spawn pack. +* Changed the spawn point shape and gave it a small power radius. [Request by Krash] +* Demon armor can now be controllable. +* Multiple instability and bug fixes. +* Fixed a crash related to sentinels firing with their turret. +* Human rapiers are no longer extremely hard to control. They now run and jet. +* Updated the command satellite to the version in CCM 3.4. +* Replaced the drone AI with the version in CCM 3.4 (because the old one caused the server to crash). +* Fixed the texture tool icon. +* Removed the advanced zombie spawns. The regular zombie spawn pack should be all you need. + +ACCM 1.3.2 --------------------------------------------- +* Fixed the tripwire modes. There are no longer "blank" modes. +* Smurf identifying upon join is no longer displayed when playing LAN. +* Changes have been made to the Merge Tool. +* Fixed zombie jumping ability. +* Targeting lasers no longer leave a lingering beacon at "0 0 0" when not in a valid position. +* Fixed a bug which disallowed keepers and admins to make other people zombies. +* There is now a "Max Zombies" function. +* Fixed a fatal (to the server) exploit. +* Fixed a scoring-related console spam error with ccKillZombies. +* Added saving support for antidote station antidotes (they didn't save, often resulting in an empty antidote station loaded) and deployed waypoint. Older saves are still compatible. +* Added a way to refill antidote stations. +* Technicians cannot deploy satchel charges anymore, but, they may deploy antidote stations. +* Fixed a bug with ForceTeamSpawn. +* ccGag and ccEcho have been replaced with a more effective ccMute. +* Added a texture tool. +* Added the blue pad. +* Improved the doors. +* Multiple chat command improvements. +* There's now an ability to give pieces. +* Fixed the demon mother so it now bites and dies correctly. + +ACCM 1.3.1 --------------------------------------------- +* Fixed the /BuyZPack command. +* Fixed the make person Zombie commands. +* Gave Zombies targets, they now have custom skins and stuff. +* Fixed the drop pods. +* Numerous bug fixes and tweaks. + +ACCM 1.3 --------------------------------------------- +* New Weapons. +* New GUI. +* Redone the /Help Command. +* Fixed the Grav Cycle. +* Fixed the DeathMessages. +* Numerous bug fixes and tweaks. + +ACCM 1.24 --------------------------------------------- +* Fixed the private messaging. +* Digger pack update rate is now set accordingly in serverDefaults.cs. +* Sentinels put back in for scripters who know how to access them. Help commands for them have been removed. +* /setScale and the editor tool now accept larger scales. +* Fixed a message error with /cancelVote. +* There is now an option to remove pods. +* Rapiers are now controllable. +* You may now vote someone "zombie keeper" if the server prefs allow it. + +ACCM 1.23 --------------------------------------------- +* Added the changename function again. +* Fixed a few editing tool bugs. +* Fixed /admin and /superadmin to correctly add the super chaingun. +* Fixed an error with the wait message. +* More variables have been added in serverDefaults.cs. +* Tweaked a few help messages. +* Sentinels have been removed until all the bugs have been worked out or an alternative comes up. (Note: They're not COMPLETELY removed, just the brain is gone.) +* Admin password changer was broken. + +ACCM 1.221 -------------------------------------------- +* Fixed a bug that caused /Stalk to lock up. +* Removed the fix for lingering player objects. Claimed to cause UEs. +* Fixed the digger pack (thanks to Dark Dragon DX for pointing out). + +ACCM 1.22 --------------------------------------------- + +* /set has been split up. +* A new chat command: /power has been added. +* Added chat-logging. Toggleable by a variable. +* Tweaked the help commands. Fixed typos and added sentinels. +* Fixed a bug which caused Sentinels and Bot Zombies to leave lingering player objects. +* Fixed a bug which caused Sentinels and Bot Zombies to not drop correctly. +* Bot Zombies can now be accessed by chat command. +* Removed a lingering $testcheats in an if statement for the medpack which caused the random "burn" check to always turn up true. +* Help commands have been re-done to look more traditional. +* SentinelArmy command has been decreased from super admin to public admin. +* BurnSN command has been decreased from super admin to public admin. +* The Drone commands have been decreased from super admin to public admin. +* Tweaked the drone commands a bit so they are more efficient. + +ACCM 1.21 --------------------------------------------- + +* Fixed an exploit which could let admins to ban anyone. +* Replaced CCM crash protection with Classic Mod version. Less prone to mistakes. +* Patch for CCM has been applied. No more drop pod problems. +* Put protection on SAD. Can be toggled with a variable. +* SAD now has different passwords for Admin and Super Admin. +* Locking the teams is now supported through the /set. +* Admin and Super Admin passwording is now supported through /set. +* /set now exports to preferences whenever necessary. +* The useless SpecOps training has now been removed. +* "Fair Teams" has been taken from Classic and put in here. +* AI addon -- ai.cs. File was missing. Fixes sentinel AI. +* The capture pack has been put on hold. +* A new pack has been added: The Digger Pack. +* Waypoint pack has been removed. +* New File Added: serverDefaults.cs +* Fixed the load menu + +ACCM 1.2 and lower ------------------------------------ +* Not sure... \ No newline at end of file diff --git a/Chat Command List.txt b/Chat Command List.txt new file mode 100644 index 0000000..25cf7cb --- /dev/null +++ b/Chat Command List.txt @@ -0,0 +1,152 @@ +ACCM Advanced Help/Chat Command list. + +Command Tutorial: + +When you see anything similar to: "[SomeMessage]" next to a chat command, that means it is required to type after the chat command in order for the command to work. +For example, the first command on this list, /CreateSquad [Name], requires you to type a Name for the squad after typing /CreateSquad. +Example: /CreateSquad Rouges - This will create a squad called "Rouges" + +There may be specified options that you MUST choose from, for example, the command /DroneBattle has many options. The most useful is that you can customize the battles. +The command looks something like this: /DroneBattle [Single/Battle/Custom], you need to choose one of those variables that are divided by a "/" by simply typing it after the command. + +There are times where the variables are told while you are typing the command, and are not specified in the /help command. For example, if you just type: /DroneBattle Custom, an error +message will appear telling you to type the radius for the drones to spawn, this same thing can also occur with zombie commands as well, and it can occur multiple times in a command. +If this comes up, don't worry, it's perfectly normal. You just need to type the varible the command is requesting. +If you type /DroneBattle Custom this exact message will pop up: "Please specify the spawn radius. Radius cannot be lower than 500 meters." This means you need to specify the radius by +typing: /DroneBattle Custom 500 (Or higher than 500 if you choose) then after you type that, another message will appear requesting another variable. So if a command is asking you to type +a variable, there is nothing wrong with the command, it's just asking to be completed. + +Now you understand the basics of ACCM chat commands, if you are still unsure about something, ask a mod developer. + +Chat Commands List: + +1. Squad Commands: + + • /CreateSquad [Name] - Create your own squad. (Sergeant Rank Required) + • /S [Text] - Privatly Chat with your squad. + • /LeaveSquad - Leave the squad you are in. + • /Invite [PlayerName] - Invite a person to your Squad. + • /RequestInvite [SquadName] - Request an Invite to a Squad. + • /Join - Use this to accept an invite to a squad. + • /SOL - Spawn around your squad's leader. + • /ListSquads - Lists all squads in the server. + • /Force [Join/Leave] [SquadName] [PlayerName] - Force a person to leave/join a squad. (General Rank Required) + +2. Rank Commands: + + • /ItemRestrictions - Gives a list of the weapons/packs that are restricted to ranks. + • /Top5 - Gives a list of players with the highest ranks. + • /ListRanks - Lists all the ranks and the points required to get that rank. + • /CheckStats - Check your current rank and score. + • /CheckStats [PlayerName] - Check the current rank and score of another person. + +3. Zombie Point Help: + + • /PlaceZombiePoint [SpawnName] [ZType] [Time] [MaxZombies] [ZombieLimit] - Places an advanced zombie point. + • /ListZombieSpawns - Lists all the advanced zombie points. + • /DisableSpawn [SpawnName] - Disables an advanced zombie point by label. + • /DisableAllSpawns - Disables all advanced zombie points. + • /EnableSpawn [SpawnName] - Enables an advanced zombie point by label. + • /EnableAllSpawns - Enables all advanced zombie points. + • /RemoveSpawn [SpawnName] - Removes an advanced zombie spawn. + • /RemoveAllSpawns - Removes all advanced zombie spawns. + • /GetStatus [SpawnName] - Get the status of a an advanced zombie spawn by label. + • /MarkZombieSpawns - Marks all zombie spawns. + • /SaveSpawns [Radius] [FileName.cs] - Save all of the advanced zombie spawns whithin said radius to said filename. + • /LoadSpawns [Radius] [FileName.cs] - Load zombie spawns whithin said filename. + • /ReplaceSpawn Type[Single/Radius/All] [Arg2] - Replaces advanced spawns with regular spawns. Which ones get replaced depends solely on your input. + +4. "Quick Help" Commands: + + • /BasicCMDS - Basic Commands. + • /BuildOptions - Building Options. + • /ZCMDS - Zombie Commands. + • /SCMDS - Sentinel Commands. + • /DCMDS- Drone Commands. + • /AdminCMDS - Admin Commands. + • /SACMDS - Super Admin Commands. + +5. The /Help Command: + + • Command Options: BasicCommands, BuildingOptions, AdminCommands, SACommands, SentinelCommands, ZombieCommands, DroneCommands and QuickCommands. + The /help command is like any command that requires options. You need to type "/Help" then type one of the options above. Example: /Help BuildingOptions will give you + a list of Building Options. It's pretty simple, there is also an in-game tutorial for using the /Help command known as "/Help Usage" + +6. Building Options: + + • /Delmypieces - Delete all of your pieces. + • /Objectscale [X Y Z] - Basic scaling function. + • /Getscale - Get the scale of an object. + • /Pos [X Y Z] - This will move an object in X Y Z format. + • /GetPos - This gets the postition of an object. + • /SetFreq [#] - Set your power frequency. + • /ObjectName [DesiredName] - Sets a name to a deployable. + • /Radius [Radius] - Sets the radius for Switches/Tripwires. + • /Cloak - Makes your pieces invisible. + +7. Basic Commands: + + • ![PlayerName] [Message] - Private messaging. + • /Opendoor - Point at a door and use this command to open it. (You can also use /Open) + • /Opendoor [Password] - Point at a door and use this command to open it if it's passworded. + • /Setdoorpass [Password] - Point at a door and use this to set it\'s password. + • /ChooseSpawn [#]- Choose a selected spawnpoint to spawn there. + • /ListSpawns - Displays all spawnpoints on your team. + • /Hack Help - Tells you how to Hack enemy teleporters. + • /SquadHelp - Tells you how to use squad commands. + • /RankHelp - Tells you basic information on ranks. + • /Tips - This will give you a random tip. + • /Info [PlayerName] - Get basic info on a specified player. + +8. Sentinel Commands: + + Unfortunatally, since our AI developer is refusing to work, the Sentinels will not be available ACCM 1.3, we appologize for the inconvienence. + +9. Drone Commands: + + • /DroneBattle [Single/Battle/Custom] - Basic Command where you can spawn either single drones, full battles, or even customize battles. + +10. Zombie Commands: + + • /BuyZpack - Buy a zombie pack. + • /MakeZLord [PlayerName] - Make the person a lord zombie. + • /MakeRapier [PlayerName] - Make the person a regular zombie. + • /Stalk [PlayerName] [ZType] Difficulty[Cool/Light/Medium/Heavy] - Spawn zombies around around a target. + • /Cure [PlayerName] - Cures a person. + • /Infect [PlayerName] - Infect the target with the zombie virus. + • /KillZombies - Kills all zombies and infected. + • /ZDetectDist [Radius] - Set how large the zombie detection distance is. + • /SemiInfect [PlayerName] - Infect that person with the altrenative virus. + • /PlacePod [ZType] Respawn[Yes/No] - Places a zombie pod at your crossairs. + • /RemovePod - Point at a pod to remove it. + • /ZombiePointHelp - Displays the commands for advanced zombie spawn points. + +11. Admin Commands: + + • /JailPlayer [PlayerName] [Time] - Sends a specified player to jail for a set amount of time. + • /AddTeam - Add/Removes a second team. + • /Gag [PlayerName] [Time] - Silences an annoying player. + • /CancelVote - Cancels a vote. + • /DelPieces [PlayerName] - Deletes a specified player\'s pieces. + • /Kill [PlayerName] - Kill someone. + • /ChangeName [PlayerName] [Color] [NewName] - Changes the target\'s name. + • /Goto [PlayerName] - Go directly to a specified person. + • /Summon [PlayerName] - Summon a specified person. + • /Moveto [X Y Z] - Go directly to a desired location. + • /Moveme [X Y Z] - Move on your X, Y or Z axis. + • /BottomPrint [Text] - Send a message on the bottom of the screen. + • /CenterPrint [Text] - Send a message in the center of the screen. + • /ForceTeamSpawn [TeamName] [SpawnNumber] - Forces an entire team to spawn at one spawnpoint. + • /SaveBuilding [Radius] [FileName.cs] - Save buildings in the server. + • /LoadBuilding [FileName.cs] - Load a building file. + • /A [Message] - Admin private messaging. + • /BuySCG - Force a SCG into your inventory. + • /Turrets - Enables/disables turrets when purebuild is on. + +12. Super Admin Commands: + + • /Admin [PlayerName] - Force someone to become Admin. + • /SuperAdmin [PlayerName] - Force someone to become Super Admin. + • /Echo [PlayerName] - Silently mute someone without them knowing. + • /Shred [PlayerName] - Put someone in the ACCM paper shredder. + • /SA [Message] - Super Admin private messaging. \ No newline at end of file diff --git a/Construction Adv Launcher.bat b/Construction Adv Launcher.bat new file mode 100644 index 0000000..9c16940 --- /dev/null +++ b/Construction Adv Launcher.bat @@ -0,0 +1,53 @@ +@echo OFF +echo Construction Mod 0.70 +echo Advanced Mod Loader +echo. +echo Please choose options below to launch server: +echo. +echo Options: +echo 1) Launch Online Dedicated Server (Ispawn). +echo 2) Launch Online Dedicated Server. +echo 3) Launch Offline Dedicated Server (Ispawn). +echo 4) Launch Offline Dedicated Server. +echo 5) Launch Online Listen Server. +echo 6) Launch Offline Listen Server. +echo 7) Exit. +set /p Input=Press enter after selection (1-7): +if %Input%==7 goto exit +set /p RemDSO=Would you like to remove all DSOs from your Tribes 2 install? (y/n): +cd .. +if %RemDSO%==y del /s /q *.dso +if %Input%==1 goto NDI +if %Input%==2 goto ND +if %Input%==3 goto FDI +if %Input%==4 goto FD +if %Input%==5 goto NL +if %Input%==6 goto FL + +goto exit + +:NDI +start ispawn.exe 28000 Tribes2.exe -dedicated -mod Construction +goto exit + +:ND +start Tribes2.exe -dedicated -mod Construction +goto exit + +:FDI +start ispawn.exe 28000 Tribes2.exe -nologin -dedicated -mod Construction +goto exit + +:FD +start Tribes2.exe -nologin -dedicated -mod Construction +goto exit + +:NL +start Tribes2.exe -online -mod Construction +goto exit + +:FL +start Tribes2.exe -nologin -mod Construction +goto exit + +:exit \ No newline at end of file diff --git a/DSO Remover.bat b/DSO Remover.bat new file mode 100644 index 0000000..92acef5 --- /dev/null +++ b/DSO Remover.bat @@ -0,0 +1,30 @@ +@echo off +color 2f +cls +echo; +echo; +echo ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +echo º 0.70 DSO Remover º +echo ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ +echo º This DOS batch file will remove those pesky dso's from º +echo º your Tribes 2 installation. Regardless of where it is. º +echo ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ͹ +echo º Remove all DSO files(not flagged as read only) from º +echo º Tribes 2 directory by pressing enter. º +echo ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ +echo; +echo; +pause +cd .. +del /s /q *.dso +cls +echo; +echo; +echo ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +echo º Success! º +echo º º +echo º All dso's from Tribes 2 have been removed. º +echo ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ +echo; +echo; +pause \ No newline at end of file diff --git a/MergeToolTemp.txt b/MergeToolTemp.txt new file mode 100644 index 0000000..f543ec2 --- /dev/null +++ b/MergeToolTemp.txt @@ -0,0 +1,22 @@ +//--- OBJECT WRITE BEGIN --- +new StaticShape() { + position = "20.5956 -19.0981 100"; + rotation = "0 0 1 47.5405"; + scale = "0.124997 0.0949413 8"; + dataBlock = "DeployedSpine"; + lockCount = "0"; + homingCount = "0"; + + Target = "-1"; + lastState = "0"; + depTime = "117579"; + dSurface = "1982"; + ownerguid = "2000181"; + grounded = "1"; + team = "1"; + needsFit = "1"; + owner = "1997"; + powerFreq = "1"; + powerCount = "0"; +}; +//--- OBJECT WRITE END --- diff --git a/Scripts/CTFGame.cs b/Scripts/CTFGame.cs new file mode 100644 index 0000000..a91301c --- /dev/null +++ b/Scripts/CTFGame.cs @@ -0,0 +1,2055 @@ +// 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) +{ + + if(isDemo()) + { + %game.SCORE_PER_SUICIDE = -1; + %game.SCORE_PER_TEAMKILL = -1; + %game.SCORE_PER_DEATH = -1; + + %game.SCORE_PER_KILL = 1; + %game.SCORE_PER_PLYR_FLAG_CAP = 3; + %game.SCORE_PER_TEAM_FLAG_CAP = 100; + %game.SCORE_PER_TEAM_FLAG_TOUCH = 1; + %game.SCORE_PER_GEN_DESTROY = 2; + %game.SCORE_PER_ESCORT_ASSIST = 1; + + %game.SCORE_PER_TURRET_KILL = 1; + %game.SCORE_PER_FLAG_DEFEND = 1; + %game.SCORE_PER_CARRIER_KILL = 1; + %game.SCORE_PER_FLAG_RETURN = 1; + %game.SCORE_PER_GEN_DEFEND = 1; + %game.SCORE_PER_GEN_REPAIR = 1; + + %game.FLAG_RETURN_DELAY = 45 * 1000; //45 seconds + + %game.TIME_CONSIDERED_FLAGCARRIER_THREAT = 3 * 1000; //after damaging enemy flag carrier + %game.RADIUS_GEN_DEFENSE = 20; //meters + %game.RADIUS_FLAG_DEFENSE = 20; //meters + + %game.TOUCH_DELAY_MS = 20000; //20 secs + + %game.fadeTimeMS = 2000; + + %game.notifyMineDist = 7.5; + + + %game.stalemate = false; + %game.stalemateObjsVisible = false; + %game.stalemateTimeMS = 60000; + %game.stalemateFreqMS = 15000; + %game.stalemateDurationMS = 6000; + } + if( !isDemo() ) + { + %game.SCORE_PER_SUICIDE = -10; + %game.SCORE_PER_TEAMKILL = -10; + %game.SCORE_PER_DEATH = 0; + + %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_TURRET_KILL = 10; // controlled + %game.SCORE_PER_TURRET_KILL_AUTO = 3; // uncontrolled + %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_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 = 7; + %game.SCORE_PER_DESTROY_STRIKEFIGHTER = 8; + %game.SCORE_PER_DESTROY_HELICOPTER = 10; + %game.SCORE_PER_DESTROY_AWACS = 5; + %game.SCORE_PER_DESTROY_BOMBER = 12; + %game.SCORE_PER_DESTROY_GUNSHIP = 15; + %game.SCORE_PER_DESTROY_HEAVYHELICOPTER = 8; + %game.SCORE_PER_DESTROY_TRANSPORT = 5; + %game.SCORE_PER_DESTROY_WILDCAT = 3; + %game.SCORE_PER_DESTROY_TANK = 5; + %game.SCORE_PER_DESTROY_HEAVYTANK = 6; + %game.SCORE_PER_DESTROY_CGTANK = 5; + %game.SCORE_PER_DESTROY_FFTRANSPORT = 5; + %game.SCORE_PER_DESTROY_ARTILLERY = 8; + %game.SCORE_PER_DESTROY_MPB = 8; + %game.SCORE_PER_DESTROY_TRANSBOAT = 4; + %game.SCORE_PER_DESTROY_SUB = 10; + %game.SCORE_PER_DESTROY_BOAT = 15; + %game.SCORE_PER_PASSENGER = 3; + + %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_SOLAR = 4; + %game.SCORE_PER_REPAIR_SENTRY = 2; + %game.SCORE_PER_REPAIR_DEP_TUR = 3; + %game.SCORE_PER_REPAIR_DEP_INV = 2; + + %game.FLAG_RETURN_DELAY = 45 * 1000; //45 seconds + + %game.TIME_CONSIDERED_FLAGCARRIER_THREAT = 3 * 1000; //after damaging enemy flag carrier + %game.RADIUS_GEN_DEFENSE = 20; //meters + %game.RADIUS_FLAG_DEFENSE = 20; //meters + + %game.TOUCH_DELAY_MS = 20000; //20 secs + + %game.fadeTimeMS = 2000; + + %game.notifyMineDist = 7.5; + + + %game.stalemate = false; + %game.stalemateObjsVisible = false; + %game.stalemateTimeMS = 60000; + %game.stalemateFreqMS = 15000; + %game.stalemateDurationMS = 6000; + } + +} + +package CTFGame { + +function ShapeBaseData::onDestroyed(%data, %obj) +{ + %scorer = %obj.lastDamagedBy; + if(!isObject(%scorer)) + return; + + if( (%scorer.getType() & $TypeMasks::GameBaseObjectType) && + %scorer.getDataBlock().catagory $= "Vehicles" ) + { + // --------------------------------------------- + // z0dd - ZOD, 6/18/02. %name was never defined. + %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 return; //unmanned turret + } + + if(!%damagingTeam) + %damagingTeam = %scorer.team; + + if(%damagingTeam == %obj.team) + { + //error("team objects dont score"); + return; + } + + if(%obj.soiledByEnemyRepair) + { + //error(%obj SPC "was once repiared by an enemy. No destruction points."); + return; + } + + %objType = %obj.getDataBlock().getName(); + if(%objType $= "GeneratorLarge") + { + %score = game.awardScoreGenDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "SensorLargePulse" || %objType $= "SensorMediumPulse") + { + %score = game.awardScoreSensorDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "TurretBaseLarge") + { + %score = game.awardScoreTurretDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "StationInventory") + { + %score = game.awardScoreInvDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "StationVehicle") + { + %score = game.awardScoreVehicleStationDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "SolarPanel") + { + %score = game.awardScoreSolarDestroy(%scorer); + game.shareScore(%score, %score); + } + else if(%objType $= "SentryTurret") + { + %score = game.awardScoreSentryDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "DeployedMotionSensor" || %objType $= "DeployedPulseSensor") + { + %score = game.awardScoreDepSensorDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "TurretDeployedWallIndoor" || %objType $= "TurretDeployedFloorIndoor" || + %objType $= "TurretDeployedCeilingIndoor" || %objType $= "TurretDeployedOutdoor") + { + %score = game.awardScoreDepTurretDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "DeployedStationInventory") + { + %score = game.awardScoreDepStationDestroy(%scorer); + game.shareScore(%scorer, %score); + } + +} + +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 class, to fix console spam. + //if((%this.getClassName() !$= "InteriorInstance") && (%this.getClassName() !$= "SimGroup")) + 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/27/02. Fixes flags hovering + // over friendly player when collision occurs + %flag.static = true; +} + +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); + game.schedule(3000, flagReturn, %obj); + } +} + +}; + +//-------------------------------------------------------------------------- +// 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(isDemo() || $host::tournamentMode) + { + 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) +{ + if ( isDemo() || $host::tournamentMode) + return $TeamName[%team]; + + //error("CTFGame::getTeamName"); + if(!$host::useCustomSkins) + { + %terrain = MissionGroup.musicTrack; + //error("Terrain type is: " SPC %terrain); + switch$(%terrain) + { + case "lush": + if(%team == 1) + %name = 'Blood Eagle'; + else if(%team == 2) + %name = 'Diamond Sword'; + + case "badlands": + if(%team == 1) + %name = 'Starwolf'; + else if(%team == 2) + %name = 'Diamond Sword'; + + case "ice": + if(%team == 1) + %name = 'Starwolf'; + else if(%team == 2) + %name = 'Blood Eagle'; + + case "desert": + if(%team == 1) + %name = 'Phoenix'; + else if(%team == 2) + %name = 'Blood Eagle'; + + case "Volcanic": + if(%team == 1) + %name = 'Diamond Sword'; + else if(%team == 2) + %name = 'Phoenix'; + + default: + if(%team == 2) + %name = 'Inferno'; + else + %name = 'Storm'; + } + + if(%name $= "") + { + //error("No team Name ============================="); + %name = $teamName[%team]; + } + } + else + %name = $TeamName[%team]; + + //error("%name = " SPC getTaggedString(%name)); + return %name; +} + +//-------------------------------------------------------------------------- +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")) + { + //flag isn't held and has been touched by a live player + if (%client.team == %flag.team) + %game.playerTouchOwnFlag(%player, %flag); + else + %game.playerTouchEnemyFlag(%player, %flag); + } + + // toggle visibility of the flag + setTargetRenderMask(%flag.waypoint.getTarget(), %flag.isHome ? 0 : 1); + +} + +function CTFGame::playerTouchOwnFlag(%game, %player, %flag) +{ + if(%flag.isHome) + { + if (%player.holdingFlag !$= "") + %game.flagCap(%player); + } + else + %game.flagReturn(%flag, %player); + + //call the AI function + %game.AIplayerTouchOwnFlag(%player, %flag); +} + +function CTFGame::playerTouchEnemyFlag(%game, %player, %flag) +{ + %client = %player.client; + %player.holdingFlag = %flag; //%player has this flag + %flag.carrier = %player; //this %flag is carried by %player + + %player.mountImage(FlagImage, $FlagSlot, true, %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))); +} + +function CTFGame::playerDroppedFlag(%game, %player) +{ + %client = %player.client; + %flag = %player.holdingFlag; + + %game.playerLostFlagTarget(%player); + + %player.holdingFlag = ""; //player isn't holding a flag anymore + %flag.carrier = ""; //flag isn't held anymore + $flagStatus[%flag.team] = ""; + + %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.~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team); + messageTeam(%flag.team, 'MsgCTFFlagDropped', '\c2Your flag has been dropped by %1!~wfx/misc/flag_drop.wav', %client.name, 0, %flag.team); + messageTeam(0, 'MsgCTFFlagDropped', '\c2%1 dropped the %2 flag.~wfx/misc/flag_drop.wav', %client.name, %teamName, %flag.team); + if(!%player.client.outOfBounds) + messageClient(%client, 'MsgCTFFlagDropped', '\c2You dropped the %2 flag.~wfx/misc/flag_drop.wav', 0, %teamName, %flag.team); + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") dropped team "@%flag.team@" flag"); + + //don't duplicate the schedule if there's already one in progress... + if ($FlagReturnTimer[%flag] <= 0) + $FlagReturnTimer[%flag] = %game.schedule(%game.FLAG_RETURN_DELAY - %game.fadeTimeMS, "flagReturnFade", %flag); + + //call the AI function + %game.AIplayerDroppedFlag(%player, %flag); +} + +function CTFGame::flagCap(%game, %player) +{ + %client = %player.client; + %flag = %player.holdingFlag; + %flag.carrier = ""; + + %game.playerLostFlagTarget(%player); + //award points to player and team + %teamName = %game.getTeamName(%flag.team); + messageTeamExcept(%client, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag!~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team); + messageTeam(%flag.team, 'MsgCTFFlagCapped', '\c2Your flag was captured by %1.~wfx/misc/flag_lost.wav', %client.name, 0, %flag.team, %client.team); + messageTeam(0, 'MsgCTFFlagCapped', '\c2%1 captured the %2 flag!~wfx/misc/flag_capture.wav', %client.name, %teamName, %flag.team, %client.team); + messageClient(%client, 'MsgCTFFlagCapped', '\c2You captured the %2 flag!~wfx/misc/flag_capture.wav', 0, %teamName, %flag.team, %client.team); + + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") capped team "@%client.team@" flag"); + %player.holdingFlag = ""; //no longer holding it. + %player.unMountImage($FlagSlot); + %game.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) +{ + 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', 0, %teamName, %flag.team); + logEcho(%client.nameBase@" (pl "@%player@"/cl "@%client@") returned team "@%flag.team@" flag"); + + // find out what type of return it is + // stalemate return? + if(!isDemo()) + { + 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 %game.awardScoreFlagReturn(%player.client); + } + else + { + //returned due to timer + messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, %teamName, %flag.team); //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(); +} + +function CTFGame::endStalemate(%game) +{ + %game.stalemate = false; + %game.hideStalemateTargets(); + cancel(%game.stalemateSchedule); +} + +function CTFGame::flagReset(%game, %flag) +{ + //any time a flag is reset, kill the stalemate schedule + %game.endStalemate(); + + //make sure if there's a player carrying it (probably one out of bounds...), it is stripped first + if (isObject(%flag.carrier)) + { + //hide the target hud icon for slot 2 (a centermass flag - visible only as part of a teams sensor network) + %game.playerLostFlagTarget(%flag.carrier); + %flag.carrier.holdingFlag = ""; //no longer holding it. + %flag.carrier.unMountImage($FlagSlot); + } + + //fades, restore default position, home, velocity, general status, etc. + %flag.setVelocity("0 0 0"); + %flag.setTransform(%flag.originalPosition); + %flag.isHome = true; + %flag.carrier = ""; + %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/27/02. Fixes flags hovering + // over friendly player when collision occurs + %flag.static = true; +} + +function CTFGame::timeLimitReached(%game) +{ + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function CTFGame::scoreLimitReached(%game) +{ + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function CTFGame::notifyMineDeployed(%game, %mine) +{ + //see if the mine is within 5 meters of the flag stand... + %mineTeam = %mine.sourceObject.team; + %homeFlag = $TeamFlag[%mineTeam]; + if (isObject(%homeFlag)) + { + %dist = VectorDist(%homeFlag.originalPosition, %mine.position); + if (%dist <= %game.notifyMineDist) + { + messageTeam(%mineTeam, 'MsgCTFFlagMined', "The flag has been mined.~wvoice/announcer/flag_minedFem.wav" ); + } + } +} + +function CTFGame::gameOver(%game) +{ + //call the default + DefaultGame::gameOver(%game); + + //send the winner message + %winner = ""; + if ($teamScore[1] > $teamScore[2]) + %winner = %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); + } + %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); + + if(!isDemo()) + { + %cl.offenseScore = %killPoints + + %cl.suicides * %game.SCORE_PER_SUICIDE + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.teamKills * %game.SCORE_PER_TEAMKILL + + %cl.scoreHeadshot * %game.SCORE_PER_HEADSHOT + + %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.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.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; + } + + if( isDemo() ) + { + %cl.offenseScore = %killPoints + + %cl.flagDefends * %game.SCORE_PER_FLAG_DEFEND + + %cl.suicides * %game.SCORE_PER_SUICIDE + //-1 + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1 + %cl.teamKills * %game.SCORE_PER_TEAMKILL + // -1 + %cl.flagCaps * %game.SCORE_PER_PLYR_FLAG_CAP + // 3 + %cl.genDestroys * %game.SCORE_PER_GEN_DESTROY; // 2 + + %cl.defenseScore = %cl.genDefends * %game.SCORE_PER_GEN_DEFEND + // 1 + %cl.carrierKills * %game.SCORE_PER_CARRIER_KILL + // 1 + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1 + %cl.turretKills * %game.SCORE_PER_TURRET_KILL + // 1 + %cl.flagReturns * %game.SCORE_PER_FLAG_RETURN + // 1 + %cl.genRepairs * %game.SCORE_PER_GEN_REPAIR; // 1 + } + + %cl.score = mFloor(%cl.offenseScore + %cl.defenseScore + (%cl.zkills * $zombie::killpoints)); + + %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::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 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' : ""); + + if(isDemo()) + { + messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %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); + messageClient(%cl, 'msgCTFFriendCap', '\c0You receive %1 point%2 for stealing and capturing the enemy flag!', %game.SCORE_PER_PLYR_FLAG_CAP, %plural); + } + + if(!isDemo()) + { + 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); + } + 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); + messageTeam(%flag.team, 'msgCTFEnemyCap', '\c0Enemy %1 received %2 point%3 for capturing your flag!', %cl.name, %game.SCORE_PER_PLYR_FLAG_CAP, %plural); + } + } + } + + %game.recalcScore(%cl); + + 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) +{ + if(isDemo()) + { + %cl.flagReturns++; + if (%game.SCORE_PER_FLAG_RETURN != 0) + { + messageClient(%cl, 'scoreFlaRetMsg', '\c0You received a %1 point bonus for returning your flag.', %game.SCORE_PER_FLAG_RETURN); + //messageTeamExcept(%cl, 'scoreFlaRetMsg', '\c0Teammate %1 received a %2 point bonus for returning your flag.', %cl.name, %game.SCORE_PER_FLAG_RETURN); + } + %game.recalcScore(%cl); + } + else + { + 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; +} + +// Asset Destruction scoring +function CTFGame::awardScoreGenDestroy(%game,%cl) +{ + %cl.genDestroys++; + if (%game.SCORE_PER_DESTROY_GEN != 0) + { + messageClient(%cl, 'msgGenDes', '\c0You received a %1 point bonus for destroying an enemy generator.', %game.SCORE_PER_DESTROY_GEN); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_GEN; +} + +function CTFGame::awardScoreSensorDestroy(%game,%cl) +{ + %cl.sensorDestroys++; + if (%game.SCORE_PER_DESTROY_SENSOR != 0) + { + messageClient(%cl, 'msgSensorDes', '\c0You received a %1 point bonus for destroying an enemy sensor.', %game.SCORE_PER_DESTROY_SENSOR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SENSOR; +} + +function CTFGame::awardScoreTurretDestroy(%game,%cl) +{ + %cl.turretDestroys++; + if (%game.SCORE_PER_DESTROY_TURRET != 0) + { + messageClient(%cl, 'msgTurretDes', '\c0You received a %1 point bonus for destroying an enemy turret.', %game.SCORE_PER_DESTROY_TURRET); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_TURRET; +} + +function CTFGame::awardScoreInvDestroy(%game,%cl) +{ + %cl.IStationDestroys++; + if (%game.SCORE_PER_DESTROY_ISTATION != 0) + { + messageClient(%cl, 'msgInvDes', '\c0You received a %1 point bonus for destroying an enemy inventory station.', %game.SCORE_PER_DESTROY_ISTATION); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_ISTATION; +} + +function CTFGame::awardScoreVehicleStationDestroy(%game,%cl) +{ + %cl.VStationDestroys++; + if (%game.SCORE_PER_DESTROY_VSTATION != 0) + { + messageClient(%cl, 'msgVSDes', '\c0You received a %1 point bonus for destroying an enemy vehicle station.', %game.SCORE_PER_DESTROY_VSTATION); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_VSTATION; +} + +function CTFGame::awardScoreSolarDestroy(%game,%cl) +{ + %cl.SolarDestroys++; + if (%game.SCORE_PER_DESTROY_SOLAR != 0) + { + messageClient(%cl, 'msgSolarDes', '\c0You received a %1 point bonus for destroying an enemy solar panel.', %game.SCORE_PER_DESTROY_SOLAR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SOLAR; +} + +function CTFGame::awardScoreSentryDestroy(%game,%cl) +{ + %cl.sentryDestroys++; + if (%game.SCORE_PER_DESTROY_SENTRY != 0) + { + messageClient(%cl, 'msgSentryDes', '\c0You received a %1 point bonus for destroying an enemy sentry turret.', %game.SCORE_PER_DESTROY_SENTRY); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SENTRY; +} + +function CTFGame::awardScoreDepSensorDestroy(%game,%cl) +{ + %cl.depSensorDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_SENSOR != 0) + { + messageClient(%cl, 'msgDepSensorDes', '\c0You received a %1 point bonus for destroying an enemy deployable.', %game.SCORE_PER_DESTROY_DEP_SENSOR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_SENSOR; +} + +function CTFGame::awardScoreDepTurretDestroy(%game,%cl) +{ + %cl.depTurretDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_TUR != 0) + { + messageClient(%cl, 'msgDepTurDes', '\c0You received a %1 point bonus for destroying an enemy deployed turret.', %game.SCORE_PER_DESTROY_DEP_TUR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_TUR; +} + +function CTFGame::awardScoreDepStationDestroy(%game,%cl) +{ + %cl.depStationDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_INV != 0) + { + messageClient(%cl, 'msgDepInvDes', '\c0You received a %1 point bonus for destroying an enemy deployed station.', %game.SCORE_PER_DESTROY_DEP_INV); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_INV; +} + +function CTFGame::awardScoreGenDefend(%game, %killerID) +{ + %killerID.genDefends++; + if (%game.SCORE_PER_GEN_DEFEND != 0) + { + messageClient(%killerID, 'msgGenDef', '\c0You received a %1 point bonus for defending a generator.', %game.SCORE_PER_GEN_DEFEND); + //messageTeamExcept(%killerID, 'msgGenDef', '\c0Teammate %1 received a %2 point bonus for defending a generator.', %killerID.name, %game.SCORE_PER_GEN_DEFEND); + } + %game.recalcScore(%cl); + 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', '\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', '\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', '\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; +} + +function CTFGame::resetScore(%game, %client) +{ + %client.offenseScore = 0; + %client.kills = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.escortAssists = 0; + %client.teamKills = 0; + %client.flagCaps = 0; + %client.flagGrabs = 0; + %client.genDestroys = 0; + %client.sensorDestroys = 0; + %client.turretDestroys = 0; + %client.iStationDestroys = 0; + %client.vstationDestroys = 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.turretKills = 0; + %client.mannedTurretKills = 0; + %client.flagReturns = 0; + %client.genRepairs = 0; + %client.SensorRepairs =0; + %client.TurretRepairs =0; + %client.StationRepairs =0; + %client.VStationRepairs =0; + %client.solarRepairs =0; + %client.sentryRepairs =0; + %client.depInvRepairs =0; + %client.depTurretRepairs=0; + %client.returnPts = 0; + %client.score = 0; +} + +function CTFGame::objectRepaired(%game, %obj, %objName) +{ + %item = %obj.getDataBlock().getName(); + switch$ (%item) + { + case generatorLarge : + %game.genOnRepaired(%obj, %objName); + case sensorMediumPulse : + %game.sensorOnRepaired(%obj, %objName); + case sensorLargePulse : + %game.sensorOnRepaired(%obj, %objName); + case stationInventory : + %game.stationOnRepaired(%obj, %objName); + case turretBaseLarge : + %game.turretOnRepaired(%obj, %objName); + case stationVehicle : + %game.vStationOnRepaired(%obj, %objName); + case solarPanel : + %game.solarPanelOnRepaired(%obj, %objName); + case sentryTurret : + %game.sentryTurretOnRepaired(%obj, %objName); + case TurretDeployedWallIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedFloorIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedCeilingIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedOutdoor: + %game.depTurretOnRepaired(%obj, %objName); + } + %obj.wasDisabled = false; +} + +function CTFGame::genOnRepaired(%game, %obj, %objName) +{ + + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgGenRepaired', '\c0%1 repaired the %2 Generator!', %repairman.name, %obj.nameTag); + %game.awardScoreGenRepair(%obj.repairedBy); + } +} + +function CTFGame::stationOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgStationRepaired', '\c0%1 repaired the %2 Inventory Station!', %repairman.name, %obj.nameTag); + %game.awardScoreStationRepair(%obj.repairedBy); + } +} + +function CTFGame::sensorOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgSensorRepaired', '\c0%1 repaired the %2 Pulse Sensor!', %repairman.name, %obj.nameTag); + %game.awardScoreSensorRepair(%obj.repairedBy); + } +} + +function CTFGame::turretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgTurretRepaired', '\c0%1 repaired the %2 Turret!', %repairman.name, %obj.nameTag); + %game.awardScoreTurretRepair(%obj.repairedBy); + } +} + +function CTFGame::vStationOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgvstationRepaired', '\c0%1 repaired the Vehicle Station!', %repairman.name); + %game.awardScoreVStationRepair(%obj.repairedBy); + } +} + +function CTFGame::solarPanelOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgsolarRepaired', '\c0%1 repaired the %2 Solar Panel!', %repairman.name, %obj.nameTag); + %game.awardScoreSolarRepair(%obj.repairedBy); + } +} + +function CTFGame::sentryTurretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgsentryTurretRepaired', '\c0%1 repaired the %2 Sentry Turret!', %repairman.name, %obj.nameTag); + %game.awardScoreSentryRepair(%obj.repairedBy); + } +} + +function CTFGame::depTurretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %game.awardScoreDepTurretRepair(%obj.repairedBy); + } +} + +function CTFGame::depInvOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %game.awardScoreDepInvRepair(%obj.repairedBy); + } +} + +function CTFGame::awardScoreGenRepair(%game, %cl) +{ + %cl.genRepairs++; + if (%game.SCORE_PER_REPAIR_GEN != 0) + { + messageClient(%cl, 'msgGenRep', '\c0You received a %1 point bonus for repairing a generator.', %game.SCORE_PER_REPAIR_GEN); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreStationRepair(%game, %cl) +{ + %cl.stationRepairs++; + if (%game.SCORE_PER_REPAIR_ISTATION != 0) + { + messageClient(%cl, 'msgIStationRep', '\c0You received a %1 point bonus for repairing a inventory station.', %game.SCORE_PER_REPAIR_ISTATION); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreSensorRepair(%game, %cl) +{ + %cl.sensorRepairs++; + if (%game.SCORE_PER_REPAIR_SENSOR != 0) + { + messageClient(%cl, 'msgSensorRep', '\c0You received a %1 point bonus for repairing a sensor.', %game.SCORE_PER_REPAIR_SENSOR); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreTurretRepair(%game, %cl) +{ + %cl.TurretRepairs++; + if (%game.SCORE_PER_REPAIR_TURRET != 0) + { + messageClient(%cl, 'msgTurretRep', '\c0You received a %1 point bonus for repairing a base turret.', %game.SCORE_PER_REPAIR_TURRET); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreVStationRepair(%game, %cl) +{ + %cl.VStationRepairs++; + if (%game.SCORE_PER_REPAIR_VSTATION != 0) + { + messageClient(%cl, 'msgVStationRep', '\c0You received a %1 point bonus for repairing a vehicle station.', %game.SCORE_PER_REPAIR_VSTATION); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreSolarRepair(%game, %cl) +{ + %cl.solarRepairs++; + if (%game.SCORE_PER_REPAIR_SOLAR != 0) + { + messageClient(%cl, 'msgsolarRep', '\c0You received a %1 point bonus for repairing a solar panel.', %game.SCORE_PER_REPAIR_SOLAR); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreSentryRepair(%game, %cl) +{ + %cl.sentryRepairs++; + if (%game.SCORE_PER_REPAIR_SENTRY != 0) + { + messageClient(%cl, 'msgSentryRep', '\c0You received a %1 point bonus for repairing a sentry turret.', %game.SCORE_PER_REPAIR_SENTRY); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreDepTurretRepair(%game, %cl) +{ + %cl.depTurretRepairs++; + if (%game.SCORE_PER_REPAIR_DEP_TUR != 0) + { + messageClient(%cl, 'msgDepTurretRep', '\c0You received a %1 point bonus for repairing a deployed turret.', %game.SCORE_PER_REPAIR_DEP_TUR); + } + %game.recalcScore(%cl); +} + +function CTFGame::awardScoreDepInvRepair(%game, %cl) +{ + %cl.depInvRepairs++; + if (%game.SCORE_PER_REPAIR_DEP_INV != 0) + { + messageClient(%cl, 'msgDepInvRep', '\c0You received a %1 point bonus for repairing a deployed station.', %game.SCORE_PER_REPAIR_DEP_INV); + } + %game.recalcScore(%cl); +} + +function CTFGame::enterMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + %player.client.outOfBounds = false; + messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area.'); + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") entered mission area"); + + //the instant a player leaves the mission boundary, the flag is dropped, and the return is scheduled... + if (%player.holdingFlag > 0) + { + cancel($FlagReturnTimer[%player.holdingFlag]); + $FlagReturnTimer[%player.holdingFlag] = ""; + } +} + +function CTFGame::leaveMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + // maybe we'll do this just in case + %player.client.outOfBounds = true; + // if the player is holding a flag, strip it and throw it back into the mission area + // otherwise, just print a message + if(%player.holdingFlag > 0) + %game.boundaryLoseFlag(%player); + else + messageClient(%player.client, 'MsgLeaveMissionArea', '\c1You have left the mission area.~wfx/misc/warning_beep.wav'); + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") left mission area"); +} + +function CTFGame::boundaryLoseFlag(%game, %player) +{ + // this is called when a player goes out of the mission area while holding + // the enemy flag. - make sure the player is still out of bounds + if (!%player.client.outOfBounds || !isObject(%player.holdingFlag)) + return; + + %client = %player.client; + %flag = %player.holdingFlag; + %flag.setVelocity("0 0 0"); + %flag.setTransform(%player.getWorldBoxCenter()); + %flag.setCollisionTimeout(%player); + + %game.playerDroppedFlag(%player); + + // now for the tricky part -- throwing the flag back into the mission area + // let's try throwing it back towards its "home" + %home = %flag.originalPosition; + %vecx = firstWord(%home) - firstWord(%player.getWorldBoxCenter()); + %vecy = getWord(%home, 1) - getWord(%player.getWorldBoxCenter(), 1); + %vecz = getWord(%home, 2) - getWord(%player.getWorldBoxCenter(), 2); + %vec = %vecx SPC %vecy SPC %vecz; + + // normalize the vector, scale it, and add an extra "upwards" component + %vecNorm = VectorNormalize(%vec); + %vec = VectorScale(%vecNorm, 1500); + %vec = vectorAdd(%vec, "0 0 500"); + + // --------------------------------------------------------------------- + // z0dd - ZOD, 6/09/02. Remove anti-hover so flag can be thrown properly + %flag.static = false; + + // apply the impulse to the flag object + %flag.applyImpulse(%player.getWorldBoxCenter(), %vec); + + //don't forget to send the message + // ----------------------------------------------------------------------------------------------------------------------------------------------------------------- + // z0dd - ZOD 3/30/02. Message was sending the wrong varible to objective hud thus not updating hud correctly. + //messageClient(%player.client, 'MsgCTFFlagDropped', '\c1You have left the mission area and lost the flag.~wfx/misc/flag_drop.wav', 0, 0, %player.holdingFlag.team); + messageClient(%player.client, 'MsgCTFFlagDropped', '\c1You have left the mission area and lost the flag.~wfx/misc/flag_drop.wav', 0, 0, %flag.team); + + logEcho(%player.client.nameBase@" (pl "@%player@"/cl "@%player.client@") lost flag (out of bounds)"); +} + +function CTFGame::dropFlag(%game, %player) +{ + if(%player.holdingFlag > 0) + { + if (!%player.client.outOfBounds) + %player.throwObject(%player.holdingFlag); + else + %game.boundaryLoseFlag(%player); + } +} + +function CTFGame::applyConcussion(%game, %player) +{ + %game.dropFlag( %player ); +} + +function CTFGame::vehicleDestroyed(%game, %vehicle, %destroyer){ + %data = %vehicle.getDataBlock(); + %vehicleType = getTaggedString(%data.targetTypeTag); + if(%vehicleType !$= "MPB") + %vehicleType = strlwr(%vehicleType); + + %enemyTeam = ( %destroyer.team == 1 ) ? 2 : 1; + + %scorer = 0; + %multiplier = 3; + %shouldshare = 0; + + %passengers = 0; + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%vehicle.getMountNodeObject(%i)) + %passengers++; + + if(%destroyer.client){ + %destroyer = %destroyer.client; + %scorer = %destroyer; + + if(%vehicle.lastDamageType == $DamageType::Mine) + %multiplier = 1; + } + else if(%destroyer.getClassName() $= "Turret"){ + if(%destroyer.getControllingClient()){ + %destroyer = %destroyer.getControllingClient(); + %scorer = %destroyer; + if(%destroyer.vehicleMounted){ + if(%destroyer.vehicleMounted.getMountNodeObject(0)){ + %driver = %destroyer.vehicleMounted.getMountNodeObject(0); + %sharer = %driver.client; + %shouldshare = 1; + } + %multiplier = 2; + } + } + else{ + %destroyerName = "A turret"; + %multiplier = 0; + } + } + else if(%destroyer.getDataBlock().catagory $= "Vehicles"){ + if(%destroyer.getMountNodeObject(0)){ + %destroyer = %destroyer.getMountNodeObject(0).client; + %scorer = %destroyer; + } + %multiplier = 2; + } + else + return; + + + if(%destroyerName $= "") + %destroyerName = %destroyer.name; + + if(%vehicle.team == %destroyer.team){ + %pref = (%vehicleType $= "Assault Tank") ? "an" : "a"; + messageAll( 'msgVehicleTeamDestroy', '\c0%1 TEAMKILLED %3 %2!', %destroyerName, %vehicleType, %pref); + } + else{ + messageTeamExcept(%destroyer, 'msgVehicleDestroy', '\c0%1 destroyed an enemy %2.', %destroyerName, %vehicleType); + messageTeam(%enemyTeam, 'msgVehicleDestroy', '\c0%1 destroyed your team\'s %2.', %destroyerName, %vehicleType); + + if(%scorer){ + %value = %game.awardScoreVehicleDestroyed(%scorer, %vehicleType, %multiplier, %passengers); + if(%shouldshare == 1) + %game.awardScoreVehicleDestroyed(%sharer, %vehicleType, (%multiplier / 2), %passengers); + %game.shareScore(%value); + } + } +} + +function CTFGame::awardScoreVehicleDestroyed(%game, %client, %vehicleType, %mult, %passengers) +{ + if(isDemo()) + return 0; + + if(%vehicleType $= "Interceptor") + %base = %game.SCORE_PER_DESTROY_SHRIKE; + + else if(%vehicleType $= "Fighter") + %base = %game.SCORE_PER_DESTROY_STRIKEFIGHTER; + + else if(%vehicleType $= "Assault Chopper") + %base = %game.SCORE_PER_DESTROY_HELICOPTER; + + else if(%vehicleType $= "AWACS") + %base = %game.SCORE_PER_DESTROY_AWACS; + + else if(%vehicleType $= "Bomber") + %base = %game.SCORE_PER_DESTROY_BOMBER; + + else if(%vehicleType $= "Gunship") + %base = %game.SCORE_PER_DESTROY_GUNSHIP; + + else if(%vehicleType $= "Transport Chopper") + %base = %game.SCORE_PER_DESTROY_HEAVYHELICOPTER; + + else if(%vehicleType $= "Heavy Transport") + %base = %game.SCORE_PER_DESTROY_TRANSPORT; + + else if(%vehicleType $= "Grav Cycle") + %base = %game.SCORE_PER_DESTROY_WILDCAT; + + else if(%vehicleType $= "Light Tank") + %base = %game.SCORE_PER_DESTROY_TANK; + + else if(%vehicleType $= "Assault Tank") + %base = %game.SCORE_PER_DESTROY_HEAVYTANK; + + else if(%vehicleType $= "chaingun tank") + %base = %game.SCORE_PER_DESTROY_CGTANK; + + else if(%vehicleType $= "APC") + %base = %game.SCORE_PER_DESTROY_FFTRANSPORT; + + else if(%vehicleType $= "Heavy Artillery") + %base = %game.SCORE_PER_DESTROY_ARTILLERY; + + else if(%vehicleType $= "MPB") + %base = %game.SCORE_PER_DESTROY_MPB; + + else if(%vehicleType $= "Boat") + %base = %game.SCORE_PER_DESTROY_TRANSBOAT; + + else if(%vehicleType $= "Submarine") + %base = %game.SCORE_PER_DESTROY_SUB; + + else if(%vehicleType $= "GunBoat") + %base = %game.SCORE_PER_DESTROY_BOAT; + + + %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) +{ + if(isDemo()) + return 0; + + //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); +} diff --git a/Scripts/CombatConGame.cs b/Scripts/CombatConGame.cs new file mode 100644 index 0000000..6b3f877 --- /dev/null +++ b/Scripts/CombatConGame.cs @@ -0,0 +1,1240 @@ +// DisplayName = CombatCon + +//--- GAME RULES BEGIN --- +// Make a base or use map bases. +// Defend your base. +// Destroy the enemy base. +// Team killing and suicide penalties. +//--- GAME RULES END --- + +// CombatCon Game +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Notes are all done by Blnukem. These are to help you know what's what when +// editing this gametype. (The notes are not completed, so don't edit anything) +//------------------------------------------------------------------------------ +// NOTICE: +// This rather huge file will be cleaned and remade in a later version of ACCM. +// So for all you file moderators out there, have fun tweaking this gametype. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Now to get rid of the overpowered weapons. +$InvBanList[CombatCon, "M4"] = 1; +$InvBanList[CombatCon, "RailGun"] = 1; +$InvBanList[CombatCon, "NapalmMortar"] = 1; +$InvBanList[CombatCon, "Flamer"] = 1; +$InvBanList[CombatCon, "FlamerAmmoPack"] = 1; +$InvBanList[CombatCon, "ELFBarrelPack"] = 1; +//------------------------------------------------------------------------------ +// Easy huh? Also as another reminder, the weapons above were removed by popular +// demand of the ACCM users and the Devs. The weapons are just too ovepowered +// for PVP style gameplay. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Lets begin the Gametype code... Shall we? +//------------------------------------------------------------------------------ +// NOTICE: +// Any notes below this point were not done by Blnukem. So it's advised you +// don't try editing anything below this point. Or you'll mess the Gametype up. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +function CombatConGame::initGameVars(%game) +{ + + if(isDemo()) + { + %game.SCORE_PER_SUICIDE = -10; + %game.SCORE_PER_TEAMKILL = -10; + %game.SCORE_PER_DEATH = 0; + + %game.SCORE_PER_KILL = 10; + %game.SCORE_PER_GEN_DESTROY = 8; + + %game.SCORE_PER_TURRET_KILL = 5; + %game.SCORE_PER_GEN_DEFEND = 5; + %game.SCORE_PER_GEN_REPAIR = 2; + + %game.RADIUS_GEN_DEFENSE = 20; //meters + + + %game.fadeTimeMS = 2000; + + %game.notifyMineDist = 7.5; + + + %game.stalemate = false; + %game.stalemateObjsVisible = false; + %game.stalemateTimeMS = 60000; + %game.stalemateFreqMS = 15000; + %game.stalemateDurationMS = 6000; + } + if( !isDemo() ) + { + %game.SCORE_PER_SUICIDE = -10; + %game.SCORE_PER_TEAMKILL = -10; + %game.SCORE_PER_DEATH = 0; + + %game.SCORE_PER_KILL = 10; + %game.SCORE_PER_HEADSHOT = 2; + + %game.SCORE_PER_TURRET_KILL = 10; + %game.SCORE_PER_TURRET_KILL_AUTO = 0; + %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_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_STRIKEFIGHTER = 5; + %game.SCORE_PER_DESTROY_HELICOPTER = 5; + %game.SCORE_PER_DESTROY_AWACS = 7; + %game.SCORE_PER_DESTROY_BOMBER = 7; + %game.SCORE_PER_DESTROY_GUNSHIP = 10; + %game.SCORE_PER_DESTROY_HEAVYHELICOPTER = 10; + %game.SCORE_PER_DESTROY_TRANSPORT = 10; + %game.SCORE_PER_DESTROY_WILDCAT = 2; + %game.SCORE_PER_DESTROY_TANK = 5; + %game.SCORE_PER_DESTROY_HEAVYTANK = 5; + %game.SCORE_PER_DESTROY_CGTANK = 5; + %game.SCORE_PER_DESTROY_FFTRANSPORT = 5; + %game.SCORE_PER_DESTROY_ARTILLERY = 10; + %game.SCORE_PER_DESTROY_MPB = 10; + %game.SCORE_PER_DESTROY_TRANSBOAT = 4; + %game.SCORE_PER_DESTROY_SUB = 10; + %game.SCORE_PER_DESTROY_BOAT = 15; + %game.SCORE_PER_PASSENGER = 3; + + %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_SOLAR = 4; + %game.SCORE_PER_REPAIR_SENTRY = 2; + %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; + } + +} + +package CombatConGame { + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Now Seriously... STOP EDITING!!! +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +function ShapeBaseData::onDestroyed(%data, %obj) +{ + %scorer = %obj.lastDamagedBy; + if(!isObject(%scorer)) + return; + + if( (%scorer.getType() & $TypeMasks::GameBaseObjectType) && + %scorer.getDataBlock().catagory $= "Vehicles" ) + { + // --------------------------------------------- + // z0dd - ZOD, 6/18/02. %name was never defined. + %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 return; //unmanned turret + } + + if(!%damagingTeam) + %damagingTeam = %scorer.team; + + if(%damagingTeam == %obj.team) + { + //error("team objects dont score"); + return; + } + + if(%obj.soiledByEnemyRepair) + { + //error(%obj SPC "was once repiared by an enemy. No destruction points."); + return; + } + + %objType = %obj.getDataBlock().getName(); + if(%objType $= "GeneratorLarge") + { + %score = game.awardScoreGenDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "SensorLargePulse" || %objType $= "SensorMediumPulse") + { + %score = game.awardScoreSensorDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "TurretBaseLarge") + { + %score = game.awardScoreTurretDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "StationInventory") + { + %score = game.awardScoreInvDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "StationVehicle") + { + %score = game.awardScoreVehicleStationDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "SolarPanel") + { + %score = game.awardScoreSolarDestroy(%scorer); + game.shareScore(%score, %score); + } + else if(%objType $= "SentryTurret") + { + %score = game.awardScoreSentryDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "DeployedMotionSensor" || %objType $= "DeployedPulseSensor") + { + %score = game.awardScoreDepSensorDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "TurretDeployedWallIndoor" || %objType $= "TurretDeployedFloorIndoor" || + %objType $= "TurretDeployedCeilingIndoor" || %objType $= "TurretDeployedOutdoor") + { + %score = game.awardScoreDepTurretDestroy(%scorer); + game.shareScore(%scorer, %score); + } + else if(%objType $= "DeployedStationInventory") + { + %score = game.awardScoreDepStationDestroy(%scorer); + game.shareScore(%scorer, %score); + } + +} + +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 CombatConGame::timeLimitReached(%game) +{ + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function CombatConGame::scoreLimitReached(%game) +{ + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function CombatConGame::gameOver(%game) +{ + //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 CombatConGame::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); + } + %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 CombatConGame::clientMissionDropReady(%game, %client) +{ + messageClient(%client, 'MsgClientReady',"", %game.class); + %game.resetScore(%client); + for(%i = 1; %i <= %game.numTeams; %i++) + { + $Teams[%i].score = 0; + messageClient(%client, 'MsgCombatConAddTeam', "", %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 CombatConGame::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 CombatConGame::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); + + if(!isDemo()) + { + %cl.offenseScore = %killPoints + + %cl.suicides * %game.SCORE_PER_SUICIDE + + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + + %cl.teamKills * %game.SCORE_PER_TEAMKILL + + %cl.scoreHeadshot * %game.SCORE_PER_HEADSHOT + + %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.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.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; + } + + if( isDemo() ) + { + %cl.offenseScore = %killPoints + + %cl.flagDefends * %game.SCORE_PER_FLAG_DEFEND + + %cl.suicides * %game.SCORE_PER_SUICIDE + //-1 + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1 + %cl.teamKills * %game.SCORE_PER_TEAMKILL + // -1 + %cl.flagCaps * %game.SCORE_PER_PLYR_FLAG_CAP + // 3 + %cl.genDestroys * %game.SCORE_PER_GEN_DESTROY; // 2 + + %cl.defenseScore = %cl.genDefends * %game.SCORE_PER_GEN_DEFEND + // 1 + %cl.carrierKills * %game.SCORE_PER_CARRIER_KILL + // 1 + %cl.escortAssists * %game.SCORE_PER_ESCORT_ASSIST + // 1 + %cl.turretKills * %game.SCORE_PER_TURRET_KILL + // 1 + %cl.flagReturns * %game.SCORE_PER_FLAG_RETURN + // 1 + %cl.genRepairs * %game.SCORE_PER_GEN_REPAIR; // 1 + } + + %cl.score = mFloor(%cl.offenseScore + %cl.defenseScore + (%cl.zkills * $zombie::killpoints)); + + %game.recalcTeamRanks(%cl); +} + +function CombatConGame::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 CombatConGame::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 CombatConGame::testCarrierKill(%game, %victimID, %killerID) +{ + %flag = %victimID.plyrDiedHoldingFlag; + return ((%flag !$= "") && (%flag.team == %killerID.team)); +} + +function CombatConGame::testEscortAssist(%game, %victimID, %killerID) +{ + return (%victimID.dmgdFlagCarrier); +} + +function CombatConGame::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 CombatConGame::resetDontScoreTimer(%game, %team) +{ + $dontScoreTimer[%team] = false; +} + +// Asset Destruction scoring +function CombatConGame::awardScoreGenDestroy(%game,%cl) +{ + %cl.genDestroys++; + if (%game.SCORE_PER_DESTROY_GEN != 0) + { + messageClient(%cl, 'msgGenDes', '\c0You received a %1 point bonus for destroying an enemy generator.', %game.SCORE_PER_DESTROY_GEN); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_GEN; +} + +function CombatConGame::awardScoreSensorDestroy(%game,%cl) +{ + %cl.sensorDestroys++; + if (%game.SCORE_PER_DESTROY_SENSOR != 0) + { + messageClient(%cl, 'msgSensorDes', '\c0You received a %1 point bonus for destroying an enemy sensor.', %game.SCORE_PER_DESTROY_SENSOR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SENSOR; +} + +function CombatConGame::awardScoreTurretDestroy(%game,%cl) +{ + %cl.turretDestroys++; + if (%game.SCORE_PER_DESTROY_TURRET != 0) + { + messageClient(%cl, 'msgTurretDes', '\c0You received a %1 point bonus for destroying an enemy turret.', %game.SCORE_PER_DESTROY_TURRET); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_TURRET; +} + +function CombatConGame::awardScoreInvDestroy(%game,%cl) +{ + %cl.IStationDestroys++; + if (%game.SCORE_PER_DESTROY_ISTATION != 0) + { + messageClient(%cl, 'msgInvDes', '\c0You received a %1 point bonus for destroying an enemy inventory station.', %game.SCORE_PER_DESTROY_ISTATION); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_ISTATION; +} + +function CombatConGame::awardScoreVehicleStationDestroy(%game,%cl) +{ + %cl.VStationDestroys++; + if (%game.SCORE_PER_DESTROY_VSTATION != 0) + { + messageClient(%cl, 'msgVSDes', '\c0You received a %1 point bonus for destroying an enemy vehicle station.', %game.SCORE_PER_DESTROY_VSTATION); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_VSTATION; +} + +function CombatConGame::awardScoreSolarDestroy(%game,%cl) +{ + %cl.SolarDestroys++; + if (%game.SCORE_PER_DESTROY_SOLAR != 0) + { + messageClient(%cl, 'msgSolarDes', '\c0You received a %1 point bonus for destroying an enemy solar panel.', %game.SCORE_PER_DESTROY_SOLAR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SOLAR; +} + +function CombatConGame::awardScoreSentryDestroy(%game,%cl) +{ + %cl.sentryDestroys++; + if (%game.SCORE_PER_DESTROY_SENTRY != 0) + { + messageClient(%cl, 'msgSentryDes', '\c0You received a %1 point bonus for destroying an enemy sentry turret.', %game.SCORE_PER_DESTROY_SENTRY); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_SENTRY; +} + +function CombatConGame::awardScoreDepSensorDestroy(%game,%cl) +{ + %cl.depSensorDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_SENSOR != 0) + { + messageClient(%cl, 'msgDepSensorDes', '\c0You received a %1 point bonus for destroying an enemy deployable.', %game.SCORE_PER_DESTROY_DEP_SENSOR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_SENSOR; +} + +function CombatConGame::awardScoreDepTurretDestroy(%game,%cl) +{ + %cl.depTurretDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_TUR != 0) + { + messageClient(%cl, 'msgDepTurDes', '\c0You received a %1 point bonus for destroying an enemy deployed turret.', %game.SCORE_PER_DESTROY_DEP_TUR); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_TUR; +} + +function CombatConGame::awardScoreDepStationDestroy(%game,%cl) +{ + %cl.depStationDestroys++; + if (%game.SCORE_PER_DESTROY_DEP_INV != 0) + { + messageClient(%cl, 'msgDepInvDes', '\c0You received a %1 point bonus for destroying an enemy deployed station.', %game.SCORE_PER_DESTROY_DEP_INV); + //messageTeamExcept(%cl, 'msgGenDes', '\c0Teammate %1 received a %2 point bonus for destroying an enemy generator.', %cl.name, %game.SCORE_PER_GEN_DESTROY); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_DESTROY_DEP_INV; +} + +function CombatConGame::awardScoreGenDefend(%game, %killerID) +{ + %killerID.genDefends++; + if (%game.SCORE_PER_GEN_DEFEND != 0) + { + messageClient(%killerID, 'msgGenDef', '\c0You received a %1 point bonus for defending a generator.', %game.SCORE_PER_GEN_DEFEND); + //messageTeamExcept(%killerID, 'msgGenDef', '\c0Teammate %1 received a %2 point bonus for defending a generator.', %killerID.name, %game.SCORE_PER_GEN_DEFEND); + } + %game.recalcScore(%cl); + return %game.SCORE_PER_GEN_DEFEND; +} + +function CombatConGame::awardScoreCarrierKill(%game, %killerID) +{ + %killerID.carrierKills++; + if (%game.SCORE_PER_CARRIER_KILL != 0) + { + messageClient(%killerID, 'msgCarKill', '\c0You received a %1 point bonus for stopping the enemy flag carrier!', %game.SCORE_PER_CARRIER_KILL); + //messageTeamExcept(%killerID, 'msgCarKill', '\c0Teammate %1 received a %2 point bonus for stopping the enemy flag carrier!', %killerID.name, %game.SCORE_PER_CARRIER_KILL); + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_CARRIER_KILL; +} + +function CombatConGame::awardScoreFlagDefend(%game, %killerID) +{ + %killerID.flagDefends++; + if (%game.SCORE_PER_FLAG_DEFEND != 0) + { + messageClient(%killerID, 'msgFlagDef', '\c0You received a %1 point bonus for defending your flag!', %game.SCORE_PER_FLAG_DEFEND); + //messageTeamExcept(%killerID, 'msgFlagDef', '\c0Teammate %1 received a %2 point bonus for defending your flag!', %killerID.name, %game.SCORE_PER_FLAG_DEFEND); + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_FLAG_DEFEND; +} + +function CombatConGame::awardScoreEscortAssist(%game, %killerID) +{ + %killerID.escortAssists++; + if (%game.SCORE_PER_ESCORT_ASSIST != 0) + { + messageClient(%killerID, 'msgEscAsst', '\c0You received a %1 point bonus for protecting the flag carrier!', %game.SCORE_PER_ESCORT_ASSIST); + //messageTeamExcept(%killerID, 'msgEscAsst', '\c0Teammate %1 received a %2 point bonus for protecting the flag carrier!', %killerID.name, %game.SCORE_PER_ESCORT_ASSIST); + } + %game.recalcScore(%killerID); + return %game.SCORE_PER_ESCORT_ASSIST; +} + +function CombatConGame::resetScore(%game, %client) +{ + %client.offenseScore = 0; + %client.kills = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.escortAssists = 0; + %client.teamKills = 0; + %client.flagCaps = 0; + %client.flagGrabs = 0; + %client.genDestroys = 0; + %client.sensorDestroys = 0; + %client.turretDestroys = 0; + %client.iStationDestroys = 0; + %client.vstationDestroys = 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.turretKills = 0; + %client.mannedTurretKills = 0; + %client.flagReturns = 0; + %client.genRepairs = 0; + %client.SensorRepairs =0; + %client.TurretRepairs =0; + %client.StationRepairs =0; + %client.VStationRepairs =0; + %client.solarRepairs =0; + %client.sentryRepairs =0; + %client.depInvRepairs =0; + %client.depTurretRepairs=0; + %client.returnPts = 0; + %client.score = 0; +} + +function CombatConGame::objectRepaired(%game, %obj, %objName) +{ + %item = %obj.getDataBlock().getName(); + switch$ (%item) + { + case generatorLarge : + %game.genOnRepaired(%obj, %objName); + case sensorMediumPulse : + %game.sensorOnRepaired(%obj, %objName); + case sensorLargePulse : + %game.sensorOnRepaired(%obj, %objName); + case stationInventory : + %game.stationOnRepaired(%obj, %objName); + case turretBaseLarge : + %game.turretOnRepaired(%obj, %objName); + case stationVehicle : + %game.vStationOnRepaired(%obj, %objName); + case solarPanel : + %game.solarPanelOnRepaired(%obj, %objName); + case sentryTurret : + %game.sentryTurretOnRepaired(%obj, %objName); + case TurretDeployedWallIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedFloorIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedCeilingIndoor: + %game.depTurretOnRepaired(%obj, %objName); + case TurretDeployedOutdoor: + %game.depTurretOnRepaired(%obj, %objName); + } + %obj.wasDisabled = false; +} + +function CombatConGame::genOnRepaired(%game, %obj, %objName) +{ + + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgGenRepaired', '\c0%1 repaired the %2 Generator!', %repairman.name, %obj.nameTag); + %game.awardScoreGenRepair(%obj.repairedBy); + } +} + +function CombatConGame::stationOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgStationRepaired', '\c0%1 repaired the %2 Inventory Station!', %repairman.name, %obj.nameTag); + %game.awardScoreStationRepair(%obj.repairedBy); + } +} + +function CombatConGame::sensorOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgSensorRepaired', '\c0%1 repaired the %2 Pulse Sensor!', %repairman.name, %obj.nameTag); + %game.awardScoreSensorRepair(%obj.repairedBy); + } +} + +function CombatConGame::turretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgTurretRepaired', '\c0%1 repaired the %2 Turret!', %repairman.name, %obj.nameTag); + %game.awardScoreTurretRepair(%obj.repairedBy); + } +} + +function CombatConGame::vStationOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgvstationRepaired', '\c0%1 repaired the Vehicle Station!', %repairman.name); + %game.awardScoreVStationRepair(%obj.repairedBy); + } +} + +function CombatConGame::solarPanelOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgsolarRepaired', '\c0%1 repaired the %2 Solar Panel!', %repairman.name, %obj.nameTag); + %game.awardScoreSolarRepair(%obj.repairedBy); + } +} + +function CombatConGame::sentryTurretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + teamRepairMessage(%repairman, 'msgsentryTurretRepaired', '\c0%1 repaired the %2 Sentry Turret!', %repairman.name, %obj.nameTag); + %game.awardScoreSentryRepair(%obj.repairedBy); + } +} + +function CombatConGame::depTurretOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %game.awardScoreDepTurretRepair(%obj.repairedBy); + } +} + +function CombatConGame::depInvOnRepaired(%game, %obj, %objName) +{ + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %game.awardScoreDepInvRepair(%obj.repairedBy); + } +} + +function CombatConGame::awardScoreGenRepair(%game, %cl) +{ + %cl.genRepairs++; + if (%game.SCORE_PER_REPAIR_GEN != 0) + { + messageClient(%cl, 'msgGenRep', '\c0You received a %1 point bonus for repairing a generator.', %game.SCORE_PER_REPAIR_GEN); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreStationRepair(%game, %cl) +{ + %cl.stationRepairs++; + if (%game.SCORE_PER_REPAIR_ISTATION != 0) + { + messageClient(%cl, 'msgIStationRep', '\c0You received a %1 point bonus for repairing a inventory station.', %game.SCORE_PER_REPAIR_ISTATION); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreSensorRepair(%game, %cl) +{ + %cl.sensorRepairs++; + if (%game.SCORE_PER_REPAIR_SENSOR != 0) + { + messageClient(%cl, 'msgSensorRep', '\c0You received a %1 point bonus for repairing a sensor.', %game.SCORE_PER_REPAIR_SENSOR); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreTurretRepair(%game, %cl) +{ + %cl.TurretRepairs++; + if (%game.SCORE_PER_REPAIR_TURRET != 0) + { + messageClient(%cl, 'msgTurretRep', '\c0You received a %1 point bonus for repairing a base turret.', %game.SCORE_PER_REPAIR_TURRET); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreVStationRepair(%game, %cl) +{ + %cl.VStationRepairs++; + if (%game.SCORE_PER_REPAIR_VSTATION != 0) + { + messageClient(%cl, 'msgVStationRep', '\c0You received a %1 point bonus for repairing a vehicle station.', %game.SCORE_PER_REPAIR_VSTATION); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreSolarRepair(%game, %cl) +{ + %cl.solarRepairs++; + if (%game.SCORE_PER_REPAIR_SOLAR != 0) + { + messageClient(%cl, 'msgsolarRep', '\c0You received a %1 point bonus for repairing a solar panel.', %game.SCORE_PER_REPAIR_SOLAR); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreSentryRepair(%game, %cl) +{ + %cl.sentryRepairs++; + if (%game.SCORE_PER_REPAIR_SENTRY != 0) + { + messageClient(%cl, 'msgSentryRep', '\c0You received a %1 point bonus for repairing a sentry turret.', %game.SCORE_PER_REPAIR_SENTRY); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreDepTurretRepair(%game, %cl) +{ + %cl.depTurretRepairs++; + if (%game.SCORE_PER_REPAIR_DEP_TUR != 0) + { + messageClient(%cl, 'msgDepTurretRep', '\c0You received a %1 point bonus for repairing a deployed turret.', %game.SCORE_PER_REPAIR_DEP_TUR); + } + %game.recalcScore(%cl); +} + +function CombatConGame::awardScoreDepInvRepair(%game, %cl) +{ + %cl.depInvRepairs++; + if (%game.SCORE_PER_REPAIR_DEP_INV != 0) + { + messageClient(%cl, 'msgDepInvRep', '\c0You received a %1 point bonus for repairing a deployed station.', %game.SCORE_PER_REPAIR_DEP_INV); + } + %game.recalcScore(%cl); +} + +function CombatConGame::enterMissionArea(%game, %playerData, %player) +{ + %player.client.outOfBounds = false; + messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area. ~wvoice/Announcer/ANN.ib.wav'); + cancel(%player.alertThread); +} + +function CombatConGame::leaveMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + + %player.client.outOfBounds = true; + messageClient(%player.client, 'LeaveMissionArea', '\c1You have left the mission area. Return or be killed. ~wvoice/Announcer/ANN.oob.wav'); + %player.alertThread = %game.schedule(10, "DMAlertPlayer", 3, %player); +} + +function CombatConGame::DMAlertPlayer(%game, %count, %player) +{ + if(%count > 1) + %player.alertThread = %game.schedule(1000, "DMAlertPlayer", %count - 1, %player); + else + %player.alertThread = %game.schedule(5000, "MissionAreaDamage", %player); +} + +function CombatConGame::MissionAreaDamage(%game, %player) +{ + if(%player.getState() !$= "Dead") { + %player.setDamageFlash(1.5); + %prevHurt = %player.getDamageLevel(); + %player.scriptkill($DamageType::OutOfBounds); + %player.alertThread = %game.schedule(1000, "MissionAreaDamage", %player); + } +} + +function CombatConGame::vehicleDestroyed(%game, %vehicle, %destroyer){ + %data = %vehicle.getDataBlock(); + %vehicleType = getTaggedString(%data.targetTypeTag); + if(%vehicleType !$= "MPB") + %vehicleType = strlwr(%vehicleType); + + %enemyTeam = ( %destroyer.team == 1 ) ? 2 : 1; + + %scorer = 0; + %multiplier = 3; + %shouldshare = 0; + + %passengers = 0; + for(%i = 0; %i < %data.numMountPoints; %i++) + if(%vehicle.getMountNodeObject(%i)) + %passengers++; + + if(%destroyer.client){ + %destroyer = %destroyer.client; + %scorer = %destroyer; + + if(%vehicle.lastDamageType == $DamageType::Mine) + %multiplier = 1; + } + else if(%destroyer.getClassName() $= "Turret"){ + if(%destroyer.getControllingClient()){ + %destroyer = %destroyer.getControllingClient(); + %scorer = %destroyer; + if(%destroyer.vehicleMounted){ + if(%destroyer.vehicleMounted.getMountNodeObject(0)){ + %driver = %destroyer.vehicleMounted.getMountNodeObject(0); + %sharer = %driver.client; + %shouldshare = 1; + } + %multiplier = 2; + } + } + else{ + %destroyerName = "A turret"; + %multiplier = 0; + } + } + else if(%destroyer.getDataBlock().catagory $= "Vehicles"){ + if(%destroyer.getMountNodeObject(0)){ + %destroyer = %destroyer.getMountNodeObject(0).client; + %scorer = %destroyer; + } + %multiplier = 2; + } + else + return; + + + if(%destroyerName $= "") + %destroyerName = %destroyer.name; + + if(%vehicle.team == %destroyer.team){ + %pref = (%vehicleType $= "Assault Tank") ? "an" : "a"; + messageAll( 'msgVehicleTeamDestroy', '\c0%1 TEAMKILLED %3 %2!', %destroyerName, %vehicleType, %pref); + } + else{ + messageTeamExcept(%destroyer, 'msgVehicleDestroy', '\c0%1 destroyed an enemy %2.', %destroyerName, %vehicleType); + messageTeam(%enemyTeam, 'msgVehicleDestroy', '\c0%1 destroyed your team\'s %2.', %destroyerName, %vehicleType); + + if(%scorer){ + %value = %game.awardScoreVehicleDestroyed(%scorer, %vehicleType, %multiplier, %passengers); + if(%shouldshare == 1) + %game.awardScoreVehicleDestroyed(%sharer, %vehicleType, (%multiplier / 2), %passengers); + %game.shareScore(%value); + } + } +} + +function CombatConGame::awardScoreVehicleDestroyed(%game, %client, %vehicleType, %mult, %passengers) +{ + if(isDemo()) + return 0; + + if(%vehicleType $= "Interceptor") + %base = %game.SCORE_PER_DESTROY_SHRIKE; + + else if(%vehicleType $= "Fighter") + %base = %game.SCORE_PER_DESTROY_STRIKEFIGHTER; + + else if(%vehicleType $= "Assault Chopper") + %base = %game.SCORE_PER_DESTROY_HELICOPTER; + + else if(%vehicleType $= "AWACS") + %base = %game.SCORE_PER_DESTROY_AWACS; + + else if(%vehicleType $= "Bomber") + %base = %game.SCORE_PER_DESTROY_BOMBER; + + else if(%vehicleType $= "Gunship") + %base = %game.SCORE_PER_DESTROY_GUNSHIP; + + else if(%vehicleType $= "Transport Chopper") + %base = %game.SCORE_PER_DESTROY_HEAVYHELICOPTER; + + else if(%vehicleType $= "Heavy Transport") + %base = %game.SCORE_PER_DESTROY_TRANSPORT; + + else if(%vehicleType $= "Grav Cycle") + %base = %game.SCORE_PER_DESTROY_WILDCAT; + + else if(%vehicleType $= "Light Tank") + %base = %game.SCORE_PER_DESTROY_TANK; + + else if(%vehicleType $= "Assault Tank") + %base = %game.SCORE_PER_DESTROY_HEAVYTANK; + + else if(%vehicleType $= "chaingun tank") + %base = %game.SCORE_PER_DESTROY_CGTANK; + + else if(%vehicleType $= "APC") + %base = %game.SCORE_PER_DESTROY_FFTRANSPORT; + + else if(%vehicleType $= "Heavy Artillery") + %base = %game.SCORE_PER_DESTROY_ARTILLERY; + + else if(%vehicleType $= "MPB") + %base = %game.SCORE_PER_DESTROY_MPB; + + else if(%vehicleType $= "Boat") + %base = %game.SCORE_PER_DESTROY_TRANSBOAT; + + else if(%vehicleType $= "Submarine") + %base = %game.SCORE_PER_DESTROY_SUB; + + else if(%vehicleType $= "GunBoat") + %base = %game.SCORE_PER_DESTROY_BOAT; + + + %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 CombatConGame::shareScore(%game, %client, %amount) +{ + if(isDemo()) + return 0; + + //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 CombatConGame::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 CombatConGame::testKill(%game, %victimID, %killerID) +{ + return ((%killerID !=0) && (%victimID.team != %killerID.team)); +} + +function CombatConGame::awardScoreKill(%game, %killerID) +{ + %killerID.kills++; + %game.recalcScore(%killerID); + return %game.SCORE_PER_KILL; +} +}; diff --git a/Scripts/ConstructionGame.cs b/Scripts/ConstructionGame.cs new file mode 100644 index 0000000..702a267 --- /dev/null +++ b/Scripts/ConstructionGame.cs @@ -0,0 +1,120 @@ +// DisplayName = Construction + +//--- GAME RULES BEGIN --- +// Freebuild - Build whatever you want. +// Type: /Help to access a list of useful commands. +// Scoring System Disabled. +//--- GAME RULES END --- + +// Now to get rid of most of the CCM Weapons. +$InvBanList[Construction, "RailGun"] = 1; +$InvBanList[Construction, "Bazooka"] = 1; +$InvBanList[Construction, "snipergun"] = 1; +$InvBanList[Construction, "flamer"] = 1; +$InvBanList[Construction, "Shotgun"] = 1; +$InvBanList[Construction, "RShotgun"] = 1; +$InvBanList[Construction, "HRPChaingun"] = 1; +$InvBanList[Construction, "RPChaingun"] = 1; +$InvBanList[Construction, "BoosterPack"] = 1; +$InvBanList[Construction, "NapalmMortar"] = 1; +$InvBanList[Construction, "M4"] = 1; +$InvBanList[Construction, "flamerammopack"] = 1; +$InvBanList[Construction, "ELFBarrelPack"] = 1; +$InvBanList[Construction, "artillerybarrelpack"] = 1; +$InvBanList[Construction, "artilleryWeaponPack"] = 1; +$InvBanList[Construction, "InventoryDeployable"] = 1; +$InvBanList[Construction, "SpySatelliteDeployable"] = 1; + +function ConstructionGame::AIInit(%game) { + //call the default AIInit() function + AIInit(); +} + +function ConstructionGame::allowsProtectedStatics(%game) { + return true; +} + +function ConstructionGame::clientMissionDropReady(%game, %client) { + messageClient(%client, 'MsgClientReady',"", %game.class); + messageClient(%client, 'MsgMissionDropInfo', '\c0You are in mission %1 (%2).', $MissionDisplayName, $MissionTypeDisplayName, $ServerName ); + DefaultGame::clientMissionDropReady(%game, %client); +} + +function ConstructionGame::onAIRespawn(%game, %client) +{ + //add the default task + if (! %client.defaultTasksAdded) + { + %client.defaultTasksAdded = true; + %client.addTask(AIPickupItemTask); + %client.addTask(AIUseInventoryTask); + %client.addTask(AITauntCorpseTask); + %client.addTask(AIEngageTurretTask); + %client.addTask(AIDetectMineTask); + %client.addTask(AIBountyPatrolTask); + %client.bountyTask = %client.addTask(AIBountyEngageTask); + } + + //set the inv flag + %client.spawnUseInv = true; +} + +function ConstructionGame::updateKillScores(%game, %clVictim, %clKiller, %damageType, %implement) { + 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); +} + +function ConstructionGame::timeLimitReached(%game) { + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function ConstructionGame::scoreLimitReached(%game) { + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function ConstructionGame::gameOver(%game) { + //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 ConstructionGame::vehicleDestroyed(%game, %vehicle, %destroyer) { +} + diff --git a/Scripts/Data/MessageData.cs b/Scripts/Data/MessageData.cs new file mode 100644 index 0000000..4916026 --- /dev/null +++ b/Scripts/Data/MessageData.cs @@ -0,0 +1,99 @@ +//============================================================================== +// ACCM Message Database - By Blnukem. +//------------------------------------------------------------------------------ +// This is the message database for Sentinels and other miscellaneous functions +// The following will be addressed in each message cluster on what can or cannot +// be used in those messages. +// +// This was made for the pleasure of ACCM users to customize Sentinel responses +// and a few other messages. +//------------------------------------------------------------------------------ +// %1 = Sentinel's Name. +// %2 = Sender's Name. +// %3 = Sender/Target's Gender. (He/She) +// %4 = Sender/Target's Present Tense Gender. (Him/Her) +// %5 = Sender/Target's Possesive Gender. (His/Her) +// %6 = Target's Name. (Only if there is a target defined) +//============================================================================== +// No Target Client for this cluster exists: + +$SentinelDenyFollowCount = 5; +$SentinelDenyFollow[0] = '\c2%1 ::\c0 %2, I am already occupied with a task, I cannot follow you.'; +$SentinelDenyFollow[1] = '\c2%1 ::\c0 I\'m already engaged in a task, %2.'; +$SentinelDenyFollow[2] = '\c2%1 ::\c0 I am occupied at the moment, %2. Sorry, but I cannot follow you.'; +$SentinelDenyFollow[3] = '\c2%1 ::\c0 %2, I\'m busy doing something. I cannot follow you.'; +$SentinelDenyFollow[4] = '\c2%1 ::\c0 I\'m already doing something, %2.'; + +//------------------------------------------------------------------------------ +// Sender and Target Clients for this cluster exist: + +$SentinelDenyAttackCount = 5; +$SentinelDenyAttack[0] = '\c2%1 ::\c0 I\'m already doing something, %2'; +$SentinelDenyAttack[1] = '\c2%1 ::\c0 %2, I\'m busy doing something. I cannot engage %6.'; +$SentinelDenyAttack[2] = '\c2%1 ::\c0 I am occupied at the moment, %2. Sorry, but I cannot do what you ask.'; +$SentinelDenyAttack[3] = '\c2%1 ::\c0 %2, I am already occupied with a task, I cannot attack %6.'; +$SentinelDenyAttack[4] = '\c2%1 ::\c0 I\'m already engaged in a task, %2.'; + +//------------------------------------------------------------------------------ +// Sender and Target Clients for this cluster exist: + +$SentinelAcceptAttackCount = 4; +$SentinelAcceptAttack[0] = '\c2%1 ::\c0 I\'m now engaging %6.'; +$SentinelAcceptAttack[1] = '\c2%1 ::\c0 Command confirmed, I will now hunt down %6.'; +$SentinelAcceptAttack[2] = '\c2%1 ::\c0 Attack protocol engaged, I will now hunt down %6.'; +$SentinelAcceptAttack[3] = '\c2%1 ::\c0 Command confirmed %2, I will now attack %6.'; + +//------------------------------------------------------------------------------ +// Sender and Target Clients for this cluster exist: + +$SentinelAttackAlreadyDeadTargetCount = 1; +$SentinelAttackAlreadyDeadTarget[0] = '\c2%1 ::\c0 %6 is already dead.'; + +//------------------------------------------------------------------------------ +// Sender and Target Clients for this cluster exist: + +$SentinelTargetTooFarCount = 1; +$SentinelTargetTooFar[0] = '\c2%1 ::\c0 Command declined. %6 is out of range.'; + +//------------------------------------------------------------------------------ +// No Target Client for this cluster exists: + +$SentinelAttackSenderCount = 1; +$SentinelAttackSender[0] = '\c2%1 ::\c0 Command declined %2, I cannot kill the person giving me an order.'; + +//------------------------------------------------------------------------------ +// No Target Client for this cluster exists: + +$SentinelAttackSenderNotAdminCount = 1; +$SentinelAttackSenderNotAdmin[0] = '\c2%1 ::\c0 %2, you must be an admin to set an attack order.'; + +//------------------------------------------------------------------------------ +// No Sender Client for this cluster exists: + +$SentinelAnnoyedCount = 3; +$SentinelAnnoyed[0] = '\c2%1 ::\c0 %2, now you will learn not to fly around sentinels.'; +$SentinelAnnoyed[1] = '\c2%1 ::\c0 Target Designated. %2, you were warned not to fly around Sentinels.'; +$SentinelAnnoyed[2] = '\c2%1 ::\c0 %2, you need to learn to not fly around Sentinels.'; + +//------------------------------------------------------------------------------ +// No Sender Client for this cluster exists: + +$SentinelWarnCount = 4; +$SentinelWarn[0] = '\c2%1 ::\c0 Please stop flying around me, %2.'; +$SentinelWarn[1] = '\c2%1 ::\c0 Quit flying around me %2.'; +$SentinelWarn[2] = '\c2%1 ::\c0 Do not fly around me, %2.'; +$SentinelWarn[3] = '\c2%1 ::\c0 %2, if you do not stop flying around me, you will be killed.'; + +//------------------------------------------------------------------------------ + +$ACCMTipCount = 10; +$ACCMTip[0] = "Random Tip:\nRemember, type /help for a list of useful commands."; +$ACCMTip[1] = "Sentinel Network:\nSentinels are your allies and will not attack you, unless you provoke them to do so."; +$ACCMTip[2] = "Central Command Intel Report:\nYou can launch drop pods from Golem heavy transports by pressing your mine key while you're piloting."; +$ACCMTip[3] = "Random Tip:\nThe easiest way to kill a Zombie or anyone for that matter, is to aim for the head."; +$ACCMTip[4] = "Central Command Intel Report:\nThe medic pack has an antidote option for infected players, useable by pressing the right mouse button."; +$ACCMTip[5] = "Central Command Intel Report:\nThe Flame turret barrel is the most effective turret barrel to kill Zombies, followed by the Chaingun turret barrel. The least effective is the Flak barrel."; +$ACCMTip[6] = "Sentinel Network:\nSentinels are intended for Zombie containment and control, and will not attack humans unless provoked to do so."; +$ACCMTip[7] = "Central Command Intel Report:\nThe Purge Field Generator is made to defend an area against Zombies, it's particle field will electrocute any Zombies within the field, but requires heavy maintenece."; +$ACCMTip[8] = "Random Tip:\nUse /CheckStats to check out your current rank and how many points you need for your next rank."; +$ACCMTip[9] = "Random Tip:\nThe Vehicle Repair Pad, Deployable Sentry Turret and the Purge Field Generator require power to operate, be sure to give them a power source when using them!"; diff --git a/Scripts/Data/PulseData.cs b/Scripts/Data/PulseData.cs new file mode 100644 index 0000000..9415ed4 --- /dev/null +++ b/Scripts/Data/PulseData.cs @@ -0,0 +1,764 @@ +datablock StaticShapeData(SelectionPad) : StaticShapeDamageProfile +{ +className = "crate"; +shapeFile = "station_inv_mpb.dts";//"vehicle_air_hapc.dts"; +}; + +function SelectionPad::onDestroyed(%this, %obj, %prevState) +{ +%obj.remTrigger(); +Parent::onDestroyed(%this, %obj, %prevState); +%obj.schedule(500, "delete"); +} + +//Litle console spam remover. +function SelectionPad::hasDismountOverrides(%data, %obj) +{ +return false; +} + +function SelectionPad::playerDismounted(%data, %obj, %player) +{ +%obj.setThreadDir(1,false); +} + +function shockwave(%pos,%nrm,%dat) +{ +if (%dat $= "") +%dat = BaseProjectile; + +%p1 = new TracerProjectile() +{ +dataBlock = %dat; +initialDirection = %nrm; +initialPosition = %pos; +}; +} + +//loading system +if ($mpm_AE != 1) +{ +$mpm_load[$mpm_loads] = Mpm_AREP_Load; +$mpm_loads++; +$mpm_load[$mpm_loads] = Mpm_ACLK_Load; +$mpm_loads++; +$mpm_load[$mpm_loads] = Mpm_ADIS_Load; +$mpm_loads++; +$mpm_load[$mpm_loads] = Mpm_AESP_Load; +$mpm_loads++; +$mpm_load[$mpm_loads] = Mpm_AMOR_Load; +$mpm_loads++; +$mpm_AE = 1; +} + + +//Loads + +datablock ItemData(Mpm_AREP_Load):Mpm_Base_Load +{ +slot = 0; +cost = 25; +name = "[AID] Repair Pulse"; +friendly = 1; +missile = Mpm_B_MIS4; + +}; + +datablock ItemData(Mpm_ACLK_Load):Mpm_Base_Load +{ +slot = 0; +cost = 30; +name = "[AID] Cloak Pulse"; +friendly = 1; +missile = Mpm_B_MIS4; + +}; + +datablock ItemData(Mpm_ADIS_Load):Mpm_Base_Load +{ +slot = 0; +cost = 25; +name = "[AID] Dissasemble Pulse"; +friendly = 0; +missile = Mpm_B_MIS4; + +}; + +datablock ItemData(Mpm_AESP_Load):Mpm_Base_Load +{ +slot = 0; +cost = 50; +name = "[AID] Electo Static Pulse"; +friendly = 0; +missile = Mpm_B_MIS4; + +}; + +datablock ItemData(Mpm_AMOR_Load):Mpm_Base_Load +{ +slot = 0; +cost = 50; +name = "[AID] Morph Pulse"; +friendly = 0; +missile = Mpm_B_MIS4; + +}; + +function Mpm_AREP_Load::Explode(%data,%p,%pos) +{ +if (IsObject(%p)) +{ +Aidpulse(%pos,%p.owner,0); +} +} + +function Mpm_ACLK_Load::Explode(%data,%p,%pos) +{ +if (IsObject(%p)) +{ +Aidpulse(%pos,%p.owner,1); +} +} + +function Mpm_ADIS_Load::Explode(%data,%p,%pos) +{ +if (IsObject(%p)) +{ +Aidpulse(%pos,%p.owner,2); +} +} + +function Mpm_AESP_Load::Explode(%data,%p,%pos) +{ +if (IsObject(%p)) +{ +Aidpulse(%pos,%p.owner,3); +} +} + +function Mpm_AMOR_Load::Explode(%data,%p,%pos) +{ +if (IsObject(%p)) +{ +Aidpulse(%pos,%p.owner,4); +} +} + +//Shockwaves + +datablock ShockwaveData(RepairWave) { +className = "ShockwaveData"; +scale = "1 1 1"; +delayMS = "0"; +delayVariance = "0"; +lifetimeMS = "10000"; +lifetimeVariance = "0"; +width = "1"; +numSegments = "60"; +numVertSegments = "30"; +velocity = "10"; +height = "20"; +verticalCurve = "5"; +acceleration = "1"; +times[0] = "0"; +times[1] = "0.25"; +times[2] = "0.9"; +times[3] = "1"; +colors[0] = "1.000000 0.200000 0.200000 1.000000"; //1.0 0.9 0.9 +colors[1] = "1.000000 0.200000 0.200000 1.000000"; //0.6 0.6 0.6 +colors[2] = "1.000000 0.200000 0.200000 1.000000"; //0.6 0.6 0.6 +colors[3] = "1.000000 0.200000 0.200000 0.000000"; +texture[0] = "special/redbump2"; +texture[1] = "special/shockwave4"; //gradient"; +texWrap = "1"; +is2D = "0"; +mapToTerrain = "0"; +orientToNormal = "1"; +renderBottom = "1"; +renderSquare = "0"; +}; + +datablock ShockwaveData(CloakWave):RepairWave +{ +colors[0] = "1 1 1 1"; +colors[1] = "1 1 1 1"; +colors[2] = "1 1 1 1"; +colors[3] = "1 1 1 0"; +texture[0] = "special/cloakTexture"; +texture[1] = "special/shockwave4"; +texWrap = "5"; +}; + +datablock ShockwaveData(DisWave):RepairWave +{ +colors[0] = "0.2 0.2 1 1"; +colors[1] = "0.2 0.2 1 1"; +colors[2] = "0.2 0.2 1 1"; +colors[3] = "0.2 0.2 1 0"; +texture[0] = "special/shockwave5"; +texture[1] = "special/shockwave4"; +texWrap = "1"; +}; + +datablock ShockwaveData(EspWave):RepairWave +{ +colors[0] = "0.8 0.2 1 1"; +colors[1] = "0.8 0.2 1 1"; +colors[2] = "0.8 0.2 1 1"; +colors[3] = "0.8 0.2 1 0"; +texture[0] = "special/shockwave5"; +texture[1] = "special/shockwave4"; +texWrap = "1"; +}; + +datablock ShockwaveData(MORWave):RepairWave +{ +colors[0] = "0.8 1 0.2 1"; +colors[1] = "0.8 1 0.2 1"; +colors[2] = "0.8 1 0.2 1"; +colors[3] = "0.8 1 0.2 0"; +texture[0] = "special/shockwave5"; +texture[1] = "special/shockwave4"; +texWrap = "1"; +}; + +//Emitters + +datablock ParticleData(RepairWave_P) +{ +dragCoeffiecient = 0.0; +gravityCoefficient = 0.0; +inheritedVelFactor = 0.0; + +lifetimeMS = 10000; +lifetimeVarianceMS = 0; +constantAcceleration = 0.1; + +spinRandomMin = -30.0; +spinRandomMax = 30.0; +windcoefficient = 0; +textureName = "special/redflare"; + +colors[0] = "1 0.2 0.2 1"; +colors[1] = "1 0.2 0.2 1"; +colors[2] = "1 0.2 0.2 1"; +colors[3] = "1 0.2 0.2 0"; + +sizes[0] = 1; +sizes[1] = 2; +sizes[2] = 4; +sizes[3] = 5; + +times[0] = "0"; +times[1] = "0.25"; +times[2] = "0.9"; +times[3] = "1"; + +}; + +datablock ParticleData(CloakWave_P):RepairWave_P +{ +textureName = "flarebase"; +constantAcceleration = 0.1; +colors[0] = "1 1 1 1"; +colors[1] = "1 1 1 1"; +colors[2] = "1 1 1 1"; +colors[3] = "1 1 1 0"; +}; + +datablock ParticleData(DISWave_P):RepairWave_P +{ +textureName = "flarebase"; +constantAcceleration = 0.1; +colors[0] = "0.2 0.2 1 1"; +colors[1] = "0.2 0.2 1 1"; +colors[2] = "0.2 0.2 1 1"; +colors[3] = "0.2 0.2 1 0"; +}; + +datablock ParticleData(ESPWave_P):RepairWave_P +{ +textureName = "flarebase"; +constantAcceleration = 0.1; +colors[0] = "0.8 0.2 1 1"; +colors[1] = "0.8 0.2 1 1"; +colors[2] = "0.8 0.2 1 1"; +colors[3] = "0.8 0.2 1 0"; +}; + +datablock ParticleData(MORWave_P):RepairWave_P +{ +textureName = "flarebase"; +constantAcceleration = 0.1; +colors[0] = "0.8 1 0.2 1"; +colors[1] = "0.8 1 0.2 1"; +colors[2] = "0.8 1 0.2 1"; +colors[3] = "0.8 1 0.2 0"; +}; + +datablock ParticleEmitterData(RepairWave_E) +{ +lifetimeMS = 1000; +ejectionPeriodMS = 1; +periodVarianceMS = 0; + +ejectionVelocity = 10; +velocityVariance = 0; +ejectionoffset = 5; +thetaMin = 0.0; +thetaMax = 180.0; + +phiReferenceVel = "0"; +phiVariance = "360"; +orientParticles = false; +orientOnVelocity = false; + +particles = "RepairWave_P"; +}; + +datablock ParticleEmitterData(CLOAKWave_E):RepairWave_E +{ +lifetimeMS = 1000; +ejectionPeriodMS = 1; +ejectionVelocity = 10; +ejectionoffset = 5; +velocityVariance = 0; +particles = "CloakWave_P"; +thetaMin = 0.0; +thetaMax = 180.0; + +phiReferenceVel = "0"; +phiVariance = "360"; +}; + +datablock ParticleEmitterData(DISWave_E):RepairWave_E +{ +lifetimeMS = 1000; +ejectionPeriodMS = 1; +ejectionVelocity = 10; +ejectionoffset = 5; +velocityVariance = 0; +particles = "DISWave_P"; +thetaMin = 0.0; +thetaMax = 180.0; + +phiReferenceVel = "0"; +phiVariance = "360"; +}; + + +datablock ParticleEmitterData(ESPWave_E):RepairWave_E +{ +lifetimeMS = 1000; +ejectionPeriodMS = 1; +ejectionVelocity = 10; +velocityVariance = 0; +ejectionoffset = 5; +particles = "ESPWave_P"; +thetaMin = 0.0; +thetaMax = 180.0; + +phiReferenceVel = "0"; +phiVariance = "360"; +}; + + +datablock ParticleEmitterData(MORWave_E):RepairWave_E +{ +lifetimeMS = 1000; +ejectionPeriodMS = 1; +ejectionVelocity = 10; +velocityVariance = 0; +ejectionoffset = 5; +particles = "MORWave_P"; +thetaMin = 0.0; +thetaMax = 180.0; + +phiReferenceVel = "0"; +phiVariance = "360"; +}; + + +datablock ExplosionData(RepairPulseExplosion):BaseExplosion //From blast.cs +{ +emitter[0] = "RepairWave_E"; +Shockwave = "RepairWave"; +}; + + +datablock ExplosionData(CloakPulseExplosion):BaseExplosion //From blast.cs +{ +emitter[0] = "CloakWave_E"; +Shockwave = "CloakWave"; +}; + +datablock ExplosionData(DisPulseExplosion):BaseExplosion //From blast.cs +{ +emitter[0] = "DISWave_E"; +Shockwave = "DisWave"; +}; + +datablock ExplosionData(ESPPulseExplosion):BaseExplosion //From blast.cs +{ +emitter[0] = "ESPWave_E"; +Shockwave = "ESPWave"; +}; + + +datablock ExplosionData(MORPulseExplosion):BaseExplosion //From blast.cs +{ +emitter[0] = "MORWave_E"; +Shockwave = "MORWave"; +}; + +datablock TracerProjectileData(RepairPulseProjectile):BaseProjectile +{ +Explosion = "RepairPulseExplosion"; +}; + +datablock TracerProjectileData(CloakPulseProjectile):BaseProjectile +{ +Explosion = "CloakPulseExplosion"; +}; + +datablock TracerProjectileData(DisPulseProjectile):BaseProjectile +{ +Explosion = "DisPulseExplosion"; +}; + +datablock TracerProjectileData(ESPPulseProjectile):BaseProjectile +{ +Explosion = "ESPPulseExplosion"; +}; + + +datablock TracerProjectileData(MORPulseProjectile):BaseProjectile +{ +Explosion = "MORPulseExplosion"; +}; + +function Aidpulse(%pos,%owner,%type,%nrm) +{ +schedule(200,0,"Serverplay3D",FlashGrenadeExplosionSound,%pos); +%types = "RepairPulseProjectile CloakPulseProjectile DisPulseProjectile ESPPulseProjectile MORPulseProjectile"; +%proj = GetWord(%types,%type); +%nrm = !%nrm ? "0 0 -1" : %nrm; +shockwave(%pos,%nrm,%proj); +%waveblock = %proj.explosion.shockwave; +%accel = %waveblock.acceleration; //accel of wave +%speed = %waveblock.velocity; //speed of wave +%mtime = %waveblock.lifetimeMS/1000; //time the wave lasts +%lastdist = 0; +%checks = %mtime/2; //2 per second +AidPulseWaved(%pos,%mtime,%speed,%accel,%waveblock,%owner); +Cancel(%owner.resetmorphsch); +%owner.resetmorphsch = Schedule(%mtime*1000+5000,%owner,"resetmorphsize",%owner); +for (%i = 1;%i<20;%i++) +{ +%time = %i/2; +%dist = %time*%speed+1/2 * mPow(%time,2)*%accel+10; +schedule(%time*1000,0,"AidpulseWaver",%pos,%dist,%lastdist-2,%waveblock,%owner); +%lastdist = %dist; +} +} + + + +function solveadist(%accel,%speed,%dist) +{ +%awn = mSolveQuadratic(%accel/2,%speed,-1*%dist); +if (getWord(%awn,0) <0 || getWord(%awn,1) > 0) +return 0; +else +return -1*getWord(%awn,1); +} + +function AidPulseWaved(%pos,%time,%speed,%accel,%wave,%owner) +{ +%area = %time*%speed+1/2 * mPow(%time,2)*%accel+10; + +InitContainerRadiusSearch(%pos, %area, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType ); + + +while ((%targetObject = containerSearchNext()) != 0) +{ +%dist = containerSearchCurrRadDamageDist(); +%ttime = solveadist(%accel,%speed,%dist-10); +if (%ttime != 0 || %ttime < %time) +%wave.schedule(%ttime*1000,"AidEffect",%targetobject,%owner,%pos); +} +} + +function AidpulseWaver(%pos,%area,%lastar,%wave,%owner) +{ + +InitContainerRadiusSearch(%pos, %area, $TypeMasks::VehicleObjectType | $TypeMasks::PlayerObjectType |$TypeMasks::ItemObjectType | $TypeMasks::CorpseObjectType ); + + +while ((%targetObject = containerSearchNext()) != 0) +{ +%dist = containerSearchCurrRadDamageDist(); + +if (%dist > %area || %dist < %lastar) +continue; + +%wave.AidEffect(%targetobject,%owner,%pos); +} + +} + +function RepairWave::AidEffect(%block,%obj,%owner,%pos) +{ +if (!isObject(%obj) || %obj.isforceField()) +return ""; +%obj.playShieldEffect("1 1 1"); +%obj.setDamageLevel(0); +} + +function CloakWave::AidEffect(%block,%obj,%owner,%pos) +{ +if (!isObject(%obj) || %obj.isforceField()) +return ""; +Cancel(%obj.uncloaksch); +%obj.setCloaked(True); +%obj.uncloaksch = %obj.schedule(60000,"setCloaked",False); +} + +function DisWave::AidEffect(%block,%obj,%owner,%Pos) +{ + +if (%obj.isRemoved || !isObject(%owner) || !isObject(%obj)) +return; +%dataBlockName = %obj.getDataBlock().getName(); +if (%dataBlockName $= "StationInventory" || +%dataBlockName $= "GeneratorLarge" || +%dataBlockName $= "SolarPanel" || +%dataBlockName $= "SensorMediumPulse" || +%dataBlockName $= "SensorLargePulse") +if (%obj.deployed != true) +return; +if ($reverseDeployItem[%obj.getDataBlock().getName()] $= "") +return; +if (%obj.team != %owner.team && +!(%owner.isAdmin || %owner.isSuperAdmin)) +return; +if ($Host::OnlyOwnerCascade == 1 && +%obj.getOwner() != %owner && +!(%owner.isAdmin || %owner.isSuperAdmin)) +return; + +%obj.getDataBlock().disassemble(%owner, %obj); // Run Item Specific code. + +} + +function ESPWave::AidEffect(%block,%obj,%owner,%pos) +{ +if (!isObject(%obj)) +return ""; +%hadsch = %obj.uncloacksch ? 1 : 0; +Cancel(%obj.uncloaksch); +if (!%obj.isforcefield()) +%obj.setCloaked(FALSE); +Cancel(%obj.unemplockschd); +if (%obj.isVehicle()) +vehemplock(%obj); +else if (%obj.isPlayer()) +PlayerEmpLock(%obj); +else if (%obj.getDatablock().maxEnergy !$= "") +{ +if (%obj.getDataBlock().className $= "Generator" && %obj.lastState) +{ +if (!%hadsch) +toggleGenerator(%obj,0); +%obj.unemplockschd = schedule(30000,%obj," toggleGenerator",%obj,0); +} +%obj.setEnergyLevel(0); +} +} + +function MORWave::AidEffect(%block,%obj,%owner,%pos) +{ +if (%obj.isRemoved || +!isObject(%owner) || +!isObject(%obj) || +%obj.isPLayer() || +%obj.isVehicle()) +return; +if (!(deployables.isMember(%obj))) +return; +%dataBlockName = %obj.getDataBlock().getName(); +if (%dataBlockName $= "StationInventory" || +%dataBlockName $= "GeneratorLarge" || +%dataBlockName $= "SolarPanel" || +%dataBlockName $= "SensorMediumPulse" || +%dataBlockName $= "SensorLargePulse") +if (%obj.deployed != true) +return; + +if (%obj.team != %owner.team && +!(%owner.isAdmin || %owner.isSuperAdmin)) +return; +if ($Host::OnlyOwnerCascade == 1 && +%obj.getOwner() != %owner && +!(%owner.isAdmin || %owner.isSuperAdmin)) +return; + +Cancel(%obj.unmorphsch); +%obj.unmorphsch = Schedule(30000,%obj,"ResetMorphObject",%obj); +if (%obj.morphed) +return; + +%obj.oldcenter = %obj.getEdge("0 0 0"); +%obj.oldrealsize = %obj.getrealSize(); +%obj.morphed = 1; + +%size = %owner.morphpulsesize ? %owner.morphpulsesize : 0.1; +%scale = VectorScale("1 1 1",%size); +%offset = VectorMultiply(VectorSub(%obj.getEdge("0 0 0"),%pos),%scale); +%obj.setRealSize(VectorMultiply(%obj.getRealSize(),%scale)); +%obj.setEdge(VectorAdd(%pos,%offset),"0 0 0"); +if (!%obj.isforcefield()) +%obj.startfade(500,0,0); +if (isObject(%obj.pzone)) +{ +%obj.pzone.setScale(%Obj.getScale); +%obj.pzone.setTransform(%obj.getTransform()); +} +if (isObject(%obj.emitter)) +{ +%obj.emitter.oldsize = %obj.getScale(); +%obj.emitter.setScale(VectorScale(%obj.emitter.getScale(),%size)); +%obj.emitter.setTransform(%obj.getTransform()); +} +if (isObject(%obj.trigger)) +{ +%obj.trigger.oldsize = %obj.getScale(); +%obj.trigger.setScale(VectorScale(%obj.trigger.getScale(),%size)); +%obj.trigger.setTransform(%obj.getTransform()); +} +} + +//ESP functions + +function vehemplock(%vehicle) +{ +Cancel(%vehicle.unemplockschd); +Cancel(%vehicle.lockff.unemplockschd); +%vehicle.setFrozenState(true); +%vehicle.zapObject(); +forceFieldLock(%vehicle); +%vehicle.unemplockschd = Schedule(30000,%vehicle,"vehUnEmpLock",%vehicle); +%vehicle.lockff.unemplockschd = %vehicle.lockff.Schedule(30000,"delete"); +} + +function vehUnEmpLock(%vehicle) +{ +Cancel(%vehicle.unemplockschd); +Cancel(%vehicle.lockff.unemplockschd); +%vehicle.lockff.delete(); +%vehicle.setFrozenState(false); +} + +function PlayerEmpLock(%player) +{ +Cancel(%player.unemplockschd); +Cancel(%player.lockff.unemplockschd); +Cancel(%player.lock.unemplockschd); +if (!%player.isemped) +{ +%lock = new StaticShape() +{ +dataBlock = SelectionPad; +scale = "0.01 0.01 0.01"; +}; +%lock.startFade(0,0,1); +%vec = VectorNormalize(%player.getVelocity()); +%vec = (VectorLen(%vec)>0.1) ? %vec : "0 0 1"; +%center = %player.getEdge("0 0 0"); +%rot = fullrot(%vec,VectorCross(%player.getEyeVector(),%vec)); +%player.setTransform(%player.getEdge("0 0 -1") SPC %rot); +%player.setEdge(%center,"0 0 0"); +%lock.setTransform(getWords(%player.getTransform(),0,2) SPC %rot); +%lock.mountObject(%player,0); +%lock.player = %player; +%player.emplock = %lock; +%player.isemped = 1; +%player.zapObject(); +forceFieldLock(%player); +} +%player.unemplockschd = Schedule(30000,%player,"PlayerUnEmpLock",%player); +if (isObject(%player.lock)) +%player.lock.unemplockschd = %player.lock.Schedule(30000,"delete"); +if (isObject(%player.lockff)) +%player.lockff.unemplockschd = %player.lockff.Schedule(30000,"delete"); +} + +function PlayerUnEmpLock(%player) +{ +%player.isemped = 0; +Cancel(%player.unemplockschd); +Cancel(%player.lockff.unemplockschd); +Cancel(%player.lock.unemplockschd); +%player.lockff.delete(); +%player.unMount(); +if (isObject(%player.emplock)) +%player.emplock.delete(); +} + +function forceFieldLock(%obj) +{ +if (!isObject(%obj.lockff)) +{ +%ff = new ForceFieldBare() { +dataBlock = DeployedForceField5; +scale = "1 1 1"; +}; +%ff.noSlow = true; +// Eolk - needed a fix here to prevent console spam... +if(isObject(%ff.pzone)) +%ff.pzone.delete(); + +%ff.pzone = ""; +%obj.lockff = %ff; +%ff.obj = %obj; +%ff.setScale(%obj.getRealSize()); +%ff.setTransform(%obj.getEdge("-1 -1 -1") SPC %Obj.getRotation()); +} +} + +//Morph functions + +function ResetMorphObject(%obj) +{ +Cancel(%obj.unmorphsch); +if (!%obj.isforcefield()) +%obj.startfade(500,0,0); +%obj.setRealSize(%obj.oldrealsize); +%obj.setEdge(%obj.oldcenter,"0 0 0"); +if (isObject(%obj.pzone)) +{ +%obj.pzone.setScale(%Obj.getScale); +%obj.pzone.setTransform(%obj.getTransform()); +} +if (isObject(%obj.emitter)) +{ +%obj.emitter.setTransform(%obj.getTransform()); +%obj.emitter.setScale(%obj.trigger.oldsize); +} +if (isObject(%obj.trigger)) +{ +%obj.trigger.setTransform(%obj.getTransform()); +%obj.trigger.setScale(%obj.trigger.oldsize); +} +%obj.oldrealsize = ""; +%obj.oldcenter = ""; +%obj.morphed = ""; +} + +function resetmorphsize(%owner) +{ +%size = (getRandom()*0.9+0.1); +%dir = (Getrandom()*2 > 1); +%owner.morphpulsesize = %dir ? %size : 1+%size+getRandom()*3; +} + diff --git a/Scripts/Data/VariableDefaults.cs b/Scripts/Data/VariableDefaults.cs new file mode 100644 index 0000000..0f7c140 --- /dev/null +++ b/Scripts/Data/VariableDefaults.cs @@ -0,0 +1,61 @@ +///////////////////////////////////////////////////////// +// VARIABLE DEFAULTS . CS +///////////////////////////////////////////////////////// +// This file overrides any important variables with blank values. +// Just for people who like to drop mods in then play them +// immediately after without setting up any preferences. +// ONLY PUT VARIABLES THAT ARE INTRODUCED BY ACCM IN HERE! + +///////////////////////////////////////////////////////// +// CLIENT SAVING VARIABLES +///////////////////////////////////////////////////////// +if($Host::ClientSaving $= "") + $Host::ClientSaving = 1; + +if($Host::MaxClientSaves $= "") + $Host::MaxClientSaves = 10; + +if($Host::MaxPieceVote $= "") + $Host::MaxPieceVote = 400; +///////////////////////////////////////////////////////// +// DEPLOYABLE VARIABLES +///////////////////////////////////////////////////////// +if($Host::AntidoteStationMaxAntidotes $= "") + $Host::AntidoteStationMaxAntidotes = 20; +///////////////////////////////////////////////////////// +// ZOMBIE VARIABLES +///////////////////////////////////////////////////////// +if($Host::MaxZombies $= "") + $Host::MaxZombies = 35; +///////////////////////////////////////////////////////// +// CHAT VARIABLES +///////////////////////////////////////////////////////// +if($Host::ACCMChatLogging $= "") + $Host::ACCMChatLogging = 1; + +if($Host::ACCMConnectionLogging $= "") + $Host::ACCMConnectionLogging = 1; + +if($Host::ACCMEchoChat $= "") + $Host::ACCMEchoChat = 1; + +if($Host::AllowKeeperPlayerVotes $= "") + $Host::AllowKeeperPlayerVotes = 1; + +if($Host::KeepersGetMakerAbility $= "") + $Host::KeepersGetMakerAbility = 1; + +if($Host::ObserversCannotChat $= "") + $Host::ObserversCannotChat = 1; +///////////////////////////////////////////////////////// +// MISC VARIABLES +///////////////////////////////////////////////////////// +if($Host::LockedTeams $= "") + $Host::LockedTeams = 0; + +if($Host::NoInfection $= "") + $Host::NoInfection = 0; + +if($Host::SADProtection $= "") + $Host::SADProtection = 1; +///////////////////////////////////////////////////////// \ No newline at end of file diff --git a/Scripts/InfectionGame.cs b/Scripts/InfectionGame.cs new file mode 100644 index 0000000..72a9b75 --- /dev/null +++ b/Scripts/InfectionGame.cs @@ -0,0 +1,219 @@ +// DisplayName = Infection + +//--- GAME RULES BEGIN --- +// Build a Base or use Map bases. +// Defend your territory from the horde of undead. +// Either contain or destroy the Zombie infection. +// -25 points per team kill. +// -10 points per death or suicide. +//--- GAME RULES END --- + +// Get Rid of those Underpowered weapons. +$InvBanList[Infection, "KriegRifle"] = 1; +$InvBanList[Infection, "PBC"] = 1; + +// Infection Game +function InfectionGame::initGameVars(%game) +{ + %game.SCORE_PER_SUICIDE = -10; + %game.SCORE_PER_TEAMKILL = -25; + %game.SCORE_PER_DEATH = -10; + + %game.SCORE_PER_KILL = 0; +} + +package InfectionGame { + +function InfectionGame::timeLimitReached(%game) +{ + logEcho("game over (timelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function InfectionGame::scoreLimitReached(%game) +{ + logEcho("game over (scorelimit)"); + %game.gameOver(); + cycleMissions(); +} + +function InfectionGame::gameOver(%game) +{ + DefaultGame::gameOver(%game); + + messageAll('MsgGameOver', "Match has ended.~wvoice/announcer/ann.gameover.wav" ); + + cancel(%game.timeThread); + messageAll('MsgClearObjHud', ""); + for(%i = 0; %i < ClientGroup.getCount(); %i ++) { + %client = ClientGroup.getObject(%i); + %game.resetScore(%client); + } +} + +function InfectionGame::clientMissionDropReady(%game, %client) +{ + messageClient(%client, 'MsgClientReady',"", %game.class); + %game.resetScore(%client); + for(%i = 1; %i <= %game.numTeams; %i++) + { + $Teams[%i].score = 0; + messageClient(%client, 'MsgInfectionAddTeam', "", %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 InfectionGame::assignClientTeam(%game, %client, %respawn) +{ + DefaultGame::assignClientTeam(%game, %client, %respawn); + messageClient(%client, 'MsgCheckTeamLines', "", %client.team); +} + +function InfectionGame::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); + + if(!isDemo()) + { + %cl.offenseScore = %killPoints + + %cl.suicides * %game.SCORE_PER_SUICIDE + + %cl.teamKills * %game.SCORE_PER_TEAMKILL + + %cl.vehicleScore + %cl.vehicleBonus; + + %cl.defenseScore = %cl.genRepairs * %game.SCORE_PER_REPAIR_GEN + + %cl.returnPts; + } + + %cl.score = mFloor(%cl.offenseScore + %cl.defenseScore + (%cl.zkills * $zombie::killpoints)); + + %game.recalcTeamRanks(%cl); + } + +function InfectionGame::updateKillScores(%game, %clVictim, %clKiller, %damageType, %implement) +{ + 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)) + %game.awardScoreTurretKill(%clVictim, %implement); + else if (%game.testKill(%clVictim, %clKiller)) + { + %value = %game.awardScoreKill(%clKiller); + %game.shareScore(%clKiller, %value); + %game.awardScoreDeath(%clVictim); + + if (%game.testEscortAssist(%clVictim, %clKiller)) + %game.awardScoreEscortAssist(%clKiller); + } + else + { + if (%game.testSuicide(%clVictim, %clKiller, %damageType)) + { + %game.awardScoreSuicide(%clVictim); + } + else + { + if (%game.testTeamKill(%clVictim, %clKiller)) + %game.awardScoreTeamKill(%clVictim, %clKiller); + } + } +} + +function InfectionGame::resetDontScoreTimer(%game, %team) +{ + $dontScoreTimer[%team] = false; +} + +function InfectionGame::resetScore(%game, %client) +{ + %client.offenseScore = 0; + %client.kills = 0; + %client.deaths = 0; + %client.suicides = 0; + %client.teamKills = 0; + %client.vehicleScore = 0; + %client.vehicleBonus = 0; + + %client.defenseScore = 0; + %client.genRepairs = 0; + %client.returnPts = 0; + %client.score = 0; +} + +function InfectionGame::objectRepaired(%game, %obj, %objName) +{ + %item = %obj.getDataBlock().getName(); + switch$ (%item) + { + case generatorLarge : + %game.genOnRepaired(%obj, %objName); + case sensorMediumPulse : + } + %obj.wasDisabled = false; +} + +function InfectionGame::genOnRepaired(%game, %obj, %objName) +{ + + if (%game.testValidRepair(%obj)) + { + %repairman = %obj.repairedBy; + %game.awardScoreGenRepair(%obj.repairedBy); + } +} + +function InfectionGame::awardScoreGenRepair(%game, %cl) +{ + %cl.genRepairs++; + if (%game.SCORE_PER_REPAIR_GEN != 0) + { + messageClient(%cl, 'msgGenRep', '\c0You received a %1 point bonus for repairing a generator.', %game.SCORE_PER_REPAIR_GEN); + } + %game.recalcScore(%cl); +} + +function InfectionGame::enterMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + %player.client.outOfBounds = false; + messageClient(%player.client, 'EnterMissionArea', '\c1You are back in the mission area.'); +} + +function InfectionGame::leaveMissionArea(%game, %playerData, %player) +{ + if(%player.getState() $= "Dead") + return; + %player.client.outOfBounds = true; + + 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 InfectionGame::testKill(%game, %victimID, %killerID) +{ + return ((%killerID !=0) && (%victimID.team != %killerID.team)); +} + +function InfectionGame::awardScoreKill(%game, %killerID) +{ + %killerID.kills++; + %game.recalcScore(%killerID); + return %game.SCORE_PER_KILL; +} +}; diff --git a/Scripts/JTLmeteorStorm.cs b/Scripts/JTLmeteorStorm.cs new file mode 100644 index 0000000..d023775 --- /dev/null +++ b/Scripts/JTLmeteorStorm.cs @@ -0,0 +1,194 @@ +// JTLmeteorStorm.cs +// +// This script (C) 2002 by JackTL +// +// Use, modify, but give credit +// +// Functions: +// +// JTLMeteorStorm(obj,forcePlayer[1/0],maxRad,numFb,dropAlt,dropAltVariance,dbName,dbType,timeOutMS,randomRot[1/0],randomPulse[1/0],maxPulse,speedVec,offsetSpeedVec[1/0]) +// + +datablock ParticleData(JTLMeteorStormFireballParticle) { + dragCoeffiecient = 0.0; + gravityCoefficient = -0.2; + inheritedVelFactor = 0.0; + + lifetimeMS = 350; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -160.0; + spinRandomMax = 160.0; + + animateTexture = true; + framesPerSec = 15; + + animTexName[0] = "special/Explosion/exp_0016"; + animTexName[1] = "special/Explosion/exp_0018"; + animTexName[2] = "special/Explosion/exp_0020"; + animTexName[3] = "special/Explosion/exp_0022"; + animTexName[4] = "special/Explosion/exp_0024"; + animTexName[5] = "special/Explosion/exp_0026"; + animTexName[6] = "special/Explosion/exp_0028"; + animTexName[7] = "special/Explosion/exp_0030"; + animTexName[8] = "special/Explosion/exp_0032"; + + colors[0] = "1.0 0.7 0.5 1.0"; + colors[1] = "1.0 0.5 0.2 1.0"; + colors[2] = "1.0 0.25 0.1 0.0"; + sizes[0] = 3.0; + sizes[1] = 1.0; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(JTLMeteorStormFireballEmitter) { + ejectionPeriodMS = 5; + periodVarianceMS = 1; + + ejectionVelocity = 0.25; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 30.0; + + particles = "JTLMeteorStormFireballParticle"; +}; + +datablock GrenadeProjectileData(JTLMeteorStormFireball) { + projectileShapeName = "weapon_chaingun_ammocasing.dts"; + emitterDelay = -1; + directDamage = 0; + directDamageType = $DamageType::Meteor; + hasDamageRadius = true; + indirectDamage = 0.5; + damageRadius = 7.5; + radiusDamageType = $DamageType::Meteor; + kickBackStrength = 1750; + explosion = PlasmaBoltExplosion; + splash = PlasmaSplash; + baseEmitter = JTLMeteorStormFireballEmitter; + armingDelayMS = 50; + grenadeElasticity = 0.15; + grenadeFriction = 0.4; + drag = 0.1; + gravityMod = 0.0; + sound = GrenadeProjectileSound; + + hasLight = true; + lightRadius = 20.0; + lightColor = "1 1 0.5"; +}; + +function JTLMeteorStormFireball::onExplode(%data,%proj,%pos,%mod) { + if (%data.hasDamageRadius) + RadiusExplosion(%proj,%pos,%data.damageRadius,%data.indirectDamage,%data.kickBackStrength,%proj.sourceObject,%data.radiusDamageType); + %pos = %proj.getPosition(); + %surface = containerRayCast(vectorAdd(%pos,"0 0 0.1"),vectorAdd(%pos,"0 0 -1"),-1,%proj); + %tObj = firstWord(%surface); + if (isObject(%tObj)) + %tObj.damage(%proj,%pos,0.4,%proj.getDataBlock().directDamageType); +// ionStormBeam(vectorAdd(%pos,"0 0" SPC $IonStorm::Height)); +} + +function JTLMeteorStorm (%obj,%forcePlayer,%maxRad,%numFb,%dropAlt,%dropAltVariance,%dbName,%dbType,%timeOutMS,%randomRot,%randomPulse,%maxPulse,%speedVec,%offsetSpeedVec,%createFB,%pos,%target) { + %pi = 3.1415926535897932384626433832795; // Whoa.. + if (%createFB) { + if (%randomRot) + %rot = "0 0 1" SPC getRandom() * (%pi * 2); + else + %rot = "1 0 0 0"; + %fb = new (%dbType) (JTLMeteor) { + dataBlock = %dbName; + position = %pos; // Needed for non-projectile types + initialPosition = %pos; + initialDirection = %speedVec; +// sourceObject = 0; + sourceSlot = 0; + vehicleObject = 0; + }; + if (isObject(%target) && $JTLMeteorStormSeek == 1 && %dbType $= "SeekerProjectile") + %fb.setObjectTarget(%target); + %fb.setRotation(%rot); + if (%randomPulse) { + %pulse = getRandom() * %maxPulse; + %iPos = vectorNormalize((getRandom() * 2) - 1 SPC (getRandom() * 2) - 1 SPC (getRandom() * 2) - 1); + %iPos = vectorAdd(%pos,%iPos); + %iVec = vectorScale(vectorNormalize(getRandom() SPC getRandom() SPC getRandom()),%pulse); +// Fix this, not for projectiles + %fb.applyImpulse(%iPos,%iVec); + } + if (%dbType $= "Item") + %fb.setVelocity(%speedVec); // Needed for non-projectile types + MissionCleanup.add(%fb); + if (%timeOutMS) { + %fb.schedule(%timeOutMS,setDamageState,Destroyed); + %fb.schedule(%timeOutMS+1000,delete); + } + return; + } + + if (%forcePlayer) { + %obj = %obj.player; + } + else { + if (%obj.getClassName() $= "GameConnection") { + %obj2 = %obj.getControlObject(); + if (isObject(%obj2)) + %obj = %obj2; + } + } + + if (isObject(%obj)) { + if (%maxRad < 1) + %maxRad = 50; + if (%numFb < 1) + %numFb = 100; + if (%dropAlt < 1) + %dropAlt = 100; + if (%dropAltVariance < 1) + %dropAltVariance = 500; + if (!isObject(%dbName)) + %dbName = "JTLMeteorStormFireball"; + if (%dbType $= "" || %dbType $= "0") + %dbType = "GrenadeProjectile"; + if (%speedVec $= "" || %speedVec $= "0") + %speedVec = "0 0 -2"; + if (%maxPulse < 1) + %maxPulse = 4000; + %p = %obj.getWorldBoxCenter(); + %x = getWord(%p,0); + %y = getWord(%p,1); + %z = getWord(%p,2); + for (%i = 0; %i < %numFb; %i++) { + %dVec = getRandom() * %pi * 2; + %dRad = getRandom() * %maxRad; + %dX =mSin(%dVec) * %dRad; + %dY =mCos(%dVec) * %dRad; + %dZ =%dropAlt + (getRandom() * %dropAltVariance); + if (%offsetSpeedVec) { + %v2 = vectorCross(vectorNormalize(%speedVec),"1 0 0"); + %v3 = vectorCross(vectorNormalize(%speedVec),%v2); + %dPos = vectorAdd(%p,vectorScale(vectorNormalize(%speedVec),-%dZ)); + %dPos = vectorAdd(%dPos,vectorScale(%v2,%dX)); + %dPos = vectorAdd(%dPos,vectorScale(%v3,%dY)); + } + else { + %dX = %x + %dX; + %dY = %y + %dY; + %dZ = %z + %dZ; + %dPos = %dX SPC %dY SPC %dZ; + } + JTLMeteorStorm(0,0,0,0,0,0,%dbName,%dbType,%timeOutMS,%randomRot,%randomPulse,%maxPulse,%speedVec,0,true,%dPos,%obj); + } + } + else { + error("-JTLMeteorStorm- no valid object."); + error("Usage: JTLMeteorStorm(obj,forcePlayer[1/0],maxRad,numFb,dropAlt,dropAltVariance,dbName,dbType,timeOutMS,randomRot[1/0],randomPulse[1/0],maxPulse,speedVec,offsetSpeedVec[1/0])"); + } +} diff --git a/Scripts/LobbyGui.cs b/Scripts/LobbyGui.cs new file mode 100644 index 0000000..2f1e752 --- /dev/null +++ b/Scripts/LobbyGui.cs @@ -0,0 +1,616 @@ +//------------------------------------------------------------------------------ +// +// LobbyGui.cs +// +//------------------------------------------------------------------------------ + +$InLobby = false; + +//------------------------------------------------------------------------------ +function LobbyGui::onAdd( %this ) +{ + // Add the Player popup menu: + new GuiControl(LobbyPlayerActionDlg) { + profile = "GuiModelessDialogProfile"; + horizSizing = "width"; + vertSizing = "height"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + + new ShellPopupMenu( LobbyPlayerPopup ) { + profile = "ShellPopupProfile"; + position = "0 0"; + extent = "0 0"; + minExtent = "0 0"; + visible = "1"; + maxPopupHeight = "200"; + noButtonStyle = "1"; + }; + }; +} + +//------------------------------------------------------------------------------ +function LobbyGui::onWake( %this ) +{ + if ( !%this.initialized ) + { + LobbyPlayerList.setSortColumn( $pref::Lobby::SortColumnKey ); + LobbyPlayerList.setSortIncreasing( $pref::Lobby::SortInc ); + + %this.initialized = true; + } + + $InLobby = true; + + //pop any key maps + moveMap.pop(); + if ( isObject( passengerkeys ) ) + passengerKeys.pop(); + if ( isObject( observerBlockMap ) ) + observerBlockMap.pop(); + if ( isObject( observerMap ) ) + observerMap.pop(); + + $enableDirectInput = "0"; + deactivateDirectInput(); + + LobbyMessageVector.attach(HudMessageVector); + LobbyMessageScroll.scrollToBottom(); + updateLobbyPlayerList(); + + LobbyServerName.setText( $clServerName ); + + %headerStyle = ""; + %statusText = "" @ %headerStyle @ "MISSION TYPE:" SPC $clMissionType + NL "" @ %headerStyle @ "MISSION:" SPC $clMissionName + NL "" @ %headerStyle @ "OBJECTIVES:"; + + for ( %line = 0; %this.objLine[%line] !$= ""; %line++ ) + %statusText = %statusText NL "* " @ %this.objLine[%line]; + + LobbyStatusText.setText( %statusText ); + + fillLobbyVoteMenu(); +} + +//------------------------------------------------------------------------------ +function LobbyGui::onSleep( %this ) +{ + if ( %this.playerDialogOpen ) + LobbyPlayerPopup.forceClose(); + + LobbyVoteMenu.clear(); + LobbyVoteMenu.mode = ""; + LobbyCancelBtn.setVisible( false ); + LobbyStatusText.setText( "" ); + $InLobby = false; +} + +//------------------------------------------------------------------------------ +function lobbyDisconnect() +{ + MessageBoxYesNo( "CONFIRM", "Are you sure you want to leave this game?", "lobbyLeaveGame();", "" ); +} + +//------------------------------------------------------------------------------ +function lobbyLeaveGame() +{ + Canvas.popDialog( LobbyGui ); + Disconnect(); +} + +//------------------------------------------------------------------------------ +function lobbyReturnToGame() +{ + Canvas.setContent( PlayGui ); +} + +//------------------------------------------------------------------------------ +function LobbyChatEnter::onEscape( %this ) +{ + %this.setValue( "" ); +} + +//------------------------------------------------------------------------------ +function LobbyChatEnter::send( %this ) +{ + %text = %this.getValue(); + if ( %text $= "" ) + %text = " "; + commandToServer( 'MessageSent', %text ); + %this.setValue( "" ); +} + +//------------------------------------------------------------------------------ +function LobbyPlayerList::initColumns( %this ) +{ + %this.clear(); + %this.clearColumns(); + %this.addColumn( 0, " ", 24, 24, 24, "center" ); // Flag column + %this.addColumn( 6, "lobby_headset", 36, 36, 36, "headericon" ); // Voice Com column + %this.addColumn( 1, "Player", $pref::Lobby::Column1, 50, 200 ); + if ( $clTeamCount > 1 ) + %this.addColumn( 2, "Team", $pref::Lobby::Column2, 50, 200 ); + %this.addColumn( 3, "Score", $pref::Lobby::Column3, 25, 200, "numeric center" ); + %this.addColumn( 4, "Ping", $pref::Lobby::Column4, 25, 200, "numeric center" ); + %this.addColumn( 5, "PL", $pref::Lobby::Column5, 25, 200, "numeric center" ); + + commandToServer( 'getScores' ); +} + +//------------------------------------------------------------------------------ +function LobbyPlayerList::onColumnResize( %this, %col, %size ) +{ + $pref::Lobby::Column[%this.getColumnKey( %col )] = %size; +} + +//------------------------------------------------------------------------------ +function LobbyPlayerList::onSetSortKey( %this, %key, %increasing ) +{ + $pref::Lobby::SortColumnKey = %key; + $pref::Lobby::SortInc = %increasing; +} + +//------------------------------------------------------------------------------ +function updateLobbyPlayerList() +{ + if ( $InLobby ) + { + // Let the server know we want an update: + commandToServer( 'getScores' ); + schedule( 4000, 0, updateLobbyPlayerList ); + } +} + +//------------------------------------------------------------------------------ +function lobbyUpdatePlayer( %clientId ) +{ + %player = $PlayerList[%clientId]; + if ( !isObject( %player ) ) + { + warn( "lobbyUpdatePlayer( " @ %clientId @ " ) - there is no client object with that id!" ); + return; + } + + // Build the text: + if ( %player.isSuperAdmin ) + %tag = "SA"; + else if ( %player.isAdmin ) + %tag = "A"; + else if ( %player.isZombieKeeper ) + %tag = "ZK"; + else if ( %player.isBot ) + %tag = "B"; + else + %tag = " "; + + if ( %player.canListen ) + { + if ( %player.voiceEnabled ) + { + %voiceIcons = "lobby_icon_speak"; + if ( %player.isListening ) + %voiceIcons = %voiceIcons @ ":lobby_icon_listen"; + } + else + %voiceIcons = %player.isListening ? "lobby_icon_listen" : ""; + } + else + %voiceIcons = "shll_icon_timedout"; + + if ( $clTeamCount > 1 ) + { + if ( %player.teamId == 0 ) + %teamName = "Observer"; + else + %teamName = $clTeamScore[%player.teamId, 0] $= "" ? "-" : $clTeamScore[%player.teamId, 0]; + %text = %tag TAB %voiceIcons TAB %player.name TAB %teamName TAB %player.score TAB %player.ping TAB %player.packetLoss; + } + else + %text = %tag TAB %voiceIcons TAB %player.name TAB %player.score TAB %player.ping TAB %player.packetLoss; + + if ( LobbyPlayerList.getRowNumById( %clientId ) == -1 ) + LobbyPlayerList.addRow( %clientId, %text ); + else + LobbyPlayerList.setRowById( %clientId, %text ); + + if ( $InLobby ) + LobbyPlayerList.sort(); +} + +//------------------------------------------------------------------------------ +function lobbyRemovePlayer( %clientId ) +{ + LobbyPlayerList.removeRowById( %clientId ); +} + +//------------------------------------------------------------------------------ +function LobbyPlayerList::onRightMouseDown( %this, %column, %row, %mousePos ) +{ + // Open the action menu: + %clientId = %this.getRowId( %row ); + LobbyPlayerPopup.player = $PlayerList[%clientId]; + + if ( LobbyPlayerPopup.player !$= "" ) + { + LobbyPlayerPopup.position = %mousePos; + Canvas.pushDialog( LobbyPlayerActionDlg ); + LobbyPlayerPopup.forceOnAction(); + } +} + +//------------------------------------------------------------------------------ +function LobbyPlayerActionDlg::onWake( %this ) +{ + LobbyGui.playerDialogOpen = true; + fillPlayerPopupMenu(); +} + +//------------------------------------------------------------------------------ +function LobbyPlayerActionDlg::onSleep( %this ) +{ + LobbyGui.playerDialogOpen = false; +} + +//------------------------------------------------------------------------------ +function LobbyPlayerPopup::onSelect( %this, %id, %text ) +{ +//the id's for these are used in DefaultGame::sendGamePlayerPopupMenu()... +//mute: 1 +//admin: 2 +//kick: 3 +//ban: 4 +//force observer: 5 +//switch team: 6 + + switch( %id ) + { + case 1: // Mute/Unmute + togglePlayerMute(%this.player.clientId); + + case 2: // Admin + MessageBoxYesNo( "CONFIRM", "Are you sure you want to make " @ %this.player.name @ " an admin?", + "lobbyPlayerVote( VoteAdminPlayer, \"ADMIN player\", " @ %this.player.clientId @ " );" ); + + case 3: // Kick + MessageBoxYesNo( "CONFIRM", "Are you sure you want to kick " @ %this.player.name @ "?", + "lobbyPlayerVote( VoteKickPlayer, \"KICK player\", " @ %this.player.clientId @ " );" ); + + case 4: // Ban + MessageBoxYesNo( "CONFIRM", "Are you sure you want to ban " @ %this.player.name @ "?", + "lobbyPlayerVote( BanPlayer, \"BAN player\", " @ %this.player.clientId @ " );" ); + + case 5: // force observer + forceToObserver(%this.player.clientId); + + case 6: //change team 1 + changePlayersTeam(%this.player.clientId, 1); + + case 7: //change team 2 + changePlayersTeam(%this.player.clientId, 2); + + case 8: + adminAddPlayerToGame(%this.player.clientId); + + case 9: // enable/disable voice communication + togglePlayerVoiceCom( %this.player ); + + case 10: + confirmAdminListAdd( %this.player, false ); + + case 11: + confirmAdminListAdd( %this.player, true ); + + Canvas.popDialog( LobbyPlayerActionDlg ); + } +} + +function confirmAdminListAdd( %client, %super ) +{ + if( %super ) + MessageBoxYesNo( "CONFIRM", "Are you sure you want to add " @ %client.name @ " to the server super admin list?", "toSuperList( " @ %client.clientId @ " );" ); + + else + MessageBoxYesNo( "CONFIRM", "Are you sure you want to add " @ %client.name @ " to the server admin list?", "toAdminList( " @ %client.clientId @ " );" ); +} + +function toSuperList( %client ) +{ + commandToServer( 'AddToSuperAdminList', %client ); +} + +function toAdminList( %client ) +{ + commandToServer( 'AddToAdminList', %client ); +} + +//------------------------------------------------------------------------------ +function LobbyPlayerPopup::onCancel( %this ) +{ + Canvas.popDialog( LobbyPlayerActionDlg ); +} + +//------------------------------------------------------------------------------ +function togglePlayerMute(%client) +{ + commandToServer( 'togglePlayerMute', %client ); +} + +//------------------------------------------------------------------------------ +function togglePlayerVoiceCom( %playerRep ) +{ + commandToServer( 'ListenTo', %playerRep.clientId, !%playerRep.voiceEnabled, true ); +} + +//------------------------------------------------------------------------------ +function forceToObserver( %client ) +{ + commandToServer( 'forcePlayerToObserver', %client ); +} + +function AdminAddPlayerToGame(%client) +{ + CommandToServer( 'clientAddToGame', %client ); +} + +//------------------------------------------------------------------------------ +function changePlayersTeam(%client, %team) +{ + commandToServer( 'changePlayersTeam', %client, %team); +} +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +function fillLobbyVoteMenu() +{ + LobbyVoteMenu.key++; + LobbyVoteMenu.clear(); + LobbyVoteMenu.tourneyChoose = 0; + commandToServer( 'GetVoteMenu', LobbyVoteMenu.key ); +} + +//------------------------------------------------------------------------------ +function fillLobbyTeamMenu() +{ + LobbyVoteMenu.key++; + LobbyVoteMenu.clear(); + LobbyVoteMenu.mode = "team"; + commandToServer( 'GetTeamList', LobbyVoteMenu.key ); + LobbyCancelBtn.setVisible( true ); +} + +//------------------------------------------------------------------------------ +function fillPlayerPopupMenu() +{ + LobbyPlayerPopup.key++; + LobbyPlayerPopup.clear(); + + LobbyPlayerPopup.add(LobbyPlayerPopup.player.name, 0); + commandToServer( 'GetPlayerPopupMenu', LobbyPlayerPopup.player.clientId, LobbyPlayerPopup.key ); +} + +//------------------------------------------------------------------------------ +function fillLobbyMissionTypeMenu() +{ + LobbyVoteMenu.key++; + LobbyVoteMenu.clear(); + LobbyVoteMenu.mode = "type"; + commandToServer( 'GetMissionTypes', LobbyVoteMenu.key ); + LobbyCancelBtn.setVisible( true ); +} + +//------------------------------------------------------------------------------ +function fillLobbyMissionMenu( %type, %typeName ) +{ + LobbyVoteMenu.key++; + LobbyVoteMenu.clear(); + LobbyVoteMenu.mode = "mission"; + LobbyVoteMenu.missionType = %type; + LobbyVoteMenu.typeName = %typeName; + commandToServer( 'GetMissionList', LobbyVoteMenu.key, %type ); +} + +//------------------------------------------------------------------------------ +function fillLobbyTimeLimitMenu() +{ + LobbyVoteMenu.key++; + LobbyVoteMenu.clear(); + LobbyVoteMenu.mode = "timeLimit"; + commandToServer( 'GetTimeLimitList', LobbyVoteMenu.key ); + LobbyCancelBtn.setVisible( true ); +} +//------------------------------------------------------------------------------ +addMessageCallback( 'MsgVoteItem', handleVoteItemMessage ); +addMessageCallback( 'MsgPlayerPopupItem', handlePlayerPopupMessage ); +addMessageCallback( 'MsgVotePassed', handleVotePassedMessage ); +addMessageCallback( 'MsgVoteFailed', handleVoteFailedMessage ); +addMessageCallback( 'MsgAdminPlayer', handleAdminPlayerMessage ); +addMessageCallback( 'MsgAdminAdminPlayer', handleAdminAdminPlayerMessage ); +addMessageCallback( 'MsgSuperAdminPlayer', handleSuperAdminPlayerMessage ); +addMessageCallback( 'MsgAdminForce', handleAdminForceMessage ); + +//------------------------------------------------------------------------------ +function handleAdminForceMessage() +{ + alxPlay(AdminForceSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function handleAdminAdminPlayerMessage( %msgType, %msgString, %client ) +{ + %player = $PlayerList[%client]; + if(%player) + %player.isAdmin = true; + alxPlay(AdminForceSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function handleAdminPlayerMessage( %msgType, %msgString, %client ) +{ + %player = $PlayerList[%client]; + if(%player) + %player.isAdmin = true; + alxPlay(VotePassSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function handleSuperAdminPlayerMessage( %msgType, %msgString, %client ) +{ + %player = $PlayerList[%client]; + if(%player) + { + %player.isSuperAdmin = true; + %player.isAdmin = true; + } + alxPlay(AdminForceSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function handleVoteItemMessage( %msgType, %msgString, %key, %voteName, %voteActionMsg, %voteText, %sort ) +{ + if ( %key != LobbyVoteMenu.key ) + return; + + %index = LobbyVoteMenu.rowCount(); + LobbyVoteMenu.addRow( %index, detag( %voteText ) ); + if ( %sort ) + LobbyVoteMenu.sort( 0 ); + $clVoteCmd[%index] = detag( %voteName ); + $clVoteAction[%index] = detag( %voteActionMsg ); +} + +//------------------------------------------------------------------------------ +function handlePlayerPopupMessage( %msgType, %msgString, %key, %voteName, %voteActionMsg, %voteText, %popupEntryId ) +{ + if ( %key != LobbyPlayerPopup.key ) + return; + + LobbyPlayerPopup.add( " " @ detag( %voteText ), %popupEntryId ); +} + +//------------------------------------------------------------------------------ +function handleVotePassedMessage( %msgType, %msgString, %voteName, %voteText ) +{ + if ( $InLobby ) + fillLobbyVoteMenu(); + + alxPlay(VotePassSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function handleVoteFailedMessage( %msgType, %msgString, %voteName, %voteText ) +{ + if ( $InLobby ) + fillLobbyVoteMenu(); + + alxPlay(VoteNotPassSound, 0, 0, 0); +} + +//------------------------------------------------------------------------------ +function lobbyVote() +{ + %id = LobbyVoteMenu.getSelectedId(); + %text = LobbyVoteMenu.getRowTextById( %id ); + + switch$ ( LobbyVoteMenu.mode ) + { + case "": // Default case... + // Test for special cases: + switch$ ( $clVoteCmd[%id] ) + { + case "JoinGame": + CommandToServer( 'clientJoinGame' ); + schedule( 100, 0, lobbyReturnToGame ); + return; + + case "ChooseTeam": + commandToServer( 'ClientJoinTeam', -1, true ); + schedule( 100, 0, lobbyReturnToGame ); + return; + + case "VoteTournamentMode": + LobbyVoteMenu.tourneyChoose = 1; + fillLobbyMissionTypeMenu(); + return; + + case "VoteMatchStart": + startNewVote( "VoteMatchStart" ); + schedule( 100, 0, lobbyReturnToGame ); + return; + + case "MakeObserver": + commandToServer( 'ClientMakeObserver' ); + schedule( 100, 0, lobbyReturnToGame ); + return; + + case "VoteChangeMission": + fillLobbyMissionTypeMenu(); + return; + + case "VoteChangeTimeLimit": + fillLobbyTimeLimitMenu(); + return; + + case "Addbot": + commandToServer( 'addBot' ); + return; + } + + case "team": + commandToServer( 'ClientJoinTeam', %id++ ); + LobbyVoteMenu.reset(); + return; + + case "type": + fillLobbyMissionMenu( $clVoteCmd[%id], %text ); + return; + + case "mission": + if( !LobbyVoteMenu.tourneyChoose ) + { + startNewVote( "VoteChangeMission", + %text, // Mission display name + LobbyVoteMenu.typeName, // Mission type display name + $clVoteCmd[%id], // Mission id + LobbyVoteMenu.missionType ); // Mission type id + } + else + { + startNewVote( "VoteTournamentMode", + %text, // Mission display name + LobbyVoteMenu.typeName, // Mission type display name + $clVoteCmd[%id], // Mission id + LobbyVoteMenu.missionType ); // Mission type id + LobbyVoteMenu.tourneyChoose = 0; + } + LobbyVoteMenu.reset(); + return; + + case "timeLimit": + startNewVote( "VoteChangeTimeLimit", $clVoteCmd[%id] ); + LobbyVoteMenu.reset(); + return; + } + + startNewVote( $clVoteCmd[%id], $clVoteAction[%id] ); + fillLobbyVoteMenu(); +} + +//------------------------------------------------------------------------------ +function LobbyVoteMenu::reset( %this ) +{ + %this.mode = ""; + %this.tourneyChoose = 0; + LobbyCancelBtn.setVisible( false ); + fillLobbyVoteMenu(); +} + +//------------------------------------------------------------------------------ +function lobbyPlayerVote(%voteType, %actionMsg, %playerId) +{ + startNewVote(%voteType, %playerId, 0, 0, 0, true); + fillLobbyVoteMenu(); +} diff --git a/Scripts/ModScripts/AI/DroneAI.cs b/Scripts/ModScripts/AI/DroneAI.cs new file mode 100644 index 0000000..2d7c6d0 --- /dev/null +++ b/Scripts/ModScripts/AI/DroneAI.cs @@ -0,0 +1,722 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Drone AI by Dondelium_X +// CCM v3.4 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +$Drone::DetectDist = 500; +$Drone::TurnImpulse = 123; +$Drone::FrdImpulse = 525; +//$Drone::TurnImpulse = 675 / 3; +//$Drone::FrdImpulse = 525 * 3; +$Drone::newTargetChance = "1 100"; + +$Drone::paths = 4; +$Drone::path[1,1] = "0 1500 1000"; +$Drone::path[1,2] = "1500 0 1000"; +$Drone::path[1,3] = "0 -1500 1000"; +$Drone::path[1,4] = "-1500 0 1000"; + +$Drone::path[2,1] = "0 -1500 1000"; +$Drone::path[2,2] = "-1500 0 1000"; +$Drone::path[2,3] = "0 1500 1000"; +$Drone::path[2,4] = "1500 0 1000"; + +$Drone::path[3,1] = "0 2500 750"; +$Drone::path[3,2] = "0 2500 1250"; +$Drone::path[3,3] = "0 -2500 1250"; +$Drone::path[3,4] = "0 -2500 750"; + +$Drone::path[4,1] = "2500 0 750"; +$Drone::path[4,2] = "2500 0 1250"; +$Drone::path[4,3] = "-2500 0 1250"; +$Drone::path[4,4] = "-2500 0 750"; + +//-----Manuver Index----- + +$flightManuvers::Behind[0] = Immelman; +$flightManuvers::Behind[1] = HalfFlip; +$flightManuvers::Behind[2] = BreakRight; +$flightManuvers::Behind[3] = BreakLeft; +$flightManuvers::Behind[4] = BarrelRoll; +$flightManuvers::Behind[5] = WingWaggle; +$flightManuvers::Behind[6] = Reversal; + + +//-----Manuvers----- + +//Immelman +$flightManuver::Points[Immelman] = 5; //This signifies how many points are in the manuver +$flightManuver::Point[Immelman,0] = "0 1 0 0 0 1"; //each point represents the 3 numbers of the vector of the desired forward vector, +$flightManuver::Point[Immelman,1] = "0 0 1 0 -1 0"; //and then the 3 numbers of the vector for the desired Up vector +$flightManuver::Point[Immelman,2] = "0 -1 0 0 0 -1"; +$flightManuver::Point[Immelman,3] = "0 -1 0 1 0 0"; +$flightManuver::Point[Immelman,4] = "0 -1 0 0 0 1"; + +//halfFlip +$flightManuver::Points[HalfFlip] = 3; +$flightManuver::Point[HalfFlip,0] = "0 1 0 0 0 1"; +$flightManuver::Point[HalfFlip,1] = "0 0 1 0 -1 0"; +$flightManuver::Point[HalfFlip,2] = "0 -1 0 0 0 -1"; + +//breakRight +$flightManuver::Points[BreakRight] = 3; +$flightManuver::Point[BreakRight,0] = "0 1 0 0 0 1"; +$flightManuver::Point[BreakRight,1] = "0 1 0 1 0 0"; +$flightManuver::Point[BreakRight,2] = "1 0 0 0 -1 0"; + +//BreakLeft +$flightManuver::Points[BreakLeft] = 3; +$flightManuver::Point[BreakLeft,0] = "0 1 0 0 0 1"; +$flightManuver::Point[BreakLeft,1] = "0 1 0 -1 0 0"; +$flightManuver::Point[BreakLeft,2] = "-1 0 0 0 -1 0"; + +//BarrelRoll +$flightManuver::Points[BarrelRoll] = 5; +$flightManuver::Point[BarrelRoll,0] = "0 1 0 0 0 1"; +$flightManuver::Point[BarrelRoll,1] = "0 1 0 1 0 0"; +$flightManuver::Point[BarrelRoll,2] = "0 1 0 0 0 -1"; +$flightManuver::Point[BarrelRoll,3] = "0 1 0 -1 0 0"; +$flightManuver::Point[BarrelRoll,4] = "0 1 0 0 0 1"; + +//WingWaggle +$flightManuver::Points[WingWaggle] = 4; +$flightManuver::Point[WingWaggle,0] = "0 1 0 0 0 1"; +$flightManuver::Point[WingWaggle,1] = "-1 1 0 0 0 1"; +$flightManuver::Point[WingWaggle,2] = "1 1 0 0 0 1"; +$flightManuver::Point[WingWaggle,3] = "0 1 0 0 0 1"; + +//Reversal +$flightManuver::Points[Reversal] = 7; +$flightManuver::Point[Reversal,0] = "0 1 0 0 0 1"; +$flightManuver::Point[Reversal,1] = "0 0 1 0 -1 0"; +$flightManuver::Point[Reversal,2] = "0 0 1 1 0 0"; +$flightManuver::Point[Reversal,3] = "1 0 0 0 0 -1"; +$flightManuver::Point[Reversal,4] = "0 0 -1 -1 0 0"; +$flightManuver::Point[Reversal,5] = "0 0 -1 0 1 0"; +$flightManuver::Point[Reversal,6] = "0 1 0 0 0 1"; + +//This is a function for mass spawning +function DroneBattle(%pos, %radius, %number, %teamlow, %teamhigh, %maxskill){ + for(%i = 0; %i < %number; %i++){ + %startpos = vectorAdd(%pos,(getRandom(0, %radius) - (%radius / 2))@" "@(getRandom(0, %radius) - (%radius / 2))@" 0"); + %rotation = "0 0 1 "@getRandom(1,360); + if(%teamlow != %teamhigh) + %team = getRandom(%teamlow,%teamhigh); + else + %team = %teamlow; + StartDrone(%startpos,%rotation,%team,getRandom(1,%maxskill)); + } +} + +//This sets up the drone and the functions needed to start the drone. +function StartDrone(%pos, %rotation, %team, %skill){ + if(%team $= "") + %team = 0; + if(%pos $= "") + %pos = "0 0 300"; + if(%rotation $= "") + %rotation = "1 0 0 0"; + if(%skill !$= "ace"){ + if(%skill $= "" || %skill < 1) + %skill = 1; + else if(%skill > 10) + %skill = 10; + } + %Drone = new FlyingVehicle() + { + dataBlock = ScoutFlyer; + position = %pos; + rotation = %rotation; + team = %team; + }; + MissionCleanUp.add(%Drone); + + setTargetSensorGroup(%Drone.getTarget(), %team); + + %drone.isdrone = 1; + %drone.dodgeGround = 0; + + if(%skill $= "ace"){ + %skill = 10; + %drone.isace = 1; + } + + %drone.skill = 0.2 + (%skill / 12.5); + + schedule(100, 0, "DroneForwardImpulse", %drone); + schedule(101, 0, "DronefindTarget", %drone); + schedule(102, 0, "DroneScanGround", %drone); + + return %drone; +} + +//This makes the drone move forward until it dies +function DroneForwardImpulse(%obj){ + if(!isObject(%obj)) + return; + if(!%obj.isace){ + if(vectorLen(%obj.getVelocity()) < 165) + %obj.applyImpulse(%obj.getPosition(),vectorScale(%obj.getForwardVector(),$Drone::FrdImpulse)); + } + else{ + if(vectorLen(%obj.getVelocity()) < 225) + %obj.applyImpulse(%obj.getPosition(),vectorScale(%obj.getForwardVector(),$Drone::FrdImpulse * 3)); + } + schedule(100, 0, "DroneForwardImpulse", %obj); +} + +function DroneScanGround(%obj){ + if(!isObject(%obj)) + return; + %vec = %obj.getForwardVector(); + %vector = vectorAdd(%obj.getPosition(),"0 0 -500"); + %searchResult = containerRayCast(%obj.getWorldBoxCenter(), %vector, $TypeMasks::TerrainObjectType, %obj); + if(%searchResult){ + %z = getWord(%vec,2); + %height = vectorDist(%obj.getPosition(),posFromRaycast(%searchresult)); + if(%z < 0){ + if(%height <= (200 + ((%z * -1) * 300))){ + %obj.dodgeground = 1; + schedule(100, 0, "DroneDodgeGround", %obj); + return; + } + } + } + schedule(100, 0, "DroneScanGround", %obj); +} + +function DroneDodgeGround(%obj){ + if(!isObject(%obj)) + return; + %vec = %obj.getForwardVector(); + %z = getWord(%vec,2); + if(%z > 0){ + %obj.dodgeground = 0; + schedule(100, 0, "DronefindTarget", %obj); + schedule(101, 0, "DroneScanGround", %obj); + return; + } + %pos = vectorAdd(%obj.getPosition(),%vec); + %obj.applyImpulse(%pos,vectorScale("0 0 1",$Drone::TurnImpulse * %obj.skill)); + schedule(100, 0, "DroneDodgeGround", %obj); +} + +//This function checks the area around it for any targets. +function DronefindTarget(%obj){ + if(!isObject(%obj)) + return; + %pos = %obj.getposition(); + InitContainerRadiusSearch(%pos, $drone::detectdist + (800 + (600 * %obj.skill)), $TypeMasks::VehicleObjectType); + while ((%searchResult = containerSearchNext()) != 0){ + %objtarget = firstWord(%searchResult); + if(isObject(%objtarget) && %objTarget !$= %obj){ + %trgtype = %objtarget.getClassName(); + if(%trgtype $= "FlyingVehicle" && %objtarget.team != %obj.team){ + %testPos = %objtarget.getWorldBoxCenter(); + %distance = vectorDist(%objtarget.getPosition(),%pos); + if (%distance > 0){ + %target = %objtarget; + DroneDetermineManuver(%obj, %target); + return; + } + } + } + } + schedule(100, 0, "DronePatrol", %obj); +} + +function DronePatrol(%obj){ + if(!isObject(%obj)) + return; + if($Drone::paths > 0){ + if(%obj.path $= ""){ + %obj.path = getRandom(1,$Drone::paths); + %obj.point = 0; + } + %obj.point++; + if($Drone::path[%obj.path,%obj.point] $= ""){ + schedule(100, 0, "DronefindTarget", %obj); + %obj.path = ""; + %obj.point = ""; + } + else + schedule(100,0,"dronemovetopoint",%obj,$Drone::path[%obj.path,%obj.point]); + } + else + schedule(500, 0, "DronefindTarget", %obj); +} + +function DroneMoveToPoint(%obj,%Tpos){ + if(!isObject(%obj)) + return; + if(%obj.dodgeground == 1) + return; + %pos = %obj.getPosition(); + %objfrd = %obj.getForwardVector(); + %objup = %obj.getUpVector(); + + %dist = vectorDist(%pos,%Tpos); + %aimvec = vectorNormalize(vectorSub(%Tpos,%pos)); + + %vec = vectorSub(%aimvec , %objfrd); + %vec = vectorCross(%vec, %objfrd); + %vec = vectorNormalize(vectorCross(%objfrd, %vec)); + if(vectorDist(%objfrd,vectorNormalize(%aimvec)) < 0.1) + %obj.applyImpulse(vectorAdd(%obj.getPosition(),vectorScale(%objfrd,($Drone::TurnImpulse / 2))),%vec); + else if(vectorDist(%objup, %vec) > 0.1){ + %vec = vectorSub(%vec, %objup); + %vec = vectorCross(%vec, %objup); + %vec = vectorNormalize(vectorCross(%objup, %vec)); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%objup,$Drone::TurnImpulse * 3 * %obj.skill)); + %obj.applyImpulse(%pos,%vec); + } + else + %obj.applyImpulse(vectorAdd(%obj.getPosition(),%objfrd),vectorScale(%vec,$Drone::TurnImpulse * %obj.skill)); + if(getRandom(0,getWord($Drone::newTargetChance,1)) <= (getWord($Drone::newTargetChance,0) * 10)) + schedule(100, 0, "DronefindTarget", %obj, %target); + else if(%dist < 25) + schedule(100, 0, "DronePatrol", %obj); + else + schedule(100, 0, "DroneMoveToPoint", %obj, %tpos); +} + +//This function figures out what the drone should do from where the target is. +function DroneDetermineManuver(%obj, %target){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + DronefindTarget(%obj); + return; + } + %pos = %obj.getPosition(); + %Tpos = %Target.getPosition(); + %objfrd = %obj.getForwardVector(); + %trgfrd = %target.getForwardVector(); + %vec = vectorSub(%Tpos,%pos); + %dirdist = vectorDist(%objfrd,vectorNormalize(%vec)); + %vecdist = vectorDist(%objfrd,%trgfrd); + if(%dirdist > 1.41){ + if(%vecdist < 1.41 && vectorDist(%pos,%Tpos) < 2000){ + UnderFireManuver(%obj, $flightManuvers::Behind[getRandom(0,6)], ""); + } + else + TurnToFire(%obj, %target); + } + else + TurnToFire(%obj, %target); + %obj.target = %target; +} + +function TurnToFire(%obj, %target){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + DronefindTarget(%obj); + return; + } + if(%obj.dodgeground == 1) + return; + + %pos = %obj.getPosition(); + %Tpos = %target.getPosition(); + %objfrd = %obj.getForwardVector(); + %trgfrd = %target.getForwardVector(); + %objup = %obj.getUpVector(); + %Tvel = %target.getVelocity(); + + %dist = vectorDist(%pos,%Tpos); + %aimPos = vectorAdd(%Tpos, vectorScale(%Tvel,(%dist / 1750))); + %aimvec = vectorNormalize(vectorSub(%aimPos,%pos)); + + if(vectorDist(%objfrd,%aimvec) < 0.1 && %dist <= 1000){ + if((vectorDist(%objfrd,%trgfrd) < 0.2 || vectorDist(%objfrd,%trgfrd) > 1.8) && %obj.missiling != 1){ + %obj.setImageTrigger(4, true); + %obj.missiling = 1; + } + else if(%obj.missiling == 1){ + %obj.setImageTrigger(4, false); + %obj.missiling = 0; + } +// if (%obj.firing != 1){ + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + %obj.firing = 1; +// } + } + else{ + if(%obj.firing == 1){ + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.firing = 0; + } + if(%obj.missiling == 1){ + %obj.setImageTrigger(4, false); + %obj.missiling = 0; + } + } + %vec = vectorSub(%aimvec , %objfrd); + %vec = vectorCross(%vec, %objfrd); + %vec = vectorNormalize(vectorCross(%objfrd, %vec)); + if(vectorDist(%objfrd,%trgfrd) > 1.8 && %dist <= 120){ + %vec = vectorScale(%vec, -1); + if(vectorDist(%objfrd,vectorNormalize(%aimvec)) > 1.8) + %obj.applyImpulse(vectorAdd(%obj.getPosition(),vectorScale(%objfrd,($Drone::TurnImpulse / 2))),%vec); + else if(vectorDist(%objup, %vec) > 0.1){ + %vec = vectorSub(%vec, %objup); + %vec = vectorCross(%vec, %objup); + %vec = vectorNormalize(vectorCross(%objup, %vec)); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%objup,$Drone::TurnImpulse * 3 * %obj.skill)); + %obj.applyImpulse(%pos,%vec); + } + else + %obj.applyImpulse(vectorAdd(%obj.getPosition(),%objfrd),vectorScale(%vec,$Drone::TurnImpulse * %obj.skill)); + } + else if(vectorDist(%objfrd,vectorNormalize(%aimvec)) < 0.1) + %obj.applyImpulse(vectorAdd(%obj.getPosition(),vectorScale(%objfrd,($Drone::TurnImpulse / 2))),%vec); + else if(vectorDist(%objup, %vec) > 0.1){ + %vec = vectorSub(%vec, %objup); + %vec = vectorCross(%vec, %objup); + %vec = vectorNormalize(vectorCross(%objup, %vec)); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%objup,$Drone::TurnImpulse * 3 * %obj.skill)); + %obj.applyImpulse(%pos,%vec); + } + else + %obj.applyImpulse(vectorAdd(%obj.getPosition(),%objfrd),vectorScale(%vec,$Drone::TurnImpulse * %obj.skill)); + if(getRandom(0,getWord($Drone::newTargetChance,1)) <= getWord($Drone::newTargetChance,0)) + schedule(100, 0, "DronefindTarget", %obj, %target); + else + schedule(100, 0, "TurnToFire", %obj, %target); +} + +//This function preforms the manuver choosen in Determine Maunver function. +function UnderFireManuver(%obj, %manuver, %point, %count){ + if(!isObject(%obj)) + return; + if(%obj.dodgeground == 1) + return; + if(%count $= "") + %count = 0; + if(%point $= ""){ + %point = 0; + SetPointVectors(%obj, %manuver); + } + if(%count >= 30){ + schedule(100, 0, "DroneDetermineManuver", %obj, %obj.target); + return; + } + %frdvec = %obj.getForwardVector(); + %upvec = %obj.getUpVector(); + if(vectorDist(%frdvec, %obj.pointvecfront[%point]) < 0.15 && vectorDist(%upvec, %obj.pointvectop[%point]) < 0.15){ + %point++; + if(%point < $flightManuver::Points[%manuver]) + schedule(100, 0, "UnderFireManuver", %obj, %manuver, %point, 0); + else + schedule(100, 0, "DroneDetermineManuver", %obj, %obj.target); + return; + } + if(vectorDist(%frdvec, %obj.pointvecfront[%point]) > 0.1){ + %vec = vectorSub(%obj.pointvecfront[%point], %frdvec); + %vec = vectorCross(%vec, %frdvec); + %vec = vectorCross(%frdvec, %vec); + %obj.applyImpulse(vectorAdd(%obj.getPosition(),vectorScale(%frdvec,$Drone::TurnImpulse * %obj.skill)),%vec); + } + if(vectorDist(%upvec, %obj.pointvectop[%point]) > 0.1){ + %vec = vectorSub(%obj.pointvectop[%point], %upvec); + %vec = vectorCross(%vec, %upvec); + %vec = vectorCross(%upvec, %vec); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%upvec,$Drone::TurnImpulse * 3 * %obj.skill)); + %obj.applyImpulse(%pos,%vec); + } + %count++; + schedule(100, 0, "UnderFireManuver", %obj, %manuver, %point, %count); +} + +//This function finds what vectors each point in the manuver is compaired to the drone. +function SetPointVectors(%obj, %manuver){ + %up = %obj.getUpVector(); + %forward = %obj.getForwardVector(); + %right = vectorNormalize(vectorSub(%obj.getEdge("1 0 0"),%obj.getEdge("-1 0 0"))); + for(%i = 0; %i < $flightManuver::Points[%manuver]; %i++){ + %pointdir = $flightManuver::Point[%manuver, %i]; + %topvec = "0 0 0"; + %frontvec = "0 0 0"; + if(getWord(%pointdir, 0) > 0) + %frontvec = vectorAdd(%frontvec, %right); + else if(getWord(%pointdir, 0) < 0) + %frontvec = vectorAdd(%frontvec, vectorScale(%right, -1)); + if(getWord(%pointdir, 1) > 0) + %frontvec = vectorAdd(%frontvec, %forward); + else if(getWord(%pointdir, 1) < 0) + %frontvec = vectorAdd(%frontvec, vectorScale(%forward, -1)); + if(getWord(%pointdir, 2) > 0) + %frontvec = vectorAdd(%frontvec, %up); + else if(getWord(%pointdir, 2) < 0) + %frontvec = vectorAdd(%frontvec, vectorScale(%up, -1)); + + if(getWord(%pointdir, 3) > 0) + %topvec = vectorAdd(%topvec, %right); + else if(getWord(%pointdir, 3) < 0) + %topvec = vectorAdd(%topvec, vectorScale(%right, -1)); + if(getWord(%pointdir, 4) > 0) + %topvec = vectorAdd(%topvec, %forward); + else if(getWord(%pointdir, 4) < 0) + %topvec = vectorAdd(%topvec, vectorScale(%forward, -1)); + if(getWord(%pointdir, 5) > 0) + %topvec = vectorAdd(%topvec, %up); + else if(getWord(%pointdir, 5) < 0) + %topvec = vectorAdd(%topvec, vectorScale(%up, -1)); + + %obj.pointvecfront[%i] = vectorNormalize(%frontvec); + %obj.pointvectop[%i] = vectorNormalize(%topvec); + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Tank AI by Dondelium_X +// CCM v3.4 +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +$DTank::ForwardForce = 7000; +$DTank::BackForce = 3000; +$DTank::TurnForce = 2500; + +function StartDTank(%pos,%team,%skill){ + if(%team $= "") + %team = 0; + if(%pos $= "") + %pos = "0 0 300"; + if(%skill $= "" || %skill < 1) + %skill = 1; + else if(%skill > 10) + %skill = 10; + + %Drone = new HoverVehicle() + { + dataBlock = HeavyTank; + position = %pos; + rotation = "0 0 1 0"; + team = %team; + }; + MissionCleanUp.add(%Drone); + + setTargetSensorGroup(%Drone.getTarget(), %team); + + %drone.skill = 0.6 + (%skill / 25); + + schedule(100, 0, "DTankScanTargets",%drone); +} + +function DTankScanTargets(%obj){ + if(!isObject(%obj)) + return; + %pos = %obj.getposition(); + %closestClient = -1; + %closestDistance = 32767; + %scandist = 2000 * %obj.skill; + %airtrgs = ""; + %groundtrgs = ""; + %inftrgs = ""; + InitContainerRadiusSearch(%pos, %scanDist, $TypeMasks::VehicleObjectType | $TypeMasks::PlayerObjectType); + while ((%searchResult = containerSearchNext()) != 0){ + %objtarget = firstWord(%searchResult); + if(isObject(%objtarget) && %objTarget !$= %obj && %objtarget.team != %obj.team){ + %trgtype = %objtarget.getClassName(); + if(%trgtype $= "FlyingVehicle") + %airtrgs = %airtrgs SPC %objtarget; + else if(%trgtype $= "HoverVehicle") + %groundtrgs = %groundtrgs SPC %objtarget; + else if(%trgtype $= "Player") + %inftrgs = %inftrgs SPC %objtarget; + } + } + if(%groundtrgs){ + %target = firstWord(%groundtrgs); + %groundtrgs = getWords(%groundtrgs,1,4); + DTankGroundCombat(%obj, %target,%groundtrgs); + } + else if(%inftrgs){ + %target = firstWord(%inftrgs); + %inftrgs = getWords(%inftrgs,1,4); + DTankInfCombat(%obj, %target,%inftrgs); + } + else if(%airtrgs){ + %target = firstWord(%airtrgs); + %airtrgs = getWords(%airtrgs,1,4); + DTankAACombat(%obj, %target,%airtrgs); + } + else{ + schedule(3000, 0, "DTankScanTargets", %obj); + } +} + +function DTankGroundCombat(%obj, %target, %Trglist){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + %obj.turretobject.setImageTrigger(4,false); + %obj.firing = ""; + if(%TrgList){ + %target = firstWord(%Trglist); + %trglist = getWords(%trglist,1,4); + } + else{ + DTankScanTargets(%obj); + return; + } + } + %pos = %obj.getPosition(); + %tpos = %target.getPosition(); + %frdvec = vectorNormalize(getWords(%obj.getForwardVector(),0,1) SPC "0"); + %chkvec = vectorSub(%tpos,%pos); + %chkvec = vectorNormalize(getWords(%chkvec,0,1) SPC "0"); + %turnvec = vectorsub(%chkvec,%frdvec); + %vecdif = vectorlen(%turnvec); + if(vectorDist(%pos,%tpos) > 2000){ + if(%obj.firing){ + %obj.firing = ""; + %obj.turret.setImageTrigger(4,false); + } + %target = ""; + } + else if(vectorDist(%pos,%tpos) > 500){ + if(%vecdif >= "0.1"){ + %vec = vectorCross(%turnvec, %frdvec); + %vec = vectorNormalize(vectorCross(%obj.getForwardVector(), %vec)); + %Imppos = vectorAdd(%pos,%obj.getForwardVector()); + %obj.applyImpulse(%Imppos,vectorScale(%vec,$DTank::TurnForce * %obj.skill)); + } + } + else{ + if(!%obj.firing){ + %obj.firing = 1; + %obj.turretobject.firetype = 3; + %obj.turretobject.setTargetObject(%Target); + %obj.turretobject.aquireTime = getSimTime(); + %obj.turretobject.setImageTrigger(4,true); + } + if(%vecdif < "1.2" || %vecdif >= "1.6"){ + %Tvec1 = vectorNormalize(vectorcross(%chkvec,"0 0 1")); + %Tvec2 = vectorScale(%Tvec1,-1); + if(vectordist(%frdvec,%Tvec1) < vectorDist(%frdvec,%Tvec2)) + %tovec = vectorSub(%Tvec1,%frdvec); + else + %tovec = vectorSub(%Tvec2,%frdvec); + %vec = vectorCross(%Tvec2, %frdvec); + %vec = vectorNormalize(vectorCross(%obj.getForwardVector(), %vec)); + %Imppos = vectorAdd(%pos,%obj.getForwardVector()); + %obj.applyImpulse(%Imppos,vectorScale(%vec,$DTank::TurnForce * %obj.skill)); + } + } + %obj.applyImpulse(%pos,vectorScale(%obj.getForwardVector(),$DTank::ForwardForce)); + schedule(100, 0, "DTankGroundCombat",%obj,%target,%Trglist); +} + +function DTankAACombat(%obj, %target, %Trglist){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + %obj.turretobject.setImageTrigger(2,false); + %obj.firing = ""; + if(%TrgList){ + %target = firstWord(%Trglist); + %trglist = getWords(%trglist,1,4); + } + else{ + DTankScanTargets(%obj); + return; + } + } + %pos = %obj.getPosition(); + %tpos = %target.getPosition(); + if(vectorDist(%pos,%tpos) < 750){ + if(!%obj.firing){ + %obj.firing = 1; + %obj.turretobject.setTargetObject(%Target); + %obj.turretobject.aquireTime = getSimTime(); + %obj.turretobject.setImageTrigger(2,true); + } + } + else if(vectorDist(%pos,%tpos) > 2000){ + if(%obj.firing){ + %obj.firing = ""; + %obj.turretobject.setImageTrigger(2,false); + } + %target = ""; + } + schedule(100, 0, "DTankAACombat",%obj,%target,%Trglist); +} + +function DTankInfCombat(%obj, %target, %Trglist){ + if(!isObject(%obj)) + return; + if(!isObject(%target) || %target.getState() $= "dead"){ + %obj.turretobject.setImageTrigger(3,false); + %obj.firing = ""; + if(%TrgList){ + %target = firstWord(%Trglist); + %trglist = getWords(%trglist,1,4); + } + else{ + DTankScanTargets(%obj); + return; + } + } + %pos = %obj.getPosition(); + %tpos = %target.getPosition(); + %frdvec = vectorNormalize(getWords(%obj.getForwardVector(),0,1) SPC "0"); + %chkvec = vectorSub(%tpos,%pos); + %chkvec = vectorNormalize(getWords(%chkvec,0,1) SPC "0"); + %turnvec = vectorsub(%chkvec,%frdvec); + %vecdif = vectorlen(%turnvec); + if(vectorDist(%pos,%tpos) > 2000){ + if(%obj.firing){ + %obj.firing = ""; + %obj.turret.setImageTrigger(3,false); + } + %target = ""; + } + else if(vectorDist(%pos,%tpos) > 300){ + if(%vecdif >= "0.1"){ + %vec = vectorCross(%turnvec, %frdvec); + %vec = vectorNormalize(vectorCross(%obj.getForwardVector(), %vec)); + %Imppos = vectorAdd(%pos,%obj.getForwardVector()); + %obj.applyImpulse(%Imppos,vectorScale(%vec,$DTank::TurnForce * %obj.skill)); + } + } + else{ + if(!%obj.firing){ + %obj.firing = 1; + %obj.turretobject.firetype = 1; + %obj.turretobject.setTargetObject(%Target); + %obj.turretobject.aquireTime = getSimTime(); + %obj.turretobject.setImageTrigger(3,true); + } + if(vectorDist(%pos,%tpos) < 100){ + if(%vecdif <= "1.6"){ + %vec = vectorCross(%turnvec, %frdvec); + %vec = vectorNormalize(vectorCross(%obj.getForwardVector(), %vec)); + %Imppos = vectorAdd(%pos,%obj.getForwardVector()); + %obj.applyImpulse(%Imppos,vectorScale(%vec,$DTank::TurnForce * %obj.skill * "-1")); + } + } + else { + if(%vecdif < "1.2" || %vecdif >= "1.6"){ + %Tvec1 = vectorNormalize(vectorcross(%chkvec,"0 0 1")); + %Tvec2 = vectorScale(%Tvec1,-1); + if(vectordist(%frdvec,%Tvec1) < vectorDist(%frdvec,%Tvec2)) + %tovec = vectorSub(%Tvec1,%frdvec); + else + %tovec = vectorSub(%Tvec2,%frdvec); + %vec = vectorCross(%Tvec2, %frdvec); + %vec = vectorNormalize(vectorCross(%obj.getForwardVector(), %vec)); + %Imppos = vectorAdd(%pos,%obj.getForwardVector()); + %obj.applyImpulse(%Imppos,vectorScale(%vec,$DTank::TurnForce * %obj.skill)); + } + } + } + %obj.applyImpulse(%pos,vectorScale(%obj.getForwardVector(),$DTank::ForwardForce)); + schedule(100, 0, "DTankInfCombat",%obj,%target,%Trglist); +} \ No newline at end of file diff --git a/Scripts/ModScripts/AI/S11AI.cs b/Scripts/ModScripts/AI/S11AI.cs new file mode 100644 index 0000000..6eec3fe --- /dev/null +++ b/Scripts/ModScripts/AI/S11AI.cs @@ -0,0 +1,283 @@ +$S11manuverforce = 75; +$S11flyforce = 350; +$S11forwardspeed = 120; +$S11hoverheight = 3; +$S11hoverforce = 100; +$S11reconheight = "450 550"; +$S11reconradius = 700; + +$S11[move] = "TAKEOFF MOVE LAND IDLE"; +$S11[guard] = "TAKEOFF RECON"; +$S11[attack] = "TAKEOFF MOE FIRE MOVE LAND IDLE"; +$S11[rearm] = "TAKEOFF MOVE LAND REARM TAKEOFF MOVE LAND IDLE"; + +function S11Think(%obj){ + if(!isObject(%obj)) + return; + if(%obj.tasks !$= ""){ + %task = getWord(%obj.tasks,0); + %fun = "S11"@%task; + %obj.mode = %task; + if(%obj.specvar[%task] !$= "") + schedule(10, 0, %fun, %obj, %obj.specvar[%task]); + else + schedule(10, 0, %fun, %obj); + } + else{ + %obj.mode = "IDLE"; + S11IDLE(%obj); + } +} + +function S11IDLE(%obj){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "IDLE") + return; + + %pos = %obj.getPosition(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,vectorScale("0 0 -1",$S11hoverheight)); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + if(%searchresult){ + %obj.applyImpulse(%pos,vectorScale("0 0 1",$S11hoverforce)); + } + + schedule(100, 0, "S11IDLE", %obj); +} + +function S11TAKEOFF(%obj){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "TAKEOFF") + return; + %pos = %obj.getPosition(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,"0 0 -20"); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + if(%searchresult) + %obj.applyImpulse(%pos,"0 0 200"); + else{ + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + schedule(100, 0, "S11TAKEOFF", %obj); +} + +function S11LAND(%obj){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "LAND") + return; + + %pos = %obj.getPosition(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,"0 0" SPC (-10 - $S11hoverheight)); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + if(%searchresult){ + %dist = vectorDist(%pos,posfromraycast(%searchresult)); + if(%dist <= $S11hoverheight){ + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + %speed = vectorScale("0 0 -1",%dist); + %obj.setVelocity(%speed); + } + else{ + %vel = %obj.getVelocity(); + %reqvel = "0 0 -15"; + %impvec = vectorScale(vectorSub(%reqvel,%vel),%obj.getDatablock().mass); + %obj.applyImpulse(%pos,%impvec); + } + schedule(100, 0, "S11LAND", %obj); +} + +function S11MOVE(%obj, %movepos){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "MOVE") + return; + + %pos = %obj.getPosition(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,"0 0 -150"); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + %vec = "0 0 0"; + %frd = %obj.getForwardVector(); + if(vectorlen(%obj.getVelocity()) <= $S11forwardspeed) + %vec = vectorScale(%frd,$S11flyforce); + if(%searchresult) + %vec = vectorAdd(%vec,"0 0 150"); + %tstpos = getWords(%movepos,0,1) SPC getWord(%pos,2); + %dist = vectorDist(%tstpos,%pos); + if(%dist > 10){ + %aimvec = vectorSub(%tstpos,%pos); + %tvec = vectorNormalize(vectorCross(%aimvec, %frd)); + %tvec = vectorNormalize(vectorCross(%frd, %tvec)); + %obj.applyImpulse(vectorAdd(%pos,%frd),vectorScale(%tvec,$S11manuverforce)); + } + else{ + if(%obj.specvar["MOVE",2] !$= ""){ + %obj.specvar["MOVE"] = %obj.specvar["MOVE",2]; + %obj.specvar["MOVE",2] = ""; + } + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + %obj.applyImpulse(%pos,%vec); + schedule(100, 0, "S11MOVE",%obj,%movepos); +} + +function S11RECON(%obj,%reconPos){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "RECON") + return; + + %pos = %obj.getPosition(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,"0 0 -1000"); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + if(%searchresult){ + %frd = %obj.getForwardVector(); + %vec = "0 0 0"; + if(vectorlen(%obj.getVelocity()) <= $S11forwardspeed) + %vec = vectorScale(%frd,$S11flyforce); + %dist = vectorDist(%pos,posfromraycast(%searchresult)); + if(%dist < getWord($S11reconheight,0)) + %vec = vectorAdd(%vec,"0 0 450"); + if(%dist > getWord($S11reconheight,1)) + %vec = vectorAdd(%vec,"0 0 -450"); + + %tstpos = getWords(%reconPos,0,1) SPC getWord(%pos,2); + %dist = vectorDist(%tstpos,%pos); + if(%dist > $S11reconradius){ + %aimvec = vectorSub(%tstpos,%pos); + %tvec = vectorNormalize(vectorCross(%aimvec, %frd)); + %tvec = vectorNormalize(vectorCross(%frd, %tvec)); + %obj.applyImpulse(vectorAdd(%pos,%frd),vectorScale(%tvec,$S11manuverforce)); + } + %obj.applyImpulse(%pos,%vec); + } + else{ + %vel = %obj.getVelocity(); + %reqvel = "0 0 -50"; + %impvec = vectorScale(vectorSub(%reqvel,%vel),%obj.getDatablock().mass); + %obj.applyImpulse(%pos,%impvec); + } + schedule(100, 0, "S11RECON",%obj,%reconpos); +} + +function S11MOE(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "MOE") + return; + if(!isObject(%target)){ + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + + %pos = %obj.getPosition(); + %frd = %obj.getForwardVector(); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%pos,vectorScale(%frd,100)); + %searchResult = containerRayCast(%pos, %vector, %mask, %obj); + if(!%searchResult){ + %tstvec = vectorAdd(%vector,"0 0 -50"); + %searchResult2 = containerRayCast(%vector, %tstvec, %mask, %obj); + if(%searchResult2) + %dir = "1"; + else + %dir = "-1"; + } + else + %dir = "2"; + + %impvec = vectorScale(%obj.getUpVector(),$S11manuverforce * %dir); + + %Tpos = %target.getPosition(); + %tstpos = getWords(%TPos,0,1) SPC getWord(%pos,2); + %aimvec = VectorNormalize(vectorSub(%tstpos,%pos)); + %dist = vectorDist(%frd,%aimvec); + if(%dist > 0.1){ + %aimvec = vectorSub(%tstpos,%pos); + %tvec = vectorNormalize(vectorCross(%aimvec, %frd)); + %tvec = vectorNormalize(vectorCross(%frd, %tvec)); + %impvec = vectorAdd(%impvec,vectorScale(%tvec,$S11manuverforce)); + } + if(vectorDist(%pos, %Tpos) < 500){ + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + + %obj.applyImpulse(vectorAdd(%pos,%frd),%impvec); + if(vectorlen(%obj.getVelocity()) <= $S11forwardspeed) + %obj.applyImpulse(%pos,vectorScale(%obj.getForwardVector(),$S11flyforce)); + + schedule(100, 0, "S11MOE", %obj,%target); +} + +function S11REARM(%obj){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "REARM") + return; + + %obj.setVelocity("0 0 0"); + + if (%obj.turret.inv[AALauncherAmmo] < 1) + %obj.turret.setInventory(AALauncherAmmo, 1); + else if(%DamageLevel > 0) + %obj.setRepairRate(0.01); + else { + %obj.setRepairRate(0); + S11Think(%obj); + return; + } + + schedule(100, 0, "S11REARM", %obj, %target); +} + +function S11FIRE(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "FIRE") + return; + if(!isObject(%target)){ + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + %turret = %obj.turret; + if(%turret.inv[AALauncherAmmo] >= 1){ + %turret.target = %target; + if(%obj.aiming != 1){ + %obj.aiming = 1; + %turret.setTargetObject(%target); + %turret.aquireTime = getSimTime(); + %turret.setImageTrigger(2,true); + } + else if(isObject(%turret.TLB)){ + %turret.setImageTrigger(3,true); + } + } + else{ + %obj.aiming = 0; + %turret.setImageTrigger(2,false); + %turret.setImageTrigger(3,false); + %obj.tasks = getWords(%obj.tasks,1,(getNumberOfWords(%obj.tasks) - 1)); + S11Think(%obj); + return; + } + %frd = %obj.getForwardVector(); + %impvec = vectorAdd(vectorScale(%frd,$S11flyforce),"0 0 150"); + if(vectorlen(%obj.getVelocity()) <= $S11forwardspeed) + %obj.applyImpulse(%obj.getPosition(),%impvec); + schedule(100, 0, "S11FIRE", %obj, %target); +} \ No newline at end of file diff --git a/Scripts/ModScripts/AI/S17AI.cs b/Scripts/ModScripts/AI/S17AI.cs new file mode 100644 index 0000000..369fd64 --- /dev/null +++ b/Scripts/ModScripts/AI/S17AI.cs @@ -0,0 +1,107 @@ +$S17moveforce = 500; + +function S17Think(%obj){ + if(!isObject(%obj)) + return; + if(%obj.task !$= ""){ + %task = %obj.task; + %fun = "S17"@%task; + %obj.mode = %task; + cancel(%obj.loop);cancel(%obj.loop);cancel(%obj.loop); + if(%obj.specvar[%task] !$= "") + %obj.loop = schedule(10, 0, %fun, %obj, %obj.specvar[%task]); + else + %obj.loop = schedule(10, 0, %fun, %obj); + } +} + +function S17GUARD(%obj,%pos){ + if(!isObject(%obj)) + return; + %obj.mode = "MOVE"; + S17MOVE(%obj,%pos); +} + +function S17MOVE(%obj,%pos){ + if(!isObject(%obj)) + return; + if(%obj.mode !$= "MOVE") + return; + + %objpos = %obj.getPosition(); + %dist = vectorDist(%pos,%objpos); + if(%dist > 10){ + %vec = vectorNormalize(vectorSub(%pos,%objpos)); + %vec = vectorScale(%vec,$S17moveforce); + %obj.applyImpulse(%objpos,%vec); + } + else + return; + %obj.loop = schedule(100, 0, "S17MOVE",%obj,%pos); +} + +function S17ATTACK(%obj,%target){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + %obj.val = ""; + return; + } + if(%obj.mode !$= "ATTACK"){ + %obj.val = ""; + return; + } + + %objpos = %obj.getPosition(); + %trgpos = %target.getPosition(); + %dist = vectorDist(%trgpos,%objpos); + if(%dist > 100){ + %vec = vectorNormalize(vectorSub(%pos,%objpos)); + %vec = vectorScale(%vec,$S17moveforce); + %obj.applyImpulse(%objpos,%vec); + } + else { + %vec = vectorSub(%trgpos,%objpos); + %random = getRandom(1,2); + if(%obj.val $= ""){ + if(%random == 1) + %obj.val = "0 0 -1"; + else + %obj.val = "0 0 1"; + } + %vec = vectorNormalize(vectorCross(%vec,%val)); + %vec = vectorScale(%vec,$S17moveforce); + %obj.applyImpulse(%objpos,%vec); + } + %obj.loop = schedule(100, 0, "S17ATTACK",%obj,%target); +} + +function S17REARM(%obj,%target){ + if(!isObject(%obj)) + return; + if(!isObject(%target)){ + %target.setRepairRate(0); + return; + } + if(%obj.mode !$= "REARM"){ + %target.setRepairRate(0); + return; + } + + %objpos = %obj.getPosition(); + %trgpos = %target.getPosition(); + %dist = vectorDist(%trgpos,%objpos); + %DamageLevel = %targetObject.getDamageLevel(); + if(%dist > 6){ + %vec = vectorNormalize(vectorCross(%vec,%val)); + %vec = vectorScale(%vec,$S17moveforce); + %obj.applyImpulse(%objpos,%vec); + } + else if(%DamageLevel > 0) + %obj.setRepairRate(0.01); + else { + %obj.setRepairRate(0); + return; + } + %obj.loop = schedule(100, 0, "S17ATTACK",%obj,%target); +} \ No newline at end of file diff --git a/Scripts/ModScripts/AI/SentinelAI.cs b/Scripts/ModScripts/AI/SentinelAI.cs new file mode 100644 index 0000000..371c115 --- /dev/null +++ b/Scripts/ModScripts/AI/SentinelAI.cs @@ -0,0 +1,71 @@ +$sent::patrolwaittime = 10; // 10 secs +$sent::patrolsaturation = 2; // 2 sentinel patrolling one point at a time + +// 1 = Normal Sentinel +// 2 = Sentinel Monitor + +//============================================================================== +// CreateSentinel +//------------------------------------------------------------------------------ +// %pos = position +// %team = team +// %quantity = how many sentinels to spawn +// %type = the type of sentinel to spawn [regular/monitor] +//------------------------------------------------------------------------------ +// It creates a sentinel. +//============================================================================== +function CreateSentinel(%pos, %quantity, %type) +{ + if(%quantity < 1) + return; + + while(%quantity) + { + %data = (%type == 2 ? "SentinelMonitor" : "SentinelVehicle"); + %sent = new FlyingVehicle() { + datablock = %data; + }; + + %sent.setTransform(%pos SPC "0 0 1 0"); + %sent.team = 1; + + MissionCleanup.add(%sent); + + setTargetSensorGroup(%sent.getTarget(), 1); + + $ignoreNextBotConnection = true; + %ai = aiConnect("_AISent"); + ChangeName(%ai, "Sentinel"@%ai); + + %ai.isSentinel = true; + %ai.sentVehicle = %sent; + %ai.sentinelType = %type; + + %ai.setControlObject(%sent); + +// SentinelAI_PatrolArea(%ai); + warn("Sentinel created: "@%ai@", VEHID"@%sent); + %quantity--; + } +} + +function DropSentinel(%id) +{ + if(!isObject(%id) || %id.isSentinel != true) + return; + + if(isObject(%id.sentVehicle) && %id.sentVehicle.getDamageState() !$= "Destroyed") + %id.sentVehicle.setDamageState(Destroyed); + + %id.drop(); +} + +function AIConnection::Sentinel_MoveTo(%client, %coords) +{ + %client.clearStep(); + + %client.setPilotAim(%coords); + %client.setPilotDestination(%coords); +} + +function AIConnection::Sentinel_Damaged \ No newline at end of file diff --git a/Scripts/ModScripts/AI/SentinelData.cs b/Scripts/ModScripts/AI/SentinelData.cs new file mode 100644 index 0000000..88a305c --- /dev/null +++ b/Scripts/ModScripts/AI/SentinelData.cs @@ -0,0 +1,654 @@ +//============================================================================== +// Sentinel Data - Made by Blnukem +//============================================================================== +// Sentinel Sounds +//------------------------------------------------------------------------------ + +datablock EffectProfile(SentinelTurretSwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(SentinelTurretFireEffect) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + minDistance = 2.5; + maxDistance = 8.0; +}; + +datablock EffectProfile(SentinelDeactivateEffect) +{ + effectname = "powered/turret_heavy_reload"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(SentinelActivateEffect) +{ + effectname = "powered/turret_light_reload"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(SentinelIdleEffect) +{ + effectname = "powered/turret_light_idle"; + minDistance = 1; + maxDistance = 2; +}; + +datablock AudioProfile(SentinelTurretSwitchSound) +{ + filename = "fx/powered/turret_light_activate.wav"; + description = AudioClose3d; + preload = true; + effect = SentinelTurretSwitchEffect; +}; + +datablock AudioProfile(SentinelTurretFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = SentinelTurretFireEffect; +}; + +datablock AudioProfile(SentinelIdleSound) +{ + filename = "fx/powered/turret_light_idle.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = SentinelIdleEffect; +}; + +datablock AudioProfile(SentinelActivateSound) +{ + filename = "fx/powered/turret_light_reload.wav"; + description = AudioClose3d; + preload = true; + effect = SentinelActivateEffect; +}; + +datablock AudioProfile(SentinelDeactivateSound) +{ + filename = "fx/powered/turret_heavy_reload.wav"; + description = AudioClose3d; + preload = true; + effect = SentinelDeactivateEffect; +}; + +//============================================================================== +// Vehicle Data +//============================================================================== +// Standard Sentinel +//------------------------------------------------------------------------------ + +datablock FlyingVehicleData(SentinelVehicle) : SentinelDamageProfile +{ + spawnOffset = "0.0 0.0 0.1"; + + catagory = "Sentinels"; + shapeFile = "stackable2s.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "debris_generic.dts"; + debris = TurretDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + numMountPoints = 0; + + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = TurretExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + maxDamage = 0.5; + destroyedLevel = 0.5; + + isShielded = true; + energyPerDamagePoint = 160; + maxEnergy = 280; + rechargeRate = 0.8; + + minDrag = 30; + rotationalDrag = 2000; + + maxAutoSpeed = 15; + autoAngularForce = 400; + autoLinearForce = 300; + autoInputDamping = 0.95; + + maxSteeringAngle = 5; + horizontalSurfaceForce = 6; + verticalSurfaceForce = 4; + maneuveringForce = 5000; + steeringForce = 3000; + steeringRollForce = 0; + rollForce = 10; + hoverHeight = 10; + createHoverHeight = 2; + maxForwardSpeed = 15; + + jetForce = 4500; + minJetEnergy = 0; + jetEnergyDrain = 0; // Auto stabilize speed + vertThrustMultiple = 0; + + mass = 100; + bodyFriction = 0; + bodyRestitution = 0.5; + minRollSpeed = 0; + softImpactSpeed = 14; + hardImpactSpeed = 25; + + minImpactSpeed = 10; + speedDamageScale = 0.06; + + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + jetSound = ""; + engineSound = SentinelIdleSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = LiftoffDustEmitter; + triggerDustHeight = 1.0; + dustHeight = 0.1; + canControl = false; + + minMountDist = 4; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'Standard'; + targetTypeTag = 'Sentinel'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 5 5"; + canObserve = true; + + runningLight[0] = ShrikeLight1; + + shieldEffectScale = "2.0 2.0 2.0"; + +}; + +//============================================================================== +// Sentinel Monitor Vehicle Data +//============================================================================== + +datablock FlyingVehicleData(SentinelMonitor) : SentinelDamageProfile +{ + spawnOffset = "0.0 0.0 0.0"; + + catagory = "Sentinels"; + shapeFile = "beacon.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "debris_generic.dts"; + debris = TurretDebris; + renderWhenDestroyed = false; + + drag = 4.15; + density = 1.0; + + numMountPoints = 0; + + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = TurretExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + maxDamage = 50.00; + destroyedLevel = 50.00; + + isShielded = true; + rechargeRate = 2.5; + energyPerDamagePoint = 1; + maxEnergy = 500; + rechargeRate = 50.0; + + minDrag = 30; + rotationalDrag = 2000; + + maxAutoSpeed = 15; + autoAngularForce = 400; + autoLinearForce = 300; + autoInputDamping = 0.95; + + maxSteeringAngle = 5; + horizontalSurfaceForce = 6; + verticalSurfaceForce = 4; + maneuveringForce = 5000; + steeringForce = 1000; + steeringRollForce = 0; + rollForce = 100; + hoverHeight = 15; + createHoverHeight = 2; + maxForwardSpeed = 15; + + jetForce = 1500; + minJetEnergy = 0; + jetEnergyDrain = 0; // Auto stabilize speed + vertThrustMultiple = 0; + + mass = 100; + bodyFriction = 0; + bodyRestitution = 0.5; + minRollSpeed = 0; + softImpactSpeed = 14; + hardImpactSpeed = 25; + + minImpactSpeed = 25; + speedDamageScale = 0.06; + + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + minTrailSpeed = 0.1; + trailEmitter = ContrailEmitter; + + jetSound = ""; + engineSound = SentinelIdleSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = LiftoffDustEmitter; + triggerDustHeight = 2.0; + dustHeight = 0.0; + canControl = false; + + damageEmitter[0] = LightDamageSmoke; + damageEmitter[1] = LightDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 0.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + minMountDist = 4; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDCameraIcon; + cmdMiniIconName = "commander/MiniIcons/com_camera_grey"; + targetNameTag = ''; + targetTypeTag = 'Monitor'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 10.5; + observeParameters = "1 5 5"; + canObserve = true; + + runningLight[0] = ShrikeLight1; + + shieldEffectScale = "2.0 2.0 2.0"; + +}; + +//============================================================================== +// Image Data +//============================================================================== +// Standard Sentinel Image Data: +//------------------------------------------------------------------------------ + +datablock ShapeBaseImageData(BodyImage) : SentinelVehicle +{ + offset = "0.0 -0.2 0.3"; + rotation = "0 0 1 0"; + shapeFile = "turret_mortar_large.dts"; +}; + +datablock ShapeBaseImageData(RightWing) : SentinelVehicle +{ + offset = "0.2 0.0 0.4"; + rotation = "0 0 -1 90"; + shapeFile = "pack_deploy_sensor_pulse.dts"; +}; + +datablock ShapeBaseImageData(LeftWing) : SentinelVehicle +{ + offset = "-0.2 0.0 0.4"; + rotation = "0 0 1 90"; + shapeFile = "pack_deploy_sensor_pulse.dts"; +}; + +datablock ShapeBaseImageData(Eye) : SentinelVehicle +{ + offset = "0.0 1.1 0.22"; + rotation = "1 0 0 90"; + shapeFile = "beacon.dts"; +}; + +//============================================================================== +// Sentinel Monitor Image Data: +//============================================================================== + +datablock ShapeBaseImageData(Front) : SentinelMonitor +{ + offset = "0.0 -0.02 0.0"; + rotation = "1.0 0.0 0.0 90.0"; + shapeFile = "camera.dts"; +}; + +datablock ShapeBaseImageData(Rear) : SentinelMonitor +{ + offset = "0.0 0.02 0.0"; + rotation = "-1.0 0.0 0.0 90.0"; + shapeFile = "camera.dts"; +}; + +//============================================================================== +// Decals and Projectiles +//============================================================================== +// Default Sentinel bullet Decal image: +//------------------------------------------------------------------------------ + +datablock DecalData(SentinelDecal1) +{ + sizeX = 0.15; + sizeY = 0.15; + textureName = "special/bullethole3"; +}; + +//============================================================================== +// Standard Sentinel Bullet: +//============================================================================== + +datablock TracerProjectileData(SentinelBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.15; + directDamageType = $DamageType::Sentinel; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + HeadMultiplier = 1.5; + LegsMultiplier = 0.5; + + kickBackStrength = 50.0; + sound = ChaingunProjectile; + + dryVelocity = 800.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 10.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 10.0/255.0 @ " " @ 30.0/255.0 @ " " @ 240.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.15; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = SentinelDecal1; + + hasLight = true; + lightRadius = 8.0; + lightColor = "0.5 0.5 0.175"; + +}; + +//============================================================================== +// Turret Data +//============================================================================== +// Standard Sentinel Turret: +//------------------------------------------------------------------------------ + +datablock TurretData(SentinelTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; + preload = true; + + mass = 1.0; + maxDamage = 1.0; + destroyedLevel = 1.0; + repairRate = 0; + maxDamage = SentinelVehicle.maxDamage; + destroyedLevel = SentinelVehicle.destroyedLevel; + rechargeRate = 0.15; + + thetaMin = 90; + thetaMax = 180; + thetaNull = 90; + + rechargeRate = 0.15; + + isShielded = false; + energyPerDamagePoint = 110; + maxEnergy = 60; + renderWhenDestroyed = true; + barrel = SentinelTurretBarrel; + heatSignature = 0; + + canControl = false; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Sentinel'; + targetTypeTag = 'Turret'; + + firstPersonOnly = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; +}; + +datablock TurretImageData(SentinelTurretBarrel) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 0; + + projectile = SentinelBullet; + projectileType = TracerProjectile; + + usesEnergy = true; + fireEnergy = 0.0; + minEnergy = 10.0; + emap = true; + + activationMS = 700; + deactivateDelayMS = 1500; + thinkTimeMS = 120; + degPerSecTheta = 2000; + degPerSecPhi = 2000; + attackRadius = 100; + + casing = ShellDebris; + shellExitDir = "0.0 -0.5 -0.5"; + shellExitOffset = "0.0 0.0 0.0"; + shellExitVariance = 20.0; + shellVelocity = 5.0; + + projectileSpread = 2.0 / 1000.0; + + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = SentinelActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateSound[3] = ChaingunFireSound; + stateAllowImageChange[3] = false; + stateEmitter[3] = "GunFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 0.1; + stateScript[3] = "onFire"; + stateFire[3] = true; + stateEjectShell[3] = true; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 0.01; + stateAllowImageChange[4] = false; + stateWaitForTimeout[4] = true; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateSound[5] = SentinelDeactivateSound; + stateEmitter[5] = "GunFireEffectEmitter"; + stateEmitterNode[5] = "muzzlepoint1"; + stateEmitterTime[5] = 0.2; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + +//============================================================================== +// Vehicle Functions +//============================================================================== +// Sentinel Functions: +//------------------------------------------------------------------------------ + +function SentinelVehicle::deleteAllMounted(%data, %obj) { + $HostGamePlayerCount = ClientGroup.GetCount(); + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if (%client = %turret.getControllingClient()) { + %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(0, delete); +} + +function SentinelVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(BodyImage, 1); + %obj.mountImage(RightWing, 2); + %obj.mountImage(LeftWing, 3); + %obj.mountImage(Eye, 4); + + %turret = TurretData::create(SentinelTurret); + %turret.scale = "0.45 0.45 0.45"; + MissionCleanup.add(%turret); + %turret.team = 1; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(SentinelTurretBarrel, 0); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(true); + %turret.mountImage(AIAimingTurretBarrel, 1); + + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//============================================================================== +// Monitor Functions: +//============================================================================== + +function SentinelMonitor::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(Front, 1); + %obj.mountImage(Rear, 2); +} diff --git a/Scripts/ModScripts/ChatCommands/AICommands.cs b/Scripts/ModScripts/ChatCommands/AICommands.cs new file mode 100644 index 0000000..1316946 --- /dev/null +++ b/Scripts/ModScripts/ChatCommands/AICommands.cs @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// ACCM AI Commands +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Drone Commands: +//------------------------------------------------------------------------------ +// Made by Blnukem (edited by Eolk) +function ccDroneBattle(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %x = getword(%sender.player.position, 0); + %y = getword(%sender.player.position, 1); + %z = getword(%sender.player.position, 2) + 1200; + %newpos = %x SPC %y SPC %z; + %base = strlwr(getword(%args, 0)); + %difficulty = getword(%args, 1); + if(%base $= "Battle") + %base = 1; + else if(%base $= "Single") + %base = 2; + else + { + messageClient(%sender, "", "\c2Invalid variable. Valid variables are:\c3 battle\c2 and \c3single\c2."); + return; + } + + if(%difficulty $= "Easy") + %d = 1; + else if(%difficulty $= "Medium") + %d = 5; + else if(%difficulty $= "Hard") + %d = 10; + else if(%difficulty $= "Ace") + %d = "ace"; + else + { + messageClient(%sender, "", "\c2Invalid difficulty. Difficulties are:\c3 Light\c2,\c3 Medium\c2,\c3 Hard\c2, and\c3 Ace\c2."); + return; + } + + if(%base == 1) + DroneBattle(%newpos, 1000, 10, 10, 15, %d); + else if(%base == 2) + StartDrone(%newpos, "0 0 1 0", 5, %d); + + messageAll("", "\c3"@%sender.nameBase@" \c2spawned a "@%difficulty@" "@(%base == 1 ? "drone battle" : "drone")@".~wgui/objective_Notification.wav"); +} + +function ccCreateSentinel(%sender, %args) +{ + CreateSentinel("0 0 310", 1, 1); +} diff --git a/Scripts/ModScripts/ChatCommands/AdminCommands.cs b/Scripts/ModScripts/ChatCommands/AdminCommands.cs new file mode 100644 index 0000000..0428024 --- /dev/null +++ b/Scripts/ModScripts/ChatCommands/AdminCommands.cs @@ -0,0 +1,947 @@ +//============================================================================== +// Admin Commands +//============================================================================== + +// Command by Eolk. Modified by Blnukem. +function ccset(%sender, %args) +{ + %base = strlwr(getword(%args, 0)); + %param = getword(%args, 1); + switch$(%base) + { + case "canzombie": + if(%sender.isAdmin) + { + if(%param != 1 && %param != 0) + { + messageClient(%sender, "MsgNo", "\c2Invalid parameters. Use 1 for canZombie to be on, use 0 for canZombie to be off."); + return; + } + + $Host::canZombie = %param; + messageAll('MsgAdminForce', "\c1"@%sender.nameBase@" \c2changed \c5HUMAN ZOMBIES \c2to \c5"@%param@"\c2."); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::canZombie (human zombies) changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::canZombie@" via ccSet"); + } + case "joinpw": + if(%sender.isSuperAdmin) + { + if(%param $= "") + { + messageClient(%sender, "MsgNo", "\c2No Changes. You did not supply a value. Use \"remove\" to remove join pw."); + return; + } + + if(strlwr(%param) $= "remove") + $Host::Password = ""; + else + $Host::Password = %param; + + messageAll('MsgAdminForce', "\c1"@%sender.nameBase@" \c2changed \c5JOIN PASSWORD\c2."); + messageClient(%sender, "MsgYes", "\c2Join password changed to "@$Host::Password); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::Password (server password) changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::Password@" via ccSet"); + } + case "maxplyrs": + if(!%sender.isSuperAdmin) + return; + + %failed = false; + for(%i = 0; %i < strlen(%param); %i++) + { + %temp = getsubstr(%param, %i, 1); + if(%temp !$= "1" && %temp !$= "2" && %temp !$= "3" && %temp !$= "4" && %temp !$= "5" && %temp !$= "6" && %temp !$= "7" && %temp !$= "8" && %temp !$= "9" && %temp !$= "0") + { + %failed = true; + break; + } + } + + if(%param < 0) + %failed = true; + + if(%failed == true) + { + messageClient(%sender, "MsgNo", "\c2No changes. You supplied an invalid number. Must be over 0."); + return; + } + + $Host::MaxPlayers = %param; + messageAll('MsgAdminForce', "\c1"@%sender.nameBase@" \c2changed \c5MAX PLAYERS\c2 to \c5"@$Host::MaxPlayers@"\c2."); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::MaxPlayers (maximum players allowed by the server) changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::MaxPlayers@" via ccSet"); + case "restrict": + if(%sender.isAdmin) + { + %target = plnametocid(%param); + if(!isObject(%target)) + { + messageClient(%sender, "MsgNo", "\c2Unable to find target."); + return; + } + + if(%target.isAdmin) + { + messageClient(%sender, "MsgNo", "\c2Cannot do this to admins."); + return; + } + + if(!%target.CannotDeploy) + { + %target.CannotDeploy = 1; + messageClient(%sender, "MsgYes", "\c2"@%target.nameBase@"'s ability to deploy things has been revoked.~wfx/misc/diagnostic_on.wav"); + echo(%sender.namebase@" ("@%sender@") disabled "@%target.nameBase@"'s ("@%target@") ability to deploy objects"); + } + else + { + %target.CannotDeploy = 0; + messageClient(%sender, "MsgYes", "\c2"@%target.nameBase@" is free to deploy things again.~wfx/misc/diagnostic_on.wav"); + echo(%sender.namebase@" ("@%sender@") enabled "@%target.nameBase@"'s ("@%target@") ability to deploy objects"); + } + } + case "noinfection": + if(%sender.isAdmin) + { + if(%param != 0 && %param != 1) + { + messageClient(%sender, "MsgNo", "\c2No changes. You supplied an invalid type. Use 1 for on, use 0 for off."); + return; + } + + $Host::NoInfection = %param; + messageAll('MsgAdminForce', "\c1"@%sender.nameBase@" \c2changed \c5NO INFECTION \c2to \c5"@%param@"\c2."); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::NoInfection (no infection) changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::NoInfection@" via ccSet"); + } + case "autosave": + if(%sender.isSuperAdmin) // SA + { + if(%param != 1 && %param != 0) + { + messageClient(%sender, "MsgNo", "\c2Invalid parameters. Input 1 for start, input 0 for end."); + return; + } + + if(%param == 1) + if($SaveBuilding::TimerEnabled) + { + saveBuildingTimerOn(); // This resets. + messageAll('MsgAdminForce', "\c2"@%sender.nameBase@" has reset the autosave timer."); + echo(%sender.nameBase@" ("@%sender@") has reset the autosave timer via ccSet"); + } + else + { + saveBuildingTimer(0, 1, 0, 0); + messageAll('MsgAdminForce', "\c2"@%sender.nameBase@" has enabled the autosave timer."); + echo(%sender.nameBase@" ("@%sender@") has enabled the autosave timer via ccSet"); + } + else + if($SaveBuilding::TimerEnabled) + { + saveBuildingTimerOff(); + messageAll('MsgAdminForce', "\c2"@%sender.nameBase@" has disabled the autosave timer."); + echo(%sender.nameBase@" ("@%sender@") has disabled the autosave timer via ccSet"); + } + else + messageClient(%sender, "", "\c2Cannot disable the autosave timer if it is already disabled!"); + } + case "lockedteams": + if(%sender.isAdmin) + { + if(%param != 1 && %param != 0) + { + messageClient(%sender, "MsgNo", "\c2Invalid parameters. Input 1 for lock, input 0 for unlock."); + return; + } + + $Host::LockedTeams = %param; + messageAll('MsgAdminForce', "\c2"@%sender.nameBase@" has changed locked teams to "@%param@"."); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::LockedTeams (locked, unchangeable teams) has been changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::LockedTeams@" via ccSet"); + } + case "sapass": + if(%sender.isSuperAdmin) + { + $Host::SuperAdminPassword = %param; + messageClient(%sender, "", "\c2Super admin password changed to "@%param); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::SuperAdminPassword (super admin password or SAD) has been changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::SuperAdminPassword@" via ccSet"); + } + case "apass": + if(%sender.isSuperAdmin) + { + $Host::AdminPassword = %param; + messageClient(%sender, "", "\c2Admin password changed to "@%param); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::AdminPassword (admin password or AD) has been changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::AdminPassword@" via ccSet"); + } + case "fairteams": + if(%sender.isAdmin) + { + if(%param != 1 && %param != 0) + { + messageClient(%sender, "MsgNo", "\c2Invalid parameters. Input 1 for enabled, input 0 for disable."); + return; + } + + $Host::FairTeams = %param; + messageAll("", "\c2"@%sender.nameBase@" has changed Fair Teams to "@%param@"."); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + echo("$Host::FairTeams (unbalanced team preventer) has been changed by "@%sender.nameBase@" ("@%sender@") to "@$Host::FairTeams@" via ccSet"); + } + default: + messageClient(%sender, "MsgNo", "\c2Invalid command. Valid commands are canzombie, zombieteam, joinpw, maxplyrs, restrict, zturrets"); + messageClient(%sender, "MsgNo", "\c2noinfection, dir, lockedteams, sapass, apass, fairteams, and autosave"); + } +} + +// Command by Eolk. +function ccA(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if(%cl.isAdmin) + messageClient(%cl, 'MsgYes', "\c3[A]\c2"@%sender.nameBase@": "@%args); + } + logEcho("[ADMIN CHAT]: "@%sender.nameBase@": "@%args); +} + +// Command by Eolk (honestly, even though it looks like someone else's code). +function ccCancelVote(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(Game.ScheduleVote $= "") + { + messageClient(%sender, "MsgNo", "\c2There is no vote to cancel!"); + return; + } + + cancel(Game.scheduleVote); + + Game.votingArgs = ""; + Game.scheduleVote = ""; + Game.kickClient = ""; + clearVotes(); + + messageAll('closeVoteHud', ""); + if(%client.team != 0) + clearBottomPrint(%client); + + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has cancelled the vote."); + messageAll('MsgVoteFailed', ""); + logEcho(%sender.nameBase@" ("@%sender@") has cancelled the vote"); +} + +// Command by Eolk. +function ccBottomPrint(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(%args $= "") + { + messageClient(%sender, 'MsgNo', "\c2Must supply text to display!"); + return; + } + + %print = %args; + %wave = strstr(%args, "~w"); + + if(%wave != -1) + %print = getsubstr(%args, 0, %wave); + + BottomprintAll(%sender.nameBase@": "@%print, 10, 3); + messageAll("MsgAll", "\c2"@ %sender.namebase@": \c4"@%print@"~wgui/objective_Notification.wav"); + logEcho("[BOTTOMPRINT]"@%sender.nameBase@" ("@%sender@"): "@%args); +} + +// Command by Eolk. Modified by Blnukem +function ccCenterPrint(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(%args $= "") + { + messageClient(%sender, 'MsgNo', "\c2Must supply text to display!"); + return; + } + + %print = %args; + %wave = strstr(%args, "~w"); + + if(%wave != -1) + %print = getsubstr(%args, 0, %wave); + + CenterprintAll(%sender.nameBase@": "@%print, 10, 3); + messageAll("MsgAll", "\c2"@ %sender.namebase@": \c4"@%print@"~wgui/objective_Notification.wav"); + logEcho("[BOTTOMPRINT]"@%sender.nameBase@" ("@%sender@"): "@%args); +} + +// Eolk +function ccJailPlayer(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(!$Host::Prison::Enabled) + { + messageClient(%sender, "MsgNo", "\c2Prison is not enabled."); + return; + } + + %target = plnametocid(getword(%args, 0)); + %time = getword(%args, 1); + if(!isObject(%target)) + { + messageClient(%sender, "MsgNo", "\c2Target does not exist."); + return; + } + + if(!isObject(%target.player)) + { + messageClient(%sender, "MsgNo", "\c2Target's player does not exist."); + return; + } + + if((%target.isAdmin && !%sender.isAdmin) || %target.isSuperAdmin) + { + messageClient(%sender, "MsgNo", "\c2You must outrank the target to jail them."); + return; + } + + if(%target == %sender && %target.isJailed && !%sender.isSuperAdmin) + { + messageClient(%sender, "MsgNo", "\c2Can only unjail yourself if you are of super admin status."); + return; + } + + if(%time > 600) + %time = 600; + + %test = %target.isJailed; + JailPlayer(%target, %target.isJailed, %time); + if(!%test) + { + messageClient(%target, "MsgAdminForce", "\c2You have been sentenced to jail."); + messageAllExcept(%target, -1, "MsgAdminForce", "\c3"@%target.nameBase@" \c2has been sentenced to jail."); + logEcho(%target.nameBase@" ("@%target@") was sentenced to jail by "@%sender.nameBase@" ("@%sender@") for "@%time@" seconds"); + } + else + { + messageClient(%target, "MsgAdminForce", "\c2You have had your jail sentence cut short.", %sender.nameBase); + messageAllExcept(%target, -1, "MsgAdminForce", "\c3"@%target.nameBase@" \c2has had their jail sentence cut short."); + logEcho(%target.nameBase@" ("@%target@") was released from jail by "@%sender.nameBase@" ("@%sender@")"); + } +} + +// Command by Eolk. +function ccMute(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2No such person."); + return; + } + + if((%target.isAdmin && !%sender.isSuperAdmin) || %target.isSuperAdmin) + { + messageClient(%sender, "", "\c2Can't mute someone you don't outrank!"); + return; + } + + if(!%target.isSilenced) + { + messageClient(%target, "", "\c2You have been muted."); + %target.isSilenced = 1; + messageClient(%sender, "", "\c3"@%target.nameBase@"\c2 has been muted."); + logEcho(%sender.nameBase@" ("@%sender@") globally muted "@%target.nameBase@" ("@%target@")"); + } + else + { + %target.isSilenced = 0; + messageClient(%sender, "", "\c3"@%target.nameBase@"\c2 has been unmuted."); + logEcho(%sender.nameBase@" ("@%sender@") globally unmuted "@%target.nameBase@" ("@%target@")"); + } +} + +// Chat command by Eolk (special thanks to Krash for bug fixes!) +// <3 +function ccaddteam(%sender) +{ + if(!%sender.isAdmin) + return; + + if(Game.numTeams >= 2 && $AddedMoreTeams != 1) + { + messageClient(%sender, 'MsgNo', "\c2There are already enough teams."); + return; + } + + if(!$AddedMoreTeams) + { + Game.numTeams++; + setSensorGroupCount(Game.numTeams); +// clearVehicleCount(Game.numTeams + 1); // Not sure about this... + $AddedMoreTeams = 1; + $NewTeam = Game.numTeams; + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has added another team."); + warn(%sender.nameBase@" ("@%sender@") has added another team"); + } + else + { + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %obj = ClientGroup.getObject(%i); + if(%obj.team == $NewTeam) + { + Game.forceObserver(%obj, "AdminForce"); + messageClient(%obj, 'MsgYes', "\c2You have been forced into observer due to being on the deleted team."); + } + } + + Game.numTeams--; + setSensorGroupCount(Game.numTeams); + $AddedMoreTeams = 0; + $NewTeam = ""; + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has deleted the new team. All people have been forced into observer."); + warn(%sender.nameBase@" ("@%sender@") has deleted the new team"); + } +} + +// Eolk +function ccSummon(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(%sender.isJailed) + { + messageclient(%sender, "MsgNo", "\c2You cannot do this command while in jail!"); + return; + } + + if(!isObject(%sender.player)) + { + messageclient(%sender, "MsgNo", "\c2You must have a player object in order to do this."); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageclient(%sender, "MsgNo", "\c2Target does not exist."); + return; + } + + if(%target.isJailed) + { + messageclient(%sender, "MsgNo", "\c2Target is jailed."); + return; + } + + if(!isObject(%target.player)) + { + messageclient(%sender, "MsgNo", "\c2Your target has to have a player object in order to do this."); + return; + } + + %x = getword(%sender.player.position, 0); + %y = getword(%sender.player.position, 1); + %z = getword(%sender.player.position, 2) + 2.5; + %newpos = %x SPC %y SPC %z; + + %target.player.setPosition(%newpos); + %target.player.setVelocity("0 0 0"); + + messageClient(%target, "MsgYes", "\c2You have been summoned by\c3 "@%sender.namebase@"\c2."); + messageClient(%sender, "MsgYes", "\c2You have summoned \c3"@%target.namebase@"\c2."); +} + +// Blnukem/Eolk +function ccDesmurf(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Cannot find target."); + return; + } + + if(!%target.isSmurf) + messageClient(%sender, "", "\c2No smurf name detected."); + else + getRealName(%target, %sender); + logEcho(%sender.nameBase@" ("@%sender@") requested non-smurf name of "@%target.nameBase@" ("@%target@")"); +} + +// Eolk +function ccGoto(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(%sender.isJailed) + { + messageclient(%sender, "MsgNo", "\c2You cannot do this command while in jail!"); + return; + } + + if(!isObject(%sender.player)) + { + messageclient(%sender, "MsgNo", "\c2You must have a player object in order to do this."); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageclient(%sender, "MsgNo", "\c2Target does not exist."); + return; + } + + if(%target.isJailed) + { + messageclient(%sender, "MsgNo", "\c2Target is jailed."); + return; + } + + if(!isObject(%target.player)) + { + messageclient(%sender, "MsgNo", "\c2Your target has to have a player object in order to do this."); + return; + } + + %x = getword(%target.player.position, 0); + %y = getword(%target.player.position, 1); + %z = getword(%target.player.position, 2) + 2.5; + %newpos = %x SPC %y SPC %z; + + %sender.player.setPosition(%newpos); + %sender.player.setVelocity("0 0 0"); + + messageClient(%sender, "MsgYes", "\c2You have gone to\c3 "@%target.namebase@"'s \c2location."); + if(%sender.isAdmin) + messageClient(%target, "MsgYes", "\c3"@%sender.nameBase@" \c2has come to you."); +} + +// Eolk +function ccMoveme(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(getwordcount(%args) != 3) + { + messageClient(%sender, "MsgNo", "\c2Your movement was not specified in X Y Z format."); + return; + } + + if(%sender.isJailed) + { + messageclient(%sender, "MsgNo", "\c2You cannot do this command while in jail!"); + return; + } + + %newpos = VectorAdd(%sender.player.position, %args); + %sender.player.setPosition(%newpos); +} + +// Eolk +function ccmoveto(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(getwordcount(%args) != 3) + { + messageClient(%sender, "MsgNo", "\c2Your movement was not specified in X Y Z format."); + return; + } + + if(%sender.isJailed) + { + messageclient(%sender, "MsgNo", "\c2You cannot do this command while in jail!"); + return; + } + + %sender.player.setPosition(%args); +} + +// Blnukem/Eolk +function ccKill(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(%args); + if(%target == %sender) + { + messageClient(%sender, "", "\c2You cannot kill yourself."); + return; + } + + if(%target.isSuperAdmin) + { + messageClient(%sender, "", "\c2You cannot kill Super Admins."); + return; + } + + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Cannot find target."); + return; + } + + if(!isObject(%target.player)) + { + messageClient(%sender, "", "\c2Cannot find target."); + return; + } + + %target.player.scriptkill($DamageType::Idiocy); + messageAll("", "~wfx/misc/bounty_completed.wav"); + warn(%sender.nameBase@" ("@%sender@") killed "@%target.nameBase@" ("@%target@") using admin force"); +} + +// Eolk +function ccForceTeamSpawn(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %team = getword(%args, 0); + %sp = getword(%args, 1); + if(%sp !$= "") + %sp = %sp - 1; // Subtract one to have things make a little more sense. + + if(%team $= "") + { + messageClient(%sender, "MsgNo", "\c2A team must be specified."); + return; + } + + if(%sp $= "") + { + $UseForcedTeamSpawn[%team] = 0; + $ForcedSpawn[%team] = ""; + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has released the forced spawn for team \c3"@%team@"\c2. If you wish to not spawn at the point anymore, use /choosespawn to reset."); + return; + } + + if(!isObject($teamSP[%team, %sp])) + { + messageClient(%sender, "MsgNo", "\c2The spawnpoint specified doesn't exist."); + return; + } + + if(!$teamSP[%team, %sp].active) + { + messageClient(%sender, "MsgNo", "\c2The spawnpoint specified is not powered."); + return; + } + + $UseForcedTeamSpawn[%team] = 1; + $ForcedSpawn[%team] = $teamSP[%team, %sp]; + + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has forced team\c3 "@%team@"\c2 to spawn at spawnpoint \c3"@%sp+1@"\c2."); + warn(%sender.nameBase@" ("@%sender@") forced team"@%team@" to spawn at spawn"@%sp); +} + +// By Eolk +function ccSaveBuilding(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %rad = getword(%args, 0); + %file = getword(%args, 1); + SaveBuilding(%sender, %rad, %file, 1, 0); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 is saving the buildings with a radius of \c3"@%rad@"\c2."); + messageClient(%sender, "", "\c2Building saved to\c3 "@$SaveBuilding::LastFile@"\c2."); + warn(%sender.nameBase@" ("@%sender@", GUID: "@%sender.guid@") attempted save buildings within a radius of "@%radius@" to file "@%file); +} + +// Eolk +function ccLoadBuilding(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + LoadBuilding(%args); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has loaded a building."); + warn(%sender.nameBase@" ("@%sender@", GUID: "@%sender.guid@") loaded file "@%args); +} + +// Blnukem +function ccTurrets(%sender) { +if (!%sender.isAdmin) + return; + if ($TurretEnableOverride) { + $TurretEnableOverride = 0; + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has disabled turrets."); + } + else { + $TurretEnableOverride = 1; + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has enabled turrets."); + } + logEcho("$TurretEnableOverride (turrets work in purebuild) changed to "@$TurretEnableOverride@" by "@%sender.nameBase@" ("@%sender@") using ccTurrets"); +} + +// Blnukem +function ccBuySCG(%sender) { + if (!%sender.isAdmin) + return; + + if(!isObject(%sender.player)) + { + messageClient(%sender, "", "\c2You must be playing in order to get a Super Chaingun."); + return; + } + + messageClient(%sender, "", "\c2A Super Chaingun has been added to your inventory."); + %sender.player.setInventory(SuperChaingun, 1, true); +} + +// Eolk +function ccChangeName(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(getword(%args, 0)); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Invalid Target."); + return; + } + + %name = getwords(%args, 1, getWordCount(%args) - 1); + if(%name $= "") + { + messageClient(%sender, 'MsgNo', "\c2Need to supply a name!"); + return; + } + + if(%name $= "reset") + { + ChangeName(%target, "reset"); + messageClient(%target, "", "\c2Your name has been reset."); + warn(%sender.nameBase@" ("@%sender@") has reset "@%target.nameBase@"'s ("@%target@") name"); + return; + } + + ChangeName(%target, %name); + messageClient(%target, "", "\c2Your new name is \c3"@%name@"\c2."); + warn(%sender.nameBase@" ("@%sender@") changed "@%target.nameBase@"'s ("@%target@") name to "@%name); +} + +// Eolk +function ccSavePlayer(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(getword(%args, 0)); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Invalid target."); + return; + } + + %name = getword(%args, 1); + if(%name $= "") + { + messageClient(%sender, "", "\c2Invalid name."); + return; + } + + %folder = $SaveBuilding::SaveFolder @ %name; // I really don't know if we should check for invalid characters + new FileObject("Building"); // or not. I think Tribes 2 does that automatically. + + Building.openforwrite(%folder); + Building.writeLine("// Saved by \"" @ getField(%sender.nameBase,0) @ "\""); + Building.writeLine("// Created in mission \"" @ $MissionName @ "\""); + Building.writeLine("// Construction " @ $ModVersion); + Building.writeLine(""); + + %group = nameToID("MissionCleanup/Deployables"); // We should really check isObject here... and we will! + if(!isObject(%group)) + { + messageClient(%sender, "", "\c2There are no deployables!"); + return; + } + + for(%i = 0; %i < %group.getCount(); %i++) + { + %depl = %group.getObject(%i); + if(isObject(%depl) && %depl.getOwner() == %target) + { + %towrite = writeBuildingComponent(%depl); // This will return null if invalid. + if(%towrite !$= "") + Building.writeline(%towrite); + } + } + + Building.close(); + Building.delete(); + + messageAll('MsgAdminForce', "\c2"@%sender.nameBase@" is saving "@%target.nameBase@"'s buildings."); + messageClient(%sender, "", "\c2Building saved to "@%folder@"."); + logEcho(%sender.nameBase@" ("@%sender@") saved "@%target.nameBase@"'s pieces to file "@%name); +} + +// Command made by Eolk. +function ccForceGivePieces(%sender, %args) +{ + if(!%sender.isAdmin) + { + messageClient(%sender, "", "\c2This is the admin give pieces. Regular clients have to use /givepieces target."); + return; + } + + %from = (getWord(%args, 0) !$= "orphan" ? plnametocid(getWord(%args, 0)) : "orphan"); + %to = plnametocid(getWord(%args, 1)); + + if(%from !$= "orphan" && !isObject(%from)) + { + messageClient(%sender, "", "\c2First argument - player doesn't exist."); + return; + } + + if(!isObject(%to)) + { + messageClient(%sender, "", "\c2Second argument - player doesn't exist."); + return; + } + + GivePieces(%from, %to); + if(%from !$= "orphan") + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has forced \c3"@%from.nameBase@"\c2 to transfer "@(%from.sex $= "Male" ? "his" : "her")@" pieces to \c3"@%to.nameBase@"\c2."); + else + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has given all orphans to \c3"@%to.nameBase@"\c2."); + logEcho(%sender.nameBase@" ("@%sender@") gave "@(%from == "orphan" ? "all orphaned" : %from.nameBase@"'s ("@%from@")")@" pieces to "@%to.nameBase@" ("@%to@")"); +} + +// Eolk +function ccGiveClientPieces(%sender, %args) +{ + if(!%sender.isAdmin) + { + messageClient(%sender, "", "\c2This command is for admins only."); + return; + } + + %from = getword(%args, 0); + %to = plnametocid(getWord(%args, 1)); + + if(!isObject(%to)) + { + messageClient(%sender, "", "\c2The receiving client doesn't exist."); + return; + } + + GivePieces(%from, %to); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 has given all the pieces of client \c3"@%from@"\c2 to \c3"@%to.nameBase@"."); + logEcho(%sender.nameBase@" ("@%sender@") gave all pieces owned by client "@%from@" to "@%to.nameBase@" ("@%to@")"); +} + +// Blnukem +function ccTest (%sender,%args) +{ + if(!%sender.isAdmin){ + return; + } + + %x = getword(%sender.player.position, 0); + %y = getword(%sender.player.position, 1); + %z = getTerrainHeight(%x SPC %y) + 5; + %rot = "0 0 1 "@getRandom(1,360); + %newpos = %x SPC %y SPC %z; + if(%args $= "Raptor") + { + %type = ScoutFlyer; + } else if(%args $= "Airwing") + { + %type = StrikeFlyer; + } else if(%args $= "Superior") + { + %type = SuperiorityFighter; + } + + if(%args $= "") + { + MessageClient(%sender, "Msg", "\c2Please specifiy the vehicle type."); + } else if(%args !$= "Raptor" && %args !$= "Airwing" && %args !$= "Superior") + { + MessageClient(%sender, "Msg", "\c3"@%args@"\c2 is not a valid vehicle."); + } + + %veh = new FlyingVehicle() + { + dataBlock = ""@%type@""; + position = ""@%newpos@""; + rotation = ""@%rot@""; + team = %sender.team; + }; + + MissionCleanUp.add(%veh); + setTargetSensorGroup(%veh.getTarget(), %sender.team); + commandToClient(%sender,'SetDefaultVehicleKeys', true); + commandToClient(%sender,'SetPilotVehicleKeys', true); + %veh.mountObject(%sender.player,0); + %veh.playAudio(0, MountVehicleSound); + %sender.player.mVehicle = %col; + %datablock = %type; + %datablock.playerMounted(%veh,%sender.player, 0); +} + +// Eolk +function ccNoVote(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Target does not exist."); + return; + } + + if(%target.isAdmin) + { + messageClient(%sender, "", "\c2This command does not affect admins."); + return; + } + + if(Game.scheduleVote !$= "") + { + messageClient(%sender, "", "\c2Cannot do this while a vote is in progress."); + return; + } + + if(%target.canVote) + { + messageClient(%sender, "", "\c3"@%target.nameBase@"'s \c2ability to vote has been taken away."); + %target.canVote = false; + } + else + { + messageClient(%sender, "", "\c3"@%target.nameBase@"'s \c2ability to vote has been given back."); + %target.canVote = true; + } +} diff --git a/Scripts/ModScripts/ChatCommands/HelpCommand.cs b/Scripts/ModScripts/ChatCommands/HelpCommand.cs new file mode 100644 index 0000000..8b94042 --- /dev/null +++ b/Scripts/ModScripts/ChatCommands/HelpCommand.cs @@ -0,0 +1,234 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Help Commands, by Blnukem. +// Note to other ACCM Devs, this method is best for telling clients each command +// and it is pretty clean. Although it can be a pain when adding new commands, +// but I'll be doing that, not you guys. (Since Eolk will fuck it up [Again]) +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +function ccSquadHelp(%sender, %args){ + messageClient(%sender, "", "\c3/CreateSquad [Name] \c2- Create your own squad. (Sergeant Rank Required)"); + messageClient(%sender, "", "\c3/S [Text] \c2- Privatly Chat with your squad."); + messageClient(%sender, "", "\c3/LeaveSquad \c2- Leave the squad you are in."); + messageClient(%sender, "", "\c3/Invite [PlayerName] \c2- Invite a person to your Squad."); + messageClient(%sender, "", "\c3/RequestInvite [SquadName] \c2- Request an Invite to a Squad."); + messageClient(%sender, "", "\c3/Join \c2- Use this to accept an invite to a squad."); + messageClient(%sender, "", "\c3/SOL \c2- Spawn around your squad's leader."); + messageClient(%sender, "", "\c3/ListSquads \c2- Lists all squads in the server."); + messageClient(%sender, "", "\c3/Force [Join/Leave] [SquadName] [PlayerName] \c2- Force a person to leave/join a squad. (General Rank Required)"); +} + +function ccRankHelp(%sender, %args){ + messageClient(%sender, "", "\c3/ItemRestrictions \c2- Gives a list of the weapons/packs that are restricted to ranks."); + messageClient(%sender, "", "\c3/Top5 \c2- Gives a list of players with the highest ranks."); + messageClient(%sender, "", "\c3/ListRanks \c2- Lists all the ranks and the points required to get that rank."); + messageClient(%sender, "", "\c3/CheckStats \c2- Check your current rank and score."); + messageClient(%sender, "", "\c3/CheckStats [PlayerName] \c2- Check the current rank and score of another person."); +} + +function ccItemRestrictions(%sender, %args){ + messageClient(%sender, "", "\c3M79 Grenade Launcher \c2- Points Required:\c3 1250\c2 Rank Required:\c3 Specialist\c2."); + messageClient(%sender, "", "\c3Gauss Cannon \c2- Points Required:\c3 4800\c2 Rank Required:\c3 Master Sergeant\c2."); + messageClient(%sender, "", "\c3Napalm Mortar \c2- Points Required:\c3 6000\c2 Rank Required:\c3 Second Lieutenant\c2."); + messageClient(%sender, "", "\c3Jet Booster Pack \c2- Points Required:\c3 2550\c2 Rank Required:\c3 Staff Sergeant\c2."); + messageClient(%sender, "", "\c3Flame Turret Barrel \c2- Points Required:\c3 1800\c2 Rank Required:\c3 Sergeant\c2."); +} + +function ccListRanks(%sender, %args){ + messageClient(%sender, "", "\c3Private\c2 - Points Required:\c3 250\c2."); + messageClient(%sender, "", "\c3Private First Class\c2 - Points Required:\c3 500\c2."); + messageClient(%sender, "", "\c3Corporal\c2 - Points Required:\c3 1250\c2."); + messageClient(%sender, "", "\c3Specialist\c2 - Points Required:\c3 1800\c2."); + messageClient(%sender, "", "\c3Sergeant\c2 - Points Required:\c3 2550\c2."); + messageClient(%sender, "", "\c3Staff Sergeant\c2 - Points Required:\c3 3600\c2."); + messageClient(%sender, "", "\c3Sergeant First Class\c2 - Points Required:\c3 4800\c2."); + messageClient(%sender, "", "\c3Master Sergeant\c2 - Points Required:\c3 6000\c2."); + messageClient(%sender, "", "\c3Second Lieutenant\c2 - Points Required:\c3 8500\c2."); + messageClient(%sender, "", "\c3First Lieutenant\c2 - Points Required:\c3 9750\c2."); + messageClient(%sender, "", "\c3Captian\c2 - Points Required:\c3 9750\c2."); + messageClient(%sender, "", "\c3Major\c2 - Points Required:\c3 11000\c2."); + messageClient(%sender, "", "\c3Lieutenant Colonel\c2 - Points Required:\c3 15500\c2."); + messageClient(%sender, "", "\c3Colonel\c2 - Points Required:\c3 18500\c2."); + messageClient(%sender, "", "\c3Brigadier General\c2 - Points Required:\c3 22500\c2."); + messageClient(%sender, "", "\c3Major General\c2 - Points Required:\c3 30000\c2."); + messageClient(%sender, "", "\c3Lieutenant General\c2 - Points Required:\c3 50000\c2."); + messageClient(%sender, "", "\c3General\c2 - Points Required:\c3 75000\c2."); + messageClient(%sender, "", "\c3General Of The Army\c2 - Points Required:\c3 90000\c2."); +} + +function ccZombiePointHelp(%sender, %args){ + return; + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + messageClient(%sender, "", "\c3/PlaceZombiePoint [SpawnName] [ZType] [Time] [MaxZombies] [ZombieLimit] \c2- Places an advanced zombie point."); + messageClient(%sender, "", "\c3/ListZombieSpawns \c2- Lists all the advanced zombie points."); + messageClient(%sender, "", "\c3/DisableSpawn [SpawnName] \c2- Disables an advanced zombie point by label."); + messageClient(%sender, "", "\c3/DisableAllSpawns \c2- Disables all advanced zombie points."); + messageClient(%sender, "", "\c3/EnableSpawn [SpawnName] \c2- Enables an advanced zombie point by label."); + messageClient(%sender, "", "\c3/EnableAllSpawns \c2- Enables all advanced zombie points."); + messageClient(%sender, "", "\c3/RemoveSpawn [SpawnName] \c2- Removes an advanced zombie spawn."); + messageClient(%sender, "", "\c3/RemoveAllSpawns \c2- Removes all advanced zombie spawns."); + messageClient(%sender, "", "\c3/GetStatus [SpawnName] \c2- Get the status of a an advanced zombie spawn by label."); + messageClient(%sender, "", "\c3/MarkZombieSpawns \c2- Mark all zombie spawns."); + messageClient(%sender, "", "\c3/SaveSpawns [Radius] [FileName.cs] \c2- Save all of the advanced zombie spawns whithin said radius to said filename."); + messageClient(%sender, "", "\c3/LoadSpawns [Radius] [FileName.cs] \c2- Load zombie spawns whithin said filename."); + messageClient(%sender, "", "\c3/ReplaceSpawn Type[Single/Radius/All] [Arg2] \c2- Replaces advanced spawns with regular spawns. Which ones get replaced depends solely on your input."); +} + +function ccHelp(%sender, %args) +{ + %base = strlwr(getword(%args, 0)); + switch$(%base){ + case "": + messageClient(%sender, "", "\c2Help command options:"); + messageClient(%sender, "", "\c3BasicCommands \c2- \c3BuildingOptions \c2- \c3AdminCommands \c2- \c3SACommands"); + messageClient(%sender, "", "\c3SentinelCommands \c2- \c3ZombieCommands \c2- \c3DroneCommands \c2- \c3QuickCommands"); + messageClient(%sender, "", "\c2Don't understand this command? Type:\c3 /Help Usage \c2to learn how it works."); + } + + switch$(%base){ + case "Usage": + messageClient(%sender, "", "\c0----------------------------------------------------------------------------------------------------------------------------------"); + messageClient(%sender, "", "\c2To use the help command simply type:\c3 /Help \c2then the option you wish to use."); + messageClient(%sender, "", "\c2For Example, type this in global chat:\c3 /Help Test"); + } + + switch$(%base){ + case "Test": + messageClient(%sender, "", "\c0----------------------------------------------------------------------------------------------------------------------------------"); + messageClient(%sender, "", "\c2Good, you succesfully used the\c3 /Help \c2command."); + messageClient(%sender, "", "\c2You can also use Quick Commands if you don't feel like typing\c3 /Help \c2before each option."); + messageClient(%sender, "", "\c2To see the list of Quick Commands, type:\c3 /Help QuickCommands"); + } + + switch$(%base){ + case "QuickCommands": + messageClient(%sender, "", "\c3/BasicCMDS \c2- Basic Commands."); + messageClient(%sender, "", "\c3/BuildOptions \c2- Building Options."); + messageClient(%sender, "", "\c3/ZCMDS \c2- Zombie Commands."); + messageClient(%sender, "", "\c3/SCMDS \c2- Sentinel Commands."); + messageClient(%sender, "", "\c3/DCMDS \c2- Drone Commands."); + messageClient(%sender, "", "\c3/AdminCMDS \c2- Admin Commands."); + messageClient(%sender, "", "\c3/SACMDS \c2- Super Admin Commands."); + } + + switch$(%base){ + case "BuildingOptions": + messageClient(%sender, "", "\c2Use the building manager in the Lobby to save your pieces."); + messageClient(%sender, "", "\c3/Delmypieces \c2- Delete all of your pieces."); + messageClient(%sender, "", "\c3/Objectscale [X Y Z] \c2- Basic scaling function."); + messageClient(%sender, "", "\c3/Getscale \c2- Get the scale of an object."); + messageClient(%sender, "", "\c3/Pos [X Y Z] \c2- This will move an object in X Y Z format."); + messageClient(%sender, "", "\c3/GetPos \c2- This gets the postition of an object."); + messageClient(%sender, "", "\c3/RankHelp \c2- Tells you basic information on ranks."); + messageClient(%sender, "", "\c3/SetFreq [#] \c2- Set your power frequency."); + messageClient(%sender, "", "\c3/ObjectName [Name] \c2- Sets a name to a deployable."); + messageClient(%sender, "", "\c3/Radius [Radius] \c2- Sets the radius for Switches/Tripwires."); + messageClient(%sender, "", "\c3/Cloak \c2- Makes your pieces invisible."); + } + + switch$(%base){ + case "BasicCommands": + messageClient(%sender, "", "\c3![PlayerName] [Message] \c2- Private messaging."); + messageClient(%sender, "", "\c3/Opendoor \c2- Point at a door and use this command to open it. (You can also use /Open)"); + messageClient(%sender, "", "\c3/Opendoor [Password] \c2- Point at a door and use this command to open it if it\'s passworded."); + messageClient(%sender, "", "\c3/Setdoorpass [Password] \c2- Point at a door and use this to set it\'s password."); + messageClient(%sender, "", "\c3/ChooseSpawn [#] \c2- Choose a selected spawnpoint to spawn there."); + messageClient(%sender, "", "\c3/ListSpawns \c2- Displays all spawnpoints on your team."); + messageClient(%sender, "", "\c3/Hack Help \c2- Tells you how to Hack enemy teleporters."); + messageClient(%sender, "", "\c3/SquadHelp \c2- Tells you how to use squad commands."); + messageClient(%sender, "", "\c3/RankHelp \c2- Tells you basic information on ranks."); + messageClient(%sender, "", "\c3/Tips \c2- This will give you a random tip."); + } + + switch$(%base){ + case "SentinelCommands": + if (!%sender.isAdmin){ + messageClient(%sender, "", "\c2Only Admins can use this command. ~wfx/misc/misc.error.wav"); + %sender.player.scriptkill($DamageType::Idiocy); + return; + } + messageClient(%sender, "", "\c2Command\'s Temporarily Disabled."); + return; + } + + switch$(%base){ + case "ZombieCommands": + if ((!%sender.isAdmin) && (!%sender.isZombieKeeper)){ + messageClient(%sender, "", "\c2Only Admins or Zombie Keepers can use this command. ~wfx/misc/misc.error.wav"); + %sender.player.scriptkill($DamageType::Idiocy); + return; + } + messageClient(%sender, "", "\c3/BuyZpack \c2- Buy a zombie pack."); + messageClient(%sender, "", "\c3/MakeZLord [PlayerName] \c2- Make the person a lord zombie."); + messageClient(%sender, "", "\c3/MakeRapier [PlayerName] \c2- Make the person a regular zombie."); + messageClient(%sender, "", "\c3/Stalk [PlayerName] [ZType] Difficulty[Cool/Light/Medium/Heavy] \c2- Spawn zombies around a target."); + messageClient(%sender, "", "\c3/Cure [PlayerName] \c2- Cures a person."); + messageClient(%sender, "", "\c3/Infect [PlayerName] \c2- Infect the target with the zombie virus."); + messageClient(%sender, "", "\c3/KillZombies \c2- Kills all zombies and infected."); + messageClient(%sender, "", "\c3/ZDetectDist [Radius] \c2- Set how large the zombie detection distance is."); + messageClient(%sender, "", "\c3/SemiInfect [PlayerName] \c2- Infect that person with the altrenative virus."); +// messageClient(%sender, "", "\c3/ZombiePointHelp \c2- Displays commands fo advanced zombie spawn points."); + return; + } + + switch$(%base){ + case "DroneCommands": + if (!%sender.isAdmin){ + messageClient(%sender, "", "\c2Only Admins can use this command. ~wfx/misc/misc.error.wav"); + %sender.player.scriptkill($DamageType::Idiocy); + return; + } + messageClient(%sender, "", "\c3/DroneBattle [Single/Battle/Custom] \c2- Basic Command where you can spawn either single drones, full battles, or even customize battles."); + return; + } + + switch$(%base){ + case "AdminCommands": + if (!%sender.isAdmin){ + messageClient(%sender, "", "\c2Only Admins can use this command. ~wfx/misc/misc.error.wav"); + %sender.player.scriptkill($DamageType::Idiocy); + return; + } + messageClient(%sender, "", "\c3/JailPlayer [PlayerName] [Time] \c2- Sends a specified player to jail for a set amount of time."); + messageClient(%sender, "", "\c3/AddTeam \c2- Add/Removes a second team."); + messageClient(%sender, "", "\c3/Gag [PlayerName] [Time] \c2- Silences an annoying player."); + messageClient(%sender, "", "\c3/CancelVote \c2- Cancels a vote."); + messageClient(%sender, "", "\c3/DelPieces [PlayerName] \c2- Deletes a specified player\'s pieces."); + messageClient(%sender, "", "\c3/Kill [PlayerName] \c2- Kill someone."); + messageClient(%sender, "", "\c3/ChangeName [PlayerName] [NewName] \c2- Changes the target\'s name."); + messageClient(%sender, "", "\c3/Goto [PlayerName] \c2- Go directly to a specified person."); + messageClient(%sender, "", "\c3/Summon [PlayerName] \c2- Summon a specified person."); + messageClient(%sender, "", "\c3/Moveto [X Y Z] \c2- Go directly to a desired location."); + messageClient(%sender, "", "\c3/Moveme [X Y Z] \c2- Move on your X, Y or Z axis."); + messageClient(%sender, "", "\c3/BottomPrint [Text] \c2- Send a message on the bottom of the screen."); + messageClient(%sender, "", "\c3/CenterPrint [Text] \c2- Send a message in the center of the screen."); + messageClient(%sender, "", "\c3/ForceTeamSpawn [TeamName] [SpawnNumber] \c2- Forces an entire team to spawn at one spawnpoint."); + messageClient(%sender, "", "\c3/SaveBuilding [Radius] [FileName.cs] \c2- Save buildings in the server."); + messageClient(%sender, "", "\c3/LoadBuilding [FileName.cs] \c2- Load a building file."); + messageClient(%sender, "", "\c3/A [Message] \c2- Admin private messaging."); + messageClient(%sender, "", "\c3/BuySCG \c2- Force a SCG into your inventory."); + messageClient(%sender, "", "\c3/DeSmurf \c2- Remove a client\'s smurf name. And reset to their normal name."); + messageClient(%sender, "", "\c3/Turrets \c2- Enables/disables turrets."); + return; + } + + switch$(%base){ + case "SACommands": + if (!%sender.isSuperAdmin){ + messageClient(%sender, "", "\c2Only Super Admins can use this command. ~wfx/misc/misc.error.wav"); + %sender.player.scriptkill($DamageType::Idiocy); + return; + } + messageClient(%sender, "", "\c3/Admin [PlayerName] \c2- Force someone to become Admin."); + messageClient(%sender, "", "\c3/SuperAdmin [PlayerName] \c2- Force someone to become Super Admin."); + messageClient(%sender, "", "\c3/Info [PlayerName] \c2- Get basic info on a specified player."); + messageClient(%sender, "", "\c3/Echo [PlayerName] \c2- Silently mute someone without them knowing."); + messageClient(%sender, "", "\c3/Shred [PlayerName] \c2- Put someone in the ACCM paper shredder."); + messageClient(%sender, "", "\c3/SA [Message] \c2- Super Admin private messaging."); + return; + } +} diff --git a/Scripts/ModScripts/ChatCommands/SACommands.cs b/Scripts/ModScripts/ChatCommands/SACommands.cs new file mode 100644 index 0000000..a68767d --- /dev/null +++ b/Scripts/ModScripts/ChatCommands/SACommands.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// ACCM Super Admin Commands +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Command by Eolk +function ccSA(%sender, %args) +{ + if(!%sender.isSuperAdmin) + return; + + for(%i = 0; %i < ClientGroup.getCount(); %i++) + { + %cl = ClientGroup.getObject(%i); + if(%cl.isSuperAdmin) + messageClient(%cl, 'MsgYes', "\c3[SA]\c2"@%sender.nameBase@": "@%args); + } + logEcho("[SUPERADMIN CHAT]: "@%sender.nameBase@": "@%args); +} + +// Command by Blnukem +function ccDeadmin(%sender, %args) +{ + if(!%sender.isSuperAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Unable to find target."); + return; + } + + if(%target.isSuperAdmin || !%target.isAdmin) + { + messageClient(%sender, "", "\c2Target has incorrect status of adminship."); + return; + } + + %target.isAdmin = false; + %target.isSuperAdmin = false; + messageAll( 'MsgStripAdminPlayer', '\c3%1 \c2has de-admined\c3 %2\c2.', %sender.name, %target.name, %target ); + %target.player.setInventory("SuperChaingun", 0); + %target.player.setInventory("SuperChaingunAmmo", 0); + logEcho(%sender.nameBase@" ("@%sender@") de-admin'd "@%target.nameBase@" ("@%target@")"); +} + +// Command by Blnukem +function ccAdmin(%sender, %args) +{ + if(!%sender.isSuperAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Unable to find target."); + return; + } + + if(%target.isAdmin) + { + messageClient(%sender, "", "\c2Target has incorrect status of adminship."); + return; + } + + Game.voteAdminPlayer(%sender, %target); + %target.player.setInventory("SuperChaingun", 1); + %target.player.setInventory("SuperChaingunAmmo", 999); + logEcho(%sender.nameBase@" ("@%sender@") gave admin to "@%target.nameBase@" ("@%target@")"); +} + +// Command by Blnukem +function ccSuperAdmin(%sender, %args) +{ + if(!%sender.isSuperAdmin) + return; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Unable to find target."); + return; + } + + if(%target.isSuperAdmin) + { + messageClient(%sender, "", "\c2Target has incorrect status of adminship."); + return; + } + + %target.isAdmin = true; + %target.isSuperAdmin = true; + %name = getTaggedString(%target.name); + messageAll( 'MsgSuperAdminPlayer', '\c3%3 \c2has made\c3 %2 \c2a super admin.', %target, %name, %sender.nameBase ); + %target.player.setInventory("SuperChaingun", 1); + %target.player.setInventory("SuperChaingunAmmo", 999); + logEcho(%sender.nameBase@" ("@%sender@") gave super admin to "@%target.nameBase@" ("@%target@")"); +} diff --git a/Scripts/ModScripts/ChatCommands/ZombieCommands.cs b/Scripts/ModScripts/ChatCommands/ZombieCommands.cs new file mode 100644 index 0000000..3242471 --- /dev/null +++ b/Scripts/ModScripts/ChatCommands/ZombieCommands.cs @@ -0,0 +1,1239 @@ +//------------------------------------------------------------------------------ +// Zombie Commands: +//------------------------------------------------------------------------------ + +if($Host::KeepersGetMakerAbility $= "") + $Host::KeepersGetMakerAbility = 1; + +if($Host::AllowKeeperPlayerVotes $= "") + $Host::AllowKeeperPlayerVotes = 1; + +// CCM Dev Team +function ccBuyZPack(%sender,%args) +{ + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2Zombies are prohibited in Construction."); + return; + } + + if((%sender.isAdmin || %sender.isSuperAdmin || %sender.isZombieKeeper) && isObject(%sender.player)) + { + if(%sender.player.getMountedImage($Backpackslot) !$= "") + %sender.getControlObject().throwPack(); + %sender.player.setinventory(ZSpawnDeployable, 1, true); + messageClient(%sender, "", "\c2Be sure to only make a few zombie spawns, not a million."); + } +} + +// Eolk +function ccInfect(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot infect someone in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target.player)) + { + messageClient(%sender, 'MsgYes', "\c2Target player does not exist."); + return; + } + + if (%target==%sender) + { + %target.player.Infected = 1; + %target.player.InfectedLoop = schedule(10, %target.player, "InfectLoop", %target.player); + messageClient(%sender, 'MsgYes', "\c2You have Infected yourself with the Zombie virus!"); + logEcho(%sender.nameBase@" ("@%sender@") remotely infected self"); + return; + } + + %target.player.Infected = 1; + %target.player.InfectedLoop = schedule(10, %target.player, "InfectLoop", %target.player); + messageClient(%target, 'MsgYes', "\c2You've been injected with the zombie infection!"); + messageClient(%sender, 'MsgYes', "\c2You've infected: \c3"@%target.nameBase@" \c2with the zombie virus."); + logEcho(%sender.nameBase@" ("@%sender@") remotely infected "@%target.nameBase@" ("@%target@")"); +} + +// Eolk +function ccMakeZombie(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot make a person a Zombie in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%target == %sender) + { + makePersonZombie(%target.player.getTransform(), %target, 0); + messageClient(%sender, "MsgAdminForce", "\c2You made yourself into a Zombie."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made self a zombie"); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target.player)) + { + messageClient(%sender, 'MsgYes', "\c2Target player does not exist."); + return; + } + + MakePersonZombie(%target.player.getTransform(), %target, 0); + messageClient(%sender, "MsgAdminForce", "\c2You made\c3 "@%target.nameBase@" \c2a Zombie."); + messageClient(%target, "MsgAdminForce", "\c3"@%sender.nameBase@" \c2has made you a Zombie."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made "@%target.nameBase@" ("@%target@") a zombie"); +} + +// Eolk +function ccMakeZLord(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot make a person a Zombie Lord in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%target == %sender) + { + makePersonZombie(%target.player.getTransform(), %target, 1); + messageClient(%sender, "MsgAdminForce", "\c2You made yourself into a Zombie Lord."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made self a zombie lord"); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target.player)) + { + messageClient(%sender, 'MsgYes', '\c2Target player does not exist.'); + return; + } + if(%target.player.getMountedImage($Weaponslot) !$= "") + %target.getControlObject().throwWeapon(); + makePersonZombie(%target.player.getTransform(), %target, 1); + messageClient(%sender, "MsgAdminForce", "\c2You made\c3 "@%target.nameBase@" \c2a Zombie Lord."); + messageClient(%target, "MsgAdminForce", "\c3"@%sender.nameBase@" \c2has made you a Zombie Lord."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made "@%target.nameBase@" ("@%target@") a zombie lord"); +} + +// Eolk +function ccMakeRapier(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot make a person a Zombie Rapier in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%target == %sender) + { + makePersonZombie(%target.player.getTransform(), %target, 2); + messageClient(%sender, "MsgAdminForce", "\c2You made yourself into a Zombie Rapier."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made self a zombie rapier"); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target.player)) + { + messageClient(%sender, 'MsgYes', '\c2Target player does not exist.'); + return; + } + + makePersonZombie(%target.player.getTransform(), %target, 2); + messageClient(%sender, "MsgAdminForce", "\c2You made\c3 "@%target.nameBase@" \c2a Zombie Rapier."); + messageClient(%target, "MsgAdminForce", "\c3"@%sender.nameBase@" \c2has made you a Zombie Rapier."); + logEcho(%sender.nameBase@" ("@%sender@") remotely made "@%target.nameBase@" ("@%target@") a zombie rapier"); +} + +// Eolk +function ccMakeDemon(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot make a person a Demon in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%target == %sender) + { + makePersonZombie(%target.player.getTransform(), %target, 3); + messageClient(%sender, "MsgAdminForce", "\c2You made yourself into a Demon."); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target.player)) + { + messageClient(%sender, 'MsgYes', '\c2Target player does not exist.'); + return; + } + + if(%target.player.getMountedImage($Weaponslot) !$= "") + %target.getControlObject().throwWeapon(); + + makePersonZombie(%target.player.getTransform(), %target, 3); + messageClient(%sender, "MsgAdminForce", "\c2You made\c3 "@%target.nameBase@" \c2a Demon."); + messageClient(%target, "MsgAdminForce", "\c3"@%sender.nameBase@" \c2has made you a Demon."); +} + +// Eolk/Blnukem +function ccKillZombies(%sender) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + MessageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has killed all of the zombies and cured all infected people."); + logEcho(%sender.nameBase@" ("@%sender@") killed all zombies and cured all infected people"); + %zgroup = nameToID("MissionCleanup/ZombieGroup"); + %zcount = %zgroup.getCount(); + for (%i = 0; %i < %zcount; %i++) + { + %zombie = %zgroup.getObject(%i); + if (isObject(%zombie)) + { + if (%obj.infected || strstr(%zombie.getDatablock().getName(), "Zombie") != -1) + { + %zombie.scriptkill($DamageType::Idiocy); + } + } + } + + %ccount = ClientGroup.GetCount(); + for(%i = 0; %i < %ccount; %i++) + { + %client = ClientGroup.getObject(%i); + if (isObject(%client)) + { + if (%client.player.infected) + { + MessageClient(%Client, "Msg", "\c2You were cured of the zombie virus."); + Cure(%client); + logEcho(%client.nameBase@" was cured via ccKillZombies"); + } + } + } +} + +// Eolk +function ccPlaceZombiePoint(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot place Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame" && !%sender.isMapAdmin) + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + %name = firstword(%args); + %ztype = strlwr(getword(%args, 1)); + %time = getword(%args, 2); + %maxzombs = getword(%args, 3); + %limitzombs = getword(%args, 4); + if(%name $= "") + { + messageClient(%sender, "MsgNo", "\c2Invalid Name. Must be lower than 25 characters."); + return; + } + + // I figured out we should do this after the check. + %name = stripchars(%name, " "); + %name = getsubstr(%name, 0, 25); // Ha, ha! + + if(%ztype !$= "regular" && %ztype !$= "ravenger" && %ztype !$= "lord" && %ztype !$= "demon" && %ztype !$= "rapier" && %ztype !$= "random") // TODO: make progressive AI. + { + messageClient(%sender, "MsgNo", "\c2Invalid zombie type. Types are regular, ravenger, lord, demon, rapier, and random."); + return; + } + + if(%time < 4 || %time > 60) + { + messageClient(%sender, "MsgNo", "\c2That time is invalid. Must be more than four but less than sixty."); + return; + } + + if(%maxzombs $= "" || %maxzombs == 0 || %maxzombs < -1) + { + messageClient(%sender, "MsgNo", "\c2Invalid number of zombies. Must be more than 0. Enter -1 for infinite."); + return; + } + + if(%limitzombs == 0 || %limitzombs < -1) + { + messageClient(%sender, "MsgNo", "\c2Invalid zombie limit. Must be more than 0. Enter -1 for infinite."); + return; + } + + %obj = new ScriptObject() + { + class = "ZombiePoint"; + }; + + ////////////////////////////////////// + // Note: Anything added here must be / + // considered in the save function! // + ////////////////////////////////////// + %obj.spawnTransform = %sender.player.getTransform(); + %obj.type = %ztype; + %obj.timeout = %time; + %obj.label = %name; + %obj.maximumZombies = %maxzombs; + %obj.zombieLimit = %limitzombs; + %obj.disabled = 1; // disabled by default. Since we don't have a global variable anymore, we have to do this... + %obj.zombieList = ""; + addToZombiePointGroup(%obj); + messageClient(%sender, "MsgYes", "\c2Zombie Spawn point placed successfully."); +} + +// Eolk +function ccListZombieSpawns(%sender) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + messageClient(%sender, "MsgZombie", "\c2Zombie Spawn Points:"); + %genericCount = 0; + %count = 0; + %grp = nameToId("MissionCleanup/ZombiePoints"); + if(!isObject(%grp)) + { + messageClient(%sender, "MsgZombie", "\c2Currently none."); + return; + } + + for(%i = 0; %i < %grp.getCount(); %i++) + { + %obj = %grp.getObject(%i); + %name = %obj.label; + if(%name $= "") // there's no name... + { + %name = "GenericSpawnName"@%genericCount; // grab a generic one... + %obj.label = %name; + %genericCount++; + } + + if((strlen(%list[%count]) + strlen(%name)) > 256) // if will be more than 256... + { + %count++; + %list[%count] = %name; // add a new variable and smack the name there... + } + else // if it won't be more than 256... + %list[%count] = %list[%count] SPC %name; // just smack it with the others... + } + + // Ok, now that that is done, we output it... + for(%x = 0; %list[%x] !$= ""; %x++) + { + messageClient(%sender, "MsgZombie", %list[%x]); + } +} + +// Eolk +function ccDisableSpawn(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%args $= "") + { + messageClient(%sender, "MsgNo", "\c2Invalid label."); + return; + } + + %obj = zombieSpawnByName(%args); + if(%obj == 0) + { + messageClient(%sender, "MsgNo", "\c2Spawn does not exist."); + return; + } + + %obj.disabled = 1; + if(isEventPending(%obj.zombieLoop)) + cancel(%obj.zombieLoop); + + %obj.zombieLoop = ""; + messageClient(%sender, "msgYes", "\c2Point\c3 "@%args@" ("@%obj@") \c2has been disabled."); + %obj.setStatus("Disabled by user request."); +} + +// Eolk +function ccDisableAllSpawns(%sender) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame" && !%sender.isMapAdmin) + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + // The old method of toggling a global variable was taken out... + // this new way is more compatible with other functions, and a lot more flexible... + %grp = nameToId("MissionCleanup/ZombiePoints"); + for(%i = 0; %i < %grp.getCount(); %i++) + { + %obj = %grp.getObject(%i); + if(isEventPending(%obj.zombieLoop)) + cancel(%obj.zombieLoop); + %obj.disabled = 1; + } + + messageClient(%sender, "MsgYes", "\c2All points have been disabled."); +} + +// Eolk +function ccEnableSpawn(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%args $= "") + { + messageClient(%sender, "MsgNo", "\c2Invalid label."); + return; + } + + %obj = zombieSpawnByName(%args); + if(%obj == 0) + { + messageClient(%sender, "MsgNo", "\c2Spawn does not exist."); + return; + } + + %obj.disabled = 0; + %obj.spawnedZombies = 0; + %obj.StartSpawnLoop(); + + messageClient(%sender, "MsgYes", "\c2Point\c3 "@%args@" ("@%obj@") \c2has been enabled."); +} + +// Eolk +function ccEnableAllSpawns(%sender) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame" && !%sender.isMapAdmin) + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + // The old method of toggling a global variable was taken out... + // this new way is more compatible with other functions, and a lot more flexible... + %grp = nameToId("MissionCleanup/ZombiePoints"); + for(%i = 0; %i < %grp.getCount(); %i++) + { + %obj = %grp.getObject(%i); + %obj.disabled = 0; + %obj.spawnedZombies = 0; + %obj.StartSpawnLoop(); + } + + messageClient(%sender, "MsgYes", "\c2All points have been enabled."); +} + +// Eolk +function ccRemoveSpawn(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if($CurrentMission $= "Affliction") + { + messageClient(%sender, "", "\c2You cannot remove zombie spawns on this map."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%args $= "") + { + messageClient(%sender, "MsgNo", "\c2Invalid label."); + return; + } + + %obj = zombieSpawnByName(%args); + if(%obj == 0) + { + messageClient(%sender, "MsgNo", "\c2Spawn does not exist."); + return; + } + + if(isEventPending(%obj.zombieLoop)) + cancel(%obj.zombieLoop); + + for(%i = -1; %i < getwordcount(%obj.zombieList); %i++) + { + %zombie = getword(%obj.zombieList, %i); + if(isObject(%zombie)) + %zombie.scriptkill(0); + } + + %obj.schedule(500, "delete"); + messageClient(%sender, "MsgYes", "\c2Point\c3 "@%args@" ("@%obj@") \c2has been removed."); +} + +// Eolk +function ccRemoveAllSpawns(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if($CurrentMission $= "Affliction") + { + messageClient(%sender, "", "\c2You cannot remove zombie spawns on this map."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + %group = nameToID("MissionCleanup/ZombiePoints"); + if(!isObject(%group)) + { + messageClient(%sender, "MsgNo", "\c2There are no points to remove!"); + return; + } + + for(%i = 0; %i < %group.getCount(); %i++) + { + %obj = %group.getObject(%i); + ccRemoveSpawn(%sender, %obj.label); + } +} + +// Eolk +function ccGetStatus(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%args $= "") + { + messageClient(%sender, "MsgNo", "\c2Invalid label."); + return; + } + + %obj = zombieSpawnByName(%args); + if(%obj == 0) + { + messageClient(%sender, "MsgNo", "\c2Spawn does not exist."); + return; + } + + messageClient(%sender, "MsgYes", "\c2Status of point\c3 "@%args@" ("@%obj@")\c2:"); + messageClient(%sender, "MsgYes", "\c2Current Status:\c3 "@%obj.status@" \c2(\c3"@(%obj.disabled == 1 ? "disabled" : "enabled")@"\c2), Zombie Spawning Position:\c3 "@posFromTransform(%obj.spawnTransform)@"\c2, Zombie Spawning Rotation:\c3 "@rotFromTransform(%obj.spawnTransform)@"\c2..."); + messageClient(%sender, "MsgYes", "\c2Zombie Type:\c3 "@%obj.type@"\c2, Maximum Zombies:\c3 "@%obj.maximumZombies@"\c2, Already Spawned Zombies:\c3 "@%obj.spawnedZombies@"\c2, Timeout Between Spawn:\c3 "@%obj.timeout@"\c2, Child Zombies:\c3 "@getwordcount(%obj.zombieList)@"\c2."); +} + +// Eolk +function ccMarkZombieSpawns(%sender, %args) +{ + if(!%sender.isAdmin && !%sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2There shouldn\'t be any Zombie Spawn points in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + %group = nameToID("MissionCleanup/ZombieWaypoints"); + if(!$SpawnsMarked) + { + if(!isObject(%group)) + { + %group = new SimGroup("ZombieWaypoints"); + MissionCleanup.add(%group); + } + + %grp = nameToId("MissionCleanup/ZombiePoints"); + if(!isObject(%grp)) + { + messageClient(%sender, "MsgZombie", "\c2There are no spawn points."); + $SpawnsMarked = 0; // This is in case of error in mission cleanup. The group gets deleted. + return; + } + + for(%x = 0; %x < %grp.getCount(); %x++) + { + %point = %grp.getObject(%x); + %o = new WayPoint() { + position = posFromTransform(%point.spawnTransform); + rotation = rotFromTransform(%point.spawnTransform); + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + name = %point.label; + team = "0"; + }; + + %group.add(%o); + } + + $SpawnsMarked = 1; + messageAll("MsgAdminForce", "\c3"@%sender.nameBase@" \c2has marked all zombie spawns."); + } + else + { + if(!isObject(%group)) + { + messageClient(%sender, "MsgNo", "\c2Zombie waypoint group not found (probably because markers have not been placed yet). Cannot remove markers."); + return; + } + + for(%i = 0; %i < %group.getCount(); %i++) + { + %obj = %group.getObject(%i); + %obj.schedule(500, "delete"); + } + + $SpawnsMarked = 0; + messageAll("MsgAdminForce", "\c3"@%sender.nameBase@" \c2has removed all the zombie spawn markers."); + } +} + +// Eolk +function ccStalk(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2Stalking has been disabled in Construction."); + return; + } + + %target = plnametocid(getword(%args, 0)); + if(!isObject(%target)) + { + messageClient(%sender, 'MsgNo', '\c2Unable to find specified target.'); + return; + } + + if(isObject(%target.stalkMonitor)) + { + %target.stalkMonitor.doneStalking(); + messageClient(%sender, 'MsgYes', '\c3%1 \c2is now no longer being stalked.', %target.nameBase); + return; + } + + %zombies = strlwr(getword(%args, 1)); + if(%zombies !$= "regular" && %zombies !$= "ravenger" && %zombies !$= "lord" && %zombies !$= "demon" && %zombies !$= "rapier" && %zombies !$= "random") + { + messageClient(%sender, 'MsgNo', '\c2Invalid zombie type. The zombie types are regular, ravenger, lord, demon, and rapier. You may also type random for a randomized zombie type upon spawn.'); + return; + } + + %swarm = strlwr(getword(%args, 2)); + if(%swarm !$= "light" && %swarm !$= "medium" && %swarm !$= "heavy" && %swarm !$= "random" && %swarm !$= "cool") + { + messageClient(%sender, 'MsgNo', '\c2Invalid swarm type. The valid types are light, medium, and heavy. Random may also be used, and Cool may be used if you want the swarm to go up, then down again.'); + return; + } + + AssignStalkMonitor(%target, %zombies, %swarm); + %target.stalkMonitor.stalkLoop(); + messageClient(%sender, 'MsgYes', '\c3%1 \c2is now being stalked.', %target.nameBase); + logEcho(%sender.nameBase@" ("@%sender@") is stalking "@%target.nameBase@" ("@%target@")"); +} + +// Eolk +function ccSemiInfect(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot infect someone in Construction."); + return; + } + + if(Game.class $= "ZombieGame") + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + if(%args $= "") + %args = %sender.nameBase; + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "MsgNo", "\c2Unable to find target."); + return; + } + + if(!isObject(%target.player)) + { + messageClient(%sender, "MsgNo", "\c2Target has no player object."); + return; + } + + if(%target.isJailed) + { + messageClient(%sender, "MsgNo", "\c2Target is jailed."); + return; + } + + if(%target.infected) + { + messageClient(%sender, "MsgNo", "\c2Cannot perform upon infected people."); + return; + } + + if(%target.player.isZombie) + { + messageClient(%sender, "MsgNo", "\c2Cannot perform upon zombies."); + return; + } + + %target.player.canZKill = 1; + ZombieBloodLust(%target.player, 0); + messageClient(%target, "MsgYes", "\c2You caught the alternative virus. You still look human, but you can infect others."); + messageClient(%sender, "MsgYes", "\c2Infecting\c3 "@%target.nameBase@" \c2with the alternative zombie virus."); + logEcho(%sender.nameBase@" ("@%sender@") remotely semi-infected "@%target.nameBase@" ("@%target@")"); +} + +// Eolk/Blnukem +function ccCure(%sender, %args) +{ + if(!%sender.isAdmin) + if((%sender.isZombieKeeper && !$Host::KeepersGetMakerAbility) || !%sender.isZombieKeeper) + { + messageClient(%sender, "", "\c2This option has been disabled for keepers."); + return; + } + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2Curing has been disabled since there should be no Zombies in Construction."); + return; + } + + if(Game.class $= "ZombieGame" && !%sender.isMapAdmin) + { + messageClient(%sender, "", "\c2A game is currently in progress."); + return; + } + + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Cannot find target."); + return; + } + + if(!isObject(%target.player)) + { + messageClient(%sender, "", "\c2Target has no player object."); + return; + } + + if(!%target.player.infected) + { + messageClient(%sender, "", "\c2Target is not infected."); + return; + } + + Cure(%target); + messageClient(%sender, "", "\c3"@%target.nameBase@" \c2has been cured."); + messageClient(%target, "", "\c2You were cured of the zombie virus!"); + logEcho(%sender.nameBase@" ("@%sender@") remotely infected "@%target.nameBase@" ("@%target@")"); +} + +// Eolk +function ccSaveSpawns(%sender, %args) +{ + return; + if(!%sender.isAdmin && %sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot Save Zombie Spawns in Construction."); + return; + } + + %radius = getword(%args, 0); + %filename = getword(%args, 1); + + if(%radius < 1) + %radius = 1000000; + if(%filename $= "") + { + messageClient(%sender, "", "\c2You need to specify a filename!"); + return; + } + + if(!isObject(%sender.player)) + if(isObject(%sender.camera)) + %transform = %sender.camera.getTransform(); + else + { + messageClient(%sender, "", "\c2You have to have a camera or a player to do this."); + return; + } + else + %transform = %sender.player.getTransform(); + + %dir = "ZombieSpawns/"@%filename; + %obj = new FileObject(); + if(%obj.openforwrite(%dir)) + { + %obj.writeLine("// ZOMBIE POINT SAVE FILE"); + %obj.writeLine("// Saved in "@$ACCMVersion@"."); + %obj.writeLine("// Saved by "@%sender.nameBase@" (GUID: "@%sender.guid@")"); // Prone to mistakes. + %obj.writeLine("// Code begins below:"); + + %pos = posFromTransform(%transform); + %grp = nameToId("MissionCleanup/ZombiePoints"); + for(%i = 0; %i < %grp.getCount(); %i++) + { + %point = %grp.getObject(%i); + if(VectorDist(%pos, posFromTransform(%point.spawnTransform)) < %radius) + { + %spawnT = %point.spawnTransform; + %type = %point.type; + %time = %point.timeout; + %label = %point.label; + %maxZombs = %point.maximumZombies; + %limZombs = %point.zombieLimit; + + // Maybe I should just use .save(); + %obj.writeline("%n = new ScriptObject() {"); + %obj.writeline(" class = \"ZombiePoint\";"); + %obj.writeline("};"); + %obj.writeline("%n.spawnTransform = \""@%spawnT@"\";"); + %obj.writeline("%n.type = \""@%type@"\";"); + %obj.writeline("%n.timeout = \""@%time@"\";"); + %obj.writeline("%n.label = \""@%label@"\";"); + %obj.writeline("%n.maximumZombies = \""@%maxZombs@"\";"); + %obj.writeline("%n.zombieLimit = \""@%limZombs@"\";"); + %obj.writeline("%n.disabled = \"1\";"); // Automatically disabled. + %obj.writeline("addToZombiePointGroup(%n);"); + } + } + } + else + messageClient(%sender, "", "\c2Error opening file."); + %obj.close(); + %obj.delete(); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2is saving a zombie spawnpoint file."); +} + +// Eolk +function ccLoadSpawns(%sender, %args) +{ + return; + if(!%sender.isAdmin && %sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot load Zombie Spawns in Construction."); + return; + } + + %dir = "ZombieSpawns/"@%args; + if(isFile(%dir)) + { + compile(%dir); + exec(%dir); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2is attempting to load a zombie spawnpoint file."); + } + else + messageClient(%sender, "", "\c2Invalid file."); +} + +// Eolk +function ccReplaceSpawn(%sender, %args) +{ + if(!%sender.isAdmin && %sender.isZombieKeeper) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot replace Zombie Spawns in Construction."); + return; + } + + %type = strlwr(getword(%args, 0)); + if(%type !$= "single" && %type !$= "Radius" && %type !$= "all") + { + messageClient(%sender, "", "\c2Invalid type. Must be either\c3 Single \c2[For one point]), \c3radius \c2[For points whithin a radius around you], or\c3 All \c2[For all points]."); + return; + } + + if(%type $= "single") + { + %arg2 = zombieSpawnByName(getword(%args, 1)); + if(!%arg2) + { + messageClient(%sender, "", "\c2Spawn doesn't exist."); + return; + } + } + else if(%type $= "radius" && %arg2 < 1) + { + messageClient(%sender, "", "\c2Invalid radius."); + return; + } + + // Unification. Will waste resources for singles, but, it's easier for me to program. + for(%i = 0; %i < ZombiePoints.getCount(); %i++) + { + %doit = 0; + %spawn = ZombiePoints.getObject(%i); + + // Eolk - brackets must be used here. + if(%type $= "single") + { + if(%spawn.label $= %spawn) + %doit = 1; + } + else if(%type $= "radius") + { + if(VectorDist(posFromTransform(%spawn.spawnTransform), posFromTransform(%sender.player.getTransform())) < %arg2) + %doit = 1; + } + else if(%type $= "all") + %doit = 1; + + if(%doit == 1) + { + %deplObj = new StaticShape() { + datablock = "DeployedZSpawnBase"; + }; + + %deplObj.setTransform(%spawn.spawnTransform); // I couldn't believe that worked. + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %sender.team; + %deplObj.setOwner(%sender.player); + %deplObj.light.lightBase = %deplObj; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %sender.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // Eolk - DON'T play the sound. + + // increment the team count for this deployed object + $TeamDeployedCount[%sender.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // Power object + checkPowerObject(%deplObj); + + if(%spawn.ztype $= "regular" || %spawn.ztype $= "random") // I took the easy way out. + %deplObj.ZType = 1; + else if(%spawn.ztype $= "ravenger") + %deplObj.ZType = 2; + else if(%spawn.ztype $= "lord") + %deplObj.zType = 3; + else if(%spawn.ztype $= "demon") + %deplObj.zType = 4; + else if(%spawn.ztype $= "rapier") + %deplObj.zType = 5; + %deplObj.numZ = %spawn.zombieLimit; // Note: It isn't maximum zombies. Trust me. + if(%spawn.maximumZombies <= 1) + %deplObj.spawnTypeSet = 1; + else + %deplObj.spawnTypeSet = 0; + + %label = %spawn.label; + ccRemoveSpawn(%sender, %spawn.label); + if(%type $= "single") + { + messageClient(%sender, "", "\c3"@%label@" \c2has been replaced with a regular zombie spawn."); + break; + } + } + } + + if(%type $= "radius") + messageClient(%sender, "", "\c2All spawns whithin\c3 "@%arg2@" \c2meters have been replaced with regular zombie spawns."); + else if(%type $= "all") + messageClient(%sender, "", "\c2All advanced zombie spawns have been replaced with regular zombie spawns."); +} + +// Eolk +function cczombieteam(%sender, %args) +{ + if(!%sender.isAdmin) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2You cannot make a Zombie Team in Construction."); + return; + } + + if(%args != 1 && %args != 2 && %args != 0) + { + messageClient(%sender, "MsgNo", "\c2Invalid Parameters. Must be either team 1 or team 2. Enter 0 for reset."); + return; + } + + if(%param == 0) + { + %temp = (%param == 0 ? 'none' : %param); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2changed \c3ZOMBIE TEAM \c2to \c3"@%temp@"\c2."); + $ZombieTeam = 0; + cckillzombies(%sender); + logEcho(%sender.nameBase@" ("@%sender@") changed zombie team to "@(%param == 0 ? "non" : %param)@" and killed all the zombies"); + return; + } + + if(Game.numTeams < 2) + { + messageClient(%sender, "MsgNo", "\c2There must be at least two teams. You may use /addteam to make another team."); + return; + } + + $ZombieTeam = %args; + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if(isObject(%cl.player) && !%cl.isAIControlled()) + game.ForceObserver(%cl, "AdminForce"); + } + + %temp = (%param == 0 ? 'none' : %param); + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has changed the zombie team to\c3 "@(%param == 0 ? 'none' : %param)@"\c2."); + logEcho(%sender.nameBase@" ("@%sender@") changed the zombie team to "@(%param == 0 ? "none" : %param)); +} + +// Blnukem +function ccZDetectDist(%sender, %args){ + if(!%sender.isAdmin) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2This cannot be changed in Construction."); + return; + } + + if(%args > 10000 || %args < 0){ + messageClient(%sender, 'MsgNo', '\c2Invalid detect distance. Must be 10000 or less or 0 or More.'); + return; + } + + if(strlwr(%args) $= "get"){ + messageClient(%sender, 'MsgNo', '\c2Current Detection Distance:\c3 %1\c2.', $zombie::detectDist); + return; + } + + %old = $zombie::detectDist; + %rad = %args; + if(strlwr(%args) $= "infinite") + %rad = 10000; + if(strlwr(%args) $= "none") + %rad = 0; + + if(%rad == %old){ + messageClient(%sender, 'MsgNo', '\c2The Zombie Detect Distance is Currently the Specified Argument:\c3 %1\c2.', %old); + return; + } + + $zombie::detectDist = %rad; + messageAll('MsgAdminForce', '\c2The Zombie Detection Distance has been Changed from: \c3%1 \c2to \c3%2.', %old, %rad); + logEcho(%sender.nameBase@" ("@%sender@") changed the zombie detection distance to "@$zombie::detectDist); + } + +//------------------------------------------------------------------------------ diff --git a/Scripts/ModScripts/ModFunctions.cs b/Scripts/ModScripts/ModFunctions.cs new file mode 100644 index 0000000..61722fe --- /dev/null +++ b/Scripts/ModScripts/ModFunctions.cs @@ -0,0 +1,763 @@ +//============================================================================== +// Mod Functions +//============================================================================== +//------------------------------------------------------------------------------ +// Made by Blnukem. + +function AutoBan(%client) +{ + MessageAll('MsgAdminForce', '\c3%1 \c2was automatically banned by the server.', %client.name); + %client.player.scriptKill(0); + %client.setDisconnectReason( "You have been automatically banned by the server." ); + %client.schedule(0, "delete"); + BanList::add(%client.guid, %client.getAddress(), $Host::BanTime); +} + +//------------------------------------------------------------------------------ +// Made by Blnukem and Dark Dragon DX. + +function applyskin(%obj,%skin,%name) +{ + %obj.target = createTarget(%obj, %name, %skin, "Male1", '', 0, PlayerSensor); + setTargetDataBlock(%obj.target, %obj.getDatablock()); + setTargetSensorData(%obj.target, PlayerSensor); + setTargetSensorGroup(%obj.target, 6); + setTargetSkin(%obj.target, %skin); + setTargetName(%obj.target, addtaggedstring(%name)); +} + +//------------------------------------------------------------------------------ +// Made by Blnukem. + +function LoadDefaultSettings() { + $Host::Cascade = 0; + Call("EnableVehicles"); +} + +//------------------------------------------------------------------------------ +// Made by Blnukem. + +function ClearBasicTimer(%obj) +{ + if(isObject(%obj)) + { + if(%obj.BasicTimer > 0) + { + %obj.BasicTimer -= 1; + %obj.BasicTimerSchedule = Schedule(1000, 0, "ClearBasicTimer", %obj); + } + } +} + +//------------------------------------------------------------------------------ +// Originally by Eolk, revised by Blnukem. + +function Cure(%args) +{ + %args.player.infected = 0; + cancel(%args.player.infectedDamage); + %args.player.infectedDamage = ""; + %args.player.beats = 0; + %args.player.canZkill = 0; + %args.player.hit = 0; + cancel(%args.player.zombieAttackImpulse); +} + +//------------------------------------------------------------------------------ +// Unknown Author + +function plnametocid(%name) +{ + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %obj = ClientGroup.getObject(%i); + %nametest = strlwr(%obj.namebase); + %name = strlwr(%name); + if(strstr(%nametest, %name) != -1) + return %obj; + } + return 0; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function addToZombiePointGroup(%obj) +{ + %grp = nameToId("MissionCleanup/ZombiePoints"); + if(!isObject(%grp)) + { + %grp = new SimGroup("ZombiePoints"); + MissionCleanup.add(%grp); + } + + %grp.add(%obj); +} + +//------------------------------------------------------------------------------ +// Made by Eolk... Just supposed to make things pretty + +function ZombiePoint::setStatus(%this, %status) +{ + %this.status = %status; +} + +//------------------------------------------------------------------------------ +// Made by Eolk... Long, isn't it? + +function ZombiePoint::StartSpawnLoop(%this) +{ + if(%this.disabled) + return; + + if(%this.maximumZombies != -1) + %this.spawnedZombies++; + + if(%this.maximumZombies != -1 && (%this.spawnedZombies >= %this.maximumZombies)) + { + %this.disabled = 1; + return; + } + + if(%this.zombieLimit != -1 && (getwordcount(%this.zombieList) >= %this.zombieLimit)) + { + %this.zombieLoop = %this.schedule(%this.timeout * 1000, "StartSpawnLoop"); + return; + } + + if(ZombieGroup.getCount() >= $Host::MaxZombies && $Host::MaxZombies != -1) + { + %this.zombieLoop = %this.schedule(%this.timeout * 1000, "StartSpawnLoop"); + return; + } + + %test = %this.type; + if(%this.type $= "random") + { + %num = getRandom(1, 5); + if(%num == 2) + %test = "ravenger"; + else if(%num == 3) + %test = "lord"; + else if(%num == 4) + %test = "demon"; + else if(%num == 5) + %test = "rapier"; + else + %test = "regular"; + } + + switch$(%test) + { + case "regular": + %zombie = new Player() { + datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + + %type = 1; + case "ravenger": + %zombie = new Player() { + datablock = "FZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Ravenger Zombie"); + + %type = 2; + case "lord": + %zombie = new player(){ + datablock = "LordZombieArmor"; + }; + applyskin(%zombie,'ZLord',"Zombie Lord"); + + %zombie.client = $zombie::Lclient; + %zombie.mountImage(ZHead, 3); + %zombie.mountImage(ZBack, 4); + %zombie.mountImage(ZDummyslotImg, 5); + %zombie.mountImage(ZDummyslotImg2, 6); + %zombie.firstFired = 0; + %zombie.canmove = 1; + %type = 3; + + case "demon": + %zombie = new player(){ + datablock = "DemonZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Demon Zombie"); + + %zombie.mountImage(ZdummyslotImg, 4); + %type = 4; + + case "rapier": + %zombie = new player(){ + datablock = "RapierZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Rapier Zombie"); + + %zombie.mountImage(ZWingImage, 3); + %zombie.mountImage(ZWingImage2, 4); + %zombie.setActionThread("scoutRoot",true); + %type = 5; + } + + %zombie.type = %type; + %zombie.setTransform(%this.spawnTransform); + %zombie.team = 0; + %zombie.canJump = 1; + %zombie.hasTarget = 0; + %zombie.isCompZomb = 1; + if(%this.zombieLimit != -1) + { + %zombie.overwatcher = %this; + %this.zombieList = listAdd(%this.zombieList, %zombie, -1); + } + + AddToZombieGroup(%zombie); + schedule(1000, %zombie, "ZSetRandomMove", %zombie); + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); + + %this.zombieLoop = %this.schedule(%this.timeout * 1000, "StartSpawnLoop"); + %this.setStatus("Spawning zombies."); +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function zombieSpawnByName(%name) +{ + %name = strlwr(%name); + %grp = nameToId("MissionCleanup/ZombiePoints"); + if(!isObject(%grp)) + return 0; + for(%i = 0; %i < %grp.getCount(); %i++) + { + %obj = %grp.getObject(%i); + %objname = strlwr(%obj.label); + if(%name $= %objname) + return %obj; + } + return 0; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function AssignStalkMonitor(%target, %ztype, %stype) +{ + if(isObject(%target.stalkMonitor)) + return; + + %s = new ScriptObject() + { + class = StalkMonitor; + }; + + MissionCleanup.add(%s); // this kills any lingering ones at the mission end. + + // assign the necessary flags. + %s.target = %target; + // note: these are already strlwr()ed. + %s.ztype = %ztype; + %s.stype = %stype; + + // this is here mainly for a reminder... +// %s.lastztype = ""; +// %s.laststype = ""; + %s.coolvar = "light"; + %s.coolcount = 0; + %s.coolup = 1; + + // tag 'em. + %target.stalkMonitor = %s; +} + +// This is for easy time adjusting. +$SwarmLoopTime["heavy"] = 1000; // 1 second. +$SwarmLoopTime["medium"] = 5000; // 5 seconds. +$SwarmLoopTime["light"] = 10000; // 10 seconds. + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function StalkMonitor::StalkLoop(%this) +{ + if(!%this) + return; + + %target = %this.target; + if(!isObject(%target)) + { + // Let's pack our bags and go. Our work is done here. + %this.doneStalking(); + return; + } + + if(!isObject(%target.player)) + { + if(%this.stype !$= "cool") + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.stype], "StalkLoop"); + else // don't update. They were in observer. + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.coolvar], "StalkLoop"); + return; + } + + if(ZombieGroup.getCount() >= $Host::MaxZombies && $Host::MaxZombies != -1) + { + if(%this.stype !$= "cool") + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.stype], "StalkLoop"); + else // don't update. There isn't an available zombie spot. + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.coolvar], "StalkLoop"); + return; + } + + %monitor = %target.stalkMonitor; + %test = %monitor.ztype; + if(%test $= "random") + { + %num = getRandom(1, 5); + if(%num == 2) + %test = "ravenger"; + else if(%num == 3) + %test = "lord"; + else if(%num == 4) + %test = "demon"; + else if(%num == 5) + %test = "rapier"; + else + %test = "regular"; + } + + switch$(%test) + { + case "regular": + %zombie = new Player() { + datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + + %type = 1; + case "ravenger": + %zombie = new Player() { + datablock = "FZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Ravenger Zombie"); + + %type = 2; + case "lord": + %zombie = new player(){ + datablock = "LordZombieArmor"; + }; + applyskin(%zombie,'ZLord',"Zombie Lord"); + + %zombie.client = $zombie::Lclient; + %zombie.mountImage(ZHead, 3); + %zombie.mountImage(ZBack, 4); + %zombie.mountImage(ZDummyslotImg, 5); + %zombie.mountImage(ZDummyslotImg2, 6); + %zombie.firstFired = 0; + %zombie.canmove = 1; + %type = 3; + + case "demon": + %zombie = new player(){ + datablock = "DemonZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Demon Zombie"); + + %zombie.mountImage(ZdummyslotImg, 4); + %type = 4; + + case "rapier": + %zombie = new player(){ + datablock = "RapierZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Rapier Zombie"); + + %zombie.mountImage(ZWingImage, 3); + %zombie.mountImage(ZWingImage2, 4); + %zombie.setActionThread("scoutRoot",true); + %type = 5; + } + + %zombie.type = %type; + %pos = %target.player.position; + // cheezy randomness. Should work. + // TODO: Add a check for interior, etc. Look at alternateSpawn. + %rand = getRandomB(); + if(%rand) + %x = getword(%pos, 0) + getRandom(25, 100); + else + %x = getword(%pos, 0) - getRandom(25, 100); + + %rand = getRandomB(); + if(%rand) + %y = getword(%pos, 1) + getRandom(25, 100); + else + %y = getword(%pos, 1) - getRandom(25, 100); + %z = getTerrainHeight(%x SPC %y); + + %dapos = %x SPC %y SPC %z; + %typeMasks = $TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%dapos, 2, %typeMasks); + if(ContainerSearchNext != 0) + { + // This position isn't valid, let's not spawn this round. + if(%this.stype !$= "cool") + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.stype], "StalkLoop"); + else // don't update. They were in observer. + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.coolvar], "StalkLoop"); + return; + } + + %zombie.setTransform(%dapos SPC "1 0 0 0"); + %zombie.team = 0; + %zombie.canJump = 1; + %zombie.hasTarget = 0; + %zombie.isCompZomb = 1; + + AddToZombieGroup(%zombie); + schedule(1000, %zombie, "ZSetRandomMove", %zombie); + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); + + if(%this.stype !$= "cool") + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.stype], "StalkLoop"); + else + { + %this.UpdateCoolVariables(); + %this.stalkSchedule = %this.schedule($SwarmLoopTime[%this.coolvar], "StalkLoop"); + } +} + +$StalkCoolTime["light"] = 10; // 100 seconds of boredom. :P +$StalkCoolTime["medium"] = 15; // 75 seconds of balanced zombie invasions. +$StalkCoolTime["heavy"] = 30; // 30 seconds of pure heck. :) + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function StalkMonitor::UpdateCoolVariables(%this) +{ + %this.coolcount++; + if(%this.coolcount >= ($StalkCoolTime[%this.coolvar] + 1)) // Add an extra to it so that the amount of times actually gets done. + { + %this.coolcount = 0; + if(%this.coolvar $= "light") + { + %this.coolvar = "medium"; + %this.coolup = 1; + } + else if(%this.coolvar $= "medium") + { + if(%this.coolup == 1) + %this.coolvar = "heavy"; + else + %this.coolvar = "light"; + } + else if(%this.coolvar $= "heavy") + { + %this.coolvar = "medium"; + %this.coolup = 0; + } + else + error(%this @": Cannot update cooldown count. Unknown coolvar."); + } +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function StalkMonitor::doneStalking(%this) +{ +// echo(%this @": Done stalking. Terminating..."); + cancel(%this.stalkSchedule); + %this.stalkSchedule = ""; + %this.target.stalkMonitor = ""; + + %this.schedule(500, "delete"); +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function opposite(%num) +{ + return %num * -1; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function SimObject::isTSStaticObject(%obj) +{ + // If it doesn't have a shape name, then it definately ain't. + if(%obj.shapeName $= "") + return 0; + + for(%i = 0; %i < $NumStaticTSObjects; %i++) + { + if(getword($StaticTSObjects[%i], 2) $= %obj.shapeName) + return 1; + } + + return 0; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ZombieGetRandom() +{ + %rand = getRandom(); + if(%rand > 0.9) + return 1; // Lord Zombie + else if(%rand <= 0.8 && %rand >= 0.7) + return 2; // Rapier + else + return 0; // Regular +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ClearSaveTimeout(%cl) +{ + if(isObject(%cl)) + { + if(%cl.saveTimeout > 0) + { + %cl.saveTimeout -= 1; + %cl.saveTimeoutSchedule = schedule(1000, 0, "ClearSaveTimeout", %cl); + } + } +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ClearLoadTimeout(%cl) +{ + if(isObject(%cl)) + { + if(%cl.loadTimeout > 0) + { + %cl.loadTimeout -= 1; + %cl.loadTimeoutSchedule = schedule(1000, 0, "ClearLoadTimeout", %cl); + } + } +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ReverseGetRot(%rot, %mult) +{ + %x = getword(%rot, 0) / %mult; + %y = getword(%rot, 1) / %mult; + %z = getword(%rot, 2) / %mult; + return %x SPC %y SPC %z SPC %mult; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ACCMChatLog(%client, %msg, %type, %target) +{ + if($Host::ACCMChatLogging != 1 && $Host::ACCMEchoChat != 1) + return; + + %msg = stripChars(%msg, "\c0\c1\c2\c3\c4\c5\c6\c7\c8\c9\x10\x11"); + switch$(%type) + { + case 0: + %t = "[GLOBAL]"; + case 1: + %t = "[TEAM]"; + case 2: + %t = "[PRIVATE]"; + } + + %logdate = formattimestring("m-d-y"); + %dir = "ServerLogs/Chat/Log of "@%logdate@".log"; + if($Host::ACCMChatLogging == 1) + { + %temp = new FileObject(); + %temp.openforappend(%dir); + %temp.writeline(%t @" ("@formattimestring("D m/d/y hh:nn a")@") "@%client.nameBase @""@ (%t $= "[PRIVATE]" ? " (to "@%target.nameBase@")" : "")@": "@%msg); + %temp.close(); + %temp.delete(); + } + + if($Host::ACCMEchoChat == 1) + echo(%t @" ("@formattimestring("D m/d/y hh:nn a")@") "@%client.nameBase @""@ (%t $= "[PRIVATE]" ? " (to "@%target.nameBase@")" : "")@": "@%msg); +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ACCMConnectionLog(%client, %type, %reason) +{ + if($Host::ACCMConnectionLogging != 1) + return; + + if(%client.nameBase $= "Sentinel" || %client.nameBase $= "Monitor") + return; + + if(%reason $= "") + %reason = "Successful manual disconnect."; + + switch$(%type) + { + case "connect": + %t = "joined the server"; + case "disconnect": + %t = "disconnected from the server (reason: "@%reason@")"; + default: + error("ACCMConnectionLog: %type turned up unknown value. Report this!"); + return; + } + + %logdate = formattimestring("m-d-y"); + %dir = "ServerLogs/Connections/Log of "@%logdate@".log"; + %authInfo = %client.getAuthInfo(); + if($playingonline) + %str = "[ONLINE] "@formattimestring("D m/d/y hh:nn a")@" (GUID: "@%client.guid@", REAL NAME: "@getField(%authInfo, 0)@", SMURF: "@%client.isSmurf@", "@%client.getAddress()@") "@%client.nameBase@" "@%t@"."; + else + %str = "[OFFLINE] "@formattimestring("D m/d/y hh:nn a")@" ("@%client.getAddress()@") "@%client.nameBase@" "@%t@"."; + %temp = new FileObject(); + %temp.openforappend(%dir); + %temp.writeline(%str); + %temp.close(); + %temp.delete(); + logEcho(%str); +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function VectorCheck(%vec) +{ + if(getword(%vec, 0) > 0) + %x = 1; + else if(getword(%vec, 0) < 0) + %x = -1; + else + %x = 0; + if(getword(%vec, 1) > 0) + %y = 1; + else if(getword(%vec, 1) < 0) + %y = -1; + else + %y = 0; + if(getword(%vec, 2) > 0) + %z = 1; + else if(getword(%vec, 2) < 0) + %z = -1; + else + %z = 0; + return %x SPC %y SPC %z; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function VectorPositive(%vec) +{ + if(getword(%vec, 0) < 0) + %x = opposite(getword(%vec, 0)); + else + %x = getword(%vec, 0); + if(getword(%vec, 1) < 0) + %y = opposite(getword(%vec, 1)); + else + %y = getword(%vec, 1); + if(getword(%vec, 2) < 0) + %z = opposite(getword(%vec, 2)); + else + %z = getword(%vec, 2); + return %x SPC %y SPC %z; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function VectorAdd2(%vec, %adder) +{ + if(getword(%vec, 0) != 0) + %new1 = getword(%vec, 0) + %adder; + else + %new1 = 0; + if(getword(%vec, 1) != 0) + %new2 = getword(%vec, 1) + %adder; + else + %new2 = 0; + if(getword(%vec, 2) != 0) + %new3 = getword(%vec, 2) + %adder; + else + %new3 = 0; + return %new1 SPC %new2 SPC %new3; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function CheckGUID(%client) +{ + if(!$playingonline) + { + error("CheckGUID: This does not work offline!"); + return; + } + + %authinfo = %client.getAuthInfo(); + %guid = getField(%authinfo, 3); + return %guid; +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function CreateWaypoint(%pos, %name) +{ + %w = new WayPoint() { + datablock = WayPointMarker; + name = %name; + }; + + %w.setTransform(%pos SPC "0 0 1 0"); + %w.team = 0; + + MissionCleanup.add(%w); + %w.schedule(10000, "delete"); +} + +//------------------------------------------------------------------------------ +// Made by Eolk. + +function ChangeName(%client, %name) +{ + if(%name $= "reset") + { + if(%client.oldName !$= "") + { + removeTaggedString(%client.name); + %client.name = ""; + %client.name = addTaggedString(%client.oldname); + + setTargetName(%client.target, addTaggedString(%client.oldName)); + messageAll('MsgClientNameChanged', "", "", %client.name, %client); + %client.oldname = ""; + return; + } + } + + if(%client.oldName $= "") + %client.oldName = getTaggedString(%client.name); + + removeTaggedString(%client.name); + %client.name = ""; + %realname = "\x10\c6"@%name@"\x11"; + %client.name = addTaggedString(%realname); + + setTargetName(%client.target, addTaggedString(%realname)); + messageAll('MsgClientNameChanged', "", %client.oldname, %client.name, %client); +} diff --git a/Scripts/OptionsDlg.cs b/Scripts/OptionsDlg.cs new file mode 100644 index 0000000..9516871 --- /dev/null +++ b/Scripts/OptionsDlg.cs @@ -0,0 +1,2772 @@ +//------------------------------------------------------------------------------ +// +// OptionsDlg.cs +// +//------------------------------------------------------------------------------ +$max_screenerror = 25; +$min_TSScreenError = 2; +$max_TSScreenError = 20; +$min_TSDetailAdjust = 0.6; +$max_TSDetailAdjust = 1.0; +//------------------------------------------------------------------------------ +function OptionsDlg::onWake( %this ) +{ + OP_VideoPane.setVisible( false ); + OP_GraphicsPane.setVisible( false ); + OP_TexturesPane.setVisible( false ); + OP_SoundPane.setVisible( false ); + OP_VoicePane.setVisible( false ); + OP_ControlsPane.setVisible( false ); + OP_NetworkPane.setVisible( false ); + OP_GamePane.setVisible( false ); + + OP_VideoTab.setValue( false ); + OP_GraphicsTab.setValue( false ); + OP_TexturesTab.setValue( false ); + OP_SoundTab.setValue( false ); + OP_VoiceTab.setValue( false ); + OP_ControlsTab.setValue( false ); + OP_NetworkTab.setValue( false ); + OP_GameTab.setValue( false ); + + // Initialize the Video Pane controls: + // First the Video Driver menu: + %buffer = getDisplayDeviceList(); + %count = getFieldCount( %buffer ); + for ( %i = 0; %i < %count; %i++ ) + OP_VideoDriverMenu.add( getField( %buffer, %i ), %i ); + + // Select the current device: + OP_FullScreenTgl.setValue( $pref::Video::fullScreen ); + + %selId = OP_VideoDriverMenu.findText( $pref::Video::displayDevice ); + if ( %selId == -1 ) + %selId = 0; // How did THAT happen? + OP_VideoDriverMenu.setSelected( %selId ); + OP_VideoDriverMenu.onSelect( %selId, "" ); + OP_FullScreenTgl.onAction(); + + OP_ApplyBtn.setActive( false ); + + // Initialize the Graphics Options controls: + OptionsDlg::deviceDependent( %this ); + + // Radeon cards don't switch color depth good until they release their beta drivers which fix this problem. + if( $RadeonRenderer == true ) + OP_BPPMenu.setActive( false ); + + OP_GammaSlider.setValue( $pref::OpenGL::gammaCorrection ); + OP_GammaSlider.setActive( $Video::setGammaCorrectionSupported ); + + OP_TerrainSlider.setValue( $max_screenerror - $pref::Terrain::screenError ); + OP_ShapeSlider.setValue( ( $max_TSScreenError - $pref::TS::screenError ) / ( $max_TSScreenError - $min_TSScreenError ) ); + OP_ShadowSlider.setValue( $pref::Shadows ); + OP_InteriorDetailSlider.setValue( $pref::Interior::detailAdjust ); + OP_VisibleDistanceSlider.setValue( $pref::VisibleDistanceMod ); + OP_ParticleDensitySlider.setValue( 4.0 - $pref::ParticleDensity ); + OP_DynamicLightSlider.setValue( 100 - $pref::Interior::DynamicLightsClipPix ); + updateDynamicLightSliderState(); + OP_SkyDetailMenu.init(); + if ( !$pref::SkyOn ) + %selId = 5; + else if ( $pref::numCloudLayers >= 0 && $pref::numCloudLayers < 4 ) + %selId = 4 - $pref::numCloudLayers; + else + %selId = 1; + OP_SkyDetailMenu.setSelected( %selId ); + OP_SkyDetailMenu.setText( OP_SkyDetailMenu.getTextById( %selId ) ); + OP_PlayerRenderMenu.init(); + %selId = $pref::Player::renderMyPlayer | ( $pref::Player::renderMyItems << 1 ); + OP_PlayerRenderMenu.setSelected( %selId ); + OP_VertexLightTgl.setValue( $pref::Interior::VertexLighting ); + + // Initialize the Textures Options controls: + OP_TerrainTexSlider.setValue( 6 - $pref::Terrain::texDetail ); + + // We're using the noDrawArraysAlpha variable here because we've already + // gone gold (hard to add a new profiling variable). But the Voodoo2/3/3500 + // cards that have the 256x256 texture limitation (in OpenGL) also have the + // noDrawArraysAlpha hack on...so that works out nice + %mipRange = $pref::OpenGL::noDrawArraysAlpha ? 4.0 : 5.0; + OP_ShapeTexSlider.setValue( (5 - $pref::OpenGL::mipReduction) / %mipRange ); + OP_BuildingTexSlider.setValue( (5 - $pref::OpenGL::interiorMipReduction) / %mipRange ); + OP_SkyTexSlider.setValue( (5 - $pref::OpenGL::skyMipReduction) / %mipRange ); + if ( !isDemo() ) + OP_HiResSkinTgl.setValue( $pref::use512PlayerSkins ); + + // Initialize the Sound Options controls: + // provider menu + %count = alxGetContexti(ALC_PROVIDER_COUNT); + for(%i = 0; %i < %count; %i++) + OP_AudioProviderMenu.add(alxGetContextstr(ALC_PROVIDER_NAME, %i), %i); + %selId = alxGetContexti(ALC_PROVIDER); + OP_AudioProviderMenu.setSelected(%selId); + OP_AudioResetProvider.setActive(false); + + // environment provider: disable and uncheck if not an environment provider + %envProvider = audioIsEnvironmentProvider(alxGetContextstr(ALC_PROVIDER_NAME, %selId)); + + if(!%envProvider) + OP_AudioEnvironmentTgl.setValue(false); + OP_AudioEnvironmentTgl.setActive(%envProvider); + + // speaker menu + %count = alxGetContexti(ALC_SPEAKER_COUNT); + for(%i = 0; %i < %count; %i++) + OP_AudioSpeakerMenu.add(alxGetContextstr(ALC_SPEAKER_NAME, %i), %i); + %selId = alxGetContexti(ALC_SPEAKER); + OP_AudioSpeakerMenu.setSelected(%selId); + OP_AudioSpeakerMenu.onSelect(%selId, ""); + + OP_AudioFrequencyMenu.init(); + OP_AudioBitRateMenu.init(); + OP_AudioChannelsMenu.init(); + + // don't allow changing of of mixer settings while in a game... + %active = !isObject(ServerConnection); + OP_AudioFrequencyMenu.setActive(%active); + // Changing these audio settings doesn't help Linux performance + if ( $platform $= "linux" ) + { + OP_AudioBitRateMenu.setActive(false); + OP_AudioChannelsMenu.setActive(false); + } + else + { + OP_AudioBitRateMenu.setActive(%active); + OP_AudioChannelsMenu.setActive(%active); + } + + // only allow for disable + if(!%active) + OP_AudioEnvironmentTgl.setActive(%active); + + OP_AudioProviderMenu.setActive(%active); + OP_AudioSpeakerMenu.setActive(%active); + + OP_MasterVolumeSlider.setValue( $pref::Audio::masterVolume ); + OP_EffectsVolumeSlider.setValue( $pref::Audio::effectsVolume ); + OP_VoiceBindVolumeSlider.setValue( $pref::Audio::radioVolume ); + OP_GuiVolumeSlider.setValue( $pref::Audio::guiVolume ); + OP_MusicTgl.onAction(); + OP_MusicVolumeSlider.setValue( $pref::Audio::musicVolume ); + + // Initialize the Voice Settings controls: + OP_MicrophoneEnabledTgl.onAction(); + OP_MicrophoneVolumeSlider.setValue( $pref::Audio::voiceVolume ); + OP_InputBoostSlider.setValue( $pref::Audio::captureGainScale ); + OP_VoiceListenMenu.init(); + OP_VoiceSendMenu.init(); + OP_VoiceCodecInfo.init(); + updateInputBoost(); + + // Initialize the Control Options controls: + OP_ControlGroupMenu.init(); + + if ( isJoystickDetected() ) + { + OP_JoystickTgl.setValue( $pref::Input::JoystickEnabled ); + OP_JoystickTgl.setActive( true ); + OP_ConfigureJoystickBtn.setActive( $pref::Input::JoystickEnabled ); + } + else + { + OP_JoystickTgl.setValue( false ); + OP_JoystickTgl.setActive( false ); + $pref::Input::JoystickEnabled = false; + OP_ConfigureJoystickBtn.setActive( false ); + } + + // Initialize the Network Options controls: + OP_NetworkDisplayHud.init(); + if( !OP_NetworkPresetsMenu.size() ) + OP_NetworkPresetsMenu.init(); + OP_PacketRateSlider.setValue( $pref::Net::PacketRateToClient ); + OP_PacketSizeSlider.setValue( $pref::Net::PacketSize ); + OP_UpdateRateSlider.setValue( $pref::Net::PacketRateToServer ); + if ( !OP_MasterServerMenu.size() ) + OP_MasterServerMenu.init(); + %selId = OP_MasterServerMenu.findText( $pref::Net::DisplayOnMaster ); + if ( %selId == -1 ) + %selId = 1; + OP_MasterServerMenu.setSelected( %selId ); + if ( !OP_RegionMenu.size() ) + OP_RegionMenu.init(); + OP_RegionMenu.setSelected( $pref::Net::RegionMask ); + + // Initialize the Game Options controls: + OP_ZoomSpeedSlider.setValue( 500 - $pref::Player::zoomSpeed ); + OP_LaunchScreenMenu.init(); + + %selId = OP_LaunchScreenMenu.findText( $pref::Shell::LaunchGui ); + if ( %selId == -1 ) + %selId = 1; + OP_LaunchScreenMenu.setText( OP_LaunchScreenMenu.getTextById( %selId ) ); + OP_LaunchScreenMenu.setSelected( %selId ); + + // Hide controls that are not relevant to the demo: + if ( isDemo() ) + { + OP_MasterServerTxt.setVisible( false ); + OP_MasterServerMenu.setVisible( false ); + OP_CheckEmailTgl.setVisible( false ); + OP_ChatDisconnectTgl.setVisible( false ); + OP_EditChatMenuBtn.setVisible( false ); + OP_LaunchScreenTxt.setVisible( false ); + OP_LaunchScreenMenu.setVisible( false ); + } + + %this.setPane( %this.pane ); +} + +//------------------------------------------------------------------------------ +function OptionsDlg::deviceDependent( %this ) +{ + if ( $SwapIntervalSupported ) + { + OP_VSyncTgl.setValue( $pref::Video::disableVerticalSync ); + OP_VSyncTgl.setActive( true ); + } + else + { + OP_VSyncTgl.setValue( false ); + OP_VSyncTgl.setActive( false ); + } + + if ( isDemo() ) + { + OP_TexQualityMenu.setText( "Palletized" ); + OP_TexQualityMenu.setActive( false ); + } + else + { + OP_TexQualityMenu.init(); + if ( $pref::OpenGL::forcePalettedTexture ) + { + $pref::OpenGL::force16bittexture = false; + %selId = 1; + } + else if ( $pref::OpenGL::force16bittexture ) + %selId = 2; + else + %selId = 3; + OP_TexQualityMenu.setSelected( %selId ); + } + + OP_CompressMenu.init(); + if ( $TextureCompressionSupported && !$pref::OpenGL::disableARBTextureCompression ) + { + OP_CompressLabel.setVisible( true ); + OP_CompressLabel_Disabled.setVisible( false ); + OP_CompressMenu.setActive( true ); + if ( !$pref::OpenGL::allowCompression ) + OP_CompressMenu.setSelected( 1 ); + else if ( $pref::OpenGL::compressionHint $= "GL_NICEST" ) + OP_CompressMenu.setSelected( 3 ); + else + OP_CompressMenu.setSelected( 2 ); + } + else + { + OP_CompressLabel_Disabled.setVisible( true ); + OP_CompressLabel.setVisible( false ); + OP_CompressMenu.setActive( false ); + OP_CompressMenu.setText( "None" ); + } + + if ( $FogCoordSupported ) + { + OP_IntTexturedFogTgl.setValue( $pref::Interior::TexturedFog ); + OP_IntTexturedFogTgl.setActive( true ); + } + else + { + OP_IntTexturedFogTgl.setValue( true ); + OP_IntTexturedFogTgl.setActive( false ); + } + + OP_AnisotropySlider.setValue( $pref::OpenGL::anisotropy ); + OP_AnisotropySlider.setActive( $AnisotropySupported ); + if ( $AnisotropySupported ) + { + OP_AnisotropyLabel.setVisible( true ); + OP_AnisotropyLabel_Disabled.setVisible( false ); + } + else + { + OP_AnisotropyLabel_Disabled.setVisible( true ); + OP_AnisotropyLabel.setVisible( false ); + } + + OP_EnvMapTgl.setValue($pref::environmentMaps); + OP_EnvMapTgl.setActive($pref::OpenGL::allowTexGen); +} + +//------------------------------------------------------------------------------ +function OptionsDlg::onSleep( %this ) +{ + OP_VideoDriverMenu.clear(); + OP_ResMenu.clear(); + OP_BPPMenu.clear(); + OP_AudioProviderMenu.clear(); + OP_AudioSpeakerMenu.clear(); + OP_NetworkDisplayHud.uninit(); + + if ( %this.resetAudio ) + { + echo( "Resetting the audio driver..." ); + audioSetDriver( "none" ); + audioSetDriver( $pref::Audio::activeDriver ); + %this.resetAudio = ""; + + // Play the shell hum: (all sources are gone) + if($HudHandle[shellScreen] $= "") + alxStop($HudHandle[shellScreen]); + + $HudHandle[shellScreen] = alxPlay(ShellScreenHumSound, 0, 0, 0); + } + + if ( isObject( ServerConnection ) && isTextureFlushRequired() ) + MessageBoxYesNo( "WARNING", "You have made changes that require Tribes 2 to flush the texture cache. " + @ "Doing this while the game is running can take a long time. " + @ "Do you wish to continue?", + "OptionsDlg.saveSettings();", "returnFromSettings();" ); + else + %this.saveSettings(); +} + +//------------------------------------------------------------------------------ +function isTextureFlushRequired() +{ + if ( $pref::Interior::VertexLighting != OP_VertexLightTgl.getValue() ) + return( true ); + + // We're using the noDrawArraysAlpha variable here because we've already + // gone gold (hard to add a new profiling variable). But the Voodoo2/3/3500 + // cards that have the 256x256 texture limitation (in OpenGL) also have the + // noDrawArraysAlpha hack on...so that works out nice + %mipRange = $pref::OpenGL::noDrawArraysAlpha ? 4 : 5; + if ( $pref::OpenGL::mipReduction != 5 - mFloor( OP_ShapeTexSlider.getValue() * %mipRange ) ) + return( true ); + + if ( $pref::OpenGL::interiorMipReduction != 5 - mFloor( OP_BuildingTexSlider.getValue() * %mipRange ) ) + return( true ); + + if ( $pref::OpenGL::skyMipReduction != 5 - mFloor( OP_SkyTexSlider.getValue() * %mipRange ) ) + return( true ); + + if ( $AnisotropySupported && $pref::OpenGL::anisotropy != OP_AnisotropySlider.getValue() ) + return( true ); + + if ( !isDemo() ) + { + %id = OP_TexQualityMenu.getSelected(); + if ( $pref::OpenGL::forcePalettedTexture ) + { + if ( %id != 1 ) + return( true ); + } + else if ( $pref::OpenGL::force16bittexture ) + { + if ( %id != 2 ) + return( true ); + } + else if ( %id != 3 ) + return( true ); + } + + if ( $TextureCompressionSupported && !$pref::OpenGL::disableARBTextureCompression ) + { + %id = OP_CompressMenu.getSelected(); + if ( $pref::OpenGL::allowCompression ) + { + if ( $pref::OpenGL::compressionHint $= "GL_FASTEST" ) + { + if ( %id != 2 ) + return( true ); + } + else if ( $pref::OpenGL::compressionHint $= "GL_NICEST" ) + { + if ( %id != 3 ) + return( true ); + } + else if ( %id == 1 ) + return( true ); + } + else if ( %id > 1 ) + return( true ); + } + + return( false ); +} + +//------------------------------------------------------------------------------ +function returnFromSettings() +{ + // to unpause singlePlayerGame when returning from options + if ( isObject( Game ) ) + Game.OptionsDlgSleep(); +} + +//------------------------------------------------------------------------------ +function OptionsDlg::saveSettings( %this ) +{ + // Save off any prefs that don't auto-update: + %flushTextures = false; + + if ( $SwapIntervalSupported && OP_VSyncTgl.getValue() != $pref::Video::disableVerticalSync ) + { + $pref::Video::disableVerticalSync = OP_VSyncTgl.getValue(); + setVerticalSync( !$pref::Video::disableVerticalSync ); + } + + %temp = OP_SkyDetailMenu.getSelected(); + if ( %temp == 5 ) + $pref::SkyOn = false; + else + { + $pref::SkyOn = true; + $pref::numCloudLayers = ( 4 - %temp ); + } + + if ( $FogCoordSupported ) + $pref::Interior::TexturedFog = OP_IntTexturedFogTgl.getValue(); + + if ( $pref::Interior::VertexLighting != OP_VertexLightTgl.getValue() ) + { + $pref::Interior::VertexLighting = OP_VertexLightTgl.getValue(); + %flushTextures = true; + } + + %temp = OP_PlayerRenderMenu.getSelected(); + $pref::Player::renderMyPlayer = %temp & 1; + $pref::Player::renderMyItems = %temp & 2; + + if ( !isDemo() ) + { + switch ( OP_TexQualityMenu.getSelected() ) + { + case 1: // 8-bit + if ( !$pref::OpenGL::forcePalettedTexture || $pref::OpenGL::force16bittexture ) + { + $pref::OpenGL::forcePalettedTexture = true; + $pref::OpenGL::force16bittexture = false; + %flushTextures = true; + } + case 2: // 16-bit + if ( $pref::OpenGL::forcePalettedTexture || !$pref::OpenGL::force16bittexture ) + { + $pref::OpenGL::forcePalettedTexture = false; + $pref::OpenGL::force16bittexture = true; + %flushTextures = true; + } + case 3: // 32-bit + if ( $pref::OpenGL::forcePalettedTexture || $pref::OpenGL::force16bittexture ) + { + $pref::OpenGL::forcePalettedTexture = false; + $pref::OpenGL::force16bittexture = false; + %flushTextures = true; + } + } + OP_TexQualityMenu.clear(); + } + + $pref::Terrain::texDetail = 6 - mFloor( OP_TerrainTexSlider.getValue() ); + + // We're using the noDrawArraysAlpha variable here because we've already + // gone gold (hard to add a new profiling variable). But the Voodoo2/3/3500 + // cards that have the 256x256 texture limitation (in OpenGL) also have the + // noDrawArraysAlpha hack on...so that works out nice + %mipRange = $pref::OpenGL::noDrawArraysAlpha ? 4 : 5; + + %temp = 5 - mFloor( OP_ShapeTexSlider.getValue() * %mipRange ); + if ( $pref::OpenGL::mipReduction != %temp ) + { + $pref::OpenGL::mipReduction = %temp; + setOpenGLMipReduction( $pref::OpenGL::mipReduction ); + %flushTextures = true; + } + + %temp = 5 - mFloor( OP_BuildingTexSlider.getValue() * %mipRange ); + if ( $pref::OpenGL::interiorMipReduction != %temp ) + { + $pref::OpenGL::interiorMipReduction = %temp; + setOpenGLInteriorMipReduction( $pref::OpenGL::interiorMipReduction ); + %flushTextures = true; + } + + %temp = 5 - mFloor( OP_SkyTexSlider.getValue() * %mipRange ); + if ( $pref::OpenGL::skyMipReduction != %temp ) + { + $pref::OpenGL::skyMipReduction = %temp; + setOpenGLSkyMipReduction( $pref::OpenGL::skyMipReduction ); + %flushTextures = true; + } + + if ( $TextureCompressionSupported && !$pref::OpenGL::disableARBTextureCompression ) + { + %temp = OP_CompressMenu.getSelected(); + if ( $pref::OpenGL::allowCompression ) + { + switch ( %temp ) + { + case 2: + if ( $pref::OpenGL::compressionHint !$= "GL_FASTEST" ) + { + $pref::OpenGL::compressionHint = "GL_FASTEST"; + setOpenGLTextureCompressionHint( $pref::OpenGL::compressionHint ); + %flushTextures = true; + } + + case 3: + if ( $pref::OpenGL::compressionHint !$= "GL_NICEST" ) + { + $pref::OpenGL::compressionHint = "GL_NICEST"; + setOpenGLTextureCompressionHint( $pref::OpenGL::compressionHint ); + %flushTextures = true; + } + + default: // None + $pref::OpenGL::allowCompression = false; + %flushTextures = true; + } + } + else if ( %temp > 1 ) + { + $pref::OpenGL::allowCompression = true; + if ( %temp == 3 ) + $pref::OpenGL::compressionHint = "GL_NICEST"; + else + $pref::OpenGL::compressionHint = "GL_FASTEST"; + setOpenGLTextureCompressionHint( $pref::OpenGL::compressionHint ); + %flushTextures = true; + } + } + OP_CompressMenu.clear(); + + if ( $AnisotropySupported ) + { + %temp = OP_AnisotropySlider.getValue(); + if ( $pref::OpenGL::anisotropy != %temp ) + { + $pref::OpenGL::anisotropy = %temp; + setOpenGLAnisotropy( $pref::OpenGL::anisotropy ); + %flushTextures = true; + } + } + + if ( !isDemo() ) + { + if ( OP_HiResSkinTgl.getValue() != $pref::use512PlayerSkins ) + { + $pref::use512PlayerSkins = OP_HiResSkinTgl.getValue(); + if ( Canvas.getContent() == GameGui.getId() && GM_WarriorPane.isVisible() ) + GMW_PlayerModel.update(); + } + } + + $pref::Terrain::screenError = $max_screenerror - mFloor( OP_TerrainSlider.getValue() ); + $pref::TS::screenError = $max_TSScreenError - mFloor( OP_ShapeSlider.getValue() * ( $max_TSScreenError - $min_TSScreenError ) ); + $pref::TS::detailAdjust = $min_TSDetailAdjust + OP_ShapeSlider.getValue() * ( $max_TSDetailAdjust - $min_TSDetailAdjust ); + $pref::Shadows = OP_ShadowSlider.getValue(); + $pref::ParticleDensity = 4.0 - OP_ParticleDensitySlider.getValue(); + %val = 100 - OP_DynamicLightSlider.getValue(); + $pref::Interior::DynamicLightsClipPix = $pref::Terrain::DynamicLightsClipPix = %val; + $pref::Interior::DynamicLightsFadePix = $pref::Terrain::DynamicLightsFadePix = 2 * %val; + setShadowDetailLevel( $pref::Shadows ); + $pref::Interior::detailAdjust = OP_InteriorDetailSlider.getValue(); + $pref::VisibleDistanceMod = OP_VisibleDistanceSlider.getValue(); + + $pref::Audio::musicVolume = OP_MusicVolumeSlider.getValue(); + $pref::Audio::masterVolume = OP_MasterVolumeSlider.getValue(); + $pref::Audio::effectsVolume = OP_EffectsVolumeSlider.getValue(); + alxSetChannelVolume( $EffectAudioType, $pref::Audio::effectsVolume ); + $pref::Audio::voiceVolume = OP_MicrophoneVolumeSlider.getValue(); + alxSetChannelVolume( $VoiceAudioType, $pref::Audio::voiceVolume ); + $pref::Audio::radioVolume = OP_VoiceBindVolumeSlider.getValue(); + alxSetChannelVolume( $ChatAudioType, $pref::Audio::radioVolume ); + $pref::Audio::guiVolume = OP_GuiVolumeSlider.getValue(); + alxSetChannelVolume( $GuiAudioType, $pref::Audio::guiVolume); + $pref::Audio::captureGainScale = OP_InputBoostSlider.getValue(); + if ( !$missionRunning ) + MusicPlayer.stop(); + + if ( $pref::Audio::enableVoiceCapture ) + { + %reinit = false; + %selId = OP_VoiceListenMenu.getSelected(); + if ( $pref::Audio::decodingMask != %selId ) + { + $pref::Audio::decodingMask = %selId; + %reinit = true; + } + + %selId = OP_VoiceSendMenu.getSelected(); + if ( $pref::Audio::encodingLevel != %selId ) + { + $pref::Audio::encodingLevel = %selId; + %reinit = true; + } + + if ( %reinit ) + { + alxCaptureDestroy(); + alxCaptureInit(); + + // If in a game, let the server know about the altered settings: + if ( isObject( ServerConnection ) ) + commandToServer( 'SetVoiceInfo', $pref::Audio::voiceChannels, $pref::Audio::decodingMask, $pref::Audio::encodingLevel ); + } + } + + updateNetworkSettings(); + + $pref::Player::zoomSpeed = 500 - mFloor( OP_ZoomSpeedSlider.getValue() ); + setZoomSpeed( $pref::Player::zoomSpeed ); + + $pref::Shell::LaunchGui = OP_LaunchScreenMenu.getText(); + + export( "$pref::*", "prefs/ClientPrefs.cs", false ); + saveActiveMapFile(); + + if ( %flushTextures ) + { + // Give the Options Dialog a chance to go away: + OptionsDlg.schedule( 0, doTextureFlush ); + } + + returnFromSettings(); +} + +//------------------------------------------------------------------------------ +function OptionsDlg::doTextureFlush( %this ) +{ + MessagePopup( "PLEASE WAIT", "Flushing texture cache...\nThis may take a while" ); + Canvas.repaint(); + flushTextureCache(); + CloseMessagePopup(); +} + +//------------------------------------------------------------------------------ +function OptionsDlg::setPane( %this, %pane ) +{ + if((%this.pane $= "Sound") && !$missionRunning) + MusicPlayer.stop(); + + if ( %this.pane !$= "None" ) + { + %paneCtrl = "OP_" @ %this.pane @ "Pane"; + %paneCtrl.setVisible( false ); + + %tabCtrl = "OP_" @ %this.pane @ "Tab"; + %tabCtrl.setValue( false ); + } + + %paneCtrl = "OP_" @ %pane @ "Pane"; + %paneCtrl.setVisible( true ); + + %tabCtrl = "OP_" @ %pane @ "Tab"; + %tabCtrl.setValue( true ); + + %this.pane = %pane; +} + +//------------------------------------------------------------------------------ +function OptionsDlg::applyGraphicChanges( %this ) +{ + %newDriver = OP_VideoDriverMenu.getText(); + %newRes = OP_ResMenu.getText(); + %newBpp = OP_BPPMenu.getText(); + %newFullScreen = OP_FullScreenTgl.getValue(); + + if ( %newDriver !$= $pref::Video::displayDevice ) + { + setDisplayDevice( %newDriver, firstWord( %newRes ), getWord( %newRes, 1 ), %newBpp, %newFullScreen ); + OptionsDlg::deviceDependent( %this ); + } + else + setScreenMode( firstWord( %newRes ), getWord( %newRes, 1 ), %newBpp, %newFullScreen ); + + OP_ApplyBtn.updateState(); +} + + +//------------------------------------------------------------------------------ +function OP_VideoDriverMenu::onSelect( %this, %id, %text ) +{ + // Attempt to keep the same res and bpp settings: + if ( OP_ResMenu.size() > 0 ) + %prevRes = OP_ResMenu.getText(); + else + %prevRes = getWords( $pref::Video::resolution, 0, 1 ); + + // Check if this device is full-screen only: + if ( isDeviceFullScreenOnly( %this.getText() ) ) + { + OP_FullScreenTgl.setValue( true ); + OP_FullScreenTgl.setActive( false ); + OP_FullScreenTgl.onAction(); + } + else + OP_FullScreenTgl.setActive( true ); + + if ( OP_FullScreenTgl.getValue() ) + { + if ( OP_BPPMenu.size() > 0 ) + %prevBPP = OP_BPPMenu.getText(); + else + %prevBPP = getWord( $pref::Video::resolution, 2 ); + } + + // Fill the resolution and bit depth lists: + OP_ResMenu.init( %this.getText(), OP_FullScreenTgl.getValue() ); + OP_BPPMenu.init( %this.getText() ); + + // Try to select the previous settings: + %selId = OP_ResMenu.findText( %prevRes ); + if ( %selId == -1 ) + %selId = 0; + OP_ResMenu.setSelected( %selId ); + + if ( OP_FullScreenTgl.getValue() ) + { + %selId = OP_BPPMenu.findText( %prevBPP ); + if ( %selId == -1 ) + %selId = 0; + OP_BPPMenu.setSelected( %selId ); + OP_BPPMenu.setText( OP_BPPMenu.getTextById( %selId ) ); + } + else + OP_BPPMenu.setText( "Default" ); + + OP_ApplyBtn.updateState(); +} + +//------------------------------------------------------------------------------ +function OP_ResMenu::init( %this, %device, %fullScreen ) +{ + %this.clear(); + %resList = getResolutionList( %device ); + %resCount = getFieldCount( %resList ); + %deskRes = getDesktopResolution(); + %count = 0; + for ( %i = 0; %i < %resCount; %i++ ) + { + %res = getWords( getField( %resList, %i ), 0, 1 ); + + if ( !%fullScreen ) + { + if ( firstWord( %res ) >= firstWord( %deskRes ) ) + continue; + if ( getWord( %res, 1 ) >= getWord( %deskRes, 1 ) ) + continue; + } + + // Only add to list if it isn't there already: + if ( %this.findText( %res ) == -1 ) + { + %this.add( %res, %count ); + %count++; + } + } +} + +//------------------------------------------------------------------------------ +function OP_ResMenu::onSelect( %this, %id, %text ) +{ + OP_ApplyBtn.updateState(); +} + +//------------------------------------------------------------------------------ +function OP_BPPMenu::init( %this, %device ) +{ + %this.clear(); + + if ( %device $= "Voodoo2" ) + %this.add( "16", 0 ); + else + { + %resList = getResolutionList( %device ); + %resCount = getFieldCount( %resList ); + %count = 0; + for ( %i = 0; %i < %resCount; %i++ ) + { + %bpp = getWord( getField( %resList, %i ), 2 ); + + // Only add to list if it isn't there already: + if ( %this.findText( %bpp ) == -1 ) + { + %this.add( %bpp, %count ); + %count++; + } + } + } +} + +//------------------------------------------------------------------------------ +function OP_BPPMenu::onSelect( %this, %id, %text ) +{ + OP_ApplyBtn.updateState(); +} + +//------------------------------------------------------------------------------ +function OP_FullScreenTgl::onAction( %this ) +{ + // Attempt to maintain current settings: + %selId = OP_ResMenu.getSelected(); + if ( %selId == -1 ) + %selId = 0; + %prevRes = OP_ResMenu.getTextById( %selId ); + + OP_ResMenu.init( OP_VideoDriverMenu.getText(), %this.getValue() ); + + %selId = OP_ResMenu.findText( %prevRes ); + if ( %selId == -1 ) + %selId = 0; + OP_ResMenu.setSelected( %selId ); + + if ( %this.getValue() ) + { + %selId = OP_BPPMenu.findText( getWord( $pref::Video::resolution, 2 ) ); + if ( %selId == - 1 ) + %selId = 0; + OP_BPPMenu.setSelected( %selId ); + OP_BPPMenu.setText( OP_BPPMenu.getTextById( %selId ) ); + OP_BPPMenu.setActive( true ); + } + else + { + OP_BPPMenu.setText( "Default" ); + OP_BPPMenu.setActive( false ); + } + + OP_ApplyBtn.updateState(); +} + +//------------------------------------------------------------------------------ +function OP_ApplyBtn::updateState( %this ) +{ + %active = false; + + if ( OP_VideoDriverMenu.getText() !$= $pref::Video::displayDevice ) + %active = true; + else if ( OP_ResMenu.getText() !$= getWords( $pref::Video::resolution, 0, 1 ) ) + %active = true; + else if ( OP_FullScreenTgl.getValue() != $pref::Video::fullScreen ) + %active = true; + else if ( OP_FullScreenTgl.getValue() ) + { + if ( OP_BPPMenu.getText() !$= getWord( $pref::Video::resolution, 2 ) ) + %active = true; + } + + %this.setActive( %active ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Graphics Settings: +// +function updateGammaCorrection() +{ + $pref::OpenGL::gammaCorrection = OP_GammaSlider.getValue(); + videoSetGammaCorrection( $pref::OpenGL::gammaCorrection ); +} + +//------------------------------------------------------------------------------ +function updateTerrainDetail() +{ + $pref::Terrain::screenError = $max_screenerror - mFloor( OP_TerrainSlider.getValue()); + if ( OP_TerrainSlider.getValue() != $max_screenerror - $pref::Terrain::screenError ) + OP_TerrainSlider.setValue( $max_screenerror - $pref::Terrain::screenError ); +} + +//------------------------------------------------------------------------------ +function updateDynamicLightSliderState() +{ + %on = $pref::Interior::DynamicLights || $pref::Terrain::dynamicLights; + OP_DynamicLightText.setVisible( %on ); + OP_DynamicLightText_Disabled.setVisible( !%on ); + OP_DynamicLightSlider.setActive( %on ); +} + +//------------------------------------------------------------------------------ +function OP_SkyDetailMenu::init( %this ) +{ + %this.clear(); + %this.add( "Full Sky", 1 ); + %this.add( "Two Cloud Layers", 2 ); + %this.add( "One Cloud Layer", 3 ); + %this.add( "Sky Box Only", 4 ); + %this.add( "No Sky", 5 ); +} + +//------------------------------------------------------------------------------ +function OP_PlayerRenderMenu::init( %this ) +{ + %this.clear(); + %this.add( "Player and Items", 3 ); + %this.add( "Player only", 1 ); + %this.add( "Items only", 2 ); + %this.add( "Neither Player nor Items", 0 ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Texture Settings: +// +function OP_CompressMenu::init( %this ) +{ + %this.clear(); + %this.add( "None", 1 ); + %this.add( "Fastest", 2 ); + %this.add( "Nicest", 3 ); +} + +//------------------------------------------------------------------------------ +function OP_TexQualityMenu::init( %this ) +{ + %this.clear(); + if ( $PalettedTextureSupported ) + %this.add( "Palletized", 1 ); + %this.add( "16 bit", 2 ); + %this.add( "32 bit", 3 ); +} + +//------------------------------------------------------------------------------ +function OP_TexQualityMenu::onSelect( %this, %id, %text ) +{ + if ( %id == 1 ) + { + // Disable these with palletized textures by default: + OP_EnvMapTgl.setValue( false ); + //OP_EnvMapTgl.setActive( false ); + OP_IntEnvMapTgl.setValue( false ); + //OP_IntEnvMapTgl.setActive( false ); + } +// else +// { +// OP_EnvMapTgl.setActive( true ); +// OP_IntEnvMapTgl.setActive( true ); +// } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Audio Settings: +// +function setAudioProvider(%idx) +{ + alxContexti(ALC_PROVIDER, %idx); + $pref::Audio::provider = alxGetContextstr(ALC_PROVIDER_NAME, %idx); + + %active = audioIsEnvironmentProvider($pref::Audio::provider); + + // unset tgl if cannot be environment provider + if(!%active) + OP_AudioEnvironmentTgl.setValue(false); + OP_AudioEnvironmentTgl.setActive(%active); + + audioUpdateProvider($pref::Audio::provider); + OP_AudioProviderMenu.setSelected(%idx); +} + +//------------------------------------------------------------------------------ +function OP_AudioEnvironmentTgl::onAction(%this) +{ + alxEnableEnvironmental(%this.getValue()); +} + +//------------------------------------------------------------------------------ +function OP_AudioProviderMenu::onSelect(%this, %id, %text) +{ + if(%id != $Audio::originalProvider) + { + if(!%this.seenWarning) + { + MessageBoxOK("Warning", "Changing sound drivers may result in incompatibilities and game oddities. If you experience such oddities, hit \"Reset\" to restore defaults.", ""); + %this.seenWarning = true; + } + OP_AudioResetProvider.setActive(true); + } + setAudioProvider(%id); +} + +//------------------------------------------------------------------------------ +function OP_AudioResetProvider::onAction(%this) +{ + setAudioProvider($Audio::originalProvider); + %this.setActive(false); +} +//------------------------------------------------------------------------------ +function OP_AudioSpeakerMenu::onSelect(%this, %id, %text) +{ + alxContexti(ALC_SPEAKER, %id); + $pref::Audio::speakerType = alxGetContextstr(ALC_SPEAKER_NAME, %id); +} + +//------------------------------------------------------------------------------ +function OP_AudioFrequencyMenu::init( %this ) +{ + %this.clear(); + %this.add( "11 KHz", 0 ); + %this.add( "22 KHz", 1 ); + %this.add( "44 KHz", 2 ); + + switch ( $pref::Audio::frequency ) + { + case 11025: %this.setSelected( 0 ); + case 22050: %this.setSelected( 1 ); + default: %this.setSelected( 2 ); + } +} + +//------------------------------------------------------------------------------ +function OP_AudioFrequencyMenu::onSelect( %this, %id, %text ) +{ + switch ( %id ) + { + case 0: %newVal = 11025; + case 1: %newVal = 22050; + default: %newVal = 44100; + } + + if ( $pref::Audio::frequency != %newVal ) + { + $pref::Audio::frequency = %newVal; + OptionsDlg.resetAudio = true; + } +} + +//------------------------------------------------------------------------------ +function OP_AudioBitRateMenu::init( %this ) +{ + %this.clear(); + %this.add( "8 bit", 0 ); + %this.add( "16 bit", 1 ); + + if ( $pref::Audio::sampleBits == 8 ) + %this.setSelected( 0 ); + else + %this.setSelected( 1 ); +} + +//------------------------------------------------------------------------------ +function OP_AudioBitRateMenu::onSelect( %this, %id, %text ) +{ + %newVal = %id == 0 ? 8 : 16; + if ( $pref::Audio::sampleBits != %newVal ) + { + $pref::Audio::sampleBits = %newVal; + OptionsDlg.resetAudio = true; + } +} + +//------------------------------------------------------------------------------ +function OP_AudioChannelsMenu::init( %this ) +{ + %this.clear(); + %this.add( "One", 0 ); + %this.add( "Two", 1 ); + + if ( $pref::Audio::channels == 1 ) + %this.setSelected( 0 ); + else + %this.setSelected( 1 ); +} + +//------------------------------------------------------------------------------ +function OP_AudioChannelsMenu::onSelect( %this, %id, %text ) +{ + %newVal = %id == 0 ? 1 : 2; + if ( $pref::Audio::channels != %newVal ) + { + $pref::Audio::channels = %newVal; + OptionsDlg.resetAudio = true; + } +} + +//------------------------------------------------------------------------------ +function OP_MusicTgl::onAction( %this ) +{ + %on = %this.getValue(); + OP_MusicVolumeLabel.setVisible( %on ); + OP_MusicVolumeLabel_Disabled.setVisible( !%on ); + OP_MusicVolumeSlider.setActive( %on ); + $pref::Audio::musicEnabled = %on; + + if ( %on ) + MusicPlayer.play(); + else + MusicPlayer.stop(); +} + +//------------------------------------------------------------------------------ +function updateMusicVolume() +{ + %volume = OP_MusicVolumeSlider.getValue(); + alxSetChannelVolume( $MusicAudioType, %volume ); +} + +//------------------------------------------------------------------------------ +function updateGuiVolume() +{ + %volume = OP_GuiVolumeSlider.getValue(); + alxSetChannelVolume( $GuiAudioType, %volume ); +} + +//------------------------------------------------------------------------------ +function updateMasterVolume() +{ + %volume = OP_MasterVolumeSlider.getValue(); + alxListenerf( AL_GAIN_LINEAR, %volume ); +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Voice Settings: +// +function OP_MicrophoneEnabledTgl::onAction( %this ) +{ + %on = %this.getValue(); + OP_RecordTestBtn.setActive( %on ); + OP_MicVolumeLabel.setVisible( %on ); + OP_MicVolumeLabel_Disabled.setVisible( !%on ); + OP_MicrophoneVolumeSlider.setActive( %on ); + OP_InputBoostLabel.setVisible( %on ); + OP_InputBoostLabel_Disabled.setVisible( !%on ); + OP_InputBoostSlider.setActive( %on ); + OP_InputBoostPercentTxt.setVisible( %on ); + OP_VoiceListenLabel.setVisible( %on ); + OP_VoiceListenLabel_Disabled.setVisible( !%on ); + OP_VoiceListenMenu.setActive( %on ); + OP_VoiceSendLabel.setVisible( %on ); + OP_VoiceSendLabel_Disabled.setVisible( !%on ); + OP_VoiceSendMenu.setActive( %on ); + + if(%on != alxIsEnabled("capture")) + { + if(%on) + alxCaptureInit(); + else + alxCaptureDestroy(); + } +} + +//------------------------------------------------------------------------------ +function updateInputBoost() +{ + %val = OP_InputBoostSlider.getValue(); + alxSetCaptureGainScale( %val ); + %val = mFloor(%val * 100); + OP_InputBoostPercentTxt.setValue(%val @ "%"); +} + +//------------------------------------------------------------------------------ +function OP_RecordTestBtn::onAction( %this ) +{ + alxCaptureStart(true); +} + +//------------------------------------------------------------------------------ +function localCaptureStart( %method ) +{ + if(%method $= "record") + { + OP_RecordTestBtn.setActive(false); + OP_RecordTestBtn.setValue(">> Recording <<"); + } + else + { + OP_RecordTestBtn.setActive(false); + OP_RecordTestBtn.setValue(">> Playing <<"); + } +} + +//------------------------------------------------------------------------------ +function localCaptureStop( %method ) +{ + if(%method $= "play") + { + OP_RecordTestBtn.setActive(true); + OP_RecordTestBtn.setValue("Test Record"); + } +} + +//------------------------------------------------------------------------------ +function OP_VoiceListenMenu::init( %this ) +{ + %this.clear(); + %this.add( "", 0 ); + if ( $platform !$= "linux" ) { + %this.add( ".v12", 1 ); + %this.add( ".v12 - .v24", 3 ); + %this.add( ".v12 - .v29", 7 ); + } + if ( $platform $= "linux" ) { + %this.add( "GSM" , 8 ); + } + + switch ( $pref::Audio::decodingMask ) + { + case 0 or 3 or 7 or 8: + %this.setSelected( $pref::Audio::decodingMask ); + default: + %this.setSelected( 1 ); + } +} + +//------------------------------------------------------------------------------ +function OP_VoiceSendMenu::init( %this ) +{ + %this.clear(); + if ( $platform !$= "linux" ) { + %this.add( ".v12", 0 ); + %this.add( ".v24", 1 ); + %this.add( ".v29", 2 ); + } + if ( $platform $= "linux" ) { + %this.add( "GSM", 3 ); + } + + %this.setSelected($pref::Audio::encodingLevel); +} + +function OP_VoiceCodecInfo::init( %this ) +{ + %headerStyle = ""; + if ( $platform $= "linux" ) { + %displayText = "" @ %headerStyle @ "Voice Codec Information:" NL + "\n" @ + " GSM: fixed bitrate codec (6.6 kbits/sec linux only)" NL + "\n" @ + "" @ + "Setting your codec levels too high can have adverse" @ + " affects on network performance." @ + ""; + } else { + %displayText = "" @ %headerStyle @ "Voice Codec Information:" NL + "\n" @ + " .v12: variable bitrate codec (~1.2 kbits/sec win only)" NL + " .v24: fixed bitrate codec (2.4 kbits/sec win only)" NL + " .v29: fixed bitrate codec (2.9 kbits/sec win only)" NL + "\n" @ + "" @ + "Setting your codec levels too high can have adverse" @ + " affects on network performance." @ + ""; + } + + %this.setText(%displayText); + %this.setActive(false); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Driver Info dialog: +// +function DriverInfoDlg::onWake( %this ) +{ + %headerStyle = ""; + %infoString = getVideoDriverInfo(); + %displayText = "" @ %headerStyle @ "VENDOR:" NL + " " @ getField( %infoString, 0 ) NL + "" @ %headerStyle @ "RENDERER:" NL + " " @ getField( %infoString, 1 ) NL + "" @ %headerStyle @ "VERSION " @ getField( %infoString, 2 ) NL + "\n" @ + "" @ %headerStyle @ "SUPPORTED OPENGL EXTENSIONS:" NL + getField( %infoString, 3 ); + + DriverInfoText.setText( %displayText ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Control remapper section: +// +$RemapCount = 0; +$RemapName[$RemapCount] = "Forward"; +$RemapCmd[$RemapCount] = "moveforward"; +$RemapCount++; +$RemapName[$RemapCount] = "Backward"; +$RemapCmd[$RemapCount] = "movebackward"; +$RemapCount++; +$RemapName[$RemapCount] = "Strafe Left"; +$RemapCmd[$RemapCount] = "moveleft"; +$RemapCount++; +$RemapName[$RemapCount] = "Strafe Right"; +$RemapCmd[$RemapCount] = "moveright"; +$RemapCount++; +$RemapName[$RemapCount] = "Turn Left"; +$RemapCmd[$RemapCount] = "turnLeft"; +$RemapCount++; +$RemapName[$RemapCount] = "Turn Right"; +$RemapCmd[$RemapCount] = "turnRight"; +$RemapCount++; +$RemapName[$RemapCount] = "Look Up"; +$RemapCmd[$RemapCount] = "panUp"; +$RemapCount++; +$RemapName[$RemapCount] = "Look Down"; +$RemapCmd[$RemapCount] = "panDown"; +$RemapCount++; +$RemapName[$RemapCount] = "Jump"; +$RemapCmd[$RemapCount] = "jump"; +$RemapCount++; +$RemapName[$RemapCount] = "Jet Pack"; +$RemapCmd[$RemapCount] = "mouseJet"; +$RemapCount++; +$RemapName[$RemapCount] = "Fire Weapon"; +$RemapCmd[$RemapCount] = "mouseFire"; +$RemapCount++; +$RemapName[$RemapCount] = "Zoom"; +$RemapCmd[$RemapCount] = "toggleZoom"; +$RemapCount++; +$RemapName[$RemapCount] = "Cycle Zoom Level"; +$RemapCmd[$RemapCount] = "setZoomFOV"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot One"; +$RemapCmd[$RemapCount] = "useFirstWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot Two"; +$RemapCmd[$RemapCount] = "useSecondWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot Three"; +$RemapCmd[$RemapCount] = "useThirdWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot Four"; +$RemapCmd[$RemapCount] = "useFourthWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot Five"; +$RemapCmd[$RemapCount] = "useFifthWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Weapon Slot Six"; +$RemapCmd[$RemapCount] = "useSixthWeaponSlot"; +$RemapCount++; +$RemapName[$RemapCount] = "Blaster"; +$RemapCmd[$RemapCount] = "useBlaster"; +$RemapCount++; +$RemapName[$RemapCount] = "Plasma Rifle"; +$RemapCmd[$RemapCount] = "usePlasma"; +$RemapCount++; +$RemapName[$RemapCount] = "Chaingun"; +$RemapCmd[$RemapCount] = "useChaingun"; +$RemapCount++; +$RemapName[$RemapCount] = "Spinfusor"; +$RemapCmd[$RemapCount] = "useDisc"; +$RemapCount++; +$RemapName[$RemapCount] = "Grenade Launcher"; +$RemapCmd[$RemapCount] = "useGrenadeLauncher"; +$RemapCount++; +$RemapName[$RemapCount] = "Laser Rifle"; +$RemapCmd[$RemapCount] = "useSniperRifle"; +$RemapCount++; +$RemapName[$RemapCount] = "ELF Projector"; +$RemapCmd[$RemapCount] = "useELFGun"; +$RemapCount++; +$RemapName[$RemapCount] = "Fusion Mortar"; +$RemapCmd[$RemapCount] = "useMortar"; +$RemapCount++; +$RemapName[$RemapCount] = "Missile Launcher"; +$RemapCmd[$RemapCount] = "useMissileLauncher"; +$RemapCount++; +$RemapName[$RemapCount] = "Shocklance"; +$RemapCmd[$RemapCount] = "useShockLance"; +$RemapCount++; +$RemapName[$RemapCount] = "Targeting Laser"; +$RemapCmd[$RemapCount] = "useTargetingLaser"; +$RemapCount++; +$RemapName[$RemapCount] = "Previous Weapon"; +$RemapCmd[$RemapCount] = "prevWeapon"; +$RemapCount++; +$RemapName[$RemapCount] = "Next Weapon"; +$RemapCmd[$RemapCount] = "nextWeapon"; +$RemapCount++; +$RemapName[$RemapCount] = "Throw Grenade"; +$RemapCmd[$RemapCount] = "throwGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Place Mine"; +$RemapCmd[$RemapCount] = "placeMine"; +$RemapCount++; +$RemapName[$RemapCount] = "Use Pack"; +$RemapCmd[$RemapCount] = "useBackpack"; +$RemapCount++; +$RemapName[$RemapCount] = "Use Health Kit"; +$RemapCmd[$RemapCount] = "useRepairKit"; +$RemapCount++; +$RemapName[$RemapCount] = "Deploy Beacon"; +$RemapCmd[$RemapCount] = "placeBeacon"; +$RemapCount++; +$RemapName[$RemapCount] = "Inventory"; +$RemapCmd[$RemapCount] = "toggleInventoryHud"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 1"; +$RemapCmd[$RemapCount] = "selectFavorite1"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 2"; +$RemapCmd[$RemapCount] = "selectFavorite2"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 3"; +$RemapCmd[$RemapCount] = "selectFavorite3"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 4"; +$RemapCmd[$RemapCount] = "selectFavorite4"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 5"; +$RemapCmd[$RemapCount] = "selectFavorite5"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 6"; +$RemapCmd[$RemapCount] = "selectFavorite6"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 7"; +$RemapCmd[$RemapCount] = "selectFavorite7"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 8"; +$RemapCmd[$RemapCount] = "selectFavorite8"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 9"; +$RemapCmd[$RemapCount] = "selectFavorite9"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 10"; +$RemapCmd[$RemapCount] = "selectFavorite10"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 11"; +$RemapCmd[$RemapCount] = "selectFavorite11"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 12"; +$RemapCmd[$RemapCount] = "selectFavorite12"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 13"; +$RemapCmd[$RemapCount] = "selectFavorite13"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 14"; +$RemapCmd[$RemapCount] = "selectFavorite14"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 15"; +$RemapCmd[$RemapCount] = "selectFavorite15"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 16"; +$RemapCmd[$RemapCount] = "selectFavorite16"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 17"; +$RemapCmd[$RemapCount] = "selectFavorite17"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 18"; +$RemapCmd[$RemapCount] = "selectFavorite18"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 19"; +$RemapCmd[$RemapCount] = "selectFavorite19"; +$RemapCount++; +$RemapName[$RemapCount] = "Favorite 20"; +$RemapCmd[$RemapCount] = "selectFavorite20"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Energy Pack"; +$RemapCmd[$RemapCount] = "quickPackEnergyPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Repair Pack"; +$RemapCmd[$RemapCount] = "quickPackRepairPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Shield Pack"; +$RemapCmd[$RemapCount] = "quickPackShieldPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Cloaking Pack"; +$RemapCmd[$RemapCount] = "quickPackCloakPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Sensor Jammer"; +$RemapCmd[$RemapCount] = "quickPackJammerPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Ammo Pack"; +$RemapCmd[$RemapCount] = "quickPackAmmoPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Satchel Charge"; +$RemapCmd[$RemapCount] = "quickPackSatchelCharge"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Inv Station"; +$RemapCmd[$RemapCount] = "quickPackDeployableStation"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Spider Turret"; +$RemapCmd[$RemapCount] = "quickPackIndoorTurret"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Landspike Turret"; +$RemapCmd[$RemapCount] = "quickPackOutdoorTurret"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Motion Sensor"; +$RemapCmd[$RemapCount] = "quickPackMotionSensor"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Deploy Pulse"; +$RemapCmd[$RemapCount] = "quickPackPulse"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Plasma Barrel"; +$RemapCmd[$RemapCount] = "quickPackPlasmaBarrel"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Missile Barrel"; +$RemapCmd[$RemapCount] = "quickPackMissileBarrel"; +$RemapCount++; +$RemapName[$RemapCount] = "Select AA Barrel"; +$RemapCmd[$RemapCount] = "quickPackAABarrel"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Mortar Barrel"; +$RemapCmd[$RemapCount] = "quickPackMortarBarrel"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Elf Barrel"; +$RemapCmd[$RemapCount] = "quickPackElfBarrel"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Grenade"; +$RemapCmd[$RemapCount] = "quickPackGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Flash Grenade"; +$RemapCmd[$RemapCount] = "quickPackFlashGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Concussion"; +$RemapCmd[$RemapCount] = "quickPackConcussionGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Camera"; +$RemapCmd[$RemapCount] = "quickPackCameraGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Flare Grenade"; +$RemapCmd[$RemapCount] = "quickPackFlareGrenade"; +$RemapCount++; +$RemapName[$RemapCount] = "Command Circuit"; +$RemapCmd[$RemapCount] = "toggleCommanderMap"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Task List"; +$RemapCmd[$RemapCount] = "toggleTaskListDlg"; +$RemapCount++; +$RemapName[$RemapCount] = "Accept Task"; +$RemapCmd[$RemapCount] = "fnAcceptTask"; +$RemapCount++; +$RemapName[$RemapCount] = "Decline Task"; +$RemapCmd[$RemapCount] = "fnDeclineTask"; +$RemapCount++; +$RemapName[$RemapCount] = "Task Completed"; +$RemapCmd[$RemapCount] = "fnTaskCompleted"; +$RemapCount++; +$RemapName[$RemapCount] = "Reset Task List"; +$RemapCmd[$RemapCount] = "fnResetTaskList"; +$RemapCount++; +$RemapName[$RemapCount] = "Vote Yes"; +$RemapCmd[$RemapCount] = "voteYes"; +$RemapCount++; +$RemapName[$RemapCount] = "Vote No"; +$RemapCmd[$RemapCount] = "voteNo"; +$RemapCount++; +$RemapName[$RemapCount] = "Voice Chat Menu"; +$RemapCmd[$RemapCount] = "activateChatMenuHud"; +$RemapCount++; +$RemapName[$RemapCount] = "Global Chat"; +$RemapCmd[$RemapCount] = "ToggleMessageHud"; +$RemapCount++; +$RemapName[$RemapCount] = "Team Chat"; +$RemapCmd[$RemapCount] = "TeamMessageHud"; +$RemapCount++; +$RemapName[$RemapCount] = "Resize Chat Hud"; +$RemapCmd[$RemapCount] = "resizeChatHud"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Microphone"; +$RemapCmd[$RemapCount] = "voiceCapture"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Help Text"; +$RemapCmd[$RemapCount] = "toggleHelpGui"; +$RemapCount++; +$RemapName[$RemapCount] = "Score Screen"; +$RemapCmd[$RemapCount] = "toggleScoreScreen"; +$RemapCount++; +$RemapName[$RemapCount] = "Free Look"; +$RemapCmd[$RemapCount] = "toggleFreeLook"; +$RemapCount++; +$RemapName[$RemapCount] = "Exterior View"; +$RemapCmd[$RemapCount] = "toggleFirstPerson"; +$RemapCount++; +$RemapName[$RemapCount] = "Drop Weapon"; +$RemapCmd[$RemapCount] = "throwWeapon"; +$RemapCount++; +$RemapName[$RemapCount] = "Drop Pack"; +$RemapCmd[$RemapCount] = "throwPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Drop Flag"; +$RemapCmd[$RemapCount] = "throwFlag"; +$RemapCount++; +$RemapName[$RemapCount] = "Suicide"; +$RemapCmd[$RemapCount] = "suicide"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Personal Wypts"; +$RemapCmd[$RemapCount] = "toggleHudWaypoints"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Mission Wypts"; +$RemapCmd[$RemapCount] = "toggleHudMarkers"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Beacons"; +$RemapCmd[$RemapCount] = "toggleHudTargets"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Commands"; +$RemapCmd[$RemapCount] = "toggleHudCommands"; +$RemapCount++; +$RemapName[$RemapCount] = "Construction Tool"; +$RemapCmd[$RemapCount] = "useConstructionTool"; +$RemapCount++; + +// Construction +$RemapName[$RemapCount] = "Select Light Support Beam"; +$RemapCmd[$RemapCount] = "quickPackLightSupportBeam"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Light Walkway"; +$RemapCmd[$RemapCount] = "quickPackLightWalkway"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Light Blast Wall"; +$RemapCmd[$RemapCount] = "quickPackLightBlastWall"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Medium Support Beam"; +$RemapCmd[$RemapCount] = "quickPackMediumSupportBeam"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Medium Floor"; +$RemapCmd[$RemapCount] = "quickPackMediumFloor"; +$RemapCount++; +//$RemapName[$RemapCount] = "Select Disc Turret"; +//$RemapCmd[$RemapCount] = "quickPackDiscTurret"; +//$RemapCount++; +$RemapName[$RemapCount] = "Select Large Inventory Station"; +$RemapCmd[$RemapCount] = "quickPackLargeInventoryStation"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Generator Pack"; +$RemapCmd[$RemapCount] = "quickPackGeneratorPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Solar Panel Pack"; +$RemapCmd[$RemapCount] = "quickPackSolarPanelPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Switch Pack"; +$RemapCmd[$RemapCount] = "quickPackSwitchPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Medium Sensor Pack"; +$RemapCmd[$RemapCount] = "quickPackMediumSensorPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Large Sensor Pack"; +$RemapCmd[$RemapCount] = "quickPackLargeSensorPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Teleport Pad"; +$RemapCmd[$RemapCount] = "quickPackTeleportPad"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Deployable Turret Base"; +$RemapCmd[$RemapCount] = "quickPackDeployableTurretBase"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Energizer"; +$RemapCmd[$RemapCount] = "quickPackEnergizer"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Tree Pack"; +$RemapCmd[$RemapCount] = "quickPackTreePack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Crate Pack"; +$RemapCmd[$RemapCount] = "quickPackCratePack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Decoration Pack"; +$RemapCmd[$RemapCount] = "quickPackDecorationPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Light Pack"; +$RemapCmd[$RemapCount] = "quickPackLightPack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Tripwire Pack"; +$RemapCmd[$RemapCount] = "quickPackTripwirePack"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Logo Projector Pack"; +$RemapCmd[$RemapCount] = "quickPackLogoProjectorPack"; +$RemapCount++; +//$RemapName[$RemapCount] = "Select Laser Turret"; +//$RemapCmd[$RemapCount] = "quickPackLaserTurret"; +//$RemapCount++; +$RemapName[$RemapCount] = "Select Missile Rack Turret"; +$RemapCmd[$RemapCount] = "quickPackMissileRackTurret"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Force Field"; +$RemapCmd[$RemapCount] = "quickPackForceField"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Gravity Field"; +$RemapCmd[$RemapCount] = "quickPackGravityField"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Jump Pad"; +$RemapCmd[$RemapCount] = "quickPackJumpPad"; +$RemapCount++; +$RemapName[$RemapCount] = "Select Escape Pod"; +$RemapCmd[$RemapCount] = "quickPackEscapePod"; +$RemapCount++; +$RemapName[$RemapCount] = "Pack Setting: Fwd"; +$RemapCmd[$RemapCount] = "cyclePackFwd"; +$RemapCount++; +$RemapName[$RemapCount] = "Pack Setting: Back"; +$RemapCmd[$RemapCount] = "cyclePackBack"; +$RemapCount++; +$RemapName[$RemapCount] = "Pack Setting: FFwd"; +$RemapCmd[$RemapCount] = "cyclePackFFwd"; +$RemapCount++; +$RemapName[$RemapCount] = "Pack Setting: FBack"; +$RemapCmd[$RemapCount] = "cyclePackFBack"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Sit Down"; +$RemapCmd[$RemapCount] = "emoteSitDown"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Squat"; +$RemapCmd[$RemapCount] = "emoteSquat"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Jig"; +$RemapCmd[$RemapCount] = "emoteJig"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Lie Down"; +$RemapCmd[$RemapCount] = "emoteLieDown"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Heart Attack"; +$RemapCmd[$RemapCount] = "emoteHeartAttack"; +$RemapCount++; +$RemapName[$RemapCount] = "Emote: Sucker Punched"; +$RemapCmd[$RemapCount] = "emoteSuckerPunched"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Flash Light"; +$RemapCmd[$RemapCount] = "toggleNightVision"; +$RemapCount++; +$RemapName[$RemapCount] = "Slow Motion"; +$RemapCmd[$RemapCount] = "SlowMotion"; +$RemapCount++; +$RemapName[$RemapCount] = "Restrain target"; +$RemapCmd[$RemapCount] = "doGrab"; +$RemapCount++; +$quickPackExtrasBind = true; +// End Construction + +if ( !isDemo() ) +{ + $RemapName[$RemapCount] = "Start Demo Record"; + $RemapCmd[$RemapCount] = "startRecordingDemo"; + $RemapCount++; + $RemapName[$RemapCount] = "Stop Demo Record"; + $RemapCmd[$RemapCount] = "stopRecordingDemo"; + $RemapCount++; +} +$RemapName[$RemapCount] = "Chat Page Up"; +$RemapCmd[$RemapCount] = "pageMessageHudUp"; +$RemapCount++; +$RemapName[$RemapCount] = "Chat Page Down"; +$RemapCmd[$RemapCount] = "pageMessageHudDown"; +$RemapCount++; +$RemapName[$RemapCount] = "Toggle Net Meter"; +$RemapCmd[$RemapCount] = "toggleNetDisplayHud"; +$RemapCount++; + +$ObsRemapCount = 0; +$ObsRemapName[$ObsRemapCount] = "Move Up"; +$ObsRemapCmd[$ObsRemapCount] = "moveup"; +$ObsRemapCount++; +$ObsRemapName[$ObsRemapCount] = "Move Down"; +$ObsRemapCmd[$ObsRemapCount] = "movedown"; +$ObsRemapCount++; +$ObsRemapName[$ObsRemapCount] = "Toggle Observer Mode"; +$ObsRemapCmd[$ObsRemapCount] = "jump"; +$ObsRemapCount++; +$ObsRemapName[$ObsRemapCount] = "Spawn/Previous"; +$ObsRemapCmd[$ObsRemapCount] = "mouseFire"; +$ObsRemapCount++; +$ObsRemapName[$ObsRemapCount] = "Cycle Camera/Next"; +$ObsRemapCmd[$ObsRemapCount] = "mouseJet"; +$ObsRemapCount++; + +//------------------------------------------------------------------------------ +function restoreDefaultMappings() +{ + moveMap.delete(); + exec( "scripts/controlDefaults.cs" ); + $pref::Input::ActiveConfig = "MyConfig"; + OP_RemapList.fillList(); +} + +//------------------------------------------------------------------------------ +function isMapFile( %file ) +{ + %fObject = new FileObject(); + if ( !%fObject.openForRead( %file ) ) + return( false ); + + while ( !%fObject.isEOF() ) + { + %line = %fObject.readLine(); + if ( %line $= "// Tribes 2 Input Map File" ) + { + %fObject.close(); + return( true ); + } + } + + %fObject.close(); + return( false ); +} + +//------------------------------------------------------------------------------ +function isValidMapFileSaveName( %file ) +{ + if (isDemo()) + %basePath = "demo_base/"; + else + %basePath = "base/"; + if ( !isWriteableFileName( %basePath @ %file ) ) + return( false ); + + if ( isFile( %file ) ) + return( isMapFile( %file ) ); + + return( true ); +} + +//------------------------------------------------------------------------------ +function loadMapFile( %filename ) +{ + exec( "prefs/" @ %filename @ ".cs" ); + $pref::Input::ActiveConfig = %filename; + OP_RemapList.fillList(); +} + +//------------------------------------------------------------------------------ +function saveActiveMapFile() +{ + if ( isValidMapFileSaveName( "prefs/" @ $pref::Input::ActiveConfig @ ".cs" ) ) + saveMapFile( $pref::Input::ActiveConfig ); + else + ShellGetSaveFilename( "SAVE CONTROL CONFIG", "prefs/*.cs", "isMapFile", "saveMapFile", "" ); +} + +//------------------------------------------------------------------------------ +function saveMapFile( %filename ) +{ + if ( strcspn( %filename, "\\/?*\"\'<>|" ) < strlen( %filename ) ) + { + MessageBoxOK( "SAVE FAILED", "Filenames may not contain any of the following characters:" NL "\\ / ? * < > \" \' |", + "ShellGetSaveFilename( \"SAVE CONTROL CONFIG\", \"prefs/*.cs\", \"isMapFile\", \"saveMapFile\", $pref::Input::ActiveConfig );" ); + return; + } + + if (isDemo()) + %basePath = "demo_base/"; + else + %basePath = "base/"; + %mapFile = "prefs/" @ %filename @ ".cs"; + if ( !isWriteableFileName( %basePath @ %mapFile ) ) + { + MessageBoxOK( "SAVE FAILED", "That is not a writeable file name. Please choose another file name.", + "ShellGetSaveFilename( \"SAVE CONTROL CONFIG\", \"prefs/*.cs\", \"isMapFile\", \"saveMapFile\", $pref::Input::ActiveConfig );" ); + return; + } + + if ( isFile( %mapFile ) && !isMapFile( %mapFile ) ) + { + MessageBoxOK( "SAVE FAILED", "A file of that name already exists and is not an input configuration file. Please choose another file name.", + "ShellGetSaveFilename( \"SAVE CONTROL CONFIG\", \"prefs/*.cs\", \"isMapFile\", \"saveMapFile\", $pref::Input::ActiveConfig );" ); + return; + } + + moveMap.save( %mapFile ); + // Append the observer action map: + observerMap.save( %mapFile, true ); + + // Write out the console toggle key: + %fObject = new FileObject(); + if ( %fObject.openForAppend( %mapFile ) ) + { + %bind = GlobalActionMap.getBinding( "toggleConsole" ); + if ( %bind !$= "" ) + { + %fObject.writeLine( "GlobalActionMap.bind(keyboard, \"" @ getField( %bind, 1 ) @ "\", toggleConsole);" ); + %fObject.close(); + } + } + %fObject.delete(); + + $pref::Input::ActiveConfig = %filename; +} + +//------------------------------------------------------------------------------ +function getMapDisplayName( %device, %action ) +{ + if ( %device $= "keyboard" ) + return( %action ); + else if ( strstr( %device, "mouse" ) != -1 ) + { + // Substitute "mouse" for "button" in the action string: + %pos = strstr( %action, "button" ); + if ( %pos != -1 ) + { + %mods = getSubStr( %action, 0, %pos ); + %object = getSubStr( %action, %pos, 1000 ); + %instance = getSubStr( %object, strlen( "button" ), 1000 ); + return( %mods @ "mouse" @ ( %instance + 1 ) ); + } + else + error( "Mouse input object other than button passed to getDisplayMapName!" ); + } + else if ( strstr( %device, "joystick" ) != -1 ) + { + // Substitute "joystick" for "button" in the action string: + %pos = strstr( %action, "button" ); + if ( %pos != -1 ) + { + %mods = getSubStr( %action, 0, %pos ); + %object = getSubStr( %action, %pos, 1000 ); + %instance = getSubStr( %object, strlen( "button" ), 1000 ); + return( %mods @ "joystick" @ ( %instance + 1 ) ); + } + else + { + %pos = strstr( %action, "pov" ); + if ( %pos != -1 ) + { + %wordCount = getWordCount( %action ); + %mods = %wordCount > 1 ? getWords( %action, 0, %wordCount - 2 ) @ " " : ""; + %object = getWord( %action, %wordCount - 1 ); + switch$ ( %object ) + { + case "upov": %object = "POV1 up"; + case "dpov": %object = "POV1 down"; + case "lpov": %object = "POV1 left"; + case "rpov": %object = "POV1 right"; + case "upov2": %object = "POV2 up"; + case "dpov2": %object = "POV2 down"; + case "lpov2": %object = "POV2 left"; + case "rpov2": %object = "POV2 right"; + default: %object = "??"; + } + return( %mods @ %object ); + } + else + error( "Unsupported Joystick input object passed to getDisplayMapName!" ); + } + } + + return( "??" ); +} + +//------------------------------------------------------------------------------ +function buildFullMapString( %index ) +{ + switch$ ( OP_ControlsPane.group ) + { + case "Observer": + %actionMap = observerMap; + %name = $ObsRemapName[%index]; + %cmd = $ObsRemapCmd[%index]; + + default: + %actionMap = moveMap; + %name = $RemapName[%index]; + %cmd = $RemapCmd[%index]; + } + + %temp = %actionMap.getBinding( %cmd ); + %device = getField( %temp, 0 ); + %object = getField( %temp, 1 ); + if ( %device !$= "" && %object !$= "" ) + %mapString = getMapDisplayName( %device, %object ); + else + %mapString = ""; + + return( %name TAB %mapString ); +} + +//------------------------------------------------------------------------------ +function OP_ControlGroupMenu::init( %this ) +{ + %selId = %this.getSelected(); + %this.clear(); + %this.add( "Main", 0 ); + %this.add( "Observer", 1 ); + %this.setSelected( %selId ); + %this.onSelect( %selId, %this.getTextById( %selId ) ); +} + +//------------------------------------------------------------------------------ +function OP_ControlGroupMenu::onSelect( %this, %id, %text ) +{ + OP_ControlsPane.group = %text; + OP_RemapList.fillList(); +} + +//------------------------------------------------------------------------------ +function OP_RemapList::fillList( %this ) +{ + switch$ ( OP_ControlsPane.group ) + { + case "Observer": %count = $ObsRemapCount; + default: %count = $RemapCount; + } + + %this.clear(); + for ( %i = 0; %i < %count; %i++ ) + %this.addRow( %i, buildFullMapString( %i ) ); + + // Set the console key: + %bind = GlobalActionMap.getBinding( "toggleConsole" ); + OP_ConsoleKeyBtn.setValue( getField( %bind, 1 ) ); +} + +//------------------------------------------------------------------------------ +function OP_RemapList::onDeleteKey( %this, %rowId ) +{ + switch$ ( OP_ControlsPane.group ) + { + case "Observer": + %actionMap = observerMap; + %cmd = $ObsRemapCmd[%rowId]; + default: + %actionMap = moveMap; + %cmd = $RemapCmd[%rowId]; + } + clearMapping( %actionMap, %cmd ); + %this.setRowById( %rowId, buildFullMapString( %rowId ) ); +} + +//------------------------------------------------------------------------------ +function OP_RemapList::doRemap( %this ) +{ + %selId = %this.getSelectedId(); + switch$ ( OP_ControlsPane.group ) + { + case "Observer": %name = $ObsRemapName[%selId]; + default: %name = $RemapName[%selId]; + } + + RemapFrame.setTitle( "REMAP \"" @ %name @ "\"" ); + RemapInputCtrl.mode = "move"; + RemapInputCtrl.index = %selId; + Canvas.pushDialog( RemapDlg ); +} + +//------------------------------------------------------------------------------ +function OP_ConsoleKeyBtn::doRemap( %this ) +{ + RemapFrame.setTitle( "REMAP \"Toggle Console\"" ); + RemapInputCtrl.mode = "consoleKey"; + RemapInputCtrl.index = 0; + Canvas.pushDialog( RemapDlg ); +} + +//------------------------------------------------------------------------------ +function RemapDlg::onWake( %this ) +{ + $enableDirectInput = "1"; + activateDirectInput(); + + if ( RemapInputCtrl.mode $= "consoleKey" ) + RemapText.setText( "Press a key to assign it to this action" NL "or Esc to cancel..." ); + else + RemapText.setText( "Press a key or button to assign it to this action" NL "or Esc to cancel..." ); +} + +//------------------------------------------------------------------------------ +function RemapDlg::onSleep( %this ) +{ + $enableDirectInput = "1"; + deactivateDirectInput(); +} + +//------------------------------------------------------------------------------ +function findRemapCmdIndex( %command ) +{ + switch$ ( OP_ControlsPane.group ) + { + case "Observer": + for ( %i = 0; %i < $ObsRemapCount; %i++ ) + { + if ( %command $= $ObsRemapCmd[%i] ) + return( %i ); + } + default: + for ( %i = 0; %i < $RemapCount; %i++ ) + { + if ( %command $= $RemapCmd[%i] ) + return( %i ); + } + } + + return( -1 ); +} + +//------------------------------------------------------------------------------ +function clearMapping( %actionMap, %cmd ) +{ + %fullMapString = %actionMap.getBinding( %cmd ); + %mapCount = getRecordCount( %fullMapString ); + for ( %i = 0; %i < %mapCount; %i++ ) + { + %temp = getRecord( %fullMapString, %i ); + %actionMap.unbind( getField( %temp, 0 ), getField( %temp, 1 ) ); + } +} + +//------------------------------------------------------------------------------ +function redoMapping( %actionMap, %device, %action, %cmd, %oldIndex, %newIndex ) +{ + //%actionMap.bind( %device, %action, $RemapCmd[%newIndex] ); + %actionMap.bind( %device, %action, %cmd ); + OP_RemapList.setRowById( %oldIndex, buildFullMapString( %oldIndex ) ); + OP_RemapList.setRowById( %newIndex, buildFullMapString( %newIndex ) ); +} + +//------------------------------------------------------------------------------ +function redoConsoleMapping( %action, %oldIndex ) +{ + moveMap.unbind( "keyboard", %action ); + GlobalActionMap.bind( "keyboard", %action, "toggleConsole" ); + OP_ConsoleKeyBtn.setValue( %action ); + OP_RemapList.setRowById( %oldIndex, buildFullMapString( %oldIndex ) ); +} + +//------------------------------------------------------------------------------ +function RemapInputCtrl::onInputEvent( %this, %device, %action ) +{ + //error( "** onInputEvent called - device = " @ %device @ ", action = " @ %action @ " **" ); + Canvas.popDialog( RemapDlg ); + + // Test for the reserved keystrokes: + if ( %device $= "keyboard" ) + { + // Cancel... + if ( %action $= "escape" ) + { + // Do nothing... + return; + } + } + + if ( %this.mode $= "consoleKey" ) + { + if ( %device !$= "keyboard" ) + { + MessageBoxOK( "REMAP FAILED", "This command can only be bound to keys on the keyboard!" ); + return; + } + + %prevMap = GlobalActionMap.getCommand( %device, %action ); + if ( %prevMap !$= "" ) + { + MessageBoxOK( "REMAP FAILED", "\"" @ getMapDisplayName( %device, %action ) @ "\" is already bound to a non-remappable command!" ); + return; + } + + %mvMap = moveMap.getCommand( %device, %action ); + if ( %mvMap $= "" ) + { + GlobalActionMap.bind( %device, %action, "toggleConsole" ); + OP_ConsoleKeyBtn.setValue( %action ); + } + else + { + %mapName = getMapDisplayName( %device, %action ); + %mvMapIndex = findRemapCmdIndex( %mvMap ); + if ( %mvMapIndex == -1 ) + MessageBoxOK( "REMAP FAILED", "\"" @ %mapName @ "\" is already bound to a non-remappable command!" ); + else + MessageBoxYesNo( "WARNING", "\"" @ %mapName @ "\" is already bound to \"" + @ $RemapName[%mvMapIndex] @ "\"!" + NL "Do you want to undo this mapping?", + "redoConsoleMapping(\"" @ %action @ "\", " @ %mvMapIndex @ ");", "" ); + return; + } + } + else + { + switch$ ( OP_ControlsPane.group ) + { + case "Observer": + %actionMap = observerMap; + %cmd = $ObsRemapCmd[%this.index]; + %name = $ObsRemapName[%this.index]; + + default: + %actionMap = moveMap; + %cmd = $RemapCmd[%this.index]; + %name = $RemapName[%this.index]; + } + + // First check to see if the given action is already mapped: + %prevMap = %actionMap.getCommand( %device, %action ); + if ( %prevMap !$= %cmd ) + { + if ( %prevMap $= "" ) + { + %actionMap.bind( %device, %action, %cmd ); + OP_RemapList.setRowById( %this.index, buildFullMapString( %this.index ) ); + } + else + { + %mapName = getMapDisplayName( %device, %action ); + %prevMapIndex = findRemapCmdIndex( %prevMap ); + if ( %prevMapIndex == -1 ) + MessageBoxOK( "REMAP FAILED", "\"" @ %mapName @ "\" is already bound to a non-remappable command!" ); + else + { + switch$ ( OP_ControlsPane.group ) + { + case "Observer": + %prevCmdName = $ObsRemapName[%prevMapIndex]; + default: + %prevCmdName = $RemapName[%prevMapIndex]; + } + + MessageBoxYesNo( "WARNING", + "\"" @ %mapName @ "\" is already bound to \"" + @ %prevCmdName @ "\"!\nDo you want to undo this mapping?", + "redoMapping(" @ %actionMap @ ", " @ %device @ ", \"" @ %action @ "\", \"" @ %cmd @ "\", " @ %prevMapIndex @ ", " @ %this.index @ ");", "" ); + } + return; + } + } + } +} + +//------------------------------------------------------------------------------ +function OP_JoystickTgl::onAction( %this ) +{ + %on = %this.getValue(); + if ( %on ) + enableJoystick(); + else + disableJoystick(); + + OP_ConfigureJoystickBtn.setActive( %on ); +} + +//------------------------------------------------------------------------------ +function MouseConfigDlg::onWake( %this ) +{ + MouseXSlider.setValue( moveMap.getScale( mouse, xaxis ) / 2 ); + MouseYSlider.setValue( moveMap.getScale( mouse, yaxis ) / 2 ); + InvertMouseTgl.setValue( moveMap.isInverted( mouse, yaxis ) ); + + MouseZActionMenu.clear(); + MouseZActionMenu.add( "Nothing", 1 ); + MouseZActionMenu.add( "Cycle Weapon", 2 ); + MouseZActionMenu.add( "Next Weapon Only", 3 ); +// MouseZActionMenu.add( "Cycle Zoom Level", 4 ); + + %bind = moveMap.getCommand( "mouse", "zaxis" ); + %selId = 1; + switch$ ( %bind ) + { + case "cycleWeaponAxis": + %selId = 2; + case "cycleNextWeaponOnly": + %selId = 3; + } + MouseZActionMenu.setSelected( %selId ); +} + +//------------------------------------------------------------------------------ +function MouseConfigDlg::onOK( %this ) +{ + %xSens = MouseXSlider.getValue() * 2; + %ySens = MouseYSlider.getValue() * 2; + moveMap.bind( mouse, xaxis, "S", %xSens, "yaw" ); + %yFlags = InvertMouseTgl.getValue() ? "SI" : "S"; + moveMap.bind( mouse, yaxis, %yFlags, %ySens, "pitch" ); + + switch ( MouseZActionMenu.getSelected() ) + { + case 2: + moveMap.bind( mouse, zaxis, cycleWeaponAxis ); + case 3: + moveMap.bind( mouse, zaxis, cycleNextWeaponOnly ); + default: + moveMap.unbind( mouse, zaxis ); + } + + Canvas.popDialog( MouseConfigDlg ); +} + +//------------------------------------------------------------------------------ +function MouseXSlider::sync( %this ) +{ + %thisValue = %this.getValue(); + MouseXText.setValue( "(" @ getSubStr( %thisValue, 0, 4 ) @ ")" ); + if ( $pref::Input::LinkMouseSensitivity ) + { + if ( MouseYSlider.getValue() != %thisValue ) + MouseYSlider.setValue( %thisValue ); + } +} + +//------------------------------------------------------------------------------ +function MouseYSlider::sync( %this ) +{ + %thisValue = %this.getValue(); + MouseYText.setValue( "(" @ getSubStr( %thisValue, 0, 4 ) @ ")" ); + if ( $pref::Input::LinkMouseSensitivity ) + { + if ( MouseXSlider.getValue() != %thisValue ) + MouseXSlider.setValue( %thisValue ); + } +} + +//------------------------------------------------------------------------------ +// Joystick Config dialog: +//------------------------------------------------------------------------------ +$JoyRemapCount = 0; +$JoyRemapName[$JoyRemapCount] = "Look Up/Down"; +$JoyRemapCmd[$JoyRemapCount] = "joyPitch"; +$JoyRemapCount++; +$JoyRemapName[$JoyRemapCount] = "Turn Left/Right"; +$JoyRemapCmd[$JoyRemapCount] = "joyYaw"; +$JoyRemapCount++; +$JoyRemapName[$JoyRemapCount] = "Move Forward/Backward"; +$JoyRemapCmd[$JoyRemapCount] = "joystickMoveY"; +$JoyRemapCount++; +$JoyRemapName[$JoyRemapCount] = "Strafe Left/Right"; +$JoyRemapCmd[$JoyRemapCount] = "joystickMoveX"; +$JoyRemapCount++; +$JoyRemapName[$JoyRemapCount] = "Cycle Weapon"; +$JoyRemapCmd[$JoyRemapCount] = "cycleWeaponAxis"; +$JoyRemapCount++; + +//------------------------------------------------------------------------------ +function JoystickConfigDlg::onWake( %this ) +{ + // Add all of the axis tabs: + %temp = getJoystickAxes( 0 ); + %tryCount = getField( %temp, 0 ); + $JoyAxisCount = 0; + + for ( %i = 0; %i < %tryCount; %i++ ) + { + %type = getField( %temp, %i + 1 ); + switch$ ( %type ) + { + case "X": %tabName = "X Axis"; %tabType = "xaxis"; + case "Y": %tabName = "Y Axis"; %tabType = "yaxis"; + case "Z": %tabName = "Z Axis"; %tabType = "zaxis"; + case "R": %tabName = "R Axis"; %tabType = "rxaxis"; + case "U": %tabName = "U Axis"; %tabType = "ryaxis"; + case "V": %tabName = "V Axis"; %tabType = "rzaxis"; + case "S": %tabName = "Slider"; %tabType = "slider"; + case "L": %tabName = "Slider 2"; %tabType = "slider2"; + default: %tabName = ""; + } + + if ( %tabName !$= "" ) + { + $JoyAxisTab[$JoyAxisCount] = new ShellTabButton() { + profile = "ShellTabProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "29" SPC ( 52 + ( %i * 30 ) ); + extent = "100 38"; + minExtent = "48 38"; + visible = "1"; + command = "JoystickConfigDlg.setPane(" @ %i @ ");"; + helpTag = "0"; + text = %tabName; + type = %tabType; + }; + + $JoyAxisCount++; + JoystickConfigFrame.add( $JoyAxisTab[%i] ); + } + } + + // Fill the action menu: + JoyAxisActionMenu.clear(); + for ( %i = 0; %i < $JoyRemapCount; %i++ ) + JoyAxisActionMenu.add( $JoyRemapName[%i], %i ); + JoyAxisActionMenu.add( "Nothing", 255 ); + + // Select the first axis: + %this.setPane( %this.pane ); +} + +//------------------------------------------------------------------------------ +function JoystickConfigDlg::onSleep( %this ) +{ + // Save the current pane's settings: + bindJoystickAxis( %this.pane, JoyAxisActionMenu.getSelected() ); + for ( %i = 0; %i < $JoyAxisCount; %i++ ) + { + JoystickConfigFrame.remove( $JoyAxisTab[%i] ); + $JoyAxisTab[%i].delete(); + } +} + +//------------------------------------------------------------------------------ +function JoystickConfigDlg::setPane( %this, %pane ) +{ + if ( %this.pane != %pane ) + { + // Save the previous axes' settings: + bindJoystickAxis( %this.pane, JoyAxisActionMenu.getSelected() ); + %this.pane = %pane; + } + + for ( %i = 0; %i < $joyAxisCount; %i++ ) + $JoyAxisTab[%i].setValue( %i == %pane ); + + // Update the config controls: + %axisType = $JoyAxisTab[%pane].type; + %bind = moveMap.getCommand( "joystick", %axisType ); + if ( %bind !$= "" ) + { + for ( %i = 0; %i < $JoyRemapCount; %i++ ) + { + if ( $JoyRemapCmd[%i] $= %bind ) + { + JoyAxisActionMenu.setSelected( %i ); + JoyAxisActionMenu.setText( $JoyRemapName[%i] ); + JoyAxisActionMenu.onSelect( %i, "" ); + break; + } + } + + if ( %i == $JoyRemapCount ) + { + JoyAxisActionMenu.setSelected( 255 ); // 255 is the code for "Nothing" + JoyAxisActionMenu.onSelect( 255, "" ); + } + + %scale = moveMap.getScale( "joystick", %axisType ); + JoyAxisSlider.setValue( %scale / 100 ); + %deadZone = moveMap.getDeadZone( "joystick", %axisType ); + if ( %deadZone $= "0 0" ) + DeadZoneSlider.setValue( 0.0 ); + else + DeadZoneSlider.setValue( abs( firstWord( %deadZone ) ) / %scale ); + InvertJoyAxisTgl.setValue( moveMap.isInverted( "joystick", %axisType ) ); + //JoyAxisRelativeTgl.setValue( moveMap.isRelativeAxis( "joystick", %axisType ) ); + } + else + { + JoyAxisActionMenu.setSelected( 255 ); // 255 is the code for "Nothing" + JoyAxisActionMenu.onSelect( 255, "" ); + JoyAxisSlider.setValue( 0.5 ); + DeadZoneSlider.setValue( 0.0 ); + InvertJoyAxisTgl.setValue( false ); + //JoyAxisRelativeTgl.setValue( %axisType $= "slider" ); + } +} + +//------------------------------------------------------------------------------ +function JoyAxisActionMenu::onSelect( %this, %id, %text ) +{ + %on = ( %id < $JoyRemapCount ); + JoyAxisSlider.setActive( %on ); + JoySensText.setVisible( %on ); + DeadZoneSlider.setActive( %on ); + DeadZoneText.setVisible( %on ); + InvertJoyAxisTgl.setActive( %on ); + //JoyAxisRelativeTgl.setActive( %on ); +} + +//------------------------------------------------------------------------------ +function JoySensText::update( %this ) +{ + %this.setValue( "(" @ getSubStr( JoyAxisSlider.getValue(), 0, 4 ) @ ")" ); +} + +//------------------------------------------------------------------------------ +function DeadZoneText::update( %this ) +{ + %val = DeadZoneSlider.getValue(); + %percent = %val * 100; + %temp = strstr( %percent, "." ); + if ( %temp != -1 ) + %percent = getSubStr( %percent, 0, %temp ); + + %this.setValue( "(" @ %percent @ "%)" ); +} + +//------------------------------------------------------------------------------ +function bindJoystickAxis( %axisIndex, %cmdIndex ) +{ + %cmd = $JoyRemapCmd[%cmdIndex]; + %axis = $JoyAxisTab[%axisIndex].type; + if ( %cmdIndex > $JoyRemapCount ) + { + // Make sure the axis is unbound: + moveMap.unbind( "joystick", %axis ); + return; + } + + %sens = JoyAxisSlider.getValue() * 100; + %delta = DeadZoneSlider.getValue() * %sens; + %flags = "S"; + if ( InvertJoyAxisTgl.getValue() ) + %flags = %flags @ "I"; +// if ( JoyAxisRelativeTgl.getValue() ) +// %flags = %flags @ "L"; + if ( %delta > 0 ) + { + %deadZone = "-" @ %delta SPC %delta; + moveMap.bind( "joystick", %axis, %flags @ "D", %deadZone, %sens, %cmd ); + } + else + moveMap.bind( "joystick", %axis, %flags, %sens, %cmd ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Network Settings: +// + +function updateNetworkSettings() +{ + $pref::Net::PacketRateToClient = mFloor( OP_PacketRateSlider.getValue() ); + $pref::Net::PacketSize = mFloor( OP_PacketSizeSlider.getValue() ); + $pref::Net::PacketRateToServer = mFloor( OP_UpdateRateSlider.getValue() ); + + // check the max rate: + if ( isObject( ServerConnection ) ) + ServerConnection.checkMaxRate(); + if ( isObject( ClientGroup ) ) + { + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %cl = ClientGroup.getObject( %i ); + %cl.checkMaxRate(); + } + } +} + +function OP_NetworkDisplayHud::init(%this) +{ + %this.getPrefs(); + + %this.textHeight = 14; + %this.textOffset = 2; + + if(!%this.infoCallback) + { + %this.textProfile = 0; + return; + } + + // profile for the text fields + %this.textProfile = new GuiControlProfile() + { + fontType = $ShellButtonFont; + fontSize = $ShellButtonFontSize; + autoSizeWidth = true; + autoSizeHeight = true; + fontColors[6] = "128 128 128"; + }; + + %yOffset = %this.textOffset; + + for(%i = 0; %i < 6; %i++) + { + // set the text color + %this.textProfile.fontColors[%i] = %this.fieldColors[%i]; + + // create the text field + %this.textField[%i] = new GuiTextCtrl() + { + profile = %this.textProfile; + horizSizing = "right"; + vertSizing = "bottom"; + position = "20 " @ %yOffset; + extent = "190 " @ %this.textHeight; + visible = "1"; + }; + + // create the toggle field + %this.toggleField[%i] = new GuiTextCtrl() + { + profile = ShellActiveTextProfile; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 " @ %yOffset; + extent = "15 " @ %this.textHeight; + visible = "1"; + }; + + // create a mouse object + %this.mouseField[%i] = new GuiMouseEventCtrl(NetworkDisplayMouseCtrl) + { + profile = GuiDefaultProfile; + horizSizing = "right"; + vertSizing = "bottom"; + position = "10 " @ %yOffset; + extent = "200 " @ %this.textHeight; + visible = "1"; + fieldIndex = %i; + }; + + OP_NetworkDisplayTextFrame.add(%this.textField[%i]); + OP_NetworkDisplayTextFrame.add(%this.toggleField[%i]); + OP_NetworkDisplayTextFrame.add(%this.mouseField[%i]); + + %yOffset += (%this.textHeight + %this.textOffset); + } + %this.infoUpdate(0, 0, 0, 0, 0, 0); +} + +function NetworkDisplayMouseCtrl::onMouseDown(%this) +{ + %b = OP_NetworkDisplayHud.renderField[%this.fieldIndex]; + OP_NetworkDisplayHud.renderField[%this.fieldIndex] = !%b; + OP_NetworkDisplayHud.updateToggles(); +} + +function OP_NetworkDisplayHud::uninit(%this) +{ + if(!%this.infoCallback) + return; + + if(isObject(%this.textProfile)) + %this.textProfile.delete(); + + for(%i = 0; %i < 6; %i++) + { + if(isObject(%this.textField[%i])) + %this.textField[%i].delete(); + + if(isObject(%this.toggleField[%i])) + %this.toggleField[%i].delete(); + + if(isObject(%this.mouseField[%i])) + %this.mouseField[%i].delete(); + } +} + +function OP_NetworkDisplayHud::updateToggles(%this) +{ + // update the toggles + $pref::Net::graphFields = 0; + + for(%i = 0; %i < 6; %i++) + { + $pref::Net::graphFields |= %this.renderField[%i] << %i; + %this.toggleField[%i].setText(%this.renderField[%i] ? "+" : "-"); + } +} + +function OP_NetworkDisplayHud::infoUpdate(%this, %ping, %packetLoss, %sendPackets, %sendBytes, %receivePackets, %receiveBytes) +{ + %this.updateToggles(); + + // set the text + %this.textField[0].setText("\c0Ping: " @ mFormatFloat(%ping, "%4.0f") @ "ms"); + %this.textField[1].setText("\c1Packet Loss: " @ mFormatFloat(%packetLoss, "%3.0f") @ "%"); + %this.textField[2].setText("\c2Send Packets: " @ mFormatFloat(%sendPackets, "%2.1f") @ "pps"); + %this.textField[3].setText("\c3Send Bytes: " @ mFormatFloat(%sendBytes, "%5.0f") @ "bps"); + %this.textField[4].setText("\c4Receive Packets: " @ mFormatFloat(%receivePackets, "%2.1f") @ "pps"); + %this.textField[5].setText("\c5Receive Bytes: " @ mFormatFloat(%receiveBytes, "%5.0f") @ "bps"); +} + +// "" +// [1,32] [8,32] [100,450] +$NetworkPresetCount = 0; +$NetworkPreset[$NetworkPresetCount] = "28.8 Modem\t12\t16\t200"; $NetworkPresetCount++; +$NetworkPreset[$NetworkPresetCount] = "56K Modem\t16\t20\t240"; $NetworkPresetCount++; +$NetworkPreset[$NetworkPresetCount] = "DSL\t20\t24\t350"; $NetworkPresetCount++; +$NetworkPreset[$NetworkPresetCount] = "Cable\t24\t24\t400"; $NetworkPresetCount++; +$NetworkPreset[$NetworkPresetCount] = "T1/LAN\t32\t32\t450"; $NetworkPresetCount++; + +function OP_NetworkPresetsMenu::init( %this ) +{ + %this.clear(); + for(%i = 0; %i < $NetworkPresetCount; %i++) + %this.add( getField($NetworkPreset[%i], 0), %i); + + // don't update settings on init (only update when values change) + %this.updateSettings = false; + %this.setSelected($pref::Net::Preset); + %this.updateSettings = true; +} + +function OP_NetworkPresetsMenu::onSelect( %this, %id, %text ) +{ + OP_PacketRateSlider.setValue( getField($NetworkPreset[%id], 1) ); + OP_UpdateRateSlider.setValue( getField($NetworkPreset[%id], 2) ); + OP_PacketSizeSlider.setValue( getField($NetworkPreset[%id], 3) ); + + if(%this.updateSettings) + updateNetworkSettings(); + $pref::Net::Preset = %id; +} + +//------------------------------------------------------------------------------ +function OP_MasterServerMenu::init( %this ) +{ + %this.clear(); + // You can change these strings, but NOT THE IDS! + %this.add( "Always", 1 ); + %this.add( "When Not Full", 2 ); + %this.add( "Never", 3 ); +} + +//------------------------------------------------------------------------------ +function OP_MasterServerMenu::onSelect( %this, %id, %text ) +{ + switch( %id ) + { + case 2: + $pref::Net::DisplayOnMaster = "NotFull"; + case 3: + $pref::Net::DisplayOnMaster = "Never"; + default: + $pref::Net::DisplayOnMaster = "Always"; + } +} + +//------------------------------------------------------------------------------ +function OP_RegionMenu::init( %this ) +{ + %this.clear(); + %this.add( "North America East", 1 ); + %this.add( "North America West", 2 ); + %this.add( "South America", 4 ); + %this.add( "Australia", 8 ); + %this.add( "Asia", 16 ); + %this.add( "Europe", 32 ); +} + +//------------------------------------------------------------------------------ +function OP_RegionMenu::onSelect( %this, %id, %text ) +{ + $pref::Net::RegionMask = %id; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Game Settings: +// +function OP_LaunchScreenMenu::init( %this ) +{ + %this.clear(); + %this.add( "Game", 1 ); + %this.add( "Email", 4 ); + %this.add( "Chat", 5 ); + %this.add( "Browser", 6 ); +} + +//------------------------------------------------------------------------------ +function toggleInvertYAxis() +{ + // Catch the case where this is toggled in-game while in a vehicle: + if ( isObject( passengerKeys ) ) + { + %bind = passengerKeys.getBinding( pitch ); + if ( %bind !$= "" ) + { + %device = getField( %bind, 0 ); + %action = getField( %bind, 1 ); + %flags = $pref::Vehicle::InvertYAxis ? "SDI" : "SD"; + %deadZone = passengerKeys.getDeadZone( %device, %action ); + %scale = passengerKeys.getScale( %device, %action ); + passengerKeys.bind( %device, %action, %flags, %deadZone, %scale, pitch ); + } + } +} + +//------------------------------------------------------------------------------ +function toggleImmersion() +{ + MessageBoxOK( "Force Feedback", "This will take effect the next time you start Tribes 2." ); +} + +//------------------------------------------------------------------------------ +function toggleVehicleTeleportPref() +{ + // If we are in a game, let the server know we've changed; + if ( isObject( ServerConnection ) ) + commandToServer( 'EnableVehicleTeleport', $pref::Vehicle::pilotTeleport ); +} diff --git a/Scripts/Packs/ArtilleryLoadoutPack.cs b/Scripts/Packs/ArtilleryLoadoutPack.cs new file mode 100644 index 0000000..3392d60 --- /dev/null +++ b/Scripts/Packs/ArtilleryLoadoutPack.cs @@ -0,0 +1,83 @@ +//-------------------------------------------------------------------------- +// Loadout Pack +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(artilleryWeaponPackImage) +{ + mass = 15; + + shapeFile = "turret_tank_barrelmortar.dts"; + item = artilleryWeaponPack; + mountPoint = 1; + offset = "0 -0.3 -0.75"; + rotation = "-1 0 0 90"; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Deactivate"; + + stateName[2] = "Deactivate"; + stateScript[2] = "onDeactivate"; + stateTransitionOnTimeOut[2] = "Idle"; + + isLarge = true; +}; + +datablock ItemData(artilleryWeaponPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "turret_tank_barrelmortar.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "artilleryWeaponPackImage"; + pickUpName = "an artillery loadout pack"; + + computeCRC = true; +}; + +function artilleryWeaponPackImage::onMount(%data, %obj, %node){ + %obj.hasArtWepPack = true; +} + +function artilleryWeaponPackImage::onUnMount(%data, %obj, %node){ + %obj.hasArtWepPack = false; +} + +function artilleryWeaponPackImage::onActivate(%data, %obj, %slot) +{ + %pos = %obj.getWorldBoxCenter(); + %vec = %obj.getEyeVector(); + %vec = vectorAdd(VectorScale(%vec,5),%pos); + %searchResult = containerRayCast(%pos, %vec, $TypeMasks::VehicleObjectType, %obj); + if(%searchResult){ + %searchObj = getWord(%searchResult, 0); + if(%searchObj.getDataBlock().getName() $= "Artillery"){ + if (%obj.packset==0){ + %searchobj.turretObject.setInventory(MortarAmmo, 12); + %searchobj.selweapontype = 1; + %obj.unmountImage($backpackslot); + %obj.decInventory(artilleryWeaponPack, 1); + } + else if (%obj.packset==1){ + %searchobj.turretObject.setInventory(MortarAmmo, 6); + %searchobj.selweapontype = 2; + %obj.unmountImage($backpackslot); + %obj.decInventory(artilleryWeaponPack, 1); + } + } + } +} + +function artilleryWeaponPackImage::onDeactivate(%data, %obj, %slot) +{ + %obj.setImageTrigger($BackpackSlot, false); +} + +function artilleryWeaponPack::onPickup(%this, %obj, %shape, %amount){} \ No newline at end of file diff --git a/Scripts/Packs/CommandSatelite.cs b/Scripts/Packs/CommandSatelite.cs new file mode 100644 index 0000000..ffb6838 --- /dev/null +++ b/Scripts/Packs/CommandSatelite.cs @@ -0,0 +1,882 @@ +//Dont reorder these or change the first word in them (S11 S17)... do so at your own risk. +$commsatPurchase[1] = "S11 90 S11 Aerial Recon"; +$commsatPurchase[2] = "S17 60 S17 Combat Drone"; + +//DO NOT CHANGE THESE, i did a bad job in scripting in a universal manner :), you can add more though shouldnt be too hard. +$commsatOrder[1] = "Move"; +$commsatOrder[2] = "Attack"; +$commsatOrder[3] = "Guard"; +$commsatOrder[4] = "Rearm"; + +// ------------------------------------------ +// SpySatellite.cs +// ------------------------------------------ + +datablock TurretData(DeployedSpySatellite) : TurretDamageProfile +{ + className = DeployedTurret; // VehicleTurret; + shapeFile = "turret_indoor_deployc.dts"; + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; + preload = true; + + canControl = false; + canObserve = true; + firstPersonOnly = true; + observeParameters = "0.5 4.5 4.5"; +// deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDCameraIcon; + cmdMiniIconName = "commander/MiniIcons/com_camera_grey"; + targetNameTag = 'command'; + targetTypeTag = 'Satellite'; + + mass = 0.7; + repairRate = 0; + maxDamage = 1.0; + disabledLevel = 1.0; + destroyedLevel = 1.0; + explosion = CameraGrenadeExplosion; + renderWhenDestroyed = false; + + thetaMin = 60; + thetaMax = 180; + thetaNull = 180; +// primaryAxis = zaxis; //revzaxis; + + isShielded = false; + energyPerDamagePoint = 1; + maxEnergy = 1000; + rechargeRate = 1.0; + + sensorData = CameraSensorObject; + sensorRadius = CameraSensorObject.detectRadius; + + heatSignature = 0.0; + barrel = HeliTurretParam; + numWeapons = 5; +}; + +datablock TurretImageData(SpySatelliteBarrel) +{ + shapeFile = "turret_muzzlepoint.dts"; + item = SpySatelliteBarrel; + + // Turret parameters + activationMS = 150; + deactivateDelayMS = 300; + thinkTimeMS = 150; + degPerSecTheta = 580; + degPerSecPhi = 960; + attackRadius = 1; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.5; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; +}; + +function SpySatelliteBarrel::onFire(%data,%obj,%slot) +{ + %range = 1000; //Max range It can move. + %rot = rotFromTransform(%obj.getTransform()); + %muzzlePos = %obj.getMuzzlePoint(%slot); + %muzzleVec = %obj.getMuzzleVector(%slot); + %endPos = VectorAdd(%muzzlePos, VectorScale(%muzzleVec, %range)); + %damageMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | + $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | + $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType | + $TypeMasks::InteriorObjectType; + + %terrain = ContainerRayCast(%muzzlePos, %endPos, $TypeMasks::TerrainObjectType, %obj); + %message = ""; + if (%terrain) + { + %xy = getWords(%terrain, 1, 2); + %z = getWord(%terrain, 3); + %pos = %xy SPC %z + 200; + %mask = ($TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType | + $TypeMasks::PlayerObjectType | $TypeMasks::TurretObjectType); + InitContainerRadiusSearch( %pos, 2, %mask ); + %test = containerSearchNext(); + if (%test) + %message = "Can\'t reposition the satellite. Trajectory Blocked"; + else + { + %obj.setTransform(%pos SPC %rot); + } + } + else + %message = "Trajectory not understood. Reenter."; + if (%message !$= "") + messageClient(%obj.client, 'msgBlocked', %message); +} + + +datablock ShapeBaseImageData(SpySatelliteDeployableImage) +{ + shapeFile = "pack_deploy_sensor_pulse.dts"; + item = SpySatelliteDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedSpySatellite; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + deploySound = SpySatelliteDeploySound; + + maxDepSlope = 40; + emap = true; + heatSignature = 0; + + minDeployDis = 0.1; + maxDeployDis = 15.0; //meters from body +}; + +datablock ItemData(SpySatelliteDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_sensor_pulse.dts"; + mass = 2.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "SpySatelliteDeployableImage"; + pickUpName = "a command satellite pack"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(DronePadDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = DronePadDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = StationInventory; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(DronePadDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "DronePadDeployableImage"; + joint = "4.5 4.5 4.5"; + pickUpName = "a Drone Pad pack"; + heatSignature = 0; + emap = true; +}; + +datablock StaticShapeData(Dronepad) : StaticShapeDamageProfile +{ + className = Station; + shapeFile = "station_teleport.dts"; + maxDamage = 3.5; + destroyedLevel = 3.5; + disabledLevel = 3.2; + explosion = DeployablesExplosion; + expDmgRadius = 10.0; + expDamage = 0.4; + expImpulse = 1000.0; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = false; + energyPerDamagePoint = 110; + maxEnergy = 50; + rechargeRate = 0.20; + renderWhenDestroyed = false; + doesRepair = true; + + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Command Satellite'; + targetTypeTag = 'Drone Pad'; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needspower = true; +}; + +datablock HoverVehicleData(commsatControlStation) : MPBDamageProfile +{ + spawnOffset = "0 0 0"; + canControl = false; + floatingGravMag = 0; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_scout.dts"; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout_debris.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + isProtectedMountPoint[0] = true; + cameraMaxDist = 5.0; + cameraOffset = 0.7; + cameraLag = 0.5; + numMountPoints = 1; + explosion = VehicleExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + // Damage Levels + maxDamage = 4; + destroyedLevel = 4; + + cantAbandon = 1; + cantTeamSwitch = 1; + + isShielded = false; + rechargeRate = 0.7; + energyPerDamagePoint = 75; + maxEnergy = 150; + minJetEnergy = 15; + jetEnergyDrain = 0.1; + + // Rigid Body + mass = 300; + bodyFriction = 0.1; + bodyRestitution = 0.5; + softImpactSpeed = 0.5; // Play SoftImpact Sound + hardImpactSpeed = 1; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 55; + speedDamageScale = 0.007; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 1; + collDamageMultiplier = 0.02; + + dragForce = 25 / 45.0; + vertFactor = 0.0; + floatingThrustFactor = 0.35; + + mainThrustForce = 10; + reverseThrustForce = 10; + strafeThrustForce = 0; + turboFactor = 1; + + brakingForce = 25; + brakingActivationSpeed = 4; + + stabLenMin = 2.25; + stabLenMax = 3.25; + stabSpringConstant = 30; + stabDampingConstant = 12; + + gyroDrag = 16; + normalForce = 30; + restorativeForce = 25; + steeringForce = 100; + rollForce = 15; + pitchForce = 7; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 0; + dustHeight = 1; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = ScoutSqueelSound; + engineSound = ScoutEngineSound; + floatSound = ScoutThrustSound; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterSoftSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeSoftSplashSound; + + minMountDist = 4; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = SmallHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 0.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.8; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + forwardJetEmitter = WildcatJetEmitter; + + cmdCategory = Tactical; + cmdIcon = CMDHoverScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_landscout_grey"; + targetNameTag = 'Command Satellite'; + targetTypeTag = 'Control Station'; + sensorData = PlayerSensor; + + checkRadius = 1.7785; + observeParameters = "1 10 10"; + + runningLight[0] = WildcatLight1; + runningLight[1] = WildcatLight2; + runningLight[2] = WildcatLight3; + + shieldEffectScale = "0.9375 1.125 0.6"; +}; + +function DeployedSpySatellite::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) { + case 0: //leftclick + if(%state){ + if(%obj.minedown == 1) + commsatCyclePurchase(%obj); + else if(%obj.grenadedown == 1) + commsatPurchase(%obj); + else if(%obj.rightdown == 1) + commsatAddToGroup(%obj,%obj.selectedWeapon); + else{ + if(%obj.selectedWeapon >= 1 && %obj.selectedWeapon <= 3) + commsatSelectGroup(%obj,%obj.selectedWeapon); + else if(%obj.selectedWeapon == 4) + commsatReleaseSelected(%obj); + else + commsatSelectObj(%obj); + } + } + case 3: //rightclick + if(%state){ + %obj.rightdown = 1; + if(%obj.minedown == 1) + commsatCycleOrders(%obj); + else if(%obj.grenadedown == 1) + commsatIssueOrders(%obj); + } + else + %obj.rightdown = 0; + case 4: //grenade + if(%state) + %obj.grenadedown = 1; + else + %obj.grenadedown = 0; + case 5: //mine + if(%state) + %obj.minedown = 1; + else + %obj.minedown = 0; + } +} + +function commsatCyclePurchase(%obj){ + %num = %obj.purchasenum + 1; + if($commsatPurchase[%num] $= "") + %num = 1; + %obj.purchasenum = %num; + bottomPrint(%obj.getcontrollingclient(), getWords($commsatPurchase[%num],3,4) SPC "selected for purchase.", 5, 2 ); +} + +function commsatPurchase(%obj){ + %team = %obj.team; + if(!isObject($commsatVPad[%obj.team])) + return; + if(getWord($commsatPurchase[%obj.purchasenum],1) <= $teamcredits[%team]){ + $teamcredits[%team] -= getWord($commsatPurchase[%obj.purchasenum],1); + $teamUsedCredits[%team] += getWord($commsatPurchase[%obj.purchasenum],1); + %pos = vectorAdd($commsatVpad[%team].getPosition(),"0 0 4"); + %rotation = "1 0 0 0"; + %drone = getWord($commsatPurchase[%obj.purchasenum],0); + if(%drone $= "S11"){ + %veh = new FlyingVehicle(){ + dataBlock = S11; + position = %pos; + rotation = %rotation; + team = %team; + }; + } else if(%drone $= "S17"){ + %veh = new HoverVehicle(){ + dataBlock = S17; + position = %pos; + rotation = %rotation; + team = %team; + }; + } + MissionCleanUp.add(%veh); + setTargetSensorGroup(%veh.getTarget(), %team); + bottomPrint(%obj.getcontrollingclient(), "Drone purchased.", 5, 2 ); + } + else + bottomPrint(%obj.getcontrollingclient(), "Not enough credits to purchase" SPC getWords(2,4), 5, 2 ); +} + +function commsatAddToGroup(%obj,%num){ + %x = 0; + %y = 0; + while(getWord(%obj.selected,%y) !$= ""){ + %trg = getWord(%obj.selected,%y); + if(isobject(%trg)){ + %obj.group[%num,%x] = %trg; + %x++; + } + %y++; + } + bottomPrint(%obj.getcontrollingclient(), "Group created.", 5, 2 ); +} + +function commsatSelectGroup(%obj,%num){ + %x = 0; + while(%obj.group[%num,%x] !$= ""){ + if(!(%obj.group[%num,%x].selected == 1)){ + if(%obj.selected $= "") + %obj.selected = %obj.group[%num,%x]; + else + %obj.selected = %obj.selected SPC %obj.group[%num,%x]; + %obj.group[%num,%x].selected = 1; + %x++; + } + } + bottomPrint(%obj.getcontrollingclient(), "Group selected.", 5, 2 ); +} + +function commsatReleaseSelected(%obj){ + %y = 0; + while(getWord(%obj.selected,%y) !$= ""){ + %trg = getWord(%obj.selected,%y); + if(isobject(%trg)) + %trg.selected = 0; + %y++; + } + %obj.selected = ""; + bottomPrint(%obj.getcontrollingclient(), "All units deselected.", 5, 2 ); +} + +function commsatSelectObj(%obj){ + %eye = %obj.getMuzzleVector(2); + %pos = %obj.getMuzzlePoint(2); + %search = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%eye,2000)), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TerrainObjectType, %obj); + if(%search){ + %srcobj = firstWord(%search); + %srcpos = posFromRaycast(%search); + InitContainerRadiusSearch(%srcpos,10,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType); + while ((%TObject = containerSearchNext()) != 0){ + if(%TObject.team == %obj.team && !(%Tobject.selected == 1)){ + if(%obj.selected $= ""){ + %obj.selected = %Tobject; + %Tobject.selected = 1; + bottomPrint(%obj.getcontrollingclient(), "Unit Selected.", 5, 2 ); + return; + } else { + %obj.selected = %obj.selected SPC %Tobject; + %Tobject.selected = 1; + bottomPrint(%obj.getcontrollingclient(), "Unit Selected.", 5, 2 ); + return; + } + } + } + } +} + +function commsatCycleOrders(%obj){ + %num = %obj.ordernum + 1; + if($commsatOrder[%num] $= "") + %num = 1; + %obj.ordernum = %num; + bottomPrint(%obj.getcontrollingclient(), "Current orders set to" SPC $commsatOrder[%num], 5, 2 ); +} + +function commsatIssueOrders(%obj){ + %eye = %obj.getMuzzleVector(0); + %pos = %obj.getMuzzlePoint(0); + %search = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%eye,2000)), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TerrainObjectType, %obj); + %tpos = posFromRaycast(%search); + if(%obj.ordernum == 2){ + if(%search){ + %srcobj = firstWord(%search); + %srcpos = posFromRaycast(%search); + InitContainerRadiusSearch(%srcpos,10,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType); + while ((%TObject = containerSearchNext()) != 0){ + if(%TObject.team != %obj.team){ + %target = %Tobject; + %valid = 1; + } + } + } + + if(%valid != 1){ + %team = %obj.team; + %beacon = new BeaconObject(){ + datablock = SubBeacon; + beaconType = "enemy"; + position = %tpos; + }; + %beacon.team = %team; + %beacon.owner = %obj; + %beacon.setTarget(%team); + MissionCleanup.add(%beacon); + %beacon.schedule(60000,"delete"); + } else { + %target.CommandAttachBeacon(); + %beacon = %target.enemybeacon; + } + } + + %y = 0; + while(getWord(%obj.selected,%y) !$= ""){ + %trg = getWord(%obj.selected,%y); + if(isobject(%trg)){ + %datablock = %trg.getDatablock().getName(); + if(%datablock $= "S11"){ + %trg.tasks = $S11[$commsatOrder[%obj.ordernum]]; + if(%obj.ordernum == 1) + %trg.specvar[MOVE] = %tpos; + else if(%obj.ordernum == 2){ + %trg.specvar[MOE] = %beacon; + %trg.specvar[FIRE] = %beacon; + %trg.specvar[MOVE] = %trg.getPosition(); + } + else if(%obj.ordernum == 3) + %trg.specvar[RECON] = %tpos; + else if(%obj.ordernum == 4){ + %trg.specvar[MOVE] = $commsatVpad[%obj.team].getPosition(); + %trg.specvar[MOVE,2] = %trg.getPosition(); + } + S11Think(%trg); + } + else if(%datablock $= "S17"){ + %trg.task = $commsatOrder[%obj.ordernum]; + if(%obj.ordernum == 1) + %trg.specvar[MOVE] = %tpos; + if(%obj.ordernum == 2) + %trg.specvar[ATTACK] = %beacon; + if(%obj.ordernum == 3) + %trg.specvar[GUARD] = %tpos; + if(%obj.ordernum == 4) + %trg.specvar[REARM] = $commsatVpad[%obj.team]; + S17Think(%trg); + } + } + %y++; + } +} + +function commsatHudUpdate(%obj){ + if(!isObject(%obj)) + return; + %client = %obj.getControllingClient(); + if(%client){ + %message = "CREDITS:"@$teamcredits[%obj.team]; + %message = %message SPC "SELECTED: Purchase/"@getWords($commsatPurchase[%obj.purchasenum],3,4); + %message = %message SPC "Order/"@$commsatOrder[%obj.ordernum]; + bottomPrint(%client, %message, 2, 2 ); + } + schedule(1000, 0, "commsatHudUpdate",%obj); +} + +function SimObject::CommandAttachBeacon(%obj){ + if(isObject(%obj.enemyBeacon)) + %obj.enemyBeacon.delete(); + if (%obj.team == 1) + %team = 2; + else + %team = 1; + + %beacon = new BeaconObject(){ + datablock = SubBeacon; + beaconType = "enemy"; + position = %obj.getWorldBoxCenter(); + }; + %beacon.team = %team; + %beacon.owner = %obj; + %beacon.setTarget(%team); + %obj.mountObject(%beacon, 9); + %obj.enemyBeacon = %beacon; + MissionCleanup.add(%beacon); + %beacon.schedule(60000,"delete"); + + %wa=new Waypoint() { + position = %obj.getWorldBoxCenter(); + rotation = "1 0 0 0"; + dataBlock = "WayPointMarker"; + team = %team; + name = "Attack this target."; + }; + MissionCleanup.add(%wa); + %wa.schedule(5000,"delete"); +} + +function SpySatelliteDeployable::onPickup(%this, %obj, %shape, %amount){} + +function SpySatelliteDeployableImage::testObjectTooClose(%item){ + %xy = getWords(%item.surfacePt, 0, 1); + %z = getWord(%item.surfacePt, 2); + %z += 200; + %item.surfacePt = %xy SPC %z; + %item.surfaceNrm = "0 0 -1"; + %mask = ($TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType | + $TypeMasks::PlayerObjectType | $TypeMasks::TurretObjectType); + InitContainerRadiusSearch( %item.surfacePt, $MinDeployDistance, %mask ); + %test = containerSearchNext(); + return %test; +} + +function SpySatelliteDeployableImage::onDeploy(%item, %plyr, %slot){ + %deplObj = Parent::onDeploy(%item, %plyr, %slot); + %deplObj.setCloaked(true); + %deplObj.setPassiveJammed(true); + %deplObj.selectedWeapon = 1; + %deplobj.mountImage(GST1Param, 0); + %deplobj.mountImage(SpySatelliteBarrel, 2); + %deplobj.purchaseNum = 1; + %deplobj.orderNum = 1; + $commsat[%deplobj.team] = %deplobj; + commsatHudUpdate(%deplobj); + + %item.surfacePt = vectorAdd(%item.surfacePt,"0 0 -200"); + + %pos = %item.surfacePt; + %Drone = new HoverVehicle() + { + dataBlock = commsatControlStation; + position = %pos; + rotation = %plyr.getRotation(); + team = %plyr.team; + }; + MissionCleanUp.add(%Drone); + + setTargetSensorGroup(%Drone.getTarget(), %team); + $commsatCPad[%deplobj.team] = %Drone; + + + + %pos = vectorAdd(%item.surfacePt,vectorScale(%plyr.getForwardvector(),1.25)); + %deplObj = new (StaticShape)() { + dataBlock = DeployedCrate12; + scale = "0.6 0.3 1"; + }; + %deplObj.setTransform(%pos SPC %plyr.getRotation()); + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + addToDeployGroup(%deplObj); + addDSurface(%item.surface,%deplObj); + + if($teamcredits[1] $= ""){ + for(%i = 1;%i <= game.numteams; %i++){ + $teamcredits[%i] = 160; + $teamUsedCredits[%i] = 0; + $teamRepCredits[%i] = 0; + } + commsatcreditloop(); + } +} + +function DeployedSpySatellite::onDestroyed(%this, %obj, %prevState){ + if (%obj.isRemoved) + return; + if ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon) { + %obj.isRemoved = true; + $TeamDeployedCount[%obj.team, SpySatelliteDeployable]--; + remDSurface(%obj); + %obj.schedule(500, delete); + $commsatCPad[%obj.team].schedule(500, delete); + } + Parent::onDestroyed(%this, %obj, %prevState); +} + +function commsatControlStation::onAdd(%this, %obj){ + Parent::onAdd(%this, %obj); + %obj.selectedWeapon = 1; + %obj.schedule(5000, "playThread", $AmbientThread, "ambient"); +} + +function commsatControlStation::playerMounted(%data, %obj, %player, %node) { + %Player.setTransform("0 0 0 0 0 1 0"); + %player.vehicleTurret = %turret; + + %Player.CanControl = true; + %Player.schedule(650, "CanControl = false"); + + // If it is not a bot, then set them up controlling the turret + %Client = %Player.client; + if (%Client.isAIControlled() == false) { + %player.setcontrolObject($commsat[%obj.team]); + %client.setcontrolObject($commsat[%obj.team]); + %client.setObjectActiveImage($commsat[%obj.team], 0); + $commsat[%obj.team].selectedWeapon = 1; + commandToClient(%client,'SetWeaponryVehicleKeys', true); + commandToClient(%client, 'setHudMode', 'Pilot', "bomber", %node); + %player.incommsat = 1; + } + %player.isBomber = true; + %Player.lastWeapon = %Player.getMountedImage($WeaponSlot); + %Player.unmountImage($WeaponSlot); + + if (%Client.HavocClient == true) + commandToClient(%obj.client,'SetPassengerVehicleKeys', true); +} + +function commsatControlStation::playerDismounted(%data, %Seat, %Player) { + %Seat.fireWeapon = false; + %Seat.setImageTrigger(0, false); + + %Player.MCSturret = ""; + %Player.CanControl = ""; + %player.incommsat = 0; + if (%Player.lastVehicle == %Seat) + %Player.lastVehicle = ""; + + %Seat.lastPilot = ""; +} + +function commsatControlStation::deleteAllMounted(%data, %obj){ + $commsat[%obj.team].damage(0, $commsat[%obj.team].getPosition(), 1000, $DamageType::Default); +} + +function commsatControlStation::playerDismounted(%data, %obj, %player){ + setTargetSensorGroup(%obj.getTarget(), %obj.team); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function DeployedSpySatelliteMOVE( %this,%data ){ + %this.setImageTrigger(2, true); + schedule(100, 0, "DeployedSpySatelliteNOMOVE", %this); +} + +function DeployedSpySatelliteNOMOVE(%obj){ + %obj.setImageTrigger(2,false); +} + +function DronePadDeployable::onPickup(%this, %obj, %shape, %amount){} + +function DronePadDeployableImage::onDeploy(%item, %plyr, %slot){ + %pos = %item.surfacePt; + %deplObj = new (StaticShape)() { + dataBlock = Dronepad; + scale = "2.5 2.5 1"; + }; + %deplObj.setTransform(%pos SPC %plyr.getRotation()); + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + addToDeployGroup(%deplObj); + addDSurface(%item.surface,%deplObj); + $commsatVPad[%deplobj.team] = %deplobj; +} + +function DronePadDeployableImage::testNoTerrainFound(%item){} + +function DronePadDeployableImage::onMount(%data, %obj, %node) {} + +function DronePad::onDestroyed(%this, %obj, %prevState){ + if (%obj.isRemoved) + return; + if ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon) { + remDSurface(%obj); + $TeamDeployedCount[%obj.team, DronePadDeployable]--; + %obj.schedule(500, delete); + } + Parent::onDestroyed(%this, %obj, %prevState); +} + +function commsatcreditloop(){ + %count = ClientGroup.getCount(); + %amount = %count * 30 + 50; + if(%amount < 160) + %amount = 160; + for(%i = 1;%i <= game.numteams; %i++){ + %currentmax = $teamRepCredits[%i] + $teamcredits[%i] + $teamUsedCredits[%i]; + if(%currentmax > %amount){ + %dif = %amount - %currentmax; + if(%dif <= $teamRepCredits[%i]) + $teamRepCredits[%i] -= %dif; + else{ + $teamcredits[%i] -= (%dif - $teamRepCredits[%i]); + $teamRepCredits[%i] = 0; + } + } + else if(%currentmax < %amount) + $teamRepCredits[%i] += %amount - %currentmax; + if($teamRepCredits[%i] > 0){ + %RepAmt = (5 + %count); + if(%RepAmt > $teamRepCredits[%i]) + %RepAmt = $teamRepCredits[%i]; + $teamcredits[%i] += %RepAmt; + $teamRepCredits -= %RepAmt; + } + } + schedule(20000, 0, "commsatcreditloop"); +} \ No newline at end of file diff --git a/Scripts/Packs/DeployableWaypoint.cs b/Scripts/Packs/DeployableWaypoint.cs new file mode 100644 index 0000000..e58d44a --- /dev/null +++ b/Scripts/Packs/DeployableWaypoint.cs @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Deployable Waypoint - Made by Dark Dragon DX +//------------------------------------------------------------------------------ + +datablock StaticShapeData(DeployedWaypoint) : StaticShapeDamageProfile { + className = "Waypoint"; + shapeFile = "camera.dts"; + + maxDamage = 5.0; + destroyedLevel = 5.0; + disabledLevel = 2.5; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.5; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Waypoint Pack'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(WaypointDeployableImage) { + mass = 1; + emap = true; + shapeFile = "stackable1s.dts"; + item = Waypointdeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = Deployedwaypoint; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(WaypointDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + joint = "1 1 1"; + rotate = true; + image = "WaypointDeployableImage"; + pickUpName = "a waypoint pack"; + heatSignature = 0; + emap = true; +}; + +function WaypointDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function WaypointDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function WaypointDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function WaypointDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %scale = getWords($packSetting["mspine",%plyr.packSet],0,2); + + %mod = 0.5; + %rndcolor = getrandom(0,5); + +%deplObj = new (%className)() { + dataBlock = DeployedWaypoint; + team = %plyr.team; + scale = "1 1 1"; + }; +%deplObj.wp = new (WayPoint)(){ + dataBlock = WayPointMarker; + name = %plyr.client.namebase@"'s Waypoint"; + team = %plyr.team; + scale = "0.1 0.1 0.1"; + }; + MissionCleanup.add(%deplObj.wp); + + %mod = 0; + %h1=vectorAdd(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm),%mod)); + %deplObj.wp.setTransform(%h1 SPC %rot); + %deplObj.setTransform(%h1 SPC %rot); + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + addToDeployGroup(%deplObj); + AIDeployObject(%plyr.client, %deplObj); + $TeamDeployedCount[%plyr.team, %item.item]++; + %deplObj.deploy(); + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + %deplObj.deploy(); + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + return %deplObj; + +} + +function deployedWaypoint::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, waypointdeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + %obj.wp.schedule(500,"delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function deployedwaypoint::disassemble(%data, %plyr, %obj){ + disassemble(%data,%plyr,%obj); + %obj.wp.schedule(500, "delete"); +} diff --git a/Scripts/Packs/Effectpacks.cs b/Scripts/Packs/Effectpacks.cs new file mode 100644 index 0000000..8e5efb5 --- /dev/null +++ b/Scripts/Packs/Effectpacks.cs @@ -0,0 +1,1823 @@ +function Gamebase::addTofxGroup(%obj,%set) +{ +if (!isObject(%obj)) + return ""; + if (!isObject(fxGroup)) + { + %main = new SimGroup("fxGroup"); + MissionCleanup.add(%main); + %em = new Simgroup("fx_emitters"); + %au = new Simgroup("fx_audio"); + %pa = new Simgroup("fx_packs"); + %main.add(%em); + %main.add(%au); + %main.add(%pa); + } +if (IsObject(%obj)) + { + if (%set == 1) + fx_audio.add(%obj); + else if (%set == 2) + fx_packs.add(%obj); + else + fx_emitters.add(%obj); + } +} + + + +/////////EMITTER PACK////////// + +//Example of using expert setting to select pack setting mode. +//Note uses custom (plugin) packsettings mode + +//Format = "%modes %lines %name" +//%modes gives the highest mode like the old settings +//%lines gives the amount of words in the settings which are the name of the mode +// -1 gives the entire content of the mode +// 2 would give the first 3 words of the mode +//%name gives the name of the pack as in %name "set to" %modename + +$expertSettings[EmitterDepImage] = "3 -1 Emitter Pack:[Options]"; + +$expertSetting[EmitterDepImage,0] = "Select Emitter"; +$expertSetting[EmitterDepImage,1] = "Select Emitter selection mode"; +$expertSetting[EmitterDepImage,2] = "Select Power logic"; +$expertSetting[EmitterDepImage,3] = "Select Cloak logic"; + +//These are my personal browse of the first few datablocks. +$packSettings[EmitterDepImage] = "13 1 Emitter Pack:[Emitter]"; +$packSetting[EmitterDepImage,0] = "Small Steam ReverseEmitter"; +$packSetting[EmitterDepImage,1] = "Small Bubbles GrenadeBubbleEmitter"; +$packSetting[EmitterDepImage,2] = "Large Foam VehicleFoamEmitter"; +$packSetting[EmitterDepImage,3] = "Small Fire PlasmaExplosionEmitter"; +$packSetting[EmitterDepImage,4] = "Plasma Stream PlasmaRifleEmitter"; +$packSetting[EmitterDepImage,5] = "Large Bubbles DiscExplosionBubbleEmitter"; +$packSetting[EmitterDepImage,6] = "Dark Smoke GDebrisSmokeEmitter"; +$packSetting[EmitterDepImage,7] = "Small Smoke GrenadeSmokeEmitter"; +$packSetting[EmitterDepImage,8] = "Small Spark ELFSparksEmitter"; +$packSetting[EmitterDepImage,9] = "Green Smoke MortarSmokeEmitter"; +$packSetting[EmitterDepImage,10] = "Fire Stream MissileFireEmitter"; +$packSetting[EmitterDepImage,11] = "Water Stream WaterStreamEmitter"; +$packSetting[EmitterDepImage,12] = "min hole LoadingE2"; +$packSetting[EmitterDepImage,13] = "wisps flair LoadingE"; + +//Note how the extra %id defines the nesting of the modes. +//While previous settings could also include %id=0 I chose not to use that +//since nesting isn't directly compatible with the unified code. +//Using non nesting format for the previous allows me to use the unified code +//for that setting. + +$packSettings[EmitterDepImage,1] = "1 -1 Emitter Pack: [Selection mode]"; +$packSetting[EmitterDepImage,0,1] = "Small selection"; +$packSetting[EmitterDepImage,1,1] = "Sellect from datablocks"; + +$packSettings[EmitterDepImage,2] = "3 -1 Emitter Pack: [Power Logic]"; +$packSetting[EmitterDepImage,0,2] = "Always on"; +$packSetting[EmitterDepImage,1,2] = "On when powered"; +$packSetting[EmitterDepImage,2,2] = "Off when powered"; +$packSetting[EmitterDepImage,3,2] = "5 sec on when power change"; + +$packSettings[EmitterDepImage,3] = "1 -1 Emitter Pack: [Cloak Logic]"; +$packSetting[EmitterDepImage,0,3] = "Always Vissible"; +$packSetting[EmitterDepImage,1,3] = "Cloaked when emitting"; +//Removed +$packSetting[EmitterDepImage,2,3] = "Cloaked when not emtting"; +$packSetting[EmitterDepImage,3,3] = "Always Cloaked"; + + +datablock ParticleData( WaterStreamParticle ) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 1.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 25000; + lifetimeVarianceMS = 100; + + textureName = "special/bubbles"; + + useInvAlpha = false; + + spinRandomMin = -360.0; + spinRandomMax = 360.0; + + colors[0] = "0.4 0.4 0.8 1.0"; + colors[1] = "0.3 0.3 0.8 0.1"; + colors[2] = "0.0 0.0 0.8 0.0"; + sizes[0] = 0.4; + sizes[1] = 0.5; + sizes[2] = 0.7; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( WaterStreamEmitter ) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 1; + + ejectionVelocity = 30.0; // A little oomph at the back end + velocityVariance = 1.0; + + thetaMin = 0.0; + thetaMax = 1.0; + + + orientParticles = False; + orientOnVelocity = False; + + particles = "WaterStreamParticle"; +}; + + +datablock StaticShapeData(EmitterDep) : DeployedCrate +{ + shapeFile = "stackable4l.dts"; + needsPower = true; +}; + +datablock ShapeBaseImageData(EmitterDepImage) { + mass = 1; + emap = true; + shapeFile = "stackable4l.dts"; + item = EmitterDepPack; + mountPoint = 1; + offset = "0 -0.18 -0.5"; + deployed = EmitterDepImage; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(EmitterDepPack) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable4l.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "EmitterDepImage"; + pickUpName = "an Emitter pack"; + heatSignature = 0; + emap = true; + }; + +function EmitterDepPack::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function EmitterDep::gainPower(%data, %obj) +{ +if (%obj.pLogic==0) + EmitterDepSet(%obj,1); +if (%obj.pLogic==1) + EmitterDepSet(%obj,1); +if (%obj.pLogic==2) + EmitterDepSet(%obj,0); +if (%obj.pLogic==3) + { + EmitterDepSet(%obj,1); + schedule(5000,%obj,"EmitterDepSet",%obj,0); + } +} + +function EmitterDep::losePower(%data, %obj) +{ +if (%obj.pLogic==0) + EmitterDepSet(%obj,1); +if (%obj.pLogic==1) + EmitterDepSet(%obj,0); +if (%obj.pLogic==2) + EmitterDepSet(%obj,1); +if (%obj.pLogic==3) + { + EmitterDepSet(%obj,1); + schedule(5000,%obj,"EmitterDepSet",%obj,0); + } + +} + + + +function EmitterDepImage::onDeploy(%item, %plyr, %slot) { + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new StaticShape() + { + dataBlock = "EmitterDep"; + scale = "0.5 0.5 0.1"; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%plyr.packset == -1) + %deplObj.Emitterblock = datablockGroup.getObject(%plyr.packset[0]); + else if (%plyr.packset[0] $= "") + %deplObj.Emitterblock = GetWord($packSetting[EmitterDepImage,0],2); + else + %deplObj.EmitterBlock = %plyr.packset[0]; + + %deplObj.pLogic = %plyr.packset[2]; + %deplObj.cLogic = %plyr.packset[3]; + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + %deplObj.powerFreq = %plyr.powerFreq; + checkPowerObject(%deplObj); + if (!%plyr.client.isAdmin) + { + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + } + + + return %deplObj; +} + +function EmitterDep::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + EmitterDepSet(%obj,0); + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, EmitterDepPack]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function EmitterDepImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); + %obj.hasEmpack = true; // not needed anymore + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + +function EmitterDepImage::onUnmount(%data, %obj, %node) { + %obj.hasEmpack = ""; // not needed anymore + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + +function EmitterDepSet(%obj,%set) +{ +if (ISobject(%obj)) + { + if (!%set && IsObject(%obj.emitter)) + { + %obj.emitter.delete(); + if (%obj.cLogic == 2 || %obj.cLogic == 3) + %obj.startfade(0,0,1); + else if (%obj.cLogic == 1) + %obj.startfade(0,0,0); + } + if (%set && !ISobject(%obj.emitter)) + { + %obj.emitter = CreateEmitter("0 0 0",%obj.emitterblock); + %obj.emitter.addTofxGroup(); + %obj.emitter.setTransform(%obj.getTransform()); + if (%obj.cLogic == 1 || %obj.cLogic == 3) + %obj.startfade(0,0,1); + else if (%obj.cLogic == 2) + %obj.startfade(0,0,0); + } + } +} + +//Example of nested setting modes with plugin capability. +//All but the first part of packssets are fully global defined. +//Expert mode and the very first packset refer back to the unified code. + +function EmitterDepImage::ChangeMode(%data,%plyr,%val,%level) +{ +if (%level == 0) + { + //Selecting emitter + if (!%plyr.expertSet) + { + if (!%plyr.packset[1]) + { + Parent::ChangeMode(%data,%plyr,%val,%level); + %plyr.packset[0] = GetWord($packSetting[EmitterDepImage,%plyr.packset],2); + } + else + { + %plyr.packset = -1; + %plyr.packset[0] = nextEmitter(%plyr.packset[0]+%val,%val); + %name = datablockGroup.getObject(%plyr.packset[0]).GetName(); + bottomPrint(%plyr.client,"Emitter pack set to emit:"SPC %name,2,1); + } + } + //Selecting selection mode/PowerLogic/CloakLogic + else if (%plyr.expertSet > 0) + { + %set = %plyr.expertSet; + %image = %data.getName(); + %settings = $packSettings[%image,%set]; + + %plyr.packSet[%set] = %plyr.packSet[%set] + %val; + if (%plyr.packSet[%set] > getWord(%settings,0)) + %plyr.packSet[%set] = 0; + if (%plyr.packSet[%set] < 0) + %plyr.packSet[%set] = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $PackSetting[%image,%plyr.packSet[%set],%set]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + bottomPrint(%plyr.client,%packname SPC "set to"SPC %line,2,1); + } + } +else + { + Parent::ChangeMode(%data,%plyr,%val,%level); + } +} + +//Custion function to browse through all datablocks for emitter types. +//Allthought the cyclign takes forever and there can't be 1 proper name for each emitter +//It allows the player to fully utilize t2's content. + +function nextEmitter(%start,%dir) +{ +if (%dir == 1) + { + for(%i = %start; %i < DatablockGroup.getCount() ; %i++) + { + if (datablockGroup.getObject(%i).particles !$= "") + { + return %i; + } + } + return nextEmitter(0,1); + } +else + { + for(%i = %start; %i > 0; %i--) + { + if (datablockGroup.getObject(%i).particles !$= "") + { + return %i; + } + } + return nextEmitter(DatablockGroup.getCount(),-1); + } +} + +/////////Audio PACK////////// +//This is a total copy/past from the emitter deployable + + +$expertSettings[AudioDepImage] = "3 -1 Emitter Pack:[Options]"; +$expertSetting[AudioDepImage,0] = "Select Emitter"; +$expertSetting[AudioDepImage,1] = "Select Emitter selection mode"; +$expertSetting[AudioDepImage,2] = "Select Power logic"; +$expertSetting[AudioDepImage,3] = "Select Loop logic"; + +//Some I made myself +$packSettings[AudioDepImage] = "16 1 Emitter Pack:[Emitter]"; +$packSetting[AudioDepImage,0] = "Jeti howl1 fx/environment/yeti_howl1.wav"; +$packSetting[AudioDepImage,1] = "Jeti howl2 fx/environment/yeti_howl2.wav"; +$packSetting[AudioDepImage,2] = "Jeti growl fx/environment/growl1.wav"; +$packSetting[AudioDepImage,3] = "Frog sounds fx/environment/frog2.wav"; +$packSetting[AudioDepImage,4] = "fly swarm fx/environment/fly_swarm.wav"; +$packSetting[AudioDepImage,5] = "Bird sound1 fx/environment/bird_echo4.wav"; +$packSetting[AudioDepImage,6] = "Bird sound2 fx/environment/bird_echo5.wav"; +$packSetting[AudioDepImage,7] = "Ocean waves fx/environment/Oceanwaves.wav"; +$packSetting[AudioDepImage,8] = "River stream fx/environment/River2.wav"; +$packSetting[AudioDepImage,9] = "Cha ching fx/misc/bounty_bonus.wav"; +$packSetting[AudioDepImage,10] = "Mario party fx/Bonuses/mario-6notes.wav"; +$packSetting[AudioDepImage,11] = "Slick music fx/Bonuses/med-level4-slick.wav"; +$packSetting[AudioDepImage,12] = "test music t2Intro.wav"; +$packSetting[AudioDepImage,13] = "Tessios Heist-swing fx/bonuses/horz_straipass2_heist.wav"; +$packSetting[AudioDepImage,14] = "Swami swashi fx/bonuses/low-level2-spitting.wav"; +$packSetting[AudioDepImage,15] = "Smoking something fx/bonuses/med-level1-modest.wav"; +$packSetting[AudioDepImage,16] = "Swami swashi2 fx/bonuses/med-level3-shining.wav"; + +$packSettings[AudioDepImage,1] = "1 -1 Emitter Pack: [Selection mode]"; +$packSetting[AudioDepImage,0,1] = "Small selection"; +$packSetting[AudioDepImage,1,1] = "Sellect from datablocks"; + +$packSettings[AudioDepImage,2] = "3 -1 Emitter Pack: [Power Logic]"; +$packSetting[AudioDepImage,0,2] = "Always on"; +$packSetting[AudioDepImage,1,2] = "On when powered"; +$packSetting[AudioDepImage,2,2] = "Off when powered"; +$packSetting[AudioDepImage,3,2] = "5 sec on when power change"; + +//Cloak logic was left out as it won't help the global image. +//And it will only worsen spam. + +$packSettings[AudioDepImage,3] = "1 -1 Emitter Pack: [Loop Logic]"; +$packSetting[AudioDepImage,0,3] = "Use standard"; +$packSetting[AudioDepImage,1,3] = "Always loop"; + + + +datablock StaticShapeData(AudioDep) : DeployedCrate +{ + shapeFile = "stackable2l.dts"; + needsPower = true; +}; + +datablock ShapeBaseImageData(AudioDepImage) { + mass = 1; + emap = true; + shapeFile = "stackable3s.dts"; + item = AudioDepPack; + mountPoint = 1; + offset = "0.5 -0.5 0"; + rotation = "0 1 0 90"; + deployed = AudioDepImage; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(AudioDepPack) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable3s.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "AudioDepImage"; + pickUpName = "an Sound pack"; + heatSignature = 0; + emap = true; + }; + + +function AudioDepPack::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function AudioDep::gainPower(%data, %obj) +{ +if (%obj.pLogic==0) + AudioDepSet(%obj,1); +if (%obj.pLogic==1) + AudioDepSet(%obj,1); +if (%obj.pLogic==2) + AudioDepSet(%obj,0); +if (%obj.pLogic==3) + { + AudioDepSet(%obj,1); + schedule(5000,%obj,"AudioDepSet",%obj,0); + } +} + +function AudioDep::losePower(%data, %obj) +{ +if (%obj.pLogic==0) + AudioDepSet(%obj,1); +if (%obj.pLogic==1) + AudioDepSet(%obj,0); +if (%obj.pLogic==2) + AudioDepSet(%obj,1); +if (%obj.pLogic==3) + { + AudioDepSet(%obj,1); + schedule(5000,%obj,"AudioDepSet",%obj,0); + } + +} + + + +function AudioDepImage::onDeploy(%item, %plyr, %slot) { + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new StaticShape() + { + dataBlock = "AudioDep"; + scale = "0.4 0.4 0.1"; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%plyr.packset == -1) + %deplObj.Audioblock = datablockGroup.getObject(%plyr.packset[0]); + else if (%plyr.packset[0] !$= "") + %deplObj.AudioBlock = %plyr.packset[0]; + else + %deplObj.Audioblock = GetWord($packSetting[AudioDepImage,0],2); + + + %deplObj.pLogic = %plyr.packset[2]; + %deplObj.lLogic = %plyr.packset[3]; + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + %deplObj.powerFreq = %plyr.powerFreq; + checkPowerObject(%deplObj); + + if (!%plyr.client.isAdmin) + { + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + } + return %deplObj; +} + +function AudioDep::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + AudioDepSet(%obj,0); + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, AudioDepPack]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function AudioDepImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); + %obj.hasAupack = true; // not needed + %obj.packSet = 0; + %obj.packSet[0] = ""; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + +function AudioDepImage::onUnmount(%data, %obj, %node) { + %obj.hasAupack = ""; + %obj.packSet = 0; + %obj.packSet[0] = ""; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + +//We will keep calling the audio emitter %obj.emitter +//This will make sure in deconstruct the auto emitter is removed +function AudioDepSet(%obj,%set) +{ +if (ISobject(%obj)) + { + if (!%set && IsObject(%obj.emitter)) + { + %obj.emitter.delete(); + } + if (%set && !ISobject(%obj.emitter)) + { + //The only working check to keep faulty emitters from staying around + //Doesn't remove the console spam thougt.. :P + %obj.emitter = CreateAudioEmitter(%obj.getTransform(),%obj.Audioblock,%obj.lLogic); + if (isObject(%obj.emitter)) + %obj.emitter.addTofxGroup(1); + if ($audiopackloaded[%obj.emitter.filename]==1 && !Isfile(%obj.emitter.filename)) + { + disassemble(%obj.getdatablock(),%obj.owner,%obj); + } + else + { + //%obj.emitter.setTransform(%obj.getTransform()); + $audiopackloaded[%obj.emitter.filename]=1; + } + } + } +} + + + +// Just copy/past the emitter changemode since it needs almost totaly the same modes +function AudioDepImage::ChangeMode(%data,%plyr,%val,%level) +{ +if (%level == 0) + { + //Selecting emitter + if (!%plyr.expertSet) + { + if (!%plyr.packset[1]) + { + Parent::ChangeMode(%data,%plyr,%val,%level); + %plyr.packset[0] = GetWord($packSetting[AudioDepImage,%plyr.packset],2); + } + else + { + %plyr.packset = -1; + %plyr.packset[0] = nextsEmitter(%plyr.packset[0]+%val,%val); + %name = datablockGroup.getObject(%plyr.packset[0]).GetName(); + bottomPrint(%plyr.client,"sound pack set to play:"SPC %name,2,1); + } + } + //Selecting selection mode/PowerLogic/CloakLogic + else if (%plyr.expertSet > 0) + { + %set = %plyr.expertSet; + %image = %data.getName(); + %settings = $packSettings[%image,%set]; + + %plyr.packSet[%set] = %plyr.packSet[%set] + %val; + if (%plyr.packSet[%set] > getWord(%settings,0)) + %plyr.packSet[%set] = 0; + if (%plyr.packSet[%set] < 0) + %plyr.packSet[%set] = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $PackSetting[%image,%plyr.packSet[%set],%set]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + bottomPrint(%plyr.client,%packname SPC "set to"SPC %line,2,1); + } + } +else + { + Parent::ChangeMode(%data,%plyr,%val,%level); + } +} + + +//Like promissed another browsing function. + + +function nextsEmitter(%start,%dir) +{ +if (%dir == 1) + { + for(%i = %start; %i < DatablockGroup.getCount() ; %i++) + { + if (datablockGroup.getObject(%i).description.volume !$= "")// && isFile(DatablockGroup.getObject(%i).filename)) + { + return %i; + } + } + return nextsEmitter(0,1); + } +else + { + for(%i = %start; %i > 0; %i--) + { + if (datablockGroup.getObject(%i).description.volume !$= "" ) //&& isFile(DatablockGroup.getObject(%i).filename)) + { + return %i; + } + } + return nextsEmitter(DatablockGroup.getCount(),-1); + } +} + + + +//Variant for createEmitter +//Can be modified to using custom audio files not found in datablocks +//Can also be futher extended to use other custom variables to people's desire +//Currently included looping mode and lifetime. +//Profile and description could be used.. however it will lessen the ability to +//modify the emitter. + +//it seems some clients deploying some sounds result in an faulty emitter +//I'l make sure those won't life to see the day.. but it's wierd still + +//Added the ability to use file name as %data, so you don't need datablocks.. :D + +function CreateAudioEmitter(%trans,%data,%forcelooping,%lifetime) +{ + +%dat = (IsObject(%data)); + +%desc = %data.description; + +if (%forcelooping) + %looping=1; +else + %looping = %desc.isLooping; + +%filename = %data.filename; + +%audio = new AudioEmitter() +{ +position = GetWords(%trans,0,2); +rotation = GetWords(%trans,3,6); +scale = "1 1 1"; +fileName = %dat ? %data.fileName : %data; +useProfileDescription = "0"; + +outsideAmbient = "1"; +volume = %dat ? %desc.volume : "1"; +isLooping = %dat ? %looping : "1"; +is3D = %dat ? %desc.is3D : "1"; +minDistance = %dat ? %desc.minDistance : "20"; +maxDistance = %dat ? %desc.maxDistance : "100"; +coneInsideAngle = %dat ? %desc.coneInsideAngle : "360"; +coneOutsideAngle = %dat ? %desc.coneOutsideAngle : "360"; +coneOutsideVolume = %dat ? %desc.coneOutsideVolume : "1"; +coneVector = %dat ? %desc.coneVector : "0 0 1"; +loopCount = %dat ? %desc.loopCount : "-1"; +minLoopGap = %dat ? %desc.minLoopGap : "0"; +maxLoopGap = %dat ? %desc.maxLoopGap : "0"; +type = "EffectAudioType"; +}; + +if (%lifetime !$="") + %audio.schedule(%lifetime.delete()); +return %audio; +} + + +/////////DISPENSER PACK////////// + +//Another nessted pack setting + +//Format = "%modes %lines %name" +//%modes gives the highest mode like the old settings +//%lines gives the amount of words in the settings which are the name of the mode +// -1 gives the entire content of the mode +// 2 would give the first 3 words of the mode +//%name gives the name of the pack as in %name "set to" %modename + +$expertSettings[DispenserDepImage] = "8 -1 Dispenser Pack:[Options]"; +$expertSetting[DispenserDepImage,0] = "[T2 Packs]"; +$expertSetting[DispenserDepImage,1] = "[Basic Building Parts]"; +$expertSetting[DispenserDepImage,2] = "[Base Assets]"; +$expertSetting[DispenserDepImage,3] = "[T2 Deployables]"; +$expertSetting[DispenserDepImage,4] = "[Decoration packs]"; +$expertSetting[DispenserDepImage,5] = "[Misc Deployables]"; +$expertSetting[DispenserDepImage,6] = "[Barrels]"; +$expertSetting[DispenserDepImage,7] = "[Weapons]"; +$expertSetting[DispenserDepImage,8] = "[Ammo]"; + + +//Hacky hacky way to get modes working.. hahaha +$packSettings[DispenserDepImage] = "8 1 Dispenser Pack:[T2 Packs]"; + +//Snagged from player.cs +$packSettings[DispenserDepImage,0] = "8 1 Dispenser Pack:[T2 Packs]"; +$packSetting[DispenserDepImage,0,0] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,0] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,0] = "Energy Pack EnergyPack"; +$packSetting[DispenserDepImage,3,0] = "Repair Pack RepairPack"; +$packSetting[DispenserDepImage,4,0] = "Shield Pack ShieldPack"; +$packSetting[DispenserDepImage,5,0] = "Cloak Pack CloakingPack"; +$packSetting[DispenserDepImage,6,0] = "Jammer Pack SensorJammerPack"; +$packSetting[DispenserDepImage,7,0] = "Ammunition Pack AmmoPack"; +$packSetting[DispenserDepImage,8,0] = "Satchel Charge SatchelCharge"; + +$packSettings[DispenserDepImage,1] = "8 1 Dispenser Pack:[Basic Building Parts]"; +$packSetting[DispenserDepImage,0,1] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,1] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,1] = "Light beam spinedeployable"; +$packSetting[DispenserDepImage,3,1] = "Light walkway wWallDeployable"; +$packSetting[DispenserDepImage,4,1] = "Light BlastWall WallDeployable"; +$packSetting[DispenserDepImage,5,1] = "Medium beam mspineDeployable"; +$packSetting[DispenserDepImage,6,1] = "Medium Floor floorDeployable"; +$packSetting[DispenserDepImage,7,1] = "Force Field ForceFieldDeployable"; +$packSetting[DispenserDepImage,8,1] = "Gravity Field GravityFieldDeployable"; + + +$packSettings[DispenserDepImage,2] = "9 1 Dispenser Pack:[Base Assets]"; +$packSetting[DispenserDepImage,0,2] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,2] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,2] = "Large Inventory-station LargeInventoryDeployable"; +$packSetting[DispenserDepImage,3,2] = "Medium-Sensor Pack MediumSensorDeployable"; +$packSetting[DispenserDepImage,4,2] = "Large-Sensor Pack LargeSensorDeployable"; +$packSetting[DispenserDepImage,5,2] = "Logo-Projector Pack LogoProjectorDeployable"; +$packSetting[DispenserDepImage,6,2] = "Deployable Turret-Base TurretBasePack"; +$packSetting[DispenserDepImage,7,2] = "Solar-Panel Pack SolarPanelDeployable"; +$packSetting[DispenserDepImage,8,2] = "Generator Pack GeneratorDeployable"; +$packSetting[DispenserDepImage,9,2] = "Switch Pack SwitchDeployable"; + +$packSettings[DispenserDepImage,3] = "6 1 Dispenser Pack:[T2 Deployables]"; +$packSetting[DispenserDepImage,0,3] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,3] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,3] = "Motion-Sensor Pack MotionSensorDeployable"; +$packSetting[DispenserDepImage,3,3] = "Pulse-Sensor Pack PulseSensorDeployable"; +$packSetting[DispenserDepImage,4,3] = "Landspike Turret TurretOutdoorDeployable"; +$packSetting[DispenserDepImage,5,3] = "SpiderClamp Turret TurretIndoorDeployable"; +$packSetting[DispenserDepImage,6,3] = "Inventory Station InventoryDeployable"; + +$packSettings[DispenserDepImage,4] = "7 1 Dispenser Pack:[Decoration packs]"; +$packSetting[DispenserDepImage,0,4] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,4] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,4] = "Decoration Pack DecorationDeployable"; +$packSetting[DispenserDepImage,3,4] = "Crate Pack PulseSensorDeployable"; +$packSetting[DispenserDepImage,4,4] = "Light Pack LightDeployable"; +$packSetting[DispenserDepImage,5,4] = "Emitter Pack EmitterDepPack"; +$packSetting[DispenserDepImage,6,4] = "Audio Pack AudioDepPack"; +$packSetting[DispenserDepImage,7,4] = "Tree Pack TreeDeployable"; + +$packSettings[DispenserDepImage,5] = "15 1 Dispenser Pack:[Misc Deployables]"; +$packSetting[DispenserDepImage,0,5] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,5] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,5] = "Jump Pad JumpadDeployable"; +$packSetting[DispenserDepImage,3,5] = "Teleport Pad TelePadPack"; +$packSetting[DispenserDepImage,4,5] = "Tripwire Pack TripwireDeployable"; +$packSetting[DispenserDepImage,5,5] = "Escape Pod EscapePodDeployable"; +$packSetting[DispenserDepImage,6,5] = "Pack Dispenser DispenserDepPack"; +$packSetting[DispenserDepImage,7,5] = "Detonation pack DetonationDeppack"; +$packSetting[DispenserDepImage,8,5] = "Energize pack EnergizerDeployable"; +$packSetting[DispenserDepImage,9,5] = "Disc turret DiscTurretDeployable"; +$packSetting[DispenserDepImage,10,5] = "Laser turret TurretLaserDeployable"; +$packSetting[DispenserDepImage,11,5] = "Rack Turret TurretMissileRackDeployable"; +$packSetting[DispenserDepImage,12,5] = "Anti Turret TurretMpm_Anti_Deployable"; +$packSetting[DispenserDepImage,13,5] = "MpmFuel pack MpmFuelPack"; +$packSetting[DispenserDepImage,14,5] = "MpmAmmo pack MpmAmmoPack"; +$packSetting[DispenserDepImage,15,5] = "Mpm TargetBeacon Mpm_BeaconPack"; + + + +$packSettings[DispenserDepImage,6] = "6 1 Dispenser Pack:[Decoration packs]"; +$packSetting[DispenserDepImage,0,6] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,6] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,6] = "ELF Barrel ELFBarrelPack"; +$packSetting[DispenserDepImage,3,6] = "Mortar Barrel MortarBarrelPack"; +$packSetting[DispenserDepImage,4,6] = "Plasma Barrel PlasmaBarrelPack"; +$packSetting[DispenserDepImage,5,6] = "AA Barrel AABarrelPack"; +$packSetting[DispenserDepImage,6,6] = "Missile Barrel MissileBarrelPack"; + +$packSettings[DispenserDepImage,7] = "19 1 Dispenser Pack:[Weapons]"; +$packSetting[DispenserDepImage,0,7] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,7] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,7] = "M32 Assault Rifle HRPChaingun"; +$packSetting[DispenserDepImage,3,7] = "M32 with RPG Attachment RPChaingun"; +$packSetting[DispenserDepImage,4,7] = "Sniper Rifle snipergun"; +$packSetting[DispenserDepImage,5,7] = "Rifle KriegRifle"; +$packSetting[DispenserDepImage,6,7] = "Shotgun Shotgun"; +$packSetting[DispenserDepImage,7,7] = "MP12 SMG LSMG"; +$packSetting[DispenserDepImage,8,7] = "AT6 Missile Launcher LMissileLauncher"; +$packSetting[DispenserDepImage,9,7] = "AA Missile Launcher AALauncher"; +$packSetting[DispenserDepImage,10,7] = "M79 RPG Launcher M4"; +$packSetting[DispenserDepImage,11,7] = "MG42 MG42"; +$packSetting[DispenserDepImage,12,7] = "Gauss Cannon RailGun"; +$packSetting[DispenserDepImage,13,7] = "Napalm Mortar NapalmMortar"; +$packSetting[DispenserDepImage,15,7] = "Flame Thrower flamer"; +$packSetting[DispenserDepImage,16,7] = "PBC Cannon PBC"; +$packSetting[DispenserDepImage,17,7] = "Construction Tool ConstructionTool"; +$packSetting[DispenserDepImage,18,7] = "Merge Tool MergeTool"; +$packSetting[DispenserDepImage,19,7] = "Editor Tool EditingTool"; + +$packSettings[DispenserDepImage,8] = "24 1 Dispenser Pack:[Ammo]"; +$packSetting[DispenserDepImage,0,8] = "[Cycle packs] cycle"; +$packSetting[DispenserDepImage,1,8] = "[Random pack] random"; +$packSetting[DispenserDepImage,2,8] = "M32 Ammo MGClip"; +$packSetting[DispenserDepImage,3,8] = "MP12 Ammo LSMGClip"; +$packSetting[DispenserDepImage,4,8] = "Shotgun Ammo ShotgunClip"; +$packSetting[DispenserDepImage,5,8] = "Rifle Ammo RifleClip"; +$packSetting[DispenserDepImage,6,8] = "Sniper Rifle Ammo snipergunAmmo"; +$packSetting[DispenserDepImage,7,8] = "M32 RPG Attachment Ammo RPGAmmo"; +$packSetting[DispenserDepImage,8,8] = "AT6 Ammo LMissileLauncherAmmo"; +$packSetting[DispenserDepImage,9,8] = "AA Missile Launcher Ammo AALauncherAmmo"; +$packSetting[DispenserDepImage,10,8] = "M79 Launcher Ammo M4Ammo"; +$packSetting[DispenserDepImage,11,8] = "PBC Ammo PBCAmmo"; +$packSetting[DispenserDepImage,12,8] = "Gauss Cannon Ammo RailGunAmmo"; +$packSetting[DispenserDepImage,13,8] = "MG42 Ammo MG42Clip"; +$packSetting[DispenserDepImage,14,8] = "Rotary Shotgun Ammo RShotgunClip"; +$packSetting[DispenserDepImage,15,8] = "Napalm Mortar Ammo NapalmAmmo"; +$packSetting[DispenserDepImage,16,8] = "Frag Grenades Grenade"; +$packSetting[DispenserDepImage,17,8] = "Flash Grenades FlashGrenade"; +$packSetting[DispenserDepImage,18,8] = "Flare Grenades FlareGrenade"; +$packSetting[DispenserDepImage,19,8] = "Concussion Grenades ConcussionGrenade"; +$packSetting[DispenserDepImage,20,8] = "Smoke Grenades SmokeGrenade"; +$packSetting[DispenserDepImage,21,8] = "Smoke Beacon Grenades BeaconSmokeGrenade"; +$packSetting[DispenserDepImage,22,8] = "Camera Grenades CameraGrenade"; +$packSetting[DispenserDepImage,23,8] = "Beacons Beacon"; +$packSetting[DispenserDepImage,24,8] = "Frag Mines Mine"; + + + +///(Arg my poor fingers.. :() + + +datablock ParticleData( DispenserEffectParticle ) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 250; + lifetimeVarianceMS = 100; + + textureName = "flarebase"; + + useInvAlpha = false; + + spinRandomMin = -360.0; + spinRandomMax = 360.0; + + colors[0] = "0.4 0.4 0.4 1.0"; + colors[1] = "0.3 0.3 0.3 0.1"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.4; + sizes[1] = 0.5; + sizes[2] = 0.7; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( DispenserEffectEmitter ) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 1; + + ejectionVelocity = 5.0; // A little oomph at the back end + velocityVariance = 2.0; + + thetaMin = 20.0; + thetaMax = 30.0; + phiReferenceVel = "0"; + phiVariance = "360"; + + orientParticles = False; + orientOnVelocity = False; + + particles = "DispenserEffectParticle"; +}; + + +datablock StaticShapeData(DispenserDep) : DeployedCrate +{ + shapeFile = "stackable4m.dts"; + needsPower = true; +}; + +datablock ShapeBaseImageData(DispenserDepImage) { + mass = 1; + emap = true; + shapeFile = "stackable4m.dts"; + item = DispenserDepPack; + mountPoint = 1; + offset = "0 -0.18 -0.5"; + deployed = DispenserDep; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(DispenserDepPack) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable4m.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "DispenserDepImage"; + pickUpName = "an pack Dispenser"; + heatSignature = 0; + emap = true; + }; + +function DispenserDepPack::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function DispenserDep::gainPower(%data, %obj) +{ +respawnpack(%obj); +} + +function DispenserDep::losePower(%data, %obj) +{ +if (isObject(%obj.emitter)) + %obj.emitter.delete(); +} + + + +function DispenserDepImage::onDeploy(%item, %plyr, %slot) { + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new StaticShape() + { + dataBlock = "DispenserDep"; + scale = "1.5 1.5 0.5"; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + + %set1 = %plyr.packSet ? %plyr.packSet : 0; + %set2 = %plyr.expertSet ? %plyr.expertSet : 0; + %name = GetWord($packSetting[DispenserDepImage, %set1,%set2],2); + + %deplObj.packblock = %name; + %deplObj.set1 = %set1; + %deplObj.set2 = %set2; + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + %deplObj.powerFreq = %plyr.powerFreq; + checkPowerObject(%deplObj); + respawnpack(%deplObj); + + if (!%plyr.client.isAdmin) + { + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + } + + return %deplObj; +} + +function DispenserDep::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + if (isObject(%obj.emitter)) + %obj.emitter.delete(); + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, DispenserDepPack]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function DispenserDepImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); + %obj.hasEmpack = true; // not needed anymore + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + +function DispenserDepImage::onUnmount(%data, %obj, %node) { + %obj.hasEmpack = ""; // not needed anymore + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; +} + + + +//Respawns dispenser's item. + +function respawnpack(%disp,%override) +{ +if ((!IsObject(%disp.pack)) && isObject(%disp)) + { + %name = %disp.packblock; + %set1 = %disp.set1; + %set2 = %disp.set2; + + if (%name $= "cycle") + { + %disp.set1++; + %max = GetWord($packSettings[DispenserDepImage,%set2 ],0); + if (%disp.set1 > %max || %disp.set1 < 2) + %disp.set1 = 2; + %name = GetWord($packSetting[DispenserDepImage, %disp.set1,%set2],2); + } + else if (%name $= "random") + { + %max = GetWord($packSettings[DispenserDepImage,%set2 ],0); + %set1 = mFloor(getRandom()*(%max-2) +2); + %name = GetWord($packSetting[DispenserDepImage, %set1,%set2],2); + } + %pack = new Item() + { + dataBlock = %name; + static = false; + rotate = true; + }; + %pack.startFade(0,0,1); + %pos = VectorAdd(RealVec(%disp,"0 0 1"),%disp.getEdge("0 0 1")); + %pack.setTransform(%pos SPC "0 0 1" SPC getRandom()*$Pi*2); + %em = createLifeEmitter(%pos,DispenserEffectEmitter,1000); + %pack.startFade(1000,0,0); + %em.setRotation(%disp.getRotation); + %pack.dispenser = %disp; + %disp.emitter = %pack; //Lil hook to make it get removed when disas + %disp.pack = %pack; + %pack.addTofxGroup(2); + } +} + +//Not much has changed.. :D + +function DispenserDepImage::ChangeMode(%data,%plyr,%val,%level) +{ +if (%level == 0) + { + if (%plyr.expertset $= "") + %plyr.expertset = 0; + //Selecting Dispenser + %set = %plyr.expertSet $= "" ? 0 : %plyr.expertSet; + %image = %data.getName(); + %settings = $packSettings[%image,%set]; + + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > getWord(%settings,0)) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $PackSetting[%image,%plyr.packSet,%set]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + bottomPrint(%plyr.client,%packname SPC "set to"SPC %line,2,1); + + } +else + { + Parent::ChangeMode(%data,%plyr,%val,%level); + } +} + +/////////Detonation PACK////////// + + +$expertSettings[DetonationDepImage] = "2 -1 Detonation Pack:[Options]"; + +$expertSetting[DetonationDepImage,0] = "Select Payload"; +$expertSetting[DetonationDepImage,1] = "Select Detonation logic"; +$expertSetting[DetonationDepImage,2] = "Select Detonation Time"; + +$packSettings[DetonationDepImage] = "12 1 Detonation Pack:[Payload]"; +$packSetting[DetonationDepImage,0] = "Dud Explosion 5"; +$packSetting[DetonationDepImage,1] = "Repair Pulse 20"; +$packSetting[DetonationDepImage,2] = "Cloack Pulse 20"; +$packSetting[DetonationDepImage,3] = "Decon Pulse 20"; +$packSetting[DetonationDepImage,4] = "ESP Pulse 60"; +$packSetting[DetonationDepImage,5] = "Morph Pulse 60"; +$packSetting[DetonationDepImage,6] = "Ion Zap 30"; +$packSetting[DetonationDepImage,7] = "Mortar Fountain 30"; +$packSetting[DetonationDepImage,8] = "Bomber Crash 30"; +$packSetting[DetonationDepImage,9] = "Satchel Charge 30"; +$packSetting[DetonationDepImage,10] = "Atommic NukeCannon 120"; +$packSetting[DetonationDepImage,11] = "ArrowIV 150ktNuke 180"; +$packSetting[DetonationDepImage,12] = "Dark Hole 360"; + +$packSettings[DetonationDepImage,1] = "4 -1 Detonation Pack: [Detonation Logic]"; +$packSetting[DetonationDepImage,0,1] = "Detonate when armed"; +$packSetting[DetonationDepImage,1,1] = "Detonate when powered"; +$packSetting[DetonationDepImage,2,1] = "Detonate when not powered"; +$packSetting[DetonationDepImage,3,1] = "Detonate when deconstructed"; +$packSetting[DetonationDepImage,4,1] = "Detonate when destroyed"; + +$packSettings[DetonationDepImage,2] = "7 -1 Detonation Pack: [Detonation Time]"; +$packSetting[DetonationDepImage,0,2] = "3 Seconds"; +$packSetting[DetonationDepImage,1,2] = "5 Seconds"; +$packSetting[DetonationDepImage,2,2] = "10 Secconds"; +$packSetting[DetonationDepImage,3,2] = "20 Seconds"; +$packSetting[DetonationDepImage,4,2] = "30 Seconds"; +$packSetting[DetonationDepImage,5,2] = "60 Seconds"; +$packSetting[DetonationDepImage,6,2] = "120 Seconds"; +$packSetting[DetonationDepImage,7,2] = "600 Seconds"; + + +datablock StaticShapeData(DetonationDep) : DeployedCrate +{ + shapeFile = "stackable2l.dts"; + needsPower = true; + targetNameTag = 'Detonation'; + targetTypeTag = ''; +}; + +datablock StaticShapeData(DetonationDepArm) : DeployedCrate +{ + shapeFile = "stackable4m.dts"; + needsPower = true; +}; + + + +datablock ShapeBaseImageData(DetonationDepImage) { + mass = 1; + emap = true; + shapeFile = "stackable2l.dts"; + item = DetonationDepPack; + mountPoint = 1; + offset = "0 -1 -1"; + deployed = DetonationDepImage; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(DetonationDepPack) { + explosion = SatchelMainExplosion; + underwaterExplosion = UnderwaterSatchelMainExplosion; + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable2l.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "DetonationDepImage"; + pickUpName = "an Detonation pack"; + heatSignature = 0; + emap = true; +armDelay = 3000; + maxDamage = 0.6; + + kickBackStrength = 4000; + + }; + +function DetonationDepPack::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function DetonationDep::gainPower(%data, %obj) +{ +if (%obj.dlogic==1 && %obj.armed) + { + %obj.detsched = schedule(%obj.dettime*1000,%obj,"DetonationDepDetonate",%obj); + PlaydetTimer(%obj,%obj.dettime); + } +else if (%obj.dlogic == 1) + { + %obj.wantdet = 1; + } +else if (%obj.dlogic == 2) + { + if (%obj.armed) + serverplay3d(MTCThinkSound,%obj.getTransform()); + StopDetTimer(%obj); + //Cancel(%Obj.armsch); + %obj.wantdet = 0; + Cancel(%obj.detsched); + } +} + +function DetonationDep::losePower(%data, %obj) +{ +if (%obj.dlogic==2 && %obj.armed) + { + %obj.detsched = schedule(%obj.dettime*1000,%obj,"DetonationDepDetonate",%obj); + PlaydetTimer(%obj,%obj.dettime); + } +else if (%obj.dlogic == 2) + { + %obj.wantdet = 1; + } +else if (%obj.dlogic == 1) + { + if (%obj.armed) + serverplay3d(MTCThinkSound,%obj.getTransform()); + StopDetTimer(%obj); + //Cancel(%Obj.armsch); + %obj.wantdet = 0; + Cancel(%obj.detsched); + } +} + + + + +function DetonationDepImage::onDeploy(%item, %plyr, %slot) { + + if (!$Host::SatchelChargeEnabled) + { + messageAll("", "\c2"@ %plyr.client.namebase @" tried to deploy a Detonation pack."); + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + return; + } + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new StaticShape() + { + dataBlock = "DetonationDep"; + scale = "0.4 0.4 1.2"; + }; + %Arm1 = new StaticShape() + { + dataBlock = "DetonationDeparm"; + scale = "2 2.2 2"; + }; + %arm2= new StaticShape() + { + dataBlock = "DetonationDeparm"; + scale = "2 2.2 2"; + }; + %arm3= new StaticShape() + { + dataBlock = "DetonationDeparm"; + scale = "0.8 0.8 0.4"; + }; + %arm4= new StaticShape() + { + dataBlock = "DetonationDeparm"; + scale = "0.8 0.8 0.4"; + }; + %deplObj.ownername = %plyr.client.namebase; + setTargetName(%deplObj.target,addTaggedString(%plyr.client.namebase SPC "\'s detonation pack [arming]")); + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + %rot1=rotadd(%rot,"0 1 0 -1.57"); + %rot2=rotadd(%rot,"0 1 0" SPC $Pi / 2); + %rot3=rotadd(%rot,"0 1 0" SPC -1*$Pi / 2); + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot1); + %deplObj.setEdge(%item.surfacePt,"1 0 0"); + + %arm1.setTransform(%item.surfacePt SPC %rot); + %arm1.setEdge(%deplObj.getEdge("1 0 1"),"0 0 -1"); + %arm2.setTransform(%item.surfacePt SPC %rot); + %arm2.setEdge(%deplObj.getEdge("1 0 -1"),"0 0 -1"); + %arm3.setTransform(%item.surfacePt SPC %rot2); + %arm3.setEdge(%arm1.getEdge("-0.9 0 1"),"1 0 -1"); + %arm4.setTransform(%item.surfacePt SPC %rot3); + %arm4.setEdge(%arm2.getEdge("0.9 0 1"),"-1 0 -1"); + + //%deplobj.emitter = CreateEmitter(%deplObj.getEdge("0 0 0"),LoadingE); + //%deplobj.emitter.setRotation(%rot); + %emitter1 = CreateEmitter(%arm1.getEdge("0 0 1.2"),LoadingE2); + %emitter2 = CreateEmitter(%arm2.getEdge("0 0 1.2"),LoadingE2); + %deplobj.children=6; + %deplobj.child[0] = %arm1; + %deplobj.child[1] = %arm2; + %deplobj.child[2] = %arm3; + %deplobj.child[3] = %arm4; + %deplobj.child[4] = %emitter1; + %deplobj.child[5] = %emitter2; + + %arm1.parent = %deplobj; + %arm2.parent = %deplobj; + %arm3.parent = %deplobj; + %arm4.parent = %deplobj; + %deplobj.payload = %plyr.packset; + %deplobj.armtime = getWord($packSetting[DetonationDepImage,%plyr.packset],2); + %deplobj.dettime = getWord($packSetting[DetonationDepImage,%plyr.packset[2],2],0); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %arm1.team = %plyr.client.Team; + %arm1.setOwner(%plyr); + %arm2.team = %plyr.client.Team; + %arm2.setOwner(%plyr); + %arm3.team = %plyr.client.Team; + %arm3.setOwner(%plyr); + %arm4.team = %plyr.client.Team; + %arm4.setOwner(%plyr); + + %deplObj.dLogic = %plyr.packset[1]; + + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + %deplObj.powerFreq = %plyr.powerFreq; + checkPowerObject(%deplObj); + + //Warnings + %warns = mfloor(%deplobj.armtime/30); + for (%i =0; %i<%warns;%i++) + { + schedule(%i*30000,%deplObj,"bottomPrint",%plyr.client,"Detonation pack arming in" SPC (%warns-%i)*30 SPC"seconds",2,1); + } + %name = %plyr.client.namebase; + messageAll("", "\c2"@ %name @" deployed a Detonation pack.");//~wfx/misc/red_alert.wav"); + + %detpoint = new WayPoint() + { + position = %item.surfacept; + rotation = "1 0 0 0"; + scale = "1 1 1"; + name = "Arming Detonation pack"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + }; + MissionCleanup.add(%detpoint); + %detpoint.schedule(5 * 1000, "delete"); + + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + %deplObj.armsch = schedule(%deplobj.armtime*1000,%deplObj,"ArmDetonationDep",%deplObj); + + return %deplObj; +} + + +function DetonationDep::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.dlogic==4 && %obj.armed) + { + %obj.armed = 0; + DetonationDepDetonate(%obj); + } + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, DetonationDepPack]--; + remDSurface(%obj); + for(%i=0;%i<%obj.children;%i++) + { + if (isObject(%obj.child[%i])) + %obj.child[%i].schedule(500, "delete"); + } + %obj.schedule(500, "delete"); +} + +function DetonationDep::disassemble(%data,%plyr,%obj) { + if (!isObject(%obj) || %obj.isRemoved) + return; + if (%obj.dlogic==3 && %obj.armed) + { + DetonationDepDetonate(%obj); + //PlaydetTimer(%obj,%obj.dettime); + + } + for(%i=0;%i<%obj.children;%i++) + { + if (isObject(%obj.child[%i])) + %obj.child[%i].delete(); + } + Parent::disassemble(%data,%plyr,%obj); +} + +function DetonationDepArm::disassemble(%data,%plyr,%obj) { + Cancel(%Obj.armsch); + if (!isObject(%obj) || %obj.isRemoved) + return; + if (isObject(%obj.parent)) { + %obj.parent.getDatablock().disassemble(%plyr,%obj.parent); + } + Parent::disassemble(%data,%plyr,%obj); +} + +function StartArmDetonationDep(%obj) +{ +Cancel(%obj.detsched); +Cancel(%Obj.armsch); +StopDetTimer(%obj); +%obj.armed = 0; +setTargetName(%Obj.target,addTaggedString(%obj.owner.namebase SPC "\'s detonation pack [arming]")); +if (isObject(%obj.child[4])) + %obj.child[4].delete(); +if (isObject(%obj.child[5])) + %obj.child[5].delete(); +%obj.child[4] = CreateEmitter(%obj.child[0].getEdge("0 0 1.2"),LoadingE2); +%obj.child[5] = CreateEmitter(%obj.child[1].getEdge("0 0 1.2"),LoadingE2); + +%Obj.armsch = schedule(%obj.armtime*1000,%Obj,"ArmDetonationDep",%obj); +} + +function ArmDetonationDep(%obj) +{ +Cancel(%obj.detsched); +Cancel(%Obj.armsch); +StopDetTimer(%obj); + +setTargetName(%Obj.target,addTaggedString(%obj.ownername SPC "\'s detonation pack [armed]")); +serverplay3d(MTCThinkSound,%obj.getTransform()); + +if (isObject(%obj.child[4])) + %obj.child[4].delete(); +if (isObject(%obj.child[5])) + %obj.child[5].delete(); +%obj.child[4] = CreateEmitter(%obj.getEdge("0 0 0"),LoadingE); +%obj.child[4].setRotation(%obj.child[0].getrotation()); +%obj.armed = 1; + +if (%obj.dlogic == 0 || %obj.wantdet) + { + %obj.detsched = schedule(%obj.dettime*1000,%obj,"DetonationDepDetonate",%obj); + PlaydetTimer(%obj,%obj.dettime); + } +} + +function PlaydetTimer(%obj,%timeleft) +{ +StopDetTimer(%obj); +%times = "1 2 3 5 10 30 60"; +%obj.counting = 1; +for (%i=0;%i<7;%i++) + { + %tt = GetWord(%times,%i); + if (%tt<=%timeleft) + { + %point = %timeleft-%tt; + %obj.countsound[%i] = schedule(%point*1000,%obj,"PlaydetSound",%obj,GetWord(%times,%i)); + } + } +if (%obj.dlogic != 0) + { + //%time = (%obj.dettime < 30) ? 30 : %obj.dettime; + //%obj.countsound[8] = schedule(%timeleft*1000,%obj,"PlaydetTimer",%obj,%time); + } +} + +function StopDetTimer(%obj) +{ +%obj.counting = 0; +for (%i=0;%i<8;%i++) + { + Cancel(%obj.countsound[%i]); + } +} + +function PlaydetSound(%obj,%time) +{ +if (isObject(%obj) && %obj.counting) + { + setTargetName(%obj.target,addTaggedString(%obj.ownername SPC "\'s detonation pack ["@ %time @" seconds mark]")); + ServerPlay3D(MessageRecieveSound,%obj.getTransform()); + } +} + +function DetonationDepImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); + %obj.hasEmpack = true; // not needed anymore + %obj.packSet = 0; + %obj.expertSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; +} + +function DetonationDepImage::onUnmount(%data, %obj, %node) { + %obj.hasEmpack = ""; // not needed anymore + %obj.packSet = 0; + %obj.expertSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; +} + + + +function DetonationDepImage::ChangeMode(%data,%plyr,%val,%level) +{ +if (%level == 0) + { + //Selecting Detonation + if (!%plyr.expertSet) + { + Parent::ChangeMode(%data,%plyr,%val,%level); + //%plyr.packset[0] = GetWord($packSetting[DetonationDepImage,%plyr.packset],2); + } + //Selecting selection mode/PowerLogic/CloakLogic + else if (%plyr.expertSet > 0) + { + %set = %plyr.expertSet; + %image = %data.getName(); + %settings = $packSettings[%image,%set]; + + %plyr.packSet[%set] = %plyr.packSet[%set] + %val; + if (%plyr.packSet[%set] > getWord(%settings,0)) + %plyr.packSet[%set] = 0; + if (%plyr.packSet[%set] < 0) + %plyr.packSet[%set] = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $PackSetting[%image,%plyr.packSet[%set],%set]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + bottomPrint(%plyr.client,%packname SPC "set to"SPC %line,2,1); + } + } +else + { + Parent::ChangeMode(%data,%plyr,%val,%level); + } +} + +function DetonationDepDetonate(%obj) +{ +if (!%obj.payload) + { + //do nothing + } +else if (%obj.payload == 1) + { + Aidpulse(%obj.getTransform(),%obj.owner,0); + } +else if (%obj.payload == 2) + { + Aidpulse(%obj.getTransform(),%obj.owner,1); + } +else if (%obj.payload == 3) + { + Aidpulse(%obj.getTransform(),%obj.owner,2); + } +else if (%obj.payload == 4) + { + Aidpulse(%obj.getTransform(),%obj.owner,3); + } +else if (%obj.payload == 5) + { + Aidpulse(%obj.getTransform(),%obj.owner,4); + } +else if (%obj.payload == 6) + { + %obj.Zap(5000); + } +else if (%obj.payload == 7) + { + for (%i=0;%i<5;%i++) + { + %ranupvec = GetRandom()*2-1 SPC GetRandom()*2-1 SPC GetRandom()*1; + MortarShot.Create(%obj.getEdge("-2 0 0"),VectorScale(realvec(%obj,%ranupvec),0.5)); + } + } +else if (%obj.payload == 8) + { + for (%i=0;%i<5;%i++) + { + %ranupvec = GetRandom()*2-1 SPC GetRandom()*2-1 SPC GetRandom()*1; + BomberBomb.Create(%obj.getEdge("-2 0 0"),realvec(%obj,%ranupvec),VectorScale(realvec(%obj,%ranupvec),20)); + } + } +else if (%obj.payload == 9) + { + scatchelkaboom(%obj.getEdge("-2 0 0"),%obj.owner); + } +else if (%obj.payload == 10) + { + BigFatNukeDrop(%obj.getTransform()); + } +else if (%obj.payload == 11) + { + ShoulderNuclear.onExplode(%obj, %obj.getTransform()); + } +else if (%obj.payload == 12) + { + %pos = %obj.getTransform(); + %mask = $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::InteriorObjectType; + %res = containerRayCast(%pos,"0 0 500",%mask, %obj); + if (%res) + %inside = 1; + dome(VectorAdd(%pos,"0 0" SPC (1-%inside)*200),60000,%inside); + } + if (!%obj.isRemoved) + { + if (%obj.payload == 0 || %obj.payload > 9) + %obj.setDamageState(Destroyed); + else + { + StartArmDetonationDep(%obj); + } + } +} + +function scatchelkaboom(%pos,%owner) +{ +%pack = new Item() + { + dataBlock = SatchelChargeThrown; + static = false; + rotate = true; + }; +//%pack.sourceObject = %owner.player; +//%pack.blowingUp=1; +%pack.setTransform(%pos); +%pack.armed = True; +%pack.Schedule(50,"setDamageState",Destroyed); +} + diff --git a/Scripts/Packs/FlamerAmmopack.cs b/Scripts/Packs/FlamerAmmopack.cs new file mode 100644 index 0000000..3892789 --- /dev/null +++ b/Scripts/Packs/FlamerAmmopack.cs @@ -0,0 +1,144 @@ +// ------------------------------------------------------------------ +// Flamer Ammo Pack + +datablock ShapeBaseImageData(FlamerAmmoPackImage) +{ + shapeFile = "ammo_plasma.dts"; + item = FlamerAmmoPack; + mountPoint = 1; + offset = "0 0 -0.4"; + mass = 32; +}; + +datablock ItemData(FlamerAmmoPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "ammo_plasma.dts"; + offset = "0 -0.2 -0.55"; + mass = 18.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "FlamerAmmoPackImage"; + pickUpName = "an flamer ammo pack"; + + computeCRC = true; + + +// lightType = "PulsingLight"; +// lightColor = "0.2 0.4 0.0 1.0"; +// lightTime = "1200"; +// lightRadius = "1.0"; + + max[ChaingunAmmo] = 0; + max[MortarAmmo] = 0; + max[MissileLauncherAmmo] = 0; + max[mgclip] = 0; + max[SniperGunAmmo] = 0; + max[BazookaAmmo] = 0; + max[MG42Clip] = 0; + max[Pistolclip] = 0; + max[FlamerAmmo] = 150; + max[AALauncherAmmo] = 0; + max[RifleClip] = 0; + max[ShotgunClip] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncherAmmo] = 0; + max[RPGAmmo] = 0; + max[PBCAmmo] = 0; +}; + +function FlamerAmmoPack::onPickup(%this,%pack,%player,%amount) +{ + // %this = AmmoPack datablock + // %pack = AmmoPack object number + // %player = player + // %amount = 1 + + for (%idx = 0; %idx < $numAmmoItems; %idx++) + { + %ammo = $AmmoItem[%idx]; + if (%pack.inv[%ammo] > 0) + { + %amount = %pack.getInventory(%ammo); + %player.incInventory(%ammo,%amount); + %pack.setInventory(%ammo,0); + } + else if(%pack.inv[%ammo] == -1) + { + // this particular type of ammo has already been exhausted for this pack; + // don't give the player any + } + else + { + // Assume it's full if no inventory has been assigned + %player.incInventory(%ammo,%this.max[%ammo]); + } + } +} + +function FlamerAmmoPack::onThrow(%this,%pack,%player) +{ + // %this = AmmoPack datablock + // %pack = AmmoPack object number + // %player = player + + %player.throwflamerAmmoPack = 1; + dropFlamerAmmoPack(%pack, %player); + // do the normal ItemData::onThrow stuff -- sound and schedule deletion + serverPlay3D(ItemThrowSound, %player.getTransform()); + %pack.schedulePop(); +} + +function FlamerAmmoPack::onInventory(%this,%player,%value) +{ + // %this = AmmoPack + // %player = player + // %value = 1 if gaining a pack, 0 if losing a pack + + // the below test is necessary because this function is called everytime the ammo + // pack gains or loses an item + if(%player.getClassName() $= "Player") + { + if(!%value) + if(%player.throwflamerAmmoPack == 1) + { + %player.throwflamerAmmoPack = 0; + } + else + { + dropflamerAmmoPack(-1, %player); + } + } + Pack::onInventory(%this,%player,%value); +} + +function dropFlamerAmmoPack(%packObj, %player) +{ + // %packObj = Ammo Pack object number if pack is being thrown, -1 if sold at inv station + // %player = player object + + for(%i = 0; %i < $numAmmoItems; %i++) + { + %ammo = $AmmoItem[%i]; + %pAmmo = %player.getInventory(%ammo); + %pMax = %player.getDatablock().max[%ammo]; + if(%pAmmo > %pMax) + { + if(%packObj > 0) + { + %packObj.setInventory(%ammo, %pAmmo - %pMax); + } + %player.setInventory(%ammo, %pMax); + } + else + { + if(%packObj > 0) + { + %packObj.inv[%ammo] = -1; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Packs/JetBooster.cs b/Scripts/Packs/JetBooster.cs new file mode 100644 index 0000000..2349da7 --- /dev/null +++ b/Scripts/Packs/JetBooster.cs @@ -0,0 +1,53 @@ +//------------------------------------------ +// Jet Booster Pack +// Made by: Blnukem. +//------------------------------------------ +//------------------------------------------ + +datablock ShapeBaseImageData(BoosterPackImage) +{ + shapeFile = "pack_upgrade_energy.dts"; + item = BoosterPack; + mountPoint = 1; + offset = "0 0 0"; + rechargeRateBoost = 1.5; + + minRankPoints = 2550; + + stateName[0] = "default"; + stateSequence[0] = "activation"; +}; + +datablock ItemData(BoosterPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_energy.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "BoosterPackImage"; + pickUpName = "a jet booster pack"; + + computeCRC = true; + +}; + +function BoosterPackImage::onMount(%data, %obj, %node) +{ + %obj.setrechargeRate(%obj.getrechargeRate() + %data.rechargeRateBoost); // Allows the charging boost + %obj.hasBoosterPack = true; +} + +function BoosterPackImage::onUnmount(%data, %obj, %node) +{ + %obj.setrechargeRate(%obj.getrechargeRate() - %data.rechargeRateBoost); // Takes away the charging boost when the pack is gone + %obj.hasBoosterPack = ""; +} + +function BoosterPack::onPickup(%this, %obj, %shape, %amount) +{ +// Prevents Console Errors +} diff --git a/Scripts/Packs/Medpack.cs b/Scripts/Packs/Medpack.cs new file mode 100644 index 0000000..d700255 --- /dev/null +++ b/Scripts/Packs/Medpack.cs @@ -0,0 +1,482 @@ +datablock AudioProfile(MedPackCureSound) +{ + filename = "fx/packs/shield_hit.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock ShapeBaseImageData(MedPackImage) +{ + shapeFile = "pack_upgrade_ammo.dts"; + item = MedPack; + mountPoint = 1; + offset = "0 0 0"; + emap = true; + + gun = MedPackGunImage; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateSequence[1] = "fire"; + stateSound[1] = RepairPackActivateSound; + stateTransitionOnTriggerUp[1] = "Deactivate"; + + stateName[2] = "Deactivate"; + stateScript[2] = "onDeactivate"; + stateTransitionOnTimeout[2] = "Idle"; +}; + +datablock ItemData(MedPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_ammo.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "MedPackImage"; + pickUpName = "a Med pack"; + + lightOnlyStatic = true; + lightType = "PulsingLight"; + lightColor = "1 0 0 1"; + lightTime = 1200; + lightRadius = 4; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(MedpackImg1) +{ + shapeFile = "pack_upgrade_repair.dts"; + mountPoint = 1; + offset = "0 -0.05 0"; + emap = true; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateSequence[1] = "fire"; + stateTransitionOnTriggerUp[1] = "Idle"; +}; + +datablock ShapeBaseImageData(MedpackImg2) +{ + shapeFile = "repair_kit.dts"; + mountPoint = 1; + offset = "0 -0.2 -0.18"; + emap = true; +}; + +datablock ShapeBaseImageData(MedpackImg2b) +{ + shapeFile = "repair_kit.dts"; + mountPoint = 1; + offset = "0 -0.2 0.15"; + emap = true; +}; + +//-------------------------------------------------------------------------- +// Repair Gun + +datablock ShockLanceProjectileData(ReviveProj) +{ + directDamage = 0; + radiusDamageType = $DamageType::ShockLance; + kickBackStrength = 0; // z0dd - ZOD, 3/30/02. More lance kick. was 2500 + velInheritFactor = 0; + sound = ""; + + zapDuration = 1.0; + impulse = 1800; + boltLength = 16.0; + extension = 16.0; + lightningFreq = 25.0; + lightningDensity = 3.0; + lightningAmp = 0.25; + lightningWidth = 0.05; + + shockwave = ShocklanceHit; + + boltSpeed[0] = 2.0; + boltSpeed[1] = -0.5; + + texWrap[0] = 1.5; + texWrap[1] = 1.5; + + startWidth[0] = 0.3; + endWidth[0] = 0.6; + startWidth[1] = 0.3; + endWidth[1] = 0.6; + + texture[0] = "special/shockLightning01"; + texture[1] = "special/shockLightning02"; + texture[2] = "special/shockLightning03"; + texture[3] = "special/ELFBeam"; + + emitter[0] = ShockParticleEmitter; +}; + +datablock ShapeBaseImageData(MedPackGunImage) +{ + shapeFile = "pack_upgrade_repair.dts"; + offset = "0 0.15 0"; + rotation = "1 0 0 90"; + + usesEnergy = true; + minEnergy = 3; + cutOffEnergy = 3.1; + emap = true; + + repairFactorPlayer = 0.005; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.25; + + stateName[1] = "ActivateReady"; + stateScript[1] = "onActivateReady"; + stateSpinThread[1] = Stop; + stateTransitionOnAmmo[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "ActivateReady"; + + stateName[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnNoAmmo[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Repair"; + + stateName[4] = "Repair"; + stateSound[4] = RepairPackFireSound; + stateScript[4] = "onRepair"; + stateAllowImageChange[4] = false; + stateSequence[4] = "fire"; + stateFire[4] = true; + stateEnergyDrain[4] = 32; + stateTransitionOnNoAmmo[4] = "Deactivate"; + stateTransitionOnTriggerUp[4] = "Deactivate"; + stateTransitionOnNotLoaded[4] = "Validate"; + + stateName[5] = "Deactivate"; + stateScript[5] = "onDeactivate"; + stateSpinThread[5] = SpinDown; + stateSequence[5] = "activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 0.2; + stateTransitionOnTimeout[5] = "ActivateReady"; +}; + +//--------------------------- +//PACK STUFF +//--------------------------- + +function MedPackImage::onMount(%data, %obj, %node){ + %obj.mountImage(MedpackImg1, 4); + %obj.mountImage(MedpackImg2, 7); +} + +function MedPackImage::onUnmount(%data, %obj, %node) +{ + if(%obj.getMountedImage($WeaponSlot)) + if(%obj.getMountedImage($WeaponSlot).getName() $= "MedPackGunImage") + %obj.unmountImage($WeaponSlot); + %obj.unmountImage(4); + %obj.unmountImage(7); +} + +function MedPackImage::onActivate(%data, %obj, %slot) +{ + %obj.setImageTrigger(4, true); + %obj.mountImage(MedpackImg2b, 7); + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'scoreScreen' ); + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'inventoryScreen' ); + commandToClient(%obj.triggeredBy.client, 'StationVehicleShowHud'); + + if(%obj.isPilot()) + { + %obj.setImageTrigger(%slot, false); + return; + } + + if(%obj.getMountedImage($WeaponSlot).getName() !$= "MedPackGunImage") + { + messageClient(%obj.client, 'MsgRepairPackOn', '\c2Repair pack activated.'); + + %obj.setArmThread(look); + + %obj.mountImage(MedPackGunImage, $WeaponSlot); + commandToClient(%obj.client, 'setRepairReticle'); + } +} + +function MedPackImage::onDeactivate(%data, %obj, %slot) +{ + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(%slot, false); + if(%obj.getMountedImage($WeaponSlot).getName() $= "MedpackGunImage") + %obj.unmountImage($WeaponSlot); + %obj.mountImage(MedpackImg2, 7); +} + +//--------------------------- +//GUN STUFF +//--------------------------- + +function MedPackGunImage::onMount(%this,%obj,%slot) +{ + %obj.setImageAmmo(%slot,true); + if ( !isDemo() ) + commandToClient( %obj.client, 'setRepairPackIconOn' ); + %obj.usingMedGun = 1; + Bottomprint(%obj.client, "Med Gun: Fire to repair whithin a radius, jet to revive someone.\nIf on mode 2 (use mine key to toggle), you will cure an infected person.", 5, 2); +} + +function MedPackGunImage::onUnmount(%this,%obj,%slot) +{ + if(%obj.isreping == 1) + MedstopRepair(%obj); + + %obj.setImageTrigger(%slot, false); + %obj.setImageTrigger($BackpackSlot, false); + if ( !isDemo() ) + commandToClient( %obj.client, 'setRepairPackIconOff' ); + %obj.usingMedGun = 0; +} + +function MedPackGunImage::onRepair(%this,%obj,%slot){ + %obj.isreping = 1; + %pos = %obj.getWorldBoxCenter(); + %obj.reptargets = ""; + InitContainerRadiusSearch(%pos, 10, $TypeMasks::PlayerObjectType); + while ((%targetObject = containerSearchNext()) != 0){ + if(%targetObject.getDamageLevel() > 0.0 && %targetObject.getState() !$= "dead") + %obj.reptargets = %obj.reptargets @ %targetObject @" "; + } + if(%obj.reptargets $= ""){ + messageclient(%obj.client, 'MsgClient', '\c2No targets to repair.'); + } + Medrepair(%obj, %obj.reptargets); +} + +function MedPackGunImage::onDeactivate(%this,%obj,%slot) +{ + MedstopRepair(%obj); +} + +function Medrepair(%obj, %targets){ + if(%obj.isreping == 0) + return; + if(%targets !$= ""){ + %numtrgs = getNumberOfWords(%targets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%targets, %i); + if(vectorDist(%obj.getWorldBoxCenter(), %target.getWorldBoxCenter()) <= 10 && %target.getDamageLevel() > 0.0){ + if(%target.reping != 1){ + %target.reping = 1; + %target.setRepairRate(%target.getRepairRate() + MedPackGunImage.repairFactorPlayer); + } + } + else{ + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - MedPackGunImage.repairFactorPlayer); + } + } + } + } + + %pos = %obj.getWorldBoxCenter(); + %obj.reptargets = ""; + InitContainerRadiusSearch(%pos, 10, $TypeMasks::PlayerObjectType); + while ((%targetObject = containerSearchNext()) != 0){ + if(%targetObject.getDamageLevel() > 0.0 && %targetObject.getState() !$= "dead") + %obj.reptargets = %obj.reptargets @ %targetObject @" "; + } + if(%obj.isreping == 1) + %obj.reploop = schedule(500, 0, "Medrepair", %obj, %obj.reptargets); +} + +function MedstopRepair(%obj){ + %obj.isreping = 0; + if(%obj.reptargets !$= ""){ + %numtrgs = getNumberOfWords(%obj.reptargets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%obj.reptargets, %i); + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - MedPackGunImage.repairFactorPlayer); + } + } + } +} + +//--------------------------- +//REVIVE +//--------------------------- + +function checkcure(%player) +{ + %pos = %player.getMuzzlePoint($WeaponSlot); + %vec = %player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,5)); + %obj = containerraycast(%pos, %targetpos, $TypeMasks::PlayerObjectType, %player); + %obj = getword(%obj, 0); + + if(!isObject(%obj)) + %obj = %player; + + if(!isObject(%obj.client)) + { + messageClient(%player.client, "", "\c2Either you're trying to cure a zombie or that is the wrong type of object."); + return; + } + + if(%obj.client.team != %player.client.team) + { + messageClient(%player.client, "", "\c2You cannot cure the other team!"); + return; + } + + if(!%obj.infected) + { + if(%obj != %player) + messageClient(%player.client, "", "\c2That person isn't infected."); + else + messageClient(%player.client, "", "\c2You aren't infected."); + return; + } + + %obj.infected = 0; + cancel(%obj.infectedDamage); + %obj.infectedDamage = ""; + %obj.beats = 0; + %obj.canZkill = 0; + %obj.hit = 0; // Ravenger bite count. + cancel(%obj.zombieAttackImpulse); + serverPlay3d(MedPackCureSound, %obj.getTransform()); + if(%obj != %player) + { + messageClient(%obj.client, "", "\c2"@%player.client.nameBase@" has cured you."); + messageClient(%player.client, "", "\c2You just cured "@%obj.client.nameBase@"!"); + } + else + messageClient(%obj.client, "", "\c2You have cured yourself."); + + if(getRandom() > 0.9) + { + %quarter = %obj.getDatablock().maxDamage / 4; + %obj.setDamageLevel(%obj.getDamageLevel() + %quarter); + %obj.setDamageFlash(0.5); + playPain(%obj); + + // Eolk - this is just a safety. + if(%obj.getDamageLevel() >= %obj.getDatablock().maxDamage) + Game.onClientKilled(%obj.client, %player.client, $DamageType::MedPackVaccine); + + messageClient(%obj.client, "", "\c2You have been overdosed on the antidote vaccine!"); + if(%obj != %player) + messageClient(%player.client, "", "\c2You overdosed the target with vaccine!"); + } +} + +function checkrevive(%obj){ + if(isObject(%obj.lasttouchedcorpse)){ + %Tobj = %obj.lasttouchedcorpse; + if(%Tobj.infected == 1 || %Tobj.kibbled == 1){ + messageclient(%obj.client, 'MsgClient', "\c2This body is destroyed or ... changing."); + return; + } + if(vectorDist(%obj.getPosition(),%Tobj.getPosition()) > 3){ + messageclient(%obj.client, 'MsgClient', "\c2Must be in contact with a body to revive it."); + return; + } + %eyevec = %obj.getEyeVector(); + %eyepos = posFromTransform(%obj.getEyeTransform()); + %Tvec = vectorNormalize(vectorSub(%Tobj.getPosition(),%eyepos)); + if(vectorDist(%eyevec, %Tvec) > 0.5){ + messageclient(%obj.client, 'MsgClient', "\c2Must be looking at body."); + return; + } + if(%Tobj.getState() !$= "dead"){ + messageclient(%obj.client, 'MsgClient', "\c2Cannot revive, target isnt dead."); + return; + } + if(%Tobj.team != %obj.team){ + messageclient(%obj.client, 'MsgClient', "\c2Revive target should be the same team as you."); + return; + } + if((%obj.getEnergyLevel() / %obj.getDataBlock().maxEnergy) >= 0.9){ + %obj.setEnergyLevel(0); + revive(%obj, %tobj); + } + else + messageclient(%obj.client, 'MsgClient', "\c2Must have more energy to initiate reviver."); + } + else + messageclient(%obj.client, 'MsgClient', "\c2Must be in contact with a body to revive it."); +} + +function revive(%obj, %target){ + if(%target.client.getControlObject() !$= %target.client.player){ + //necessitys + %target.setDamageLevel(%target.getdatablock().maxDamage - 0.1); + %target.client.setControlObject(%target); + %target.revived = 1; + Cancel(%target.ParaLoop); + Cancel(%target.revcheck); + %target.client.player = %target; + + //points and message + %obj.client.revivecount++; + messageclient(%target.client, 'MsgClient', "\c2You were revived by "@%obj.client.namebase@"."); + messageclient(%obj.client, 'MsgClient', "\c2You revived "@%target.client.namebase@"."); + + //effects + %target.setDamageFlash(1); + %target.setMoveState(true); + playDeathCry(%target); + revivestand(%target, 0); + for(%i =0; %i<$InventoryHudCount; %i++) + %target.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1); + %target.client.clearBackpackIcon(); + + %obj.playAudio(0, ShockLanceHitSound); + %p = new ShockLanceProjectile() { + dataBlock = ReviveProj; + initialDirection = vectorNormalize(vectorSub(%target.getPosition(),%obj.getMuzzlePoint($WeaponSlot))); + initialPosition = %obj.getMuzzlePoint($WeaponSlot); + sourceObject = %obj; + sourceSlot = %obj.getMuzzlePoint($WeaponSlot); + targetId = %target; + }; + MissionCleanup.add(%p); + } + else + messageclient(%obj.client, 'MsgClient', "\c2Target already has another clone in use."); +} + +function revivestand(%obj, %count){ + if(%obj.getstate() $= "dead") + return; + if(%count <= 2){ + %obj.setActionThread("scoutRoot",true); + %obj.setDamageFlash(0.7); + } + else if(%count <= 5){ + %obj.setActionThread("sitting",true); + %obj.setDamageFlash(0.4); + } + else if(%count >= 6){ + %obj.setActionThread("ski",true); + %obj.setMoveState(false); + return; + } + %count++; + schedule(500, 0, "revivestand", %obj, %count); +} diff --git a/Scripts/Packs/PurgeGenerator.cs b/Scripts/Packs/PurgeGenerator.cs new file mode 100644 index 0000000..2713292 --- /dev/null +++ b/Scripts/Packs/PurgeGenerator.cs @@ -0,0 +1,406 @@ +//============================================================================== +// 'Purge' Shield Generator - Version 1.3 +// Made by Blnukem. +//------------------------------------------------------------------------------ +// NOTES TO USERS: +// • Useful for protecting an area from zombies. +// • A large number of Zombie Demons or Lords can destroy the Purge easily. +// • Heavy maintenence is required to keep the Purge active for a long period of +// time, such as repairing the Generator constantly. +// • The Purge has an automatic maintenence system that works great if you're +// only fighting anything smaller than a Zombie Demon, but disables after a +// certain amount of damage, so you need constant maintenence. +// +// When the maintenence system is near to be disabled or when it's first +// activated, the Generator will signal an alarm. +//============================================================================== +// Lets set the amount of Purge Generators per team. + $TeamDeployableMax[ShieldDeployable] = 2; + +//------------------------------------------------------------------------------ + +datablock EffectProfile(PurgeAlarmEffect) +{ + effectname = "Bonuses/upward_perppass2_quark"; + minDistance = 25.0; + maxDistance = 50.0; +}; +//------------------------------------------------------------------------------ + +datablock AudioProfile(PurgeAlarmSound) +{ + filename = "fx/Bonuses/upward_perppass2_quark"; + description = AudioClosest3d; + preload = true; + effect = PurgeAlarmEffect; +}; + +//------------------------------------------------------------------------------ + +datablock StaticShapeData(DeployedShield) : StaticShapeDamageProfile { + className = shieldbase; + shapeFile = "sensor_pulse_medium.dts"; + scale = "0.4 0.4 0.4"; + + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 1.5; + + maxEnergy = 50; + rechargeRate = 0.8; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + humSound = SensorHumSound; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Purge'; + targetTypeTag = 'Shield Generator'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; + deploySound = StationDeploySound; +}; + +//------------------------------------------------------------------------------ + +datablock ShapeBaseImageData(ShieldDeployableImage) { + mass = 20; + emap = true; + shapeFile = "pack_deploy_sensor_pulse.dts"; + item = ShieldDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedShield; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +//------------------------------------------------------------------------------ + +datablock ItemData(ShieldDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_sensor_pulse.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "ShieldDeployableImage"; + pickUpName = "a shield generator"; + heatSignature = 0; + emap = true; +}; + +//============================================================================== +// Shield Generator Functions. +//============================================================================== + +function ShieldDeployableImage::testObjectTooClose(%item) { + return ""; +} + +//------------------------------------------------------------------------------ + +function ShieldDeployableImage::testNoTerrainFound(%item) { +} + +//------------------------------------------------------------------------------ + +function ShieldDeployable::onPickup(%this, %obj, %shape, %amount) { +} + +//------------------------------------------------------------------------------ + +function ShieldDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + // Scale it. + %deplobj.setscale(%deplobj.getdatablock().scale); + + %deplObj.setTransform(%item.surfacePt SPC %rot); + + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %deplObj.shieldbase = %deplObj; + + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + addToDeployGroup(%deplObj); + + AIDeployObject(%plyr.client, %deplObj); + + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + %deplObj.powerFreq = %plyr.powerFreq; + + checkPowerObject(%deplObj); + + // Start the loops. + DeployedShieldLoop(%deplobj); + RepairLoop(%deplobj); + + return %deplObj; +} + +//------------------------------------------------------------------------------ + +function DeployedShield::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, ShieldDeployable]--; + remDSurface(%obj); + %obj.shield.delete(); + %obj.emission.delete(); + %obj.schedule(800, "delete"); +} + +//------------------------------------------------------------------------------ + +function DeployedShield::disassemble(%data,%plyr,%obj) +{ + disassemble(%data,%plyr,%obj); + Parent::onDestroyed(%this,%obj,%prevState); + + %obj.shield.delete(); + %obj.emission.delete(); +} + +//------------------------------------------------------------------------------ +// By Blnukem: + +function RepairLoop(%obj) +{ // Lets initiate self-maintenence when we reach a certain amount of damage. + if (!IsObject(%obj)) + return; + + if (%obj.getDamageLevel() == 1.5 || %obj.getDamageLevel() > 1.5) + { // Initiate alarm - Generator is disabled. + %obj.setRepairRate(0.0); + serverPlay3d("PurgeAlarmSound", %obj.getWorldBoxCenter()); + Schedule(800,0,"RepairLoop",%obj); + return; + } + + if (%obj.getDamageLevel() < 0.01) + { // Stop repairing. + %obj.setRepairRate(0.0); + } else if (%obj.getDamageLevel() > 1.0) + { // Lets start repairing. + %obj.setRepairRate(0.0025); + } + Schedule(800,0,"RepairLoop",%obj); +} + +//------------------------------------------------------------------------------ +// By Blnukem: + +function DeployedShieldLoop(%obj) +{ + if (!IsObject(%obj)) + return; + + if (%obj.getDamageLevel() == 1.5 || %obj.getDamageLevel() > 1.5) + { + schedule(100, 0, "DeployedShieldLoop", %obj); + return; + } + + if (!%obj.PowerCount) + { + schedule(100, 0, "DeployedShieldLoop", %obj); + return; + } + + %Type = $TypeMasks::ProjectileObjectType | $TypeMasks::PlayerObjectType; + InitContainerRadiusSearch(%obj.getPosition(), 25, %Type); + while ((%target = ContainerSearchNext()) != 0) + { + if ((%target !$= %obj) && (strstr(%target.getDatablock().getName(), "Zombie") != -1)) + { // Lets kill any Zombies in contact with the shield. + %target.zapObject(1); + %target.scriptkill($DamageType::Idiocy); + serverPlay3d("ShockLanceHitSound",%target.getWorldBoxCenter()); + %obj.applydamage(0.1); // Damage the Purge Generator. + %obj.playShieldEffect("1 1 1"); + } else if ((%target !$= %obj) && (%target.getDatablock().getName() $= "DemonFireball") || (%target.getDatablock().getName() $= "LZombieAcidBall")) + { // Lets delete any Zombie projectiles in contact with the shield. + %obj.applydamage(0.05); // Damage the Purge Generator. + %obj.playShieldEffect("1 1 1"); + %target.delete(); + } + } + schedule(100,0,"DeployedShieldLoop",%obj); +} + +//------------------------------------------------------------------------------ + +function ShieldDeployableImage::onMount(%data, %obj, %node) { + return %data SPC %obj SPC %node; +} + +//------------------------------------------------------------------------------ + +function ShieldDeployableImage::onUnmount(%data, %obj, %node) { + return %data SPC %obj SPC %node; +} + +//------------------------------------------------------------------------------ + +function DeployedShield::onGainPowerEnabled(%data,%obj) { + Parent::onGainPowerEnabled(%data,%obj); + + if (!IsObject(%obj.shield)) + %obj.shield = createemitter(%obj.getposition(),ShieldBarrierSM,"0 0 0 0"); + if (!IsObject(%obj.emission)) + %obj.emission = createemitter(%obj.getposition(),ShieldEmission,"0 0 0 0"); +} + +//------------------------------------------------------------------------------ + +function DeployedShield::onLosePowerDisabled(%data,%obj) { + Parent::onLosePowerDisabled(%data,%obj); + + if (IsObject(%obj.shield)) + %obj.shield.delete(); + if (IsObject(%obj.emission)) + %obj.emission.delete(); +} + +//============================================================================== +// Shield Particles. +//============================================================================== +// Made by Sloik: + +datablock ParticleData(ShieldBarrierParticles) +{ + dragCoefficient = 0.0; + windCoefficient = 0; + gravityCoefficient = 0; + inheritedVelFactor = 0.0; + constantAcceleration = 0; + lifetimeMS = 5000; + lifetimeVarianceMS = 0; + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + textureName = "special/blasterHit"; + colors[0] = "0.01 0.01 0.01 1.0"; + colors[1] = "0.5 1.0 1.0 1.0"; + colors[2] = "0.01 0.01 0.01 0.0"; + sizes[0] = 0.05; + sizes[1] = 0.2; + sizes[2] = 0.05; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +//------------------------------------------------------------------------------ +// Made by Sloik: + +datablock ParticleEmitterData(ShieldBarrierSM) +{ + ejectionPeriodMS = 1; + ejectionOffset = 25; + periodVarianceMS = 0; + ejectionVelocity = 0.0; + velocityVariance = 0.0; + thetaMin = 5; + thetaMax = 175; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "ShieldBarrierParticles"; +}; + +//------------------------------------------------------------------------------ + +datablock ParticleData(ShieldEmissionParticle) +{ + dragCoefficient = 2; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 800; + textureName = "special/crescent3"; + colors[0] = "0.01 0.01 0.01 1.0"; + colors[1] = "0.5 1.0 1.0 1.0"; + colors[2] = "0.01 0.01 0.01 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.8; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +//------------------------------------------------------------------------------ + +datablock ParticleEmitterData(ShieldEmission) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 40; + velocityVariance = 5.0; + ejectionOffset = 4.0; + thetaMin = 5; + thetaMax = 175; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + particles = "ShieldEmissionParticle"; +}; diff --git a/Scripts/Packs/SentinelPack.cs b/Scripts/Packs/SentinelPack.cs new file mode 100644 index 0000000..94d44cd --- /dev/null +++ b/Scripts/Packs/SentinelPack.cs @@ -0,0 +1,158 @@ +datablock StaticShapeData(DeployedSentinelPatrol) : StaticShapeDamageProfile { + className = "patrolpoint"; + shapeFile = "deploy_sensor_motion.dts"; + + maxDamage = 5.0; + destroyedLevel = 5.0; + disabledLevel = 4.21; + + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Sentinel'; + targetTypeTag = 'Patrol Point'; + deployAmbientThread = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + + heatSignature = 0; + needsPower = false; +}; + +datablock ShapeBaseImageData(SentinelDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = SentinelDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedSentinelPatrol; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(SentinelDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "ZSpawnDeployableImage"; + pickUpName = "a sentinel patrol point pack"; + heatSignature = 0; + emap = true; +}; + +function SentinelDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + %deplObj.setScale("5 5 5"); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // Power object + checkPowerObject(%deplObj); + + SentinelAI_AddPatrolPoint(%deplObj); + return %deplObj; +} + +function SentinelDeployable::onPickup(%data, %obj, %shape, %amount) +{ + // No more spam +} + +function DeployedSentinelPatrol::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + SentinelAI_RemPatrolPoint(%obj); + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, SentinelDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function DeployedSentinelPatrol::disassemble(%data, %plyr, %obj) +{ + SentinelAI_RemPatrolPoint(%obj); + parent::disassemble(%data, %plyr, %obj); +} diff --git a/Scripts/Packs/ZSpawnpack.cs b/Scripts/Packs/ZSpawnpack.cs new file mode 100644 index 0000000..9c366a6 --- /dev/null +++ b/Scripts/Packs/ZSpawnpack.cs @@ -0,0 +1,1938 @@ +//--------------------------------------------------------- +// Zombie Spawn Point +//--------------------------------------------------------- + +$zombie::detectDist = 500; +$zombie::lungDist = 10; +$zombie::LKillDist = 5; +$zombie::Rupvec = 750; +$zombie::killpoints = 2; + +datablock AudioProfile(ZombieMoan) +{ + filename = "fx/environment/growl3.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ZombieHOWL) +{ + filename = "fx/environment/Yeti_Howl1.wav"; + description = AudioBomb3d; + preload = true; +}; + +datablock StaticShapeData(DeployedZSpawnBase) : StaticShapeDamageProfile { + className = "lightbase"; + shapeFile = "pack_deploy_sensor_motion.dts"; + + maxDamage = 2.5; + destroyedLevel = 2.5; + disabledLevel = 2.0; + + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Zombie Spawner'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(ZSpawnDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = ZSpawnDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedZSpawnBase; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(ZSpawnDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "ZSpawnDeployableImage"; + pickUpName = "a Zombie Spawn pack"; + heatSignature = 0; + emap = true; +}; + +datablock ShapeBaseImageData(ZHead) +{ + shapeFile = "bioderm_heavy.dts"; + emap = false; + mountPoint = 1; + offset = "0 0.25 -1.5"; + rotation = "1 0 0 15"; +}; + +datablock ShapeBaseImageData(ZBack) +{ + shapeFile = "bioderm_medium.dts"; + emap = false; + mountPoint = 1; + offset = "0 0.25 -1.25"; + rotation = "-1 0 0 10"; +}; + +datablock ShapeBaseImageData(ZDummyslotImg) +{ + shapeFile = "turret_muzzlepoint.dts"; + emap = false; +}; + +datablock ShapeBaseImageData(ZDummyslotImg2) +{ + shapeFile = "turret_muzzlepoint.dts"; + emap = false; + offset = "-1.5 0 0"; +}; + +datablock ShapeBaseImageData(ZWingImage) +{ + shapeFile = "flag.dts"; + mountPoint = 1; + + offset = "0 0 0"; // L/R - F/B - T/B + rotation = "0 1 0 90"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(ZWingImage2) +{ + shapeFile = "flag.dts"; + mountPoint = 1; + + offset = "0 0 0"; // L/R - F/B - T/B + rotation = "0 -1 0 90"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(ZWingAltImage) +{ + shapeFile = "flag.dts"; + mountPoint = 1; + + offset = "0 0 0"; // L/R - F/B - T/B + rotation = "-0.5 2 0 35"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(ZWingAltImage2) +{ + shapeFile = "flag.dts"; + mountPoint = 1; + + offset = "0 0 0"; // L/R - F/B - T/B + rotation = "-0.5 -2 0 35"; // L/R - F/B - T/B +}; + +function ZSpawnDeployableImage::testObjectTooClose(%item) { + return ""; +} +function ZSpawnDeployableImage::testNoTerrainFound(%item) {} +function ZSpawnDeployable::onPickup(%this, %obj, %shape, %amount) {} + +function ZSpawnDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %deplObj.light.lightBase = %deplObj; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // Power object + checkPowerObject(%deplObj); + + %deplObj.Ztype = %plyr.packSet + 1; + if(%deplObj.Ztype == 3) // Lord Zombie + %deplObj.numZ = 2; + if(%deplObj.Ztype == 6) // Demon Mother + %deplObj.numZ = 2; + + %deplobj.spawnTypeSet = %plyr.expertset; + + return %deplObj; +} + +function DeployedZSpawnBase::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, ZSpawnDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (%obj.ZCloop !$= "") + Cancel(%obj.ZCLoop); +} + +function DeployedZSpawnBase::disassemble(%data,%plyr,%obj) { + if (%obj.ZCloop !$= "") + Cancel(%obj.ZCLoop); + disassemble(%data,%plyr,%obj); +} + +function ZSpawnDeployableImage::onMount(%data, %obj, %node) { + %obj.hasZSpawn = true; + %obj.expertset = 0; +} + +function ZSpawnDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasZSpawn = ""; +} + +function DeployedZSpawnBase::onGainPowerEnabled(%data,%obj) { + if(%obj.spawnTypeSet == 1) + %obj.numz = 0; + if (%obj.ZCloop !$= "") + Cancel(%obj.ZCLoop); + %obj.ZCLoop = schedule(1000, 0, "ZcreateLoop", %obj); + Parent::onGainPowerEnabled(%data,%obj); +} + +function DeployedZSpawnBase::onLosePowerDisabled(%data,%obj) { + if (%obj.ZCloop !$= "") + Cancel(%obj.ZCLoop); + Parent::onLosePowerDisabled(%data,%obj); +} + +function ZcreateLoop(%obj) +{ + if(isObject(%obj)) + { + if(%obj.timedout == 0){ + if((%obj.numZ <= 2 || %obj.numZ $= "") && (ZombieGroup.getCount() < $Host::MaxZombies || $Host::MaxZombies == -1)){ + ZPCreateZombie(%obj); + if(%obj.numZ $= "") + %obj.numZ = 0; + %obj.numZ++; + %obj.timedout = 1; + schedule(10000, %obj, "TimedInF", %obj); + } + } + %obj.ZCLoop = schedule(2000, 0, "ZcreateLoop", %obj); + } +} + +function TimedInF(%obj){ + %obj.timedout = 0; +} + +//this is for when a ZSpawn spawns a zombie +function ZPCreateZombie(%obj){ + %Cpos = vectorAdd(%obj.getposition() ,%obj.getUpvector()); + if(%obj.Ztype $= ""){ + %obj.ZType = 1; + } + if(%obj.ZType == 1){ + %Zombie = new player(){ + Datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),1.15),"0 0 -1.15"),%obj.getposition()); + } + else if(%obj.ZType == 2){ + %Zombie = new player(){ + Datablock = "FZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Ravenger Zombie"); + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),1.15),"0 0 -1.15"),%obj.getposition()); + } + else if(%obj.ZType == 3){ + %Zombie = new player(){ + Datablock = "LordZombieArmor"; + }; + applyskin(%zombie,'ZLord',"Zombie Lord"); + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),2.4),"0 0 -2.4"),%obj.getposition()); + %Zombie.mountImage(ZHead, 3); + %Zombie.mountImage(ZBack, 4); + %Zombie.mountImage(ZDummyslotImg, 5); + %Zombie.mountImage(ZDummyslotImg2, 6); + %zombie.firstFired = 0; + %zombie.canmove = 1; + } + else if(%obj.ZType == 4){ + %Zombie = new player(){ + Datablock = "DemonZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Demon Zombie"); + %zombie.mountImage(ZdummyslotImg, 4); + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),1.3),"0 0 -1.3"),%obj.getposition()); + } + else if(%obj.ZType == 5){ + %Zombie = new player(){ + Datablock = "RapierZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Rapier Zombie"); + %Zombie.mountImage(ZWingImage, 3); + %Zombie.mountImage(ZWingImage2, 4); + %zombie.setActionThread("scoutRoot",true); + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),1),"0 0 -0.6"),%obj.getposition()); + } + else if(%obj.ZType == 6){ + %Cpos = vectoradd(vectoradd(vectorscale(%obj.getUpvector(),1.15),"0 0 -1.15"),%obj.getposition()); + %Zombie = DemonMotherCreate(%Cpos); + } + + %zombie.type = %obj.Ztype; + %Zombie.setTransform(%Cpos); + %Zombie.team = 0; + %zombie.HasCP = 1; + if(%obj.spawnTypeset == 1) + %obj.numZ = 3; + else + %zombie.CP = %obj; + AddToZombieGroup(%Zombie); + if(%obj.ZType != 6) + { + %zombie.canjump = 1; + %zombie.hastarget = 1; + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); + } +} + +//This is for creation of a zombie at a location using the console +function StartAZombie(%pos, %type){ + if(ZombieGroup.getCount >= $Host::MaxZombies && $Host::MaxZombies != -1) + { + error("StartAZombie: Cannot spawn. There are too many zombies on the server."); + return; + } + + if(%Type == 1){ + %Zombie = new player(){ + Datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + } + if(%Type == 2){ + %Zombie = new player(){ + Datablock = "FZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Ravenger Zombie"); + } + if(%Type == 3){ + %Zombie = new player(){ + Datablock = "LordZombieArmor"; + }; + applyskin(%zombie,'ZLord',"Zombie Lord"); + %zombie.client = $zombie::Lclient; + %Zombie.mountImage(ZHead, 3); + %Zombie.mountImage(ZBack, 4); + %Zombie.mountImage(ZDummyslotImg, 5); + %Zombie.mountImage(ZDummyslotImg2, 6); + %zombie.firstFired = 0; + %zombie.canmove = 1; + } + if(%type == 4){ + %Zombie = new player(){ + Datablock = "DemonZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Demon Zombie"); + %zombie.mountImage(ZdummyslotImg, 4); + } + %zombie.type = %type; + %Zombie.setTransform(%pos); + %Zombie.team = 0; + %zombie.canjump = 1; + %zombie.hastarget = 1; + AddToZombieGroup(%Zombie); + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); +} + +//This is for when someone is killed by a zombie and spawns a new one +function CreateZombie(%obj){ + if(ZombieGroup.getCount() >= $Host::MaxZombies && $Host::MaxZombies != -1) + return; + + %Cpos = %obj.getposition(); + %Zombie = new player(){ + Datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + %Zombie.setTransform(%Cpos); + %zombie.type = 1; + %Zombie.team = 0; + %zombie.canjump = 1; + %zombie.hastarget = 1; + AddToZombieGroup(%Zombie); + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); +} + +function ZombieLookforTarget(%zombie) +{ + if(!isObject(%zombie)) { + return; + } + + if(%Zombie.getState() $= "dead") { + freeClientTarget(%zombie); + return; + } + + %pos = %zombie.getposition(); + %wbpos = %zombie.getworldboxcenter(); + %count = ClientGroup.getCount(); + %closestClient = -1; + %closestDistance = 32767; + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if(isObject(%cl.player)){ + %testPos = %cl.player.getWorldBoxCenter(); + %distance = vectorDist(%wbpos, %testPos); + if (%distance > 0 && %distance < %closestDistance && %cl.player.isFTD != 1 && %cl.player.iszombie != 1) + { + %closestClient = %cl; + %closestDistance = %distance; + } + } + } + if(%closestClient){ + if (%zombie.type == 1) + Zombiemovetotarget(%zombie,%closestClient,%closestDistance); + else if (%zombie.type == 2) + FZombiemovetotarget(%zombie,%closestClient,%closestDistance); + else if (%zombie.type == 3) + LZombiemovetotarget(%zombie,%closestClient,%closestDistance); + else if (%zombie.type == 4) + DZombiemovetotarget(%zombie,%closestClient,%closestDistance); + else if (%zombie.type == 5) + RZombiemovetotarget(%zombie,%closestClient,%closestDistance); + } + %zombie.ZombieTargeting = schedule(500, %zombie, "ZombieLookForTarget", %zombie); +} + +function Zombiemovetotarget(%zombie,%closestClient,%closestDistance){ + %pos = %zombie.getworldboxcenter(); + %closestClient = %closestClient.Player; + if(%closestDistance <= $zombie::detectDist){ + if(%zombie.hastarget != 1){ + %zombie.hastarget = 1; + } + %chance = (getrandom() * 20); + if(%chance >= 19) + ZDoMoan(%zombie); + + %clpos = %closestClient.getPosition(); + %vector = vectorNormalize(vectorSub(%clpos, %pos)); + %v1 = getword(%vector, 0); + %v2 = getword(%vector, 1); + %nv1 = %v2; + %nv2 = (%v1 * -1); + %none = 0; + %vector2 = %nv1@" "@%nv2@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector2)); + + %fmultiplier = $Zombie::ForwardSpeed; + if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1) + %fmultiplier = (%fmultiplier * 4); + %vector = vectorscale(%vector, %Fmultiplier); + %upvec = "150"; + if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1){ + %upvec = %upvec * 2; + %zombie.canjump = 0; + schedule(1500, %zombie, "Zsetjump", %zombie); + } + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + if(%z >= 600) + %upvec = (%upvec * 5); + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + } + else if(%zombie.hastarget == 1){ + %zombie.hastarget = 0; + %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); + } +} + +function FZombiemovetotarget(%zombie,%closestClient,%closestDistance){ + %pos = %zombie.getworldboxcenter(); + %closestClient = %closestClient.Player; + if(%closestDistance <= $zombie::detectDist){ + if(%zombie.hastarget != 1){ + %zombie.hastarget = 1; + } + %zombie.setActionThread("scoutRoot",true); + %upvec = "250"; + %fmultiplier = $Zombie::FForwardSpeed; + + //moanStuff + %chance = (getrandom() * 50); + if(%chance >= 49) + ZDoMoan(%zombie); + + //Rotation stuff + %clpos = %closestClient.getPosition(); + %vector = vectorNormalize(vectorSub(%clpos, %pos)); + %v1 = getword(%vector, 0); + %v2 = getword(%vector, 1); + %nv1 = %v2; + %nv2 = (%v1 * -1); + %none = 0; + %vector2 = %nv1@" "@%nv2@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector2)); + + //Move Stuff + if(%closestDistance <= $zombie::lungDist && %zombie.canjump == 1 && getword(%vector, 2) <= "0.8" ){ + %zombie.setvelocity("0 0 0"); + %fmultiplier = (%fmultiplier * 2); + %upvec = (%upvec * 3.5); + %zombie.canjump = 0; + schedule(2000, %zombie, "Zsetjump", %zombie); + } + %vector = vectorscale(%vector, %Fmultiplier); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + if(%z >= "1200" && %zombie.canjump == 1){ + %zombie.setvelocity("0 0 0"); + %upvec = (%upvec * 8); + %x = (%x * 0.5); + %y = (%y * 0.5); + %zombie.canjump = 0; + schedule(2500, %zombie, "Zsetjump", %zombie); + } + + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + } + else if(%zombie.hastarget == 1){ + %zombie.hastarget = 0; + %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); + %zombie.setActionThread("ski",true); + } +} + +function LZombiemovetotarget(%zombie,%closestClient,%closestDistance){ + %pos = %zombie.getworldboxcenter(); + %closestClient = %closestClient.Player; + if(%closestDistance <= $zombie::detectDist && %zombie.canmove != 0){ + if(%zombie.hastarget != 1){ + LZDoYell(%zombie); + %zombie.hastarget = 1; + } + %chance = (getrandom() * 20); + if(%chance >= 19) + LZDoMoan(%zombie); + + %clpos = %closestClient.getPosition(); + %vector = vectorNormalize(vectorSub(%clpos, %pos)); + %nv2 = (getword(%vector, 0) * -1); + %nv1 = getword(%vector, 1); + %none = 0; + %vector2 = %nv1@" "@%nv2@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector2)); + if(%zombie.ATCount >= 20){ + %zombie.ATCount = 0; + %zombie.nomove = 1; + %zombie.Fire = schedule(1250, %zombie, "ZFire", %zombie, %closestClient); + %zombie.Charge = schedule(500, %zombie, "ChargeEmitter", %zombie); + %zombie.chargecount = 0; + } + if(%zombie.nomove != 1) { + %fmultiplier = $Zombie::LForwardSpeed; + %upvec = "150"; + if(%closestDistance <= $zombie::LKillDist && %zombie.canjump == 1){ + %vec = vectoradd(%pos,vectorScale(%vector,($zombie::LkillDist - 1.6))); + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::PlayerObjectType; + %searchResult = containerRayCast(vectoradd(%pos,vectorScale(%vector,1.6)), %vec, %mask, %zombie); + if(%searchResult){ + %searchObj = getWord(%searchResult,0); + if(%searchObj $= %closestClient){ + %chance = getrandom(1,5); + if(%chance == 1){ + %dir = %zombie.getEyeVector(); + %dir = vectornormalize(getword(%dir,0)@" "@getword(%dir,1)@" 0"); + %dir = vectoradd(vectorscale(%dir,7500),"0 0 1000"); + %closestclient.applyimpulse(%clpos,%dir); + %closestclient.damage(0, %clpos, 0.8, $DamageType::ZombieL); + } + else{ + %zombie.setvelocity("0 0 0"); + %zombie.canjump = 0; + schedule(1000, %zombie, "Zsetjump", %zombie); + Llifttarget(%zombie,%closestclient,0); + return; + } + } + } + } + else if(%closestDistance <= ($zombie::LKillDist + $zombie::lungDist)){ + %zombie.setvelocity("0 0 0"); + %fmultiplier = (%fmultiplier * 2.5); + } + %vector = vectorscale(%vector, %Fmultiplier); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + if(%z >= 4000) + %upvec = (%upvec * 5); + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + %zombie.ATCount++; + } + } + else if(%zombie.hastarget == 1 && %zombie.canmove != 0){ + %zombie.hastarget = 0; + %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); + } +} + +function DZombiemovetotarget(%zombie,%closestClient,%closestDistance){ + %pos = %zombie.getworldboxcenter(); + %closestClient = %closestClient.Player; + if(%closestDistance <= $zombie::detectDist){ + if(%zombie.hastarget != 1 && %closestdistance >= 10 && %closestdistance <= 150){ + DzombieFire(%zombie,%closestclient); + %zombie.canjump = 0; + schedule(4000, %zombie, "Zsetjump", %zombie); + } + if(%zombie.hastarget != 1){ + LZDoYell(%zombie); + %zombie.hastarget = 1; + } + %chance = (getrandom() * 20); + if(%chance >= 19) + LZDoMoan(%zombie); + + %clpos = %closestClient.getPosition(); + %vector = vectorNormalize(vectorSub(%clpos, %pos)); + %v1 = getword(%vector, 0); + %v2 = getword(%vector, 1); + %nv1 = %v2; + %nv2 = (%v1 * -1); + %none = 0; + %vector2 = %nv1@" "@%nv2@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector2)); + + if (%closestdistance >= 10 && %closestdistance <= 150 && %zombie.canjump == 1){ + DzombieFire(%zombie,%closestclient); + %zombie.canjump = 0; + schedule(4000, %zombie, "Zsetjump", %zombie); + return; + } + %vector = vectorscale(%vector, $Zombie::DForwardSpeed); + %upvec = "150"; + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + if(%z >= ($Zombie::DForwardSpeed / 3 * 2)) + %upvec = (%upvec * 5); + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + } + else if(%zombie.hastarget == 1){ + %zombie.hastarget = 0; + %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); + } +} + +function RZombiemovetotarget(%zombie,%closestClient,%closestDistance){ + %pos = %zombie.getworldboxcenter(); + %zombie.setActionThread("scoutRoot",true); + %closestClient = %closestClient.Player; + if(%closestDistance <= $zombie::detectDist){ + if(%zombie.wingset == 1){ + %zombie.wingset = 0; + %Zombie.mountImage(ZWingImage, 3); + %Zombie.mountImage(ZWingImage2, 4); + } + else{ + %zombie.wingset = 1; + %Zombie.mountImage(ZWingaltImage, 3); + %Zombie.mountImage(ZWingaltImage2, 4); + } + %chance = (getrandom() * 20); + if(%chance >= 19) + ZDoMoan(%zombie); + if(%zombie.iscarrying == 1){ + %vector = vectorscale(%zombie.getForwardVector(),($Zombie::RForwardSpeed / 2)); + %vector = getword(%vector, 0)@" "@getword(%vector, 1)@" "@($zombie::Rupvec * 1.5); + %zombie.applyImpulse(%zombie.getposition(), %vector); + return; + } + + %clpos = %closestClient.getWorldBoxCenter(); + %vector = vectorNormalize(vectorSub(%clpos, %pos)); + %v1 = getword(%vector, 0); + %v2 = getword(%vector, 1); + %nv1 = %v2; + %nv2 = (%v1 * -1); + %none = 0; + %vector2 = %nv1@" "@%nv2@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector2)); + + %z = Getword(%vector,2); + %spd = vectorLen(%zombie.getVelocity()); + %fallpoint = 0.05 - (%spd / 630); + if(%closestdistance <= 15 || %z > (0.25 + %fallpoint) || %z < (-1 * (0.25 + %fallpoint))){ + if(%z < 0) + %upvec = ($zombie::Rupvec * (%z - (%spd / 130))); + if(%z >= 0) + %upvec = ($zombie::Rupvec * (%z + 1)); + if(%spd <= 5) + %vector = vectorScale(%vector,3); + } + else{ + %upvec = $zombie::Rupvec * (%z + 1.2); + %spdmod = 1; + } + if(%z < 0) + %z = %z * -1; + + %Zz = getWord(%zombie.getVelocity(),2); + if(%Zz <= -40){ + %result = containerRayCast(%pos, vectoradd(%pos,vectorScale("0 0 1",%Zz * 2)), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %zombie); + if(%result) + %upvec = $zombie::Rupvec * 5; + } + + %vector = vectorscale(%vector, ($Zombie::RForwardSpeed * (1 - %z))); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %vector = %x@" "@%y@" "@%upvec; + %zombie.applyImpulse(%pos, %vector); + } +} + +function ZDoMoan(%zombie){ + %chance = (getRandom() * 12); + if(%chance <= 11) + serverPlay3d("ZombieMoan",%zombie.getWorldBoxCenter()); + else + serverPlay3d("ZombieHOWL",%zombie.getWorldBoxCenter()); +} + +function LZDoMoan(%zombie){ + serverPlay3d("ZombieMoan",%zombie.getWorldBoxCenter()); +} + +function LZDoYell(%zombie){ + serverPlay3d("ZombieHOWL",%zombie.getWorldBoxCenter()); +} + +function Zsetjump(%zombie){ + %zombie.canjump = 1; +} + +function ChargeEmitter(%zombie){ + if(!isobject(%zombie)) + return; + if(%zombie.chargecount >= 2){ + %charge2 = new ParticleEmissionDummy() + { + position = %zombie.getMuzzlePoint(6); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%charge2); + %charge2.schedule(100, "delete"); + } + if(%zombie.chargecount <= 7){ + %charge = new ParticleEmissionDummy() + { + position = %zombie.getMuzzlePoint(5); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(100, "delete"); + } + if(%zombie.chargecount <= 9){ + %zombie.Fire = schedule(100, %zombie, "ChargeEmitter", %zombie); + %zombie.chargecount++; + } + else + %zombie.chargecount = 0; +} + +function ZFire(%zombie, %target){ + if(isobject(%zombie) && isobject(%target)){ + if(%Zombie.firstFired == 1){ + %vec = vectorsub(%target.getworldboxcenter(),%zombie.getMuzzlePoint(6)); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(),vectorlen(%vec)/100)); + %zombie.firstFired = 0; + %zombie.nomove = 0; + %p = new TracerProjectile() + { + dataBlock = LZombieAcidBall; + initialDirection = %vec; + initialPosition = %zombie.getMuzzlePoint(6); + sourceObject = %zombie; + sourceSlot = 6; + }; + } + else{ + %vec = vectorsub(%target.getworldboxcenter(),%zombie.getMuzzlePoint(5)); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(),vectorlen(%vec)/100)); + %p = new TracerProjectile() + { + dataBlock = LZombieAcidBall; + initialDirection = %vec; + initialPosition = %zombie.getMuzzlePoint(5); + sourceObject = %zombie; + sourceSlot = 5; + }; + %zombie.firstFired = 1; + %zombie.Fire = schedule(250, %zombie, "ZFire", %zombie, %target); + } + } + else{ + %zombie.firstFired = 0; + %zombie.nomove = 0; + } +} + +function Llifttarget(%zombie,%closestclient,%count){ + if(%count == 0) + %zombie.canmove = 0; + if(%closestclient.getState() $= "dead" || %Zombie.getState() $= "dead"){ + %zombie.canmove = 1; + return; + } + %target = %closestclient; + if(!isobject(%target)){ + %zombie.canmove = 1; + return; + } + %pos = %zombie.getworldboxcenter(); + %Tpos = %target.getworldboxcenter(); + %ZtoT = vectorsub(%tpos,%pos); + if (%count <= 12){ + %newpos = vectoradd(%ZtoT,vectoradd(%pos,"0 0 -0.6")); + %target.setTransform(%newpos); + %target.setvelocity("0 0 0"); + } + else { + %killtype = getrandom(1,2); + if(%killtype == 1){ + %closestwall = 20; + %nv2 = (getword(%ZtoT, 0) * -1); + %nv1 = getword(%ZtoT, 1); + %vector1 = vectorscale(vectornormalize(%nv1@" "@%nv2@" 0"),20); + %nvector1 = vectoradd(%tpos,%vector1); + %nv2 = getword(%ZtoT, 0); + %nv1 = (getword(%ZtoT, 1) * -1); + %vector2 = vectorscale(vectornormalize(%nv1@" "@%nv2@" 0"),20); + %nvector2 = vectoradd(%tpos,%vector2); + %searchresultR = containerRayCast(%tpos, %nvector1, $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType); + %searchresultL = containerRayCast(%tpos, %nvector2, $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType); + if(%searchresultR){ + %Hpos = getword(%searchresultR,1)@" "@getword(%searchresultR,2)@" "@getword(%searchresultR,3); + %distance = vectordist(%Tpos, %Hpos); + if(%distance <= %closestwall){ + %closestwall = %distance; + %vector = vectoradd(vectorscale(%vector1,1500),"0 0 100"); + } + } + if(%searchresultL){ + %Hpos = getword(%searchresultL,1)@" "@getword(%searchresultL,2)@" "@getword(%searchresultL,3); + %distance = vectordist(%Tpos, %Hpos); + if(%distance <= %closestwall){ + %closestwall = %distance; + %vector = vectoradd(vectorscale(%vector2,1500),"0 0 100"); + } + } + if(%closestwall == 20){ + %x = getword(%ZtoT, 0); + %y = getword(%ZtoT, 1); + %outvec = vectorscale(vectornormalize(%x@" "@%y@" 0"),50); + %vector = vectoradd("0 0 -15000",%outvec); + } + %target.applyimpulse(%target.getposition(),%vector); + } + else if(%killtype == 2){ + %target.infected = 1; + %target.damage(0, %target.getposition(), 10.0, $DamageType::ZombieL); + } + %count = 0; + %zombie.canmove = 1; + return; + } + %count++; + %zombie.killingplayer = schedule(150, %zombie, "Llifttarget", %zombie, %closestclient, %count); +} + + +function DZombieFire(%zombie,%closestclient){ + %pos = %zombie.getMuzzlePoint(4); + %tpos = %closestclient.getWorldBoxCenter(); + %tvel = %closestclient.getvelocity(); + %vec = vectorsub(%tpos,%pos); + %dist = vectorlen(%vec); + %velpredict = vectorscale(%tvel,(%dist / 50)); + %vector = vectoradd(%vec,%velpredict); + %ndist = vectorlen(%vector); + %upvec = "0 0 "@((%ndist / 50) * (%ndist / 50) * 2); + %vector = vectornormalize(vectoradd(%vector,%upvec)); + %p = new GrenadeProjectile() + { + dataBlock = DemonFireball; + initialDirection = %vector; + initialPosition = %pos; + sourceObject = %zombie; + sourceSlot = 4; + }; +} + +function RkillLoop(%zombie,%target,%count){ + if(!isObject(%zombie)){ + %zombie.iscarrying = 0; + %target.grabbed = 0; + return; + } + if(!isObject(%target)){ + %zombie.iscarrying = 0; + %target.grabbed = 0; + return; + } + if (%Zombie.getState() $= "dead"){ + %zombie.iscarrying = 0; + %target.grabbed = 0; + return; + } + if (%target.getState() $= "dead"){ + %zombie.iscarrying = 0; + %target.grabbed = 0; + return; + } + if(%count == 50){ + %chance = getRandom(1,3); + if(%chance == 3) + %target.damage(0, %tpos, 10.0, $DamageType::Zombie); + else{ + %target.isFTD = 1; + if(%target.getMountedImage($Backpackslot) !$= "") + %target.throwPack(); + %target.finishingfall = schedule(5000, 0, "stopFTD", %target); + } + %zombie.iscarrying = 0; + return; + } + %target.setPosition(vectoradd(%zombie.getPosition(),"0 0 -4")); + %target.setVelocity(%zombie.getVelocity()); + %count++; + %zombie.killingplayer = schedule(100, 0, "RkillLoop", %zombie, %target, %count); +} + +function stopFTD(%target){ + %target.isFTD = 0; + %target.grabbed = 0; +} + +function ZSetRandomMove(%zombie){ + if (!isobject(%zombie)) + return; + %RX = getrandom(-10, 10); + %RY = getrandom(-10, 10); + %RZ = getrandom(); + %vector = %RX@" "@%RY@" "@%RZ; + %zombie.direction = vectornormalize(%vector); + %zombie.Mnum = getrandom(1, 20); + %zombie.zombieRmove = schedule(500, %zombie, "ZrandommoveLoop", %zombie); +} + +function ZrandommoveLoop(%zombie){ + if (!isobject(%zombie)) { + return; + } + + if (%Zombie.getState() $= "dead") { + return; + } + + if (%zombie.hastarget == 1){ + %zombie.direction = ""; + return; + } + if (%zombie.Mnum >= 1){ + %X = getword(%zombie.direction, 1); + %Y = (getword(%zombie.direction, 0) * -1); + %none = 0; + %vector = %X@" "@%Y@" "@%none; + %zombie.setRotation(fullrot("0 0 0",%vector)); + if(%zombie.type == 1) + %speed = ($zombie::forwardspeed); + else if(%zombie.type == 2) + %speed = ($zombie::Fforwardspeed * 0.6); + else if(%zombie.type == 4) + %speed = ($zombie::Dforwardspeed * 0.75); + else if(%zombie.type == 3) + %speed = ($zombie::Lforwardspeed * 0.8); + %vector = vectorscale(%zombie.direction, %speed); + %zombie.applyimpulse(%zombie.getposition(), %vector); + %zombie.Mnum = (%zombie.Mnum - 1); + %zombie.zombieRmove = schedule(500, %zombie, "ZrandommoveLoop", %zombie); + } + else if(%zombie.Mnum == 0) + %zombie.zombieRmove = schedule(100, %zombie, "ZSetRandomMove", %zombie); +} + +function InfectLoop(%obj){ + if(%obj.isSentinel) + return; + + if((%obj.armor $= "Pure") || (%obj.armor $= "Light")) { + return; + } + + if ($Host::NoInfection) + { + %obj.Infected = 0; + cancel(%obj.infectedDamage); + + %obj.infectedDamage = ""; + %obj.beats = 0; + %obj.canZKill = 0; + + cancel(%player.zombieAttackImpulse); + return; + } + + if (%obj.getState() $= "dead") + return; + if(isObject(%obj)){ + if(%obj.beats $= "") + zombieAttackImpulse(%obj,0); + if(%obj.beats < 15) + %obj.setWhiteOut(%obj.beats * 0.05); + else + %obj.setDamageFlash(1); + if(%obj.beats == 15){ + %obj.canZkill = 1; + } + if(%obj.beats >=15) + serverPlay3d("ZombieMoan",%obj.getWorldBoxCenter()); + else if (%obj.beats >= 10) + playDeathCry(%obj); + else + playPain(%obj); + if(%obj.beats == 20){ + if($host::canZombie $= "") + $host::canZombie = 0; + if($host::canZombie == 1) + makePersonZombie(%obj.getTransform(), %obj.client, ZombieGetRandom()); + else + %obj.damage(0, %obj.getposition(), 10.0, $DamageType::Zombie); + return; + } + %obj.beats++; + %obj.infectedDamage = schedule(2000, %obj, "InfectLoop", %obj); + } +} + +function ZkillUpdateScore(%game, %client,%implement){ + if( %implement.getClassName() $= "Turret") + %client = %implement.getControllingClient(); + else if(%implement.getDataBlock().catagory $= "Vehicles") + %client = %implement.getControllingClient(); + %client.Zkills++; + %game.recalcScore(%client); +} + +function zombieSpawnLoop(%pos, %radius, %freq){ + if(ZombieGroup.getCount() >= $Host::MaxZombies && $Host::MaxZombies != -1) + return; + + if(%freq > 10) + %freq = 10; + if(%freq < 1) + %freq = 1; + %chance = getRandom(1,50); + if(%chance <= %freq && $numspawnedzombies < (%freq * 5)){ + %spawnPos = vectorAdd(%pos,(getRandom(0,%radius) - (%radius / 2))@" "@(getRandom(0,%radius) - (%radius / 2))@" getRandom(0,15)"); + %search = containerRayCast(%spawnPos, vectorAdd(%spawnPos,"0 0 -1000"), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType); + if(%search) + %spawnPos = getWord(%search,1)@" "@getWord(%search,2)@" "@getWord(%search,3); + %chance = getRandom(1,65); + if(%chance <= 25){ + %Zombie = new player(){ + Datablock = "ZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Zombie"); + %Ztype = 1; + } + else if(%chance <= 35){ + %Zombie = new player(){ + Datablock = "FZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Ravenger Zombie"); + %Ztype = 2; + } + else if(%chance <= 40){ + %Zombie = new player(){ + Datablock = "LordZombieArmor"; + }; + applyskin(%zombie,'ZLord',"Zombie Lord"); + %Zombie.mountImage(ZHead, 3); + %Zombie.mountImage(ZBack, 4); + %Zombie.mountImage(ZDummyslotImg, 5); + %Zombie.mountImage(ZDummyslotImg2, 6); + %zombie.firstFired = 0; + %zombie.canmove = 1; + %Ztype = 3; + } + else if(%chance <= 55){ + %Zombie = new player(){ + Datablock = "DemonZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Demon Zombie"); + %zombie.mountImage(ZdummyslotImg, 4); + %Ztype = 4; + } + else if(%chance <= 65){ + %Zombie = new player(){ + Datablock = "RapierZombieArmor"; + }; + applyskin(%zombie,'Zombie',"Rapier Zombie"); + %Zombie.mountImage(ZWingImage, 3); + %Zombie.mountImage(ZWingImage2, 4); + %zombie.setActionThread("scoutRoot",true); + %Ztype = 5; + } + %zombie.type = %Ztype; + %Zombie.setTransform(%spawnPos); + %Zombie.team = 0; + %zombie.canjump = 1; + %zombie.hastarget = 1; + %zombie.randspawnerstarted = 1; + AddToZombieGroup(%Zombie); + schedule(1000, %zombie, "ZombieLookforTarget", %zombie); + $numspawnedzombies++; + } + $zombieloop = schedule(500, 0, "zombieSpawnLoop", %pos, %radius, %freq); +} + +function StopZombieSpawnLoop(){ + cancel($zombieloop); +} + +function ZombieBloodLust(%obj, %count){ + if(!isObject(%obj)) + return; + if (%obj.getState() $= "dead") + return; + %obj.setDamageFlash(0.5); + if(%count == 10){ + serverPlay3d("ZombieMoan",%obj.getWorldBoxCenter()); + %count = 0; + } + %count++; + schedule(200, %obj, "ZombieBloodLust", %obj, %count); +} + +function makePersonZombie(%trans, %client, %special) +{ + %client.player.delete(); + + %client.setWeaponsHudClearAll(); + if(%special == 1) + %data = "ControlLordZombieArmor"; + else if(%special == 2) + %data = "ControlRapierZombieArmor"; + else if(%special == 3) + %data = "ControlDemonZombieArmor"; + else + %data = "ZombieArmor"; + + %player = new Player() { + dataBlock = %data; + }; + + if(%special == 1) + { +// %player.mountImage(ZHead, 3); + %player.mountImage(ZBack, 4); + %player.mountImage(ZDummyslotImg, 5); + %player.mountImage(ZDummyslotImg2, 6); + } + else if(%special == 2) + { + %player.mountImage(ZWingImage, 3); + %player.mountImage(ZWingImage2, 4); + } + + %player.setTransform( %trans ); + AddToZombieGroup(%player); + + // setup some info + %player.setOwnerClient(%client); + %player.team = 0; + %client.outOfBounds = false; + %player.setEnergyLevel(0); + %client.player = %player; + + // updates client's target info for this player + %player.setTarget(%client.target); + setTargetDataBlock(%client.target, %player.getDatablock()); + setTargetSensorData(%client.target, PlayerSensor); + setTargetSensorGroup(%client.target, 1); + %client.setSensorGroup(1); + + %client.setControlObject(%player); + %player.client.clearBackpackIcon(); + + %player.iszombie = 1; + + if(%special == 1) + { + %player.setInventory(LordAcidGun, 1, true); + %player.use("LordAcidGun"); // Eolk - attempt to fix this problem. + } + else if(%special == 2) + RapierCarryCheck(%player); + else if(%special == 3) + { + %player.setInventory(DZShot, 1, true); + %player.use("DZShot"); // Eolk - attempt to fix this problem. + } + + ZombieBloodLust(%player,0); + if(!%special) + zombieAttackImpulse(%player,80); +} + +// This keeps the rapier-human squatting and acting like a real rapier when carrying someone to die. +function RapierCarryCheck(%obj) +{ + if(!isObject(%obj) || %obj.getState() $= "Dead") + return; + + if(%obj.isCarrying == 1) + { + %vector = VectorScale(%obj.getEyeVector(), $Zombie::RForwardSpeed + %obj.getDatablock().mass); + %vector = getword(%vector, 0) SPC getword(%vector, 1) SPC ($Zombie::rupvec * 1.5); + %obj.applyImpulse(%obj.position, %vector); + } + + %obj.setActionThread("scoutroot", 1); + %obj.RImpulse = schedule(500, 0, "RapierCarryCheck", %obj); +} + +function zombieAttackImpulse(%obj, %count){ + if(!isObject(%obj)) { + return; + } + + if (%obj.getState() $= "Dead") { + return; + } + + %pos = %obj.getposition(); + InitContainerRadiusSearch(%pos, %count, $TypeMasks::PlayerObjectType || $TypeMasks::VehicleObjectType); + while ((%objtarget = containerSearchNext()) != 0){ + if(isObject(%objtarget) && %objtarget !$= %obj){ + %objarmortype = %objtarget.getdatablock().getname(); + if(%objarmortype $= "ZombieArmor" || %objarmortype $= "FZombieArmor" || %objarmortype $= "LordZombieArmor" || %objarmortype $= "DemonZombieArmor" || %objarmortype $= "ControlDemonZombieArmor" || %objarmortype $= "RapierZombieArmor" || %objarmortype $= "ControlRapierZombieArmor" || %objarmortype $= "ControlLordZombieArmor") + %objiszomb = 1; + if(!(%objiszomb) && %objtarget.iszombie != 1) { + %vec = vectorNormalize(vectorSub(%objtarget.getWorldBoxCenter(),%obj.getWorldBoxCenter())); + if(vectorDist(%vec,%obj.getForwardVector()) <= 0.5){ + if(%count <= 200){ + %impulse = (%count / 20) * 90; + %up = (%count / 100) * 90; + } + else{ + %impulse = 900; + %up = 180; + } + %vec = vectorScale(%vec,%impulse); + %vec = vectorAdd(%vec,"0 0 "@%up); + %obj.applyimpulse(%obj.getPosition(),%vec); + %count++; + %obj.zombieAttackImpulse = schedule(500, 0, "zombieAttackImpulse", %obj, %count); + return; + } + } + } + %objiszomb = 0; + } + %count++; + %obj.zombieAttackImpulse = schedule(500, 0, "zombieAttackImpulse", %obj, %count); +} + +function AddToZombieGroup(%player) +{ + if(isObject(%player)) + { + %group = nameToID("MissionCleanup/ZombieGroup"); + if(!isObject(%group)) + { + %group = new SimGroup("ZombieGroup"); + MissionCleanup.add(%group); + } + + %group.add(%player); + } +} + +function RemoveFromZombieGroup(%player) +{ + if(isObject(%player)) + { + %group = nameToID("MissionCleanup/ZombieGroup"); + if(!isObject(%group)) + { + %group = new SimGroup("ZombieGroup"); + MissionCleanup.add(%group); + } + + %group.remove(%player); + } +} + +//--------------------------------------------------------------------- +//------------------------DEMON MOTHER STUFF--------------------------- +//--------------------------------------------------------------------- + +datablock SeekerProjectileData(DMMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.5; + damageRadius = 5.0; + radiusDamageType = $DamageType::Zombie; + kickBackStrength = 2000; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MortarSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 10000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 10.0; + maxVelocity = 35.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 23.0; + acceleration = 15.0; + + proximityRadius = 2.5; + + terrainAvoidanceSpeed = 10; + terrainScanAhead = 7; + terrainHeightFail = 1; + terrainAvoidanceRadius = 3; + + flareDistance = 40; + flareAngle = 20; + minSeekHeat = 0.0; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 250; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +datablock LinearFlareProjectileData(DMPlasma) +{ + doDynamicClientHits = true; + + directDamage = 0; + directDamageType = $DamageType::Zombie; + hasDamageRadius = true; + indirectDamage = 0.8; // z0dd - ZOD, 4/25/02. Was 0.5 + damageRadius = 15.0; + kickBackStrength = 1500; + radiusDamageType = $DamageType::Zombie; + explosion = MortarExplosion; + splash = PlasmaSplash; + + dryVelocity = 85.0; // z0dd - ZOD, 4/25/02. Was 50. Velocity of projectile out of water + wetVelocity = -1; + velInheritFactor = 1.0; + fizzleTimeMS = 4000; + lifetimeMS = 2500; // z0dd - ZOD, 4/25/02. Was 6000 + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = true; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = -1; + + activateDelayMS = 100; + + scale = "3.0 3.0 3.0"; + numFlares = 30; + flareColor = "0.1 0.3 1.0"; + flareModTexture = "flaremod"; + flareBaseTexture = "flarebase"; +}; + +function DemonMotherCreate(%pos){ + if(ZombieGroup.getCount() >= $Host::MaxZombies && $Host::MaxZombies != -1) + { + error("Too many zombies. Cannot create demon mother"); + return; + } + + %obj = new player(){ + Datablock = "DemonMotherZombieArmor"; + }; + %obj.setTransform(%pos); + %obj.team = 0; + MissionCleanup.add(%obj); + AddToZombieGroup(%obj); + schedule(1000, 0, "DemonMotherInitiate", %obj); + + // Eolk - Needs to return %obj now + return %obj; +} + +function DemonMotherInitiate(%obj){ + if(!isObject(%obj)) + return; + DemonMotherDefense(%obj); + DemonMotherThink(%obj); + %obj.mountImage(ZdummyslotImg, 4); + %obj.justshot = 0; + %obj.justmelee = 0; +} + +function DemonMotherThink(%obj){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + %pos = %obj.getposition(); + %count = ClientGroup.getCount(); + %closestClient = -1; + %closestDistance = 32767; + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if(isObject(%cl.player)){ + %testPos = %cl.player.getWorldBoxCenter(); + %distance = vectorDist(%pos, %testPos); + if (%distance > 0 && %distance < %closestDistance && %cl.player.isFTD != 1 && %cl.player.iszombie != 1) + { + %closestClient = %cl; + %closestDistance = %distance; + } + } + } + if(%closestClient != -1){ + %searchobject = %closestclient.player; + %dist = vectorDist(%pos,%searchobject.getPosition()); + if(%dist <= 100){ + if(%dist <= 50){ //ok were now in combat mode, lets decide on what we should do, move attack, or shoot. + if(%obj.justmelee == 1){ //if we just used a melee attack, maybe we should follow it up with a shot attack. + if(%dist >= 10){ //good were far enough away, lets shoot em. + %rand = getrandom(1,3); + if(%rand <= 2) + DemonMotherSpermAttack(%obj,%searchobject); + else + DemonMotherFireRainAttack(%obj,%searchobject); + } + else //damn, to close, ok lung at him + DemonMotherLungAttack(%obj,%searchobject); + } + else{ + %rand = getRandom(1,5); //ok so theres 3 good possible attacks here, so lets get a random variable and decide what to do. + if(%rand == 1) + DemonMotherPlasmaAttack(%obj,%searchobject); + else if(%rand <= 3) + DemonMotherStrafeAttack(%obj,%searchobject); + else + DemonMotherFlyAttack(%obj,%searchobject); + } + } + else{ //ok, were to far away, maybe we should shoot at them. + if(%obj.justshot == 1) //humm we just attacked, ok, let charge him, get in close + DemonMotherChargeIn(%obj,%searchobject); + else{ //were good to fire, FIRE AWAY! + %rand = getRandom(1,5); //ok so theres 3 good possible attacks here, so lets get a random variable and decide what to do. + if(%rand == 1) + DemonMotherFireRainAttack(%obj,%searchobject); + else if(%rand <= 3) + DemonMotherMissileAttack(%obj,%searchobject); + else + DemonMotherSpermAttack(%obj,%searchobject); + } + } + } + else if(%dist > 200){ + %rand = getrandom(1,120); + if(%rand == 94) //please, dont ask why i choose this number, it just poped in my head + DemonMotherDemonSpawn(%obj); + else + DemonMotherMoveToTarget(%obj,%searchobject); + } + else + DemonMotherMoveToTarget(%obj,%searchobject); + + %obj.justshot = 0; + %obj.justmelee = 0; + } + else{ + schedule(500, 0, "DemonMotherThink", %obj); + } +} + +function DemonMotherDefense(%obj){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + %pos = %obj.getposition(); + InitContainerRadiusSearch(%pos, 250, $TypeMasks::ProjectileObjectType); + while ((%searchObject = containerSearchNext()) != 0){ + %projpos = %searchobject.getPosition(); + %dist = vectorDist(%pos,%projpos); + if(%dist <= 100){ + if(%searchobject.lastpos) + %searchobject.delete(); + } + else + %searchobject.lastpos = %projpos; + } + schedule(50, "DemonMotherDefense", %obj); +} + +function DemonMotherSpermAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(!isObject(%target)){ + DemonMotherThink(%obj); + return; + } + FaceTarget(%obj,%target); + if(%obj.chargecount $= "") + %obj.chargecount = 0; + %charge = new ParticleEmissionDummy() + { + position = %obj.getMuzzlePoint(4); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(100, "delete"); + + if(%obj.chargecount == 7){ + %vec = vectorsub(%target.getworldboxcenter(),%obj.getMuzzlePoint(4)); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(),vectorlen(%vec)/100)); + %p = new TracerProjectile() + { + dataBlock = LZombieAcidBall; + initialDirection = %vec; + initialPosition = %obj.getMuzzlePoint(4); + sourceObject = %obj; + sourceSlot = 6; + }; + } + if(%obj.chargecount == 9){ + %vec = vectorsub(%target.getworldboxcenter(),%obj.getMuzzlePoint(4)); + %vec = vectoradd(%vec, vectorscale(%target.getvelocity(),vectorlen(%vec)/100)); + %p = new TracerProjectile() + { + dataBlock = LZombieAcidBall; + initialDirection = %vec; + initialPosition = %obj.getMuzzlePoint(4); + sourceObject = %obj; + sourceSlot = 6; + }; + } + + if(%obj.chargecount <= 9){ + schedule(100, 0, "DemonMotherSpermAttack", %obj, %target); + %obj.chargecount++; + } + else{ + %obj.chargecount = 0; + %obj.justshot = 1; + DemonMotherThink(%obj); + } +} + +function DemonMotherLungAttack(%obj,%target){ + FaceTarget(%obj,%target); + %vector = vectorNormalize(vectorSub(%target.getPosition(), %obj.getPosition())); + %vector = vectorscale(%vector, 4000); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + %vector = %x@" "@%y@" 400"; + %obj.applyImpulse(%obj.getPosition(), %vector); + + %obj.justmelee = 1; + schedule(750, 0, "DemonMotherThink", %obj); +} + +function DemonMotherStrafeAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount <= 8){ + %obj.setVelocity("0 0 0"); + FaceTarget(%obj,%target); + %vector = vectorNormalize(vectorSub(%target.getPosition(), %obj.getPosition())); + %vector = vectorscale(%vector, 3250); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %nv1 = %y; + %nv2 = (%x * -1); + %vector = %nv1@" "@%nv2@" 0"; + %obj.applyImpulse(%obj.getPosition(), %vector); + } + else if(%obj.chargecount <= 11){ + %obj.setvelocity("0 0 0"); + FaceTarget(%obj,%target); + %vector = vectorNormalize(vectorSub(%target.getPosition(), %obj.getPosition())); + %vector = vectorscale(%vector, 4500); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + %vector = %x@" "@%y@" 150"; + %obj.applyImpulse(%obj.getPosition(), %vector); + } + else{ + %obj.chargecount = 0; + %obj.justmelee = 1; + schedule(250, 0, "DemonMotherThink", %obj); + return; + } + schedule(250, 0, "DemonMotherStrafeAttack", %obj, %target); + %obj.chargecount++; +} + +function DemonMotherFlyAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount <= 9){ + FaceTarget(%obj,%target); + %obj.setvelocity("0 0 10"); + %obj.chargecount++; + schedule(100, 0, "DemonMotherFlyAttack",%obj,%target); + } + else if(%obj.chargecount == 10){ + FaceTarget(%obj,%target); + %obj.setvelocity("0 0 5"); + %vector = vectorSub(%target.getPosition(),%obj.getPosition()); + %nVec = vectorNormalize(%vector); + %vector = vectorAdd(%vector,vectorscale(%nvec,-4)); + %obj.attackpos = vectorAdd(%obj.getPosition(),%vector); + %obj.attackdir = %nVec; +// echo(%obj.getPosition() SPC %target.getPosition() SPC %obj.attackpos SPC %obj.attackdir); + %obj.startFade(400, 0, true); + %obj.chargecount++; + schedule(400, 0, "DemonMotherFlyAttack",%obj,%target); + } + else if(%obj.chargecount >= 11){ + %obj.startFade(500, 0, false); + %obj.setPosition(%obj.attackpos); + %obj.setvelocity(vectorscale(%obj.attackdir,25)); + %obj.justmelee = 1; + %obj.chargecount = 0; +// echo(%obj.getPosition() SPC %target.getPosition() SPC %obj.attackpos SPC %obj.attackdir); + %obj.attackpos = ""; + %obj.attackdir = ""; + schedule(1000, 0, "DemonMotherThink",%obj); + } +} + +function DemonMotherFireRainAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount == 0){ + FaceTarget(%obj, %target); + for(%i = 0; %i < 10; %i++){ + %pos = %obj.getPosition(); + %x = getRandom(0,10) - 5; + %y = getRandom(0,10) - 5; + %vec = vectorAdd(%pos,%x SPC %y SPC "5"); + %searchResult = containerRayCast(%vec, vectorAdd(%vec,"0 0 -10"), $TypeMasks::TerrainObjectType, %obj); + + %charge = new ParticleEmissionDummy() + { + position = posFromRaycast(%searchresult); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(1500, "delete"); + } + %obj.chargecount++; + schedule(1000, 0, "DemonMotherFireRainAttack",%obj,%target); + } + else{ + %x = (getRandom() * 2) - 1; + %y = (getRandom() * 2) - 1; + %z = getRandom(); + %vec = vectorNormalize(%x SPC %y SPC %z); + %pos = vectorAdd(%target.getPosition(),vectorScale(%vec, 20)); + for(%i = 0;%i < 10;%i++){ + %x = getRandom(0,14) - 7; + %y = getRandom(0,14) - 7; + %spwpos = vectorAdd(%pos,%x SPC %y SPC "2"); + %p = new GrenadeProjectile() + { + dataBlock = DemonFireball; + initialDirection = vectorScale(%vec,-1); + initialPosition = %spwpos; + sourceObject = %obj; + sourceSlot = 4; + }; + } + %obj.justshot = 1; + %obj.chargecount = 0; + schedule(1000, 0, "DemonMotherThink",%obj); + } +} + +function DemonMotherMissileAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount == 0){ + %obj.chargecount++; + schedule(1000, 0, "DemonMotherMissileAttack", %obj, %target); + } + else{ + %vec = vectorNormalize(vectorSub(%target.getPosition(),%obj.getPosition())); + %p = new SeekerProjectile() + { + dataBlock = DMMissile; + initialDirection = %vec; + initialPosition = %obj.getMuzzlePoint(4); + sourceObject = %obj; + sourceSlot = 4; + }; + %beacon = new BeaconObject() { + dataBlock = "SubBeacon"; + beaconType = "vehicle"; + position = %target.getWorldBoxCenter(); + }; + %beacon.team = 0; + %beacon.setTarget(0); + MissionCleanup.add(%beacon); + %p.setObjectTarget(%beacon); + DemonMotherMissileFollow(%target,%beacon,%p); + + %obj.justshot = 1; + %obj.chargecount = 0; + schedule(1000, 0, "DemonMotherThink", %obj); + } +} + +function DemonMotherMissileFollow(%target,%beacon,%missile){ + if(!isObject(%target)){ + %beacon.delete(); + return; + } + if(!isObject(%missile)){ + %beacon.delete(); + return; + } + %beacon.setPosition(%target.getWorldBoxCenter()); + schedule(100, 0, "DemonMotherMissileFollow", %target, %beacon, %missile); +} + +function DemonMotherPlasmaAttack(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount <= 9){ + %obj.setVelocity("0 0 10"); + %obj.chargecount++; + schedule(100, 0, "DemonMotherPlasmaAttack", %obj, %target); + } + else{ + %obj.setVelocity("0 0 3"); + %vec = vectorNormalize(vectorSub(%target.getPosition(),%obj.getPosition())); + %p = new LinearFlareProjectile() + { + dataBlock = DMPlasma; + initialDirection = %vec; + initialPosition = %obj.getMuzzlePoint(4); + sourceObject = %obj; + sourceSlot = 4; + }; + %obj.chargecount = 0; + %obj.justshot = 1; + schedule(1000, 0, "DemonMotherThink", %obj); + } +} + +function DemonMotherChargeIn(%obj,%target){ + if(!isObject(%obj)) + return; + if(%obj.getState() $= "dead") + return; + if(%obj.chargecount $= "") + %obj.chargecount = 0; + if(%obj.chargecount <= 4){ + FaceTarget(%obj, %target); + %vec = vectorNormalize(vectorSub(%target.getPosition(),%obj.getPosition())); + %obj.setvelocity(vectorScale(%vec,50)); + %obj.chargecount++; + schedule(500, 0, "DemonMotherChargeIn", %obj, %target); + } + else{ + %obj.justmelee = 1; + %obj.chargecount = 0; + DemonMotherThink(%obj); + } +} + +function DemonMotherMoveToTarget(%obj,%target){ + FaceTarget(%obj,%target); + %vector = vectorNormalize(vectorSub(%target.getPosition(), %obj.getPosition())); + %vector = vectorscale(%vector, 1200); + %x = Getword(%vector,0); + %y = Getword(%vector,1); + %z = Getword(%vector,2); + %vector = %x@" "@%y@" 150"; + %obj.applyImpulse(%obj.getPosition(), %vector); + + schedule(500, 0, "DemonMotherThink", %obj); +} + +function DemonMotherDemonSpawn(%obj){ + for(%i = 0; %i < 5; %i++){ + %pos = %obj.getPosition(); + %x = getRandom(0,200) - 100; + %y = getRandom(0,200) - 100; + %vec = vectorAdd(%pos,%x SPC %y SPC "40"); + %searchResult = containerRayCast(%vec, vectorAdd(%vec,"0 0 -80"), $TypeMasks::TerrainObjectType, %obj); + + %charge = new ParticleEmissionDummy() + { + position = posFromRaycast(%searchresult); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(1100, "delete"); + schedule(1000, 0, "startAzombie", posFromRaycast(%searchresult), 4); + } + schedule(1500, 0, "DemonMotherThink", %obj); +} + +function FaceTarget(%obj,%target){ + %vector = vectorNormalize(vectorSub(%target.getPosition(), %obj.getPosition())); + %v1 = getword(%vector, 0); + %v2 = getword(%vector, 1); + %nv1 = %v2; + %nv2 = (%v1 * -1); + %vector2 = %nv1@" "@%nv2@" 0"; + %obj.setRotation(fullrot("0 0 0",%vector2)); +} diff --git a/Scripts/Packs/ammopack.cs b/Scripts/Packs/ammopack.cs new file mode 100644 index 0000000..7a94f00 --- /dev/null +++ b/Scripts/Packs/ammopack.cs @@ -0,0 +1,334 @@ +// ------------------------------------------------------------------ +// AMMO PACK +// can be used by any armor type +// uses no energy, does not have to be activated +// increases the max amount of non-energy ammo carried + +// put vars here for ammo max counts with ammo pack + +$AmmoItem[0] = ChaingunAmmo; +$AmmoItem[1] = MortarAmmo; +$AmmoItem[2] = MissileLauncherAmmo; +$AmmoItem[3] = MGclip; +$AmmoItem[4] = SniperGunAmmo; +$AmmoItem[5] = BazookaAmmo; +$AmmoItem[6] = MG42Clip; +$AmmoItem[7] = RepairKit; +$AmmoItem[8] = FlamerAmmo; +$AmmoItem[9] = AALauncherAmmo; +$AmmoItem[10] = RifleClip; +$AmmoItem[11] = ShotgunClip; +$AmmoItem[12] = RShotgunClip; +$AmmoItem[13] = LMissileLauncherAmmo; +$AmmoItem[14] = RPGAmmo; +$AmmoItem[15] = PBCAmmo; +$AmmoItem[16] = NapalmAmmo; +$AmmoItem[17] = RailGunAmmo; + + +$numAmmoItems = 18; + +$grenAmmoType[0] = Grenade; +$grenAmmoType[1] = ConcussionGrenade; +$grenAmmoType[2] = FlashGrenade; +$grenAmmoType[3] = FlareGrenade; +$grenAmmoType[4] = SmokeGrenade; +$grenAmmoType[5] = BeaconSmokeGrenade; + +$numGrenTypes = 6; + +datablock ShapeBaseImageData(AmmoPackImage) +{ + shapeFile = "pack_upgrade_ammo.dts"; + item = AmmoPack; + mountPoint = 1; + offset = "0 0 0"; + mass = 20; +}; + +datablock ItemData(AmmoPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_ammo.dts"; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "AmmoPackImage"; + pickUpName = "an ammo pack"; + + computeCRC = true; + + +// lightType = "PulsingLight"; +// lightColor = "0.2 0.4 0.0 1.0"; +// lightTime = "1200"; +// lightRadius = "1.0"; + + max[ChaingunAmmo] = 1500; + max[MortarAmmo] = 4; + max[MissileLauncherAmmo] = 4; + max[Mgclip] = 3; + max[SniperGunAmmo] = 10; + max[BazookaAmmo] = 2; + max[MG42Clip] = 2; + max[FlamerAmmo] = 0; + max[Grenade] = 2; + max[Mine] = 1; + max[AALauncherAmmo] = 2; + max[RepairKit] = 1; + max[RifleClip] = 2; + max[ShotgunClip] = 2; + max[RShotgunClip] = 1; + max[LMissileLauncherAmmo] = 1; + max[RPGAmmo] = 2; + max[PBCAmmo] = 2; + max[NapalmAmmo] = 5; + max[RailGunAmmo] = 2; +}; + +function AmmoPack::onPickup(%this,%pack,%player,%amount) +{ + // %this = AmmoPack datablock + // %pack = AmmoPack object number + // %player = player + // %amount = 1 + + for (%idx = 0; %idx < $numAmmoItems; %idx++) + { + %ammo = $AmmoItem[%idx]; + if (%pack.inv[%ammo] > 0) + { + %amount = %pack.getInventory(%ammo); + %player.incInventory(%ammo,%amount); + %pack.setInventory(%ammo,0); + } + else if(%pack.inv[%ammo] == -1) + { + // this particular type of ammo has already been exhausted for this pack; + // don't give the player any + } + else + { + // Assume it's full if no inventory has been assigned + %player.incInventory(%ammo,%this.max[%ammo]); + } + } + // now check what type grenades (if any) player is carrying + %gFound = false; + for (%i = 0; %i < $numGrenTypes; %i++) + { + %gType = $grenAmmoType[%i]; + if(%player.inv[%gType] > 0) + { + %gFound = true; + %playerGrenType = %gType; + break; + } + } + // if player doesn't have any grenades at all, give 'em whatever the ammo pack has + if(!%gFound) + { + %given = false; + for(%j = 0; %j < $numGrenTypes; %j++) + { + %packGren = $grenAmmoType[%j]; + if(%pack.inv[%packGren] > 0) + { + // pack has some of these grenades + %player.incInventory(%packGren, %pack.getInventory(%packGren)); + %pack.setInventory(%packGren, 0); + %given = true; + break; + } + else if(%pack.inv[%packGren] == -1) + { + // the pack doesn't have any of this type of grenades + } + else + { + // pack has full complement of this grenade type + %player.incInventory(%packGren, %this.max[%packGren]); + %given = true; + } + if(%given) + break; + } + } + else + { + // player had some amount of grenades before picking up pack + if(%pack.inv[%playerGrenType] > 0) + { + // pack has 1 or more of this type of grenade + %player.incInventory(%playerGrenType, %pack.getInventory(%playerGrenType)); + %pack.setInventory(%playerGrenType, 0); + } + else if(%pack.inv[%playerGrenType] == -1) + { + // sorry Chester, the pack is out of these grenades. + } + else + { + // pack is uninitialized for this grenade type -- meaning it has full count + %player.incInventory(%playerGrenType, %this.max[%playerGrenType]); + } + } + // now see if player had mines selected and if they're allowed in this mission type + %mineFav = %player.client.favorites[getField(%player.client.mineIndex, 0)]; + if ( ( $InvBanList[$CurrentMissionType, "Mine"] !$= "1" ) + && !( ( %mineFav $= "EMPTY" ) || ( %mineFav $= "INVALID" ) ) ) + { + // player has selected mines, and they are legal in this mission type + if(%pack.inv[Mine] > 0) + { + // and the pack has some mines in it! bonus! + %player.incInventory(Mine, %pack.getInventory(Mine)); + %pack.setInventory(Mine, 0); + } + else if(%pack.inv[Mine] == -1) + { + // no mines left in the pack. do nothing. + } + else + { + // assume it's full of mines if no inventory has been assigned + %player.incInventory(Mine,%this.max[Mine]); + } + } +} + +function AmmoPack::onThrow(%this,%pack,%player) +{ + // %this = AmmoPack datablock + // %pack = AmmoPack object number + // %player = player + + %player.throwAmmoPack = 1; + dropAmmoPack(%pack, %player); + // do the normal ItemData::onThrow stuff -- sound and schedule deletion + serverPlay3D(ItemThrowSound, %player.getTransform()); + %pack.schedulePop(); +} + +function AmmoPack::onInventory(%this,%player,%value) +{ + // %this = AmmoPack + // %player = player + // %value = 1 if gaining a pack, 0 if losing a pack + + // the below test is necessary because this function is called everytime the ammo + // pack gains or loses an item + if(%player.getClassName() $= "Player") + { + if(!%value) + if(%player.throwAmmoPack == 1) + { + %player.throwAmmoPack = 0; + // ammo stuff already handled by AmmoPack::onThrow + } + else + { + // ammo pack was sold at inventory station -- no need to worry about + // setting the ammo in the pack object (it doesn't exist any more) + dropAmmoPack(-1, %player); + } + } + Pack::onInventory(%this,%player,%value); +} + +function dropAmmoPack(%packObj, %player) +{ + // %packObj = Ammo Pack object number if pack is being thrown, -1 if sold at inv station + // %player = player object + + for(%i = 0; %i < $numAmmoItems; %i++) + { + %ammo = $AmmoItem[%i]; + // %pAmmo == how much of this ammo type the player has + %pAmmo = %player.getInventory(%ammo); + // %pMax == how much of this ammo type the player's datablock says can be carried + %pMax = %player.getDatablock().max[%ammo]; + if(%pAmmo > %pMax) + { + // if player has more of this ammo type than datablock's max... + if(%packObj > 0) + { + // put ammo that player can't carry anymore in pack + %packObj.setInventory(%ammo, %pAmmo - %pMax); + } + // set player to max for this ammo type + %player.setInventory(%ammo, %pMax); + } + else + { + if(%packObj > 0) + { + // the pack gets -1 of this ammo type -- else it'll assume it's full + // can't use setInventory() because it won't allow values less than 1 + %packObj.inv[%ammo] = -1; + } + } + } + // and, of course, a pass for the grenades. Whee. + %gotGren = false; + // check to see what kind of grenades the player has. + for(%j = 0; %j < $numGrenTypes; %j++) + { + %gType = $grenAmmoType[%j]; + if(%player.inv[%gType] > 0) + { + %gotGren = true; + %playerGren = %gType; + break; + } + else + { + // ammo pack only carries type of grenades that player who bought it (or picked + // it up) had at the time -- all else are zeroed out (value of -1) + if(%packObj > 0) + %packObj.inv[%gType] = -1; + } + } + if(%gotGren) + { + %pAmmo = %player.getInventory(%playerGren); + %pMax = %player.getDatablock().max[%playerGren]; + if(%pAmmo > %pMax) + { + if(%packObj > 0) + %packObj.setInventory(%playerGren, %pAmmo - %pMax); + %player.setInventory(%playerGren, %pMax); + } + else + if(%packObj > 0) + %packObj.inv[%playerGren] = -1; + } + else + { + // player doesn't have any grenades at all. nothing needs to be done here. + } + // crap. forgot the mines. + if(%player.getInventory(Mine) > %player.getDatablock().max[Mine]) + { + // if player has more mines than datablock's max... + if(%packObj > 0) + { + // put mines that player can't carry anymore in pack + %packObj.setInventory(Mine, %player.getInventory(Mine) - %player.getDatablock().max[Mine]); + } + // set player to max mines + %player.setInventory(Mine, %player.getDatablock().max[Mine]); + } + else + { + if(%packObj > 0) + { + // the pack gets -1 for mines -- else it'll assume it's full + // can't use setInventory() because it won't allow values less than 1 + %packObj.inv[Mine] = -1; + } + } +} diff --git a/Scripts/Packs/artillerybarrelPack.cs b/Scripts/Packs/artillerybarrelPack.cs new file mode 100644 index 0000000..bed4ef2 --- /dev/null +++ b/Scripts/Packs/artillerybarrelPack.cs @@ -0,0 +1,59 @@ +//-------------------------------------------------------------------------- +// Chaingun barrel pack +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(artilleryBarrelPackImage) +{ +mass = 15; + +shapeFile = "pack_barrel_fusion.dts"; +item = artilleryBarrelPack; +mountPoint = 1; +offset = "0 0 0"; +turretBarrel = "artilleryBarrelLarge"; + +stateName[0] = "Idle"; +stateTransitionOnTriggerDown[0] = "Activate"; + +stateName[1] = "Activate"; +stateScript[1] = "onActivate"; +stateTransitionOnTriggerUp[1] = "Deactivate"; + +stateName[2] = "Deactivate"; +stateScript[2] = "onDeactivate"; +stateTransitionOnTimeOut[2] = "Idle"; + +isLarge = true; +}; + +datablock ItemData(artilleryBarrelPack) +{ +className = Pack; +catagory = "Packs"; +shapeFile = "pack_barrel_fusion.dts"; +mass = 1; +elasticity = 0.2; +friction = 0.6; +pickupRadius = 2; +rotate = true; +image = "artilleryBarrelPackImage"; +pickUpName = "a artillery barrel pack"; + +computeCRC = true; + +}; + +function artilleryBarrelPackImage::onActivate(%data, %obj, %slot) +{ +checkTurretMount(%data, %obj, %slot); // This cheks if there is a turret where the player is aiming. +} + +function artilleryBarrelPackImage::onDeactivate(%data, %obj, %slot) +{ +%obj.setImageTrigger($BackpackSlot, false); +} + +function artilleryBarrelPack::onPickup(%this, %obj, %shape, %amount) +{ +// created to prevent console errors +} \ No newline at end of file diff --git a/Scripts/Packs/blastfloor.cs b/Scripts/Packs/blastfloor.cs new file mode 100644 index 0000000..004ffb5 --- /dev/null +++ b/Scripts/Packs/blastfloor.cs @@ -0,0 +1,240 @@ +//--------------------------------------------------------- +// Deployable floor +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedFloor) : StaticShapeDamageProfile { + className = "floor"; + shapeFile = "smiscf.dts"; + + maxDamage = 4; + destroyedLevel = 4; + disabledLevel = 3.5; + + isShielded = true; + energyPerDamagePoint = 30; + maxEnergy = 200; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Medium Blast floor'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(FloorDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = FloorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedFloor; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; // also see FloorDeployableImage::testSlopeTooGreat() + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(FloorDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "FloorDeployableImage"; + pickUpName = "a medium floor pack"; + heatSignature = 0; + emap = true; +}; + +function FloorDeployableImage::testSlopeTooGreat(%item) { + if (%item.surface) { + // Do we link with another Medium Floor? + if (%item.surface.getClassName() $= "StaticShape") { + if (%item.surface.getDataBlock().className $= "floor") { + %floor = 1; + } + } + if (%floor) { + if ($Host::Purebuild == true && $Host::Hazard::Enabled != true) + return getTerrainAngle(%item.surfaceNrm) > %item.maxDepSlope; + else + return getTerrainAngle(%item.surfaceNrm) > 1; + } + else + return getTerrainAngle(%item.surfaceNrm) > %item.maxDepSlope; + } +} + +function FloorDeployableImage::testObjectTooClose(%item) { + if ($Host::Purebuild == true && $Host::Hazard::Enabled != true) + return ""; + else { + %terrain = %item.surface.getClassName() $= "TerrainBlock"; + %interior = %item.surface.getClassName() $= "InteriorInstance"; + if (%item.surface.getClassName() $= "StaticShape") { + if (%item.surface.getDataBlock().className $= "floor") { + %floor = 1; + } + } + return !(%terrain || %interior || %floor); + } +} + +function FloorDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function FloorDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function FloorDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + %rot = intRot("1 0 0 0",%playerVector); + + %scale = getWords($packSetting["floor",%plyr.packSet],0,2); + + if ($Host::ExpertMode == 1) + %scale = getWords(%scale,0,1) SPC firstWord($expertSetting["floor",%plyr.expertSet]); + + %surfaceAdjust = 1; + // Do we link with another Medium Floor? + if (%item.surface.getClassName() $= "StaticShape") { + if (%item.surface.getDataBlock().className $= "floor") { + %surfaceAdjust = 0; + // [[Most]] Get the relative x y location from the "pure" information. + %link = sidelink(%item.surface,%item.surfaceNrm,%item.surfacePt,getWords($packSetting["floor",%plyr.packSet],3,5),"0 0 -0.5"); + %location = getWords(%link,0,2); + %side = getWords(%link,3,5); + %dirside = getWords(%link,6,8); + // [[Most]] Set the rotation to match the surfaceNrm and match the side. + // The power of the full rotation system.. ;) + %rot = fullRot(%item.surfaceNrm,vectorScale(%dirside,-1)); + // [[Most]] Adjust the location to match the scale. + %adjust = vectorScale(%item.surfaceNrm,GetWord(%scale,2)); + %item.surfacePt = vectorSub(%location, %adjust); + } + } + + // If we do not link with another Medium Floor + if(%surfaceAdjust) { + %item.surfacePt = vectorSub(%item.surfacePt,"0 0" SPC getWord(%scale,2) - 1); // Rise 1 meter above surface + } + + %scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + scale = %scale; + }; + +//////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function DeployedFloor::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, FloorDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function FloorDeployableImage::onMount(%data, %obj, %node) { + %obj.hasFloor = true; // set for floorcheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function FloorDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasFloor = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} diff --git a/Scripts/Packs/blastwall.cs b/Scripts/Packs/blastwall.cs new file mode 100644 index 0000000..dce060f --- /dev/null +++ b/Scripts/Packs/blastwall.cs @@ -0,0 +1,271 @@ +//--------------------------------------------------------- +// Deployable wall, Code by Parousia +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedWall) : StaticShapeDamageProfile { + className = "wall"; + shapeFile = "Bmiscf.dts"; // dmiscf.dts, alternate + + maxDamage = 2; + destroyedLevel = 2; + disabledLevel = 1.5; + + isShielded = true; + energyPerDamagePoint = 60; + maxEnergy = 100; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Light Blast Wall'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(WallDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = WallDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedWall; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0; + maxDeployDis = 50.0; +}; + +datablock ItemData(WallDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "WallDeployableImage"; + pickUpName = "a light blast wall pack"; + heatSignature = 0; + emap = true; +}; + +function WallDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function WallDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function WallDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function WallDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %scale = "0.5 8 40"; // Search range for total blast wall size. + + %mCenter = "0 0 -0.5"; + %pad = pad(%item.surfacePt SPC %item.surfaceNrm SPC %item.surfaceNrm2,%scale,%mCenter); + %scale = getWords(%pad,0,2); + + %item.surfacePt = getWords(%pad,3,5); + %rot = getWords(%pad,6,9); + %offset = %plyr.packSet - 1; + if (%plyr.packSet == 3) { + %double = 1; + %offset = 1; + } + + %scale = vectorAdd(%scale,vectorScale("1.01 1.01" SPC %double,mAbs(%offset))); + + %pup = mCeil(getWord(%scale,0)/4); //4 = Avarage blast wall height. + %pside = mCeil(getWord(%scale,1)/4); //4 = Avarage blast wall width. + %upiece = getWord(%scale,0)/%pup; + %spiece = getWord(%scale,1)/%pside; + + %scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + %dir = VectorNormalize(vectorSub(%item.surfacePt,%plyr.getposition())); + %adjust = vectorNormalize(vectorProject(%dir,vectorCross(%item.surfaceNrm,%item.surfaceNrm2))); +// %adjust = vectorNormalize(vectorCross(%item.surfaceNrm,%item.surfaceNrm2)); + + if (%plyr.packSet == 3) + %offset = Lev(vectorCouple(%dir,vectorCross(%item.surfaceNrm,%item.surfaceNrm2))); + + %adjust = vectorScale(%adjust,-0.5 * %offset); + + if ($Host::ExpertMode == 1 && %plyr.expertSet == 1) { + for (%x = 0;%x < %pup;%x++){ + for (%y = 0;%y < %pside;%y++){ + %scale = %upiece SPC %spiece SPC 0.5 + %double; + %scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + %deplObj = new (%className)() { + dataBlock = %item.deployed; + scale = %scale; + }; + %up = vectorScale(%item.surfaceNrm,%upiece * (%x - (%pup / 2) + 0.5)); + %side = vectorScale(%item.surfaceNrm2,%spiece * (%y - (%pside / 2) + 0.5)); + %tadjust = vectorAdd(%up,vectorAdd(%side,%adjust)); + %deplObj.setTransform(vectorAdd(%item.surfacePt,%tadjust) SPC %rot); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.Needsfit = 1; + addDSurface(%item.surface,%deplObj); + + // [Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if(%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + // let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + } + } + } + else { + %deplObj = new (%className)() { + dataBlock = %item.deployed; + scale = %scale; + }; + + %deplObj.setTransform(vectorAdd(%item.surfacePt,%adjust) SPC %rot); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.Needsfit = 1; + addDSurface(%item.surface,%deplObj); + + // [Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if(%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + } + + // Power object + checkPowerObject(%deplObj); + + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"pad"); + + return %deplObj; +} + +function DeployedWall::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, WallDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function WallDeployableImage::onMount(%data, %obj, %node) { + %obj.hasBlast = true; // set for blastcheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function WallDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasBlast = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} + +function doorfunction(%player,%door) +{ +%vec = VectorSub(%door,%player); +%dir = VectorNormalize(%vec); +%nrm = topVec(VirVec(%door,%dir)); +} diff --git a/Scripts/Packs/blastwwall.cs b/Scripts/Packs/blastwwall.cs new file mode 100644 index 0000000..8e8da17 --- /dev/null +++ b/Scripts/Packs/blastwwall.cs @@ -0,0 +1,225 @@ +//--------------------------------------------------------- +// Deployable wall, Code by Parousia +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedwWall) : StaticShapeDamageProfile { + className = "wwall"; + shapeFile = "smiscf.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Light Walk Way'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(wWallDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = wwallDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedwWall; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0; + maxDeployDis = 50.0; +}; + +datablock ItemData(wwallDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "wWallDeployableImage"; + pickUpName = "a light walkway pack"; + heatSignature = 0; + emap = true; +}; + +function wWallDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function wWallDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function wwallDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function wWallDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + %rot = intRot("1 0 0 0",%playerVector); + + %scale ="5 5 0.5"; + + if ($Host::ExpertMode == 1) { + if (%plyr.expertSet == 2) + %scale = getWord(%scale,0) * 2 SPC getWords(%scale,1,2); + if (%plyr.expertSet == 3) + %scale = getWord(%scale,0) SPC getWord(%scale,1) * 2 SPC getWord(%scale,2); + } + + %surfaceAdjust = 1; + // Do we link with another shape? + if (%item.surface.getClassName() $= "StaticShape") { + %surfaceAdjust = 0; + // [[Most]] Get the relative x y location from the "pure" informtation. + %link = sidelink(%item.surface,%item.surfaceNrm,%item.surfacePt,%scale,"0 0 -0.5"); + %location = getWords(%link,0,2); + %side = getWords(%link,3,5); + %dirside = getWords(%link,6,8); + // [[Most]] Set the rotation to match the surfaceNrm and match the side. + // The power of the full rotation system.. ;) + %rot = fullRot(%item.surfaceNrm,%dirside); + // [[Most]] Adjust the location to match the scale. + %adjust = vectorScale(%item.surfaceNrm,getWord(%scale,2)); + %item.surfacePt = vectorSub(%location, %adjust); + } + + // If we do not link with another shape + if(%surfaceAdjust) { + %item.surfacePt = vectorSub(%item.surfacePt,"0 0" SPC getWord(%scale,2) - 1); // Rise 1 meter above surface + } + + %scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + scale = %scale; + }; + + %deplObj.setTransform(%item.surfacePt SPC %rot); + + %axis = vectorCross(virvec(%item.surface,%item.surfaceNrm),virvec(%item.surface,%side)); + %axis = vectorScale(%axis,-1); + %vec1 = vectorScale(virvec(%item.surface,%side),0.5); + %vec2 = vectorAdd(vectorScale(virvec(%item.surface,%item.surfaceNrm),-0.5),"0 0 -0.5"); + + deployEffect(%deplObj,%location,%side,"walk"); + %angle = getWord($packSetting["walk",%plyr.packSet],0)/180*$Pi; + %deplObj.setTransform(remoterotate(%deplObj,%axis SPC %angle,%item.surface,vectorAdd(%vec2,%vec1))); + + if ($Host::ExpertMode == 1 && %plyr.expertSet == 1) { + %v = getRandom()*0.02 SPC getRandom()*0.02 SPC getRandom()*0.02; + %deplObj.setTransform(vectorAdd(pos(%deplObj),%v) SPC rot(%deplObj)); + } + +//////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function DeployedwWall::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, wwallDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function wWallDeployableImage::onMount(%data, %obj, %node) { + %obj.hasWalk = true; // set for wwallcheck + %obj.packSet = 0; + displayPowerFreq(%obj); +} + +function wWallDeployableImage::onUnmount(%data, %obj, %node) { + %obj.packSet = 0; + %obj.hasWalk = ""; +} diff --git a/Scripts/Packs/cratepack.cs b/Scripts/Packs/cratepack.cs new file mode 100644 index 0000000..60c515a --- /dev/null +++ b/Scripts/Packs/cratepack.cs @@ -0,0 +1,272 @@ +//--------------------------------------------------------- +// Deployable Crates +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedCrate) : StaticShapeDamageProfile { + className = "crate"; + shapeFile = "stackable3s.dts"; + + maxDamage = 4.5; + destroyedLevel = 4.5; + disabledLevel = 4.0; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.3; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Crate'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock StaticShapeData(DeployedCrate0) : DeployedCrate { + shapeFile = "stackable1s.dts"; +}; + +datablock StaticShapeData(DeployedCrate1) : DeployedCrate { + shapeFile = "stackable1m.dts"; +}; + +datablock StaticShapeData(DeployedCrate2) : DeployedCrate { + shapeFile = "stackable1l.dts"; +}; + +datablock StaticShapeData(DeployedCrate3) : DeployedCrate { + shapeFile = "stackable2s.dts"; +}; + +datablock StaticShapeData(DeployedCrate4) : DeployedCrate { + shapeFile = "stackable2m.dts"; +}; + +datablock StaticShapeData(DeployedCrate5) : DeployedCrate { + shapeFile = "stackable2l.dts"; +}; + +datablock StaticShapeData(DeployedCrate6) : DeployedCrate { + shapeFile = "stackable3s.dts"; +}; + +datablock StaticShapeData(DeployedCrate7) : DeployedCrate { + shapeFile = "stackable3m.dts"; +}; + +datablock StaticShapeData(DeployedCrate8) : DeployedCrate { + shapeFile = "stackable3l.dts"; +}; + +datablock StaticShapeData(DeployedCrate9) : DeployedCrate { + shapeFile = "stackable4m.dts"; +}; + +datablock StaticShapeData(DeployedCrate10) : DeployedCrate { + shapeFile = "stackable4l.dts"; +}; + +datablock StaticShapeData(DeployedCrate11) : DeployedCrate { + shapeFile = "stackable5m.dts"; +}; + +datablock StaticShapeData(DeployedCrate12) : DeployedCrate { + shapeFile = "stackable5l.dts"; +}; + +datablock ShapeBaseImageData(CrateDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = CrateDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedCrate; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(CrateDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "CrateDeployableImage"; + pickUpName = "a crate pack"; + heatSignature = 0; + emap = true; + }; + +function CrateDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function CrateDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function CrateDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function CrateDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed @ %plyr.packSet; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function DeployedCrate::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, CrateDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function CrateDeployableImage::onMount(%data, %obj, %node) { + %obj.hasCrate = true; // set for cratecheck + %obj.packSet = 0; +} + +function CrateDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasCrate = ""; + %obj.packSet = 0; +} + +function DeployedCrate0::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate1::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, CrateDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + %pos = %obj.getposition(); + %rot = %obj.getrotation(); + %team = %obj.team; + schedule(501, 0, "MakeFCrate", %pos, %rot, %team); +} + +function DeployedCrate2::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate3::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate4::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate5::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate6::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate7::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate8::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate9::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate10::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate11::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function DeployedCrate12::onDestroyed(%this,%obj,%prevState) { + DeployedCrate::onDestroyed(%this,%obj,%prevState); +} + +function MakeFCrate(%pos, %rot, %team){ + %pos = vectorAdd(%pos, "0 0 0.1"); + %Fcrate = new WheeledVehicle() + { + dataBlock = VehicleTestCrate1; + position = %pos; + rotation = %rot; + team = %team; + }; + MissionCleanUp.add(%Fcrate); + %Fcrate.setTransform(%pos @ " " @ %rot); + %Fcrate.schedule(60000, delete); +} + diff --git a/Scripts/Packs/deconExamples.cs b/Scripts/Packs/deconExamples.cs new file mode 100644 index 0000000..92f1046 --- /dev/null +++ b/Scripts/Packs/deconExamples.cs @@ -0,0 +1,51 @@ +//------------------------------------------------ +// Examples of item specific disassemble. +//================================================ +// ---------------- +// 1. ONE +// ================ +function TelePadDeployedBase::disassemble(%data, %plyr, %hTgt) +{ + %teleteam = %hTgt.team; + if(%obj.Hacked) // is it hacked currently? + { + %teleteam = %hTgt.OldTeam; + echo("hacked!"); + } + + // dising a telepad makes it yours, remove from the other teams list + if($TeamDeployedCount[%teleteam, TelePadPack] > 0) // this wasnt the last + { + if($firstPad[%teleteam] == %hTgt) // the first was disassembled + { + echo("first pad disassembled"); + $firstPad[%teleteam] = %ohTgtbj.nextPad; // make the second one the first + %hTgt.prevPad = -1; + %hTgt.nextPad = %obj; + } + else + { + %lastPad = $firstPad[%teleteam]; + %hTgt.prevPad.nextPad = %hTgt.nextPad; + %hTgt.nextPad.prevPad = %hTgt.prevPad; + } + + } + else // last one + { + $firstPad[%teleteam] = ""; // remove it + } + + %hTgt.shield.delete(); +} + +// -------------- +// 2. Two +// ============== + +function MobileBaseVehicle::disassemble(%data, %plyr, %hTgt) +{ + %mpbpos = %hTgt.gettransform(); + %hTgt.delete(); + teleporteffect(posfromtransform(%mpbpos)); +} diff --git a/Scripts/Packs/decorationpack.cs b/Scripts/Packs/decorationpack.cs new file mode 100644 index 0000000..e3d128b --- /dev/null +++ b/Scripts/Packs/decorationpack.cs @@ -0,0 +1,380 @@ +//--------------------------------------------------------- +// Deployable Decoration +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedDecoration) : StaticShapeDamageProfile { + className = "decoration"; + shapeFile = "banner_unity.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Decoration'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock StaticShapeData(DeployedDecoration0) : DeployedDecoration { + shapeFile = "banner_unity.dts"; +}; + +datablock StaticShapeData(DeployedDecoration1) : DeployedDecoration { + shapeFile = "banner_strength.dts"; +}; + +datablock StaticShapeData(DeployedDecoration2) : DeployedDecoration { + shapeFile = "banner_honor.dts"; +}; + +datablock StaticShapeData(DeployedDecoration3) : DeployedDecoration { + shapeFile = "light_male_dead.dts"; +}; + +datablock StaticShapeData(DeployedDecoration4) : DeployedDecoration { + shapeFile = "medium_male_dead.dts"; +}; + +datablock StaticShapeData(DeployedDecoration5) : DeployedDecoration { + shapeFile = "heavy_male_dead.dts"; +}; + +datablock StaticShapeData(DeployedDecoration6) : DeployedDecoration { + shapeFile = "statue_base.dts"; +}; + +datablock StaticShapeData(DeployedDecoration7) : DeployedDecoration { + shapeFile = "statue_lmale.dts"; +}; + +datablock StaticShapeData(DeployedDecoration8) : DeployedDecoration { + shapeFile = "statue_lfemale.dts"; +}; + +datablock StaticShapeData(DeployedDecoration9) : DeployedDecoration { + shapeFile = "statue_hmale.dts"; +}; + +datablock StaticShapeData(DeployedDecoration10) : DeployedDecoration { + shapeFile = "vehicle_grav_tank_wreck.dts"; +}; + +datablock StaticShapeData(DeployedDecoration11) : DeployedDecoration { + shapeFile = "vehicle_air_scout_wreck.dts"; +}; + +datablock StaticShapeData(DeployedDecoration12) : DeployedDecoration { + shapeFile = "billboard_1.dts"; +}; + +datablock StaticShapeData(DeployedDecoration13) : DeployedDecoration { + shapeFile = "billboard_2.dts"; +}; + +datablock StaticShapeData(DeployedDecoration14) : DeployedDecoration { + shapeFile = "billboard_3.dts"; +}; + +datablock StaticShapeData(DeployedDecoration15) : DeployedDecoration { + shapeFile = "billboard_4.dts"; +}; + +datablock StaticShapeData(DeployedDecoration16) : DeployedDecoration { + shapeFile = "goal_panel.dts"; +}; + +datablock ShapeBaseImageData(DecorationDeployableImage) { + mass = 20; + emap = true; + shapeFile = "statue_lfemale.dts"; // "stackable1s.dts"; + item = DecorationDeployable; + mountPoint = 1; + offset = "0 -0.6 -1.9"; + rotation = "0 0.1 1 180"; + deployed = DeployedDecoration; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(DecorationDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "DecorationDeployableImage"; + pickUpName = "a decoration pack"; + heatSignature = 0; + emap = true; + }; + +function DecorationDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function DecorationDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function DecorationDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function DecorationDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") { + if (%plyr.packSet == 3 || %plyr.packSet == 5) + %item.surfaceNrm2 = vectorScale(%playerVector,-1); + else + %item.surfaceNrm2 = %playerVector; + } + else { + if (%plyr.packSet < 3) + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + else { + %item.surfaceNrm2 = %playerVector; + if (%plyr.packSet == 3 || %plyr.packSet == 5) + %item.surfaceNrm2 = vectorScale(%item.surfaceNrm2,-1); + } + } + if (%item.surface.needsFit == 1) { + if (%plyr.packSet > 5 && %plyr.packSet < 10 && %item.surface.getDataBlock().getName() $= "DeployedDecoration6") { + %item.surfaceNrm2 = vectorCross(%item.surfaceNrm,realVec(%item.surface,"0 -1 0")); + } + } + %item.surfaceNrm3 = vectorCross(%item.surfaceNrm,%item.surfaceNrm2); + + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed @ %plyr.packSet; + }; + + if (%plyr.packSet < 3) { + %surfacePt2 = %item.surfacePt; + %rot2 = %rot; + %item.surfaceNrm2 = vectorScale(%item.surfaceNrm2,-1); + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm3,2.80)); + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm,0.25)); + %rot = rotAdd(%rot,"1 0 0" SPC $Pi / 2); + %rot = rotAdd(%rot,"0 1 0" SPC $Pi); + %deplObj.lTarget = new StaticShape () { + datablock = DeployedLTarget; + scale = 2.5/4 SPC 5/3 SPC 0.15 * 2; + }; + %deplObj.lTarget.setTransform(%surfacePt2 SPC %rot2); + %deplObj.lTarget.lMain = %deplObj; + } + + if (%plyr.packSet > 2 && %plyr.packSet < 6) { + %surfacePt2 = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm,0.2)); + %rot2 = %rot; + if (%plyr.packSet == 3) { + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm2,-0.14)); + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm3,0.51)); + } + else if (%plyr.packSet == 4) { + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm2,0.25)); + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm3,-0.4)); + } + else if (%plyr.packSet == 5) { + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm2,0.05)); + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm3,0.4)); + %rot = rotAdd(%rot,"0 0 -1" SPC $Pi / 4); + } + %deplObj.lTarget = new StaticShape () { + datablock = DeployedLTarget; + scale = 0.4/4 SPC 0.5/3 SPC 0.05 * 2; + }; + %deplObj.lTarget.setTransform(%surfacePt2 SPC %rot2); + %deplObj.lTarget.lMain = %deplObj; + } + + if (%plyr.packSet == 11) + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm,-2)); + + if (%plyr.packSet > 11 && %plyr.packSet < 16) { + %surfacePt2 = %item.surfacePt; + %rot2 = %rot; + %item.surfaceNrm2 = vectorScale(%item.surfaceNrm2,-1); + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm,10.9)); + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm,34)); + %surfacePt2 = vectorAdd(%surfacePt2,vectorScale(%item.surfaceNrm3,-1)); + %rot2 = rotAdd(%rot,"1 0 0" SPC $Pi / 2); + %deplObj.lTarget = new StaticShape () { + datablock = DeployedLTarget; + scale = 74/4 SPC 49/3 SPC 4; + }; + %deplObj.lTarget.setTransform(%surfacePt2 SPC %rot2); + %deplObj.lTarget.lMain = %deplObj; + } + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) { + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + %deplObj.lTarget.setRechargeRate(%deplObj.getDatablock().rechargeRate); + } + + // [[Settings]]: + %deplObj.needsFit = 1; + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%deplObj.lTarget) { + %deplObj.lTarget.team = %plyr.client.Team; + %deplObj.lTarget.setOwner(%plyr); + } + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + if (%deplObj.lTarget) + addToDeployGroup(%deplObj.lTarget); + + //let the AI know as well... + AIDeployObject(%plyr.client,%deplObj); + if (%deplObj.lTarget) + AIDeployObject(%plyr.client,%deplObj.lTarget); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + if (%deplObj.lTarget) + addDSurface(%deplObj,%deplObj.lTarget); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function DeployedDecoration::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, DecorationDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (isObject(%obj.lTarget)) + %obj.lTarget.schedule(500, "delete"); + fireBallExplode(%obj,1); +} + +function DecorationDeployableImage::onMount(%data, %obj, %node) { + %obj.hasDecoration = true; // set for decorationcheck + %obj.packSet = 0; +} + +function DecorationDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasDecoration = ""; + %obj.packSet = 0; +} + +function DeployedDecoration0::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration1::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration2::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration3::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration4::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration5::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration6::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration7::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration8::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration9::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration10::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration11::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration12::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration13::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration14::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration15::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} + +function DeployedDecoration16::onDestroyed(%this,%obj,%prevState) { + DeployedDecoration::onDestroyed(%this,%obj,%prevState); +} diff --git a/Scripts/Packs/door.cs b/Scripts/Packs/door.cs new file mode 100644 index 0000000..b1b9044 --- /dev/null +++ b/Scripts/Packs/door.cs @@ -0,0 +1,397 @@ +//client.player.setInventory(doorDeployable,1,true); +//--------------------------------------------------------- +// Deployable mspine, Code by Parousia +//--------------------------------------------------------- +datablock StaticShapeData(DeployedDoor) : StaticShapeDamageProfile { +className = "door"; +shapeFile = "Pmiscf.dts"; // b or dmiscf.dts, alternate + +maxDamage = 5; +destroyedLevel = 5; +disabledLevel = 4; + +isShielded = true; +energyPerDamagePoint = 60; +maxEnergy = 100; +rechargeRate = 0.25; + +explosion = HandGrenadeExplosion; +expDmgRadius = 3.0; +expDamage = 0.1; +expImpulse = 200.0; + +dynamicType = $TypeMasks::StaticShapeObjectType; +deployedObject = true; +cmdCategory = "DSupport"; +cmdIcon = CMDSensorIcon; +cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; +targetNameTag = 'Door'; +deployAmbientThread = true; +debrisShapeName = "debris_generic_small.dts"; +debris = DeployableDebris; +heatSignature = 0; +needsPower = true; +}; + +datablock ShapeBaseImageData(DoorDeployableImage) { +mass = 1; +emap = true; +shapeFile = "stackable1s.dts"; +item = DoorDeployable; +mountPoint = 1; +offset = "0 0 0"; +deployed = DeployedDoor; +heatSignature = 0; + +stateName[0] = "Idle"; +stateTransitionOnTriggerDown[0] = "Activate"; + +stateName[1] = "Activate"; +stateScript[1] = "onActivate"; +stateTransitionOnTriggerUp[1] = "Idle"; + +isLarge = false; +maxDepSlope = 360; +deploySound = ItemPickupSound; + +minDeployDis = 0; +maxDeployDis = 50.0; +}; + +datablock ItemData(DoorDeployable) { +className = Pack; +catagory = "Deployables"; +shapeFile = "stackable1s.dts"; +mass = 1; +elasticity = 0.2; +friction = 0.6; +pickupRadius = 1; +rotate = true; +image = "DoorDeployableImage"; +pickUpName = "a door pack"; +heatSignature = 0; +emap = true; +}; + +function DoorDeployableImage::testObjectTooClose(%item) { +return ""; +} + +function DoorDeployableImage::testNoTerrainFound(%item) { +// don't check this for non-Landspike turret deployables +} + +function DoorDeployable::onPickup(%this, %obj, %shape, %amount) { +// created to prevent console errors +} + +function doorDeployableImage::onMount(%data, %obj, %node) { +%obj.hasDoor = true; // set for blastcheck +%obj.packSet = 0; +%obj.expertSet = 3; +displayPowerFreq(%obj); + +} + +function doorDeployableImage::onUnmount(%data, %obj, %node) { +%obj.hasDoor = ""; +%obj.packSet = 0; +%obj.expertSet = 0; +} + +function doorDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1"){ + %item.surfaceNrm2 = %playerVector; + } + else{ + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + } + + %rot1 = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %scale1 = "0.5 6 160"; + %scale2 = "0.5 8 160"; + %dataBlock = %item.deployed; + + %space = rayDist(%item.surfacePt SPC %item.surfaceNrm,%scale1,$AllObjMask); + if (%space != getWord(%scale1,1)) + %type = true; + %scale1 = getWord(%scale1,0) SPC getWord(%scale1,0) SPC %space; + + + %mCenter = "0 0 -0.5"; + %pad = pad(%item.surfacePt SPC %item.surfaceNrm SPC %item.surfaceNrm2,%scale2,%mCenter); + %scale2 = getWords(%pad,0,2); + %item.surfacePt2 = getWords(%pad,3,5); + + //%vec1 = realVec(getWord(%item.surface,0),%item.surfaceNrm); + //%vec1 = realVec(%pad,%item.surfaceNrm); + %vec1 =validateVal(MatrixMulVector("0 0 0",%item.surfaceNrm)); + + if (!%scaleIsSet){ + %scale1 = vectorMultiply(%scale1,1/4 SPC 1/3 SPC 2); + %scale2 = vectorMultiply(%scale2,1/4 SPC 1/3 SPC 2); + %x = (getWord(%scale2,1)/0.166666)*0.125; + %scale3 = %x SPC 0.166666 SPC getWord(%scale1,2); + } + + + + %dir1 = VectorNormalize(vectorSub(%item.surfacePt,%item.surfacePt2)); + %adjust1 = vectorNormalize(vectorProject(%dir1,vectorCross(%item.surfaceNrm,%item.surfaceNrm2))); + %offset1 = -0.5; + %adjust1 = vectorScale(%adjust1,-0.5 * %offset1); + + %deplObj = new (%className)() { + dataBlock = %dataBlock; + scale = %scale3; + }; + %deplObj.closedscale = %scale3; + %deplObj.openedscale = getwords(%scale3,0,1) SPC 0.1; +//////////////////////////Apply settings////////////////////////////// + + // exact: + //%deplObj.setTransform(%item.surfacePt SPC %rot1); + %deplObj.setTransform(vectorAdd(VectorAdd(%item.surfacePt2, VectorScale(%vec1,getword(%scale3,2)/-4)),%adjust1) SPC %rot1); + + // misc info + addDSurface(%item.surface,%deplObj); + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + %deplObj.isdoor = 1; + // [[Normal Stuff]]: + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + %deplobj.timeout = getWord($expertSetting["Door", %plyr.expertSet], 0); + %deplobj.hasslided = 0; + %deplobj.state = "Closed"; + + if (%plyr.packset==0) + %deplobj.toggletype=0; + + if (%plyr.packset==1) + %deplobj.toggletype=1; + + if (%plyr.packset==2) { + %deplobj.powercontrol=1; + %deplobj.toggletype=1; + } + + if (%plyr.packset==3) { + %deplobj.powercontrol=2; + %deplobj.toggletype=1; + } + + if (%plyr.packset==4) { + %deplobj.collisionType = 1; + %deplobj.toggletype=0; // This is left mostly as a reference. + } + + if (%plyr.packset==5) + { + %deplobj.collisionType = 2; + %deplobj.accessLevel = 1; + %deplobj.toggletype=0; // This is left mostly as a reference. + messageClient(%plyr.client, "", "\c2Use \c3/setAccess [level] \c2- to set the access level needed for this door, use \c3/setPAccess [name] [level] \c2- to set someone's access level over your pieces."); + } + + if (%plyr.packset==6) { + %deplobj.collisionType = 3; + %deplobj.toggletype=0; // This is left mostly as a reference. + } + + if (%plyr.packset==7) { + %deplobj.collisionType = 4; + %deplobj.toggletype=0; // This is left mostly as a reference. + } + + %deplobj.canmove = true; + + // Power object + checkPowerObject(%deplObj); // Keep this down here. It updates the state of the powered doors. + + return %deplObj; +} + +function Deployeddoor::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, DoorDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function Deployeddoor::disassemble(%data,%plyr,%hTgt) { + disassemble(%data,%plyr,%hTgt); +} + +function doorDeployableImage::onMount(%data,%obj,%node) { + %obj.hasdoor = true; // set for mspinecheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function doorDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasdoor = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} + + +function Deployeddoor::onGainPowerEnabled(%data,%obj) { + if(%obj.canmove == true && %obj.powercontrol >= 1) + { + if(%obj.powerControl == 1 && %obj.state !$= "opened") // open when powered + schedule(10, 0, "open", %obj); + else if(%obj.powerControl == 2 && %obj.state !$= "closed") // closed when powered + schedule(10, 0, "close", %obj, 1); + } + Parent::onGainPowerEnabled(%data,%obj); +} + +function Deployeddoor::onLosePowerDisabled(%data,%obj) { + if(%obj.canmove == true && %obj.powercontrol >= 1) + { + if(%obj.powerControl == 1 && %obj.state !$= "closed") // open when unpowered + schedule(10, 0, "close", %obj, 1); + else if(%obj.powerControl == 2 && %obj.state !$= "opened") // closed when unpowered + schedule(10, 0, "open", %obj); + } + Parent::onLosePowerDisabled(%data,%obj); +} + +function DeployedDoor::onCollision(%data, %obj, %col) +{ + if(%obj.canMove == true && %obj.collisionType > 0 && %col.getDataBlock().getClassName() $= "PlayerData") + { + switch$(%obj.collisionType) + { + case 1: + Open(%obj); + case 2: + if(%col.client.givenAccess[%obj.getOwner()] >= %obj.accessLevel || %obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2You need \c3level-"@%obj.accessLevel@"\c2 from \c3"@%obj.getOwner().nameBase@"\c2 to access this door."); + case 3: + if(%obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2This door is only for \c3"@%obj.getOwner().nameBase@"\c2."); + case 4: + if(%col.client.isAdmin || %obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2This door is for \c3admins \c2or \c3"@%obj.getOwner().nameBase@"\c2 only."); + } + } + + // Don't call parent! It causes console spam! + // -- Eolk +} + +//////////////////// +//////////////////// +function open(%obj) +{ + if (!isObject(%obj)) + return; + + if (%obj.canmove == true) + { + %obj.moving = "open"; + %obj.prevscale = %obj.scale; + %obj.closedscale= %obj.scale; + %obj.canmove = false; + } + + if (getWord(%obj.scale, 2) < 0.3) + { + %obj.issliding = 0; + %obj.scale = getWord(%obj.scale,0) SPC getWord(%obj.scale,1) SPC 0.1; + %obj.settransform(%obj.gettransform()); + %obj.state = "opened"; + %obj.openedscale= %obj.scale; + + if (%obj.toggletype == 0) + schedule(%obj.timeout * 1000, 0, "close", %obj, 1); + else + %obj.canmove = true; + return; + } + + %obj.scale = getWord(%obj.scale, 0) SPC getWord(%obj.scale, 1) SPC getWord(%obj.prevscale ,2) - 0.4; + %obj.settransform(%obj.gettransform()); + %obj.prevscale = %obj.scale; + schedule(40, 0, "open", %obj); +} +///////////////////// +///////////////////// +function close(%obj,%timeout) +{ + if (!isObject(%obj)) + return; + + if (%obj.canmove == true) + { + %obj.moving = "close"; + %obj.prevscale =%obj.scale; + %obj.openedscale=%obj.scale; + %obj.canmove = false; + } + + if (getWord(%obj.scale, 2) > getWord(%obj.closedscale, 2) - 0.2) + { + %obj.issliding = 0; + %obj.scale = getWord(%obj.scale,0) SPC getWord(%obj.scale,1) SPC getWord(%obj.closedscale,2); + %obj.settransform(%obj.gettransform()); + %obj.state = "closed"; + %obj.closedscale= %obj.scale; + %obj.canmove = true; + return; + } + %obj.scale = getWord(%obj.scale, 0) SPC getWord(%obj.scale, 1) SPC getWord(%obj.prevscale, 2) + 0.4; + %obj.settransform(%obj.gettransform()); + %obj.prevscale = %obj.scale; + schedule(40, 0, "close", %obj); +} diff --git a/Scripts/Packs/forcefieldpack.cs b/Scripts/Packs/forcefieldpack.cs new file mode 100644 index 0000000..aaac1cb --- /dev/null +++ b/Scripts/Packs/forcefieldpack.cs @@ -0,0 +1,1161 @@ +//--------------------------------------------------------- +// Deployable Force Field +//--------------------------------------------------------- +//modified by Captn Power +// Translucencies +%noPassTrans = 1.0; +%teamPassTrans = 0.6; +%allPassTrans = 0.2; + +%dimDiv = 4; + +// RGB +%colourOn = 0.3; +%colourOff = 0.0; + +datablock ForceFieldBareData(DeployedForceField) { + className = "forcefield"; + fadeMS = 200; + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "1.0 1.0 1.0"; + powerOffColor = "0.0 0.0 0.0"; + targetNameTag = 'Force Field'; + targetTypeTag = 'ForceField'; + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + framesPerSec = 1; // 10 + numFrames = 5; + scrollSpeed = 15; + umapping = 0.01; // 1.0 + vmapping = 0.01; // 0.15 + deployedFrom = ForceFieldDeployable; + needsPower = true; +}; + +// No pass +datablock ForceFieldBareData(DeployedForceField0) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOn SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField1) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOn SPC %colourOff SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField2) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOff SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField3) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOff SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField4) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOff SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField5) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOn SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField6) : DeployedForceField { + baseTranslucency = %noPassTrans; + powerOffTranslucency = %noPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = %colourOn SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +// Team pass +datablock ForceFieldBareData(DeployedForceField7) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOn SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField8) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOn SPC %colourOff SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField9) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOff SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField10) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOff SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField11) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOff SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField12) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOn SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField13) : DeployedForceField { + baseTranslucency = %teamPassTrans; + powerOffTranslucency = %teamPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = %colourOn SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +// All pass +datablock ForceFieldBareData(DeployedForceField14) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField15) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOff SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField16) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOff SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField17) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOff SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField18) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOff SPC %colourOn SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField19) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; +}; + +datablock ForceFieldBareData(DeployedForceField20) : DeployedForceField { + baseTranslucency = %allPassTrans; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; +}; + +// thx to quantium mod for idea but those are diferent i made shure of it +//lightning no pass +datablock ForceFieldBareData(DeployedForceField21) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + texture[0] = "special/shockLightning01"; + texture[1] = "special/shockLightning02"; + texture[2] = "special/shockLightning03"; + color = "0.1 0.6 0.2"; + powerOffColor = "0.0 0.0 0.0"; + framesPerSec = 12; + numFrames = 3; + scrollSpeed = 0.1; + umapping = 0.35;//0.45; + vmapping = 5; +}; +//scan line no pass +datablock ForceFieldBareData(DeployedForceField22) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.1 0.5 0.1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/sniper00"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 0.3; + umapping = 1; + vmapping = 0.05; + +}; +//grid no pass +datablock ForceFieldBareData(DeployedForceField23) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.7 0.5 0.5"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/shocklance_effect01";//"desert/CP_itec01";//"liquidTiles/Modulation03"; // pack_rep05 + texture[1] = "special/shocklance_effect02"; + framesPerSec = 10; // plrec04 + numFrames = 2; // beampulse + scrollSpeed = 1; + umapping = 1.3; + vmapping = 1.3; +}; +// fire wall special no pass +datablock ForceFieldBareData(DeployedForceField24) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.7 0.3 0"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/redbump2"; + texture[1] = "special/ELFBeam"; + framesPerSec = 5; + numFrames = 2; + scrollSpeed = 5; + umapping = 0.5; + vmapping = 0.6; +}; +//energy feild no pass +datablock ForceFieldBareData(DeployedForceField25) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color[0] = "0.6 0.6 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/jammermap"; + framesPerSec = 2; + numFrames = 1; + scrollSpeed = 1; + umapping = 0.3; + vmapping = 0.3; +}; +//flashing no pass +datablock ForceFieldBareData(DeployedForceField26) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.4 0.4 0.9"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/stationglow"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 1.5; + umapping = 0.01; + vmapping = 0.01; +}; +//dirty window no pass +datablock ForceFieldBareData(DeployedForceField27) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "1 1 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "skins/tribes1.plaque";//"skins/tribes1.plaque";//"liquidtiles/icebound_water"; + + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 0; + umapping = 0.4; + vmapping = 0.4; +}; +//////////////////////////////////////////////////////////// +// scan line team pass +datablock ForceFieldBareData(DeployedForceField28) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = "0.1 0.5 0.1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/sniper00"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 0.3; + umapping = 1; + vmapping = 0.05; + +}; +//grid team pass +datablock ForceFieldBareData(DeployedForceField29) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = "0.5 0.7 0.5"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/shocklance_effect01";//"desert/CP_itec01";//"liquidTiles/Modulation03"; // pack_rep05 + texture[1] = "special/shocklance_effect02"; + framesPerSec = 10; // plrec04 + numFrames = 2; // beampulse + scrollSpeed = 1; + umapping = 1.3; + vmapping = 1.3; +}; +//energy feild team pass +datablock ForceFieldBareData(DeployedForceField30) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color[0] = "0.6 0.6 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/jammermap"; + framesPerSec = 2; + numFrames = 1; + scrollSpeed = 1; + umapping = 0.3; + vmapping = 0.3; +}; +//flashing team pass +datablock ForceFieldBareData(DeployedForceField31) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = false; + color = "0.4 0.4 0.9"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/stationglow"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 1.5; + umapping = 0.01; + vmapping = 0.01; +}; +/////////////////////////////////////////////////////////////////////////// +// scan line all pass +datablock ForceFieldBareData(DeployedForceField32) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.1 0.5 0.1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/sniper00"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 0.3; + umapping = 1; + vmapping = 0.05; + +}; +//grid team pass +datablock ForceFieldBareData(DeployedForceField33) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.5 0.5 0.7"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/shocklance_effect01";//"desert/CP_itec01";//"liquidTiles/Modulation03"; // pack_rep05 + texture[1] = "special/shocklance_effect02"; + framesPerSec = 10; // plrec04 + numFrames = 2; // beampulse + scrollSpeed = 1; + umapping = 1.3; + vmapping = 1.3; +}; +//energy feild all pass +datablock ForceFieldBareData(DeployedForceField34) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color[0] = "0.6 0.6 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/jammermap"; + framesPerSec = 2; + numFrames = 1; + scrollSpeed = 1; + umapping = 0.3; + vmapping = 0.3; +}; +//flashing all pass +datablock ForceFieldBareData(DeployedForceField35) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.4 0.4 0.9"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/stationglow"; + framesPerSec = 10; + numFrames = 1; + scrollSpeed = 1.5; + umapping = 0.01; + vmapping = 0.01; +}; +///////////////////////////////////////////////////// + +//weird firewall all pass +datablock ForceFieldBareData(DeployedForceField36) : DeployedForceField { + baseTranslucency =1; //0.8; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "1 0.3 0.1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "special/water2";//"desert/CP_itec01";//"liquidTiles/Modulation03"; // pack_rep05 + framesPerSec = 1; // plrec04 + numFrames = 1; // beampulse + scrollSpeed = 2.5; + umapping = 0.05; + vmapping = 0.05; +}; + +//my lava all pass +datablock ForceFieldBareData(DeployedForceField37) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.8 0.8 0.8"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "liquidTiles/lavapool01"; + texture[1] = "liquidTiles/lavapool02"; + texture[2] = "liquidTiles/lavapool03"; + texture[3] = "liquidTiles/lavapool04"; + framesPerSec = 0.1; // plrec04 + numFrames = 4; // beampulse + scrollSpeed = 0.01; + umapping = 0.1; + vmapping = 0.2; +}; +//water all pass +datablock ForceFieldBareData(DeployedForceField38) : DeployedForceField { + baseTranslucency = 0.8; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.1 0.4 0.7"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "liquidTiles/Modulation03"; // pack_rep05 + framesPerSec = 10; // plrec04 + numFrames = 1; // beampulse + scrollSpeed = 0.6; + umapping = 0.4; + vmapping = 0.4; +}; + +//Flashing Crosshair +datablock ForceFieldBareData(DeployedForceField39) : DeployedForceField { + baseTranslucency =1;//1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "1 0.5 0"; + powerOffColor = "0.0 0 0"; + texture[0] = "commander/icons/com_waypoint_6"; + texture[1] = "commander/icons/com_waypoint_5"; + texture[2] = "commander/icons/com_waypoint_4"; + texture[3] = "commander/icons/com_waypoint_3"; + texture[4] = "commander/icons/com_waypoint_2"; + framesPerSec = 5; + numFrames = 5; + scrollSpeed = 0; + umapping = 0.6;//0.1; + vmapping = 0.6;//0.1; +}; +//Glass Tile +datablock ForceFieldBareData(DeployedForceField40) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color[0] = "0.5 0.5 0.5"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "skins/silvercube";//"special/jammermap"; + framesPerSec = 1; + numFrames = 1; + scrollSpeed = 0; + umapping = 1; + vmapping = 1; +}; +//Vehicle Icons +datablock ForceFieldBareData(DeployedForceField41) : DeployedForceField { + baseTranslucency =1;//3; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color ="1 1 1";//"0.4 0.4 0.9"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "gui/hud_veh_icon_assault";//"special/stationglow"; + texture[1] = "gui/hud_veh_icon_bomber"; + texture[2] = "gui/hud_veh_icon_hapc"; + texture[3] = "gui/hud_veh_icon_mpb"; + texture[4] = "gui/hud_veh_icon_shrike"; + framesPerSec = 1;//10; + numFrames = 5; + scrollSpeed = 0;//1.5; + umapping = 0.3;//0.01; + vmapping = 0.3;//0.01; +}; +// Space 1 +datablock ForceFieldBareData(DeployedForceField42) : DeployedForceField { + baseTranslucency =1;//3; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color ="1 1 1";// "0.1 0.5 0.1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "redplanet_1"; + texture[1] = "redplanet_2"; + texture[2] = "redplanet_3"; + texture[3] = "redplanet_4"; + texture[4] = "redplanet_5"; + framesPerSec = 1; + numFrames = 5; + scrollSpeed = 0; + umapping = 0.1; + vmapping = 0.1; + +}; +//clouds 1 +datablock ForceFieldBareData(DeployedForceField43) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = true; + otherPermiable = true; + color = "0.8 0.8 0.8"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "desert/skies/desertmove2"; + framesPerSec = 1; // plrec04 + numFrames = 1; // beampulse + scrollSpeed = 1; + umapping = 0.01; + vmapping = 0.01; +}; +//Fuzzy Scanlines +datablock ForceFieldBareData(DeployedForceField44) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color[0] ="1 0.1 1";// "0.6 0.6 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "skins/scanline2"; + texture[1] = "skins/scanline3"; + texture[2] = "skins/scanline4"; + texture[3] = "skins/scanline5"; + texture[4] = "skins/scanline6"; + framesPerSec = 8; + numFrames = 5; + scrollSpeed = 0; + umapping = 0.05; + vmapping = 0.2; +}; +//Space 2 +datablock ForceFieldBareData(DeployedForceField45) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "1 1 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "badlands/skies/starrynite_v2_fr";//nef_tr2_red_1"; + texture[1] = "ice/skies/starrynite_v1_bk";//nef_tr2_red_2"; + texture[2] = "lava/skies/starrynite_v5_lf";//nef_tr2_red_3"; + texture[3] = "lush/skies/starrynite_v6_rt";//nef_tr2_red_7"; + texture[4] = "lush/skies/starrynite_v4_fr";//nef_tr2_red_5"; + framesPerSec = 1; + numFrames = 5; + scrollSpeed = 0; + umapping = 0.1; + vmapping = 0.1; +}; +//Signs +datablock ForceFieldBareData(DeployedForceField46) : DeployedForceField { + baseTranslucency =1;//1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "1 1 1"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "skins/billboard_1"; + texture[1] = "skins/billboard_2"; + texture[2] = "skins/billboard_3"; + texture[3] = "skins/billboard_4"; + framesPerSec = 2; + numFrames = 4; + scrollSpeed = 0; + umapping = 0.1; + vmapping = 0.1; +}; + +//clouds 2 +datablock ForceFieldBareData(DeployedForceField47) : DeployedForceField { + baseTranslucency =0.2; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.2 0.2 0.2"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "desert/skies/desertmove1"; + framesPerSec = 1; // plrec04 + numFrames = 1; // beampulse + scrollSpeed = 0; + umapping = 0.01; + vmapping = 0.01; +}; +//////////////////////////////////////////////////////////// +//computer screen +datablock ForceFieldBareData(DeployedForceField48) : DeployedForceField { + baseTranslucency =1; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.7 0.7 0.7"; + powerOffColor = "0.0 0.0 0.0"; + texture[0] = "lava/ds_mriveted2";//"ice/sw_ewal02a";//"desert/cp_icomp01f"; + framesPerSec = 1;//10; + numFrames = 1; + scrollSpeed = 0;//0.3; + umapping = 0.67;//1; + vmapping = 0.495;//0.05; + +}; +//Orb Symbol +datablock ForceFieldBareData(DeployedForceField49) : DeployedForceField { + baseTranslucency =1; //0.8; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "0.7 0.7 0.7"; + powerOffColor = "0.0 0.0 0.0"; +texture[0] = "badlands/bd_itec02"; //3.5 6.5 0.1 for pad size + framesPerSec = 1; + numFrames = 1; + scrollSpeed = 0; +umapping = 0.6; +vmapping = 0.3; +}; +//esp ff +datablock ForceFieldBareData(DeployedForceField50) : DeployedForceField { + baseTranslucency =1; //0.8; + powerOffTranslucency = %allPassTrans / %dimDiv; + teamPermiable = false; + otherPermiable = false; + color = "1 0.2 1"; + powerOffColor = "0.0 0.0 0.0"; + //texture[0] = "gui/trn_1charybdis";//texture[0] = "precipitation/snowflake017"; + //texture[1] = "gui/trn_2sehrganda";//texture[1] = "precipitation/snowflake016"; + //texture[2] = "gui/trn_3ymir"; + //texture[3] = "gui/trn_4bloodjewel"; + //texture[4] = "gui/trn_5draconis"; + texture[0] = "skins/noise"; + framesPerSec = 1; // plrec04 + numFrames = 1; // beampulse + scrollSpeed = 7; + umapping = 3; + vmapping = 1; +}; + + +datablock ShapeBaseImageData(ForceFieldDeployableImage) { + mass = 20; + emap = true; + shapeFile = "ammo_chaingun.dts"; + item = ForceFieldDeployable; + mountPoint = 1; + offset = "-0.2 -0.125 0"; + rotation = "0 -1 0 90"; + deployed = DeployedForceField; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(ForceFieldDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "ForceFieldDeployableImage"; + pickUpName = "a force field pack"; + heatSignature = 0; + emap = true; +}; +function ForceFieldDeployableImage::testObjectTooClose(%item) { + return; +} + +function ForceFieldDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function ForceFieldDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function ForceFieldDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "ForceFieldBare"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %scale = getWords($packSetting["forcefield",%plyr.packSet],0,2); + %mCenter = "-0.5 -0.5 -0.5"; + %pad = pad(%item.surfacePt SPC %item.surfaceNrm SPC %item.surfaceNrm2,%scale,%mCenter); + %scale = getWords(%pad,0,2); + %item.surfacePt = getWords(%pad,3,5); + %rot = getWords(%pad,6,9); + + // Add padding + %padSize = 0.01; + %scale = vectorAdd(%scale,%padSize * 2 SPC %padSize * 2 SPC -%padSize * 2); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm),%padSize)); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm2),%padSize)); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(vectorCross(%item.surfaceNrm,%item.surfaceNrm2)),-%padSize)); + + if (%item.squaresize !$="") + %test = buddychek(%item.surface.getOwner(),%plyr.client.guid); + else + %test=1; + + if ($Host::ExpertMode == 1) { + if (isCubic(%item.surface) && (%plyr.expertSet == 1 || %plyr.expertSet == 3 || %plyr.expertSet == 4) && %plyr.team == %item.surface.team + && %item.surface.getType() & $TypeMasks::StaticShapeObjectType + && ((($Host::OnlyOwnerCubicReplace == 0) || (%plyr.client == %item.surface.getOwner())) + || %test ==1 )) { + %scale = vectorAdd(realSize(%item.surface),%padSize * 2 SPC %padSize * 2 SPC %padSize * 2); + %center = realVec(%item.surface,vectorScale(getWords(%scale,0,1) SPC "0",-0.5)); + %item.surfacePt = vectorAdd(pos(%item.surface),%center); + %rot = rot(%item.surface); + %mod = vectorScale(matrixMulVector("0 0 0" SPC %rot ,"0 0 1"),-%padSize); + %item.surfacePt = vectorAdd(%item.surfacePt,%mod); + %item.surface.getDataBlock().disassemble(%plyr, %item.surface); + if( %plyr.expertSet == 4){ + %deplObj.disarm = 1; + } + } + } + + // TODO - temporary test fix - remove? + %scale = vectorAdd(%scale,"0 0 0"); + %x = getWord(%scale,0); + %y = getWord(%scale,1); + %z = getWord(%scale,2); + if (%x <= 0) + %x = 0.001; + if (%y <= 0) + %y = 0.001; + if (%z <= 0) + %z = 0.001; + %scale = %x SPC %y SPC %z; + + %deplObj = new (%className)() { + dataBlock = %item.deployed @ %plyr.packSet; + scale = %scale; + }; + + // Take the deployable off the player's back and out of inventory + //if ($Host::ExpertMode == 0) { + // %plyr.unMountImage(%slot); + // %plyr.decInventory(%item.item,1); + //} + +////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + %deplObj.pzone.setTransform(%item.surfacePt SPC %rot); + + if ($Host::ExpertMode == 1) { + if (%plyr.expertSet == 2 || %plyr.expertSet == 3){ + %deplObj.noSlow = true; + %deplObj.pzone.delete(); + %deplObj.pzone = ""; + } + } + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + %deplObj.notogle=0; + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + // Power object + checkPowerObject(%deplObj); + + if (!%deplObj.powerCount > 0) { + %deplObj.getDataBlock().disassemble(0,%deplObj); // Run Item Specific code. + messageClient(%plyr.client,'MsgDeployFailed','\c2Force field lost - no power source found!%1','~wfx/misc/misc.error.wav'); + } + + return %deplObj; +} +////////////////////////////////////////////////////////////////// +function ForceFieldDeployableImage::onMount(%data, %obj, %node) { + %obj.hasForceField = true; // set for forcefieldcheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function ForceFieldDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasForceField = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} +function DeployedForceField::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.pzone)) + %obj.pzone.delete(); + disassemble(%data,%plyr,%obj); +} + +function DeployedForceField0::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField1::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField2::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField3::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField4::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField5::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField6::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField7::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField8::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField9::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField10::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField11::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField12::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField13::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField14::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField15::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField16::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField17::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField18::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField19::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField20::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + + + + + +/////////////// + + +function DeployedForceField21::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField22::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField23::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField24::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField25::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField26::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField27::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField28::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField29::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField30::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + + +////////// + + +function DeployedForceField31::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField32::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField33::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField34::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField35::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField36::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +// + +function DeployedForceField37::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField38::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField39::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +///////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +function DeployedForceField40::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField41::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField42::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField43::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField44::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField45::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField46::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField47::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField48::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} + +function DeployedForceField49::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} +function DeployedForceField50::disassemble(%data,%plyr,%obj) { + DeployedForceField::disassemble(%data,%plyr,%obj); +} diff --git a/Scripts/Packs/generator.cs b/Scripts/Packs/generator.cs new file mode 100644 index 0000000..a7bed53 --- /dev/null +++ b/Scripts/Packs/generator.cs @@ -0,0 +1,145 @@ +//-------------------------------------------------------------------------- +// Deployable Generator +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(GeneratorDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = GeneratorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = GeneratorLarge; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 2; + maxDeployDis = 5; +}; + +datablock ItemData(GeneratorDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + + hasLight = true; + lightType = "PulsingLight"; + lightColor = "0.1 0.8 0.8 1.0"; + lightTime = "1000"; + lightRadius = "3"; + + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + rotate = true; + image = "GeneratorDeployableImage"; + pickUpName = "a generator pack"; + heatSignature = 0; + emap = true; +}; + +function GeneratorDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function GeneratorDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function GeneratorDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,-1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(vectorNormalize(vectorCross(%item.surfaceNrm,%item.surfaceNrm2)),2)); + + %deplObj = new (%className)() { + dataBlock = GeneratorLarge; + deployed = true; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set power + %deplObj.setSelfPowered(); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // add to power list + $PowerList = listAdd($PowerList,%deplObj,-1); + + return %deplObj; +} + +function GeneratorLarge::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.deployed && ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon)) { + %obj.isRemoved = true; + %loc = findWord($PowerList,%obj); + if (%loc !$= "") + $PowerList = listDel($PowerList,%loc); + $TeamDeployedCount[%obj.team,GeneratorDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +function GeneratorDeployableImage::onMount(%data,%obj,%node) { + %obj.hasGen = true; // set for gencheck + displayPowerFreq(%obj); +} + +function GeneratorDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasGen = ""; +} diff --git a/Scripts/Packs/gravityfieldpack.cs b/Scripts/Packs/gravityfieldpack.cs new file mode 100644 index 0000000..b6d4c3c --- /dev/null +++ b/Scripts/Packs/gravityfieldpack.cs @@ -0,0 +1,344 @@ +//--------------------------------------------------------- +// Deployable Gravity Field +//--------------------------------------------------------- + +// Translucencies +%fieldTrans = 1.0; +%powerOffTrans = 0.25; + +// RGB +%colourOn = 0.6; +%colourOff = 0.15; +%dimDiv = 3; + +datablock ForceFieldBareData(DeployedGravityField) { + className = "gravityfield"; + fadeMS = 1000; + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = "1.0 1.0 1.0"; + powerOffColor = "0.0 0.0 0.0"; + targetTypeTag = 'GravityField'; + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; + deployedFrom = GravityFieldDeployable; + velocityMod = 1; + gravityMod = 1; + appliedForce = "0 0 0"; + needsPower = true; +}; + +// slow +datablock ForceFieldBareData(DeployedGravityField0) : DeployedGravityField { + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = %colourOff/%dimDiv SPC %colourOn/%dimDiv SPC %colourOff/%dimDiv; + powerOffColor = "0.0 0.0 0.0"; + velocityMod = 1; + gravityMod = 0; +}; + +// fast +datablock ForceFieldBareData(DeployedGravityField1) : DeployedGravityField { + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = %colourOff SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; + velocityMod = 1; + gravityMod = 0; +}; + +// zero gravity +datablock ForceFieldBareData(DeployedGravityField2) : DeployedGravityField { + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOn SPC %colourOff; + powerOffColor = "0.0 0.0 0.0"; + velocityMod = 1; + gravityMod = 0; +}; + +// fastfield +datablock ForceFieldBareData(DeployedGravityField3) : DeployedGravityField { + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = %colourOn/%dimDiv SPC %colourOff/%dimDiv SPC %colourOn/%dimDiv; + powerOffColor = "0.0 0.0 0.0"; + velocityMod = 1.5; + gravityMod = 0; +}; + +// super fastfield +datablock ForceFieldBareData(DeployedGravityField4) : DeployedGravityField { + baseTranslucency = %fieldTrans; + powerOffTranslucency = %powerOffTrans; + teamPermiable = true; + otherPermiable = true; + color = %colourOn SPC %colourOff SPC %colourOn; + powerOffColor = "0.0 0.0 0.0"; + velocityMod = 1.7; + gravityMod = 0; +}; + +datablock ShapeBaseImageData(GravityFieldDeployableImage) { + mass = 20; + emap = true; + shapeFile = "ammo_chaingun.dts"; + item = GravityFieldDeployable; + mountPoint = 1; + offset = "-0.2 -0.125 0"; + rotation = "0 -1 0 90"; + deployed = DeployedGravityField; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(GravityFieldDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + joint = "4.75 4.75 4.75"; + rotate = true; + image = "GravityFieldDeployableImage"; + pickUpName = "a gravity field pack"; + heatSignature = 0; + emap = true; +}; + +function GravityFieldDeployableImage::testObjectTooClose(%item) { + return; +} + +function GravityFieldDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function GravityFieldDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function GravityFieldDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "ForceFieldBare"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %scale = getWords($packSetting["gravfield",%plyr.packSet],0,2); + + %space = rayDist(%item.surfacePt SPC %item.surfaceNrm,%scale); + %scale = getWord(%scale,0) SPC getWord(%scale,0) SPC %space; + + // Shift field position since field handle is not field center + %mod = firstWord($packSetting["gravfield",%plyr.packSet]) / 2; + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm2),%mod)); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(vectorCross(%item.surfaceNrm,%item.surfaceNrm2)),%mod)); + + // Add padding + %padSize = 0.01; + %scale = vectorAdd(%scale,%padSize * 2 SPC %padSize * 2 SPC %padSize * 2); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm),%padSize)); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm2),%padSize)); + %item.surfacePt = vectorSub(%item.surfacePt,vectorScale(vectorNormalize(vectorCross(%item.surfaceNrm,%item.surfaceNrm2)),%padSize)); + + // Set datablock + if (%plyr.packSet == 0) + %dataBlock = nameToID(%item.deployed @ 0); + else if (%plyr.packSet == 1) + %dataBlock = nameToID(%item.deployed @ 1); + else if (%plyr.packSet == 2) + %dataBlock = nameToID(%item.deployed @ 0); + else if (%plyr.packSet == 3) + %dataBlock = nameToID(%item.deployed @ 1); + else if (%plyr.packSet == 4) + %dataBlock = nameToID(%item.deployed @ 2); + else if (%plyr.packSet == 5) + %dataBlock = nameToID(%item.deployed @ 3); + else if (%plyr.packSet == 6) + %dataBlock = nameToID(%item.deployed @ 4); + + %appliedForceVec = vectorNormalize(%item.surfaceNrm); + + if ($Host::ExpertMode == 1) { + if (isCubic(%item.surface) && (%plyr.expertSet == 1 || %plyr.expertSet == 2) && %plyr.team == %item.surface.team + && %item.surface.getType() & $TypeMasks::StaticShapeObjectType + && (($Host::OnlyOwnerCubicReplace == 0) || (%plyr.client == %item.surface.getOwner()))) { + if (%plyr.expertSet == 2) + %appliedForceVec = vectorNormalize(realVec(%item.surface,"0 0 1")); + %scale = vectorAdd(realSize(%item.surface),%padSize * 2 SPC %padSize * 2 SPC %padSize * 2); + %center = realVec(%item.surface,vectorScale(getWords(%scale,0,1) SPC "0",-0.5)); + %item.surfacePt = vectorAdd(pos(%item.surface),%center); + %rot = rot(%item.surface); + %mod = vectorScale(matrixMulVector("0 0 0" SPC %rot ,"0 0 1"),-%padSize); + %item.surfacePt = vectorAdd(%item.surfacePt,%mod); + %item.surface.getDataBlock().disassemble(%plyr, %item.surface); + } + } + + %velocityMod = %dataBlock.velocityMod; + %gravityMod = %dataBlock.gravityMod; + %appliedForce = %dataBlock.appliedForce; + + %slowForce = 500; + %fastForce = 1000; + + if (%plyr.packSet == 0) + %appliedForce = vectorScale(%appliedForceVec,%slowForce); + else if (%plyr.packSet == 1) + %appliedForce = vectorScale(%appliedForceVec,%fastForce); + else if (%plyr.packSet == 2) + %appliedForce = vectorScale(%appliedForceVec,-%slowForce); + else if (%plyr.packSet == 3) + %appliedForce = vectorScale(%appliedForceVec,-%fastForce); + + %deplObj = new (%className)() { + dataBlock = %dataBlock; + scale = %scale; + velocityMod = %velocityMod; + gravityMod = %gravityMod; + appliedForce = %appliedForce; + }; + + // Take the deployable off the player's back and out of inventory + if ($Host::ExpertMode == 0) { + %plyr.unMountImage(%slot); + %plyr.decInventory(%item.item,1); + } + +////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + %deplObj.pzone.setTransform(%item.surfacePt SPC %rot); + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + // Power object + checkPowerObject(%deplObj); + + if (!%deplObj.powerCount > 0) { + %deplObj.getDataBlock().disassemble(0,%deplObj); // Run Item Specific code. + messageClient(%plyr.client,'MsgDeployFailed','\c2Gravity field lost - no power source found!%1','~wfx/misc/misc.error.wav'); + } + + return %deplObj; +} + +///////////////////////////////////// + +function GravityFieldDeployableImage::onMount(%data, %obj, %node) { + %obj.hasGravField = true; // set for gravfieldcheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function GravityFieldDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasGravField = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} + +function DeployedGravityField::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.pzone)) + %obj.pzone.delete(); + disassemble(%data,%plyr,%obj); +} + +function DeployedGravityField0::disassemble(%data,%plyr,%obj) { + DeployedGravityField::disassemble(%data,%plyr,%obj); +} + +function DeployedGravityField1::disassemble(%data,%plyr,%obj) { + DeployedGravityField::disassemble(%data,%plyr,%obj); +} + +function DeployedGravityField2::disassemble(%data,%plyr,%obj) { + DeployedGravityField::disassemble(%data,%plyr,%obj); +} + +function DeployedGravityField3::disassemble(%data,%plyr,%obj) { + DeployedGravityField::disassemble(%data,%plyr,%obj); +} + +function DeployedGravityField4::disassemble(%data,%plyr,%obj) { + DeployedGravityField::disassemble(%data,%plyr,%obj); +} diff --git a/Scripts/Packs/jumpad.cs b/Scripts/Packs/jumpad.cs new file mode 100644 index 0000000..78a3ec3 --- /dev/null +++ b/Scripts/Packs/jumpad.cs @@ -0,0 +1,176 @@ +//-------------------------------------------------------------------------- +// Jumpad +//-------------------------------------------------------------------------- + +datablock StaticShapeData(DeployedJumpad) : StaticShapeDamageProfile { + className = "jumpad"; + shapeFile = "nexusbase.dts"; // dmiscf.dts, alternate + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 2.0; + mass = 1.2; + elasticity = 0.1; + friction = 0.9; + collideable = 1; + pickupRadius = 1; + sticky=false; + + impulse = 5000; + + hasLight = true; + lightType = "PulsingLight"; + lightColor = "0.1 0.8 0.8 1.0"; + lightTime = "100"; + lightRadius = "3"; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + + targetNameTag = 'Jump'; + targetTypeTag = 'Pad'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock ShapeBaseImageData(JumpadDeployableImage) { + mass = 10; + emap = true; + shapeFile = "stackable1s.dts"; + item = JumpadDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedJumpad; + heatSignature = 0; + collideable = 1; + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; // 30 + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(JumpadDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "JumpadDeployableImage"; + pickUpName = "a jump pad pack"; + heatSignature = 0; + emap = true; +}; + +function JumpadDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function JumpadDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %impulse = firstWord($packSetting["jumpad",%plyr.packSet]); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + scale = "1 1 2"; + }; + + // set impulse (jump pad strength) + %deplObj.impulse = %impulse; + + // set orientation + %deplObj.setDeployRotation(getWords(%item.surfacePt, 0, 1) SPC getWord(%item.surfacePt, 2) + 0.1, %item.surfaceNrm); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + return %deplObj; +} + +function DeployedJumpad::onCollision(%data,%obj,%col) { + // TODO - update escape pod + if (%col.getClassName() !$= "Player" && %col.getDataBlock().getName() !$= "EscapePodVehicle") + return; // Only boost players + if (%obj.team == %col.team) { + %vel = %col.getVelocity(); + %vec = realVec(%obj,"0 0 1"); + %position = getWords(%col.getTransform(), 0, 2); +// %impulseVec = vectorScale(%vec,1000); // Jump clear of the pad +// %col.applyImpulse(%position, %impulseVec); + %col.playAudio(0, MortarFireSound); + %impulseVec = vectorScale(%vec,%obj.impulse); +// %col.schedule(50, "applyImpulse", %position, %impulseVec); + %col.applyImpulse(%position, %impulseVec); + } +} + +function DeployedJumpad::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, JumpadDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + fireBallExplode(%obj,1); +} + +function JumpadDeployableImage::onMount(%data, %obj, %node) { + %obj.hasJumpad = true; // set for jumpadcheck + %obj.packSet = 0; +} + +function JumpadDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasJumpad = ""; + %obj.packSet = 0; +} diff --git a/Scripts/Packs/largeInventory.cs b/Scripts/Packs/largeInventory.cs new file mode 100644 index 0000000..f2fe784 --- /dev/null +++ b/Scripts/Packs/largeInventory.cs @@ -0,0 +1,123 @@ +//-------------------------------------------------------------------------- +// Large Inventory Station +// +// +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(LargeInventoryDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = LargeInventoryDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = StationInventory; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(LargeInventoryDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "LargeInventoryDeployableImage"; + joint = "4.5 4.5 4.5"; + pickUpName = "a large inventory station pack"; + heatSignature = 0; + emap = true; +}; + +function LargeInventoryDeployable::onPickup(%this, %obj, %shape, %amount) +{ + // created to prevent console errors +} + +function LargeInventoryDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + %plyr.unMountImage(%slot); + %plyr.decInventory(%item.item,1); + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,-1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() //Inventory Station + { + dataBlock = StationInventory; + position = %surfacePt; + rotation = %rot; + deployed = true; + }; + %deplObj.setTransform(%item.surfacePt SPC %rot); + + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + addDSurface(%item.surface,%deplObj); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + if(%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + + addToDeployGroup(%deplObj); + + //let the AI know as well... + + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // Adjust the trigger object + adjustTrigger(%deplObj); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function LargeInventoryDeployableImage::testNoTerrainFound(%item) +{ +//return %item.surface.getClassName() !$= TerrainBlock; +} + +function LargeInventoryDeployableImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); +} diff --git a/Scripts/Packs/largeSensor.cs b/Scripts/Packs/largeSensor.cs new file mode 100644 index 0000000..29f0b21 --- /dev/null +++ b/Scripts/Packs/largeSensor.cs @@ -0,0 +1,127 @@ +//-------------------------------------------------------------------------- +// Large Pulse Sensor +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(LargeSensorDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = LargeSensorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = SensorLargePulse; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(LargeSensorDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "LargeSensorDeployableImage"; + joint = "4.5 4.5 4.5"; + pickUpName = "a large pulse sensor pack"; + heatSignature = 0; + emap = true; +}; + +function LargeSensorDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function LargeSensorDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function LargeSensorDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = SensorLargePulse; + deployed = true; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; +// setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + +// %deplObj.playThread($PowerThread,"Power"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function SensorLargePulse::onDestroyed(%data,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.deployed) { + %obj.isRemoved = true; + $TeamDeployedCount[%obj.team,LargeSensorDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +function LargeSensorDeployableImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); +} diff --git a/Scripts/Packs/laserturret.cs b/Scripts/Packs/laserturret.cs new file mode 100644 index 0000000..988d433 --- /dev/null +++ b/Scripts/Packs/laserturret.cs @@ -0,0 +1,164 @@ +//-------------------------------------------------------------------------- +// Large Mortar Turret +// +// +//-------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// Sounds +//-------------------------------------- +datablock EffectProfile(MBLSwitchEffect) +{ + effectname = "powered/turret_heavy_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(MBLFireEffect) +{ + effectname = "powered/turret_mortar_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(MBLSwitchSound) +{ + filename = "fx/powered/turret_heavy_activate.wav"; + description = AudioClose3d; + preload = true; + effect = MBLSwitchEffect; +}; + +datablock AudioProfile(MBLFireSound) +{ + filename = "fx/powered/turret_mortar_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = MBLFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock AudioProfile(MortarTurretProjectileSound) +{ + filename = "fx/weapons/mortar_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock GrenadeProjectileData(MortarTurretShot) +{ + projectileShapeName = "mortar_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 1.32; + damageRadius = 30.0; + radiusDamageType = $DamageType::MortarTurret; + kickBackStrength = 3000; + + explosion = "MortarExplosion"; + velInheritFactor = 0.5; + splash = MortarSplash; + + baseEmitter = MortarSmokeEmitter; + bubbleEmitter = MortarBubbleEmitter; + + grenadeElasticity = 0.25; + grenadeFriction = 0.4; + armingDelayMS = 1500; // z0dd - ZOD, 4/25/02. Was 2000 + gravityMod = 1.2; // z0dd - ZOD, 5/18/02. Make mortar projectile heavier, less floaty + muzzleVelocity = 75.95; // z0dd - ZOD, 8/13/02. More speed to compensate for higher gravity. Was 63.7 + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +//-------------------------------------------------------------------------- +//-------------------------------------- Fusion Turret Image +// +datablock TurretImageData(MortarBarrelLarge) +{ + shapeFile = "turret_mortar_large.dts"; + item = MortarBarrelPack; // z0dd - ZOD, 4/25/02. Was wrong: MortarBarrelLargePack + + projectile = MortarTurretShot; + projectileType = GrenadeProjectile; + usesEnergy = true; + fireEnergy = 30; + minEnergy = 30; + emap = true; + + // don't let a mortar turret blow itself up + dontFireInsideDamageRadius = true; + damageRadius = 40; + + param = ScoutMortarParam; + + // Turret parameters + activationMS = 700; // z0dd - ZOD, 4/25/02. Was 1000. Amount of time it takes turret to unfold + deactivateDelayMS = 1500; + thinkTimeMS = 140; // z0dd - ZOD, 4/25/02. Was 200. Amount of time before turret starts to unfold (activate) + degPerSecTheta = 580; + degPerSecPhi = 960; + attackRadius = 400; // z0dd - ZOD, 4/25/02. Was 160 + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = MBLSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.3; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = MBLFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 1.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + + + diff --git a/Scripts/Packs/lightpack.cs b/Scripts/Packs/lightpack.cs new file mode 100644 index 0000000..633b793 --- /dev/null +++ b/Scripts/Packs/lightpack.cs @@ -0,0 +1,325 @@ +//--------------------------------------------------------- +// Deployable Light +//--------------------------------------------------------- + +%colourOn = 0.5; +%colourOff = 0.1; + +%strobeColourOn = 1.0; +%strobeColourOff = 0.0; + +datablock StaticShapeData(DeployedLightBase) : StaticShapeDamageProfile { + className = "lightbase"; + shapeFile = "pack_deploy_sensor_motion.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Light'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ItemData(DeployedLight) { + shapeFile = "turret_muzzlepoint.dts"; + hasLight = true; + lightType = "ConstantLight"; + lightColor = "1.0 1.0 1.0 1.0"; + lightTime = "1000"; + lightRadius = "15"; +}; + +// Constant + +datablock ItemData(DeployedLight0) : DeployedLight { + lightColor = %colourOn SPC %colourOn SPC %colourOn; +}; + +datablock ItemData(DeployedLight1) : DeployedLight { + lightColor = %colourOn SPC %colourOff SPC %colourOff; +}; + +datablock ItemData(DeployedLight2) : DeployedLight { + lightColor = %colourOff SPC %colourOn SPC %colourOff; +}; + +datablock ItemData(DeployedLight3) : DeployedLight { + lightColor = %colourOff SPC %colourOff SPC %colourOn; +}; + +datablock ItemData(DeployedLight4) : DeployedLight { + lightColor = %colourOff SPC %colourOn SPC %colourOn; +}; + +datablock ItemData(DeployedLight5) : DeployedLight { + lightColor = %colourOn SPC %colourOff SPC %colourOn; +}; + +datablock ItemData(DeployedLight6) : DeployedLight { + lightColor = %colourOn SPC %colourOn SPC %colourOff; +}; + +// Strobe + +datablock ItemData(DeployedLight7) : DeployedLight { + lightColor = %strobeColourOn SPC %strobeColourOn SPC %strobeColourOn; + lightType = "PulsingLight"; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight8) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOn SPC %strobeColourOff SPC %strobeColourOff; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight9) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOff SPC %strobeColourOn SPC %strobeColourOff; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight10) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOff SPC %strobeColourOff SPC %strobeColourOn; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight11) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOff SPC %strobeColourOn SPC %strobeColourOn; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight12) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOn SPC %strobeColourOff SPC %strobeColourOn; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ItemData(DeployedLight13) : DeployedLight { + lightType = "PulsingLight"; + lightColor = %strobeColourOn SPC %strobeColourOn SPC %strobeColourOff; + lightTime = "50"; + lightRadius = "10"; +}; + +datablock ShapeBaseImageData(LightDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = LightDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedLightBase; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(LightDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "LightDeployableImage"; + pickUpName = "a light pack"; + heatSignature = 0; + emap = true; +}; + +function LightDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function LightDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function LightDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function LightDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + %deplObj.LgtColor = %plyr.packset; + + %deplObj.light = new Item() { + datablock = DeployedLight @ %plyr.packSet; + static = true; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + adjustLight(%deplObj); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %deplObj.light.lightBase = %deplObj; + %deplObj.lightPowerMode = %plyr.expertSet; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function DeployedLightBase::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, LightDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (isObject(%obj.light)) + %obj.light.schedule(500, "delete"); +} + +function DeployedLightBase::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.light)) + %obj.light.delete(); + disassemble(%data,%plyr,%obj); +} + +function adjustLight(%obj) { + %obj.light.setTransform(vectorAdd(%obj.getPosition(),vectorScale(realVec(%obj,"0 0 1"),1)) SPC %obj.getRotation()); +} + +function LightDeployableImage::onMount(%data, %obj, %node) { + %obj.hasLight = true; // set for lightcheck + %obj.packSet = 0; +} + +function LightDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasLight = ""; + %obj.packSet = 0; +} + +function DeployedLightBase::onGainPowerEnabled(%data,%obj) { + if (%obj.lightPowerMode) + { + if(isObject(%obj.light)) + %obj.light.delete(); + } // have to put brackets here or `else` will be confused + else + { + if (isObject(%obj.light)) + %obj.light.delete(); + %Obj.light = new Item() { + datablock = DeployedLight @ %Obj.LgtColor; + static = true; + }; + // set orientation + adjustLight(%Obj); + } + + Parent::onGainPowerEnabled(%data,%obj); +} + +function DeployedLightBase::onLosePowerDisabled(%data,%obj) { + if (!%obj.lightPowerMode) + { + if(isObject(%obj.light)) + %obj.light.delete(); + } // have to put brackets here or `else` will be confused + else + { + if (isObject(%obj.light)) + %obj.light.delete(); + %Obj.light = new Item() { + datablock = DeployedLight @ %Obj.LgtColor; + static = true; + }; + // set orientation + adjustLight(%Obj); + } + + Parent::onLosePowerDisabled(%data,%obj); +} diff --git a/Scripts/Packs/logoprojectorpack.cs b/Scripts/Packs/logoprojectorpack.cs new file mode 100644 index 0000000..911d58b --- /dev/null +++ b/Scripts/Packs/logoprojectorpack.cs @@ -0,0 +1,237 @@ +//--------------------------------------------------------- +// Deployable Logo Logo Projector +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedLogoProjector) : StaticShapeDamageProfile { + className = "logoprojector"; + shapeFile = "teamlogo_projector.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Logo Projector'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(LogoProjectorDeployableImage) { + mass = 20; + emap = true; + shapeFile = "teamlogo_projector.dts"; + item = LogoProjectorDeployable; + mountPoint = 1; + offset = "0 0 0"; + rotation = "-1 0 0 90"; + deployed = DeployedLogoProjector; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(LogoProjectorDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "LogoProjectorDeployableImage"; + pickUpName = "a logo projector pack"; + heatSignature = 0; + emap = true; + }; + +function LogoProjectorDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function LogoProjectorDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function LogoProjectorDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function LogoProjectorDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + if ($Host::Purebuild == 1) + if (%plyr.packSet == 0) + %logo = 0; + else + %logo = %plyr.packSet; + else { + %logo = 0; + } + + switch (%logo) { + case 1: + %logo = "Base"; + case 2: + %logo = "BaseB"; + case 3: + %logo = "Swolf"; + case 4: + %logo = "DSword"; + case 5: + %logo = "BEagle"; + case 6: + %logo = "COTP"; + case 7: + %logo = "Bioderm"; + default: + %logo = "0"; + } + + if (%logo $= "0") + %deplObj.holoBlock = getTaggedString(Game.getTeamSkin(%plyr.client.team)) @ "Logo"; + else + %deplObj.holoBlock = %logo @ "Logo"; + + %deplObj.holo = new StaticShape() { + datablock = %deplObj.holoBlock; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + adjustHolo(%deplObj); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %deplObj.holo.team = %plyr.client.Team; + %deplObj.holo.setOwner(%plyr); + %deplObj.holo.projector = %deplObj; + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client,%deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function DeployedLogoProjector::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, LogoProjectorDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (isObject(%obj.holo)) + %obj.holo.schedule(500, "delete"); + fireBallExplode(%obj,1); +} + +function DeployedLogoProjector::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.holo)) + %obj.holo.delete(); + disassemble(%data,%plyr,%obj); +} + +function DeployedLogoProjector::onGainPowerEnabled(%data,%obj) { + if (shouldChangePowerState(%obj,true)) { + if (isObject(%obj.holo)) + %obj.holo.delete(); + %obj.holo = new StaticShape() { + datablock = %obj.holoBlock; + projector = %obj; + }; + adjustHolo(%obj); + } + Parent::onGainPowerEnabled(%data,%obj); +} + +function DeployedLogoProjector::onLosePowerDisabled(%data,%obj) { + if (shouldChangePowerState(%obj,false)) { + if (isObject(%obj.holo)) + %obj.holo.delete(); + } + Parent::onLosePowerDisabled(%data,%obj); +} + +function adjustHolo(%obj) { + %obj.holo.setTransform(vectorAdd(%obj.getPosition(),vectorScale(realVec(%obj,"0 0 1"),10)) SPC %obj.getRotation()); +} + +function LogoProjectorDeployableImage::onMount(%data, %obj, %node) { + %obj.hasProjector = true; // set for projectorcheck + %obj.packSet = 0; + displayPowerFreq(%obj); +} + +function LogoProjectorDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasProjector = ""; + %obj.packSet = 0; +} diff --git a/Scripts/Packs/mediumSensor.cs b/Scripts/Packs/mediumSensor.cs new file mode 100644 index 0000000..ad9f8e9 --- /dev/null +++ b/Scripts/Packs/mediumSensor.cs @@ -0,0 +1,127 @@ +//-------------------------------------------------------------------------- +// Medium Pulse Sensor +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(MediumSensorDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = MediumSensorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = SensorMediumPulse; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(MediumSensorDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "MediumSensorDeployableImage"; + joint = "4.5 4.5 4.5"; + pickUpName = "a medium pulse sensor pack"; + heatSignature = 0; + emap = true; +}; + +function MediumSensorDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function MediumSensorDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function MediumSensorDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = SensorMediumPulse; + deployed = true; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; +// setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + +// %deplObj.playThread($PowerThread,"Power"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // Power object + checkPowerObject(%deplObj); + + return %deplObj; +} + +function SensorMediumPulse::onDestroyed(%data,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.deployed) { + %obj.isRemoved = true; + $TeamDeployedCount[%obj.team,MediumSensorDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +function MediumSensorDeployableImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); +} diff --git a/Scripts/Packs/mspine.cs b/Scripts/Packs/mspine.cs new file mode 100644 index 0000000..373cc33 --- /dev/null +++ b/Scripts/Packs/mspine.cs @@ -0,0 +1,274 @@ +//--------------------------------------------------------- +// Deployable mspine, Code by Parousia +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedMSpine) : StaticShapeDamageProfile { + className = "mspine"; + shapeFile = "dmiscf.dts"; + + maxDamage = 5.0; + destroyedLevel = 5.0; + disabledLevel = 2.5; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.5; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Medium Support Beam'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock StaticShapeData(DeployedMSpinering) : DeployedMSpine { + maxDamage = 1.0; + destroyedLevel = 1.0; + disabledLevel = 0.75; +}; + +datablock ShapeBaseImageData(mspineDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = mspineDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedMSpine; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(mspineDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + joint = "1 1 1"; + rotate = true; + image = "mspineDeployableImage"; + pickUpName = "a medium support beam pack"; + heatSignature = 0; + emap = true; +}; + +function mspineDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function mspineDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %scale = getWords($packSetting["mspine",%plyr.packSet],0,2); + + %mod = 0.5; + + if (%plyr.packSet >= 5 && %plyr.packSet < 8) { + %space = rayDist(%item.surfacePt SPC %item.surfaceNrm,%scale,$AllObjMask); + + if (%space != getWord(%scale,1)) + %type = 1; + + %scale = getWord(%scale,0) SPC getWord(%scale,0) SPC %space; + if (%plyr.packSet == 7) + %mod = -0.01; + } + + %scaler = getWords($packSetting["mspine",%plyr.packSet],3,5); + + %deplObj = new (%className)() { //Main Spine + dataBlock = %item.deployed; + scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + }; + + if (%plyr.packSet != 0 && (%plyr.packSet == 6 || %plyr.packSet == 7 || %plyr.expertSet == 1)) { + %deplObj1 = new (%className)() { //Top add + dataBlock = "DeployedMSpinering"; + scale = vectorMultiply(%scaler,1/4 SPC 1/3 SPC 2); + }; + %deplObj2 = new (%className)() { //Bottom add + dataBlock = "DeployedMSpinering"; + scale = vectorMultiply(%scaler,1/4 SPC 1/3 SPC 2); + }; + + %h1=vectorAdd(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm),%mod)); + %h2=vectorAdd(%item.surfacePt,vectorScale(vectorNormalize(%item.surfaceNrm),GetWord(%scale,2)-%mod-0.5)); + + %deplObj1.setTransform(%h1 SPC %rot); + %deplObj2.setTransform(%h2 SPC %rot); + addDSurface(%deplObj,%deplObj1); + %deplObj1.grounded = %grounded; + %deplObj1.needsFit = 1; + addDSurface(%deplObj,%deplObj2); + %deplObj2.grounded = %grounded; + %deplObj2.needsFit = 1; + %deplObj1.team = %plyr.client.team; + %deplObj1.setOwner(%plyr); + %deplObj2.team = %plyr.client.team; + %deplObj2.setOwner(%plyr); + if(%deplObj1.getTarget() != -1) + setTargetSensorGroup(%deplObj2.getTarget(), %plyr.client.team); + if(%deplObj2.getTarget() != -1) + setTargetSensorGroup(%deplObj2.getTarget(), %plyr.client.team); + addToDeployGroup(%deplObj1); + addToDeployGroup(%deplObj2); + AIDeployObject(%plyr.client, %deplObj1); + AIDeployObject(%plyr.client, %deplObj2); + %deplObj.right = %deplObj1; + %deplObj.left = %deplObj2; + } + +//////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + %deplObj.right.powerFreq = %plyr.powerFreq; + %deplObj.left.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // Power object + checkPowerObject(%deplObj); + if (isObject(%deplObj.right)) + checkPowerObject(%deplObj.right); + if (isObject(%deplObj.left)) + checkPowerObject(%deplObj.left); + + if (!%type) + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"mspine"); + else + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"mspine1"); + + return %deplObj; +} + +function DeployedMSpine::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, mspineDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); + if (isObject(%obj.right)) + %obj.right.schedule(500,setDamageState,Destroyed); + if (isObject(%obj.left)) + %obj.left.schedule(500,setDamageState,Destroyed); +} + +function DeployedMSpinering::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function DeployedMSpine::disassemble(%data,%plyr,%hTgt) { + if ($Host::Purebuild == 1) { // Remove console spam + if (isObject(%hTgt.right)) + %hTgt.right.getDataBlock().schedule(500,disassemble,0,%hTgt.right); + if (isObject(%hTgt.left)) + %hTgt.left.getDataBlock().schedule(500,disassemble,0,%hTgt.left); + } + disassemble(%data,%plyr,%hTgt); +} + +function mspineDeployableImage::onMount(%data,%obj,%node) { + %obj.hasMSpine = true; // set for mspinecheck + %obj.packSet = 0; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function mspineDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasMSpine = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} diff --git a/Scripts/Packs/parachutepack.cs b/Scripts/Packs/parachutepack.cs new file mode 100644 index 0000000..aa5301d --- /dev/null +++ b/Scripts/Packs/parachutepack.cs @@ -0,0 +1,105 @@ +// ------------------------------------------------------------------ +// PARACHUTE PACK +// + +datablock ShapeBaseImageData(ParachutePackImage) +{ + shapeFile = "pack_upgrade_ammo.dts"; + item = ParachutePack; + mountPoint = 1; + offset = "0 0 0"; + mass = 10; + + usesEnergy = true; + minEnergy = 3; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateSequence[1] = "fire"; + stateSound[1] = CloakingPackActivateSound; + stateEnergyDrain[1] = 1; + stateTransitionOnTriggerUp[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "Deactivate"; + + stateName[2] = "Deactivate"; + stateScript[2] = "onDeactivate"; + stateTransitionOnTimeout[2] = "Idle"; +}; + +datablock ItemData(ParachutePack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_ammo.dts"; + mass = 5.0; + elasticity = 0.5; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "ParachutePackImage"; + pickUpName = "a Parachute pack"; + + computeCRC = true; +}; + +datablock ShapeBaseImageData(parachuteImage) +{ +shapeFile = "Pmiscf.dts"; +mountPoint = 1; + +offset = "0.0 0.0 2.0"; // L/R - F/B - T/B +rotation = "0 0 0 0"; // L/R - F/B - T/B +}; + +function ParachutePackImage::onUnmount(%data, %obj, %node) +{ + if (%obj.getState() !$= "dead"){ + Cancel(%obj.ParaLoop); + %obj.unmountImage(4); + } +} + +function ParachutePackImage::onActivate(%data, %obj, %slot) +{ + if(%obj.getArmorSize() $= "Light") + { + messageClient(%obj.client, "", "\c2Can't use this with Technician."); + return; + } + + messageClient(%obj.client, 'MsgParachuteOpened', '\c2Parachute opened.'); + %newspeed = vectorscale(%obj.getvelocity(),0.5); + %obj.setvelocity(%newspeed); + %obj.paraLoop = schedule(1000, 0, "parachuteLoop", %obj); + %obj.mountImage(ParachuteImage, 4); +} + +function ParachutePackImage::onDeactivate(%data, %obj, %slot) +{ + if(%obj.getArmorSize() $= "Light") + { + messageClient(%obj.client, "", "\c2Can't use this with Technician."); + return; + } + + messageClient(%obj.client, 'MsgParachuteClosed', '\c2Parachute Closed.'); + Cancel(%obj.ParaLoop); + %obj.unmountImage(4); +} + +function parachuteLoop(%obj) +{ + if(isObject(%obj)) + { + %vec = vectornormalize(%obj.getMuzzleVector($WeaponSlot)); + %move = vectorscale(%vec,10); + %x = getword(%move,0); + %y = getword(%move,1); + %z = (getword(%move,2) - 15); + %obj.setvelocity(%x@" "@%y@" "@%z); + %obj.paraLoop = schedule(100, 0, "parachuteLoop", %obj); + } +} diff --git a/Scripts/Packs/repairpack.cs b/Scripts/Packs/repairpack.cs new file mode 100644 index 0000000..1cb23ef --- /dev/null +++ b/Scripts/Packs/repairpack.cs @@ -0,0 +1,650 @@ +//-------------------------------------------------------------------------- +// Repair Pack +// can be used by any armor type +// when activated, gives user a "repair gun" that can be used to +// repair a damaged object or player. If there is no target in +// range for the repair gun, the user is repaired. + +//-------------------------------------------------------------------------- +// Sounds & feedback effects + +datablock EffectProfile(RepairPackActivateEffect) +{ + effectname = "packs/packs.repairPackOn"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(RepairPackFireEffect) +{ + effectname = "packs/repair_use"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(RepairPackActivateSound) +{ + filename = "fx/packs/packs.repairPackOn.wav"; + description = AudioClosest3d; + preload = true; + effect = RepairPackActivateEffect; +}; + +datablock AudioProfile(RepairPackFireSound) +{ + filename = "fx/packs/repair_use.wav"; + description = CloseLooping3d; + preload = true; + effect = RepairPackFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile + +datablock RepairProjectileData(DefaultRepairBeam) +{ + sound = RepairPackFireSound; + +// JTL + beamRange = 10; //10 +// End JTL + beamWidth = 0.15; + numSegments = 20; + texRepeat = 0.20; + blurFreq = 10.0; + blurLifetime = 1.0; + cutoffAngle = 25.0; + + textures[0] = "special/redbump2"; + textures[1] = "special/redflare"; + +}; + + +//------------------------------------------------------------------------- +// shapebase datablocks + +datablock ShapeBaseImageData(RepairPackImage) +{ + shapeFile = "pack_upgrade_repair.dts"; + item = RepairPack; + mountPoint = 1; + offset = "0 0 0"; + emap = true; + + gun = RepairGunImage; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateSequence[1] = "fire"; + stateSound[1] = RepairPackActivateSound; + stateTransitionOnTriggerUp[1] = "Deactivate"; + + stateName[2] = "Deactivate"; + stateScript[2] = "onDeactivate"; + stateTransitionOnTimeout[2] = "Idle"; +}; + +datablock ItemData(RepairPack) +{ + className = Pack; + catagory = "Packs"; + shapeFile = "pack_upgrade_repair.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + image = "RepairPackImage"; + pickUpName = "a repair pack"; + + lightOnlyStatic = true; + lightType = "PulsingLight"; + lightColor = "1 0 0 1"; + lightTime = 1200; + lightRadius = 4; + + computeCRC = true; + emap = true; +}; + +//-------------------------------------------------------------------------- +// Repair Gun + +datablock ShapeBaseImageData(RepairGunImage) +{ + shapeFile = "weapon_repair.dts"; + offset = "0 0 0"; + + usesEnergy = true; + minEnergy = 0; + cutOffEnergy = 5.0; + emap = true; + + repairFactorPlayer = 0.002; // <--- attention DaveG! + repairFactorObject = 0.008; // <--- attention DaveG! + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.25; + + stateName[1] = "ActivateReady"; + stateScript[1] = "onActivateReady"; + stateSpinThread[1] = Stop; + stateTransitionOnAmmo[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "ActivateReady"; + + stateName[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnNoAmmo[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Validate"; + + stateName[3] = "Validate"; + stateTransitionOnTimeout[3] = "Validate"; + stateTimeoutValue[3] = 0.2; + stateEnergyDrain[3] = 3; + stateSpinThread[3] = SpinUp; + stateScript[3] = "onValidate"; + stateIgnoreLoadedForReady[3] = true; + stateTransitionOnLoaded[3] = "Repair"; + stateTransitionOnNoAmmo[3] = "Deactivate"; + stateTransitionOnTriggerUp[3] = "Deactivate"; + + stateName[4] = "Repair"; + stateSound[4] = RepairPackFireSound; + stateScript[4] = "onRepair"; + stateSpinThread[4] = FullSpeed; + stateAllowImageChange[4] = false; + stateSequence[4] = "activate"; + stateFire[4] = true; + stateEnergyDrain[4] = 32; + stateTimeoutValue[4] = 0.2; + stateTransitionOnTimeOut[4] = "Repair"; + stateTransitionOnNoAmmo[4] = "Deactivate"; + stateTransitionOnTriggerUp[4] = "Deactivate"; + stateTransitionOnNotLoaded[4] = "Validate"; + + stateName[5] = "Deactivate"; + stateScript[5] = "onDeactivate"; + stateSpinThread[5] = SpinDown; + stateSequence[5] = "activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 0.2; + stateTransitionOnTimeout[5] = "ActivateReady"; +}; + +function RepairPackImage::onUnmount(%data, %obj, %node) +{ + // dismount the repair gun if the player had it mounted + // need the extra "if" statement to avoid a console error message + if(%obj.getMountedImage($WeaponSlot)) + if(%obj.getMountedImage($WeaponSlot).getName() $= "RepairGunImage") + %obj.unmountImage($WeaponSlot); + // if the player was repairing something when the pack was thrown, stop repairing it + if(%obj.repairing != 0) + stopRepairing(%obj); +} + +function RepairPackImage::onActivate(%data, %obj, %slot) +{ + // don't activate the pack if player is piloting a vehicle + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'scoreScreen' ); + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'inventoryScreen' ); + + commandToClient(%obj.triggeredBy.client, 'StationVehicleShowHud'); + + if(%obj.isPilot()) + { + %obj.setImageTrigger(%slot, false); + return; + } + + if(!isObject(%obj.getMountedImage($WeaponSlot)) || %obj.getMountedImage($WeaponSlot).getName() !$= "RepairGunImage") + { + messageClient(%obj.client, 'MsgRepairPackOn', '\c2Repair pack activated.'); + + // make sure player's arm thread is "look" + %obj.setArmThread(look); + + // mount the repair gun + %obj.mountImage(RepairGunImage, $WeaponSlot); + // clientCmdsetRepairReticle found in hud.cs + commandToClient(%obj.client, 'setRepairReticle'); + } +} + +function RepairPackImage::onDeactivate(%data, %obj, %slot) +{ + //called when the player hits the "pack" key again (toggle) + %obj.setImageTrigger(%slot, false); + // if repair gun was mounted, unmount it + if(%obj.getMountedImage($WeaponSlot).getName() $= "RepairGunImage") + %obj.unmountImage($WeaponSlot); +} + +function RepairGunImage::onMount(%this,%obj,%slot) +{ + %obj.setImageAmmo(%slot,true); + if ( !isDemo() ) + commandToClient( %obj.client, 'setRepairPackIconOn' ); +} + +function RepairGunImage::onUnmount(%this,%obj,%slot) +{ + // called when player switches to another weapon + + // stop repairing whatever player was repairing + if(%obj.repairing) + stopRepairing(%obj); + + %obj.setImageTrigger(%slot, false); + // "turn off" the repair pack -- player needs to hit the "pack" key to + // activate the repair gun again + %obj.setImageTrigger($BackpackSlot, false); + if ( !isDemo() ) + commandToClient( %obj.client, 'setRepairPackIconOff' ); +} + +function RepairGunImage::onActivateReady(%this,%obj,%slot) +{ + %obj.errMsgSent = false; + %obj.selfRepairing = false; + %obj.repairing = 0; + %obj.setImageLoaded(%slot, false); +} + +function RepairGunImage::onValidate(%this,%obj,%slot) +{ + // this = repairgunimage datablock + // obj = player wielding the repair gun + // slot = weapon slot + + if(%obj.getEnergyLevel() <= %this.cutOffEnergy) + { + stopRepairing(%obj); + return; + } + %repGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the repair gun's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %repairRange = DefaultRepairBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %repairRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if(%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) + { + // a target in range was found + %repTgt = firstWord(%scanTarg); + // is the prospective target damaged? + if(%repTgt.notRepairable) + { + // this is an object that cant be repaired at all + // -- mission specific flag set on the object + if(!%obj.errMsgSent) + { + messageClient(%obj.client, 'MsgRepairPackIrrepairable', '\c2Target is not repairable.', %repTgt); + %obj.errMsgSent = true; + } + // if player was repairing something, stop the repairs -- we're done + if(%obj.repairing) + stopRepairing(%obj); + } + else if(%repTgt.getDamageLevel()) + { + // yes, it's damaged + if(%repTgt != %obj.repairing) + { + if(isObject(%obj.repairing)) + stopRepairing(%obj); + + %obj.repairing = %repTgt; + } + // setting imageLoaded to true sends us to repair state (function onRepair) + %obj.setImageLoaded(%slot, true); + } + else + { + // there is a target in range, but it's not damaged + if(!%obj.errMsgSent) + { + // if the target isn't damaged, send a message to that effect only once + // JTL - repair ourselves if target is not damaged + if(%obj.getDamageLevel()) { + if(%obj.repairing != 0) + if(%obj.repairing != %obj) + stopRepairing(%obj); + if(isObject(%obj.repairing)) + stopRepairing(%obj); + + %obj.repairing = %obj; + // quick, to onRepair! + %obj.setImageLoaded(%slot, true); + return; + } + else { + messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2Target is not damaged.', %repTgt); + %obj.errMsgSent = true; + } + } + // if player was repairing something, stop the repairs -- we're done + if(%obj.repairing) + stopRepairing(%obj); + } + } + + //AI hack - too many things influence the aiming, so I'm going to force the repair object for bots only + else if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject)) + { + %repTgt = %obj.client.repairObject; + %repPoint = %repTgt.getAIRepairPoint(); + if (%repPoint $= "0 0 0") + %repPoint = %repTgt.getWorldBoxCenter(); + %repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint)); + %aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd)); + + //if the dot product is very close (ie. we're aiming in the right direction) + if (VectorDot(%repTgtVector, %aimVector) > 0.85) + { + //do an LOS to make sure nothing is in the way... + %scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj); + if (firstWord(%scanTarg) == %repTgt) + { + // yes, it's damaged + + if(isObject(%obj.repairing)) + stopRepairing(%obj); + + %obj.repairing = %repTgt; + // setting imageLoaded to true sends us to repair state (function onRepair) + %obj.setImageLoaded(%slot, true); + } + } + } + else if(%obj.getDamageLevel()) + { + // there is no target in range, but the player is damaged + // check to see if we were repairing something before -- if so, stop repairing old target + if(%obj.repairing != 0) + if(%obj.repairing != %obj) + stopRepairing(%obj); + if(isObject(%obj.repairing)) + stopRepairing(%obj); + + %obj.repairing = %obj; + // quick, to onRepair! + %obj.setImageLoaded(%slot, true); + } + else + { + // there is no target in range, and the player isn't damaged + if(!%obj.errMsgSent) + { + // send an error message only once + messageClient(%obj.client, 'MsgRepairPackNoTarget', '\c2No target to repair.'); + %obj.errMsgSent = true; + } + stopRepairing(%obj); + } +} + +function RepairGunImage::onRepair(%this,%obj,%slot) +{ + // this = repairgunimage datablock + // obj = player wielding the repair gun + // slot = weapon slot + + if(%obj.getEnergyLevel() <= %this.cutOffEnergy) + { + stopRepairing(%obj); + return; + } + // reset the flag that indicates an error message has been sent + %obj.errMsgSent = false; + %target = %obj.repairing; + if(!%target) + { + // no target -- whoops! never mind + stopRepairing(%obj); + } + else + { + %target.repairedBy = %obj.client; //keep track of who last repaired this item + if(%obj.repairing == %obj) + { + // player is self-repairing + if(%obj.getDamageLevel()) + { + if(!%obj.selfRepairing) + { + // no need for a projectile, just send a message and up the repair rate + messageClient(%obj.client, 'MsgRepairPackPlayerSelfRepair', '\c2Repairing self.'); + %obj.selfRepairing = true; + startRepairing(%obj, true); + } + } + else + { + messageClient(%obj.client, 'MsgRepairPackSelfDone', '\c2Repairs completed on self.'); + stopRepairing(%obj); + %obj.errMsgSent = true; + } + } + else + { + // make sure we still have a target -- more vector fun!!! + %muzVec = %obj.getMuzzleVector(%slot); + %muzNVec = VectorNormalize(%muzVec); + %repairRange = DefaultRepairBeam.beamRange; + %muzScaled = VectorScale(%muzNVec, %repairRange); + %muzPoint = %obj.getMuzzlePoint(%slot); + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + + %searchMasks = $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType; + + //AI hack to help "fudge" the repairing stuff... + if (%obj.client.isAIControlled() && isObject(%obj.client.repairObject) && %obj.client.repairObject == %obj.repairing) + { + %repTgt = %obj.client.repairObject; + %repPoint = %repTgt.getAIRepairPoint(); + if (%repPoint $= "0 0 0") + %repPoint = %repTgt.getWorldBoxCenter(); + %repTgtVector = VectorNormalize(VectorSub(%muzPoint, %repPoint)); + %aimVector = VectorNormalize(VectorSub(%muzPoint, %rangeEnd)); + + //if the dot product is very close (ie. we're aiming in the right direction) + if (VectorDot(%repTgtVector, %aimVector) > 0.85) + %scanTarg = ContainerRayCast(%muzPoint, %repPoint, %searchMasks, %obj); + } + else + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + + if (%scanTarg) + { + %pos = getWords(%scanTarg, 1, 3); + %obstructMask = $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %obstruction = ContainerRayCast(%muzPoint, %pos, %obstructMask, %obj); + if (%obstruction) + %scanTarg = "0"; + } + + if(%scanTarg) + { + // there's still a target out there + %repTgt = firstWord(%scanTarg); + // is the target damaged? + if(%repTgt.getDamageLevel()) + { + if(%repTgt != %obj.repairing) + { + // the target is not the same as the one we were just repairing + // stop repairing old target, start repairing new target + stopRepairing(%obj); + if(isObject(%obj.repairing)) + stopRepairing(%obj); + + %obj.repairing = %repTgt; + // extract the name of what player is repairing based on what it is + // if it's a player, it's the player's name (duh) + // if it's an object, look for a nametag + // if object has no nametag, just say what it is (e.g. generatorLarge) + if(%repTgt.getClassName() $= Player) + %tgtName = getTaggedString(%repTgt.client.name); + else if(%repTgt.getGameName() !$= "") + %tgtName = %repTgt.getGameName(); + else + %tgtName = %repTgt.getDatablock().getName(); + messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt); + startRepairing(%obj, false); + } + else + { + // it's the same target as last time + // changed to fix "2 players can't repair same object" bug + if(%obj.repairProjectile == 0) + { + if(%repTgt.getClassName() $= Player) + %tgtName = getTaggedString(%repTgt.client.name); + else if(%repTgt.getGameName() !$= "") + %tgtName = %repTgt.getGameName(); + else + %tgtName = %repTgt.getDatablock().getName(); + messageClient(%obj.client, 'MsgRepairPackRepairingObj', '\c2Repairing %1.', %tgtName, %repTgt); + startRepairing(%obj, false); + } + } + } + else + { + %rateOfRepair = %this.repairFactorObject; + if(%repTgt.getClassName() $= Player) + { + %tgtName = getTaggedString(%repTgt.client.name); + %rateOfRepair = %this.repairFactorPlayer; + } + else if(%repTgt.getGameName() !$= "") + %tgtName = %repTgt.getGameName(); + else + %tgtName = %repTgt.getDatablock().getName(); + if(%repTgt != %obj.repairing) + { + // it isn't the same object we were repairing previously + messageClient(%obj.client, 'MsgRepairPackNotDamaged', '\c2%1 is not damaged.', %tgtName); + } + else + { + // same target, but not damaged -- we must be done + messageClient(%obj.client, 'MsgRepairPackDone', '\c2Repairs completed.'); + Game.objectRepaired(%repTgt, %tgtName); + } + %obj.errMsgSent = true; + stopRepairing(%obj); + } + } + else + { + // whoops, we lost our target + messageClient(%obj.client, 'MsgRepairPackLostTarget', '\c2Repair target no longer in range.'); + stopRepairing(%obj); + } + } + } +} + +function RepairGunImage::onDeactivate(%this,%obj,%slot) +{ + stopRepairing(%obj); +} + +function stopRepairing(%player) +{ + // %player = the player who was using the repair pack + + if(%player.selfRepairing) + { + // there is no projectile for self-repairing + %player.setRepairRate(%player.getRepairRate() - %player.repairingRate); + %player.selfRepairing = false; + } + else if(%player.repairing > 0) + { + // player was repairing something else + //if(%player.repairing.beingRepaired > 0) + //{ + // don't decrement this stuff if it's already at 0 -- though it shouldn't be + //%player.repairing.beingRepaired--; + %player.repairing.setRepairRate(%player.repairing.getRepairRate() - %player.repairingRate); + //} + if(%player.repairProjectile > 0) + { + // is there a repair projectile? delete it + %player.repairProjectile.delete(); + %player.repairProjectile = 0; + } + } + %player.repairing = 0; + %player.repairingRate = 0; + %player.setImageTrigger($WeaponSlot, false); + %player.setImageLoaded($WeaponSlot, false); +} + +function startRepairing(%player, %self) +{ + // %player = the player who was using the repair pack + // %self = boolean -- is player repairing him/herself? + + if(%self) + { + // one repair, hold the projectile + %player.setRepairRate(%player.getRepairRate() + RepairGunImage.repairFactorPlayer); + %player.selfRepairing = true; + %player.repairingRate = RepairGunImage.repairFactorPlayer; + } + else + { + //if(%player.repairing.beingRepaired $= "") + // %player.repairing.beingRepaired = 1; + //else + // %player.repairing.beingRepaired++; + + //AI hack... + if (%player.client.isAIControlled() && %player.client.repairObject == %player.repairing) + { + %initialPosition = %player.getMuzzlePoint($WeaponSlot); + %initialDirection = VectorSub(%initialPosition, %player.repairing.getWorldBoxCenter()); + } + else + { + %initialDirection = %player.getMuzzleVector($WeaponSlot); + %initialPosition = %player.getMuzzlePoint($WeaponSlot); + } + if(%player.repairing.getClassName() $= Player) + %repRate = RepairGunImage.repairFactorPlayer; + else + %repRate = RepairGunImage.repairFactorObject; + %player.repairing.setRepairRate(%player.repairing.getRepairRate() + %repRate); + + %player.repairingRate = %repRate; + %player.repairProjectile = new RepairProjectile() { + dataBlock = DefaultRepairBeam; + initialDirection = %initialDirection; + initialPosition = %initialPosition; + sourceObject = %player; + sourceSlot = $WeaponSlot; + targetObject = %player.repairing; + }; + MissionCleanup.add(%player.repairProjectile); + } +} diff --git a/Scripts/Packs/satchelCharge.cs b/Scripts/Packs/satchelCharge.cs new file mode 100644 index 0000000..7949c41 --- /dev/null +++ b/Scripts/Packs/satchelCharge.cs @@ -0,0 +1,751 @@ +//-------------------------------------------------------------------------- +// Satchel Charge pack +// can be used by any armor type +// when activated, throws the pack -- when activated again (before +// picking up another pack), detonates with a BIG explosion. + +// Set up defaults for nonexisting vars + +$SatchelChargeMultiplier = 1; + +//-------------------------------------------------------------------------- +// Sounds + +datablock EffectProfile(SatchelChargeActivateEffect) +{ + effectname = "packs/satchel_pack_activate"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(SatchelChargeExplosionEffect) +{ + effectname = "packs/satchel_pack_detonate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(SatchelChargePreExplosionEffect) +{ + effectname = "explosions/explosion.xpl03"; + minDistance = 10.0; + maxDistance = 30.0; +}; + +datablock AudioProfile(SatchelChargeActivateSound) +{ + filename = "fx/packs/satchel_pack_activate.wav"; + description = AudioClose3d; + preload = true; + effect = SatchelChargeActivateEffect; +}; + +datablock AudioProfile(SatchelChargeExplosionSound) +{ + filename = "fx/packs/satchel_pack_detonate.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = SatchelChargeExplosionEffect; +}; + +datablock AudioProfile(SatchelChargePreExplosionSound) +{ + filename = "fx/explosions/explosion.xpl03.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = SatchelChargePreExplosionEffect; +}; + +datablock AudioProfile(UnderwaterSatchelChargeExplosionSound) +{ + filename = "fx/weapons/mortar_explode_UW.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = SatchelChargeExplosionEffect; +}; + +//---------------------------------------------------------------------------- +// Satchel Debris +//---------------------------------------------------------------------------- +datablock ParticleData( SDebrisSmokeParticle ) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + colors[0] = "0.4 0.4 0.4 1.0"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.0; + sizes[1] = 2.0; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( SDebrisSmokeEmitter ) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 1; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "SDebrisSmokeParticle"; +}; + + +datablock DebrisData( SatchelDebris ) +{ + emitters[0] = SDebrisSmokeEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 0.3; + lifetimeVariance = 0.02; +}; + +//---------------------------------------------------------------------------- +// Bubbles +//---------------------------------------------------------------------------- +datablock ParticleData(SatchelBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 2.0; + sizes[1] = 2.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.8; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(SatchelBubbleEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 7.0; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "MortarExplosionBubbleParticle"; +}; + +//-------------------------------------------------------------------------- +// Satchel Explosion Particle effects +//-------------------------------------- +datablock ParticleData(SatchelExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.0; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 2000; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.0 1.0"; + colors[1] = "0.2 0.2 0.2 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 7.0; + sizes[1] = 17.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.4; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(SatchelExplosionSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 14.25; + velocityVariance = 2.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "SatchelExplosionSmoke"; +}; + +datablock ParticleData(UnderwaterSatchelExplosionSmoke) +{ + dragCoeffiecient = 105.0; + gravityCoefficient = -0.0; + inheritedVelFactor = 0.025; + + constantAcceleration = -1.0; + + lifetimeMS = 1500; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.4 0.4 1.0 1.0"; + colors[1] = "0.4 0.4 1.0 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 7.0; + sizes[1] = 17.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(UnderwaterSatchelExplosionSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 14.25; + velocityVariance = 2.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "UnderwaterSatchelExplosionSmoke"; +}; + + +datablock ParticleData(SatchelSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 150; + textureName = "special/bigSpark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.75; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(SatchelSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 40; + velocityVariance = 20.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 200; + particles = "SatchelSparks"; +}; + +datablock ParticleData(UnderwaterSatchelSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/underwaterSpark"; + colors[0] = "0.6 0.6 1.0 1.0"; + colors[1] = "0.6 0.6 1.0 1.0"; + colors[2] = "0.6 0.6 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.75; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterSatchelSparksEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + ejectionVelocity = 30; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 70; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "UnderwaterSatchelSparks"; +}; + + +//--------------------------------------------------------------------------- +// Explosion +//--------------------------------------------------------------------------- + +datablock ExplosionData(SatchelSubExplosion) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "0.5 0.5 0.5"; + + debris = SatchelDebris; + debrisThetaMin = 10; + debrisThetaMax = 80; + debrisNum = 8; + debrisVelocity = 60.0; + debrisVelocityVariance = 15.0; + + lifetimeMS = 1000; + delayMS = 0; + + emitter[0] = SatchelExplosionSmokeEmitter; + emitter[1] = SatchelSparksEmitter; + + offset = 0.0; + + playSpeed = 1.5; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "3.0 3.0 3.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ExplosionData(SatchelSubExplosion2) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "0.7 0.7 0.7"; + + debris = SatchelDebris; + debrisThetaMin = 10; + debrisThetaMax = 170; + debrisNum = 8; + debrisVelocity = 60.0; + debrisVelocityVariance = 15.0; + + lifetimeMS = 1000; + delayMS = 50; + + emitter[0] = SatchelExplosionSmokeEmitter; + emitter[1] = SatchelSparksEmitter; + + offset = 9.0; + + playSpeed = 1.5; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "1.5 1.5 1.5"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ExplosionData(SatchelSubExplosion3) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + debris = SatchelDebris; + debrisThetaMin = 10; + debrisThetaMax = 170; + debrisNum = 8; + debrisVelocity = 60.0; + debrisVelocityVariance = 15.0; + + lifetimeMS = 2000; + delayMS = 100; + + emitter[0] = SatchelExplosionSmokeEmitter; + emitter[1] = SatchelSparksEmitter; + + offset = 9.0; + + playSpeed = 2.5; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ExplosionData(SatchelMainExplosion) +{ + soundProfile = SatchelChargePreExplosionSound; + + subExplosion[0] = SatchelSubExplosion; + subExplosion[1] = SatchelSubExplosion2; + subExplosion[2] = SatchelSubExplosion3; +}; + +//--------------------------------------------------------------------------- +// Underwater Explosion +//--------------------------------------------------------------------------- + +datablock ExplosionData(UnderwaterSatchelSubExplosion) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "0.5 0.5 0.5"; + + + lifetimeMS = 1000; + delayMS = 0; + + emitter[0] = UnderwaterSatchelExplosionSmokeEmitter; + emitter[1] = UnderwaterSatchelSparksEmitter; + emitter[2] = SatchelBubbleEmitter; + + offset = 0.0; + + playSpeed = 0.75; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "2.5 2.5 2.5"; + sizes[2] = "2.0 2.0 2.0"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ExplosionData(UnderwaterSatchelSubExplosion2) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "0.7 0.7 0.7"; + + + lifetimeMS = 1000; + delayMS = 50; + + emitter[0] = UnderwaterSatchelExplosionSmokeEmitter; + emitter[1] = UnderwaterSatchelSparksEmitter; + emitter[2] = SatchelBubbleEmitter; + + offset = 9.0; + + playSpeed = 0.75; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "1.0 1.0 1.0"; + sizes[2] = "0.75 0.75 0.75"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ExplosionData(UnderwaterSatchelSubExplosion3) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + + lifetimeMS = 2000; + delayMS = 100; + + emitter[0] = UnderwaterSatchelExplosionSmokeEmitter; + emitter[1] = UnderwaterSatchelSparksEmitter; + emitter[2] = SatchelBubbleEmitter; + + offset = 9.0; + + playSpeed = 1.25; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "1.0 1.0 1.0"; + sizes[2] = "0.5 0.5 0.5"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ExplosionData(UnderwaterSatchelMainExplosion) +{ + soundProfile = UnderwaterSatchelChargeExplosionSound; + + subExplosion[0] = UnderwaterSatchelSubExplosion; + subExplosion[1] = UnderwaterSatchelSubExplosion2; + subExplosion[2] = UnderwaterSatchelSubExplosion3; +}; + + +//-------------------------------------------------------------------------- +// Projectile + +//------------------------------------------------------------------------- +// shapebase datablocks +datablock ShapeBaseImageData(SatchelChargeImage) +{ + shapeFile = "pack_upgrade_satchel.dts"; + item = SatchelCharge; + mountPoint = 1; + offset = "0 0 0"; + emap = true; + mass = 16; +}; + +datablock ItemData(SatchelCharge) +{ + className = Pack; + catagory = "Packs"; + image = SatchelChargeImage; + shapeFile = "pack_upgrade_satchel.dts"; + mass = 8.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + pickUpName = "a satchel charge pack"; + + computeCRC = true; +}; + +datablock ItemData(SatchelChargeThrown) +{ + shapeFile = "pack_upgrade_satchel.dts"; + explosion = SatchelMainExplosion; + underwaterExplosion = UnderwaterSatchelMainExplosion; + mass = 1.5; + elasticity = 0.1; + friction = 0.9; + rotate = false; + pickupRadius = 0; + noTimeout = true; + armDelay = 1000; + maxDamage = 2.5; + + kickBackStrength = 5000; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- + +function SatchelCharge::onUse(%this, %obj) +{ + if (!$Host::SatchelChargeEnabled) { + if ($Host::Purebuild == 1) + messageAll('msgClient','\c2%1 just tried to drop a Satchel Charge!',%obj.client.name); + else + messageTeam(%obj.client.team,'msgClient','\c2%1 just tried to drop a Satchel Charge!',%obj.client.name); + %obj.decInventory(SatchelCharge, 1); + return; + } + if ($Host::Purebuild == 1) + messageAll('msgClient','\c2%1 just dropped a Satchel Charge!',%obj.client.name); + else + messageTeam(%obj.client.team,'msgClient','\c2%1 just dropped a Satchel Charge!',%obj.client.name); + %item = new Item() { + dataBlock = SatchelChargeThrown; + rotation = "0 0 1 " @ (getRandom() * 360); + scale = $SatchelChargeMultiplier SPC $SatchelChargeMultiplier SPC $SatchelChargeMultiplier; + }; + if ($Host::Purebuild == 0) + %item = new Item() { + dataBlock = SatchelChargeThrown; + rotation = "0 0 1 " @ (getRandom() * 360); + scale = $SatchelChargeMultiplier SPC $SatchelChargeMultiplier SPC $SatchelChargeMultiplier; + }; + MissionCleanup.add(%item); + // take pack out of inventory and unmount image + %obj.decInventory(SatchelCharge, 1); + %obj.throwObject(%item); + //error("throwing satchel charge #" @ %item); + %obj.thrownChargeId = %item; + %item.sourceObject = %obj; + %item.armed = false; + %item.damaged = 0.0; + %item.thwart = false; + // arm itself 3 seconds after being thrown + schedule(%item.getDatablock().armDelay, %item, "initArmSatchelCharge", %item); + messageClient(%obj.client, 'MsgSatchelChargePlaced', "\c2Satchel charge deployed."); +} + +function initArmSatchelCharge(%satchel) +{ + // "deet deet deet" sound + %satchel.playAudio(1, SatchelChargeActivateSound); + // also need to play "antenna extending" animation + %satchel.playThread(0, "deploy"); + %satchel.playThread(1, "activate"); + + // delay the actual arming until after sound is done playing + schedule( 2200, 0, "armSatchelCharge", %satchel ); +} + +function armSatchelCharge(%satchel) +{ + %satchel.armed = true; + commandToClient( %satchel.sourceObject.client, 'setSatchelArmed' ); +} + +function detonateSatchelCharge(%player) +{ + %satchelCharge = %player.thrownChargeId; + // can't detonate the satchel charge if it isn't armed + if(!%satchelCharge.armed) + return; + + //error("Detonating satchel charge #" @ %satchelCharge @ " for player #" @ %player); + + if(%satchelCharge.getDamageState() !$= Destroyed) + { + %satchelCharge.setDamageState(Destroyed); + %satchelCharge.blowup(); + } + + // Clear the player's HUD: + if (isObject(%player.client)) + %player.client.clearBackpackIcon(); +} + +function SatchelChargeThrown::onEnterLiquid(%data, %obj, %coverage, %type) +{ + // lava types + if(%type >=4 && %type <= 6) + { + if(%obj.getDamageState() !$= "Destroyed") + { + %obj.armed = true; + detonateSatchelCharge(%obj.sourceObject); + return; + } + } + + // quickSand + if(%type == 7) + if(isObject(%obj.sourceObject)) + %obj.sourceObject.thrownChargeId = 0; + + Parent::onEnterLiquid(%data, %obj, %coverage, %type); +} + +function SatchelChargeImage::onMount(%data, %obj, %node) +{ + %obj.thrownChargeId = 0; +} + +function SatchelChargeThrown::onDestroyed(%this, %object, %lastState) +{ + if(%object.kaboom) + return; + else + { + %object.kaboom = true; + + // the "thwart" flag is set if the charge is destroyed with weapons rather + // than detonated. A less damaging explosion, but visually the same scale. + if(%object.thwart) + { + messageClient(%object.sourceObject.client, 'msgSatchelChargeDetonate', "\c2Satchel charge destroyed."); + %dmgRadius = 23 * $SatchelChargeMultiplier; + %dmgMod = 2.0 * $SatchelChargeMultiplier; + %expImpulse = limitSatchelImpulse(1000 * $SatchelChargeMultiplier); + %dmgType = $DamageType::SatchelCharge; + } + else + { + messageClient(%object.sourceObject.client, 'msgSatchelChargeDetonate', "\c2Satchel charge detonated!"); + %dmgRadius = 30 * $SatchelChargeMultiplier; + %dmgMod = 3.0 * $SatchelChargeMultiplier; + %expImpulse = limitSatchelImpulse(2500 * $SatchelChargeMultiplier); + %dmgType = $DamageType::SatchelCharge; + } + + %object.blowingUp = true; + RadiusExplosion(%object, %object.getPosition(), %dmgRadius, %dmgMod, %expImpulse, %object.sourceObject, %dmgType); + + %object.schedule(1000, "delete"); + } + + // ------------------------------------------------------------------- + // z0dd - ZOD, 5/8/02. Addition. Satchel bug fix. Prior to fix, + // clients couldn't pick up packs when satchel was destroyed from dmg. + if(isObject(%object.sourceObject)) + %object.sourceObject.thrownChargeId = 0; +} + +function SatchelChargeThrown::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) +{ + if (!%object.blowingUp) + { + %targetObject.damaged += %amount; + + if(%targetObject.damaged >= %targetObject.getDataBlock().maxDamage && + %targetObject.getDamageState() !$= Destroyed) + { + %targetObject.thwart = true; + %targetObject.setDamageState(Destroyed); + %targetObject.blowup(); + + // clear the player's HUD: + if (isObject(%targetObject.sourceObject.client)) + %targetObject.sourceObject.client.clearBackPackIcon(); + } + } +} + +function limitSatchelImpulse(%val) { + if (%val > 50000) + %val = 50000; + return %val; +} diff --git a/Scripts/Packs/sentryturretpack.cs b/Scripts/Packs/sentryturretpack.cs new file mode 100644 index 0000000..ac41e95 --- /dev/null +++ b/Scripts/Packs/sentryturretpack.cs @@ -0,0 +1,134 @@ +datablock ShapeBaseImageData(SentryTurretDeployableImage) +{ + mass = 15; + emap = true; + + shapeFile = "stackable1s.dts"; + item = TurretSentryPack; + mountPoint = 1; + offset = "0 -0.2 0"; + + minDeployDis = 0.5; + maxDeployDis = 5.0; + + deployed = TurretDeployedSentry; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = StationDeploySound; +}; + +datablock ItemData(TurretSentryPack) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "SentryTurretDeployableImage"; + pickUpName = "a sentry turret pack"; + heatSignature = 0; + + computeCRC = true; + emap = true; +}; + +datablock TurretData(TurretDeployedSentry) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_sentry.dts"; + + rechargeRate = 0.40; + + selfPower = false; + + needsPower = true; + mass = 5.0; + maxDamage = 1.2; + destroyedLevel = 1.2; + disabledLevel = 0.84; + repairRate = 0; + explosion = ShapeExplosion; + expDmgRadius = 5.0; + expDamage = 0.4; + expImpulse = 1000.0; + + deployedObject = true; + + thetaMin = 89; + thetaMax = 175; + + isShielded = true; + energyPerDamagePoint = 100; + maxEnergy = 150; + + heatSignature = 1; + + canControl = true; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Deployed Sentry'; + targetTypeTag = 'Turret'; + sensorData = SentryMotionSensor; + sensorRadius = SentryMotionSensor.detectRadius; + sensorColor = "9 136 255"; + + firstPersonOnly = true; +}; + +function SentryTurretDeployableImage::onDeploy(%item, %plyr, %slot) +{ + %deplObj = Parent::onDeploy(%item, %plyr, %slot); + + %deplObj.mountImage(SentryTurretBarrel, 0, false); + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + + %item.surfacenrm = VectorNormalize(%item.surfacenrm); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj.setTransform(%item.surfacePt SPC %rot); + + MTZAxisReverse(%plyr.client, %deplObj); // Super cheap hack. Makes things REALLY easy, though. + + %realVec = realVec(%deplObj, "0 0 1"); + %toAdd = VectorScale(%realvec, -0.6); + %newPos = VectorAdd(posFromTransform(%deplObj.getTransform()), %toAdd); + %deplObj.setTransform(%newPos SPC %deplObj.getRotation()); + + addDSurface(%item.surface,%deplObj); + addToDeployGroup(%deplObj); + + %deplObj.powerFreq = %plyr.powerFreq; + %deplObj.team = %plyr.team; + + checkPowerObject(%deplObj); +} + +function SentryTurretDeployableImage::onMount(%data, %obj, %node) +{ + displayPowerFreq(%obj); +} + +function TurretSentryPack::onPickup(%this, %obj, %shape, %amount) +{ + // No more spam! +} diff --git a/Scripts/Packs/solarpanel.cs b/Scripts/Packs/solarpanel.cs new file mode 100644 index 0000000..4eec5d1 --- /dev/null +++ b/Scripts/Packs/solarpanel.cs @@ -0,0 +1,143 @@ +//-------------------------------------------------------------------------- +// Deployable Solar Panel +//-------------------------------------------------------------------------- + +datablock ShapeBaseImageData(SolarPanelDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = SolarPanelDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = SolarPanel; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 1.5; + maxDeployDis = 5; +}; + +datablock ItemData(SolarPanelDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + + hasLight = true; + lightType = "PulsingLight"; + lightColor = "0.1 0.8 0.8 1.0"; + lightTime = "1000"; + lightRadius = "3"; + + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + rotate = true; + image = "SolarPanelDeployableImage"; + pickUpName = "a solar panel pack"; + heatSignature = 0; + emap = true; +}; + +function SolarPanelDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function SolarPanelDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function SolarPanelDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = SolarPanel; + deployed = true; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set power + %deplObj.setSelfPowered(); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + // add to power list + $PowerList = listAdd($PowerList,%deplObj,-1); + + return %deplObj; +} + +function SolarPanel::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.deployed && ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon)) { + %obj.isRemoved = true; + %loc = findWord($PowerList,%obj); + if (%loc !$= "") + $PowerList = listDel($PowerList,%loc); + $TeamDeployedCount[%obj.team,SolarPanelDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +function SolarPanelDeployableImage::onMount(%data,%obj,%node) { + %obj.hasGen = true; // set for gencheck + displayPowerFreq(%obj); +} + +function SolarPanelDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasGen = ""; +} diff --git a/Scripts/Packs/spawnpointpack.cs b/Scripts/Packs/spawnpointpack.cs new file mode 100644 index 0000000..9180b2e --- /dev/null +++ b/Scripts/Packs/spawnpointpack.cs @@ -0,0 +1,212 @@ +//--------------------------------------------------------- +// Deployable Telepad +//--------------------------------------------------------- +// Thanks to Krash for the idea to make spawn points power things within a small radius + +datablock ShapeBaseImageData(SpawnPointImage) { + mass = 15; + emap = true; + shapeFile = "stackable1s.dts"; + item = SpawnPointPack; + mountPoint = 1; + offset = "0 0 0"; + deployed = SpawnPoint; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = StationDeploySound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(SpawnPointPack) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "SpawnPointImage"; + pickUpName = "a SpawnPoint pack"; + heatSignature = 0; + joint = "2 2 2"; + computeCRC = true; + emap = true; +}; + +datablock StaticShapeData(SpawnPoint) : StaticShapeDamageProfile { + className = "Generator"; + shapeFile = "station_teleport.dts"; + + maxDamage = 5.00; + destroyedLevel = 5.00; + disabledLevel = 4.0; + + isShielded = true; + energyPerDamagePoint = 150; + maxEnergy = 150; + rechargeRate = 1; + + explosion = ShapeExplosion; // DeployablesExplosion; + expDmgRadius = 18.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StationObjectType; + deployedObject = true; + cmdCategory = "Support"; + cmdIcon = CMDSwitchIcon; + cmdMiniIconName = "commander/MiniIcons/com_switch_grey"; + targetNameTag = 'Deployed'; + targetTypeTag = 'Spawn Point'; + + debrisShapeName = "debris_generic.dts"; + debris = DeployableDebris; + + heatSignature = 0; +// needsPower = true; + +// humSound = SensorHumSound; +// pausePowerThread = true; + sensorData = TelePadBaseSensorObj; + sensorRadius = TelePadBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + firstPersonOnly = true; + + lightType = "PulsingLight"; + lightColor = "0 1 0 1"; + lightTime = 1200; + lightRadius = 6; + powerRadius = 25; +}; + +function SpawnPoint::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; +// checkEndTCCMGame(%obj.team); + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team,SpawnPoint]--; + %obj.isRemoved = true; + remDSurface(%obj); + %obj.active = 0; + + remTeamSpawnPoint(%obj); + + if(!$Host::InvincibleDeployables) + %obj.schedule(500,"delete"); +} + +function SpawnPoint::disassemble(%data,%plyr,%obj) { +// checkEndTCCMGame(%obj.team); + remTeamSpawnPoint(%obj); + parent::disassemble(%data, %plyr, %obj); +} + +function SpawnPointPack::onPickup(%this, %obj, %shape, %amount) +{ + // Thou shalt not spam. +} + +function SpawnPoint::onGainPowerEnabled(%data,%obj) { + %obj.active = 1; + parent::onGainPowerEnabled(%data, %obj); // Eolk - call parent +} + +function SpawnPoint::onLosePowerDisabled(%data,%obj) { + %obj.active = 0; + parent::onLosePowerDisabled(%data, %obj); // Eolk - call parent +} + +function SpawnPointImage::onDeploy(%item,%plyr,%slot) { + %className = "StaticShape"; + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,-1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %deplObj = new (%className)() { + dataBlock = SpawnPoint; + scale = "1 1 1"; + deployed = true; + }; + %deplObj.setTransform(%item.surfacePt SPC %rot); + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + %deplObj.powerFreq = %plyr.powerFreq; + setTargetName(%deplObj.target, addTaggedString("Frequency" SPC %deplObj.powerFreq)); + %deplObj.setSelfPowered(); + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(),%plyr.client.team); + addToDeployGroup(%deplObj); + AIDeployObject(%plyr.client,%deplObj); + addDSurface(%item.surface,%deplObj); + serverPlay3D(%item.deploySound,%deplObj.getTransform()); + $TeamDeployedCount[%plyr.team,%item.item]++; + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item,1); + + addTeamSpawnPoint(%deplObj); + + if(%plyr.client.SPname $= "") + %piece.name = getWords(%position,0,1); + else + %piece.name = %plyr.client.SPname; + %piece.active = 0; + + setTargetName(%deplObj.target,addTaggedString(%deplObj.name)); + + $PowerList = listAdd($PowerList, %deplObj, -1); + return %deplObj; +} + +function SpawnPointImage::onMount(%data,%obj,%node) { + %obj.hasGen = true; + displayPowerFreq(%obj); +} + +function SpawnPointImage::onUnmount(%data,%obj,%node) { + %obj.hasGen = ""; +} + +function addTeamSpawnPoint(%obj) +{ + if(%obj.getDatablock().getName() !$= "SpawnPoint") + return; + + if($teamSPs[%obj.team] $= "") + $TeamSPs[%obj.team] = 0; + $teamSP[%obj.team,$teamSPs[%obj.team]] = %obj; + $teamSPs[%obj.team]++; +} + +function remTeamSpawnPoint(%obj) +{ + if(%obj.getDatablock().getName() !$= "SpawnPoint") + return; + + for(%i = 0; %i < $teamSPs[%obj.team]; %i++){ + if($teamSP[%obj.team,%i] $= %obj) + %spawn = %i; + } + + if(%spawn < ($teamSPs[%obj.team] - 1)){ + $teamSP[%obj.team,%spawn] = $teamSP[%obj.team,($teamSPs[%obj.team] - 1)]; + $teamSP[%obj.team,($teamSPs[%obj.team] - 1)] = ""; + } + + $teamSPs[%obj.team]--; +} diff --git a/Scripts/Packs/spine.cs b/Scripts/Packs/spine.cs new file mode 100644 index 0000000..19eeda3 --- /dev/null +++ b/Scripts/Packs/spine.cs @@ -0,0 +1,297 @@ +//--------------------------------------------------------- +// Deployable Spine, Code by Mostlikely, Prettied by JackTL +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedSpine) : StaticShapeDamageProfile { + className = "spine"; + shapeFile = "Pmiscf.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Light Support Beam'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; +datablock StaticShapeData(DeployedSpine2) : StaticShapeDamageProfile { + className = "spine"; + shapeFile = "Xmiscf.dts"; + + maxDamage = 25.0; + destroyedLevel = 25.0; + disabledLevel = 24.0; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Light Support Beam'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; +datablock StaticShapeData(DeployedWoodSpine) : StaticShapeDamageProfile { + className = "spine"; + shapeFile = "stackable5m.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + isShielded = true; + energyPerDamagePoint = 240; + maxEnergy = 50; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Light Support Beam'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock ShapeBaseImageData(spineDeployableImage) { + mass = 20; + emap = true; + shapeFile = "ammo_plasma.dts"; + item = spineDeployable; + mountPoint = 1; + offset = "0 -0.2 -0.55"; + deployed = DeployedSpine; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.1; + maxDeployDis = 50.0; +}; + +datablock ItemData(spineDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "spineDeployableImage"; + pickUpName = "a light support beam pack"; + heatSignature = 0; + emap = true; +}; + +function spineDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function spineDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function spineDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function spineDeployableImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %item.surfaceNrm3 = vectorCross(%item.surfaceNrm,%item.surfaceNrm2); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %scale = getWords($packSetting["spine",%plyr.packSet],0,2); + %dataBlock = %item.deployed; + + if (%plyr.packSet == 5) { + %space = rayDist(%item.surfacePt SPC %item.surfaceNrm,%scale); + if (%space != getWord(%scale,1)) + %type = true; + %scale = getWord(%scale,0) SPC getWord(%scale,0) SPC %space; + } + else if (%plyr.packSet == 6 || %plyr.packSet == 7) { + %mCenter = "0 0 -0.5"; + %pad = pad(%item.surfacePt SPC %item.surfaceNrm SPC %item.surfaceNrm2,%scale,%mCenter); + %scale = getWords(%pad,0,2); + %item.surfacePt = getWords(%pad,3,5); + %rot = getWords(%pad,6,9); + if (%plyr.packSet == 7) { + %scale = vectorMultiply(%scale,1.845 SPC 2 SPC 1.744); // Update dfunctions.cs if changed! + %scaleIsSet = 1; + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm3,0.5)); + %rot = rotAdd(%rot,"1 0 0" SPC $Pi); + %dataBlock = "DeployedWoodSpine"; + } + } + + if (!%scaleIsSet) + %scale = vectorMultiply(%scale,1/4 SPC 1/3 SPC 2); + + %deplObj = new (%className)() { + dataBlock = %dataBlock; + scale = %scale; + }; + +//////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]: + +// if(%deplObj.getDatablock().rechargeRate) +// %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // Power object + checkPowerObject(%deplObj); + + if (%pad) + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"pad"); + else if (%type) + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"spine1"); + else + deployEffect(%deplObj,%item.surfacePt,%item.surfaceNrm,"spine"); + + return %deplObj; +} + +///////////////////////////////////// + +function DeployedSpine::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, spineDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function DeployedSpine2::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, spineDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + cascade(%obj); + fireBallExplode(%obj,1); +} + +function DeployedWoodSpine::onDestroyed(%this, %obj, %prevState) { + DeployedSpine::onDestroyed(%this, %obj, %prevState); +} + +function spineDeployableImage::onMount(%data, %obj, %node) { + %obj.hasSpine = true; // set for spinecheck + %obj.packSet = 0; + displayPowerFreq(%obj); +} + +function spineDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasSpine = ""; + %obj.packSet = 0; +} diff --git a/Scripts/Packs/switch.cs b/Scripts/Packs/switch.cs new file mode 100644 index 0000000..d79567d --- /dev/null +++ b/Scripts/Packs/switch.cs @@ -0,0 +1,273 @@ +//-------------------------------------------------------------------------- +// Deployable Switch +//-------------------------------------------------------------------------- + +datablock StaticShapeData(DeployedSwitch) : StaticShapeDamageProfile { + className = "switch"; + shapeFile = "switch.dts"; + + maxDamage = 1.00; + destroyedLevel = 1.00; + disabledLevel = 0.75; + + isShielded = true; + energyPerDamagePoint = 30; + maxEnergy = 100; + rechargeRate = 0.05; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.3; + expImpulse = 500; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_switch_grey"; + targetNameTag = 'Deployed'; + targetTypeTag = 'Switch'; +// deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + emap = true; +}; + +datablock ShapeBaseImageData(SwitchDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = SwitchDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedSwitch; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + flatMinDeployDis = 0.25; + flatMaxDeployDis = 5.0; + + minDeployDis = 2; + maxDeployDis = 5; +}; + +datablock ItemData(SwitchDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + + hasLight = true; + lightType = "PulsingLight"; + lightColor = "0.1 0.8 0.8 1.0"; + lightTime = "1000"; + lightRadius = "3"; + + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + rotate = true; + image = "SwitchDeployableImage"; + pickUpName = "a switch pack"; + heatSignature = 0; + emap = true; +}; + +datablock AudioProfile(SwitchToggledSound) { + filename = "fx/misc/flipflop_taken.wav"; + description = AudioClosest3d; + preload = true; +}; + +function SwitchDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function SwitchDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function SwitchDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + scale = "0.5 0.5 0.5"; + dataBlock = DeployedSwitch; + deployed = true; + }; + + %deplObj.switchRadius = $packSetting["switch",%plyr.packSet]; + + // TODO - handle normal state on deploy + if ($Host::ExpertMode == 1) { + if (%plyr.expertSet == 1) + %deplObj.timed = 1; + else if (%plyr.expertSet == 2) + %deplObj.timed = 2; + } + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set skin + setTargetSkin(%deplObj.target,Game.getTeamSkin(%plyr.client.team)); + + // set power + %deplObj.setSelfPowered(); + setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + %deplObj.playThread($PowerThread,"Power"); + %deplObj.playThread($AmbientThread,"ambient"); + + if (%deplObj.timed == 2) { + %deplObj.stopThread($AmbientThread); + setTargetName(%deplObj.target,addTaggedString("Disabled Frequency" SPC %deplObj.powerFreq)); + %deplObj.isSwitchedOff = true; + } + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function DeployedSwitch::onCollision(%data,%obj,%col) { + toggleSwitch(%obj,-1,%col); +} + +function toggleSwitch(%obj,%state,%col,%delayed) { + if (%obj.isRemoved) + return; + // TODO - prevent switching while waiting for timed delay / cancel timed delay if switch is hit? + %switchDelay = 1000; + %switchTimedDelay = 5000; + if (%state $= "") + %state = -1; + if (%col == 0 || %col $= "") + %force = true; + if (!%force) { + if (%col.getClassName() !$= "Player") + return; + if (%col.getState() $= "Dead" || %col.FFZapped == true) + return; + if (%obj.team != %col.team && %obj.team != 0) + return; + if (!(%obj.switchTime < getSimTime())) { + messageClient(%col.client, 'msgClient', '\c2Must wait %1 seconds between switching states.',mCeil((%obj.switchTime - getSimTime())/1000)); + return; + } + } + if ((%obj.isSwitchedOff || (%force == true && %state == true)) && !(%force == true && %state == false)) { + %state = true; + %obj.isSwitchedOff = ""; + } + else { + %state = false; + %obj.isSwitchedOff = true; + } + %switchCount = 0; + %count = getWordCount($PowerList); + // TODO - report number of successes and failures + for(%i=0;%i<%count;%i++) { + %powerObj = getWord($PowerList,%i); + if (vectorDist(%obj.getPosition(),%powerObj.getPosition()) < %obj.switchRadius + && !%powerObj.isRemoved && %obj.powerFreq == %powerObj.powerFreq + && %obj.team == %powerObj.team) { + toggleGenerator(%powerObj,%state); + %switchCount++; + } + } + if (%state == true) { + %obj.play3D(SwitchToggledSound); + %obj.playThread($AmbientThread,"ambient"); + setTargetName(%obj.target,addTaggedString("Frequency" SPC %obj.powerFreq)); + if (!%force) + messageClient(%col.client, 'msgClient', '\c2%1 objects attempted switched on.',%switchCount); + } + else { + %obj.play3D(SwitchToggledSound); + %obj.stopThread($AmbientThread); + setTargetName(%obj.target,addTaggedString("Disabled Frequency" SPC %obj.powerFreq)); + if (!%force) + messageClient(%col.client, 'msgClient', '\c2%1 objects attempted switched off.',%switchCount); + } + %obj.switchTime = getSimTime() + %switchDelay; + cancel(%obj.timedSched); + if (%obj.timed > 0 && !%delayed) { + if (%obj.timed == 1) + %obj.timedSched = schedule(%switchTimedDelay,0,toggleSwitch,%obj,1,0,true); + else if (%obj.timed == 2) + %obj.timedSched = schedule(%switchTimedDelay,0,toggleSwitch,%obj,0,0,true); + %obj.switchTime = getSimTime() + %switchDelay + %switchTimedDelay; + } +} + +function DeployedSwitch::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, SwitchDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function SwitchDeployableImage::onMount(%data,%obj,%node) { + %obj.hasSwitch = true; // set for switchcheck + %obj.packSet = 2; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function SwitchDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasSwitch = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} diff --git a/Scripts/Packs/telepadpack.cs b/Scripts/Packs/telepadpack.cs new file mode 100644 index 0000000..c8c480e --- /dev/null +++ b/Scripts/Packs/telepadpack.cs @@ -0,0 +1,562 @@ +//--------------------------------------------------------- +// Deployable Telepad +//--------------------------------------------------------- + +datablock ShapeBaseImageData(TelePadDeployableImage) { + mass = 15; + emap = true; + shapeFile = "stackable1s.dts"; + item = TelePadPack; + mountPoint = 1; + offset = "0 0 0"; + deployed = TelePadDeployedBase; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = StationDeploySound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(TelePadPack) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "TelePadDeployableImage"; + pickUpName = "a teleport pad pack"; + heatSignature = 0; + joint = "2 2 2"; + computeCRC = true; + emap = true; +}; + +datablock SensorData(TelePadBaseSensorObj) { + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 10; +}; + +datablock StaticShapeData(TelePadDeployedBase) : StaticShapeDamageProfile { + className = "teleport"; + shapeFile = "nexuscap.dts"; + + maxDamage = 2.00; + destroyedLevel = 2.00; + disabledLevel = 1.35; + + isShielded = true; + energyPerDamagePoint = 250; + maxEnergy = 100; + rechargeRate = 1; + + explosion = ShapeExplosion; // DeployablesExplosion; + expDmgRadius = 18.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StationObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSwitchIcon; + cmdMiniIconName = "commander/MiniIcons/com_switch_grey"; + targetNameTag = 'Deployed'; + targetTypeTag = 'Teleport Pad'; + + debrisShapeName = "debris_generic.dts"; + debris = DeployableDebris; + + heatSignature = 0; + needsPower = true; + + humSound = SensorHumSound; + pausePowerThread = true; + sensorData = TelePadBaseSensorObj; + sensorRadius = TelePadBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + firstPersonOnly = true; + + lightType = "PulsingLight"; + lightColor = "0 1 0 1"; + lightTime = 1200; + lightRadius = 6; +}; + +datablock StaticShapeData(TelePadBeam) { + className = "Station"; + catagory = "DSupport"; + shapefile = "nexus_effect.dts"; + collideable = 1; + needsNoPower = true; + emap="true"; + sensorData = TelePadBaseSensorObj; + sensorRadius = TelePadBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + + cmdCategory = "DSupport"; + targetNameTag = 'Teleport'; + targetTypeTag = 'Pad'; + + lightType = "PulsingLight"; + lightColor = "0 1 0 1"; + lightTime = 1200; + lightRadius = 6; +}; + +datablock AudioProfile(TelePadAccessDeniedSound) { + filename = "gui/vote_nopass.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(TelePadBeamSound) { + filename = "fx/vehicles/inventory_pad_on.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(TelePadPowerUpSound) { + filename = "fx/powered/turret_heavy_activate.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(TelePadPowerDownSound) { + filename = "fx/powered/inv_pad_off.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock ParticleData(TelePadTeleportParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 1200; + lifetimeVarianceMS = 400; + + textureName = "special/lightFalloffMono"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + colors[0] = "0.5 0.8 0.2 1.0"; + colors[1] = "0.6 0.9 0.3 1.0"; + colors[2] = "0.7 1.0 0.4 1.0"; + sizes[0] = 0.2; + sizes[1] = 0.1; + sizes[2] = 0.05; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(TelePadTeleportEmitter) +{ + ejectionPeriodMS = 1; + ejectionOffset = 7.0; + periodVarianceMS = 0.0; + ejectionVelocity = 2.0; + velocityVariance = 2.0; + thetaMin = 0.0; + thetaMax = 10.0; + lifetimeMS = 0; + + particles = "TelePadTeleportParticle"; +}; + +function TelePadDeployedBase::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team,TelePadPack]--; + %obj.isRemoved = true; + remDSurface(%obj); + %obj.beam.schedule(150,"delete"); + %obj.schedule(500,"delete"); +} + +function TelePadDeployedBase::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.beam)) + %obj.beam.delete(); + %obj.isRemoved = true; + disassemble(%data,%plyr,%obj); +} + +function TelePadDeployedBase::onGainPowerEnabled(%data,%obj) { + if (shouldChangePowerState(%obj,true)) { + tp_fadeIn(%obj,true); + %obj.playThread($AmbientThread,"ambient"); + %obj.setThreadDir($AmbientThread,true); + + %obj.playThread($ActivateThread,"transition"); + %obj.setThreadDir($ActivateThread,false); + + %obj.play3D(TelePadPowerUpSound); + } + Parent::onGainPowerEnabled(%data,%obj); +} + +function TelePadDeployedBase::onLosePowerDisabled(%data,%obj) { + if (shouldChangePowerState(%obj,false)) { + tp_fadeOut(%obj,true); + %obj.stopThread($AmbientThread); + + %obj.playThread($ActivateThread,"transition"); + %obj.setThreadDir($ActivateThread,true); + + %obj.play3D(TelePadPowerDownSound); + } + Parent::onLosePowerDisabled(%data,%obj); +} + +function TelePadDeployedBase::onCollision(%data,%obj,%col) { + if (%col.justTeleported || %obj.isRemoved) + return; + + // verify pad.team is team associated and is on player's team + if (%obj.team != %col.team && %obj.team != 0 && %obj.teleMode != 1 && %obj.frequency > 0 && %obj.ishacked != 1) { + %obj.play3D(TelePadAccessDeniedSound); + messageClient(%col.client,'msgClient','\c2Access Denied -- Wrong team use, /hack help , to hack this teleport.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + // verify that pad can transmit + if (%obj.teleMode == 3) { + %obj.play3D(TelePadAccessDeniedSound); + messageClient(%col.client,'msgClient','\c2Access Denied -- This pad can only receive.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + // Discover next pad to teleport to + %dgroup = nameToID(Deployables); + %destPad = 0; + %firstPad = 0; + for(%depIndex = 0; %depIndex < %dgroup.getCount(); %depIndex++) { + %dep = %dgroup.getObject(%depIndex); + if (%dep.getDataBlock().className $= "teleport") { + if (%dep != %obj && %dep.frequency == %obj.frequency && %dep.team == %obj.team && %dep.teleMode != 2) { + if (%dep.isPowered() && %dep.isEnabled() && !(%dep.getEnergyLevel() < %dep.getDataBlock().maxEnergy)) { + if (!%firstPad || %dep < %firstPad) + %firstPad = %dep; + if (%dep > %obj && (!%destPad || %dep < %destPad)) { + %destPad = %dep; + } + } + else + %notEnabled = true; + } + } + } + if (!%destPad) + %destPad = %firstPad; + + if (!%obj.isPowered() || !%obj.isEnabled()) { + %obj.play3D(TelePadAccessDeniedSound); + if (!%obj.isPowered() && !%obj.isEnabled()) + messageClient(%col.client,'msgClient','\c2Unable to teleport, telepad is damaged and has no power.'); + else if (!%obj.isEnabled()) + messageClient(%col.client,'msgClient','\c2Unable to teleport, telepad is damaged.'); + else if (!%obj.isPowered()) + messageClient(%col.client,'msgClient','\c2Unable to teleport, telepad is not powered.'); + else + messageClient(%col.client,'msgClient','\c2Unable to teleport, telepad malfunction.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + if (!%destPad) { + %obj.play3D(TelePadAccessDeniedSound); + if (%notEnabled) + messageClient(%col.client,'msgClient','\c2Unable to teleport, destination is damaged, has no power or is recharging.'); + else + messageClient(%col.client,'msgClient','\c2Unable to teleport, no other pads to teleport to.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + if (tp_isBlocked(%destPad,%col)) { + %obj.play3D(TelePadAccessDeniedSound); + messageClient(%col.client,'msgClient','\c2Unable to teleport, destination is blocked.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + if (%obj.getEnergyLevel() < %obj.getDataBlock().maxEnergy) { + %obj.play3D(TelePadAccessDeniedSound); + messageClient(%col.client,'msgClient','\c2Unable to teleport, telepad is recharging.'); + %col.justTeleported = true; + schedule(2000,0,"unTeleport",%col); + return; + } + + // center player on pad + if (!tp_isBlocked(%obj,%col)) + tp_adjustPlayer(%obj,%col); + + // fade out player + %col.disableMove(true); + pl_fadeOut(%col); + + // pad power up effect + tp_fadePadIn(%obj); + tp_fadePadIn(%destPad); + + // fade out beams + schedule(600,0,"tp_fadeOut",%obj); + schedule(750,0,"tp_fadeOut",%destPad); + + // pad power down effect + schedule(1500,0,"tp_fadePadOut",%obj); + schedule(1550,0,"tp_fadePadOut",%destPad); + + // fade in beams + schedule(3000,0,"tp_fadeIn",%obj); + schedule(3050,0,"tp_fadeIn",%destPad); + + // Zap energy + %obj.setEnergyLevel(0); + %destPad.setEnergyLevel(0); + + %col.justTeleported = true; + + messageClient(%col.client,'msgClient',"~wfx/misc/diagnostic_on.wav"); + schedule(500,0,"teleport",%col,%destPad,%obj); // schedule their teleportation + tp_emitter(%obj); +} + +function tp_isBlocked(%pad,%obj) { + %padPos = %pad.getPosition(); + %telePos = vectorAdd(%padPos,vectorScale(realVec(%pad,"0 0 1"),-2.5)); + if (containerRayCast(%padPos,%telePos,-1,%pad)) + %blocked = true; + if (!$Host::AllowUnderground) { + %terrain = getTerrainHeight2(%telePos); + if (getWord(%telePos,2) < getWord(%terrain,2) || %terrain $= "") + %blocked = true; + } + if (isObject(%obj)) { + %adjust = vectorAdd(vectorScale(vectorNormalize(realVec(%pad,"0 0 1")),-1.35),vectorSub(%obj.getPosition(),%obj.getWorldBoxCenter())); + %pos = vectorAdd(%padPos,%adjust); + if (!$Host::AllowUnderground) { + %terrain = getTerrainHeight2(%pos); + if (getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") + %blocked = true; + } + if (containerRayCast(%telePos,%pos,-1,%obj)) + %blocked = true; + } + return %blocked; +} + +// player +function pl_fadeIn(%obj) { + %obj.startFade(500,0,false); + messageClient(%col.client,'msgClient',"~wfx/misc/diagnostic_on.wav"); +} + +function pl_fadeOut(%obj) { + %obj.startFade(500,0,true); + messageClient(%col.client,'msgClient',"~wfx/misc/diagnostic_on.wav"); +} + +// beam and sound +function tp_fadeIn(%obj,%silent) { + if (%obj.isPowered() && %obj.isEnabled()) { + if (!%silent) + %obj.play3D(DiscReloadSound); + %obj.beam.startFade(100,0,false); + } +} + +function tp_fadeOut(%obj,%silent) { + if (!%silent) + %obj.play3D(TelePadBeamSound); + %obj.beam.startFade(100,0,true); +} + +// pad +function tp_fadePadIn(%obj) { + %obj.playThread($ActivateThread,"transition"); + %obj.setThreadDir($ActivateThread,true); +} + +function tp_fadePadOut(%obj) { + %obj.playThread($ActivateThread,"transition"); + %obj.setThreadDir($ActivateThread,false); +} + +// fancy emitter +function tp_emitter(%obj) { + %em = new ParticleEmissionDummy() { + scale = "1 1 1"; + dataBlock = "defaultEmissionDummy"; + emitter = "TelePadTeleportEmitter"; + velocity = "1"; + }; + %adjust = vectorScale(realVec(%obj,"0 0 1"),7); + %em.setTransform(vectorAdd(%obj.getPosition(),%adjust) SPC rotAdd(%obj.getRotation(),"1 0 0" SPC $Pi)); + MissionCleanup.add(%em); + %em.schedule(1000,"delete"); +} + +function unTeleport(%pl) { + if (isObject(%pl)) + %pl.justTeleported = false; +} + +function teleport(%pl,%destPad,%src) { + %pl.setVelocity("0 0 0"); //slow me down, ive been falling :) + %pl.schedule(500,disableMove,false); + pl_fadeIn(%pl); + schedule(2650,0,"unTeleport",%pl); + + if (!isObject(%destPad)) {// lost the destination + messageClient(%pl.client,'msgClient','\c2Lost destination!'); + tp_adjustPlayer(%src,%pl); + } + else { + if (%pl.team == %src.team) + messageClient(%pl.client,'msgClient','\c2Teleporting on frequency %1.',%src.frequency); + else + messageClient(%pl.client,'msgClient','\c2Teleporting on ENEMY frequency %1!',%src.frequency); + tp_adjustPlayer(%destPad,%pl); + tp_emitter(%destPad); + } +} + +function tp_adjustPlayer(%pad,%obj) { + %adjust = vectorAdd(vectorScale(vectorNormalize(realVec(%pad,"0 0 1")),-1.35),vectorSub(%obj.getPosition(),%obj.getWorldBoxCenter())); + %rot = %obj.getRotation(); + %obj.setTransform(vectorAdd(%pad.getPosition(),%adjust) SPC %rot); +} + +function tp_adjustBeam(%pad) { + %rot = rotAdd(%pad.getRotation(),"1 0 0" SPC $Pi); + %pad.beam.setTransform(%pad.getPosition() SPC %rot); +} + +function TelePadDeployableImage::onDeploy(%item,%plyr,%slot) { + %className = "StaticShape"; + + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(%item.surfaceNrm,0.4)); + + %playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,-1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(vectorScale(%item.surfaceNrm,-1),%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = TelePadDeployedBase; + scale = "1 1 1"; + deployed = true; + teleMode = %plyr.expertSet; + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(),%plyr.client.team); + + // set frequency + %frequency = %plyr.packSet; + if (!%frequency) + %frequency = 1; + %deplObj.frequency = %frequency; + setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %frequency)); + + // attach beam + %deplObj.beam = new (StaticShape)() { + dataBlock = TelePadBeam; + scale = "1 1 0.4"; + }; + + // set orientation + tp_adjustBeam(%deplObj); + + %deplObj.beam.playThread(0,"ambient"); + %deplObj.beam.setThreadDir(0,true); + // The flash animation plays forwards, then back automatically,so we have to alternate the thread direcction... + %deplObj.beam.flashThreadDir = true; + + %deplObj.beam.base = %deplObj; + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client,%deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound,%deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team,%item.item]++; + + addDSurface(%item.surface,%deplObj); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item,1); + + // Power object + checkPowerObject(%deplObj); + + teleresethack(%deplObj); + + return %deplObj; +} + +function TelePadDeployableImage::onMount(%data,%obj,%node) { + %obj.hasTele = true; // set for telecheck + %obj.packSet = 1; + %obj.expertSet = 0; + displayPowerFreq(%obj); +} + +function TelePadDeployableImage::onUnmount(%data,%obj,%node) { + %obj.hasTele = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} diff --git a/Scripts/Packs/treepack.cs b/Scripts/Packs/treepack.cs new file mode 100644 index 0000000..f4e7c0a --- /dev/null +++ b/Scripts/Packs/treepack.cs @@ -0,0 +1,258 @@ +//--------------------------------------------------------- +// Deployable Tree, original code by Parousia +//--------------------------------------------------------- + +datablock StaticShapeData(DeployedTree) : StaticShapeDamageProfile { + className = "tree"; + shapeFile = "borg19.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed Tree'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock StaticShapeData(DeployedTree0) : DeployedTree { + shapeFile = "borg16.dts"; +}; + +datablock StaticShapeData(DeployedTree1) : DeployedTree { + shapeFile = "borg17.dts"; +}; + +datablock StaticShapeData(DeployedTree2) : DeployedTree { + shapeFile = "borg18.dts"; +}; + +datablock StaticShapeData(DeployedTree3) : DeployedTree { + shapeFile = "borg19.dts"; +}; + +datablock StaticShapeData(DeployedTree4) : DeployedTree { + shapeFile = "dorg15.dts"; +}; + +datablock StaticShapeData(DeployedTree5) : DeployedTree { + shapeFile = "dorg16.dts"; +}; + +datablock StaticShapeData(DeployedTree6) : DeployedTree { + shapeFile = "dorg17.dts"; +}; + +datablock StaticShapeData(DeployedTree7) : DeployedTree { + shapeFile = "dorg18.dts"; +}; + +datablock StaticShapeData(DeployedTree8) : DeployedTree { + shapeFile = "dorg19.dts"; +}; + +datablock StaticShapeData(DeployedTree9) : DeployedTree { + shapeFile = "porg3.dts"; +}; + +datablock StaticShapeData(DeployedTree10) : DeployedTree { + shapeFile = "porg6.dts"; +}; + +datablock StaticShapeData(DeployedTree11) : DeployedTree { + shapeFile = "sorg20.dts"; +}; + +datablock StaticShapeData(DeployedTree12) : DeployedTree { + shapeFile = "sorg22.dts"; +}; + +datablock StaticShapeData(DeployedTree13) : DeployedTree { + shapeFile = "xorg3.dts"; +}; + +datablock ShapeBaseImageData(TreeDeployableImage) { + mass = 20; + emap = true; + shapeFile = "stackable1s.dts"; + item = TreeDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedTree; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.5; + maxDeployDis = 50.0; +}; + +datablock ItemData(TreeDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = true; + image = "TreeDeployableImage"; + pickUpName = "a tree pack"; + heatSignature = 0; + emap = true; + }; + +function TreeDeployableImage::testObjectTooClose(%item) { + return ""; +} + +function TreeDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function TreeDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function TreeDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + %item.surfaceNrm2 = %playerVector; + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = %item.deployed @ %plyr.packSet; + scale = vectorScale("1 1 1",$expertSetting["tree",%plyr.expertSet]); + }; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + addDSurface(%item.surface,%deplObj); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function DeployedTree::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, TreeDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function TreeDeployableImage::onMount(%data, %obj, %node) { + %obj.hasTree = true; // set for treecheck + %obj.packSet = 0; + %obj.expertSet = 5; +} + +function TreeDeployableImage::onUnmount(%data, %obj, %node) { + %obj.hasTree = ""; + %obj.packSet = 0; + %obj.expertSet = 0; +} + +function DeployedTree0::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree1::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree2::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree3::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree4::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree5::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree6::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree7::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree8::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree9::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree10::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree11::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree12::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} + +function DeployedTree13::onDestroyed(%this,%obj,%prevState) { + DeployedTree::onDestroyed(%this,%obj,%prevState); +} diff --git a/Scripts/Packs/tripwire.cs b/Scripts/Packs/tripwire.cs new file mode 100644 index 0000000..2529c0c --- /dev/null +++ b/Scripts/Packs/tripwire.cs @@ -0,0 +1,432 @@ +// Tripwire + +datablock ForceFieldBareData(TripField) : DeployedForceField { + baseTranslucency = 0.1; + powerOffTranslucency = 0.0; + teamPermiable = true; + otherPermiable = true; + color = "1 0 0"; + powerOffColor = "0.0 0.0 0.0"; + deployedFrom = ""; + needsPower = false; +}; + +datablock AudioProfile(TripwireSound) { + filename = "fx/misc/rolechange.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock TriggerData(TripTrigger) { + // TODO - check this + tickPeriodMS = 200; +}; + +function TripTrigger::onEnterTrigger(%data, %obj, %colObj) { +// TODO - keep trip FF scaled down until trip is triggered - vehicle fix? + %baseObj = %obj.baseObj; + %baseObj.triggerCount++; + if (%baseObj.triggerCount > 1) + return; + %baseObj.play3D(TripwireSound); + if (isObject(%baseObj.tripField)) { + cancel(%baseObj.tripField.hideSched); + %baseObj.tripField.getDataBlock().gainPower(%baseObj.tripField); + } + %tripMode = %baseObj.tripMode; + while (%tripMode > 1 && %tripMode > -1 && %tripMode !$= "") + %tripMode = %tripMode - 2; + %count = getWordCount($PowerList); + for(%i=0;%i<%count;%i++) { + %powerObj = getWord($PowerList,%i); + if (vectorDist(%baseObj.getPosition(),%powerObj.getPosition()) < %baseObj.switchRadius + && !%powerObj.isRemoved && %baseObj.powerFreq == %powerObj.powerFreq + && %baseObj.team == %powerObj.team) { + %r = toggleGenerator(%powerObj,!%tripMode); + %r1 = firstWord(%r); + %r2 = getTaggedString(getWord(%r,1)); + %r3 = getWord(%r,2); + if (%r1 == -3) { + cancel(%powerObj.toggleSched); + %powerObj.toggleSched = schedule(%r3 + 100,0,toggleGenerator,%powerObj,!%tripMode); + } + } + } +} + +function TripTrigger::onLeaveTrigger(%data, %obj, %colObj) { + %baseObj = %obj.baseObj; + %baseObj.triggerCount--; + if (%baseObj.triggerCount > 0) + return; + %baseObj.play3D(TripwireSound); + if (isObject(%baseObj.tripField)) { + cancel(%baseObj.tripField.hideSched); + %baseObj.tripField.hideSched = %baseObj.tripField.getDataBlock().schedule(2000,losePower,%baseObj.tripField); + } + %tripMode = %baseObj.tripMode; + if (%tripMode == 2 || %tripMode == 3) + return; + if (%tripMode == 4 || %tripMode == 5) + %timed = true; + while (%tripMode > 1 && %tripMode > -1 && %tripMode !$= "") + %tripMode = %tripMode - 2; + %count = getWordCount($PowerList); + for(%i=0;%i<%count;%i++) { + %powerObj = getWord($PowerList,%i); + if (vectorDist(%baseObj.getPosition(),%powerObj.getPosition()) < %baseObj.switchRadius + && !%powerObj.isRemoved && %baseObj.powerFreq == %powerObj.powerFreq + && %baseObj.team == %powerObj.team) { + if (%timed) { + cancel(%powerObj.toggleSched); + %powerObj.toggleSched = schedule(5000,0,delayedToggleGenerator,%powerObj,%tripMode); + } + else { + %r = toggleGenerator(%powerObj,%tripMode); + %r1 = firstWord(%r); + %r2 = getTaggedString(getWord(%r,1)); + %r3 = getWord(%r,2); + if (%r1 == -3) { + cancel(%powerObj.toggleSched); + %powerObj.toggleSched = schedule(%r3 + 100,0,toggleGenerator,%powerObj,%tripMode); + } + } + } + } +} + +function delayedToggleGenerator(%powerObj,%tripMode) { + %r = toggleGenerator(%powerObj,%tripMode); + %r1 = firstWord(%r); + %r2 = getTaggedString(getWord(%r,1)); + %r3 = getWord(%r,2); + if (%r1 == -3) { + cancel(%powerObj.toggleSched); + %powerObj.toggleSched = schedule(%r3 + 100,0,toggleGenerator,%powerObj,%tripMode); + } +} + +function TripTrigger::onTickTrigger(%data, %obj) { +} + +function TripTrigger::onTrigger(%this, %triggerId, %on) { +} + +datablock StaticShapeData(DeployedTripwire) : StaticShapeDamageProfile { + className = "tripwire"; + shapeFile = "camera.dts"; + + maxDamage = 0.2; + destroyedLevel = 0.2; + disabledLevel = 0.2; + + isShielded = false; + energyPerDamagePoint = 40; + maxEnergy = 30; + rechargeRate = 0.05; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.3; + expImpulse = 500; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed'; + targetTypeTag = 'Tripwire'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + emap = true; +}; + +datablock ShapeBaseImageData(TripwireDeployableImage) { + mass = 20; + emap = true; + shapeFile = "camera.dts"; + item = TripwireDeployable; + mountPoint = 1; + offset = "0 0 0"; + rotation = "-1 0 0 90"; + deployed = DeployedTripwire; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = false; + maxDepSlope = 360; + deploySound = ItemPickupSound; + + minDeployDis = 0.25; + maxDeployDis = 5; +}; + +datablock ItemData(TripwireDeployable) { + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 5.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + rotate = true; + image = "TripwireDeployableImage"; + pickUpName = "a tripwire pack"; + heatSignature = 0; + emap = true; +}; + +function TripwireDeployableImage::testNoTerrainFound(%item) { + // don't check this for non-Landspike turret deployables +} + +function TripwireDeployable::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +function TripwireDeployableImage::onDeploy(%item, %plyr, %slot) { + %className = "StaticShape"; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + + %deplObj = new (%className)() { + dataBlock = DeployedTripwire; + }; + + %deplObj.switchRadius = getWord($packSetting[TripwireDeployableImage, %plyr.packSet[0]], 0); + %deplObj.fieldMode = %plyr.packSet[2]; + %deplObj.tripMode = %plyr.packSet[1]; + if (%deplObj.fieldMode == 1) + %deplObj.beamRange = 160; + else + %deplObj.beamRange = 30; + + if(!%plyr.packSet[3]) + { + %deplObj.tripField = new ForceFieldBare() { + dataBlock = TripField; + scale = "0.05 0.05 0.1"; + }; + + %deplObj.tripField.pzone.delete(); + } + + %deplObj.tripTrigger = new Trigger() { + dataBlock = TripTrigger; + polyhedron = "0 1 0 1 0 0 0 -1 0 0 0 1"; + scale = "0.05 0.05 0.1"; + }; + + %deplObj.tripTrigger.baseObj = %deplObj; + + // set orientation + %deplObj.setTransform(%item.surfacePt SPC %rot); + adjustTripwire(%deplObj); + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + setTargetName(%deplObj.target,addTaggedString("Frequency" SPC %deplObj.powerFreq)); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + addDSurface(%item.surface,%deplObj); + + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function DeployedTripwire::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + $TeamDeployedCount[%obj.team, TripwireDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (isObject(%obj.tripField)) + %obj.tripField.schedule(500, "delete"); + if (isObject(%obj.tripTrigger)) + %obj.tripTrigger.schedule(500, "delete"); +} + +function DeployedTripwire::disassemble(%data,%plyr,%obj) { + if (isObject(%obj.tripField)) + %obj.tripField.delete(); + if (isObject(%obj.tripTrigger)) + %obj.tripTrigger.delete(); + disassemble(%data,%plyr,%obj); +} + +function adjustTripwire(%obj) { + if (!isObject(%obj)) + return; + cancel(%obj.adjustSched); + %pos = %obj.getPosition(); + %nrm = realVec(%obj,"0 0 1"); + %nrm2 = realVec(%obj,"1 0 0"); + %nrm3 = realVec(%obj,"0 1 0"); + + // TODO - temporary - remove + if ($TripWireFlareMode == true) { + %pos = vectorAdd(%obj.getPosition(),vectorScale(%nrm,0.35)); + %vec = vectorScale(%nrm,0.7); + %p = new FlareProjectile() { + dataBlock = FlareGrenadeProj; + initialDirection = %vec; + initialPosition = %pos; + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000,"delete"); + } + + // save ourselves re-doing some calculations + %mask = $TypeMasks::VehicleObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType | $TypeMasks::MoveableObjectType | $TypeMasks::DamagableItemObjectType; + if (%obj.fieldMode == 1) { + // new pad data can be done here for both field and trigger + // "normal" (no hit) beamRange limited to 0.5m - 8m + // max beamRange limited to 0.5m - 160m + %pad = pad(%obj.getPosition() SPC realVec(%obj,"0 0 1") SPC realVec(%obj,"1 0 0"),0.5 SPC limit(%obj.beamRange,0.5,8) SPC limit(%obj.beamRange,0.5,160),"-0.5 -0.5 -0.5",%mask,%obj.tripField); + %newScale = getWords(%pad,0,2); + %newPos = getWords(%pad,3,5); + %newRot = getWords(%pad,6,9); + } + else { + // beam + %dist = rayDist(vectorAdd(%pos,vectorScale(%nrm,0.1)) SPC %nrm,1 SPC %obj.beamRange SPC %obj.beamRange,%mask,%obj.tripField); + } + + if (isObject(%obj.tripField)) { + %obj2 = %obj.tripField; + if (%obj.fieldMode == 0) { + %scale = %obj2.getScale(); + %newPos = vectorAdd(%pos,vectorAdd(vectorScale(%nrm2,-(getWord(%scale,0) / 2)),vectorScale(%nrm3,-(getWord(%scale,1) / 2)))); + %newRot = %obj.getRotation(); + %newScale = getWords(%scale,0,1) SPC %dist; + } + %oldData = %obj2.oldData; + %newData = %newPos SPC %newRot SPC %newScale; + if (%oldData !$= %newData && %obj.triggerCount == 0) { + // call gainPower() before modifying forcefield! + %obj2.getDataBlock().gainPower(%obj2); + %obj2.setTransform(%newPos SPC %newRot); + %obj2.setScale(%newScale); + %obj2.oldData = %newData; + cancel(%obj2.hideSched); + %obj2.hideSched = %obj2.getDataBlock().schedule(2000,losePower,%obj2); + } + } + if (isObject(%obj.tripTrigger)) { + %obj2 = %obj.tripTrigger; + if (%obj.fieldMode == 0) { + %scale = %obj2.getScale(); + %newPos = vectorAdd(%pos,vectorAdd(vectorScale(%nrm2,-(getWord(%scale,0) / 2)),vectorScale(%nrm3,-(getWord(%scale,1) / 2)))); + %newRot = %obj.getRotation(); + %newScale = getWords(%scale,0,1) SPC %dist; + } + %oldData = %obj2.oldData; + %newData = %newPos SPC %newRot SPC %newScale; + if (%oldData !$= %newData && %obj.triggerCount == 0) { + %obj2.setTransform(%newPos SPC %newRot); + %obj2.setScale(%newScale); + %obj2.oldData = %newData; + } + } + %obj.adjustSched = schedule(1000,0,adjustTripwire,%obj); +} + +function TripwireDeployableImage::onMount(%data, %obj, %node) { + %obj.hasTripwire = true; // set for tripcheck + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; + displayPowerFreq(%obj); +} + +function TripwireDeployableImage::onUnmount(%data, %obj, %node) { + %obj.packSet = 0; + %obj.packSet[0] = 0; + %obj.packSet[1] = 0; + %obj.packSet[2] = 0; + %obj.packSet[3] = 0; + %obj.hasTripwire = ""; +} + +function TripwireDeployableImage::ChangeMode(%data,%plyr,%val,%level) +{ + if (%level == 0) + { + %set = %plyr.expertSet; + if(%set $= "") + %set = 0; + %image = %data.getName(); + %settings = $packSettings[%image,%set]; + + %plyr.packSet[%set] = %plyr.packSet[%set] + %val; + if (%plyr.packSet[%set] > getWord(%settings,0)) + %plyr.packSet[%set] = 0; + if (%plyr.packSet[%set] < 0) + %plyr.packSet[%set] = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $PackSetting[%image,%plyr.packSet[%set],%set]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + bottomPrint(%plyr.client,%packname SPC "set to"SPC %line,2,1); + } + else + { + Parent::ChangeMode(%data,%plyr,%val,%level); + } +} diff --git a/Scripts/Packs/turretpack.cs b/Scripts/Packs/turretpack.cs new file mode 100644 index 0000000..7e98bb9 --- /dev/null +++ b/Scripts/Packs/turretpack.cs @@ -0,0 +1,138 @@ +// turretpack.cs - turret pack +// + +datablock ShapeBaseImageData(TurretDeployableImage) +{ + mass = 15; + emap = true; + + shapeFile = "stackable1s.dts"; + item = TurretBasePack; + mountPoint = 1; + offset = "0 -0.2 0"; + + minDeployDis = 0.5; + maxDeployDis = 5.0; + + deployed = TurretDeployedBase; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 360; + deploySound = StationDeploySound; +}; + +datablock ItemData(TurretBasePack) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "stackable1s.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "TurretDeployableImage"; + pickUpName = "a base turret pack"; + heatSignature = 0; + + computeCRC = true; + emap = true; +}; + +datablock TurretData(TurretDeployedBase) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_base_large.dts"; + + rechargeRate = 0.31; + + selfPower = true; + + needsPower = true; + mass = 5.0; + maxDamage = 2.25; + destroyedLevel = 2.25; + disabledLevel = 1.35; + repairRate = 0; + explosion = TurretExplosion; + expDmgRadius = 15.0; + expDamage = 0.7; + expImpulse = 2000.0; + + deployedObject = true; + + thetaMin = 15; + thetaMax = 140; + //thetaNull = 90; + + isShielded = false; + energyPerDamagePoint = 50; + maxEnergy = 150; + + humSound = SensorHumSound; + heatSignature = 1; + pausePowerThread = true; + + canControl = true; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Deployed Base'; + targetTypeTag = 'Turret'; + sensorData = TurretBaseSensorObj; + sensorRadius = TurretBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + + firstPersonOnly = true; + + debrisShapeName = "debris_generic.dts"; + debris = TurretDebris; +}; + +function TurretDeployableImage::onDeploy(%item, %plyr, %slot) +{ +%deplObj = Parent::onDeploy(%item, %plyr, %slot); +%origBarrel = %item.item.origBarrel; +if(%origBarrel !$= "") +{ +%deplObj.mountImage(%origBarrel, 0, false); +%item.item.origBarrel = ""; +} +%playerVector = vectorNormalize(getWord(%plyr.getEyeVector(),1) SPC -1 * getWord(%plyr.getEyeVector(),0) SPC "0"); + +%item.surfacenrm = VectorNormalize(%item.surfacenrm); +//echo(%playervector); +if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + +%deplObj.setTransform(%item.surfacePt SPC %rot); +//%deplObj.setSelfPowered(); +//%deplObj.playThread($PowerThread,"Power"); + +addDSurface(%item.surface,%deplObj); +addToDeployGroup(%deplObj); +%deplObj.powerFreq = %plyr.powerFreq; +%deplObj.team = %plyr.team; +checkPowerObject(%deplObj); +} + +function TurretDeployableImage::onMount(%data, %obj, %node) { + displayPowerFreq(%obj); +} + +function TurretBasePack::onPickup(%this, %obj, %shape, %amount) +{ + // This is here to prevent console spam +} diff --git a/Scripts/Packs/vehiclepad.cs b/Scripts/Packs/vehiclepad.cs new file mode 100644 index 0000000..1682d5d --- /dev/null +++ b/Scripts/Packs/vehiclepad.cs @@ -0,0 +1,816 @@ +//====================================== +// made by dynablade +//====================================== Deployable Vehicle Pad + + +datablock StaticShapeData(DeployableVehicleStation) : StaticShapeDamageProfile +{ +className = Station; +catagory = "Stations"; +shapeFile = "Vehicle_pad_station.dts"; +maxDamage = 7.5; +destroyedLevel = 7.5; +disabledLevel = 7.5; +explosion = ShapeExplosion; +expDmgRadius = 10.0; +expDamage = 0.4; +expImpulse = 1500.0; +dynamicType = $TypeMasks::StationObjectType; +isShielded = true; +energyPerDamagePoint = 500; +maxEnergy = 250; +rechargeRate = 0.31; +humSound = StationVehicleHumSound; + +cmdCategory = "Support"; +cmdIcon = CMDVehicleStationIcon; +cmdMiniIconName = "commander/MiniIcons/com_vehicle_pad_inventory"; +targetTypeTag = 'Deployable Vehicle Station'; + +debrisShapeName = "debris_generic.dts"; +debris = StationDebris; +needsPower = true; +}; + +datablock StaticShapeData(DeployableVehiclePad) +{ +className = vpad; +catagory = "Stations"; +shapeFile = "Vehicle_pad.dts"; +maxDamage = 7.5; +destroyedLevel = 7.5; +disabledLevel = 7.5; +explosion = ShapeExplosion; +expDmgRadius = 10.0; +expDamage = 0.4; +expImpulse = 1500.0; +rechargeRate = 0.05; +targetTypeTag = 'Deployable Vehicle Station'; +needsPower = true; +}; + +datablock StaticShapeData(DeployableVehiclePad2) : DeployableVehiclePad +{ +className = vpad; +shapeFile = "station_teleport.dts"; +}; + + + + +datablock StaticShapeData(DeployableVehiclePadBottom) : StaticShapeDamageProfile { + className = "floor"; + shapeFile = "bmiscf.dts"; + + maxDamage = 4; + destroyedLevel = 4; + disabledLevel = 3.5; + + isShielded = true; + energyPerDamagePoint = 30; + maxEnergy = 200; + rechargeRate = 0.25; + + explosion = HandGrenadeExplosion; + expDmgRadius = 3.0; + expDamage = 0.1; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Medium Blast floor'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needsPower = true; +}; + +datablock StaticShapeData(PotPipe) : DeployableVehiclePadBottom +{ +shapeFile = "silver_pole.dts"; +targetNameTag = 'Pot Powered'; +targetTypeTag = 'Alloy forge'; +}; + +datablock ParticleData(DVPADP) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 1500; + lifetimeVarianceMS = 0; + + spinRandomMin = 30.0; + spinRandomMax = 30.0; + windcoefficient = 0; + textureName = "skins/jetflare03"; + + colors[0] = "0.3 0.3 1.0 0.1"; + colors[1] = "0.3 0.3 1.0 1"; + colors[2] = "0.3 0.3 1.0 1"; + colors[3] = "0.3 0.3 1.0 0.1"; + + sizes[0] = 5; + sizes[1] = 5; + sizes[2] = 5; + sizes[3] = 5; + + times[0] = 0.25; + times[1] = 0.5; + times[2] = 0.75; + times[3] = 1; + +}; + +datablock ParticleEmitterData(DVPADE) +{ + lifetimeMS = 10; + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 0.01; + velocityVariance = 0.0; + ejectionoffset = 8; + thetaMin = 80.0; + thetaMax = 100.0; + + phiReferenceVel = "180"; + phiVariance = "5"; + orientParticles = false; + orientOnVelocity = false; + + particles = "DVPADP"; +}; + +datablock ParticleData(SIGMAP) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.5; + inheritedVelFactor = 0.0; + + lifetimeMS = 1500; + lifetimeVarianceMS = 1000; + + spinRandomMin = -30.0; + spinRandomMax = 30.0; + windcoefficient = 0; + textureName = "skins/jetflare03"; + + colors[0] = "1 1 0 0"; //Wacky collors :P + colors[1] = "0 1 1 1"; + colors[2] = "1 0 1 1"; + colors[3] = "0 1 0 1"; + + sizes[0] = 5; + sizes[1] = 5; + sizes[2] = 5; + sizes[3] = 5; + + times[0] = 0.5; + times[1] = 0.6; + times[2] = 0.8; + times[3] = 1; + +}; + +datablock ParticleEmitterData(SIGMAE) +{ + lifetimeMS = 10; + ejectionPeriodMS = 50; + periodVarianceMS = 0; + + ejectionVelocity = 1.0; + velocityVariance = 0.5; + ejectionoffset = 0.5; + thetaMin = 80.0; + thetaMax = 100.0; + + phiReferenceVel = "0"; + phiVariance = "360"; + orientParticles = false; + orientOnVelocity = false; + + particles = "SIGMAP"; +}; + + +function DeployableVehicleStation::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.75 0.75 0.0 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.0"; + }; + MissionCleanup.add(%trigger); + %trigger.setTransform(%obj.getTransform()); + %trigger.station = %obj; + %obj.trigger = %trigger; +} + +function DeployableVehicleStation::stationReady(%data, %obj) +{ + // Make sure none of the other popup huds are active: + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'scoreScreen' ); + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'inventoryScreen' ); + + //Display the Vehicle Station GUI + commandToClient(%obj.triggeredBy.client, 'StationVehicleShowHud'); +} + +function DeployableVehicleStation::stationFinished(%data, %obj) +{ + //Hide the Vehicle Station GUI + commandToClient(%obj.triggeredBy.client, 'StationVehicleHideHud'); +} + +function DeployableVehicleStation::getSound(%data, %forward) +{ + if(%forward) + return "StationVehicleAcitvateSound"; + else + return "StationVehicleDeactivateSound"; +} + +function DeployableVehicleStation::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %posXY = getWords(%trigger.getTransform(),0 ,1); + %posZ = getWord(%trigger.getTransform(), 2); + %rotZ = getWord(%obj.getTransform(), 5); + %angle = getWord(%obj.getTransform(), 6); + %angle += 3.141592654; + if(%angle > 6.283185308) + %angle = %angle - 6.283185308; + %colObj.setvelocity("0 0 0"); + %colObj.setTransform(%posXY @ " " @ %posZ + 0.2 @ " " @ "0 0 " @ %rotZ @ " " @ %angle );//center player on object + return true; + } + return false; +} + +function DeployableVehiclePad::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.ready = true; + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); +} + +function DeployableVehiclePad2::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.ready = true; + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); +} + + +function GiveStation(%obj,%transform) +{ + %pos = getWords(%transform,0,2); + %rot = getWords(%transform,3,5) SPC (getWord(%transform,6)/3.14*180); + %sv = new StaticShape() + { + scale = "1 1 1"; + dataBlock = DeployableVehicleStation; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + position = %pos; + rotation = %rot; + }; + + %sv.setTransform(%transform); + %sv.powerFreq = %obj.powerFreq; + MissionCleanup.add(%sv); + //%sv.getDataBlock().gainPower(%sv); + //%obj.getDatablock().gainPower(%obj); + checkPowerObject(%obj); + checkPowerObject(%sv); + %sv.pad = %obj; + %obj.station = %sv; + %sv.trigger.mainObj = %obj; + %sv.trigger.disableObj = %sv; + adjustTrigger(%sv); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Original Code in Station.cs +// Code made by Blnukem. +//------------------------------------------------------------------------------ + + // Basic Restriction to Construction Vehicles in the Construction Gametype. + if ($CurrentMissionType $= "Construction") { + %sv.vehicle[HawkFlyer] = true; + %sv.vehicle[Personelboat] = true; + // Now to set the vehicle defaults for Non-Construction Gametypes. + } else if ($CurrentMissionType !$= "Construction") { + %sv.vehicle[boat] = true; + %sv.vehicle[sub] = true; + %sv.vehicle[Personelboat] = true; + %sv.vehicle[scoutFlyer] = true; + %sv.vehicle[AWACS] = true; + %sv.vehicle[helicopter] = true; + %sv.vehicle[HeavyChopper] = true; + } +} + + function GiveStation2(%obj,%transform) +{ + %pos = getWords(%transform,0,2); + %rot = getWords(%transform,3,5) SPC (getWord(%transform,6)/3.14*180); + %sv = new StaticShape() + { + scale = "1 1 1"; + dataBlock = DeployableVehicleStation; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + position = %pos; + rotation = %rot; + }; + + %sv.setTransform(%transform); + %sv.powerFreq = %obj.powerFreq; + MissionCleanup.add(%sv); + checkPowerObject(%obj); + checkPowerObject(%sv); + %sv.pad = %obj; + %obj.station = %sv; + %sv.trigger.mainObj = %obj; + %sv.trigger.disableObj = %sv; + adjustTrigger(%sv); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Original Code in Station.cs +// Code made by Blnukem. +//------------------------------------------------------------------------------ + + // Basic Restriction to Construction Vehicles in the Construction Gametype. + if ($CurrentMissionType $= "Construction") { + %sv.vehicle[SuperScoutVehicle] = true; + %sv.vehicle[HawkFlyer] = true; + %sv.vehicle[mobileBasevehicle] = true; + %sv.vehicle[hapcFlyer] = true; + %sv.vehicle[bomberFlyer] = true; + // Now to set the vehicle defaults for Non-Construction Gametypes. + } else if ($CurrentMissionType !$= "Construction") { + %sv.vehicle[mobileBasevehicle] = true; + %sv.vehicle[scoutFlyer] = true; + %sv.vehicle[hapcFlyer] = true; + %sv.vehicle[CGTank] = true; + %sv.vehicle[helicopter] = true; + %sv.vehicle[StrikeFlyer] = true; + } +} + +function DeployableVehiclePad::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $ActivateThread) + { + %obj.ready = true; + %obj.stopThread($ActivateThread); + } + Parent::onEndSequence(%data, %obj, %thread); +} + +function DeployableVehiclePad2::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $ActivateThread) + { + %obj.ready = true; + %obj.stopThread($ActivateThread); + } + Parent::onEndSequence(%data, %obj, %thread); +} + +function DeployableVehiclePad::gainPower(%data, %obj) +{ + if (isObject(%obj.station)) + %obj.station.setSelfPowered(); + Parent::gainPower(%data, %obj); +} + +function DeployableVehiclePad2::gainPower(%data, %obj) +{ + if (isObject(%obj.station)) + %obj.station.setSelfPowered(); + Parent::gainPower(%data, %obj); +} + +function DeployableVehiclePad::losePower(%data, %obj) +{ + if (isObject(%obj.station)) + %obj.station.clearSelfPowered(); + Parent::losePower(%data, %obj); +} + +function DeployableVehiclePad2::losePower(%data, %obj) +{ + if (isObject(%obj.station)) + %obj.station.clearSelfPowered(); + Parent::losePower(%data, %obj); +} + +function DeployableVehiclePad::onDestroyed(%this, %obj, %prevState) +{ + if (isObject(%obj)) + disassembleVehilcepad(%obj,%plyr); + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, VehiclePadPack]--; + %obj.schedule(500, "delete"); + %obj.station.schedule(500, "delete"); + %obj.back.schedule(500,"delete"); +} + +function DeployableVehiclePad2::onDestroyed(%this, %obj, %prevState) +{ + if (isObject(%obj)) + disassembleVehilcepad(%obj,%plyr); + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, VehiclePadPack]--; + %obj.schedule(500, "delete"); + %obj.station.schedule(500, "delete"); + %obj.back.schedule(500,"delete"); +} + + + +function DeployableVehicleStation::onDestroyed(%this, %obj, %prevState) +{ + if (isObject(%obj.pad)) + disassembleVehilcepad(%obj.pad,%plyr); + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, VehiclePadPack]--; + %obj.schedule(500, "delete"); + %obj.station.schedule(500, "delete"); + %obj.back.schedule(500,"delete"); +} + +function DeployableVehiclePadBottom::onDestroyed(%this, %obj, %prevState) +{ + if (isObject(%obj.station)) + disassembleVehilcepad(%obj.station,%plyr); + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, VehiclePadPack]--; + %obj.schedule(500, "delete"); + %obj.station.schedule(500, "delete"); + %obj.back.schedule(500,"delete"); +} + + + + +datablock ShapeBaseImageData(VehiclePadPackImage) +{ + mass = 10; + emap = true; + + shapeFile = "pack_deploy_inventory.dts"; + item = VehiclePadPack; + mountPoint = 1; + offset = "0 0 0"; + heatSignature = 0; + deployed = DeployableVehiclePad; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + maxDepSlope = 360; + deploySound = StationDeploySound; + + minDeployDis = 0; + maxDeployDis = 50.0; //meters from body +}; + +datablock ItemData(VehiclePadPack) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_inventory.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "VehiclePadPackImage"; + pickUpName = "a deployable vehicle pad"; + heatSignature = 0; + + emap = true; +}; + +function VehiclePadPackImage::onMount(%this, %obj, %slot) +{ + %this.imagemount = %obj; + %obj.hasVehiclepad = 1; +} + +function VehiclePadPackImage::onUnmount(%data,%obj,%node) +{ + %obj.hasVehiclepad = ""; +} + +function VehiclePadPack::onPickup(%this, %pack, %player, %amount) +{ + %player.packcharge = %pack.charge; + %player.lastvpad = %pack.vpad; +} + + +function VehiclePadPack::onThrow(%this,%pack,%player) +{ + %this.charge = %player.packcharge; + %this.vpad = %player.lastvpad; + %player.packcharge = ""; + %player.lastvpad = ""; + serverPlay3D(ItemThrowSound, %player.getTransform()); + %pack.schedulePop(); +} + + + +function VehiclePadPackImage::onDeploy(%item, %plyr, %slot) +{ + if (IsObject(%plyr.lastvpad) && IsObject(%plyr.lastvpad.station)) + %plyr.packcharge = 0; + if (IsObject(%plyr.lastvpad) && !IsObject(%plyr.lastvpad.station)) + %plyr.packcharge = 1; + if (!IsObject(%plyr.lastvpad)) + %plyr.packcharge = 0; + + + // take the deployable off the player's back and out of inventory + if (%plyr.packcharge == 1) + { + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + $TeamDeployedCount[%plyr.team, %item.item]++; + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = vectorScale(%playerVector,1); + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 -1")); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %vpad = %plyr.lastvpad; + if (%plyr.packset == 1) + %deplObj = GiveStation(%vpad,%item.surfacePt SPC %rot); + else + %deplObj = GiveStation2(%vpad,%item.surfacePt SPC %rot); + %deplObj.deploy(); + addDSurface(%item.surface,%deplObj); + %deplObj.setOwner(%plyr); + %plyr.packcharge = ""; + %plyr.lastvpad = ""; + %vpad.isRemoved = 0; + %vpad.back.isRemoved = 0; + if (%vpad.style ==2) + { + %deplObj.emitter = CreateEmitter(%item.surfacePt,SIGMAE); + %deplObj.emitter.setRotation(%deplObj.getRotation()); + } + } + else + { + %dist = VectorSub(%item.surfacept,%plyr.getTransform()); + %nrm = VirVec(%item.surface,%item.surfacenrm); + %img = VirVec(%item.surface,VectorNormalize(VectorCross(VectorCross(%item.surfacenrm,%dist),%item.surfacenrm))); + %item.surfacenrm2 = realvec(%item.surface,VectorCross(%nrm,topvec(%img))); + // create the actual deployable + %rot = %item.getInitialRotation(%plyr); + + if (%plyr.packset == 1) + %block = DeployableVehiclePad2; + else + %block = DeployableVehiclePad; + + %deplObj = new StaticShape() + { + dataBlock = %block; + }; + %back = new StaticShape() + { + dataBlock = DeployableVehiclePadBottom; + }; + %back.needsfit = 1; + %deplObj.needsfit = 1; + %deplObj.style = %plyr.packset; + %pos = getWords(%item.surface.getEdge(%nrm),0,2); + %deplObj.setTransform(%pos SPC fullrot(%item.surfacenrm,%item.surfacenrm2)); + if (VectorDot(vAbs(VectorNormalize(topvec(%img))),%item.surface.getRealSize())== %item.surfacesizex) + { + %x= %item.surfacesizex; + %y= %item.surfacesizey; + %item.surfacesizex = %y; + %item.surfacesizey = %x; + } + %deplObj.setRealSize(%item.surfacesizex SPC %item.surfacesizey SPC "1.5"); + %back.setTransform(%pos SPC fullrot(%item.surfacenrm,%item.surfacenrm2)); + %back.setRealSize(VectorMultiply(%deplObj.getRealSize(),"1 1 0.1")); + if (%plyr.packset == 2) + { + %back.potpipes(); + } + // %deplObj.setTransform(modifyTransform(%pos SPC %rot, "0 0 -1 0 0 0 0")); + %deplObj.back = %back; + %back.station = %deplObj; + // set the recharge rate right away + if(%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %back.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + %back.setOwner(%plyr); + + // set the sensor group if it needs one + if(%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + addToDeployGroup(%deplObj.back); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + %deplObj.getDatablock().onAdd(%deplObj); + %deplObj.deploy(); + //%deplObj.setSelfPowered(); + %plyr.packcharge++; + %plyr.lastvpad = %deplObj; + %deplObj.powerFreq = %plyr.powerFreq; + %back.powerFreq = %plyr.powerFreq; + checkPowerObject(%deplobj); + checkPowerObject(%back); + %deplObj.isRemoved = 1; + %back.isRemoved = 1; + schedule(6000,0,"FadePad",%deplObj,%plyr); + addDSurface(%item.surface,%deplObj); + addDSurface(%item.surface,%back); + } +} + +function FadePad(%obj,%plyr) +{ + if (!Isobject(%obj.station)) + { + schedule(100,%obj,"disassembleVehilcepad",%obj,%plyr); + } +} + +function GameBase::PotPipes(%obj) +{ + %obj.pipe1 = new StaticShape() + { + dataBlock = PotPipe; + scale = "0.5 0.5 0.5"; + }; + %obj.pipe2 = new StaticShape() + { + dataBlock = PotPipe; + scale = "0.5 0.5 0.5"; + }; + %obj.pipe3 = new StaticShape() + { + dataBlock = PotPipe; + scale = "0.5 0.5 0.5"; + }; + %obj.pipe4 = new StaticShape() + { + dataBlock = PotPipe; + scale = "0.5 0.5 0.5"; + }; + %obj.pipe1.setTransform(%obj.getEdge("0.9 0.9 1") SPC %obj.getRotation()); + %obj.pipe2.setTransform(%obj.getEdge("0.9 -0.9 1")SPC %obj.getRotation()); + %obj.pipe3.setTransform(%obj.getEdge("-0.9 -0.9 1")SPC %obj.getRotation()); + %obj.pipe4.setTransform(%obj.getEdge("-0.9 0.9 1")SPC %obj.getRotation()); + %obj.pipe1.emitter = CreateEmitter(%obj.pipe1.getEdge("0 0 1"),HeavyDamageSmoke); + %obj.pipe2.emitter = CreateEmitter(%obj.pipe2.getEdge("0 0 1"),HeavyDamageSmoke); + %obj.pipe3.emitter = CreateEmitter(%obj.pipe3.getEdge("0 0 1"),HeavyDamageSmoke); + %obj.pipe4.emitter = CreateEmitter(%obj.pipe4.getEdge("0 0 1"),HeavyDamageSmoke); + addToDeployGroup(%obj.pipe1); + addToDeployGroup(%obj.pipe2); + addToDeployGroup(%obj.pipe3); + addToDeployGroup(%obj.pipe4); +} + +function GameBase::RemPotPipes(%obj) +{ + if (IsObject(%obj.pipe1)) + { + %obj.pipe1.emitter.delete(); + %obj.pipe1.delete(); + } + if (IsObject(%obj.pipe2)) + { + %obj.pipe2.emitter.delete(); + %obj.pipe2.delete(); + } + if (IsObject(%obj.pipe3)) + { + %obj.pipe3.emitter.delete(); + %obj.pipe3.delete(); + } + if (IsObject(%obj.pipe4)) + { + %obj.pipe4.emitter.delete(); + %obj.pipe4.delete(); + } +} + +function VehiclePadPackImage::testSurfaceTooNarrow(%item,%surface) +{ + %nrm = getWords(%item.surface,4,6); + %mask = invFace(%nrm); + %narrower = vectorMultiply(%mask,%item.surface.getRealSize()); + %fx = vectorNormalize(topVec(%narrower)); + %fy = VectorCross(%nrm,%fx); + %sx = VectorLen(VectorMultiply(%fx,%item.surface.getRealSize())); + %sy = VectorLen(VectorMultiply(%fy,%item.surface.getRealSize())); + + if (%sx < 17 || %sy < 17 || %sx < %sy*0.5 || %sy < %sx*0.5) + { + return !%item.imagemount.packcharge; + } + else + { + %item.surfacesizex = %sx; + %item.surfacesizey = %sy; + return false; + } +} + +function VehiclePadPackImage::testNoInteriorFound(%item,%surface) +{ + return !IsCubic(%item.surface) && !%item.imagemount.packcharge; +} + +function DeployableVehiclePad::disassemble(%data,%plyr,%obj) +{ + if (isObject(%obj)) + disassembleVehilcepad(%obj,%plyr); + disassemble(%data,%plyr,%obj); +} + +function DeployableVehiclePad2::disassemble(%data,%plyr,%obj) +{ + if (isObject(%obj)) + disassembleVehilcepad(%obj,%plyr); + disassemble(%data,%plyr,%obj); +} + +function DeployableVehicleStation::disassemble(%data,%plyr,%obj) +{ + if (isObject(%obj.pad)) + disassembleVehilcepad(%obj.pad,%plyr); + disassemble(%data,%plyr,%obj); +} + +function DeployableVehiclePadBottom::disassemble(%data,%plyr,%obj) +{ + if (isObject(%obj.station)) + disassembleVehilcepad(%obj.station,%plyr); + disassemble(%data,%plyr,%obj); +} + +function PotPipe::disassemble(%data,%plyr,%obj) +{ + if (%obj.emitter) + %obj.emitter.delete(); + disassemble(%data,%plyr,%obj); +} + +function disassembleVehilcepad(%station,%plyr) +{ + %station.back.rempotpipes(); + if (%station.station.emitter) + %station.station.emitter.delete(); + if (isObject(%station.station)) + disassemble("",%plyr,%station.station); + + if (isObject(%station.back)) + disassemble("",%plyr,%station.back); + disassemble("",%plyr,%station); +} diff --git a/Scripts/Packs/vehiclerepairpad.cs b/Scripts/Packs/vehiclerepairpad.cs new file mode 100644 index 0000000..24cfbf4 --- /dev/null +++ b/Scripts/Packs/vehiclerepairpad.cs @@ -0,0 +1,292 @@ +datablock StaticShapeData(RepairPad) : StaticShapeDamageProfile +{ + className = Station; + shapeFile = "station_teleport.dts"; + maxDamage = 5.50; + destroyedLevel = 5.50; + disabledLevel = 4.75; + explosion = DeployablesExplosion; + expDmgRadius = 15.0; + expDamage = 0.65; + expImpulse = 1500.0; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = false; + energyPerDamagePoint = 110; + maxEnergy = 50; + rechargeRate = 0.20; + renderWhenDestroyed = false; + doesRepair = true; + + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Vehicle'; + targetTypeTag = 'Repair Pad'; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; + needspower = true; +}; + +datablock ShapeBaseImageData(RepairPadImage) +{ + mass = 15; + emap = true; + + shapeFile = "pack_deploy_inventory.dts"; + item = RepairPadDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = RepairPad; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 30; + deploySound = StationDeploySound; + + flatMinDeployDis = 1.0; + flatMaxDeployDis = 5.0; + + minDeployDis = 2.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(RepairPadDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_inventory.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "RepairPadImage"; + pickUpName = "a vehicle repair pad"; + heatSignature = 0; + + computeCRC = true; + emap = true; + +}; + +function RepairPadImage::onDeploy(%item, %plyr, %slot) { + //Object + %className = "StaticShape"; + + %grounded = 0; + if (%item.surface.getClassName() $= TerrainBlock) + %grounded = 1; + + %playerVector = vectorNormalize(-1 * getWord(%plyr.getEyeVector(),1) SPC getWord(%plyr.getEyeVector(),0) SPC "0"); + + if (%item.surfaceinher == 0) { + if (vAbs(floorVec(%item.surfaceNrm,100)) $= "0 0 1") + %item.surfaceNrm2 = %playerVector; + else + %item.surfaceNrm2 = vectorNormalize(vectorCross(%item.surfaceNrm,"0 0 1")); + } + + %item.surfaceNrm3 = vectorCross(%item.surfaceNrm,%item.surfaceNrm2); + + %rot = fullRot(%item.surfaceNrm,%item.surfaceNrm2); + %dataBlock = %item.deployed; + + + %deplObj = new (%className)() { + dataBlock = %dataBlock; + scale = "4.5 4.5 1"; + }; + +//////////////////////////Apply settings////////////////////////////// + + // [[Location]]: + + // exact: + %deplObj.setTransform(%item.surfacePt SPC %rot); + + // misc info + addDSurface(%item.surface,%deplObj); + + // [[Settings]]: + + %deplObj.grounded = %grounded; + %deplObj.needsFit = 1; + + // [[Normal Stuff]]; + + // set team, owner, and handle + %deplObj.team = %plyr.client.team; + %deplObj.setOwner(%plyr); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + $TeamDeployedCount[%plyr.team, %item.item]++; + + %deplObj.deploy(); + + // set power frequency + %deplObj.powerFreq = %plyr.powerFreq; + + // Power object + checkPowerObject(%deplObj); + + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + return %deplObj; +} + +function RepairPad::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + if(%obj.isreping == 1) + stopVRepairLoop(%obj); + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, RepairPadDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); + fireBallExplode(%obj,1); +} + +function RepairPad::onGainPowerEnabled(%data,%obj) { + if (%obj.reploop !$= "") + Cancel(%obj.repLoop); + %obj.isreping = 1; + %obj.repLoop = schedule(1000, 0, "VRepairLoop", %obj); + Parent::onGainPowerEnabled(%data,%obj); +} + +function RepairPad::onLosePowerDisabled(%data,%obj) { + if (%obj.ZCloop !$= ""){ + Cancel(%obj.repLoop); + stopVRepairLoop(%obj); + } + Parent::onLosePowerDisabled(%data,%obj); +} + +function VRepairLoop(%obj){ + if(isObject(%obj)){ + if(%obj.isreping == 0) + return; + + %targets = %obj.reptargets; + %pos = %obj.getWorldBoxCenter(); + if(%targets !$= ""){ + %numtrgs = getNumberOfWords(%targets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%targets, %i); + if(vectorDist(%pos, %target.getWorldBoxCenter()) <= 20 && %target.getDamageLevel() > 0.0){ + if(%target.reping != 1){ + %target.reping = 1; + %target.setRepairRate(%target.getRepairRate() + 0.01); + if(%target.affected && %target.getDamagelevel() < %target.getDatablock().HDAddMassLevel) { + if (%target.lastPilot.isPilot() == true) + messageClient(%target.lastPilot.client, '', 'Vehicle Repaired, and Manuverability restored!'); + %target.unmountImage(7); + %target.Affected = 0; + if(%target.getClassName() $= "FlyingVehicle") + Cancel(%target.dmgStallLoop); + if(%target.getdatablock().getname() $= "scoutFlyer" || %target.getdatablock().getname() $= "strikeFlyer") + checkStallLoop(%obj); + } + } + } + else{ + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - 0.01); + } + } + %datablock = %target.getDatablock(); + if(%datablock.max[chaingunAmmo] !$= "" && %target.inv[chaingunAmmo] < %datablock.max[chaingunAmmo]){ + if(%datablock.max[chaingunAmmo] < 100) + %CGamount = 10; + else + %CGamount = 100; + %target.incInventory(chaingunAmmo, %CGamount); + %reloaded = 1; + } + else if(%datablock.max[MissileLauncherAmmo] !$= "" && %target.inv[MissileLauncherAmmo] < %datablock.max[MissileLauncherAmmo]){ + %target.incInventory(MissileLauncherAmmo, 1); + %reloaded = 1; + } + else if(%datablock.max[MortarAmmo] !$= "" && %target.inv[MortarAmmo] < %datablock.max[MortarAmmo]){ + %target.incInventory(MortarAmmo, 1); + %reloaded = 1; + } + else if(%datablock.max[PlasmaAmmo] !$= "" && %target.inv[PlasmaAmmo] < %datablock.max[PlasmaAmmo]){ + %target.incInventory(PlasmaAmmo, 4); + %reloaded = 1; + } + if(%reloaded == 1){ + %reloaded = 0; + serverPlay3d("MissileReloadSound",%target.getWorldBoxCenter()); + } + + if(isObject(%target.turretObject)){ + %datablock = %target.turretObject.getDatablock(); + if(%datablock.max[chaingunAmmo] !$= "" && %target.turretObject.inv[chaingunAmmo] < %datablock.max[chaingunAmmo]){ + %target.turretObject.incInventory(chaingunAmmo, 100); + %reloaded = 1; + } + else if(%datablock.max[MissileLauncherAmmo] !$= "" && %target.turretObject.inv[MissileLauncherAmmo] < %datablock.max[MissileLauncherAmmo]){ + %target.turretObject.incInventory(MissileLauncherAmmo, 1); + %reloaded = 1; + } + else if(%datablock.max[MortarAmmo] !$= "" && %target.turretObject.inv[MortarAmmo] < %datablock.max[MortarAmmo]){ + %target.turretObject.incInventory(MortarAmmo, 1); + %reloaded = 1; + } + if(%reloaded == 1) + serverPlay3d("MissileReloadSound",%target.getWorldBoxCenter()); + } + } + } + %obj.reptargets = ""; + InitContainerRadiusSearch(%pos, 15, $TypeMasks::VehicleObjectType); + while ((%targetObject = containerSearchNext()) != 0){ + %obj.reptargets = %obj.reptargets @ %targetObject @" "; + } + if(%obj.isreping == 1) + %obj.reploop = schedule(500, 0, "VRepairLoop", %obj); + } +} + +function stopVRepairLoop(%obj){ + %obj.isreping = 0; + if(%obj.reptargets !$= ""){ + %numtrgs = getNumberOfWords(%obj.reptargets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%obj.reptargets, %i); + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - 0.01); + } + } + } +} diff --git a/Scripts/RankStuff.cs b/Scripts/RankStuff.cs new file mode 100644 index 0000000..cc37816 --- /dev/null +++ b/Scripts/RankStuff.cs @@ -0,0 +1,842 @@ +//-------------------------------------------------- +//RANKS +//-------------------------------------------------- + +$Ranks::MinPoints[0] = 0; +$Ranks::NewRank[0] = "Private"; +$Ranks::MinPoints[1] = 250; +$Ranks::NewRank[1] = "Private First Class"; +$Ranks::MinPoints[2] = 500; +$Ranks::NewRank[2] = "Corporal"; +$Ranks::MinPoints[3] = 1250; +$Ranks::NewRank[3] = "Specialist"; +$Ranks::MinPoints[4] = 1800; +$Ranks::NewRank[4] = "Sergeant"; +$Ranks::MinPoints[5] = 2550; +$Ranks::NewRank[5] = "Staff Sergeant"; +$Ranks::MinPoints[6] = 3600; +$Ranks::NewRank[6] = "Sergeant First Class"; +$Ranks::MinPoints[7] = 4800; +$Ranks::NewRank[7] = "Master Sergeant"; +$Ranks::MinPoints[8] = 6000; +$Ranks::NewRank[8] = "Second Lieutenant"; +$Ranks::MinPoints[9] = 8500; +$Ranks::NewRank[9] = "First Lieutenant"; +$Ranks::MinPoints[10] = 9750; +$Ranks::NewRank[10] = "Captain"; +$Ranks::MinPoints[11] = 11000; +$Ranks::NewRank[11] = "Major"; +$Ranks::MinPoints[12] = 15500; +$Ranks::NewRank[12] = "Lieutenant Colonel"; +$Ranks::MinPoints[13] = 18500; +$Ranks::NewRank[13] = "Colonel"; +$Ranks::MinPoints[14] = 22500; +$Ranks::NewRank[14] = "Brigadier General"; +$Ranks::MinPoints[15] = 30000; +$Ranks::NewRank[15] = "Major General"; +$Ranks::MinPoints[16] = 50000; +$Ranks::NewRank[16] = "Lieutenant General"; +$Ranks::MinPoints[17] = 75000; +$Ranks::NewRank[17] = "General"; +$Ranks::MinPoints[18] = 90000; +$Ranks::NewRank[18] = "General Of The Army"; +exec( "prefs/Ranks.cs" ); +exec( "prefs/Squads.cs" ); +$canRecalcTop5 = 1; + +function updateRankScores(%shouldloop,%clearLS){ + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + if($Rank::numplayers $= "") + $Rank::numplayers=0; + %cl = ClientGroup.getObject(%i); + %name = %cl.NameBase; + if(%cl.lastscore $= "") + %cl.lastscore = 0; + if(%cl.ranknum $= ""){ + for(%k = 0; %k < $Rank::numplayers; %k++){ + if(%name $= $Rank::Name[%k]){ + %cl.ranknum = %k; + %k = $Rank::numplayers - 1; + } + else if(%k >= ($Rank::numplayers - 1)){ + $Rank::Name[$Rank::numplayers] = %name; + $Rank::Rank[$Rank::numplayers] = "Private"; + $Rank::Score[$Rank::numplayers] = 0; + $Rank::numplayers++; + } + } + if(%k == 0 && %k == $Rank::numplayers){ + $Rank::Name[$Rank::numplayers] = %name; + $Rank::Rank[$Rank::numplayers] = "Private"; + $Rank::Score[$Rank::numplayers] = 0; + $Rank::numplayers++; + } + } + if($Rank::Squad[%cl.ranknum] !$= ""){ + %squad = getWord($Rank::Squad[%cl.ranknum],1); + $squad::Score[%squad] = $squad::Score[%squad] + (%cl.score - %cl.lastscore); + %mem = 0; + %num = getWord($Rank::squad[%cl.ranknum],1)@%mem; + while($squad::member[%num] !$= ""){ + %player = $squad::member[%num]; + for(%l = 0; %l < %count; %l++){ + %clt = ClientGroup.getObject(%l); + if(%clt.ranknum == %player){ + $Rank::Squadscore[%player] = $Rank::Squadscore[%player] + ((%cl.score - %cl.lastscore) / 2); + $Rank::Score[%player] = $Rank::Score[%player] + ((%cl.score - %cl.lastscore) / 2); + %l = %count; + } + } + %mem++; + %num = %squad@%mem; + } + } + $Rank::Score[%cl.ranknum] = $Rank::Score[%cl.ranknum] + (%cl.score - %cl.lastscore); + %stat = $Rank::Score[%cl.ranknum]; + %cl.lastscore = %cl.score; + for(%j = 18; %j > 0; %j--){ + if($Rank::Score[%cl.ranknum] >= $Ranks::MinPoints[%j]){ + if($Rank::Rank[%cl.ranknum] !$= $Ranks::NewRank[%j]){ + $Rank::Rank[%cl.ranknum] = $Ranks::NewRank[%j]; + messageAll('msgClient',"\c3"@%name@" \c2has become a\c3 "@$Ranks::NewRank[%j]@" \c2with a score of\c3 "@$Rank::Score[%cl.ranknum]@"\c2!"); + bottomPrint(%cl, "Congratulations "@%name@" you have been promoted to the rank of: "@$Ranks::NewRank[%j]@"!", 5, 2 ); + } + %j = 1; + } + } + if(%clearLS == 1){ + %cl.lastscore = 0; + %cl.Zkills = 0; + } + } + export( "$squad::*", "prefs/Squads.cs", false ); + export( "$Rank::*", "prefs/Ranks.cs", false ); + if(%shouldloop == 1) + $RankUpdate = schedule(60000,0,"updateRankScores",1); +} + +function ccCheckStats(%client, %args){ + if(%args $= ""){ + if(%client.ranknum $= ""){ + messageClient(%client, 'MsgClient', "\c2Please wait a minute for your stats to load"); + return; + } + %name = %client.NameBase; + %Rank = $Rank::Rank[%client.ranknum]; + %Stats = $Rank::Score[%client.ranknum]; + for(%i = 18; %i >= 0; %i--){ + if($Rank::Score[%client.ranknum] >= $Ranks::MinPoints[%i]){ + %nextrank = $Ranks::NewRank[(%i + 1)]; + %nextrankscore = $Ranks::MinPoints[(%i + 1)]; + %i = 0; + } + } + messageClient(%client, 'MsgClient', "\c2Your current rank is a\c3 "@%Rank@" \c2and you have a total of\c3 "@%stats@" \c2points. Your next rank is a\c3 "@%nextrank@" \c2and you need\c3 "@(%nextrankscore - %stats)@" \c2points."); + } + else{ + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.nameBase $= %args){ + if(%cl.ranknum $= ""){ + messageClient(%client, 'MsgClient', "\c2Please wait a minute for this persons stats to load"); + return; + } + %Rank = $Rank::Rank[%cl.ranknum]; + %Stats = $Rank::Score[%cl.ranknum]; + messageClient(%client, 'MsgClient', "\c3"@%args@"\'s \c2rank is a\c3 "@%Rank@"\c2 and his total score is: \c3"@%stats@"\c2 points."); + %i = %count; + } + else if(%i == (%count - 1)) + messageClient(%client, 'MsgClient', "\c3"@%args@" \c2is not a valid player."); + } + } +} + +function FindTopRanks() +{ + %noabove = 1000000; + for (%i = 1; %i <= 5; %i++) + { + for (%j = 0; %j < $Rank::numplayers; %j++) + { + if (($Rank::Score[%j] >= %highest || %highest $= "") && $Rank::Score[%j] < %noabove) + { + %highest = $Rank::Score[%j]; + %player = %j; + } + } + + $Rank::Top[%i] = $Rank::Name[%player]; + $Rank::TopScore[%i] = %highest; + %noabove = %highest; + %highest = ""; + } +} + +function ccTop5(%client,%args) +{ + if($canrecalcTop5 == 1) + { + FindTopRanks(); + $canrecalcTop5 = 0; + schedule(30000, 0, "restorerecalc"); + } + messageClient(%client, 'MsgClient', "\c2Top five players are:"); + + if ($Rank::numplayers > 5) + { + %num = 5; + } else if ($Rank::numplayers <= 5) + %num = $Rank::numplayers; + + for(%i = 1; %i <= %num; %i++) + { + messageClient(%client, 'MsgClient', "\c2"@%i@". "@$Rank::Top[%i]@" - Score: "@$Rank::TopScore[%i]@""); + } +} + +function restorerecalc(){ + $canreclacTop5 = 1; +} + +//-------------------------------------------------- +//SQUADS +//-------------------------------------------------- + +function cccreatesquad(%sender, %args){ + if(%sender.ranknum $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please wait a minute for your stats to load.'); + return; + } + if($Rank::Score[%sender.ranknum] < $Rank::MinPoints[4]){ + messageclient(%sender, 'MsgClient', '\c2You must have a Sergeant rank or higher to make a squad.'); + return; + } + if(%args $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please specify the name of new squad.'); + return; + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(getWord(%args,0) $= $squad::Name[%i]){ + messageclient(%sender, 'MsgClient', '\c2This squad already exists.'); + return; + } + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%sender.namebase $= $squad::Leader[%i]){ + messageclient(%sender, 'MsgClient', '\c2You already have a squad, you may not create a new one.'); + return; + } + if($squad::Leader[%i] $= "") + %replacable = %i; + } + if($squad::numsquads $= "") + $squad::numsquads = 0; + if(%replacable !$= ""){ + $squad::Name[%replacable] = getWord(%args,0); + $squad::Leader[%replacable] = %sender.namebase; + $squad::Score[%replacable] = 0; + } else{ + $squad::Name[$squad::numsquads] = getWord(%args,0); + $squad::Leader[$squad::numsquads] = %sender.namebase; + $squad::Score[$squad::numsquads] = 0; + $squad::numsquads++; + } + messageclient(%sender, 'MsgClient', "\c2Squad\c3 "@getWord(%args,0)@" \c2has been created."); + ccJoin(%sender,getWord(%args,0)); +} + +function ccJoin(%sender,%name){ + if(%sender.ranknum $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please wait a minute for your stats to load.'); + return; + } + if($Rank::Squad[%sender.ranknum] !$= ""){ + messageClient(%client, 'MsgClient', "\c2You are already in a squad."); + return; + } + if(%name $= ""){ + messageclient(%sender, 'MsgClient', '\c2You have not been invited to a squad yet.'); + return; + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%name $= $squad::Name[%i]){ + %mem = 0; + %num = %i@%mem; + while($squad::member[%num] !$= ""){ + %mem++; + %num = %i@%mem; + } + $squad::member[%num] = %sender.ranknum; + $Rank::Squad[%sender.ranknum] = %name@" "@%i; + $Rank::SquadScore[%sender.ranknum] = 0; + messageclient(%sender, 'MsgClient', "\c2You are now a part of squad\c3 "@%name@"\c2."); + return; + } + } + messageclient(%sender, 'MsgClient', "\c2Squad\c3 "@%name@" \c2dosent exist."); +} + +function ccLeaveSquad(%sender,%args){ + if(%sender.ranknum $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please wait a minute for your stats to load.'); + return; + } + if($Rank::Squad[%sender.ranknum] $= ""){ + messageClient(%sender, 'MsgClient', "\c2You are not currently in a squad."); + return; + } + %squad = getWord($Rank::Squad[%sender.ranknum],1); + if($squad::Leader[%squad] $= %sender.namebase){ + $squad::Leader[%squad] = ""; + $squad::Name[%squad] = ""; + $squad::Score[%squad] = ""; + %mem = 1; + %num = %squad@%mem; + while($squad::member[%num] !$= ""){ + $Rank::Squad[$squad::member[%num]] = ""; + $Rank::SquadScore[$squad::member[%num]] = ""; + + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.ranknum == $squad::member[%num]) + %i = %count; + } + + messageClient(%cl, 'MsgClient', "\c2Your squad has disbanned."); + $squad::member[%num] = ""; + + %mem++; + %num = %squad@%mem; + } + $Rank::Squad[%sender.ranknum] = ""; + $Rank::SquadScore[%sender.ranknum] = ""; + $squad::member[%squad@"0"] = ""; + messageClient(%sender, 'MsgClient', "\c2You have disbanned your squad."); + } else { + %mem = 0; + %num = %squad@%mem; + while($squad::member[%num] !$= ""){ + if(%sender.ranknum == $squad::member[%num]) + %plrnum = %num; + %mem++; + %num = %squad@%mem; + } + %last = %squad@(%mem - 1); + $Rank::Squad[%sender.ranknum] = ""; + $Rank::SquadScore[%sender.ranknum] = ""; + if(%plrnum != %last){ + $squad::member[%plrnum] = $squad::member[%last]; + $squad::member[%last] = ""; + } + messageClient(%sender, 'MsgClient', "\c2You have left the Squad."); + } +} + +function ccInvite(%sender, %args){ + if(%args $= ""){ + messageclient(%sender, 'MsgClient', '\c2You must specify a player.'); + return; + } + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.nameBase $= %args) + %i = %count; + else if(%i == (%count - 1)){ + messageClient(%sender, 'MsgClient', "\c3"@%args@" \c2is not a valid player."); + return; + } + } + if($Rank::Squad[%cl.ranknum] !$= ""){ + messageClient(%sender, 'MsgClient', "\c3"@%args@" \c2is already in a squad."); + return; + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%sender.namebase $= $squad::Leader[%i]){ + %cl.sqinv = $squad::Name[%i]; + messageclient(%sender, 'MsgClient', "\c3"@%args@" \c2as been invited to your squad."); + messageclient(%cl, 'MsgClient', "\c2You have been invited to squad\c3 "@$squad::Name[%i]@" \c2to join, type:\c3 /Join\c2."); + return; + } + } + messageclient(%sender, 'MsgClient', "\c2You are not a squad leader and cannot invite."); +} + +function ccS(%sender, %args){ + if($squad::Name[getWord($Rank::Squad[%sender.ranknum],1)] $= ""){ + messageclient(%sender, 'MsgClient', "\c2You are not in a squad."); + return; + } + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + %mem = 0; + %num = getWord($Rank::Squad[%sender.ranknum],1)@%mem; + while($squad::member[%num] !$= ""){ + if(%cl.ranknum == $squad::member[%num] && %cl.team == %sender.team) + messageclient(%cl, 'MsgClient', "\c0[Squad Chat] \c2"@%sender.namebase@": \c3"@%args); + %mem++; + %num = getWord($Rank::Squad[%sender.ranknum],1)@%mem; + } + } +} + +function ccListSquads(%client, %args){ + for(%i = 0; %i < $Squad::numsquads; %i++){ + messageClient(%client, 'MsgClient', "\c2SQUAD "@(%i + 1)@": \c3"@$squad::Name[%i]@" \c2SCORE: \c3"@$squad::score[%i]@""); + %temp = 0; + %num = %i@%temp; + while($squad::member[%num] !$= ""){ + if(%temp == 0) + %prefix = "Leader:"; + else + %prefix = "member"@(%temp + 1)@":"; + messageClient(%client, 'MsgClient', "\c2"@%prefix@" \c3"@$rank::Name[$squad::member[%num]]@" \c2teamscore: \c3"@$rank::squadscore[$squad::member[%num]]@""); + %temp++; + %num = %i@%temp; + } + if((%i + 1) < $squad::numsquads) + messageClient(%client, 'MsgClient', "\c2 "); + } +} + +function ccRequestInvite(%client, %args){ + if(%client.ranknum $= ""){ + messageClient(%client, 'MsgClient', "\c2Please wait a minute for your stats to load"); + return; + } + if($rank::squad[%client.ranknum] !$= ""){ + messageClient(%client, 'MsgClient', "\c2You are already in a squad."); + return; + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%args $= $squad::Name[%i]){ + messageclient(%sender, 'MsgClient', "\c2Request was sent to squa\c3 "@%args@"\c2."); + for(%j = 0; %j < %count; %j++){ + %cl = ClientGroup.getObject(%j); + if(%cl.namebase $= $squad::Leader[%i]) + messageclient(%cl, 'MsgClient', "\c3"@%client.namebase@"\c2 has requested to join your squad."); + } + return; + } + } + messageclient(%sender, 'MsgClient', "\c2Squad\c3 "@%args@" \c2dosent exist."); +} + +//-------------------------------------------------- +//COMMANDS +//-------------------------------------------------- + +function ccO(%sender,%args){ + if(%sender.ranknum $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please wait a minute for your stats to load.'); + return; + } + if($Rank::Score[%sender.ranknum] < $Ranks::MinPoints[12]){ + messageclient(%sender, 'MsgClient', '\c2You must have a General or Commander rank to give orders.'); + return; + } + if(%args $= "" || getWord(%args,1) $= ""){ + messageclient(%sender, 'MsgClient', '\c2You must specify the order and then the target squad.'); + return; + } + if(getWord(%args,0) !$= "attack" && getWord(%args,0) !$= "defend"){ + messageclient(%sender, 'MsgClient', '\c2Order must be either "attack" or "defend".'); + return; + } + %order = getWord(%args,0); + %name = getWord(%args,1); + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%name $= $squad::Name[%i]){ + %check = 1; + %j = %i; + } + } + if(%check != 1){ + messageclient(%sender, 'MsgClient', "\c2Squad\c3 "@%name@" \c2dosent exist."); + return; + } + %obj = %sender.getControlObject(); + %eyeTrans = %obj.getEyeTransform(); + %eyePos = posFromTransform(%eyeTrans); + %eyeVec = vectorScale(%obj.getEyeVector(),1000); + %searchResult = containerRayCast(%eyePos, vectorAdd(%eyeVec,%eyePos), $TypeMasks::TerrainObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType, %obj); + if(%searchResult){ + %mem = 0; + %num = %j@%mem; + while($squad::member[%num] !$= ""){ + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.ranknum == $squad::member[%num] && %cl.team == %sender.team) + messageclient(%cl, 'MsgClient', "\c2A general has ordered your squad to\c3 "@%order@" \c2this location."); + } + %mem++; + %num = %j@%mem; + } + + %pos = posFromRaycast(%searchResult); + %wa=new Waypoint() { + position = %pos; + rotation = "1 0 0 0"; + dataBlock = "WayPointMarker"; + team = %sender.team; + name = %name@" "@%order@" this position"; + }; + MissionCleanup.add(%wa); + + %nveh = 0; + %nplr = 0; + %nTrt = 0; + %ndep = 0; + InitContainerRadiusSearch(%pos, 100, $TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::VehicleObjectType); + while ((%targetObject = containerSearchNext()) != 0){ + %target = getWord(%targetObject,0); + if(%order $= "attack" && %target.team != %sender.team){ + if(%target.getType() & $TypeMasks::PlayerObjectType) + %nplr++; + if(%target.getType() & $TypeMasks::StaticShapeObjectType){ + %Ttype = %target.getDataBlock().className ; + if(!(%Ttype $= "wall" || %Ttype $= "wWall" || %Ttype $= "spine" || %Ttype $= "mSpine" || %Ttype $= "floor" || %Ttype$= "forcefield")) + %ndep++; + } + if(%target.getType() & $TypeMasks::TurretObjectType) + %ntrt++; + if(%target.getType() & $TypeMasks::VehicleObjectType) + %nveh++; + } + else if(%order $= "defend" && %target.team == %sender.team){ + if(%target.getType() & $TypeMasks::PlayerObjectType) + %nplr++; + if(%target.getType() & $TypeMasks::StaticShapeObjectType){ + %Ttype = %target.getDataBlock().className ; + if(!(%Ttype $= "wall" || %Ttype $= "wWall" || %Ttype $= "spine" || %Ttype $= "mSpine" || %Ttype $= "floor" || %Ttype$= "forcefield")) + %ndep++; + } + if(%target.getType() & $TypeMasks::TurretObjectType) + %ntrt++; + if(%target.getType() & $TypeMasks::VehicleObjectType) + %nveh++; + } + } + %numtrg = %ntrt@" "@%nplr@" "@%nveh@" "@%ndep; + %numtrgs = %ntrt + %nplr + %nveh + %ndep; + if(%numtrgs >= 3){ + schedule(%numtrgs * 20000, 0, "completeCommand", %order, %name, %numtrg, %pos, %sender); + schedule(10000, 0, "commandCheckupLoop", %name, %pos, %sender, (%numtrgs * 2) - 1); + %wa.schedule(%numtrgs * 20000, "delete"); + } + else{ + messageclient(%sender, 'MsgClient', "\c2Please specify a location with more targets."); + %wa.schedule(100, "delete"); + } + } + else + messageclient(%sender, 'MsgClient', "\c2Please specify a valid target position."); +} + +function commandCheckupLoop(%name, %pos, %issuer, %LCount){ + if(%LCount < 1) + return; + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%name $= $squad::Name[%i]){ + %j = %i; + } + } + %mem = 0; + %num = %j@%mem; + while($squad::member[%num] !$= ""){ + %count = ClientGroup.getCount(); + %player = $squad::member[%num]; + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.ranknum == %player && %cl.team == %issuer.team){ + %clpos = %cl.player.getPosition(); + if(vectorDist(%clpos,%pos) <= 100){ + bottomPrint(%cl, "\c2"@(%LCount * 10)@" seconds remain until objective is complete.", 5); + if(%cl.objectivecount $= "") + %cl.objectivecount = 0; + %cl.objectivecount++; + } + else + bottomPrint(%cl, "\c2You are to far away from the objective zone, you have "@(%LCount * 10)@" seconds left.", 5); + } + } + %mem++; + %num = %j@%mem; + } + %LCount--; + schedule(10000, 0, "commandCheckupLoop", %name, %pos, %issuer, %LCount); +} + +function completeCommand(%order, %name, %numtrg, %pos, %issuer){ + %oTrt = getWord(%numtrg,0); + %oplr = getWord(%numtrg,1); + %oveh = getWord(%numtrg,2); + %odep = getWord(%numtrg,3); + %nTrt = 0; + %nplr = 0; + %nveh = 0; + %ndep = 0; + InitContainerRadiusSearch(%pos, 100, $TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::VehicleObjectType); + while ((%targetObject = containerSearchNext()) != 0){ + %target = getWord(%targetObject,0); + if(%order $= "attack" && %target.team != %issuer.team){ + if(%target.getType() & $TypeMasks::PlayerObjectType) + %nplr++; + if(%target.getType() & $TypeMasks::StaticShapeObjectType){ + %Ttype = %target.getDataBlock().className ; + if(!(%Ttype $= "wall" || %Ttype $= "wWall" || %Ttype $= "spine" || %Ttype $= "mSpine" || %Ttype $= "floor" || %Ttype$= "forcefield")) + %ndep++; + } + if(%target.getType() & $TypeMasks::TurretObjectType) + %ntrt++; + if(%target.getType() & $TypeMasks::VehicleObjectType) + %nveh++; + } + else if(%order $= "defend" && %target.team == %issuer.team){ + if(%target.getType() & $TypeMasks::PlayerObjectType) + %nplr++; + if(%target.getType() & $TypeMasks::StaticShapeObjectType){ + %Ttype = %target.getDataBlock().className ; + if(!(%Ttype $= "wall" || %Ttype $= "wWall" || %Ttype $= "spine" || %Ttype $= "mSpine" || %Ttype $= "floor" || %Ttype$= "forcefield")) + %ndep++; + } + if(%target.getType() & $TypeMasks::TurretObjectType) + %ntrt++; + if(%target.getType() & $TypeMasks::VehicleObjectType) + %nveh++; + } + } + %numtrg = %ntrt@" "@%nplr@" "@%nveh@" "@%ndep; + %numtrgs = %otrt + %oplr + %oveh + %odep; + %points = ((%otrt - %ntrt) * 4) + + ((%oplr - %nplr) * 3) + + ((%oveh - %nveh) * 2) + + ((%odep - %ndep) * 2); + if(%order $= "defend"){ + %startpoints = (%otrt * 8) + (%oplr * 6) + (%oveh * 4) + (%odep * 4); + %points = %startpoints - %points; + %percentRemaining = ((%ntrt + %nplr + %nveh + %ndep) / (%otrt + %oplr + %oveh + %odep)) * 100; + if(%percentRemaining >= 75){ + %status = "success"; + %points = %points + (15 * (%percentRemaining / 100) * %numtrgs); + %recomend = "keep up the good work!"; + } + else{ + %status = "failure"; + %points = %points - (7.5 * ((100 - %percentRemaining) / 100) * %numtrgs); + %recomend = "try harder next time."; + } + } + else{ + %percentRemaining = ((%ntrt + %nplr + %nveh + %ndep) / (%otrt + %oplr + %oveh + %odep)) * 100; + if(%percentRemaining <= 50){ + %status = "success"; + %points = %points + (20 * ((100 - %percentRemaining) / 100) * %numtrgs); + %recomend = "keep up the good work!"; + } + else{ + %status = "failure"; + %points = %points - (10 * (%percentRemaining / 100) * %numtrgs); + %recomend = "try harder next time."; + } + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%name $= $squad::Name[%i]){ + %j = %i; + } + } + %mem = 0; + %num = %j@%mem; + while($squad::member[%num] !$= ""){ + %count = ClientGroup.getCount(); + %player = $squad::member[%num]; + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.ranknum == %player && %cl.team == %issuer.team){ + %clpos = %cl.player.getPosition(); + if((vectorDist(%clpos,%pos) <= 50 && %order $= "attack") || %cl.objectivecount >= (%numtrgs - 1) * 1.5){ + messageclient(%cl, 'MsgClient', "\c2Your Objective was deemed a "@%status@" and you were given "@%points@" for this objective. You should "@%recomend); + $Rank::Squadscore[%player] = $Rank::Squadscore[%player] + %points; + $Rank::Score[%player] = $Rank::Score[%player] + %points; + } + else{ + messageclient(%cl, 'MsgClient', "\c2You were to far away from the objective marker. "@(3 * %numtrgs)@" points were deducted from your score."); + $Rank::Squadscore[%player] = $Rank::Squadscore[%player] - (4 * %numtrgs); + $Rank::Score[%player] = $Rank::Score[%player] - (3 * %numtrgs); + } + %cl.objectivecount = 0; + } + } + %mem++; + %num = %j@%mem; + } +} + +//-------------------------------------- +//Force Capabilitys +//-------------------------------------- + +function ccforce(%sender, %args){ + if(%sender.ranknum $= ""){ + messageclient(%sender, 'MsgClient', '\c2Please wait a minute for your stats to load.'); + return; + } + if($Rank::Score[%sender.ranknum] < $Ranks::MinPoints[8]){ + messageclient(%sender, 'MsgClient', '\c2You are not high enough rank to force players.'); + return; + } + if(%args $= ""){ + messageclient(%sender, 'MsgClient', '\c2You must specify what you are forcing, "join" or "leave".'); + return; + } + %cmd = getWord(%args, 0); + if(%cmd $= "join"){ + %squad = getWord(%args, 1); + %target = getWords(%args, 2, 1 + getNumberOfWords(%args)); + if(%squad $= "" || %target $= ""){ + messageclient(%sender, 'MsgClient', '\c2You must specify the squad name and then player name for this to work.'); + return; + } + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.namebase $= %target) + %i = %count; + else if(%i == (%count - 1)){ + messageClient(%sender, 'MsgClient', "\c2"@%target@" is not a valid player."); + return; + } + } + if($Rank::Score[%cl.ranknum] >= $Ranks::MinPoints[8] && !($Rank::Score[%cl.ranknum] < $Ranks::MinPoints[14] && $Rank::Score[%sender.ranknum] >= $Ranks::MinPoints[14])){ + messageclient(%sender, 'MsgClient', '\c2You may not force this player, he has too high of a rank.'); + return; + } + for(%i = 0; %i < $squad::numsquads; %i++){ + if(%squad $= $squad::Name[%i]){ + if($Rank::Squad[%cl.ranknum] !$= ""){ + messageclient(%cl, 'MsgClient', '\c2Higher Rank forced:'); + ccLeaveSquad(%cl,""); + messageclient(%cl, 'MsgClient', '\c2 '); + } + messageclient(%sender, 'MsgClient', "\c3"@%target@" \c2has joined squad\c3 "@%squad@"\c2."); + messageclient(%cl, 'MsgClient', '\c2Higher Rank forced:'); + ccJoin(%cl, %squad); + return; + } + if($squad::Leader[%i] $= "") + %replacable = %i; + } + + if($squad::numsquads $= "") + $squad::numsquads = 0; + if(%replacable !$= ""){ + $squad::Name[%replacable] = %squad; + $squad::Leader[%replacable] = %cl.namebase; + $squad::Score[%replacable] = 0; + } else{ + $squad::Name[$squad::numsquads] = %squad; + $squad::Leader[$squad::numsquads] = %cl.namebase; + $squad::Score[$squad::numsquads] = 0; + $squad::numsquads++; + } + messageclient(%sender, 'MsgClient', "\c2Squad\c3 "@%squad@" \c2has been created with\c3 "@%target@" \c2as leader."); + if($Rank::Squad[%cl.ranknum] !$= ""){ + messageclient(%cl, 'MsgClient', '\c2Higher Rank forced:'); + ccLeaveSquad(%cl,""); + messageclient(%cl, 'MsgClient', '\c2 '); + } + messageclient(%cl, 'MsgClient', '\c2Higher Rank forced:'); + ccJoin(%cl, %squad); + + } + else if(%cmd $= "leave"){ + %target = getWords(%args, 1, getNumberOfWords(%args) - 1); + echo(getNumberOfWords(%args)); + echo(%target); + if(%target $= ""){ + messageclient(%sender, 'MsgClient', '\c2You must specify the person you are forcing.'); + return; + } + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.namebase $= %target) + %i = %count; + else if(%i == (%count - 1)){ + messageClient(%client, 'MsgClient', "\c3"@%target@" \c2is not a valid player."); + return; + } + } + if($Rank::Score[%cl.ranknum] >= $Ranks::MinPoints[8] && !($Rank::Score[%cl.ranknum] < $Ranks::MinPoints[14] && $Rank::Score[%sender.ranknum] >= $Ranks::MinPoints[14])){ + messageclient(%sender, 'MsgClient', '\c2You may not force this player, he has too high of a rank.'); + return; + } + if($Rank::Squad[%cl.ranknum] !$= ""){ + messageclient(%cl, 'MsgClient', '\c2Higher Rank forced:'); + ccLeaveSquad(%cl,""); + messageclient(%sender, 'MsgClient', '\c3'@%target@' \c2has been forced out of his squad.'); + } + else + messageclient(%sender, 'MsgClient', '\c2This person does not have a squad to leave.'); + } + else + messageclient(%sender, 'MsgClient', '\c2Please use "Join" or "leave" as command.'); +} + +//-------------------------------------- +//Misc +//-------------------------------------- + +function cleanRanks(){ + for(%i = 0; %i < $Rank::numplayers; %i++){ + if($Rank::Score[%i] <= 10){ + $Rank::Score[%i] = ""; + $Rank::Name[%i] = ""; + $Rank::Rank[%i] = ""; + } + } + for(%i = 0; %i < $Rank::numplayers; %i++){ + if($Rank::Score[%i] $= ""){ + %replacenum = $Rank::numplayers; + while($Rank::Score[%replacenum] $= ""){ + %replacenum--; + } + if(%i > %replacenum){ + $Rank::numplayers = %i; + return; + } + $Rank::Score[%i] = $Rank::Score[%replacenum]; + $Rank::Name[%i] = $Rank::Name[%replacenum]; + $Rank::Rank[%i] = $Rank::Rank[%replacenum]; + $Rank::Score[%replacenum] = ""; + $Rank::Name[%replacenum] = ""; + $Rank::Rank[%replacenum] = ""; + } + } +} + +function ccSOL(%sender, %args){ + if(%sender.spawnOnLead $= "" || %sender.spawnOnLead == 0){ + %sender.spawnOnLead = 1; + messageclient(%sender, 'MsgClient', '\c2You will now spawn on your squad leader.'); + } else { + %sender.spawnOnLead = 0; + messageclient(%sender, 'MsgClient', '\c2You will no longer spawn on your squad leader.'); + } +} + +function getNumberOfWords(%path){ + %number = 0; + for(%i = 0; %i < 1000; %i++){ + if(getWord(%path,%i) !$= "") + %number++; + else + return %number; + } +} diff --git a/Scripts/SpecOpsFeatures.cs b/Scripts/SpecOpsFeatures.cs new file mode 100644 index 0000000..5d5985e --- /dev/null +++ b/Scripts/SpecOpsFeatures.cs @@ -0,0 +1,77 @@ +$NightVisionValue = 0.2; + +function NightVisionLoop(%client){ + if(!isObject(%client.player)) + return; + %client.player.setWhiteOut($NightVisionValue); + %client.player.nightvision = schedule(50, 0, "NightVisionLoop", %client); +} + +function serverCmdDoGrab(%client){ + %obj = %client.player; + if(!isObject(%obj)) + return; + if(%obj.grabbing != 1){ + %pos = %obj.getMuzzlePoint($WeaponSlot); + %vec = %obj.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,5)); + %Tobj = containerraycast(%pos, %targetpos, $typemasks::PlayerObjectType | $typemasks::CorpseObjectType, %obj); + %Tobj = getword(%Tobj,0); + if(%Tobj.client.team == %client.team && !$TeamDamage) + return; // Can't pickup teammates now. + if(isObject(%Tobj)){ + if(vectorDist(%obj.getForwardVector(),%Tobj.getForwardVector()) > 1.6) + return; + %armortype = %obj.getdatablock().getname(); + if(!(%armortype $= "SpecOpsMaleHumanArmor" || %armortype $= "SpecOpsFemaleHumanArmor" || %armortype $= "SpecOpsMaleBiodermArmor")) + return; + if(%Tobj.grabbing == 1) + %Tobj.grabbing = 0; + if(%Tobj.grabbed == 1) + return; + %dataBlock = %Tobj.getDataBlock(); + %mass = %dataBlock.mass; + if(%mass >= 150){ + return; + } + %Tobj.grabbed = 1; + %Tobj.setMoveState(true); + %obj.grabbing = 1; + schedule(100, 0, "GrabLoop", %obj, %Tobj); + } + } + else + %obj.grabbing = 0; +} + +function GrabLoop(%obj, %Tobj){ + if(!(isObject(%obj) && isObject(%Tobj))) + return; + if(%obj.getstate() $= "dead"){ + %Tobj.setMoveState(false); + %Tobj.grabbed = 0; + return; + } + if(%obj.grabbing != 1){ + %Tobj.setMoveState(false); + %Tobj.grabbed = 1; + if(%Tobj.getState() !$= "Dead") + %Tobj.getDataBlock().damageObject(%Tobj, %obj, %Tobj.getPosition(), 10, $DamageType::blah, "0 0 1", %obj.getControllingClient(), %obj); + return; + } + if(%Tobj.getMountedImage($weaponslot) !$= "") + %Tobj.throwWeapon(); + if(%Tobj.getMountedImage($Backpackslot) !$= "") + %Tobj.throwPack(); + if(%obj.getMountedImage($weaponslot) !$= "") + %obj.unmountImage($weaponslot); + + %pos = %obj.getPosition(); + %rot = %obj.getRotation(); + %vel = vectoradd(%obj.getvelocity(), "0 0 1"); + %vec = vectorScale(%obj.getForwardVector(),3); + %vec = vectorAdd(%vec, "0 0 0.5"); + %Tobj.setTransform(vectorAdd(%pos, %vec)@" "@%rot); + %Tobj.setvelocity(%vel); + schedule(100, 0, "GrabLoop", %obj, %Tobj); +} \ No newline at end of file diff --git a/Scripts/TR2Game.cs b/Scripts/TR2Game.cs new file mode 100644 index 0000000..cafc1f5 --- /dev/null +++ b/Scripts/TR2Game.cs @@ -0,0 +1,31 @@ +// Bastardized for Contruction Mod to load the player datablocks +// without screwing up the rest of the game. + +// DisplayName = Team Rabbit 2 + +//--- GAME RULES BEGIN --- +//Get the flag and throw it into the other team's goal +//You can only hold onto the flag for 15 seconds +//Passing the flag increases the size of the Jackpot +//Scoring a goal awards the Jackpot to your team! +//When your health reaches zero, you are knocked down +//Replenish your ammo by pressing your suicide button +//--- GAME RULES END --- + + + +// Team Rabbit 2 +// Created by Codality, Inc. +// www.codality.com +// ------------------------------- +// Michael "KineticPoet" Johnston - Designer, Lead Programmer, Maps +// Dan "daunt" Kolta - Physics design, Maps +// Scott "FSB-AO" Estabrook - Programmer +// John "CObbler" Carter - Bonus sound effects +// Buddy "sLaM" Pritchard - Sound effects +// Gregg "illy" Fellows - 3D models and skins +// Alan "Nefilim" Schwertel; - Maps +// Kenneth "SONOFMAN" Cook - Sky art + + +exec("scripts/TR2Physics.cs"); diff --git a/Scripts/TR2medium_male.cs b/Scripts/TR2medium_male.cs new file mode 100644 index 0000000..7b18da4 --- /dev/null +++ b/Scripts/TR2medium_male.cs @@ -0,0 +1,43 @@ + +datablock TSShapeConstructor(TR2MediumMaleDts) +{ + baseShape = "TR2medium_male.dts"; + sequence0 = "TR2medium_male_root.dsq root"; + sequence1 = "TR2medium_male_forward.dsq run"; + sequence2 = "TR2medium_male_back.dsq back"; + sequence3 = "TR2medium_male_side.dsq side"; + sequence4 = "medium_male_lookde.dsq look"; + sequence5 = "medium_male_head.dsq head"; + sequence6 = "TR2medium_male_fall.dsq fall"; + sequence7 = "TR2medium_male_jet.dsq jet"; + sequence8 = "TR2medium_male_land.dsq land"; + sequence9 = "TR2medium_male_jump.dsq jump"; + sequence10 = "medium_male_recoilde.dsq light_recoil"; + sequence11 = "medium_male_headside.dsq headside"; + sequence12 = "medium_male_looksn.dsq looksn"; + sequence13 = "medium_male_lookms.dsq lookms"; + sequence14 = "TR2medium_male_sitting.dsq sitting"; + sequence15 = "TR2medium_male_diehead.dsq death1"; + sequence16 = "TR2medium_male_diechest.dsq death2"; + sequence17 = "TR2medium_male_dieback.dsq death3"; + sequence18 = "TR2medium_male_diesidelf.dsq death4"; + sequence19 = "TR2medium_male_diesidert.dsq death5"; + sequence20 = "TR2medium_male_dieleglf.dsq death6"; + sequence21 = "TR2medium_male_diechest.dsq death7"; // medium_male_dielegrt + sequence22 = "TR2medium_male_dieback.dsq death8"; + sequence23 = "TR2medium_male_dieknees.dsq death9"; + sequence24 = "TR2medium_male_dieforward.dsq death10"; + sequence25 = "TR2medium_male_diespin.dsq death11"; + sequence26 = "medium_male_idlepda.dsq pda"; + sequence27 = "TR2medium_male_celsalute.dsq cel1"; + sequence28 = "TR2medium_male_celwave.dsq cel2"; + sequence29 = "TR2medium_male_tauntbest.dsq cel3"; + sequence30 = "TR2medium_male_tauntimp.dsq cel4"; + sequence31 = "TR2medium_male_celdance.dsq cel5"; + sequence32 = "TR2medium_male_celflex.dsq cel6"; + sequence33 = "TR2medium_male_celtaunt.dsq cel7"; + sequence34 = "TR2medium_male_celrocky.dsq cel8"; + sequence35 = "TR2medium_male_ski.dsq ski"; + sequence36 = "TR2medium_male_standjump.dsq standjump"; + sequence37 = "medium_male_looknw.dsq looknw"; +}; diff --git a/Scripts/Turrets/Artillerybarrellarge.cs b/Scripts/Turrets/Artillerybarrellarge.cs new file mode 100644 index 0000000..93ebd84 --- /dev/null +++ b/Scripts/Turrets/Artillerybarrellarge.cs @@ -0,0 +1,308 @@ +//-------------------------------------------------------------------------- +// Artillery Turret +//-------------------------------------------------------------------------- + +datablock ShockwaveData(artilleryShockwave) +{ + width = 6.0; + numSegments = 32; + numVertSegments = 6; + velocity = 50; + acceleration = 20.0; + lifetimeMS = 500; + height = 3.0; + verticalCurve = 0.5; + is2D = false; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "0.6 1.0 0.2 0.50"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "0.56 0.36 0.26 0.0"; + + mapToTerrain = true; + orientToNormal = false; + renderBottom = false; +}; + + +datablock ParticleData(artilleryExplosionSmoke) +{ + dragCoeffiecient = 6.0; + gravityCoefficient = 0.25; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 3000; + lifetimeVarianceMS = 200; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "0.2 0.2 0.2 0.6"; + colors[1] = "0.25 0.25 0.25 0.4"; + colors[2] = "0.4 0.4 0.4 0.0"; + sizes[0] = 5.0; + sizes[1] = 8.0; + sizes[2] = 15.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleData(artilleryFallerSmoke) +{ + dragCoeffiecient = 0.7; + gravityCoefficient = 2.0; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 6000; + lifetimeVarianceMS = 750; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "1.0 0.8 0.1 0.6"; + colors[1] = "0.3 0.3 0.3 0.6"; + colors[2] = "0.3 0.3 0.3 0.6"; + colors[3] = "0.35 0.35 0.35 0.3"; + colors[4] = "0.4 0.4 0.4 0.0"; + sizes[0] = 3.0; + sizes[1] = 6.0; + sizes[2] = 7.0; + sizes[3] = 10.0; + sizes[4] = 15.0; + times[0] = 0.0; + times[1] = 0.1; + times[2] = 0.25; + times[3] = 0.5; + times[4] = 0.75; +}; + +datablock ParticleEmitterData(artilleryExplosionSmokeEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionOffset = 0.0; + + + ejectionVelocity = 15.0; + velocityVariance = 10.0; + + thetaMin = 89.0; + thetaMax = 90.0; + + lifetimeMS = 500; + + particles = "artilleryExplosionSmoke"; + +}; + +datablock ParticleEmitterData(artilleryExplosionSmokeEmitter2) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionOffset = 0.0; + + ejectionVelocity = 15.0; + velocityVariance = 15.0; + + thetaMin = 0.0; + thetaMax = 10.0; + + lifetimeMS = 350; + + particles = "artilleryFallerSmoke"; + +}; + +datablock ParticleEmitterData(artilleryExplosionSmokeEmitter3) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionOffset = 12.0; + + + ejectionVelocity = 20.0; + velocityVariance = 5.0; + + thetaMin = 0.0; + thetaMax = 25.0; + + lifetimeMS = 350; + + particles = "artilleryFallerSmoke"; + +}; + +datablock ExplosionData(artillerysubExplosion1) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + + delayMS = 0; + + offset = 0.0; + + playSpeed = 0.6; + + sizes[0] = "10.0 10.0 10.0"; + sizes[1] = "15.0 15.0 15.0"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(artillerybarrelExplosion) +{ + soundProfile = MortarExplosionSound; + + shockwave = artilleryShockwave; + shockwaveOnTerrain = true; + + subExplosion[0] = artillerysubExplosion1; + + emitter[0] = artilleryExplosionSmokeEmitter; + emitter[1] = artilleryExplosionSmokeEmitter2; + emitter[2] = artilleryExplosionSmokeEmitter3; + + shakeCamera = true; + camShakeFreq = "8.0 9.0 7.0"; + camShakeAmp = "100.0 100.0 100.0"; + camShakeDuration = 1.3; + camShakeRadius = 40.0; +}; + + + + +datablock GrenadeProjectileData(artillerybarrelshot) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 4.25; + damageRadius = 25.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 3000; + + explosion = "artillerybarrelexplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.25; + grenadeFriction = 0.3; + armingDelayMS = 500; + gravityMod = 0.8; + muzzleVelocity = 65.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +//-------------------------------------------------------------------------- +// artillery explosion +//-------------------------------------------------------------------------- + +datablock TurretImageData(ArtilleryBarrelLarge) +{ + shapeFile = "turret_tank_barrelmortar.dts"; + item = artilleryBarrelLargePack; + + projectile = artillerybarrelshot; + projectileType = GrenadeProjectile; + usesEnergy = true; + fireEnergy = 1; + minEnergy = 1; + emap = true; + + // Eolk - There should be no spread. + muzzleFlash = ChaingunTurretMuzzleFlash; + + // Turret parameters + activationMS = 300; + deactivateDelayMS = 600; + thinkTimeMS = 200; + degPerSecTheta = 580; + degPerSecPhi = 960; + attackRadius = 370; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = PBLSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + // fire off about 2 quick shots + stateName[3] = "Fire"; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = AssaultMortarFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 3.0; //0.3 + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Ready"; + // stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "NoAmmo"; + + stateName[8] = "Reload"; + stateTimeoutValue[7] = 5.0; + stateAllowImageChange[8] = false; + stateSequence[8] = "Reload"; + stateTransitionOnTimeout[8] = "Ready"; + stateTransitionOnNotLoaded[8] = "Deactivate"; + stateTransitionOnNoAmmo[8] = "NoAmmo"; + + stateName[9] = "Deactivate"; + stateSequence[9] = "Activate"; + stateDirection[9] = false; + stateTimeoutValue[9] = 1; + stateTransitionOnLoaded[9] = "ActivateReady"; + stateTransitionOnTimeout[9] = "Dead"; + + stateName[10] = "Dead"; + stateTransitionOnLoaded[10] = "ActivateReady"; + + stateName[11] = "NoAmmo"; + stateTransitionOnAmmo[11] = "Reload"; + stateSequence[11] = "NoAmmo"; +}; diff --git a/Scripts/Turrets/ELFBarrelLarge.cs b/Scripts/Turrets/ELFBarrelLarge.cs new file mode 100644 index 0000000..f4baff1 --- /dev/null +++ b/Scripts/Turrets/ELFBarrelLarge.cs @@ -0,0 +1,163 @@ +//-------------------------------------------------------------------------- +// Gauss Cannon Turret +//-------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// Sounds +//-------------------------------------------------------------------------- +datablock EffectProfile(EBLSwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(EBLSwitchSound) +{ + filename = "fx/powered/turret_heavy_activate.wav"; + description = AudioClose3d; + preload = true; + effect = EBLSwitchEffect; +}; + +datablock EffectProfile(FlameTurretFireEffect) +{ + effectname = "Bonuses/Nouns/snake"; + minDistance = 2.5; + maxDistance = 8.0; +}; + +datablock AudioProfile(FlameTurretFireSound) +{ + filename = "fx/Bonuses/Nouns/snake.wav"; + description = AudioClose3d; + preload = true; + effect = FlameTurretFireEffect; +}; + +datablock LinearFlareProjectileData(FlameTurretBolt) +{ + projectileShapeName = "turret_muzzlepoint.dts"; + scale = "1.0 1.0 1.0"; + faceViewer = true; + directDamage = 0.08; + hasDamageRadius = true; + indirectDamage = 0.1; + damageRadius = 4.0; + kickBackStrength = 0.0; + radiusDamageType = $DamageType::Plasma; + + explosion = "flameBoltExplosion"; + splash = PlasmaSplash; + + baseEmitter = FlameEmitter; + + dryVelocity = 80.0; // z0dd - ZOD, 7/20/02. Faster plasma projectile. was 55 + wetVelocity = -1; + velInheritFactor = 2.0; + fizzleTimeMS = 250; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = true; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = -1; + + //activateDelayMS = 100; + activateDelayMS = -1; + + size[0] = 0.2; + size[1] = 0.5; + size[2] = 0.1; + + + numFlares = 35; + flareColor = "1 0.18 0.03"; + flareModTexture = "flaremod"; + flareBaseTexture = "flarebase"; + + sound = PlasmaProjectileSound; + fireSound = PlasmaFireSound; + wetFireSound = PlasmaFireWetSound; + + hasLight = true; + lightRadius = 10.0; + lightColor = "0.94 0.03 0.12"; +}; + +datablock TurretImageData(ELFBarrelLarge) +{ + shapeFile = "turret_elf_large.dts"; + item = ELFBarrelPack; + + projectile = FlameTurretBolt; + projectileType = LinearFlareProjectile; + usesEnergy = true; + fireEnergy = 0.01; + minEnergy = 0.01; + emap = true; + + minRankPoints = 1800; + + projectileSpread = 0.5 / 1000.0; + + // Turret parameters + activationMS = 700; + deactivateDelayMS = 1500; + thinkTimeMS = 120; + degPerSecTheta = 300; + degPerSecPhi = 500; + attackRadius = 80; + + // State transiltions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = EBLSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.05; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = FlameTurretFireSound; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 0.05; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateSound[4] = PlasmaReloadSound; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + diff --git a/Scripts/Turrets/aabarrellarge.cs b/Scripts/Turrets/aabarrellarge.cs new file mode 100644 index 0000000..1c2b6a3 --- /dev/null +++ b/Scripts/Turrets/aabarrellarge.cs @@ -0,0 +1,457 @@ +//-------------------------------------- +// Flak Turret - Created by Dondelium_X, Greatly Modified by Blnukem +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Sounds and feedback effects +//-------------------------------------- + +datablock EffectProfile(AASwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(AAFireEffect) +{ + effectname = "weapons/chaingun_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(AASwitchSound) +{ + filename = "fx/powered/turret_aa_activate.wav"; + description = AudioClose3d; + preload = true; + effect = AASwitchEffect; +}; + +datablock AudioProfile(AAFireSound) +{ + filename = "fx/weapons/plasma_rifle_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = AAFireEffect; +}; + +//-------------------------------------------------------------------------- +// Particle effects +//-------------------------------------- +datablock ParticleData(FlakDust) +{ + 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.3 0.3 0.3 0.5"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 6.6; + sizes[1] = 10.8; + sizes[2] = 12.0; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(FlakDustEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 20.0; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 88; + thetaMax = 92; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "FlakDust"; +}; + + +datablock ParticleData(FlakExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.5; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.7 0.7 0.7 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.1 0.1 0.1 0.0"; + sizes[0] = 4.0; + sizes[1] = 13.0; + sizes[2] = 4.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FExplosionSmokeEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + + ejectionVelocity = 6.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "FlakExplosionSmoke"; +}; + +datablock ParticleData(FlakSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 650; + textureName = "special/bigspark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 3.0; + sizes[1] = 3.0; + sizes[2] = 4.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FlakSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 15; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "FlakSparks"; +}; + +datablock ParticleData(FlakFlashSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 250; + lifetimeVarianceMS = 650; + textureName = "special/bigspark"; + colors[0] = "1.0 0.2 0.2 1.0"; + colors[1] = "0.7 0.7 0.2 1.0"; + colors[2] = "0.5 0.5 0.1 0.0"; + sizes[0] = 4.0; + sizes[1] = 5.0; + sizes[2] = 6.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FlakFlashSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 25; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "FlakFlashSparks"; +}; + +//---------------------------------------------------- +// Explosion +//---------------------------------------------------- +datablock ExplosionData(FlakExplosion) +{ + soundProfile = GrenadeExplosionSound; + + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + emitter[0] = FlakDustEmitter; + emitter[1] = FExplosionSmokeEmitter; + emitter[2] = FlakSparksEmitter; + emitter[3] = FlakFlashSparksEmitter; + + shakeCamera = true; + camShakeFreq = "10.0 6.0 9.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 20.0; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(Flak_Bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.3; + directDamageType = $DamageType::AATurret; + explosion = FlakExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.15; + damageRadius = 25.0; + radiusDamageType = $DamageType::Flak; + + kickBackStrength = 15; + sound = ChaingunProjectile; + + dryVelocity = 600.0; + wetVelocity = 200.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 2000; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TracerProjectileData(Flak_Bullet_exp) +{ + doDynamicClientHits = true; + + directDamage = 0.3; + directDamageType = $DamageType::AATurret; + explosion = FlakExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.15; + damageRadius = 25.0; + radiusDamageType = $DamageType::Flak; + + kickBackStrength = 150; + sound = ChaingunProjectile; + + dryVelocity = 0.0; + wetVelocity = 0.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 10; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 0.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock TurretImageData(AABarrelLarge) +{ + shapeFile = "turret_aa_large.dts"; + item = AABarrelPack; // z0dd - ZOD, 4/25/02. Was wrong: AABarrelLargePack + + projectileType = TracerProjectile; + projectile = Flak_Bullet; + projectileSpread = 25.0 / 1000.0; + usesEnergy = true; + fireEnergy = 1.0; + minEnergy = 1.0; + emap = true; + isSeeker = true; + seekRadius = 300; + maxSeekAngle = 6; + seekTime = 1.0; + minSeekHeat = 0.6; + useTargetAudio = false; + + // Turret parameters + activationMS = 175; // z0dd - ZOD, 3/27/02. Was 250. Amount of time it takes turret to unfold + deactivateDelayMS = 500; + thinkTimeMS = 140; // z0dd - ZOD, 3/27/02. Was 200. Amount of time before turret starts to unfold (activate) + degPerSecTheta = 600; + degPerSecPhi = 1080; + attackRadius = 1000; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AASwitchSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + stateScript[1] = "AAActivateReady"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire1"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateScript[2] = "AAReady"; + + stateName[3] = "Fire1"; + stateTransitionOnTimeout[3] = "Reload1"; + stateTimeoutValue[3] = 1.00; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire1"; + stateSound[3] = AAFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload1"; + stateTimeoutValue[4] = 0.08; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Fire2"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Fire2"; + stateTransitionOnTimeout[5] = "Reload2"; + stateTimeoutValue[5] = 1.00; + stateFire[5] = true; + stateRecoil[5] = LightRecoil; + stateAllowImageChange[5] = false; + stateSequence[5] = "Fire2"; + stateSound[5] = AAFireSound; + stateScript[5] = "onFire"; + + stateName[6] = "Reload2"; + stateTimeoutValue[6] = 0.08; + stateAllowImageChange[6] = false; + stateSequence[6] = "Reload"; + stateTransitionOnTimeout[6] = "Ready"; + stateTransitionOnNotLoaded[6] = "Deactivate"; + stateTransitionOnNoAmmo[6] = "NoAmmo"; + + stateName[7] = "Deactivate"; + stateSequence[7] = "Activate"; + stateDirection[7] = false; + stateTimeoutValue[7] = 1.0; + stateTransitionOnLoaded[7] = "ActivateReady"; + stateTransitionOnTimeout[7] = "Dead"; + + stateName[8] = "Dead"; + stateTransitionOnLoaded[8] = "ActivateReady"; + + stateName[9] = "NoAmmo"; + stateTransitionOnAmmo[9] = "Reload2"; + stateSequence[9] = "NoAmmo"; +}; + +function AABarrelLarge::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.doexplodecheck = schedule(250, 0, "TurretFlakExplode", %p); +} + +function TurretFlakExplode(%p){ + if(!isObject(%p)) + return; + InitContainerRadiusSearch(%p.getPosition(), 25, $TypeMasks::VehicleObjectType); + while ((%SearchResult = containerSearchNext()) != 0) + { + %pn = new (TracerProjectile)() { + dataBlock = Flak_Bullet_exp; + initialPosition = %p.getPosition(); + sourceObject = %p.sourceObject; + sourceSlot = %p.sourceSlot; + vehicleObject = %p.vehicleObject; + }; + MissionCleanup.add(%pn); + %p.delete(); + return; + } + %p.doexplodecheck = schedule(30, 0, "TurretFlakExplode", %p); +} + diff --git a/Scripts/Turrets/indoordeployablebarrel.cs b/Scripts/Turrets/indoordeployablebarrel.cs new file mode 100644 index 0000000..f25bd7d --- /dev/null +++ b/Scripts/Turrets/indoordeployablebarrel.cs @@ -0,0 +1,531 @@ +datablock EffectProfile(IBLSwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 5.0; + maxDistance = 5.0; +}; + +datablock EffectProfile(IBLFireEffect) +{ + effectname = "powered/turret_indoor_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(IBLSwitchSound) +{ + filename = "fx/powered/turret_light_activate.wav"; + description = AudioClose3d; + preload = true; + effect = IBLSwitchEffect; +}; + +datablock AudioProfile(IBLFireSound) +{ + filename = "fx/weapons/chaingun_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = IBLFireEffect; +}; + +datablock SensorData(DeployedIndoorTurretSensor) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 130; +}; + +datablock ShockwaveData(IndoorTurretMuzzleFlash) +{ + width = 0.5; + numSegments = 13; + numVertSegments = 1; + velocity = 2.0; + acceleration = -1.0; + lifetimeMS = 300; + height = 0.1; + verticalCurve = 0.5; + + mapToTerrain = false; + renderBottom = false; + orientToNormal = true; + renderSquare = true; + + texture[0] = "special/blasterHit"; + texture[1] = "special/gradient"; + texWrap = 3.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "1.0 0.3 0.3 1.0"; + colors[1] = "1.0 0.3 0.3 1.0"; + colors[2] = "1.0 0.3 0.3 0.0"; +}; + +datablock TurretData(TurretDeployedFloorIndoor) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_indoor_deployf.dts"; + + mass = 5.0; + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 1.8; + explosion = SmallTurretExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + expImpulse = 500.0; + repairRate = 0; + heatSignature = 0.0; + + deployedObject = true; + + thetaMin = 5; + thetaMax = 145; + thetaNull = 90; + + primaryAxis = zaxis; + + isShielded = true; + energyPerDamagePoint = 110; + maxEnergy = 30; + rechargeRate = 0.10; + barrel = DeployableIndoorBarrel; + + canControl = false; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Emplacment'; + targetTypeTag = 'Turret'; + sensorData = DeployedIndoorTurretSensor; + sensorRadius = DeployedIndoorTurretSensor.detectRadius; + sensorColor = "191 0 226"; + + firstPersonOnly = true; + renderWhenDestroyed = false; + + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; + + noFire = 1; +}; + +datablock TurretData(TurretDeployedWallIndoor) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_indoor_deployw.dts"; + + mass = 5.0; + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 1.8; + explosion = SmallTurretExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + expImpulse = 500.0; + repairRate = 0; + heatSignature = 0.0; + + thetaMin = 5; + thetaMax = 145; + thetaNull = 90; + + deployedObject = true; + + primaryAxis = yaxis; + + isShielded = true; + energyPerDamagePoint = 110; + maxEnergy = 30; + rechargeRate = 0.10; + barrel = DeployableIndoorBarrel; + + canControl = false; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Emplacment'; + targetTypeTag = 'Turret'; + sensorData = DeployedIndoorTurretSensor; + sensorRadius = DeployedIndoorTurretSensor.detectRadius; + sensorColor = "191 0 226"; + + firstPersonOnly = true; + renderWhenDestroyed = false; + + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; + + noFire = 1; +}; + +datablock TurretData(TurretDeployedCeilingIndoor) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_indoor_deployc.dts"; + + mass = 5.0; + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 1.8; + explosion = SmallTurretExplosion; + expDmgRadius = 5.0; + expDamage = 0.5; + expImpulse = 500.0; + repairRate = 0; + explosion = SmallTurretExplosion; + + //thetaMin = 5; + //thetaMax = 145; + thetaMin = 35; + thetaMax = 175; + thetaNull = 90; + heatSignature = 0.0; + + deployedObject = true; + + primaryAxis = revzaxis; + + isShielded = true; + energyPerDamagePoint = 110; + maxEnergy = 75; + rechargeRate = 0.5; + barrel = DeployableIndoorBarrel; + + canControl = false; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Implacment'; + targetTypeTag = 'Turret'; + sensorData = DeployedIndoorTurretSensor; + sensorRadius = DeployedIndoorTurretSensor.detectRadius; + sensorColor = "191 0 226"; + + firstPersonOnly = true; + renderWhenDestroyed = false; + + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; + + noFire = 1; +}; + +datablock StaticShapeData(ControlStation) : StaticShapeDamageProfile +{ + className = Station; + shapeFile = "station_teleport.dts"; + maxDamage = 2.0; + destroyedLevel = 2.0; + disabledLevel = 1.5; + explosion = DeployablesExplosion; + expDmgRadius = 8.0; + expDamage = 0.35; + expImpulse = 500.0; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = false; + energyPerDamagePoint = 110; + maxEnergy = 50; + rechargeRate = 0.20; + renderWhenDestroyed = false; + + deployedObject = true; + + cantAbandon = 1; + + cmdCategory = "DSupport"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Control'; + targetTypeTag = 'Station'; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock TracerProjectileData(ImplacmentBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.12; // z0dd - ZOD, 9-27-02. Was 0.0825 + directDamageType = $DamageType::IndoorDepTurret; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + + kickBackStrength = 1000.0; + sound = ChaingunProjectile; + + //dryVelocity = 425.0; + dryVelocity = 1500.0; // z0dd - ZOD, 8-12-02. Was 425.0 + wetVelocity = 1000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.12; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +datablock TurretImageData(DeployableIndoorBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + item = TurretIndoorDeployable; // z0dd - ZOD, 4/25/02. Was wrong: IndoorTurretBarrel + offset = "0 -1 0"; // L/R - F/B - T/B + rotation = "0 1 0 90"; + + projectile = ImplacmentBullet; + projectileType = TracerProjectile; + + usesEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.5; + projectileSpread = 0.5 / 1000.0; + maxSpread = 8 / 1000.0; + + lightType = "WeaponFireLight"; + lightColor = "0.25 0.15 0.15 1.0"; + lightTime = "1000"; + lightRadius = "2"; + + muzzleFlash = IndoorTurretMuzzleFlash; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1000; + thinkTimeMS = 100000; + degPerSecTheta = 580; + degPerSecPhi = 960; + attackRadius = 240; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = IBLSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.01; + stateFire[3] = true; + stateShockwave[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = IBLFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 0.03; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + +function DeployableIndoorBarrel::onFire(%data, %obj, %slot){ + %data.lightStart = getSimTime(); + + if(%obj.spread $= "") + %obj.spread = %data.projectileSpread; + else + %obj.spread = %obj.spread + (4 / 10000); + + if(%obj.lowSpreadLoop $= "") + %obj.lowSpreadLoop = schedule(250, 0, "lowerSpread", %data, %obj); + if(%obj.spread > %data.maxspread) + %obj.spread = %data.maxspread; + + %vec = %obj.getMuzzleVector(%slot); + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + %initialPos = %obj.getMuzzlePoint(%slot); + + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + }; + %obj.lastProjectile = %p; + MissionCleanup.add(%p); + + if(%obj.client) + %obj.client.projectile = %p; +} + +function lowerSpread(%data, %obj){ + %obj.spread = %obj.spread - (4 / 10000); + if(%obj.spread < %data.projectileSpread){ + %obj.spread = %data.projectileSpread; + %obj.lowSpreadLoop = ""; + return; + } + %obj.lowSpreadLoop = schedule(120, 0, "lowerSpread", %data, %obj); +} + +function TurretIndoorDeployableImage::onDeploy(%item, %plyr, %slot) +{ + if(!isObject(%plyr.depTurret)){ + %plyr.deplStation = ""; + %plyr.depTurret = ""; + } + if(%plyr.deplStation != 1){ + %rot = %item.getInitialRotation(%plyr); + %className = "Turret"; + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + %deplObj.setDeployRotation(%item.surfacePt, %item.surfaceNrm); + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + addToDeployGroup(%deplObj); + AIDeployObject(%plyr.client, %deplObj); + + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + $TeamDeployedCount[%plyr.team, %item.item]++; + + if (%classname !$= "Item") + %deplObj.deploy(); + addDSurface(%item.surface,%deplObj); + + %plyr.deplStation = 1; + %plyr.depTurret = %deplObj; + + return %deplObj; + } + else { + %rot = %item.getInitialRotation(%plyr); + %className = "StaticShape"; + + %deplObj = new (%className)() { + dataBlock = "ControlStation"; + scale = "0.3 0.3 1"; + }; + + %rot = %plyr.getRotation(); + + %deplObj.setTransform(VectorAdd(%item.surfacePt, %plac) SPC %rot); + + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + addToDeployGroup(%deplObj); + + AIDeployObject(%plyr.client, %deplObj); + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + %deplObj.deploy(); + + %deplObj.Turret = %plyr.depTurret; + %deplObj.Turret.Base = %deplObj; + + %startvec = vectorAdd(%deplObj.getPosition(), "0 0 2.5"); + %endvec = %deplObj.Turret.getWorldBoxCenter(); + if(vectorLen(vectorSub(%plyr.depTurret.getPosition(), %startvec)) >= 4){ + %deplObj.schedule(10, "delete"); + messageclient(%plyr.client, 'MsgClient', "Control position must be close to turret."); + return; + } + + %search = containerRayCast(%startvec, %endvec, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TurretObjectType); + if(%search){ + if(getWord(%search,0) $= %deplObj){ + echo(%deplObj.Turret@" "@getWord(%search,0)); + %deplObj.schedule(10, "delete"); + messageclient(%plyr.client, 'MsgClient', "Must be able to grasp turret from control position."); + return; + } + } + + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + + %plyr.deplStation = ""; + %plyr.depTurret = ""; + + addDSurface(%item.surface,%deplObj); + return %deplObj; + } +} + +function ControlStation::onCollision(%data,%obj,%col) { + if(%col.getDatablock().className !$= "Armor") + return; + if(%obj.getMountNodeObject(0) == 0 && %col.mountVehicle){ + %obj.mountObject(%col,0); + %col.setControlObject(%obj.Turret); + %col.client.setObjectActiveImage(%obj.Turret, 0); + } +} diff --git a/Scripts/Turrets/missileBarrelLarge.cs b/Scripts/Turrets/missileBarrelLarge.cs new file mode 100644 index 0000000..b1c7659 --- /dev/null +++ b/Scripts/Turrets/missileBarrelLarge.cs @@ -0,0 +1,236 @@ +//-------------------------------------------------------------------------- +// Large Missile Turret +// +// z0dd - ZOD, 4/25/02. Labels for sound datablocks were wrong. +//------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// Sounds +//-------------------------------------- +datablock EffectProfile(MILSwitchEffect) +{ + effectname = "powered/turret_heavy_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(MILFireEffect) +{ + effectname = "powered/turret_missile_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(MILSwitchSound) +{ + filename = "fx/powered/turret_missile_activate.wav"; + description = AudioClose3d; + preload = true; + effect = MILSwitchEffect; +}; + +datablock AudioProfile(MILFireSound) +{ + filename = "fx/powered/turret_missile_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = MILFireEffect; +}; + + +//-------------------------------------------------------------------------- +// Particle effects: Note that we pull the below datablocks from +// scripts/weapons/missileLauncher.cs +//-------------------------------------- +//datablock ParticleData(MissileSmokeParticle) +//datablock ParticleEmitterData(MissileSmokeEmitter) + + +//-------------------------------------------------------------------------- +// Explosion: from scripts/weapons/disc.cs +//-------------------------------------- +//dataBlock ExplosionData(DiscExplosion) + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(TurretMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 1.0; + damageRadius = 4.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 2500; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.6; + + explosion = "MissileExplosion"; + velInheritFactor = 0.2; + + splash = MissileSplash; + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 20000; + muzzleVelocity = 90.0; // z0dd - ZOD, 3/27/02. Was 80. Velocity of projectile + turningSpeed = 90.0; + + proximityRadius = 4; + + terrainAvoidanceSpeed = 180; + terrainScanAhead = 25; + terrainHeightFail = 12; + terrainAvoidanceRadius = 100; + + useFlechette = true; + flechetteDelayMs = 550; + casingDeb = FlechetteDebris; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(sidewinder_Turret) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 1.0; + damageRadius = 3.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 650; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 15000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 80.0; + maxVelocity = 350.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 58.0; + acceleration = 75.0; + + proximityRadius = 3; + + terrainAvoidanceSpeed = 180; + terrainScanAhead = 25; + terrainHeightFail = 12; + terrainAvoidanceRadius = 100; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 1000; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +//-------------------------------------------------------------------------- +//-------------------------------------- Fusion Turret Image +// +datablock TurretImageData(MissileBarrelLarge) +{ + shapeFile = "turret_missile_large.dts"; + item = MissileBarrelPack; // z0dd - ZOD, 4/25/02. Was wrong: MissileBarrelLargePack + + projectile = SideWinder_Turret; + projectileType = SeekerProjectile; + + usesEnergy = true; + fireEnergy = 60.0; + minEnergy = 60.0; + + isSeeker = true; + seekRadius = 400; + maxSeekAngle = 30; + seekTime = 0.35; + minSeekHeat = 0.6; + emap = true; + minTargetingDistance = 40; + + // Turret parameters + activationMS = 175; // z0dd - ZOD, 3/27/02. Was 250. Amount of time it takes turret to unfold + deactivateDelayMS = 500; + thinkTimeMS = 140; // z0dd - ZOD, 3/27/02. Was 200. Amount of time before turret starts to unfold (activate) + degPerSecTheta = 580; + degPerSecPhi = 1080; + attackRadius = 400; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = MILSwitchSound; // z0dd - ZOD, 3/27/02. Was MBLSwitchSound, changed for sound fix. + stateTimeoutValue[1] = 2; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.3; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = MILFireSound; // z0dd - ZOD, 3/27/02. Was MBLFireSound, changed for sound fix. + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 3.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 2; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + diff --git a/Scripts/Turrets/mortarBarrelLarge.cs b/Scripts/Turrets/mortarBarrelLarge.cs new file mode 100644 index 0000000..d3f5a12 --- /dev/null +++ b/Scripts/Turrets/mortarBarrelLarge.cs @@ -0,0 +1 @@ + diff --git a/Scripts/Turrets/outdoorDeployableBarrel.cs b/Scripts/Turrets/outdoorDeployableBarrel.cs new file mode 100644 index 0000000..9d96321 --- /dev/null +++ b/Scripts/Turrets/outdoorDeployableBarrel.cs @@ -0,0 +1,242 @@ +// -------------------------------------------------------------- +// Outdoor Deployable Turret barrel +// -------------------------------------------------------------- + +// -------------------------------------------------------------- +// Sound datablocks +// -------------------------------------------------------------- +datablock EffectProfile(OBLSwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(OBLFireEffect) +{ + effectname = "powered/turret_outdoor_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(OBLSwitchSound) +{ + filename = "fx/powered/turret_light_activate.wav"; + description = AudioClose3d; + preload = true; + effect = OBLSwitchEffect; +}; + +datablock AudioProfile(OBLFireSound) +{ + filename = "fx/powered/turret_outdoor_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = OBLFireEffect; +}; + +// -------------------------------------------------------------- +// Projectile data +// -------------------------------------------------------------- + +datablock GrenadeProjectileData(AntiPersonnelMortar) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.15; + damageRadius = 20.0; + radiusDamageType = $DamageType::OutdoorDepTurret; + kickBackStrength = 0; + bubbleEmitTime = 1.0; + + sound = GrenadeProjectileSound; + explosion = "GrenadeExplosion"; + underwaterExplosion = "UnderwaterGrenadeExplosion"; + velInheritFactor = 0.85; // z0dd - ZOD, 3/30/02. Was 0.5 + splash = GrenadeSplash; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.30; + grenadeFriction = 0.2; + armingDelayMS = 100; + muzzleVelocity = 50.0; + gravityMod = 1.0; +}; + +// -------------------------------------------------------------- + +datablock SensorData(DeployedOutdoorTurretSensor) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 60; +}; + +datablock ShockwaveData(OutdoorTurretMuzzleFlash) +{ + width = 0.5; + numSegments = 13; + numVertSegments = 1; + velocity = 2.0; + acceleration = -1.0; + lifetimeMS = 300; + height = 0.1; + verticalCurve = 0.5; + + mapToTerrain = false; + renderBottom = false; + orientToNormal = true; + renderSquare = true; + + texture[0] = "special/blasterHit"; + texture[1] = "special/gradient"; + texWrap = 3.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "1.0 0.8 0.5 1.0"; + colors[1] = "1.0 0.8 0.5 1.0"; + colors[2] = "1.0 0.8 0.5 0.0"; +}; + +datablock TurretData(TurretDeployedOutdoor) : TurretDamageProfile +{ + className = DeployedTurret; + shapeFile = "turret_outdoor_deploy.dts"; + + rechargeRate = 0.15; + + mass = 5.0; + maxDamage = 1.2; + destroyedLevel = 1.2; + disabledLevel = 0.9; + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.3; + expImpulse = 500.0; + repairRate = 0; + + deployedObject = true; + + thetaMin = 0; + thetaMax = 145; + thetaNull = 90; + + yawVariance = 30.0; // these will smooth out the elf tracking code. + pitchVariance = 30.0; // more or less just tolerances + + isShielded = false; + energyPerDamagePoint = 110; + maxEnergy = 60; + renderWhenDestroyed = true; + barrel = DeployableOutdoorBarrel; + heatSignature = 0; + + canControl = true; + cmdCategory = "DTactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Anti-Infantry Mortar'; + targetTypeTag = 'Turret'; + sensorData = DeployedOutdoorTurretSensor; + sensorRadius = DeployedOutdoorTurretSensor.detectRadius; + sensorColor = "191 0 226"; + + firstPersonOnly = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = TurretDebrisSmall; +}; + +datablock TurretImageData(DeployableOutdoorBarrel) +{ + shapeFile = "turret_muzzlepoint.dts"; + item = TurretOutdoorDeployable; + + projectileType = GrenadeProjectile; + projectile = AntiPersonnelMortar; + usesEnergy = true; + fireEnergy = 0.0; + minEnergy = 30.0; + + lightType = "WeaponFireLight"; + lightColor = "0.25 0.25 0.15 0.2"; + lightTime = "1000"; + lightRadius = "2"; + + muzzleFlash = OutdoorTurretMuzzleFlash; + projectileSpread = 3.0 / 1000.0; + + DontFireInsideDamageRadius = true; + DamageRadius = 30; + + // Turret parameters + activationMS = 300; + deactivateDelayMS = 600; + thinkTimeMS = 200; + degPerSecTheta = 580; + degPerSecPhi = 960; + attackRadius = 240; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = OBLSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 1.5; + stateFire[3] = true; + stateShockwave[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = OBLFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 0.8; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; + diff --git a/Scripts/Turrets/plasmaBarrelLarge.cs b/Scripts/Turrets/plasmaBarrelLarge.cs new file mode 100644 index 0000000..e17fc45 --- /dev/null +++ b/Scripts/Turrets/plasmaBarrelLarge.cs @@ -0,0 +1,178 @@ +//-------------------------------------------------------------------------- +// Chaingun Turret - Created by Dondelium_X, Greatly Modified by Blnukem +//-------------------------------------------------------------------------- + +//-------------------------------------------------------------------------- +// Sounds +//-------------------------------------------------------------------------- +datablock EffectProfile(HeavyCGTurretSwitchEffect) +{ + effectname = "powered/turret_light_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; +datablock AudioProfile(HeavyCGTurretSwitchSound) +{ + filename = "fx/powered/turret_light_activate.wav"; + description = AudioClose3d; + preload = true; + effect = HeavyCGTurretSwitchEffect; +}; + +datablock EffectProfile(HeavyCGTurretFireEffect) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + minDistance = 2.5; + maxDistance = 8.0; +}; + +datablock AudioProfile(HeavyCGTurretFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = HeavyCGTurretFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------------------------------------------- + +datablock TracerProjectileData(HVCGTurretBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.13; + directDamageType = $DamageType::PlasmaTurret; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + HeadMultiplier = 1.5; + LegsMultiplier = 0.5; + + kickBackStrength = 50.0; + sound = ChaingunProjectile; + + dryVelocity = 3000.0; + wetVelocity = 1000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 10.0/255.0 @ " " @ 30.0/255.0 @ " " @ 240.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.2; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; + + hasLight = true; + lightRadius = 4.0; + lightColor = "0.5 0.5 0.175"; + +}; + +//-------------------------------------------------------------------------- +// Chaingun Turret Image +//-------------------------------------------------------------------------- + +datablock TurretImageData(PlasmaBarrelLarge) +{ + shapeFile = "turret_tank_barrelchain.dts"; + item = ChaingunBarrelPack; // z0dd - ZOD, 4/25/02. Was wrong: ChaingunBarrelLargePack + mountPoint = 0; + + projectile = HVCGTurretBullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "0.0 -10.0 10.0"; + shellExitOffset = "0.0 0.0 1.0"; + shellExitVariance = 10.0; + shellVelocity = 3.5; + + usesEnergy = true; + fireEnergy = 0; + minEnergy = 1; + emap = true; + + projectileSpread = 6.0 / 1000.0; + + // Turret parameters + activationMS = 700; // z0dd - ZOD, 3/27/02. Was 1000. Amount of time it takes turret to unfold + deactivateDelayMS = 1500; + thinkTimeMS = 120; // z0dd - ZOD, 3/27/02. Was 200. Amount of time before turret starts to unfold (activate) + degPerSecTheta = 300; + degPerSecPhi = 500; + attackRadius = 200; // z0dd - ZOD, 3/27/02. Was 120 + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = HeavyCGTurretSwitchSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = HeavyCGTurretFireSound; + stateScript[3] = "onFire"; + stateFire[3] = true; + stateEjectShell[3] = true; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateTimeoutValue[4] = 0.01; + stateAllowImageChange[4] = false; + stateWaitForTimeout[4] = true; + stateSequence[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 1; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + + stateName[7] = "NoAmmo"; + stateTransitionOnAmmo[7] = "Reload"; + stateSequence[7] = "NoAmmo"; +}; diff --git a/Scripts/Vehicles/Vehicle_strikefighter.cs b/Scripts/Vehicles/Vehicle_strikefighter.cs new file mode 100644 index 0000000..0e97df4 --- /dev/null +++ b/Scripts/Vehicles/Vehicle_strikefighter.cs @@ -0,0 +1,1635 @@ +//******************************************************** +// Strike Fighter +//******************************************************** +//Contains: +//-1 forward heavy Chain Gun +//-2 Missiles +//-3 Napalm bombs +//******************************************************** + +//---------------------------------------- +//effects +//---------------------------------------- +datablock ParticleData(NapalmInitExpFlameParticle) +{ + dragCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.1; + lifetimeMS = 2000; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "1 0.18 0.03 0.6"; + colors[1] = "1 0.18 0.03 0.0"; + sizes[0] = 7; + sizes[1] = 8; +}; + +datablock ParticleEmitterData(NapalmInitExpFlameEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionOffset = 2.0; + ejectionVelocity = 30.0; + velocityVariance = 10.0; + thetaMin = 0.0; + thetaMax = 90.0; + lifetimeMS = 250; + + particles = "NapalmInitExpFlameParticle"; +}; + +datablock ParticleData(NapalmExpGroundBurnParticle) +{ + dragCoefficient = 2; + gravityCoefficient = -0.4; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "1 0.18 0.03 0.6"; + colors[1] = "1 0.18 0.03 0.0"; + sizes[0] = 6; + sizes[1] = 6.75; +}; + +datablock ParticleEmitterData(NapalmExpGroundBurnEmitter) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionOffset = 0.0; + ejectionVelocity = 10.0; + velocityVariance = 10.0; + thetaMin = 87.0; + thetaMax = 88.0; + lifetimeMS = 10000; + + particles = "NapalmExpGroundBurnParticle"; +}; + +datablock ParticleData(NapalmExpGroundBurnSmokeParticle) +{ + dragCoefficient = 2; + gravityCoefficient = -0.4; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "particleTest"; + colors[0] = "0.3 0.3 0.3 0.6"; + colors[1] = "0.3 0.3 0.3 0.0"; + sizes[0] = 3; + sizes[1] = 8; +}; + +datablock ParticleEmitterData(NapalmExpGroundBurnSmokeEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionOffset = 7.0; + ejectionVelocity = 10.0; + velocityVariance = 10.0; + thetaMin = 0.0; + thetaMax = 60.0; + lifetimeMS = 10000; + + particles = "NapalmExpGroundBurnSmokeParticle"; +}; + +datablock ExplosionData(NapalmExplosion) +{ +// soundProfile = BombExplosionSound; + soundProfile = MortarExplosionSound; + emitter[0] = NapalmInitExpFlameEmitter; + emitter[1] = NapalmExpGroundBurnEmitter; + emitter[2] = NapalmExpGroundBurnSmokeEmitter; + + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 10000; + playSpeed = 0.7; + + sizes[0] = "7.0 7.0 7.0"; + sizes[1] = "7.0 7.0 7.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + + +datablock FlyingVehicleData(StrikeFlyer) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_air_scout.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = false; + isProtectedMountPoint[1] = false; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 10.0; + + maxDamage = 3.5; + destroyedLevel = 3.5; + + HDAddMassLevel = 2.625; + HDMassImage = LflyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 500; // Afterburner and any energy weapon pool + rechargeRate = 4; + + minDrag = 25; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 38; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 1; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.8; // Dampen control input so you don't` whack out at very slow speeds + + + // Maneuvering + maxSteeringAngle = 7.0; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 4750; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 625; // Steering jets (force applied when you move the mouse) + steeringRollForce = 3000; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 2.5; // Height off the ground at rest + createHoverHeight = 1; // Height off the ground when created + maxForwardSpeed = 150; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 2000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 12; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 0.0; + + // Rigid body + mass = 180; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 150; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MeLightDamageSmoke; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.4; + damageLevelTolerance[1] = 0.75; + numDmgEmitterAreas = 1; + + // + max[chaingunAmmo] = 1500; + max[MissileLauncherAmmo] = 2; + max[MortarAmmo] = 2; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'F41 Awring Strike'; + targetTypeTag = 'Fighter'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + + numWeapons = 3; + + replaceTime = 110; + + max[plasmaammo] = 50; + flaretime = 100; + flarelife = 850; + flarechance = 0.35; +}; + +//---------------------------------------------- +//Projectiles +//---------------------------------------------- + +datablock BombProjectileData(NapalmBomb) +{ + projectileShapeName = "bomb.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 1.0; + damageRadius = 30; + radiusDamageType = $DamageType::Plasma; + kickBackStrength = 2000; + + explosion = "VehicleBombExplosion"; + velInheritFactor = 1.0; + + grenadeElasticity = 0.25; + grenadeFriction = 0.4; + armingDelayMS = 2000; + muzzleVelocity = 0.1; + drag = 0.3; + + minRotSpeed = "60.0 0.0 0.0"; + maxRotSpeed = "80.0 0.0 0.0"; + scale = "1.0 1.0 1.0"; + + sound = BomberBombProjectileSound; +}; + +datablock TracerProjectileData(napalmSubExplosion) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::Plasma; + explosion = NapalmExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.3; + damageRadius = 20; + radiusDamageType = $DamageType::Plasma; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 30.0; + wetVelocity = 30.0; + velInheritFactor = 0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.20; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 1.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock SeekerProjectileData(sidewinder_MarkIII) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 1.0; + damageRadius = 12.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 750; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 20000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 12.0; + maxVelocity = 215.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 60.0; + acceleration = 100.0; + + proximityRadius = 5; + + terrainAvoidanceSpeed = 100; + terrainScanAhead = 25; + terrainHeightFail = 12; + terrainAvoidanceRadius = 30; + + flareDistance = 100; + flareAngle = 25; + minSeekHeat = 0.5; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 100; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +//-------------------------------------------------- +//weaponImages +//-------------------------------------------------- + +datablock ShapeBaseImageData(StrikeChaingunImage) +{ + className = WeaponImage; + shapeFile = "turret_missile_large.dts"; //was turret_tank_barrelchain.dts + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Heli_CG_Bullet; + projectileType = TracerProjectile; + mountPoint = 10; + offset = "0 1.5 -0.2"; // L/R - F/B - T/B was "0 3.25 0.75" + rotation = "0 1 0 180"; + + projectileSpread = 1.0 / 1000.0; + + usesEnergy = false; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateFire[3] = true; + stateScript[3] = "onFire"; + stateSound[3] = ShrikeBlasterFire; + stateTimeoutValue[3] = 0.01; + stateTransitionOnTimeout[3] = "Reload"; + stateAllowImageChange[3] = false; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.01; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(StrikeMissileImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = MissileLauncher; + ammo = MissileLauncherAmmo; + projectile = sidewinder_MarkIII; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(StrikeBombImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = Mortar; + ammo = MortarAmmo; + projectile = NapalmBomb; + projectileType = BombProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = BomberBombFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = BomberBombDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +//-------------------------------------- +// STRIKE FIGHTER TURRET CHARACTERISTICS +//-------------------------------------- + +datablock TurretData(StrikeTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = StrikeFlyer.maxDamage; + destroyedLevel = StrikeFlyer.destroyedLevel; + + thetaMin = 90; + thetaMax = 180; + + heatSignature = 0.0; + + // capacitor + maxCapacitorEnergy = 500; + capacitorRechargeRate = 5.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + targetNameTag = 'Strike Fighter'; + targetTypeTag = 'Turret'; + + max[ChaingunAmmo] = 4; + max[MortarAmmo] = 1; +}; + +datablock TurretImageData(StrikeTATMissileImage) +{ + className = WeaponImage; + item = MissileLauncher; + ammo = ChaingunAmmo; + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + + shapeFile = "turret_missile_large.dts"; + mountPoint = 0; + offset = "-0.45 0 0.0"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = BomberTurretFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + //stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateDirection[5] = false; + stateSequence[5] = "Activate"; + stateTimeoutValue[5] = 1.0; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultMortarDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TurretImageData(StrikeTBombImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = Mortar; + ammo = MortarAmmo; + projectile = shrikeBomb; + projectileType = BombProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = BomberBombFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = BomberBombDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock TurretImageData(StrikeTL) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 1; + offset = "0 0 0"; + + projectile = GunshipTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = true; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + + stateName[1] = "Ready"; + stateTransitionOnTriggerDown[1] = "Fire"; + + stateName[2] = "Fire"; + stateEnergyDrain[2] = 0; + stateFire[2] = true; + stateScript[2] = "onFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "Deconstruct"; + + stateName[3] = "Deconstruct"; + stateScript[3] = "onDecon"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Ready"; +}; + +function StrikeTL::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.lastprojectile = %p; +} + +function StrikeTL::onDecon(%data, %obj, %slot) +{ + %pos = %obj.lastProjectile.getTargetPoint(); + + if(%obj.beacon) + %obj.beacon.delete(); + %obj.beacon = new BeaconObject() { + dataBlock = "TargeterBeacon"; + beaconType = "vehicle"; + position = %pos; + }; + + %obj.beacon.playThread($AmbientThread, "ambient"); + %obj.beacon.team = %obj.team; + %obj.beacon.sourceObject = %obj; + + // give it a team target + %obj.beacon.setTarget(%obj.team); + MissionCleanup.add(%obj.beacon); + + %obj.posLaze = 0; + Parent::deconstruct(%data, %obj, %slot); +} + +function StrikeTATMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.beacon)) + %p.setObjectTarget(%obj.beacon); + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +} + +function StrikeTBombImage::onFire(%data,%obj,%slot){ + %obj.decInventory(%data.ammo, 1); + %data = "LGB"; + %pn = %data.create(0); + %vec = vectorScale(%obj.vehicleMounted.getUpVector(),-4); + %pos = vectorAdd(%obj.getPosition(),%vec); + %rot = getWords(%obj.vehicleMounted.getTransform(),3,6); + %pn.setTransform(%pos SPC %rot); + %pn.LGBsourceObject = %obj; + %pn.LGBsourceSlot = %slot; + %pn.applyImpulse(%pn.getPosition(),vectorScale(%obj.vehicleMounted.getVelocity(),%data.mass)); + MissionCleanup.add(%pn); + + if(isObject(%obj.beacon)) + LGBhomein(%pn,%obj.beacon); +} + +function StrikeTurret::onTrigger(%data, %obj, %trigger, %state) +{ + //error("onTrigger: trigger = " @ %trigger @ ", state = " @ %state); + //error("obj = " @ %obj @ ", class " @ %obj.getClassName()); + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(3, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(3, true); + else + %obj.setImageTrigger(3, false); + } + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + case 4: + if(%state) + %obj.setImageTrigger(4, true); + else + %obj.setImageTrigger(4, false); + + } +} + +function strikeTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %client = %obj.getControllingClient(); + %client.player.setInventory(ChaingunAmmo, 0); + %client.player.setInventory(MortarAmmo, 0); + %client.player.isBomber = false; + %client.player.mountVehicle = false; + if(%client.player.getState() !$= "Dead") + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +//------------------------------------------------------ +//Vehicle Functions +//------------------------------------------------------ + +function StrikeFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(ScoutChaingunParam, 0); + %obj.mountImage(StrikeMissileImage, 2); + %obj.mountImage(StrikeBombImage, 3); + %obj.mountImage(StrikeChaingunImage, 4); + %obj.selectedWeapon = 1; + %obj.nextWeaponFire = 2; + %obj.setInventory(chaingunammo, 1500); + %obj.setInventory(MissileLauncherAmmo, 2); + %obj.setInventory(MortarAmmo, 2); + %obj.setInventory(plasmaAmmo, 50); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(StrikeTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(AIAimingTurretBarrel, 0); + %turret.mountImage(StrikeTATMissileImage, 2); + %turret.mountImage(StrikeTbombImage, 3); + %turret.mountImage(StrikeTL, 4); + %turret.setInventory(MortarAmmo, 1); + %turret.setInventory(ChaingunAmmo, 4); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function strikeFlyer::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if (%client = %turret.getControllingClient()) + { + %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); + if(isObject(%turret.beacon)) + %turret.beacon.delete(); +} + +function StrikeFlyer::playerMounted(%data, %obj, %player, %node){ + if (%node == 0) + { + $numVWeapons = 3; + %ammoAmt = %player.inv[MissileLauncherAmmo]; + if(%ammoAmt) + %obj.setInventory(MissileLauncherAmmo, 2); + %ammoAmt = %player.inv[MortarAmmo]; + if(%ammoAmt) + %obj.setInventory(MortarAmmo, 3); + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.incInventory(chaingunAmmo, %ammoAmt); + if(%ammoAmt) + %obj.incInventory(plasmaAmmo, 50); + bottomPrint(%player.client, "Strike Fighter: wep1 CG, wep2 missile, wep3 Napalm bombs", 5, 2 ); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Shrike2", %node); + %obj.selectedWeapon = 1; + commandToClient(%player.client, 'SetWeaponryVehicleKeys', true); + } + else if (%node == 1) + { + $numVWeapons = 2; + %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.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + + commandToClient(%player.client, 'setHudMode', 'Pilot', "bomber", %node); + %player.isBomber = true; + %ammoAmt = %player.inv[MortarAmmo]; + if(%ammoAmt) + %obj.turretobject.setInventory(MortarAmmo, 1); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.turretobject.setInventory(chaingunAmmo, 4); + setTargetSensorGroup(%turret.getTarget(), %player.team); + bottomPrint(%player.client, "Strike Fighter: wep1 LGM Wep2 LG bunker buster bomb", 5, 2 ); + } + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function StrikeFlyer::playerDismounted(%data, %obj, %player) +{ + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + %obj.setImageTrigger(6, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function strikeTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function strikeTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function StrikeFlyer::onTrigger(%data, %obj, %trigger, %state) +{ + // data = ScoutFlyer datablock + // obj = ScoutFlyer object number + // trigger = 0 for "fire", 1 for "jump", 3 for "thrust" + // state = 1 for firing, 0 for not firing + if(%trigger == 0) + { + switch (%state) { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + case 1: + %obj.fireWeapon = true; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, true); + } + else if(%obj.selectedWeapon == 2) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, true); + %obj.setImageTrigger(4, false); + } + } + } + else if (%trigger ==4){ + switch (%state) + { + case 0: + %obj.flaring = 0; + case 1: + %obj.flaring = 1; + schedule(%data.flaretime, 0, "fighterdropflares",%obj,%data.flaretime,%data.flarelife,%data.flarechance); + } + } +} + +function strikeflyer::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastPilot.lastVehicle == %obj) + if(%obj.getMountNodeObject(0) == %obj.lastPilot) + schedule(200, %obj.lastPilot, "scKillPilot", %obj.lastPilot, %obj.lastDamagedBy); + + Parent::onDestroyed(%data, %obj, %prevState); +} + +//------------------------------------------------------ +//Weapon Functions +//------------------------------------------------------ + +function StrikeMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function StrikeBombImage::onFire(%data,%obj,%slot){ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + %p.spdvec = %obj.getVelocity(); +} + +function NapalmBomb::onExplode(%data, %proj, %pos, %mod) +{ + %vec = %proj.spdvec; + %vec = getword(%vec, 0) SPC getword(%vec, 1)@" 0"; + %maxupward = vectorLen(%vec) / 10; + %vec = vectorScale(vectorNormalize(%vec),(%maxupward * 2)); + %Tpos = vectoradd(%pos,%vec); + %result = containerRayCast(vectoradd(%tpos,"0 0" SPC %maxupward), vectoradd(%tpos,"0 0 -50"), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + if(!%result) + %Tpos = NapalmFindNewDir(%pos,%vec); + else + %Tpos = posFromRaycast(%result); + + %Tpos = vectorAdd(%Tpos,"0 0 5"); + + %result = containerRayCast(vectorAdd(%pos,"0 0 10"), %Tpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + if(%result){ + %Tpos = NapalmFindNewDir(%pos,%vec); + %Tpos = vectorAdd(%pos,"0 0 5"); + } + + for(%i = 0; %i < 3; %i++){ + %rndvec = (getRandom(1, 15) - 7.5)@" "@(getRandom(1, 15) - 7.5)@" "@((getRandom() * 5) + 5); + %newpos = vectoradd(%Tpos,%rndvec); + %p = new TracerProjectile() + { + dataBlock = napalmSubExplosion; + initialDirection = "0 0 -1"; + initialPosition = %newpos; + sourceObject = %proj.sourceobject; + sourceSlot = 5; + }; + %p.sourceobject = %proj.sourceobject; + %p.vector = %vec; + %p.count = 1; + } + if (%data.hasDamageRadius) + RadiusExplosion(%proj, %pos, %data.damageRadius, %data.indirectDamage, %data.kickBackStrength, %proj.sourceObject, %data.radiusDamageType); +} + +function napalmSubExplosion::onExplode(%data, %proj, %pos, %mod) +{ + if(%proj.count < 5){ + %vec = %proj.vector; + %Tpos = vectoradd(%pos,%vec); + %maxupward = vectorLen(%vec) / 2; + %result = containerRayCast(vectoradd(%tpos,"0 0" SPC %maxupward), vectoradd(%tpos,"0 0 -50"), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + if(!%result) + %Tpos = NapalmFindNewDir(%pos,%vec); + else + %Tpos = posFromRaycast(%result); + + %Tpos = vectorAdd(%Tpos,"0 0 5"); + + %result = containerRayCast(vectorAdd(%pos,"0 0 10"), %Tpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + if(%result){ + %Tpos = NapalmFindNewDir(%pos,%vec); + %Tpos = vectorAdd(%pos,"0 0 5"); + } + + %rndvec = (getRandom(1, 10) - 5)@" "@(getRandom(1, 10) - 5)@" "@((getRandom() * 5) + 5); + %newpos = vectoradd(%Tpos,%rndvec); + %p = new TracerProjectile() + { + dataBlock = napalmSubExplosion; + initialDirection = "0 0 -1"; + initialPosition = %newpos; + sourceObject = %proj.sourceobject; + sourceSlot = 5; + }; + %p.sourceobject = %proj.sourceobject; + %p.vector = %vec; + %p.count = %proj.count + 1; + } + + if (%data.hasDamageRadius) + RadiusExplosion(%proj, %pos, %data.damageRadius, %data.indirectDamage, %data.kickBackStrength, %proj.sourceObject, %data.radiusDamageType); +} + +function NapalmFindNewDir(%pos, %vec){ + echo("new dir called"); + %maxupward = vectorLen(%vec) / 2; + %count = 0; + while((!%result || %chksearch) && %count < 10){ + if(%count == 3 || %count == 8) + %vec = VectorScale(%vec,0.5); + else if(%count == 5) + %vec = VectorScale(%vec,-2); + %rndvec = getRandom(-10,10) SPC getRandom(-10,10) SPC "0"; + %vec = vectoradd(%vec,%rndvec); + %Tpos = vectoradd(%pos,%vec); + %result = containerRayCast(vectoradd(%tpos,"0 0" SPC %maxupward), vectoradd(%tpos,"0 0 -50"), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + %Tpos = vectorAdd(posFromRaycast(%result),"0 0 10"); + %chkresult = containerRayCast(vectorAdd(%pos,"0 0 10"), %Tpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %proj); + %count++; + } + %Tpos = posFromRaycast(%result); + return %Tpos; +} + +function Strikeflyer::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} + +//---------------------------------------------------------- +//LGB +//---------------------------------------------------------- + +$LGBhomeForce = 1000; +$LGBDetDepth = 2.5; + +datablock TracerProjectileData(LGBDet) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::BomberBombs; + explosion = "artillerybarrelexplosion"; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 1.2; + damageRadius = 5.0; + radiusDamageType = $DamageType::BomberBombs; + + kickBackStrength = 500; + sound = ChaingunProjectile; + + dryVelocity = 15.0; + wetVelocity = 15.0; + velInheritFactor = 1.0; + fizzleTimeMS = 200; + lifetimeMS = 250; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TracerProjectileData(LGBUnderDet) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::BomberBombs; + explosion = "VehicleBombExplosion"; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 5.0; + damageRadius = 15.0; + radiusDamageType = $DamageType::BomberBombs; + + kickBackStrength = 500; + sound = ChaingunProjectile; + + dryVelocity = 1.0; + wetVelocity = 1.0; + velInheritFactor = 1.0; + fizzleTimeMS = 32; + lifetimeMS = 33; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock WheeledVehicleData(LGB) : MPBDamageProfile +{ + spawnOffset = "0 0 1.0"; + renderWhenDestroyed = false; + canControl = true; + catagory = "Vehicles"; + shapeFile = "vehicle_grav_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 20.0; + + numMountPoints = 0; + + cantAbandon = 1; + cantTeamSwitch = 1; + + cameraMaxDist = 20; + cameraOffset = 6; + cameraLag = 1.5; + explosion = VehicleExplosion; + explosionDamage = 0.25; + explosionRadius = 3.0; + + maxSteeringAngle = 0.3; // 20 deg. + + // Used to test if the station can deploy + stationPoints[1] = "-2.3 -7.38703 -0.65"; + stationPoints[2] = "-2.3 -11.8 -0.65"; + stationPoints[3] = "0 -7.38703 -0.65"; + stationPoints[4] = "0 -11.8 -0.65"; + stationPoints[5] = "2.3 -7.38703 -0.65"; + stationPoints[6] = "2.3 -11.8 -0.65"; + + // Rigid Body + mass = 400; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 10; + gyroDamping = 0.3; + stabilizerForce = 10; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 25; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 1; + speedDamageScale = 0.05; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 5; + collDamageMultiplier = 0.03; + + // Engine + engineTorque = 7.0 * 745; + breakTorque = 7.0 * 745; + maxWheelSpeed = 20; + + // Springs + springForce = 8000; + springDamping = 1300; + antiSwayForce = 6000; + staticLoadScale = 2; + + // Tires + tireRadius = 1.6; + tireFriction = 10.0; + tireRestitution = 0.5; + tireLateralForce = 3000; + tireLateralDamping = 400; + tireLateralRelaxation = 1; + tireLongitudinalForce = 12000; + tireLongitudinalDamping = 600; + tireLongitudinalRelaxation = 1; + tireEmitter = TireEmitter; + + // + maxDamage = 0.5; + destroyedLevel = 0.5; + + HDAddMassLevel = 0.4; + HDMassImage = APCHDMassImage; + + isShielded = false; + energyPerDamagePoint = 125; + maxEnergy = 600; + jetForce = 2800; + minJetEnergy = 60; + jetEnergyDrain = 2.75; + rechargeRate = 1.0; + + jetSound = MPBThrustSound; + engineSound = MPBEngineSound; + squeelSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 8.0; + hardSplashSoundVelocity = 12.0; + exitSplashSoundVelocity = 8.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 3; + + damageEmitter[0] = LightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "3.0 0.5 0.0 "; + damageEmitterOffset[1] = "-3.0 0.5 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundMPBIcon; + cmdMiniIconName = "commander/MiniIcons/com_mpb_grey"; + targetNameTag = 'Laser Guided'; + targetTypeTag = 'Bomb'; + sensorData = VehiclePulseSensor; + + checkRadius = 7.5225; + + observeParameters = "1 12 12"; + + runningLight[0] = MPBLight1; + runningLight[1] = MPBLight2; + + shieldEffectScale = "0.85 1.2 0.7"; +}; + +datablock StaticShapeData(LGBShape) : StaticShapeDamageProfile { + shapeFile = "bomb.dts"; + mass = 1.0; + repairRate = 0; + dynamicType = $TypeMasks::StaticShapeObjectType; + heatSignature = 0; +}; + +function LGB::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %obj.startFade(0,10,1); + %body = new StaticShape() + { + scale = "2 2 2"; + dataBlock = "LGBShape"; + }; + MissionCleanup.add(%body); + %obj.mountObject(%body, 1); + %body.vehicleMounted = %obj; +} + +function LGBhomeIn(%obj,%trg){ + if(!isObject(%obj)) + return; + if(!isObject(%trg)) + return; + + %pos = %obj.getPosition(); + %vel = %obj.getVelocity(); + %tpos = %trg.getPosition(); + %lvel = getWords(%vel,0,1) SPC "0"; + + %DDis = getWord(%pos,2) - getWord(%tpos,2); + if(%DDis <= 0) + return; + %Dtime = %DDis / (getWord(%vel,2) * -1); + %predTrg = vectorScale(%lvel,%Dtime); + %predTrg = getWords(vectorAdd(%pos,%predtrg),0,1) SPC getWord(%tpos,2); + + %vec = vectorNormalize(vectorSub(%tpos,%predTrg)); + %obj.applyImpulse(%pos,vectorScale(%vec,$LGBhomeForce)); + %obj.lastpos = %pos; + schedule(250, 0, "LGBhomeIn", %obj, %trg); +} + +function LGB::deleteAllMounted(%data, %obj){ + %body = %obj.getMountNodeObject(1); + if (isObject(%body)) + %body.schedule(2000, delete); + + %pn = new (TracerProjectile)() { + dataBlock = LGBDet; + initialDirection = vectorNormalize(vectorSub(%obj.getposition(),%obj.lastpos)); + initialPosition = %obj.getWorldBoxCenter(); + sourceObject = %obj.LGBsourceObject; + sourceSlot = %obj.LGBsourceSlot; + }; + %pn.detvec = vectorNormalize(vectorSub(%obj.getposition(),%obj.lastpos)); + MissionCleanup.add(%pn); +} + +function LGBDet::onExplode(%data, %proj, %pos, %mod) +{ + %newpos = vectoradd(%pos,vectorScale(%proj.detvec,$LGBDetDepth)); + %p = new TracerProjectile() + { + dataBlock = LGBUnderDet; + initialDirection = %proj.detvec; + initialPosition = %newpos; + sourceObject = %proj.sourceobject; + sourceSlot = %proj.sourceSlot; + }; + %p.vector = %vec; + %p.count = 1; + if (%data.hasDamageRadius) + RadiusExplosion(%proj, %pos, %data.damageRadius, %data.indirectDamage, %data.kickBackStrength, %proj.sourceObject, %data.radiusDamageType); +} \ No newline at end of file diff --git a/Scripts/Vehicles/serverVehicleHud.cs b/Scripts/Vehicles/serverVehicleHud.cs new file mode 100644 index 0000000..b248cde --- /dev/null +++ b/Scripts/Vehicles/serverVehicleHud.cs @@ -0,0 +1,525 @@ +//------------------------------------------------------------------------------ +datablock EffectProfile(VehicleAppearEffect) +{ + effectname = "vehicles/inventory_pad_appear"; + minDistance = 5; + maxDistance = 10; +}; + +datablock EffectProfile(ActivateVehiclePadEffect) +{ + effectname = "powered/vehicle_pad_on"; + minDistance = 20; + maxDistance = 30; +}; + +datablock AudioProfile(VehicleAppearSound) +{ + filename = "fx/vehicles/inventory_pad_appear.wav"; + description = AudioClosest3d; + preload = true; + effect = VehicleAppearEffect; +}; + +datablock AudioProfile(ActivateVehiclePadSound) +{ + filename = "fx/powered/vehicle_pad_on.wav"; + description = AudioClose3d; + preload = true; + effect = ActivateVehiclePadEffect; +}; + +datablock StationFXVehicleData( VehicleInvFX ) +{ + lifetime = 6.0; + + glowTopHeight = 1.5; + glowBottomHeight = 0.1; + glowTopRadius = 12.5; + glowBottomRadius = 12.0; + numGlowSegments = 26; + glowFadeTime = 3.25; + + armLightDelay = 2.3; + armLightLifetime = 3.0; + armLightFadeTime = 1.5; + numArcSegments = 10.0; + + sphereColor = "0.1 0.1 0.5"; + spherePhiSegments = 13; + sphereThetaSegments = 8; + sphereRadius = 12.0; + sphereScale = "1.05 1.05 0.85"; + + glowNodeName = "GLOWFX"; + + leftNodeName[0] = "LFX1"; + leftNodeName[1] = "LFX2"; + leftNodeName[2] = "LFX3"; + leftNodeName[3] = "LFX4"; + + rightNodeName[0] = "RFX1"; + rightNodeName[1] = "RFX2"; + rightNodeName[2] = "RFX3"; + rightNodeName[3] = "RFX4"; + + + texture[0] = "special/stationGlow"; + texture[1] = "special/stationLight2"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +function serverCmdBuyVehicle(%client, %blockName) +{ + %team = %client.getSensorGroup(); + if(vehicleCheck(%blockName, %team)) + { + %station = %client.player.station.pad; + if( (%station.ready) && (%station.station.vehicle[%blockName]) ) + { + %trans = %station.getTransform(); + %pos = getWords(%trans, 0, 2); + %matrix = VectorOrthoBasis(getWords(%trans, 3, 6)); + %yrot = getWords(%matrix, 3, 5); + %p = vectorAdd(%pos,vectorScale(%yrot, -3)); + + //%adjust = vectorMultiply(realVec(%station,"0 0 4"),"1 1 3"); + //%adjustUp = getWord(%adjust,2); + //%adjust = getWords(%adjust,0,1) SPC ((%adjustUp * 0.5) + (mAbs(%adjustUp) * -0.5)); + %p = VectorAdd(%p,RealVec(%station,"0 0 7")); + +// error(%blockName); +// error(%blockName.spawnOffset); + + + ///[Most] + ///Updated Build code for rotatable vehicle pad. + %p = vectorAdd(%p, RealVec(%station,VectorAdd(%blockName.spawnOffset,"0 0 1"))); + %forward = VectorCross(VectorCross("0 0 1",realvec(%station,"1 0 0")),"0 0 1"); + + %rot = FullRot("0 0 1",%forward); + %rrot = RotAdd(%rot,"0 0 1 3.14"); + //%rrot= %rot; + %rot = getWords(%rrot, 0,2); + %angle = getWord(%rrot, 3); + //[Most] + %mask = $TypeMasks::VehicleObjectType | $TypeMasks::PlayerObjectType | + $TypeMasks::StationObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%p, %blockName.checkRadius, %mask); + + %clear = 1; + for (%x = 0; (%obj = containerSearchNext()) != 0; %x++) + { + if((%obj.getType() & $TypeMasks::VehicleObjectType) && (%obj.getDataBlock().checkIfPlayersMounted(%obj))) + { + %clear = 0; + break; + } + else + if (%obj !=%station.station) + %removeObjects[%x] = %obj; + } + if(%clear) + { + %fadeTime = 0; + for(%i = 0; %i < %x; %i++) + { + if(%removeObjects[%i].getType() & $TypeMasks::PlayerObjectType) + { + %pData = %removeObjects[%i].getDataBlock(); + %pData.damageObject(%removeObjects[%i], 0, "0 0 0", 1000, $DamageType::VehicleSpawn); + } + else + { + %removeObjects[%i].mountable = 0; + %removeObjects[%i].startFade( 1000, 0, true ); + %removeObjects[%i].schedule(1001, "delete"); + %fadeTime = 1500; + } + } + schedule(%fadeTime, 0, "createVehicle", %client, %station, %blockName, %team , %p, %rot, %angle); + } + else + MessageClient(%client, "", 'Can\'t create vehicle. A player is on the creation pad.'); + } + } +} + +function createVehicle(%client, %station, %blockName, %team , %pos, %rot, %angle) +{ + %obj = %blockName.create(%team); + if(%obj) + { + %station.ready = false; + %obj.team = %team; + %obj.useCreateHeight(true); + %obj.schedule(5500, "useCreateHeight", false); + %obj.getDataBlock().isMountable(%obj, false); + %obj.getDataBlock().schedule(6500, "isMountable", %obj, true); + vehicleListAdd(%blockName, %obj); + MissionCleanup.add(%obj); + + %turret = %obj.getMountNodeObject(10); + if(%turret > 0) + { + %turret.setCloaked(true); + %turret.schedule(4800, "setCloaked", false); + } + + %obj.setCloaked(true); + %obj.setTransform(%pos @ " " @ %rot @ " " @ %angle); + + %obj.schedule(3700, "playAudio", 0, VehicleAppearSound); + %obj.schedule(4800, "setCloaked", false); + + if(%client.player.lastVehicle) + { + %client.player.lastVehicle.lastPilot = ""; + vehicleAbandonTimeOut(%client.player.lastVehicle); + %client.player.lastVehicle = ""; + } + %client.player.lastVehicle = %obj; + %obj.lastPilot = %client.player; + %station.playAudio($ActivateSound, ActivateVehiclePadSound); + %ppos = VectorAdd(%station.getTransform(),RealVec(%station,"0 0 2")); + if (%station.getDatablock().getName() $= "DeployableVehiclePad") + { + %station.playThread($ActivateThread,"activate2"); + %up = realvec(%station,"0 0 1"); + %forward = realvec(%station,"1 0 0"); + %p1 = CreateEmitter(%ppos,DVPADE); + %p2 = CreateEmitter(%ppos,DVPADE); + %p1.setRotation(FullRot(%up,%forward)); + %p2.setRotation(FullRot(VectorScale(%up,-1),%forward)); + %p1.schedule(5000,"delete"); + %p2.schedule(5000,"delete"); + } + else if (%station.getDatablock().getName() $= "DeployableVehiclePad2") + { + %station.playThread($ActivateThread,"activate"); + %up = realvec(%station,"0 0 1"); + %forward = realvec(%station,"1 0 0"); + %p1 = CreateEmitter(%ppos,DVPADE); + %p2 = CreateEmitter(%ppos,DVPADE); + %p1.setRotation(FullRot(%up,%forward)); + %p2.setRotation(FullRot(VectorScale(%up,-1),%forward)); + %p1.schedule(5000,"delete"); + %p2.schedule(5000,"delete"); + } + else + { + %station.playThread($ActivateThread,"activate2"); + // play the FX + %fx = new StationFXVehicle() + { + dataBlock = VehicleInvFX; + stationObject = %station; + }; + } +//[[CHANGE]]!! If player is telebuying.. put him incontrol... + if ( (%client.isVehicleTeleportEnabled()) && (!%client.telebuy)) + %obj.getDataBlock().schedule(5000, "mountDriver", %obj, %client.player); + else + { + if(%obj.getDataBlock().canControl) + { + //serverCmdResetControlObject(%client); + %client.setControlObject(%obj); + commandToClient(%client, 'ControlObjectResponse', true, getControlObjectType(%obj,%client.player)); + %obj.clientControl = %client; + } + } +//[[End CHANGE]] + } + if(%obj.getTarget() != -1) + setTargetSensorGroup(%obj.getTarget(), %client.getSensorGroup()); + // We are now closing the vehicle hud when you buy a vehicle, making the following call + // unnecessary (and it breaks stuff, too!) + //VehicleHud.updateHud(%client, 'vehicleHud'); +} + +//------------------------------------------------------------------------------ +function VehicleData::mountDriver(%data, %obj, %player) +{ + if(isObject(%obj) && %obj.getDamageState() !$= "Destroyed") + { + %player.startFade(1000, 0, true); + schedule(1000, 0, "testVehicleForMount", %player, %obj); + %player.schedule(1500,"startFade",1000, 0, false); + } +} + +function testVehicleForMount(%player, %obj) +{ + if(isObject(%obj) && %obj.getDamageState() !$= "Destroyed") + %player.getDataBlock().onCollision(%player, %obj, 0); +} + + +//------------------------------------------------------------------------------ +function VehicleData::checkIfPlayersMounted(%data, %obj) +{ + for(%i = 0; %i < %obj.getDatablock().numMountPoints; %i++) + if (%obj.getMountNodeObject(%i)) + return true; + return false; +} + +//------------------------------------------------------------------------------ +function VehicleData::isMountable(%data, %obj, %val) +{ + %obj.mountable = %val; +} + +//------------------------------------------------------------------------------ +function vehicleCheck(%blockName, %team) +{ + if(($VehicleMax[%blockName] - $VehicleTotalCount[%team, %blockName]) > 0) + return true; +// else +// { +// for(%i = 0; %i < $VehicleMax[%blockName]; %i++) +// { +// %obj = $VehicleInField[%blockName, %i]; +// if(%obj.abandon) +// { +// vehicleListRemove(%blockName, %obj); +// %obj.delete(); +// return true; +// } +// } +// } + return false; +} + +//------------------------------------------------------------------------------ +function VehicleHud::updateHud( %obj, %client, %tag ) { + %client.vehInvTag = %tag; + %client.vehInvPage = ""; + if (%client.usedVehHud != 1) { + bottomPrint(%client, "Cycle weapons to view more vehicles.", 5, 1); + %client.usedVehHud = 1; + } + cycleVehicleHud(%obj, %client); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +function cycleVehicleHud(%obj, %client,%data) { + %tag = %client.vehInvTag; + %page = %client.vehInvPage; + if (%page $= "") + %page = 0; + else { + if (%data $= "prev") + %page--; + else + %page++; + } + %count = %client.player.station.lastCount; + VehicleHud::clearHud( %obj, %client, %tag, %count ); + %station = %client.player.station; + %team = %client.getSensorGroup(); + %count = 0; + %i = 0; + + if ( %station.vehicle[AWACS] ) { + %vehicleSet[AWACS] = %i; + %i++; + } + if ( %station.vehicle[HawkFlyer] ) { + %vehicleSet[HawkFlyer] = %i; + %i++; + } + if ( %station.vehicle[scoutFlyer] ) { + %vehicleSet[scoutFlyer] = %i; + %i++; + } + if ( %station.vehicle[StrikeFlyer] ) { + %vehicleSet[StrikeFlyer] = %i; + %i++; + } + if ( %station.vehicle[SuperiorityFighter] ) { + %vehicleSet[SuperiorityFighter] = %i; + %i++; + } + if ( %station.vehicle[helicopter] ) { + %vehicleSet[helicopter] = %i; + %i++; + } + if ( %station.vehicle[bomberFlyer] ) { + %vehicleSet[bomberFlyer] = %i; + %i++; + } + if ( %station.vehicle[HeavyTank] ) { + %vehicleSet[HeavyTank] = %i; + %i++; + } + if ( %station.vehicle[Personelboat] ) { + %vehicleSet[Personelboat] = %i; + %i++; + } + if ( %station.vehicle[boat] ) { + %vehicleSet[boat] = %i; + %i++; + } + if ( %station.vehicle[Sub] ) { + %vehicleSet[Sub] = %i; + %i++; + } + if ( %station.vehicle[scoutVehicle] ) { + %vehicleSet[scoutVehicle] = %i; + %i++; + } + if ( %station.vehicle[hapcFlyer] ) { + %vehicleSet[hapcFlyer] = %i; + %i++; + } + if ( %station.vehicle[gunship] ) { + %vehicleSet[gunship] = %i; + %i++; + } + if ( %station.vehicle[HeavyChopper] ) { + %vehicleSet[HeavyChopper] = %i; + %i++; + } + if ( %station.vehicle[CGTank] ) { + %vehicleSet[CGTank] = %i; + %i++; + } + if ( %station.vehicle[AssaultVehicle] ) { + %vehicleSet[AssaultVehicle] = %i; + %i++; + } + if ( %station.vehicle[mobileBaseVehicle] ) { + %vehicleSet[mobileBaseVehicle] = %i; + %i++; + } + if ( %station.vehicle[SuperScoutVehicle] && $Host::Purebuild == 1) { + %vehicleSet[SuperScoutVehicle] = %i; + %i++; + } + if ( %station.vehicle[SuperHAPCFlyer] && $Host::Purebuild == 1) { + %vehicleSet[SuperHAPCFlyer] = %i; + %i++; + } + if ( %station.vehicle[Artillery] ) { + %vehicleSet[Artillery] = %i; + %i++; + } + + %totalPages = mCeil(%i / 6) - 1; + if (%page < 0) + %page = %totalPages; + if (%page > %totalPages) + %page = 0; + %initPos = (%page * 6); + %endPos = %initPos + 5; + + if ( checkVehSet(%vehicleSet[AWACS], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "AWACS", "", AWACS, $VehicleMax[AWACS] - $VehicleTotalCount[%team, AWACS] ); + %count++; + } + if ( checkVehSet(%vehicleSet[HawkFlyer], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "FC-13 Hawk Interceptor", "", HawkFlyer, $VehicleMax[HawkFlyer] - $VehicleTotalCount[%team, HawkFlyer] ); + %count++; + } + if ( checkVehSet(%vehicleSet[SuperScoutVehicle], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Super Grav Cycle", "", SuperScoutVehicle, $VehicleMax[SuperScoutVehicle] - $VehicleTotalCount[%team,SuperScoutVehicle] ); + %count++; + } + + if ( checkVehSet(%vehicleSet[Superiorityfighter], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Tornado Superiority Fighter", "", Superiorityfighter, $VehicleMax[Superiorityfighter] - $VehicleTotalCount[%team, Superiorityfighter] ); + %count++; + } + if ( checkVehSet(%vehicleSet[scoutFlyer], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "F39 Raptor II Interceptor", "", ScoutFlyer, $VehicleMax[ScoutFlyer] - $VehicleTotalCount[%team, ScoutFlyer] ); + %count++; + } + if ( checkVehSet(%vehicleSet[StrikeFlyer], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "F41 Airwing Strike Fighter", "", StrikeFlyer, $VehicleMax[StrikeFlyer] - $VehicleTotalCount[%team, StrikeFlyer] ); + %count++; + } + if ( checkVehSet(%vehicleSet[helicopter], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "WhiteHorse Assault Chopper", "", helicopter, $VehicleMax[helicopter] - $VehicleTotalCount[%team, helicopter] ); + %count++; + } + if ( checkVehSet(%vehicleSet[bomberFlyer], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "B-34 Bomber", "", BomberFlyer, $VehicleMax[BomberFlyer] - $VehicleTotalCount[%team, BomberFlyer] ); + %count++; + } + if ( checkVehSet(%vehicleSet[HeavyTank], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "M3A2 Faustes Assualt Tank", "", HeavyTank, $VehicleMax[HeavyTank] - $VehicleTotalCount[%team, HeavyTank] ); + %count++; + } + if ( checkVehSet(%vehicleSet[Personelboat], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Grandens Transport Boat", "", Personelboat, $VehicleMax[Personelboat] - $VehicleTotalCount[%team, Personelboat] ); + %count++; + } + if ( checkVehSet(%vehicleSet[boat], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Star II Heavy GunBoat", "", boat, $VehicleMax[boat] - $VehicleTotalCount[%team, boat] ); + %count++; + } + if ( checkVehSet(%vehicleSet[sub], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Darwine IV Submarine", "", sub, $VehicleMax[sub] - $VehicleTotalCount[%team, sub] ); + %count++; + } + if ( checkVehSet(%vehicleSet[scoutVehicle], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "MK II WildCat Grav Cycle", "", scoutVehicle, $VehicleMax[scoutVehicle] - $VehicleTotalCount[%team,scoutVehicle] ); + %count++; + } + if ( checkVehSet(%vehicleSet[hapcFlyer], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "HVC Golem Heavy Transport", "", HAPCFlyer, $VehicleMax[HAPCFlyer] - $VehicleTotalCount[%team, HAPCFlyer] ); + %count++; + } + if ( checkVehSet(%vehicleSet[gunship], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "AC-290 Saber Gunship", "", gunship, $VehicleMax[gunship] - $VehicleTotalCount[%team, gunship] ); + %count++; + } + if ( checkVehSet(%vehicleSet[HeavyChopper], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Eagle VII Transport Chopper", "", HeavyChopper, $VehicleMax[HeavyChopper] - $VehicleTotalCount[%team, HeavyChopper] ); + %count++; + } + if ( checkVehSet(%vehicleSet[CGTank], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Banshee 50mm Chaingun Tank", "", CGTank, $VehicleMax[CGTank] - $VehicleTotalCount[%team, CGTank] ); + %count++; + } + if ( checkVehSet(%vehicleSet[AssaultVehicle], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "M4A1 Wolf Light Tank", "", AssaultVehicle, $VehicleMax[AssaultVehicle] - $VehicleTotalCount[%team, AssaultVehicle] ); + %count++; + } + if ( checkVehSet(%vehicleSet[mobileBaseVehicle], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Jericho MPB", "", MobileBaseVehicle, $VehicleMax[MobileBaseVehicle] - $VehicleTotalCount[%team, MobileBaseVehicle] ); + %count++; + } + if ( checkVehSet(%vehicleSet[Artillery], %initPos)) { + messageClient( %client, 'SetLineHud', "", %tag, %count, "Grendel Heavy Artillery", "", Artillery, $VehicleMax[Artillery] - $VehicleTotalCount[%team, Artillery] ); + %count++; + } + + %station.lastCount = %count; + %client.vehInvPage = %page; +} + +//------------------------------------------------------------------------------ +function VehicleHud::clearHud( %obj, %client, %tag, %count ) { + for ( %i = 0; %i < %count; %i++ ) + messageClient( %client, 'RemoveLineHud', "", %tag, %i ); +} + +//------------------------------------------------------------------------------ +function checkVehSet(%obj, %initpos) { + if ((%obj !$= "") && (%obj >= %initpos) && (%obj <= (%initpos + 5))) + return true; + else + return false; +} + +//------------------------------------------------------------------------------ +function serverCmdEnableVehicleTeleport( %client, %enabled ) +{ + %client.setVehicleTeleportEnabled( %enabled ); +} diff --git a/Scripts/Vehicles/vehicle.cs b/Scripts/Vehicles/vehicle.cs new file mode 100644 index 0000000..1c0c60f --- /dev/null +++ b/Scripts/Vehicles/vehicle.cs @@ -0,0 +1,2247 @@ +// 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) { + $VehicleList = listAdd($VehicleList,%obj,-1); + 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) + { + if ($countDownStarted) + %data.schedule(($Host::WarmupTime * 1000) / 2, "vehicleDeploy", %obj, 0, 1); + else + { + $VehiclesDeploy[$NumVehiclesDeploy] = %obj; + $NumVehiclesDeploy++; + } + } + if (%obj.mountable || %obj.mountable $= "") + %data.isMountable(%obj, true); + else + %data.isMountable(%obj, false); + + %obj.setSelfPowered(); +// %data.canObserve = true; + + if(%obj.getdatablock().getname() $= "scoutFlyer" || %obj.getdatablock().getname() $= "strikeFlyer") + checkStallLoop(%obj); +} + +function VehicleData::onRemove(%this, %obj) +{ + // if there are passengers/driver, kick them out + if(!(%obj.didDAM == 1)) + %this.deleteAllMounted(%obj); + +//[[CHANGE]] Kick commander out as well + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + + 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 = ""; + + %loc = findWord($VehicleList,%obj); + if (%loc !$= "") + $VehicleList = listDel($VehicleList,%loc); + + //[most] yah.. nukes can now be removed :D + if (%obj.nuke !$= "") + { + %obj.nuke.mpm_all_off(1); + %obj.unmountObject(%obj.nuke); + %obj.nuke.schedule(1500,"delete"); + } + //[most] + + Parent::onRemove(%this, %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); + } + 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); + } + 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) { + // TODO - temporary - remove + if ($VehicleDestroyedOverride == 1) { + %obj.setDamageLevel(0); + %obj.setDamageState(Enabled); + return; + } + 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); + %flingee.damage(0, %obj.getPosition(), 0.4, $DamageType::Crash); + } + } +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.didDAM = 1; + %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) - 20000); + %rotation = getWord(%transform, 3) SPC getWord(%transform, 4) SPC getWord(%transform, 5) SPC getWord(%transform, 6); + %obj.setTransform(%position SPC %rotation); +} +// ---------------------------------------------------------------------------------- + +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.isforcefield() && %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 == 1) + %coverage = calcBuildingInWay(%position, %targetObject); + 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); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(ScoutChaingunParam, 0); + %obj.mountImage(ScoutChaingunImage, 2); + %obj.mountImage(ScoutChaingunPairImage, 3); + %obj.mountImage(ShrikeMissileImage, 4); + %obj.mountImage(ShrikebombImage, 5); + %obj.selectedWeapon = 1; + %obj.nextWeaponFire = 2; + %obj.setInventory(MissileLauncherAmmo, 4); + %obj.setInventory(MortarAmmo, 3); + %obj.setInventory(chaingunammo, 1500); + %obj.setInventory(plasmaAmmo,15); + %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(BomberCGImage, 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.setInventory(plasmaAmmo, 20); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); +} + +//---------------------------- +// FF Transport +//---------------------------- + +function FFTransport::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(APCTurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 1); + %turret.mountImage(APCTurretBarrel, 2); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %obj.turretObject = %turret; + %turret.setAutoFire(false); + %turret.mountImage(APCTurretParam, 0); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +//---------------------------- +// SUPER HAVOC TRANSPORT FLIER +//---------------------------- + +function SuperHAPCFlyer::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); +} + +//---------------------------- +// Panzer VEHICLE +//---------------------------- + +function HeavyTank::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setInventory(plasmaAmmo, 4); + + %turret = TurretData::create(TankTurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(TankMGTurretBarrel, 2); + %turret.mountImage(TankCoaxBarrel, 3); + %turret.mountImage(TankATurretBarrel, 4); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.mountobj = %obj; + %obj.turretObject = %turret; + + //vehicle turrets should not auto fire at targets + %turret.setAutoFire(false); + + //Needed so we can set the turret parameters.. + %turret.mountImage(TankTurretParam, 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"); +} + +//************************************************************** +//* MULTI-CREW VEHICLE DELETION +//************************************************************** + +//---------------------------- +//BEOWULF ASSAULT VEHICLE +//---------------------------- + +function AssaultVehicle::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%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); +} + +//---------------------------- +//Panzer Tank +//---------------------------- + +function HeavyTank::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%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 (%obj.station !$= "") + { + %obj.station.getDataBlock().onLosePowerDisabled(%obj.station); + %obj.unmountObject(%obj.station); + %obj.station.trigger.schedule(2000, delete); + %obj.station.schedule(2000, delete); + } + if (%obj.turret !$= "") + { + %obj.turret.getDataBlock().onLosePowerDisabled(%obj.turret); + %obj.unmountObject(%obj.turret); + %obj.turret.schedule(2000, delete); + } + //[most] + if (%obj.nuke !$= "") + { + %obj.nuke.mpm_all_off(1); + %obj.unmountObject(%obj.nuke); + %obj.nuke.schedule(1500,"delete"); + } + //[most] + if (isObject(%obj.shield)) + %obj.shield.schedule(2000, delete); + + if (isObject(%obj.beacon)) + { + %obj.beacon.schedule(0, delete); + } +} + +//---------------------------- +// FF Transport +//---------------------------- + +function FFTransport::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(1); + if (!%turret) + return; + if (%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(1000, delete); + if(isObject(%obj.beacon)) + %obj.beacon.schedule(50, delete); +} + +//************************************************************** +//* WEAPON MOUNTING ON VEHICLES +//************************************************************** + +//---------------------------- +// SHRIKE SCOUT FLIER +//---------------------------- + +function Scoutflyer::playerMounted(%data, %obj, %player, %node) +{ + %ammoAmt = %player.inv[MissileLauncherAmmo]; + if(%ammoAmt) + %obj.incInventory(MissileLauncherAmmo, %ammoAmt); + + %ammoAmt = %player.inv[MortarAmmo]; + if(%ammoAmt) + %obj.incInventory(MortarAmmo, %ammoAmt); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.incInventory(chaingunAmmo, %ammoAmt); + + bottomPrint(%player.client, "Shrike: wep1 CG, wep2 missile, wep3 bombs", 5, 2 ); + + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Shrike2", %node); + %obj.selectedWeapon = 1; + $numVWeapons = 3; + commandToClient(%player.client, 'SetWeaponryVehicleKeys', true); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// THUNDERSWORD BOMBER +//---------------------------- + +function BomberFlyer::playerMounted(%data, %obj, %player, %node) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + 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) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) { + // pilot position + commandToClient(%player.client, 'setHudMode', 'Pilot', "HAPC", %node); + bottomPrint(%player.client, "Havoc: can carry 6 peopel and one ground vehicle", 5, 2 ); + } + else { + // all others + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + bottomPrint(%player.client, "Strap in and hold tight", 5, 2 ); + } + // 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 ); +} + +//---------------------------- +// FF Transport +//---------------------------- + +function FFTransport::playerMounted(%data, %obj, %player, %node) +{ + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + if (%node == 0) { + commandToClient(%player.client, 'setHudMode', 'Pilot', "HAPC", %node); + } + else if (%node == 5) + { + %turret = %obj.getMountNodeObject(1); + %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; + + $aWeaponActive = 0; + %obj.getMountNodeObject(1).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", 1); + } + else { + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + bottomPrint(%player.client, "fast and carrys 6 people", 5, 2 ); + +} + +//---------------------------- +// SUPER HAVOC TRANSPORT FLIER +//---------------------------- + +function SuperHAPCFlyer::playerMounted(%data, %obj, %player, %node) { + // [[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + 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) +{ + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + bottomPrint(%player.client, "Wildcat: person 1 driver person 2 gunner", 5, 2 ); + + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) + { + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Hoverbike", %node); + } + else if (%node == 1) + { + %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.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", 1); + %player.isBomber = true; + } + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//---------------------------- +// SUPER WILDCAT GRAV CYCLE +//---------------------------- + +function SuperScoutVehicle::playerMounted(%data, %obj, %player, %node) { + // [[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + // 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) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + 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); +} + +//---------------------------- +// PanzerTank +//---------------------------- + +function HeavyTank::playerMounted(%data, %obj, %player, %node) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + 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); + + bottomPrint(%player.client, "wep1 accurate flak cannon, wep2 long range punchey artillery ", 5, 2 ); +} + + +//---------------------------- +// JERICHO FORWARD BASE +//---------------------------- + +function MobileBaseVehicle::playerMounted(%data, %obj, %player, %node) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + // 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 (isObject(%obj.turret)) + { + 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); + } + //[most] + if (isObject(%obj.nuke)) + %obj.nuke.mpm_all_off(0); + //[most] + %obj.shield.open(); + %obj.shield.schedule(1000,"delete"); + %obj.deploySchedule = ""; + + %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; + + //[most] Only give mpb's nukes when enabled in options + if ($MPM::NukeMPB) + { + %obj.nuke=%obj.Mpm_Turret(); + %obj.nuke.ammo = %obj.nukeammo; + } + else //Otherwise give normal turret. + { + %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); + } + + + %obj.beacon = new BeaconObject() { + dataBlock = "DeployedBeacon"; + position = %obj.position; + rotation = %obj.rotation; + team = %obj.team; + }; + %obj.beacon.setBeaconType(friend); + %obj.beacon.setTarget(%obj.team); + // --------------------------------- + // z0dd - ZOD, 5/8/02. Invalid call. + //checkSpawnPos(%obj, 20); + } + } + else + { + %obj.station.setSelfPowered(); + %obj.station.playThread($PowerThread,"Power"); + //[most] //check if there "is" an turret + if (isObject(%obj.turret)) + { + %obj.turret.setSelfPowered(); + %obj.turret.playThread($PowerThread,"Power"); + } + //[most] + } + if (%deployMessage $= "" || %force) + { + //[most] + if (isObject(%obj.turret)) + { + 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); + } + if (isObject(%obj.nuke)) + %obj.nuke.mpm_all_on(); + //[most] + %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(); + //[most] Handles removal of nuke. + if (isObject(%obj.nuke)) + { + %obj.nukeammo = %obj.nuke.ammo; + %obj.nuke.mpm_all_off(1); + %obj.unmountObject(%obj.nuke); + %obj.nuke.schedule(1500,"delete"); + } + if (isObject(%obj.turret)) + { + %obj.unmountObject(%obj.turret); + %obj.turret.delete(); + %obj.turret = ""; + } + //[most] + 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; + } + 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, 100, $TypeMasks::TurretObjectType | $TypeMasks::InteriorObjectType); + 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[SuperScoutVehicle] = 0; +$VehicleMax[AssaultVehicle] = 3; +$VehicleMax[MobileBaseVehicle] = 1; +$VehicleMax[ScoutFlyer] = 5; +$VehicleMax[BomberFlyer] = 1; +$VehicleMax[HAPCFlyer] = 1; +$VehicleMax[SuperHAPCFlyer] = 0; +$VehicleMax[Artillery] = 1; +$VehicleMax[FFTransport] = 2; +$VehicleMax[HeavyTank] = 2; +$VehicleMax[boat] = 1; +$VehicleMax[sub] = 2; +$VehicleMax[Personelboat] = 3; +$vehicleMax[CGTank] = 1; +$VehicleMax[helicopter] = 2; +$VehicleMax[AWACS] = 1; +$VehicleMax[HeavyChopper] = 1; +$VehicleMax[StrikeFlyer] = 1; +$VehicleMax[superiorityFighter] = 1; +$VehicleMax[gunship] = 1; + + +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; + schedule((%data.replaceTime * 1000), 0, "changeVcount", %blockName, %obj.team); + 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 changeVcount(%blockName, %team){ + $VehicleTotalCount[%team, %blockname]--; +} + +function clearVehicleCount(%team) +{ + $VehicleTotalCount[%team, ScoutVehicle] = 0; + $VehicleTotalCount[%team, SuperScoutVehicle] = 0; + $VehicleTotalCount[%team, AssaultVehicle] = 0; + $VehicleTotalCount[%team, MobileBaseVehicle] = 0; + $VehicleTotalCount[%team, ScoutFlyer] = 0; + $VehicleTotalCount[%team, BomberFlyer] = 0; + $VehicleTotalCount[%team, HAPCFlyer] = 0; + $VehicleTotalCount[%team, SuperHAPCFlyer] = 0; + $VehicleTotalCount[%team, Artillery] = 0; + $VehicleTotalCount[%team, FFTransport] = 0; + $VehicleTotalCount[%team, HeavyTank] = 0; + $VehicleTotalCount[%team, boat] = 0; + $VehicleTotalCount[%team, sub] = 0; + $VehicleTotalCount[%team, Personelboat] = 0; + $VehicleTotalCount[%team, beamflyer] = 0; + $VehicleTotalCount[%team, CGTank] = 0; + $VehicleTotalCount[%team, helicopter] = 0; + $VehicleTotalCount[%team, AWACS] = 0; + $VehicleTotalCount[%team, HeavyChopper] = 0; + $VehicleTotalCount[%team, StrikeFlyer] = 0; + $VehicleTotalCount[%team, SuperiorityFighter] = 0; + $VehicleTotalCount[%team, gunship] = 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'; + } +} +// End z0dd - ZOD +// --------------------------------------------------------- + +function findAIEmptySeat(%vehicle, %player) +{ + %dataBlock = %vehicle.getDataBlock(); + if (%dataBlock.getName() $= "BomberFlyer") + %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" || %player.client.armor $= "Pure" || %player.client.armor $= "SpecOps") + %minNode = 0; + else + %message = '\c2Only Scout and SpecOps Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + } + else if (%player.client.armor $= "Light" || %player.client.armor $= "Medium" || %player.client.armor $= "Pure" || %player.client.armor $= "SpecOps") + %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 (%datablock.getName() $= "DropPod") + return 0; + if (%node == 0) + %message = '\c2Only Scout, SpecOps, Assault or Pure Armors can pilot this vehicle.~wfx/misc/misc.error.wav'; + else + %message = '\c2Only Scout, SpecOps, Assault or Pure 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) +{ + %DamageLevel = %targetObject.getDamageLevel(); + if (%DamageLevel == 0.0) + %Affected = 0; + 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; + } + +// TODO - check + // check for team damage + if (isObject(%sourceObject)) + %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()); + else + %sourceTeam = %sourceObject ? getTargetSensorGroup(%sourceObject.getTarget()) : -1; + + // 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; + + + // 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); + } +//------- + %DamageLevel = %targetObject.getDamageLevel(); + if (%DamageLevel > %Data.HDAddMassLevel && %targetObject.Affected == 0) + { + // Eolk - we don't really need this. + //if (%targetObject.lastPilot.isPilot() == true) + //messageClient(%targetObject.lastPilot.client, "", "!Heavy Damage to vehicle! Internal components hit, and manuverability lowered!"); // Eolk - Change from tagged string to regular string. + + %MassImage = %Data.HDMassImage; + %targetObject.mountImage(%MassImage, 7); + %targetObject.Affected = 1; + if(%targetObject.getdatablock().getname() $= "HawkFlyer" + || %targetObject.getdatablock().getname() $= "SentinelVehicle" + || %targetObject.getdatablock().getname() $= "TitanSentinel" + || %targetObject.getdatablock().getname() $= "SentinelMonitor") + return; + if(%targetObject.getClassName() $= "FlyingVehicle") + %targetObject.dmgStallLoop = schedule(250, 0, "vehicledmgStall", %targetObject); + if(%targetObject.getdatablock().getname() $= "scoutFlyer" || %targetObject.getdatablock().getname() $= "strikeFlyer") + Cancel(%targetObject.checkstallLoop); + } + if (%DamageLevel < %Data.HDAddMassLevel && %targetObject.Affected == 1) + { + // Eolk - don't need this either. + //if (%targetObject.lastPilot.isPilot() == true) + //messageClient(%targetObject.lastPilot.client, "", "Vehicle Repaired, and Manuverability restored!"); // Eolk - Change from tagged string to regular string. + %targetObject.unmountImage(7); + %targetObject.Affected = 0; + if(%targetObject.getClassName() $= "FlyingVehicle") + Cancel(%targetObject.dmgStallLoop); + if(%targetObject.getdatablock().getname() $= "scoutFlyer" || %targetObject.getdatablock().getname() $= "strikeFlyer") + checkStallLoop(%obj); + } +//------- +} + +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(2400, %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(2400, 0, true); + %vehicle.schedule(2401, "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 = %data; + respawn = "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 = "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; + 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; + 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 = "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); +} + +function HoverVehicleData::switchSides(%data, %oldObj) +{ + %team = %oldObj.curTeam == 1 ? 2 : 1; + %oldObj.curTeam = %team; + %obj = new HoverVehicle() + { + dataBlock = %data; + respawn = "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; +} + +//------------------------------------------------------------------- +//START Dons +//------------------------------------------------------------------- + +datablock ShapeBaseImageData(BoatHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 5000; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(TankHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 1750; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(APCHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 3250; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(HFlyerHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 450; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(HHeliHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 250; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(LflyerHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 350; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(WCHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 350; + emap = true; + ismass = 1; +}; +datablock ShapeBaseImageData(MissileHDMassImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 10; + offset = "0 0 0"; + mass = 425; + emap = true; + ismass = 1; +}; + +$vehicleReticle[AssaultVehicle, 1, bitmap] = "gui/hud_ret_tankchaingun"; +$vehicleReticle[AssaultVehicle, 1, frame] = true; +$vehicleReticle[AssaultVehicle, 2, bitmap] = "gui/hud_ret_tankmortar"; +$vehicleReticle[AssaultVehicle, 2, frame] = true; + +$vehicleReticle[BomberFlyer, 1, bitmap] = "gui/hud_ret_shrike"; +$vehicleReticle[BomberFlyer, 1, frame] = false; +$vehicleReticle[BomberFlyer, 2, bitmap] = ""; +$vehicleReticle[BomberFlyer, 2, frame] = false; +$vehicleReticle[BomberFlyer, 3, bitmap] = "gui/hud_ret_targlaser"; +$vehicleReticle[BomberFlyer, 3, frame] = false; + +$vehicleReticle[boat, 1, bitmap] = "gui/ret_mortor"; +$vehicleReticle[boat, 1, frame] = false; + +$vehicleReticle[HeavyChopper, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[HeavyChopper, 1, frame] = false; + +$vehicleReticle[HeavyTank, 1, bitmap] = "gui/hud_ret_sniper"; +$vehicleReticle[HeavyTank, 1, frame] = false; +$vehicleReticle[HeavyTank, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[HeavyTank, 2, frame] = false; + +$vehicleReticle[helicopter, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[helicopter, 1, frame] = false; +$vehicleReticle[helicopter, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[helicopter, 2, frame] = false; + +$vehicleReticle[lightflyer, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[lightflyer, 1, frame] = false; + +$vehicleReticle[scoutflyer, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[scoutflyer, 1, frame] = false; +$vehicleReticle[scoutflyer, 2, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[scoutflyer, 2, frame] = false; +$vehicleReticle[scoutflyer, 3, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[scoutflyer, 3, frame] = false; + +$vehicleReticle[strikeflyer, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[strikeflyer, 1, frame] = false; +$vehicleReticle[strikeflyer, 2, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[strikeflyer, 2, frame] = false; +$vehicleReticle[strikeflyer, 3, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[strikeflyer, 3, frame] = false; + +$vehicleReticle[SuperiorityFighter, 1, bitmap] = "gui/ret_missile"; +$vehicleReticle[SuperiorityFighter, 1, frame] = false; +$vehicleReticle[SuperiorityFighter, 2, bitmap] = "gui/ret_missile"; +$vehicleReticle[SuperiorityFighter, 2, frame] = false; +$vehicleReticle[SuperiorityFighter, 3, bitmap] = ""; +$vehicleReticle[SuperiorityFighter, 3, frame] = false; +$vehicleReticle[scoutvehicle, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[scoutvehicle, 1, frame] = false; + +$vehicleReticle[FFTransport, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[FFTransport, 1, frame] = false; + +$vehicleReticle[Artillery, 1, bitmap] = "gui/ret_mortor"; +$vehicleReticle[Artillery, 1, frame] = false; + +$vehicleReticle[Sub, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[Sub, 1, frame] = false; + +$vehicleReticle[Gunship, 1, bitmap] = "gui/hud_ret_sniper"; +$vehicleReticle[Gunship, 1, frame] = false; +$vehicleReticle[Gunship, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[Gunship, 2, frame] = false; + +function vehicledmgStall(%obj){ + if(isobject(%obj)){ + %vec = %obj.getForwardVector(); + %vec = vectorNormalize(getword(%vec,0)@" "@getword(%vec,1)@" 0"); + %vec = vectorScale(%vec,33); + %impPos = vectoradd(%obj.getPosition(),%vec); + %obj.applyImpulse(%impPos,"0 0 -1"); + %obj.dmgStallLoop = schedule(100, 0, "vehicledmgStall", %obj); + } +} + +function checkStallLoop(%obj){ + if(!isObject(%obj)) + return; + %data = %obj.getDatablock(); + %vel = %obj.getVelocity(); + %spd = vectorLen(%vel); + %frdvec = %obj.getForwardVector(); + if(%spd < %data.maxAutoSpeed || vectorDist(vectorNormalize(%vel),%frdvec) > 0.5){ + %pos = %obj.getPosition(); + %searchresult = containerRayCast(%pos, vectorAdd(%pos, "0 0 -20"), $TypeMasks::TerrainObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType); + %searchObject = getWord(%searchResult, 0); + if(!isObject(%searchObject)){ + %plr = %obj.getMountNodeObject(0); + %vec = vectorNormalize(getword(%frdvec,0)@" "@getword(%frdvec,1)@" 0"); + %vec = vectorScale(%vec,33); + %impPos = vectoradd(%obj.getPosition(),%vec); + %obj.applyImpulse(%impPos,"0 0 -1"); + } + } + %obj.checkstallloop = schedule(100, 0, "checkStallLoop", %obj); +} + +function missileCheckAirTarget(%obj){ + if(!isObject(%obj)) + return; + + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType; + %pos = %obj.getWorldBoxCenter(); + %searchResult = containerRayCast(%pos,vectorAdd(%pos,"0 0 -15"), %mask, %obj); + if(%searchResult) + %result = false; + else + %result = true; + return %result; +} diff --git a/Scripts/Vehicles/vehicle_AWACS.cs b/Scripts/Vehicles/vehicle_AWACS.cs new file mode 100644 index 0000000..541aad1 --- /dev/null +++ b/Scripts/Vehicles/vehicle_AWACS.cs @@ -0,0 +1,772 @@ +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock SensorData(AWACSSensor) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 1750; +}; + +datablock FlyingVehicleData(AWACS) : BomberDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_bomber.dts"; + multipassenger = true; + computeCRC = true; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_bomber.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.2; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 3; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + isProtectedMountPoint[2] = false; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + cameraMaxDist = 22; + cameraOffset = 5; + cameraLag = 1.0; + explosion = MFVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 20.0; + + maxDamage = 5.0; // Total health + destroyedLevel = 5.0; // Damage textures show up at this health level + + HDAddMassLevel = 3.5; + HDMassImage = HFlyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 150; + maxEnergy = 400; // Afterburner and any energy weapon pool + minDrag = 60; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 1800; // Angular Drag (dampens the drift after you stop moving the mouse...also tumble drag) + rechargeRate = 0.8; + + // Auto stabilize speed + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 1500; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 300; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.95; // Dampen control input so you don't whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 8; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 5; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 8; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 5500; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 800; // Steering jets (force applied when you move the mouse) + steeringRollForce = 3000; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 4; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 130; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 5000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40.0; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 2.5; // Energy use of the afterburners (low number is less drain...can be fractional) + vertThrustMultiple = 3.0; + + dustEmitter = LargeVehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 2.0; + + damageEmitter[0] = MFLightDamageSmoke; + damageEmitter[1] = MFHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "3.0 -3.0 0.0 "; + damageEmitterOffset[1] = "-3.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + // Rigid body + mass = 350; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 20; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 25; + collDamageMultiplier = 0.020; + + // + minTrailSpeed = 15; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = BomberFlyerThrustSound; + engineSound = BomberFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 15.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterHardSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeHardSplashSound; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'C-410'; + targetTypeTag = 'AWACS'; + sensorData = AWACSSensor; + sensorRadius = AWACSSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 7.1895; + observeParameters = "1 10 10"; + shieldEffectScale = "0.75 0.975 0.375"; + showPilotInfo = 1; + + max[PlasmaAmmo] = 50; + + replaceTime = 20; +}; + +datablock BombProjectileData(BuoyBomb) +{ + projectileShapeName = "nexuscap.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.0; + damageRadius = 0; + radiusDamageType = $DamageType::BomberBombs; + kickBackStrength = 0; + + explosion = "ChaingunExplosion"; + velInheritFactor = 1.0; + + grenadeElasticity = 0.25; + grenadeFriction = 0.4; + armingDelayMS = 2000; + muzzleVelocity = 0.1; + drag = 0.3; + + minRotSpeed = "0.0 0.0 0.0"; + maxRotSpeed = "0.0 0.0 10.0"; + scale = "1.0 1.0 1.0"; + +// sound = BomberBombProjectileSound; +}; + +datablock TurretImageData(AWACSTorp) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 0 0"; + item = Chaingun; + projectile = shrikeBomb; + projectileType = BombProjectile; + emap = true; + + mountPoint = 1; + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + minEnergy = 5; + fireEnergy = 5.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4; + stateAllowImageChange[4] = false; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileReloadSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock TurretImageData(AWACSBuoy) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 0 0"; + item = Chaingun; + projectile = BuoyBomb; + projectileType = BombProjectile; + emap = true; + + mountPoint = 1; + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + minEnergy = 5; + fireEnergy = 5.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4; + stateAllowImageChange[4] = false; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileReloadSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock StaticShapeData(SonarBuoy) : StaticShapeDamageProfile { + className = "teleport"; + shapeFile = "nexuscap.dts"; + + maxDamage = 2.00; + destroyedLevel = 2.00; + disabledLevel = 1.35; + + isShielded = true; + energyPerDamagePoint = 250; + maxEnergy = 100; + rechargeRate = 1; + + explosion = ShapeExplosion; + expDmgRadius = 18.0; + expDamage = 0.3; + expImpulse = 200.0; + + dynamicType = $TypeMasks::StationObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSwitchIcon; + cmdMiniIconName = "commander/MiniIcons/com_switch_grey"; + targetNameTag = 'Deployed'; + targetTypeTag = 'Sonar Buoy'; + + debrisShapeName = "debris_generic.dts"; + debris = DeployableDebris; + + heatSignature = 0; + needsPower = false; + + humSound = SensorHumSound; + pausePowerThread = true; + sensorData = TelePadBaseSensorObj; + sensorRadius = TelePadBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + firstPersonOnly = true; +}; + + +datablock ShapeBaseImageData(Rearmer) +{ + mountPoint = 7; + className = WeaponImage; + shapeFile = "deploy_ammo.dts"; + offset = "0 -35 -8"; + rotation = "1 0 0 -120"; + emap = true; + + stateName[0] = "Activate"; + stateSequence[0] = "Deploy"; +}; + +function AWACS::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setInventory(PlasmaAmmo, 50); + + %turret = TurretData::create(BomberTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %turret.selectedWeapon = 1; + %obj.mountObject(%turret, 10); + %turret.mountImage(GST1Param, 0); + %turret.mountImage(APCTurretBarrel, 2); + %turret.mountImage(AWACSTorp, 4); + %turret.mountImage(AWACSBuoy, 6); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + %turret.mountobj = %obj; + %turret.team = %obj.team; + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function AWACS::playerMounted(%data, %obj, %player, %node) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) + { + // pilot position + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + bottomPrint(%player.client, "Pilot Postion: nade for flares and mine for resupply arm.", 5, 2 ); + } + 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); + } + %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; + centerPrint(%player.client, "WEP1: CG WEP2: Torp WEP3: sonar buoy dropper", 5, 2 ); + } + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function AWACS::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (isObject(%turret)){ + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + if (%client = %turret.getControllingClient()) + { + %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); + } +} + +function AWACS::onTrigger(%data, %obj, %trigger, %state) +{ + if (%trigger ==4){ + switch (%state) + { + case 0: + %obj.fireWeapon = false; + case 1: + if (%obj.inv[PlasmaAmmo] > 0){ + %obj.fireWeapon = true; + %obj.decInventory(PlasmaAmmo, 1); + %Upvec = %obj.getUpVector(); + + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = vectorScale(%Upvec, -1); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000, "delete"); + + %vec = vectornormalize(vectorcross(%obj.getForwardvector(),%Upvec)); + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = %vec; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000, "delete"); + + + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = vectorscale(%vec,-1); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000, "delete"); + + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } + } + } + else if (%trigger ==5){ + switch (%state) + { + case 1: + if(%obj.isreping == 1){ + stopRearm(%obj); + %obj.unmountImage(6); + } + else{ + %obj.isreping = 1; + RearmLoop(%obj); + %obj.mountImage(Rearmer, 6); + } + } + } +} + +function RearmLoop(%obj){ + if(isObject(%obj)){ + if(%obj.isreping == 0) + return; + + %targets = %obj.reptargets; + %vec = vectorAdd(vectorscale(%obj.getForwardVector(),-35),vectorscale(%obj.getUpVector(),-8)); + %pos = vectorAdd(%obj.getWorldBoxCenter(),%vec); + if(%targets !$= ""){ + %numtrgs = getNumberOfWords(%targets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%targets, %i); + if(vectorDist(%pos, %target.getWorldBoxCenter()) <= 20 && %target.getDamageLevel() > 0.0){ + if(%target.reping != 1){ + %target.reping = 1; + %target.setRepairRate(%target.getRepairRate() + 0.01); + } + } + else{ + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - 0.01); + } + } + %datablock = %target.getDatablock(); + if(%datablock.max[chaingunAmmo] !$= "" && %target.inv[chaingunAmmo] < %datablock.max[chaingunAmmo]){ + if(%datablock.max[chaingunAmmo] < 100) + %CGamount = 10; + else + %CGamount = 100; + %target.incInventory(chaingunAmmo, %CGamount); + %reloaded = 1; + } + if(%datablock.max[MissileLauncherAmmo] !$= "" && %target.inv[MissileLauncherAmmo] < %datablock.max[MissileLauncherAmmo]){ + %target.incInventory(MissileLauncherAmmo, 1); + %reloaded = 1; + } + if(%datablock.max[MortarAmmo] !$= "" && %target.inv[MortarAmmo] < %datablock.max[MortarAmmo]){ + %target.incInventory(MortarAmmo, 1); + %reloaded = 1; + } + if(%datablock.max[PlasmaAmmo] !$= "" && %target.inv[PlasmaAmmo] < %datablock.max[PlasmaAmmo]){ + %target.incInventory(PlasmaAmmo, 4); + %reloaded = 1; + } + if(%reloaded == 1){ + %reloaded = 0; + serverPlay3d("MissileReloadSound",%target.getWorldBoxCenter()); + } + + if(isObject(%target.turretObject)){ + %datablock = %target.turretObject.getDatablock(); + if(%datablock.max[chaingunAmmo] !$= "" && %target.turretObject.inv[chaingunAmmo] < %datablock.max[chaingunAmmo]){ + %target.turretObject.incInventory(chaingunAmmo, 100); + %reloaded = 1; + } + if(%datablock.max[MissileLauncherAmmo] !$= "" && %target.turretObject.inv[MissileLauncherAmmo] < %datablock.max[MissileLauncherAmmo]){ + %target.turretObject.incInventory(MissileLauncherAmmo, 1); + %reloaded = 1; + } + if(%datablock.max[MortarAmmo] !$= "" && %target.turretObject.inv[MortarAmmo] < %datablock.max[MortarAmmo]){ + %target.turretObject.incInventory(MortarAmmo, 1); + %reloaded = 1; + } + if(%reloaded == 1) + serverPlay3d("MissileReloadSound",%target.getWorldBoxCenter()); + } + } + } + %obj.reptargets = ""; + InitContainerRadiusSearch(%pos, 15, $TypeMasks::VehicleObjectType); + while ((%targetObject = containerSearchNext()) != 0){ +// if(%targetObject.getClassName() $= "FlyingVehicle") + %obj.reptargets = %obj.reptargets @ %targetObject @" "; + } + if(%obj.isreping == 1) + %obj.reploop = schedule(500, 0, "RearmLoop", %obj); + } +} + +function stopRearm(%obj){ + %obj.isreping = 0; + if(%obj.reptargets !$= ""){ + %numtrgs = getNumberOfWords(%obj.reptargets); + for(%i = 0; %i < %numtrgs; %i++){ + %target = getWord(%obj.reptargets, %i); + if(%target.reping == 1){ + %target.reping = 0; + %target.setRepairRate(%target.getRepairRate() - 0.01); + } + } + } +} + + +function AWACSTorp::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.initVel = vectorNormalize(%obj.mountobj.getVelocity()); + %z = getWord(%p.initVel,2); + if(%z > 0) + %p.initVel = vectorNormalize(getWord(%p.initVel,0)@" "@getWord(%p.initVel,1)@" 0"); + schedule(500, 0, "changethebomb", %p, "Torp"); +} + +function AWACSBuoy::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + schedule(500, 0, "changethebomb", %p, "Buoy"); +} + +function changethebomb(%obj, %type){ + if(!isObject(%obj)) + return; + %vec = "0 0 100"; + %pos = %obj.getPosition(); + %search = containerRayCast(%pos, vectorAdd(%pos,%vec), $TypeMasks::WaterObjectType, %obj); + if(%search){ + if(%type $= "Torp"){ + %p = new SeekerProjectile() + { + dataBlock = Torpedo; + initialDirection = %obj.initVel; + initialPosition = %pos; + sourceObject = %obj.sourceobject; + sourceSlot = 3; + }; + %p.sourceobject = %obj.sourceobject; + %p.vector = %vec; + %p.count = 1; + TorpedoSeekLoop(%p); + %obj.schedule(10, "delete"); + return; + } + else{ + %pos = getWord(%search,1)@" "@getWord(%search,2)@" "@getWord(%search,3); + if($TeamDeployedCount[%obj.sourceObject.team, SonarBuoy] < 5){ + %deplObj = new (StaticShape)() { + dataBlock = "SonarBuoy"; + position = %pos; + rotation = "0 0 0 0"; + scale = "1 1 1"; + team = %obj.sourceObject.team; + deployed = "1"; + }; + + %deplObj.setOwner(%obj.sourceObject.getControllingClient().player); + setTargetSensorGroup(%deplObj.getTarget(), %obj.sourceObject.team); + addToDeployGroup(%deplObj); + $TeamDeployedCount[%obj.sourceObject.team, SonarBuoy]++; + %deplObj.deploy(); + SonarPingLoop(%deplObj); + + %sensor = new (StaticShape) () { + datablock = "DeployedPulseSensor"; + position = vectorAdd(%pos, "0 0 0.5"); + rotation = "0 0 0 0"; + scale = "1 1 1"; + team = %obj.sourceObject.team; + deployed = "1"; + powerFreq = "0"; + }; + %sensor.setOwner(%obj.sourceObject.getControllingClient().player); + setTargetSensorGroup(%sensor.getTarget(), %obj.sourceObject.team); + addToDeployGroup(%sensor); + + %deplObj.sensor = %sensor; + } + else + BottomPrint(%obj.sourceObject.getControllingClient(), "Your team has reached the max of 5 Sonar Buoys.", 5); + %obj.schedule(10, "delete"); + return; + } + } + schedule(100, 0, "changethebomb", %obj, %type); +} + +function sonarPingLoop(%obj){ + if(!isObject(%obj)) + return; + InitContainerRadiusSearch(%obj.getPosition(), 300, $TypeMasks::VehicleObjectType); + while ((%SearchResult = containerSearchNext()) != 0){ + %SearchObj = FirstWord(%SearchResult); + if(%searchObj.getDataBlock().getName() $= "Sub" || %searchObj.getDataBlock().getName() $= "Boat"){ + if(isObject(%SearchObj.SnrBeacon[%obj.team])) + %SearchObj.SnrBeacon[%obj.team].setPosition(%searchObj.getWorldBoxCenter()); + else{ + %SearchObj.SnrBeacon[%obj.team] = new BeaconObject() { + dataBlock = "SubBeacon"; + beaconType = "vehicle"; + position = %SearchObj.getWorldBoxCenter(); + }; + %SearchObj.SnrBeacon[%obj.team].playThread($AmbientThread, "ambient"); + %SearchObj.SnrBeacon[%obj.team].team = %obj.team; + %SearchObj.SnrBeacon[%obj.team].sourceObject = %SearchObj; + + %SearchObj.SnrBeacon[%obj.team].setTarget(%obj.team); + MissionCleanup.add(%SearchObj.SnrBeacon[%obj.team]); + %SearchObj.SnrBeacon[%obj.team].schedule(9500, "delete"); + } + serverPlay3d("InboundTorpPing",%SearchObj.getWorldBoxCenter()); + } + } + %obj.sonarloop = schedule(2000, 0, "sonarPingLoop", %obj); +} + +function AWACS::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 SonarBuoy::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, SonarBuoy]--; +// remDSurface(%obj); + %obj.schedule(500, "delete"); + %obj.sensor.schedule(500, "delete"); + fireBallExplode(%obj,1); +} diff --git a/Scripts/Vehicles/vehicle_Artillery.cs b/Scripts/Vehicles/vehicle_Artillery.cs new file mode 100644 index 0000000..1790a2a --- /dev/null +++ b/Scripts/Vehicles/vehicle_Artillery.cs @@ -0,0 +1,705 @@ +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock ParticleData(CLSubExplosionSmoke) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = -0.5; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.5 0.5 0.5 1.0"; + colors[1] = "0.5 0.5 0.5 0.5"; + colors[2] = "0.6 0.6 0.6 0.0"; + sizes[0] = 1.0; + sizes[1] = 2.0; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(CLSubSmokeEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + + ejectionVelocity = 6.0; + velocityVariance = 6.0; + + thetaMin = 80.0; + thetaMax = 90.0; + + lifetimeMS = 750; + + particles = "CLSubExplosionSmoke"; +}; + +datablock ParticleData(CLSubSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 100; + textureName = "special/bigspark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 1.0; + sizes[1] = 1.0; + sizes[2] = 1.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(CLSubSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 15; + velocityVariance = 10.0; + + ejectionOffset = 0.0; + + thetaMin = 0; + thetaMax = 90; + + lifetimeMS = 500; + + particles = "CLSubSparks"; +}; + +//---------------------------------------------------- +// Explosion +//---------------------------------------------------- +datablock ExplosionData(ClusterSubExplosion) +{ + soundProfile = GrenadeExplosionSound; + + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + emitter[0] = CLSubSmokeEmitter; + emitter[1] = CLSubSparksEmitter; + + shakeCamera = true; + camShakeFreq = "10.0 6.0 9.0"; + camShakeAmp = "10.0 10.0 10.0"; + camShakeDuration = 1.0; + camShakeRadius = 10.0; +}; + +datablock WheeledVehicleData(Artillery) : TankDamageProfile +{ + spawnOffset = "0 0 1.0"; + renderWhenDestroyed = false; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_land_mpbase.dts"; + multipassenger = true; + computeCRC = true; + + debrisShapeName = "vehicle_land_mpbase.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 20.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + + cantAbandon = 1; + + cameraMaxDist = 20; + cameraOffset = 6; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 4.0; + explosionRadius = 20.0; + + maxSteeringAngle = 0.3; // 20 deg. + + // Rigid Body + mass = 2000; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 10; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 25; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 30; + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 30; + collDamageMultiplier = 0.070; + + // Engine + engineTorque = 10.0 * 745; + breakTorque = 6.0 * 745; + maxWheelSpeed = 15; + + // Springs + springForce = 10000; + springDamping = 1000; + antiSwayForce = 7500; + staticLoadScale = 2; + + // Tires + tireRadius = 2.5; + tireFriction = 50.0; + tireRestitution = 0.5; + tireLateralForce = 3000; + tireLateralDamping = 400; + tireLateralRelaxation = 1; + tireLongitudinalForce = 10000; + tireLongitudinalDamping = 400; + tireLongitudinalRelaxation = 1; + tireEmitter = TireEmitter; + + // + maxDamage = 5.5; + destroyedLevel = 5.5; + + HDAddMassLevel = 3.85; + HDMassImage = APCHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 600; + jetForce = 2800; + minJetEnergy = 60; + jetEnergyDrain = 0.1; + rechargeRate = 1.0; + + jetSound = MPBThrustSound; + engineSound = MPBEngineSound; + squeelSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 8.0; + hardSplashSoundVelocity = 12.0; + exitSplashSoundVelocity = 8.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 10; + + damageEmitter[0] = LightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "3.0 0.5 0.0 "; + damageEmitterOffset[1] = "-3.0 0.5 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundMPBIcon; + cmdMiniIconName = "commander/MiniIcons/com_mpb_grey"; + targetNameTag = 'Grendel'; + targetTypeTag = 'Heavy Artillery'; + sensorData = PlayerSensor; + + checkRadius = 7.5225; + + observeParameters = "1 12 12"; + + runningLight[0] = MPBLight1; + runningLight[1] = MPBLight2; + + shieldEffectScale = "0.85 1.2 0.7"; + + max[MortarAmmo] = 12; + + replaceTime = 60; +}; + +//--------------------------------------------- +// Weapon Stuff +//--------------------------------------------- + +datablock TurretData(ArtilleryTurret) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Artillery'; + targetTypeTag = 'Turret'; + mass = 1.0; // Not really relevant + + maxEnergy = 1000; + maxDamage = Artillery.maxDamage; + destroyedLevel = Artillery.destroyedLevel; + repairRate = 0; + + // capacitor + maxCapacitorEnergy = 100; + capacitorRechargeRate = 1.5; + + thetaMin = 30; + thetaMax = 100; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 1; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = 'APC CG'; + targetTypeTag = 'Turret'; + + max[MortarAmmo] = 12; +}; + +datablock GrenadeProjectileData(ArtilleryShot) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 3.5; + damageRadius = 25.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 4000; + + explosion = "artillerybarrelexplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = TankArtillerySmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.3; + armingDelayMS = -1; + gravityMod = 1.0; + muzzleVelocity = 225.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +datablock GrenadeProjectileData(ClusterSubProj) +{ + projectileShapeName = "grenade.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.5; + damageRadius = 12.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 2000; + + explosion = "ClusterSubExplosion"; + underwaterExplosion = "GrenadeExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + baseEmitter = TankArtillerySmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.0; + armingDelayMS = -1; + + gravityMod = 1.0; + muzzleVelocity = 225.0; + drag = 0.2; + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 4; + lightColor = "0.05 0.2 0.05"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "0.05 0.075 0.2"; +}; + +datablock TurretImageData(ArtilleryBarrel) +{ + className = WeaponImage; + shapeFile = "turret_tank_barrelmortar.dts"; + item = MissileLauncher; + ammo = MortarAmmo; + projectile = ArtilleryShot; + projectileType = GrenadeProjectile; + + offset = "0.5 0 0"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 1; + stateSequence[0] = "Activate"; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 2.0; + stateFire[2] = true; + stateEmitter[2] = "WhiteHorseHeatSeekerFireEffectEmitter"; + stateEmitterNode[2] = "muzzlepoint1"; + stateEmitterTime[2] = 1; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = AssaultMortarFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "AmmoLoading1"; + stateSequence[4] = "NoAmmo1"; + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 0.5; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 4.0; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "AmmoLoading2"; + stateSequence[9] = "NoAmmo2"; + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; + + stateName[11] = "AmmoLoading1"; + stateSound[11] = MissileReloadSound; + stateTimeoutValue[11] = 0.1; + stateAllowImageChange[11] = false; + stateTransitionOnTimeout[11] = "Reload1"; + + stateName[12] = "AmmoLoading2"; + stateSound[12] = MissileReloadSound; + stateTimeoutValue[12] = 0.1; + stateAllowImageChange[12] = false; + stateTransitionOnTimeout[12] = "Reload2"; +}; + +datablock TurretImageData(ArtilleryBarrel2) +{ + className = WeaponImage; + shapeFile = "turret_tank_barrelmortar.dts"; + item = MissileLauncher; + ammo = MortarAmmo; + projectile = ArtilleryShot; + projectileType = GrenadeProjectile; + + offset = "-0.5 0 0"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 2.0; + stateFire[1] = true; + stateEmitter[1] = "WhiteHorseHeatSeekerFireEffectEmitter"; + stateEmitterNode[1] = "muzzlepoint1"; + stateEmitterTime[1] = 1; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = AssaultMortarFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock TurretImageData(ArtilleryParam) +{ + mountPoint = 0; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 1.0 0.0"; + + projectile = ArtilleryShot; + projectileType = GrenadeProjectile; + + useCapacitor = false; + usesEnergy = true; + + // Turret parameters + activationMS = 500; + deactivateDelayMS = 550; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 225; +}; + +//--------------------------------------------- +// Vehicle.cs stuff +//--------------------------------------------- + +function Artillery::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + %obj.selweapontype = 1; + %obj.firing = 0; + + %turret = TurretData::create(ArtilleryTurret); + + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + + %obj.mountObject(%turret, 1); + %turret.mountImage(ArtilleryParam, 0); + %turret.mountImage(ArtilleryBarrel, 2); + %turret.mountImage(ArtilleryBarrel2, 3); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + + %obj.turretObject = %turret; + %turret.setAutoFire(false); + + %turret.setInventory(MortarAmmo, 10); + %turret.mountedto = %obj; + + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function Artillery::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(1); + if (!%turret) + return; + if (%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(1000, delete); + if(isObject(%obj.beacon)) + %obj.beacon.schedule(50, delete); +} + +function Artillery::playerMounted(%data, %obj, %player, %node) +{ + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + if(%obj.firing == 1){ + %turret = %obj.getMountNodeObject(1); + if (!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + } + } + commandToClient(%player.client, 'setHudMode', 'Pilot', "MPB", %node); + bottomPrint(%player.client, "Press Grenade button to switch to firing mode.", 5, 2 ); + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +//--------------------------------------------- +// Weapon Stuff +//--------------------------------------------- + +function Artillery::onTrigger(%data, %obj, %trigger, %state) +{ + %player = %obj.getMountNodeObject(0); + if(%trigger == 4) + { + switch (%state) + { + case 1: + %turret = %obj.getMountNodeObject(1); + if (!%player.client.isAIControlled()) + { + %player.setControlObject(%turret); + %player.client.setObjectActiveImage(%turret, 2); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", 1); + } + %obj.firing = 1; + bottomPrint(%player.client, "Will need to be reloaded with Artillery Reloader Pack.", 5, 2 ); + } + } +} + +function ArtilleryTurret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + case 2: + if(%state) + %obj.getDataBlock().playerDismount(%obj); + case 4: + if(%state){ + %client = %obj.getControllingClient(); + if (!%client.isAIControlled()) + %client.player.unmount(); + %obj.mountedto.firing = 0; + } + } +} + +function ArtilleryBarrel::firePair(%this, %obj, %slot){ + %obj.setImageTrigger( 3, true); +} + +function ArtilleryBarrel2::stopFire(%this, %obj, %slot){ + %obj.setImageTrigger( 3, false); +} + +function ArtilleryBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + if(%obj.mountedto.selweapontype == 2) + %p.cluster = schedule(1000, %p, "ARTdocluster", %p, %slot, %obj, 0); +} + +function ArtilleryBarrel2::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + if(%obj.mountedto.selweapontype == 2) + %p.cluster = schedule(1000, %p, "ARTdocluster", %p, %slot, %obj, 0); +} + +function ArtilleryTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %client = %obj.getControllingClient(); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function ARTdocluster(%proj,%slot,%obj,%pos2){ + %pos = %proj.getPosition(); + if(%pos2 != 0){ + %vec = vectorNormalize(vectorSub(%pos,%pos2)); + %newvec = vectorscale(%vec, 380); + %newvec = vectoradd(%pos,%newvec); + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType; + %searchresult = containerRayCast(%pos, %newvec, %mask); + if(%searchresult){ + for (%i=0; %i < 45; %i++) + { + %x = (getRandom() - 0.5) * 2 * 3.1415926 * 0.025; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * 0.013; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * 0.03; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vec); + %newvector = vectorScale(%newvector, (getRandom() + 0.5)); + + %p = new GrenadeProjectile() + { + dataBlock = ClusterSubProj; + initialDirection = %newvector; + initialPosition = %pos; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + } + %proj.delete(); + return; + } + } + %pos2 = %pos; + %proj.cluster = schedule(100, %proj, "ARTdocluster", %proj, %slot, %obj, %pos2); +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_CGM.cs b/Scripts/Vehicles/vehicle_CGM.cs new file mode 100644 index 0000000..aa9f726 --- /dev/null +++ b/Scripts/Vehicles/vehicle_CGM.cs @@ -0,0 +1,449 @@ +$missile::maxturnspeed = 220; +$missile::maxforwardspeed = 900; + +datablock TracerProjectileData(CGM_exp) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::MissileTurret; + explosion = "artillerybarrelexplosion"; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 5.5; + damageRadius = 45.0; + radiusDamageType = $DamageType::MissileTurret; + + kickBackStrength = 500; + sound = ChaingunProjectile; + + dryVelocity = 1.0; + wetVelocity = 1.0; + velInheritFactor = 1.0; + fizzleTimeMS = 32; + lifetimeMS = 33; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock FlyingVehicleData(CGmissile) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_grav_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + numMountPoints = 0; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 20.0; + + maxDamage = 0.5; + destroyedLevel = 0.5; + + HDAddMassLevel = 0.4; + HDMassImage = ShrikeHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 280; // Afterburner and any energy weapon pool + rechargeRate = 0.8; + + minDrag = 30; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 10000; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 200; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 0; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 1.0; // Dampen control input so you don't` whack out at very slow speeds + + + // Maneuvering + maxSteeringAngle = 4.5; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 5250; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 600; // Steering jets (force applied when you move the mouse) + steeringRollForce = 3000; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 1; // Height off the ground at rest + createHoverHeight = 1; // Height off the ground when created + maxForwardSpeed = 130; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 2500; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 28; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 0.1; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 1.25; + + // Rigid body + mass = 150; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 150; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerThrustSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MissileSmokeEmitter; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -0.3 0.0 "; + damageLevelTolerance[0] = 0.0; + damageLevelTolerance[1] = 0.75; + numDmgEmitterAreas = 1; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'TomaHawk'; + targetTypeTag = 'Cruise Missile'; + sensorData = PlayerSensor; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; + + shieldEffectScale = "0.937 1.125 0.60"; +}; + +datablock TurretData(CGMTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = CGmissile.maxDamage; + destroyedLevel = CGmissile.destroyedLevel; + + thetaMin = 1; + thetaMax = 180; + thetaNull = 90; + + // capacitor + maxCapacitorEnergy = 200; + capacitorRechargeRate = 5.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 1; + + targetNameTag = 'CGM'; + targetTypeTag = 'Turret'; +}; + +datablock TurretImageData(CGMTL) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 0 0"; + mountPoint = 1; + + projectile = GunshipTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + + stateName[1] = "Ready"; + stateTransitionOnTriggerDown[1] = "Fire"; + + stateName[2] = "Fire"; + stateEnergyDrain[2] = 0; + stateFire[2] = true; + stateScript[2] = "onFire"; + stateTransitionOnTriggerUp[2] = "Deconstruct"; + + stateName[3] = "Deconstruct"; + stateScript[3] = "onDecon"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Ready"; +}; + +datablock StaticShapeData(TMShape) : StaticShapeDamageProfile { + shapeFile = "weapon_missile_projectile.dts"; + mass = 1.0; + repairRate = 0; + dynamicType = $TypeMasks::StaticShapeObjectType; + heatSignature = 0; +}; + +function CGMTL::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.TL = %p; + CGMfollowloop(%obj.vehicleMounted); + schedule(1000, 0, 'CGMExplode', %obj.vehicleMounted); +} + +function CGMTL::onDecon(%data,%obj,%slot){ + %obj.TL.delete(); +} + +function CGmissile::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(CGMTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(AIAimingTurretBarrel, 0); + %turret.mountImage(CGMTL, 2); + %turret.setImageTrigger(2, true); + %obj.turretObject = %turret; + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + %obj.startFade(0,10,1); + %turret.startFade(10,20,1); + + %body = new StaticShape() + { + scale = "5 5 5"; + dataBlock = "TMShape"; + }; + MissionCleanup.add(%body); + %obj.mountObject(%body, 1); + %body.vehicleMounted = %obj; +} + +function startCGM(%p){ + if(!isObject(%p)) + return; + %data = "CGmissile"; + %pn = %data.create(0); + %pn.setTransform(%p.getPosition() SPC "1 0 0 -90"); + %pn.CGMsourceObject = %p.sourceObject; + %pn.CGMsourceSlot = %p.sourceSlot; + %pn.applyImpulse(%pn.getPosition(),vectorScale(%p.sourceObject.vehicleObject.getVelocity(),%data.mass)); + MissionCleanup.add(%pn); + %client = %p.sourceObject.getControllingClient(); + if (!%client.isAIControlled()) + { + %client.player.setControlObject(%pn.turretObject); + %client.player.client.setObjectActiveImage(%pn.turretObject, 2); + } + %p.delete(); +} + +function CGMfollowloop(%obj){ + if(!isObject(%obj)) + return; + if(!isObject(%obj.turretObject.TL)) + return; + %pos = %obj.getPosition(); + %targetpos = %obj.turretObject.TL.getTargetPoint(); + if(%targetpos $= "0 0 0 -1"){ + %Tpos = %obj.turretObject.getPosition(); + %Tvec = vectorScale(%obj.turretObject.getMuzzleVector(2),1500); + %targetpos = vectorAdd(%Tpos,%Tvec); + } + %frontvec = %obj.getForwardVector(); + %vector = vectorNormalize(vectorSub(%targetpos,%pos)); + %obj.applyImpulse(vectorAdd(%pos,%frontvec),vectorScale(%vector,$missile::maxturnspeed)); //make it turn toward Target + %obj.applyImpulse(%pos,vectorScale(%frontvec,$missile::maxforwardspeed)); //make it go forward + %obj.following = schedule(100, 0, "CGMfollowloop", %obj); +} + +function CGMExplode(%obj){ + if(!isObject(%obj)) + return; + InitContainerRadiusSearch(%obj.getWorldBoxCenter(), 15, $TypeMasks::VehicleObjectType); + while ((%SearchResult = containerSearchNext()) != 0) + { + if(%searchObj !$= %obj){ + %pn = new (TracerProjectile)() { + dataBlock = CGM_exp; + initialDirection = vectorNormalize(%obj.getVelocity()); + initialPosition = %obj.getWorldBoxCenter(); + sourceObject = %obj.CGMsourceObject; + sourceSlot = %obj.CGMsourceSlot; + }; + MissionCleanup.add(%pn); + return; + } + } + %obj.doexplodecheck = schedule(30, 0, "CGMExplode", %obj); +} + +function CGmissile::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (isObject(%turret)){ + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + if (%client = %turret.getControllingClient()) + { + if(isObject(%obj.CGMSourceObject)){ + %client.player.setControlObject(%obj.CGMSourceObject); + %obj.CGMSourceObject.selectedWeapon = 1; + commandToClient(%client,'SetWeaponryVehicleKeys', true); + commandToClient(%client, 'setHudMode', 'Pilot', "bomber", %node); + } else { + %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); + if(isObject(%turret.TL)) + %turret.TL.delete(); + + } + %body = %obj.getMountNodeObject(1); + if (isObject(%body)) + %body.schedule(2000, delete); + + %pn = new (TracerProjectile)() { + dataBlock = CGM_exp; + initialDirection = "0 0 1"; + initialPosition = %obj.getWorldBoxCenter(); + sourceObject = %obj.CGMsourceObject; + sourceSlot = %obj.CGMsourceSlot; + }; + MissionCleanup.add(%pn); +} + +function CGmissile::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_CGTank.cs b/Scripts/Vehicles/vehicle_CGTank.cs new file mode 100644 index 0000000..e05bc34 --- /dev/null +++ b/Scripts/Vehicles/vehicle_CGTank.cs @@ -0,0 +1,525 @@ +//************************************************************** +// Long Range Artillery +//************************************************************** + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(CGTank) : TankDamageProfile +{ + spawnOffset = "0 0 4"; + + floatingGravMag = 4.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_tank.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_grav_tank.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = false; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 25.0; + + maxSteeringAngle = 0.5; // 20 deg. + + maxDamage = 3.2; + destroyedLevel = 3.2; + + HDAddMassLevel = 2.24; + HDMassImage = TankHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 300; + maxEnergy = 400; + minJetEnergy = 15; + jetEnergyDrain = 2.0; + + // Rigid Body + mass = 4000; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 18; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 17; + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 18; + collDamageMultiplier = 0.045; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.15; + + mainThrustForce = 40; + reverseThrustForce = 25; + strafeThrustForce = 25; + turboFactor = 1.7; + + brakingForce = 25; + brakingActivationSpeed = 4; + + stabLenMin = 3.25; + stabLenMax = 4; + stabSpringConstant = 50; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 10; + steeringForce = 15; + rollForce = 5; + pitchForce = 3; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 7; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Bannshe 50mm'; + targetTypeTag = 'chaingun tank'; + sensorData = PlayerSensor; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + replaceTime = 30; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock TracerProjectileData(BigBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.4; + directDamageType = $DamageType::TankChaingun; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + + kickBackStrength = 2000.0; + sound = ChaingunProjectile; + + dryVelocity = 4000.0; // z0dd - ZOD, 8-12-02. Was 1000.0 + wetVelocity = 1600.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 25.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.25; + crossSize = 0.25; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; +}; + +//------------------------------------- +// Artillery CHAINGUN CHARACTERISTICS +//------------------------------------- + +datablock TurretImageData(CGTurretParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + + projectile = BigBullet; + projectileType = TracerProjectile; + + useCapacitor = true; + usesEnergy = true; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 750; +}; + +datablock TurretData(CGTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "Turret_tank_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Bannshe'; + targetTypeTag = '50mm chaingun turret'; + mass = 1.0; + + maxEnergy = 1; + maxDamage = CGTank.maxDamage; + destroyedLevel = CGTank.destroyedLevel; + repairRate = 0; + + // capacitor + maxCapacitorEnergy = 1000; + capacitorRechargeRate = 10.0; + + thetaMin = 0; + thetaMax = 100; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = '50mm chaingun'; + targetTypeTag = 'Turret'; +}; + +datablock TurretImageData(CGTurretBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + mountPoint = 1; + + projectile = BigBullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 1.5 / 1000.0; + + useCapacitor = true; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 0.75; + minEnergy = 1.0; + + // Turret parameters + activationMS = 2000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 0.1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 0.1; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TurretImageData(CGTurretBarrel2) : CGTurretBarrel +{ + mountPoint = 0; +}; + +function CGTank::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(CGTurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(CGTurretBarrel, 2); + %turret.mountImage(CGTurretBarrel2, 3); + %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(CGTurretParam, 0); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + + // set the turret's target info + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function CGTank::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%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); +} + +function CGTank::playerMounted(%data, %obj, %player, %node) +{ + if (%node == 0) + { + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + else if (%node == 1) + { + %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; + $aWeaponActive = 0; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); +} + + +function CGTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function CGTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function CGTurret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + } + case 2: + if(%state) + %obj.getDataBlock().playerDismount(%obj); + } +} + +function CGTurret::playerDismount(%data, %obj) +{ + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %client = %obj.getControllingClient(); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function CGTank::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_DropPod.cs b/Scripts/Vehicles/vehicle_DropPod.cs new file mode 100644 index 0000000..e00e6d5 --- /dev/null +++ b/Scripts/Vehicles/vehicle_DropPod.cs @@ -0,0 +1,410 @@ +//************************************************************** +// Drop Pod +//************************************************************** + +datablock AudioProfile(DropPodThrustSound) +{ + filename = "fx/vehicles/shrike_boost.wav"; + description = AudioDefault3d; + preload = true; + effect = ScoutFlyerThrustEffect; +}; + +datablock WheeledVehicleData(DropPod) : MPBDamageProfile +{ + spawnOffset = "0 0 1.0"; + renderWhenDestroyed = false; + canControl = true; + catagory = "Vehicles"; + shapeFile = "stackable2l.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "stackable2l.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 20.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + + cameraMaxDist = 20; + cameraOffset = 6; + cameraLag = 1.5; + explosionDamage = 0.0; + explosionRadius = 0.0; + + maxSteeringAngle = 0.3; // 20 deg. + + // Used to test if the station can deploy + stationPoints[1] = "-2.3 -7.38703 -0.65"; + stationPoints[2] = "-2.3 -11.8 -0.65"; + stationPoints[3] = "0 -7.38703 -0.65"; + stationPoints[4] = "0 -11.8 -0.65"; + stationPoints[5] = "2.3 -7.38703 -0.65"; + stationPoints[6] = "2.3 -11.8 -0.65"; + + // Rigid Body + mass = 1000; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 10; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 25; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 24; + speedDamageScale = 0.025; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 15; + collDamageMultiplier = 0.03; + + // Engine + engineTorque = 0 * 745; + breakTorque = 0 * 745; + maxWheelSpeed = 10; + + // Springs + springForce = 8000; + springDamping = 1300; + antiSwayForce = 6000; + staticLoadScale = 2; + + // Tires + tireRadius = 1.6; + tireFriction = 10.0; + tireRestitution = 0.5; + tireLateralForce = 3000; + tireLateralDamping = 400; + tireLateralRelaxation = 1; + tireLongitudinalForce = 12000; + tireLongitudinalDamping = 600; + tireLongitudinalRelaxation = 1; + tireEmitter = TireEmitter; + + // + maxDamage = 2.0; + destroyedLevel = 2.0; + + HDAddMassLevel = 1.5; + HDMassImage = WCHDMassImage; + + isShielded = false; + energyPerDamagePoint = 125; + maxEnergy = 600; + jetForce = 0; + minJetEnergy = 60; + jetEnergyDrain = 0; + rechargeRate = 1.0; + + jetSound = MPBThrustSound; + engineSound = MPBEngineSound; + squeelSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 8.0; + hardSplashSoundVelocity = 12.0; + exitSplashSoundVelocity = 8.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 3; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = SmallHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "3.0 0.5 0.0 "; + damageEmitterOffset[1] = "-3.0 0.5 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundMPBIcon; + cmdMiniIconName = "commander/MiniIcons/com_mpb_grey"; + targetNameTag = 'Drop'; + targetTypeTag = 'Pod'; + sensorData = VehiclePulseSensor; + + checkRadius = 7.5225; + + observeParameters = "1 12 12"; + + runningLight[0] = MPBLight1; + runningLight[1] = MPBLight2; + + shieldEffectScale = "0.85 1.2 0.7"; + + replaceTime = 1; +}; + +datablock ParticleData(DropPodParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.02; + inheritedVelFactor = 1.0; + + lifetimeMS = 4000; + lifetimeVarianceMS = 500; + + textureName = "special/Smoke/bigSmoke"; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + + colors[0] = "0.8 0.4 0.1 0.5"; + colors[1] = "0.6 0.2 0.2 0.4"; + colors[2] = "0.4 0.4 0.4 0.0"; + colors[3] = "0.4 0.4 0.4 0.3"; + colors[4] = "0.5 0.5 0.5 0.3"; + colors[5] = "0.6 0.6 0.6 0.0"; + sizes[0] = 1; + sizes[1] = 1.75; + sizes[2] = 0; + sizes[3] = 5; + sizes[4] = 6; + sizes[5] = 8; + times[0] = 0.0; + times[1] = 0.05; + times[2] = 0.1; + times[3] = 0.15; + times[4] = 0.7; + times[5] = 1.0; +}; + +datablock ParticleEmitterData(DropPodEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 35; + velocityVariance = 10; + + thetaMin = 175.0; + thetaMax = 180.0; + + particles = "DropPodParticle"; +}; + +function MakeDropPod(%pos, %team){ + %pod = new WheeledVehicle() + { + dataBlock = DropPod; + position = %pos; + rotation = "0 0 1 0"; + team = %team; + }; + MissionCleanUp.add(%pod); + return %pod; +} + +function DropPod::playerMounted(%data, %obj, %player, %node) +{ + centerPrint(%player.client, "WARNING: Do not eject out of the drop pod while it is in flight. \nUse /SetCoord and /LaunchPod to launch the Drop Pod to a desired location.", 9, 3 ); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function DropPod::deleteAllMounted(%data, %obj) +{ + if(%veh.beacon){ + %veh.beacon.delete(); + %veh.beacon = ""; + } +} + +function ccSetCoord(%sender, %args){ + if(!isObject(%sender.player)) + return; + %plyr = %sender.player; + if(%args $= ""){ + messageclient(%sender, 'MsgClient', "\c2Coordinates must be specified."); + } + if(%plyr.mountedToV){ + %veh = %plyr.VmountedTo; + if(%veh.getDataBlock().getName() $= "DropPod"){ + %veh.DPcoord = getWord(%args, 0)@" "@getWord(%args, 1); + messageclient(%sender, 'MsgClient', "\c2Drop Pod coordinates set to\c3 "@%veh.DPcoord@""); + + + %search = containerRayCast(%veh.DPcoord@" 10000",%veh.DPcoord@" -100",$TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, %veh); + %pos = getWords(%search,1,3); + echo(%pos); + if(%veh.beacon) + { + %veh.beacon.delete(); + %veh.beacon = ""; + } + %veh.beacon = new BeaconObject() { + dataBlock = "TargeterBeacon"; + beaconType = "vehicle"; + position = %pos; + }; + + %veh.beacon.playThread($AmbientThread, "ambient"); + %veh.beacon.team = %plyr.team; + %veh.beacon.sourceObject = %veh; + + // give it a team target + %veh.beacon.setTarget(%plyr.team); + MissionCleanup.add(%veh.beacon); + + %sender.camera.setOrbitMode(%veh.beacon, %pos, 0.5, 10, 10); + %sender.camera.targetObj = %veh.beacon; + %sender.setControlObject( %sender.camera ); + %sender.schedule(4000, "setControlObject", %plyr); + } + else + messageclient(%sender, 'MsgClient', '\c2You are not in a Drop Pod.'); + } + else + messageclient(%sender, 'MsgClient', '\c2You are not in a Drop Pod.'); +} + +function ccLaunchPod(%sender, %args){ + if(!isObject(%sender.player)) + return; + %plyr = %sender.player; + if(%plyr.mountedToV){ + %veh = %plyr.VmountedTo; + if(%veh.getDataBlock().getName() $= "DropPod"){ + if(%veh.DPcoord !$= "") + launchDropPod(%veh); + else + messageclient(%sender, 'MsgClient', '\c2Please set the coordinates at which you wish to land with\c3 /SetCoord\c2.'); + } + else + messageclient(%sender, 'MsgClient', '\c2You are not in a Drop Pod.'); + } + else + messageclient(%sender, 'MsgClient', '\c2You are not in a Drop Pod.'); +} + +function launchDropPod(%obj){ + if(!isObject(%obj)) + return; + %vel = %obj.getVelocity(); + if(vectorLen(%vel) < 200){ + %obj.applyImpulse(%obj.getPosition(),vectorScale("0 0 1",(3.5 * %obj.getDatablock().mass))); + %charge = new ParticleEmissionDummy() + { + position = %obj.getPosition(); + rotation = %obj.getRotation(); + dataBlock = "defaultEmissionDummy"; + emitter = "DropPodEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(100, "delete"); + serverPlay3d("DropPodThrustSound",%obj.getPosition()); + } + %z = getWord(%obj.getPosition(),2); + if(%z >= 10000){ + %dist = vectorDist(%obj.DPcoord@" "@%z,%obj.getPosition()); + %obj.setTransform(%obj.DPcoord@" "@%z@" 0 0 1 0"); + %obj.setFrozenState(true); + %obj.schedule((%dist / 100) * 500,"setFrozenState",false); + schedule((%dist / 100) * 500, 0, "slowDropPod", %obj); + return; + } + if(vectorDist(%obj.getUpVector(),"0 0 1") >= 0.1){ + %upvec = %obj.getUpVector(); + %vec = vectorSub("0 0 1",%upvec); + %vec = vectorCross(%vec,%upvec); + %vec = vectorNormalize(vectorCross(%upvec,%vec)); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%upvec,(0.1 * %obj.getDatablock().mass))); + %obj.applyImpulse(%pos,%vec); + } + schedule(100, 0, "launchDropPod", %obj); +} + +function slowDropPod(%obj){ + if(!isObject(%obj)) + return; + %vel = %obj.getVelocity(); + if(%obj.slwspeed $= ""){ + %Aresult = containerRayCast(%obj.getPosition(), vectorAdd(%obj.getPosition(),"0 0 -10000"), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, 0); + %Avec = vectorSub(getWord(%Aresult,1)@" "@getWord(%Aresult,2)@" "@getWord(%Aresult,3),%obj.getPosition()); + %obj.slwspeed = 3.0 + (mSqrt(vectorLen(%Avec)) / 18); + } + if(%obj.slowingdown != 1){ + %result = containerRayCast(%obj.getPosition(), vectorAdd(%obj.getPosition(),vectorScale(%vel,3)), $TypeMasks::StaticShapeObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::TerrainObjectType, 0); + if(%result) + %obj.slowingdown = 1; + } + if(%obj.slowingdown == 1){ + if(vectorLen(%vel) > 20){ + %obj.applyImpulse(%obj.getPosition(),vectorScale("0 0 1",(%obj.slwspeed * %obj.getDatablock().mass))); + %charge = new ParticleEmissionDummy() + { + position = %obj.getPosition(); + rotation = %obj.getRotation(); + dataBlock = "defaultEmissionDummy"; + emitter = "DropPodEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(100, "delete"); + serverPlay3d("DropPodThrustSound",%obj.getPosition()); + } + else if(getWord(%vel,2) > -5){ + DropPodDismount(%obj); + return; + } + } + if(vectorDist(%obj.getUpVector(),"0 0 1") >= 0.1){ + %upvec = %obj.getUpVector(); + %vec = vectorSub("0 0 1",%upvec); + %vec = vectorCross(%vec,%upvec); + %vec = vectorNormalize(vectorCross(%upvec,%vec)); + %pos = vectorAdd(%obj.getPosition(),vectorScale(%upvec,(0.1 * %obj.getDatablock().mass))); + %obj.applyImpulse(%pos,%vec); + } + schedule(100, 0, "slowDropPod", %obj); +} + +function DropPodDismount(%obj){ + if(!isObject(%obj)) + return; + if(isObject(%obj.getMountNodeObject(0))){ + %passenger = %obj.getMountNodeObject(0); + %passenger.unmount(); + %pos = %obj.getPosition(); + for(%i = 0; %i < 3; %i++){ + %x = getWord(%pos, 0) + getRandom(3,8); + %y = getWord(%pos, 1) + getRandom(3,8); + %z = getWord(%pos, 2) + 10; + %startpos = %x@" "@%y@" "@%z; + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType; + %searchResult = containerRayCast(%startpos, vectorAdd(%startpos, "0 0 -20"), %mask, %leader.player); + if(%searchResult){ + %trns = getWord(%searchResult, 1)@" "@getWord(%searchResult, 2)@" "@getWord(%searchResult, 3); + %i = 3; + } + } + if(%trns $= "") + %trns = %pos; + %passenger.setPosition(%trns); +// commandToClient(%passenger.client, 'setHudMode', 'Standard', "", 0); + schedule(100, 0, "commandToClient", %passenger.client, 'setHudMode', 'Standard', "", 0); + } + %obj.schedule(1000,"delete"); +} diff --git a/Scripts/Vehicles/vehicle_HeavyHelicopter.cs b/Scripts/Vehicles/vehicle_HeavyHelicopter.cs new file mode 100644 index 0000000..133cb79 --- /dev/null +++ b/Scripts/Vehicles/vehicle_HeavyHelicopter.cs @@ -0,0 +1,630 @@ +//************************************************************** +// Chopper +//************************************************************** + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(heavychopper) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_bomber.dts"; + multipassenger = true; + computeCRC = true; + + debrisShapeName = "vehicle_air_bomber.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.3; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + mountPose[3] = sitting; + mountPose[4] = sitting; + mountPose[5] = sitting; + numMountPoints = 6; + isProtectedMountPoint[0] = false; + isProtectedMountPoint[1] = false; + isProtectedMountPoint[2] = false; + isProtectedMountPoint[3] = true; + isProtectedMountPoint[4] = true; + isProtectedMountPoint[5] = true; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MFVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 10.0; + + maxDamage = 5.0; + destroyedLevel = 5.0; + + HDAddMassLevel = 3.5; + HDMassImage = HHeliHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 150; // Afterburner and any energy weapon pool + rechargeRate = 1.0; + + minDrag = 20; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 550; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 150; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 1.0; // Dampen control input so you don't` whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 7; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 2; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 0; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 425; // Steering jets (force applied when you move the mouse) + steeringRollForce = 30; // Steering jets (how much you heel over when you turn) + rollForce = 6; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 3; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 30; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 1000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 1; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 0.1; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 3.5; + + // Rigid body + mass = 250; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 15; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.08; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 100; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MFLightDamageSmoke; + damageEmitter[1] = MFHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'Eagle VII'; + targetTypeTag = 'Transport Chopper'; + sensorData = PlayerSensor; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + + max[chaingunAmmo] = 1000; + max[PlasmaAmmo] = 20; + + replaceTime = 50; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock ShapeBaseImageData(ChopperCGPairImage) +{ + className = WeaponImage; + shapeFile = "turret_missile_large.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Heli_CG_Bullet; + projectileType = TracerProjectile; + mountPoint = 10; + offset = "4.93 -4.52 0.044"; // L/R - F/B - T/B + + projectileSpread = 1.0 / 1000.0; + + usesEnergy = false; + emap = true; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.05; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateSound[3] = ShrikeBlasterFire; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.05; + stateAllowImageChange[4] = false; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(ChopperCGImage) : ChopperCGPairImage +{ + offset = "-4.93 -4.52 0.044"; +}; + +datablock TracerProjectileData(HheliT_bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.2; // z0dd - ZOD, 9-27-02. Was 0.0825 + directDamageType = $DamageType::Bullet; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + + kickBackStrength = 2000.0; + sound = ChaingunProjectile; + + dryVelocity = 3000.0; // z0dd - ZOD, 8-12-02. Was 425.0 + wetVelocity = 2500.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 2500; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 10.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.06; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +datablock ShapeBaseImageData(HHeliChaingunImage) +{ + className = WeaponImage; + shapeFile = "turret_tank_barrelchain.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = HheliT_bullet; + projectileType = TracerProjectile; + projectileSpread = 1.0 / 1000.0; + emap = true; + offset = "0.37 -0.4 -0.5"; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 5.0; + shellVelocity = 0.0; + + mountPoint = 1; + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + minEnergy = 8; + fireEnergy = 8; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; +// stateSound[0] = GravChaingunIdleSound; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + stateName[3] = "Spinup"; + stateScript[3] = "onSpinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ChaingunSpinupSound; + stateTimeoutValue[3] = 0.5; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = HeliturretFireSound; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.05; + stateTransitionOnTimeout[4] = "Fire"; + + stateName[5] = "Spindown"; + stateScript[5] = "onSpindown"; + stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.5; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + stateName[6] = "EmptySpindown"; + stateScript[6] = "onSpindown"; + stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + stateTimeoutValue[6] = 2.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(HHeliChaingunImage2) : HHeliChaingunImage +{ + offset = "-0.23 -0.4 -0.5"; +}; + +datablock ShapeBaseImageData(ChopperParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 5 0"; // L/R - F/B - T/B + + projectile = BigBullet; + projectileType = TracerProjectile; +}; + +function HeavyChopper::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastPilot.lastVehicle == %obj) + if(%obj.getMountNodeObject(0) == %obj.lastPilot) + schedule(200, %obj.lastPilot, "scKillPilot", %obj.lastPilot, %obj.lastDamagedBy); + + Parent::onDestroyed(%data, %obj, %prevState); +} + +datablock ShapeBaseImageData(DummyslotImg) +{ + shapeFile = "grenade.dts"; + emap = false; +}; + +//****************************************************** +// vehicle.cs and weapturretcode.cs functions +//****************************************************** + +//---------------------------- +// Helicopter FLIER +//---------------------------- + +function HeavyChopper::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(ChopperParam, 0); + %obj.mountImage(ChopperCGImage, 2); + %obj.mountImage(ChopperCGPairImage, 3); + %obj.mountImage(DummySlotImg, 4); + %obj.selectedWeapon = 1; + %obj.nextWeaponFire = 2; + %obj.setInventory(chaingunammo, 1000); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(HeliTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(heliTurretParam, 0); + %turret.mountImage(Hhelichaingunimage, 2); + %turret.mountImage(Hhelichaingunimage2, 3); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + %turret.mountImage(AIAimingTurretBarrel, 0); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + %obj.canSwitchP = 1; +} + +function HeavyChopper::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if (%client = %turret.getControllingClient()) + { + %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(1000, delete); +} + +function HeavyChopper::playerMounted(%data, %obj, %player, %node) +{ + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + bottomPrint(%player.client, "Transport Chopper: carrys 6 peeps has 2 forward mounted cg's controlled by pilot and cg turret on bottom", 5, 2 ); + + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) + { + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + } + else if (%node == 1) + { + %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.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 + { + // 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 ") + %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 ); +} + +function HeavyChopper::onTrigger(%data, %obj, %trigger, %state) +{ + // data = ScoutFlyer datablock + // obj = ScoutFlyer object number + // trigger = 0 for "fire", 1 for "jump", 3 for "thrust" + // state = 1 for firing, 0 for not firing + if(%trigger == 0) + { + switch (%state) { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + case 1: + %obj.fireWeapon = true; + if(%obj.selectedWeapon == 1) + { + if(%obj.nextWeaponFire == 2) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + } +// else +// { +// %obj.setImageTrigger(2, false); +// %obj.setImageTrigger(3, true); +// } + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + } + } + } + else if (%trigger ==4) // Throw flare + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(5, false); + case 1: + if (%obj.inv[PlasmaAmmo] > 0) + { + %obj.fireWeapon = true; + %obj.setImageTrigger(5, true); + %obj.decInventory(PlasmaAmmo, 1); + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = "0 0 -1"; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + + FlareSet.add(%p); + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(6000, "delete"); + // miscellaneous grenade-throwing cleanup stuff + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } + } + } +} + +function HeavyChopper::playerDismounted(%data, %obj, %player) +{ + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function ChopperCGImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); +} + +function ChopperCGPairImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); +} + +function HHeliChaingunImage::OnSpinup(%data,%obj,%slot){ + %obj.setImageTrigger(3,true); +} +function HHeliChaingunImage::OnSpindown(%data,%obj,%slot){ + %obj.setImageTrigger(3,false); +} + +function heavychopper::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_HeavyTank.cs b/Scripts/Vehicles/vehicle_HeavyTank.cs new file mode 100644 index 0000000..e265109 --- /dev/null +++ b/Scripts/Vehicles/vehicle_HeavyTank.cs @@ -0,0 +1,1078 @@ +//************************************************************** +// JERICHO FORWARD BASE (Mobile Point Base) +//************************************************************** + +datablock ParticleData(FlakDust) +{ + 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.3 0.3 0.3 0.5"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 6.6; + sizes[1] = 10.8; + sizes[2] = 12.0; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(FlakDustEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 20.0; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 88; + thetaMax = 92; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "FlakDust"; +}; + + +datablock ParticleData(FlakExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.5; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.7 0.7 0.7 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.1 0.1 0.1 0.0"; + sizes[0] = 4.0; + sizes[1] = 13.0; + sizes[2] = 4.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FExplosionSmokeEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + + ejectionVelocity = 6.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "FlakExplosionSmoke"; +}; + +datablock ParticleData(FlakSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 650; + textureName = "special/bigspark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 3.0; + sizes[1] = 3.0; + sizes[2] = 4.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FlakSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 15; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "FlakSparks"; +}; + +datablock ParticleData(FlakFlashSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 250; + lifetimeVarianceMS = 650; + textureName = "special/bigspark"; + colors[0] = "1.0 0.2 0.2 1.0"; + colors[1] = "0.7 0.7 0.2 1.0"; + colors[2] = "0.5 0.5 0.1 0.0"; + sizes[0] = 4.0; + sizes[1] = 5.0; + sizes[2] = 6.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FlakFlashSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 25; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "FlakFlashSparks"; +}; + +//---------------------------------------------------- +// Explosion +//---------------------------------------------------- +datablock ExplosionData(FlakExplosion) +{ + soundProfile = GrenadeExplosionSound; + + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + emitter[0] = FlakDustEmitter; + emitter[1] = FExplosionSmokeEmitter; + emitter[2] = FlakSparksEmitter; + emitter[3] = FlakFlashSparksEmitter; + + shakeCamera = true; + camShakeFreq = "10.0 6.0 9.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 20.0; +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(HeavyTank) : TankDamageProfile +{ + spawnOffset = "0 0 4"; + canControl = false; + floatingGravMag = 4.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_tank.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_grav_tank.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 2.0; + explosionRadius = 25.0; + + maxSteeringAngle = 0.35; // 20 deg. + + maxDamage = 4.0; + destroyedLevel = 4.0; + + HDAddMassLevel = 3.2; + HDMassImage = TankHDMassImage; + + isShielded = false; + rechargeRate = 1.5; + energyPerDamagePoint = 0; + maxEnergy = 1000; + minJetEnergy = 60; + jetEnergyDrain = 2.75; + + // Rigid Body + mass = 1500; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 18; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 35; + speedDamageScale = 0.017; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 20; + collDamageMultiplier = 0.030; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.15; + + mainThrustForce = 70; + reverseThrustForce = 30; + strafeThrustForce = 5; + turboFactor = 1.25; + + brakingForce = 30; + brakingActivationSpeed = 4; + + stabLenMin = 3.25; + stabLenMax = 4; + stabSpringConstant = 50; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 10; + steeringForce = 25; + rollForce = 7; + pitchForce = 3; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.5; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 7; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.4; + damageLevelTolerance[1] = 0.8; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'M3A2 Faustes'; + targetTypeTag = 'Assault Tank'; + sensorData = PlayerSensor; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + max[PlasmaAmmo] = 4; + + replaceTime = 60; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +//------------------------------------- +// ASSAULT CHAINGUN CHARACTERISTICS +//------------------------------------- + +datablock TurretData(TankTurret) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "Turret_tank_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Panzer'; + targetTypeTag = 'Tank turret'; + mass = 1.0; // Not really relevant + + maxEnergy = 1; + maxDamage = 1.5; + destroyedLevel = 1.5; + repairRate = 0; + + // capacitor + maxCapacitorEnergy = 5000; + capacitorRechargeRate = 1.5; + + thetaMin = 0; + thetaMax = 100; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = 'Panzer'; + targetTypeTag = 'tank Turret'; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + repairRate = 0.0; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(TankFlak_Bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.2; + directDamageType = $DamageType::TankChaingun; + explosion = FlakExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.3; + damageRadius = 25.0; + radiusDamageType = $DamageType::Flak; + + kickBackStrength = 15; + sound = ChaingunProjectile; + + dryVelocity = 600.0; + wetVelocity = 200.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 1000; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TracerProjectileData(TankFlak_Bullet_exp) +{ + doDynamicClientHits = true; + + directDamage = 0.2; + directDamageType = $DamageType::TankChaingun; + explosion = FlakExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.3; + damageRadius = 25.0; + radiusDamageType = $DamageType::Flak; + + kickBackStrength = 150; + sound = ChaingunProjectile; + + dryVelocity = 0.0; + wetVelocity = 0.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 10; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 0.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TurretImageData(TankMGTurretBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + mountPoint = 1; + + projectile = TankFlak_Bullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 1.0 / 1000.0; + + useCapacitor = true; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 10.0; + minEnergy = 10.0; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.75; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.45; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 30; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; + +}; + +datablock ParticleEmitterData( TankArtillerySmokeEmitter ) +{ + ejectionPeriodMS = 50; + periodVarianceMS = 3; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "GDebrisSmokeParticle"; +}; + + +datablock GrenadeProjectileData(TankArtillery) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 2.0; + damageRadius = 25.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 3000; + + explosion = "artillerybarrelexplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = TankArtillerySmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.3; + armingDelayMS = -1; + gravityMod = 1.0; + muzzleVelocity = 175.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +datablock GrenadeProjectileData(TankATShell) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 4.0; + damageRadius = 4.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 3000; + + explosion = "MissileExplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = MissileFireEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.3; + armingDelayMS = -1; + gravityMod = 1.0; + muzzleVelocity = 400.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +//------------------------------------- +// ASSAULT MORTAR CHARACTERISTICS +//------------------------------------- + +datablock TurretImageData(TankATurretBarrel) +{ + shapeFile = "turret_tank_barrelmortar.dts"; + mountPoint = 0; + +// ammo = AssaultMortarAmmo; + projectile = TankArtillery; + projectile2 = TankATShell; + projectileType = GrenadeProjectile; + + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 100.00; + minEnergy = 100.00; + useCapacitor = true; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 2.5; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultMortarFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 2.5; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateSound[4] = MobileBaseStationDeploySound; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateDirection[5] = false; + stateSequence[5] = "Activate"; + stateTimeoutValue[5] = 1.0; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultMortarDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TracerProjectileData(Coax_bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.3; + directDamageType = $DamageType::MG; + explosion = ChaingunExplosion; + splash = ChaingunSplash; + + hasDamageRadius = false; + + kickBackStrength = 0; + sound = ChaingunProjectile; + + dryVelocity = 5000.0; + wetVelocity = 3000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 0.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.15; + crossSize = 0.3; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TurretImageData(TankCoaxBarrel) +{ + shapeFile = "weapon_chaingun.dts"; + mountPoint = 1; + offset = "0.4 -0.2 0"; + + projectile = Coax_bullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 2.0 / 1000.0; + + useCapacitor = true; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 1.0; + minEnergy = 1.0; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = ChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.07; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 30; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; + +}; + +datablock TurretImageData(TankTurretParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 3.0"; + + projectile = ImplacmentBullet; + projectileType = TracerProjectile; + + useCapacitor = true; + usesEnergy = true; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 75; +}; + +function TankMGTurretBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.doexplodecheck = schedule(250, 0, "FlakExplode", %p); +} + +function HeavyTank::onTrigger(%data, %obj, %trigger, %state) +{ + if (%trigger ==4){ + switch (%state){ + case 0: + %obj.fireWeapon = false; + case 1: + if (%obj.inv[PlasmaAmmo] > 0){ + %frd = %obj.getForwardVector(); + %bck = vectorScale(%frd, -0.85); + %lft = vectorNormalize(vectorCross(%frd,"0 0 1")); + %rgt = vectorScale(%lft, -0.9); + %obj.decInventory(PlasmaAmmo, 4); + %p = new GrenadeProjectile() { + dataBlock = SmokeGrenadeProj; + initialDirection = vectorScale(%frd,0.85); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + %p = new GrenadeProjectile() { + dataBlock = SmokeGrenadeProj; + initialDirection = %bck; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + %p = new GrenadeProjectile() { + dataBlock = SmokeGrenadeProj; + initialDirection = vectorScale(%lft,0.9); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + %p = new GrenadeProjectile() { + dataBlock = SmokeGrenadeProj; + initialDirection = %rgt; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %obj.schedule(30000, "setInventory", PlasmaAmmo, 4); + } + } + } +} + +function TankATurretBarrel::onFire(%data,%obj,%slot) +{ + %data.lightStart = getSimTime(); + + %vehicle = 0; + %useEnergyObj = %obj.getObjectMount(); + if(!%useEnergyObj) + %useEnergyObj = %obj; + %energy = %useEnergyObj.getEnergyLevel(); + %vehicle = %useEnergyObj; + + if( %useEnergyObj.turretObject.getCapacitorLevel() < %data.minEnergy ) + { + return; + } + %vector = %obj.getMuzzleVector(%slot); + %initialPos = %obj.getMuzzlePoint(%slot); + + %datablock = %data.projectile; + if(%obj.firetype == 2){ + %datablock = %data.projectile2; + } + + %p = new (%data.projectileType)() { + dataBlock = %datablock; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + vehicleObject = %vehicle; + }; + + %obj.lastProjectile = %p; + MissionCleanup.add(%p); + + if(%obj.client) + %obj.client.projectile = %p; + + %vehicle.turretObject.setCapacitorLevel( %vehicle.turretObject.getCapacitorLevel() - %data.fireEnergy ); + %vec = vectornormalize(%obj.getMuzzleVector(4)); + %obj.mountobj.applyimpulse(%obj.getMuzzlePoint(4),vectorscale(%vec,"-1500")); +} + +function FlakExplode(%p){ + if(!isObject(%p)) + return; + InitContainerRadiusSearch(%p.getPosition(), 20, $TypeMasks::VehicleObjectType); + while ((%SearchResult = containerSearchNext()) != 0) + { + %pn = new (TracerProjectile)() { + dataBlock = TankFlak_Bullet_exp; + initialDirection = vectorNormalize(%p.getVelocity()); + initialPosition = %p.getPosition(); + sourceObject = %p.sourceObject; + sourceSlot = %p.sourceSlot; + vehicleObject = %p.vehicleObject; + }; + MissionCleanup.add(%pn); + %p.delete(); + return; + } + %p.doexplodecheck = schedule(30, 0, "FlakExplode", %p); +} + +function HeavyTank::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_Helicopter.cs b/Scripts/Vehicles/vehicle_Helicopter.cs new file mode 100644 index 0000000..885a846 --- /dev/null +++ b/Scripts/Vehicles/vehicle_Helicopter.cs @@ -0,0 +1,1472 @@ +//************************************************************** +// SHRIKE SCOUT FLIER +//************************************************************** + $helicopter::SeekRadius = 500; + $helicopter::SeekTime = 0.5; + $helicopter::minSeekHeat = 0.6; + $helicopter::minTargetingDistance = 15; + $helicopter::useTargetAudio = false; + +datablock AudioProfile(HeliturretFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = AssaultChaingunFireEffect; +}; + +datablock ParticleEmitterData(WhiteHorseHeatSeekerFireEffectEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionOffset = 1.2; + + ejectionVelocity = 10.0; + velocityVariance = 0.0; + + thetaMin = 179.0; + thetaMax = 180.0; + + lifetimeMS = 20; + + particles = "BazookaFireEffectSmoke"; + +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(helicopter) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_air_scout.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = false; + isProtectedMountPoint[1] = false; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 10.0; + + maxDamage = 2.0; + destroyedLevel = 2.0; + + HDAddMassLevel = 1.6; //if damage is above this Heavy Damage effects kick in + HDMassImage = LflyerHDMassImage; //this is the mass image that mounts when heavy damage kicks in + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 150; // Afterburner and any energy weapon pool + rechargeRate = 1.0; + + minDrag = 10; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 700; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 300; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.95; // Dampen control input so you don't` whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 7; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 4; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 2; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 10; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 620; // Steering jets (force applied when you move the mouse) + steeringRollForce = 35; // Steering jets (how much you heel over when you turn) + rollForce = 20; // was 20 Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 1; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 50; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 1250; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 1; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 0.1; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 3.5; + + // Rigid body + mass = 125; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 10; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.07; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 45; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MeLightDamageSmoke; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.4; + damageLevelTolerance[1] = 0.8; + numDmgEmitterAreas = 1; + + minMountDist = 4; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'WhiteHorse'; + targetTypeTag = 'Assault Chopper'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + + max[chaingunAmmo] = 24; + max[MissileLauncherAmmo] = 8; + max[PlasmaAmmo] = 12; + + numWeapons = 2; + + replaceTime = 120; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TargetProjectileData(GunshipTlProj) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + + maxRifleRange = 1500; + beamColor = "0.0 0.0 0.0"; + + startBeamWidth = 0; //0.02 + pulseBeamWidth = 0; //0.025 + beamFlareAngle = 3.0; + minFlareSize = 0.0; + maxFlareSize = 0.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + textureName[0] = "special/nonlingradient"; + textureName[1] = "special/flare"; + textureName[2] = "special/pulse"; + textureName[3] = "special/expFlare"; + beacon = true; +}; + +datablock SeekerProjectileData(HelicopterMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.6; + damageRadius = 9.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 1000; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + explosion = "MissileExplosion"; + velInheritFactor = 0.0; + + splash = MissileSplash; + baseEmitter = TankArtillerySmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 5000; + gravityMod = 0.4; + muzzleVelocity = 250.0; + turningSpeed = 0.0; + + proximityRadius = 4; + + terrainAvoidanceSpeed = 220; + terrainScanAhead = 50; + terrainHeightFail = 50; + terrainAvoidanceRadius = 50; + + useFlechette = true; + flechetteDelayMs = 100; + casingDeb = FlechetteDebris; +}; + +datablock SeekerProjectileData(HammerATMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 2.8; + damageRadius = 17.5; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 500; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 15000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 10.0; + maxVelocity = 200.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 40.0; + acceleration = 100.0; + + proximityRadius = 5; + + terrainAvoidanceSpeed = 90; + terrainScanAhead = 25; + terrainHeightFail = 12; + terrainAvoidanceRadius = 100; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 750; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = true; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock ShapeBaseImageData(HelicopterMissilePairImage) +{ + className = WeaponImage; + shapeFile = "turret_missile_large.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = HelicopterMissile; + projectileType = SeekerProjectile; + mountPoint = 10; + offset = "1.0 1 0.5"; + + projectileSpread = 1.0 / 1000.0; + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 1; + fireEnergy = 1; + fireTimeout = 125; + emap = true; + + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "AmmoLoading"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateTimeoutValue[3] = 0.01; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + //-------------------------------------- + stateName[4] = "Fire"; + stateSpinThread[4] = FullSpeed; + stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateSound[4] = ShrikeBlasterFire; + stateTimeoutValue[4] = 0.15; + stateTransitionOnTimeout[4] = "checkState"; + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.01; + stateWaitForTimeout[5] = false; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + //-------------------------------------- + stateName[6] = "EmptySpindown"; +// stateSound[6] = ChaingunSpindownSound; + stateSpinThread[6] = SpinDown; + stateTransitionOnAmmo[6] = "Ready"; + stateTimeoutValue[6] = 0.01; + stateTransitionOnTimeout[6] = "NoAmmo"; + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTransitionOnTriggerUp[7] = "NoAmmo"; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "checkState"; + stateTransitionOnTriggerUp[8] = "Spindown"; + stateTransitionOnNoAmmo[8] = "EmptySpindown"; + stateTimeoutValue[8] = 0.01; + stateTransitionOnTimeout[8] = "ready"; + + stateName[9] = "AmmoLoading"; + stateSound[9] = MissileReloadSound; + stateTimeoutValue[9] = 0.1; + stateAllowImageChange[9] = false; + stateTransitionOnTimeout[9] = "Ready"; +}; + +datablock ShapeBaseImageData(HelicopterMissileImage) : HelicopterMissilePairImage +{ +//**original** offset = "-.73 0 0"; + offset = "-1.0 1 0.5"; + stateScript[3] = "onTriggerDown"; + stateTimeoutValue[3] = 0.08; + stateScript[5] = "onTriggerUp"; + stateScript[6] = "onTriggerUp"; +}; + +datablock SeekerProjectileData(Heli_heavy) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.75; + damageRadius = 10.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 900; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.6; + + explosion = "MissileExplosion"; + velInheritFactor = 1.0; + + splash = MissileSplash; + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 10000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 70.0; + maxVelocity = 250.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 45.0; + acceleration = 80.0; + + proximityRadius = 7; + + terrainAvoidanceSpeed = 100; + terrainScanAhead = 1; + terrainHeightFail = 1; + terrainAvoidanceRadius = 20; + + useFlechette = true; + flechetteDelayMs = 225; + casingDeb = FlechetteDebris; +}; + +datablock ShapeBaseImageData(HelicopterATMissileImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = MissileLauncher; + ammo = MissileLauncherAmmo; + projectile = Heli_heavy; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "1.1 -4.5 2.5"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 1; + stateSequence[0] = "Activate"; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 0.1; + stateFire[2] = true; + stateEmitter[2] = "WhiteHorseHeatSeekerFireEffectEmitter"; + stateEmitterNode[2] = "muzzlepoint1"; + stateEmitterTime[2] = 1; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = MissileFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 0.3; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "AmmoLoading1"; + stateSequence[4] = "NoAmmo1"; + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 0.1; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 0.3; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "AmmoLoading2"; + stateSequence[9] = "NoAmmo2"; + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; + + stateName[11] = "AmmoLoading1"; + stateSound[11] = MissileReloadSound; + stateTimeoutValue[11] = 0.1; + stateAllowImageChange[11] = false; + stateTransitionOnTimeout[11] = "Reload1"; + + stateName[12] = "AmmoLoading2"; + stateSound[12] = MissileReloadSound; + stateTimeoutValue[12] = 0.1; + stateAllowImageChange[12] = false; + stateTransitionOnTimeout[12] = "Reload2"; +}; + +datablock ShapeBaseImageData(HelicopterATMissileImage2) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = MissileLauncher; + ammo = MissileLauncherAmmo; + projectile = Heli_heavy; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "-1.1 -4.5 2.5"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.1; + stateFire[1] = true; + stateEmitter[1] = "WhiteHorseHeatSeekerFireEffectEmitter"; + stateEmitterNode[1] = "muzzlepoint1"; + stateEmitterTime[1] = 1; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberTurretFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock ShapeBaseImageData(HelicopterMissileParam) +{ + mountPoint = 2; + shapeFile = "turret_missile_large.dts"; + + projectile = Heli_heavy; + projectileType = SeekerProjectile; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = 0.5; + minSeekHeat = 0.6; + minTargetingDistance = 50; + useTargetAudio = true; +}; + +datablock ShapeBaseImageData(HATDummy) +{ + shapeFile = "stackable2m.dts"; + mountPoint = 10; + offset = "1.1 -5 2.5"; + rotation = "1 0 0 90"; + mass = 20; + emap = true; +}; + +datablock ShapeBaseImageData(HATDummy2) +{ + shapeFile = "stackable2m.dts"; + mountPoint = 10; + offset = "-1.1 -5 2.5"; + rotation = "1 0 0 90"; + mass = 20; + emap = true; +}; + +function HelicopterATMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function HelicopterATMissileImage2::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function HelicopterATMissileImage::firePair(%this, %obj, %slot){ + %obj.setImageTrigger( 5, true); +} + +function HelicopterATMissileImage2::stopFire(%this, %obj, %slot){ + %obj.setImageTrigger( 5, false); +} + +function Helicopter::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastPilot.lastVehicle == %obj) + if(%obj.getMountNodeObject(0) == %obj.lastPilot) + schedule(200, %obj.lastPilot, "scKillPilot", %obj.lastPilot, %obj.lastDamagedBy); + if(%obj.lastBomber.lastVehicle == %obj) + if(%obj.getMountNodeObject(1) == %obj.lastBomber) + schedule(200, %obj.lastBomber, "scKillPilot", %obj.lastBomber, %obj.lastDamagedBy); + + Parent::onDestroyed(%data, %obj, %prevState); +} + + +//------------------------------------- +// CHOPPER BELLY TURRET CHARACTERISTICS +//------------------------------------- + +datablock TurretData(HeliTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = helicopter.maxDamage; + destroyedLevel = helicopter.destroyedLevel; + + thetaMin = 90; + thetaMax = 180; + + heatSignature = 0.0; + + // capacitor + maxCapacitorEnergy = 500; + capacitorRechargeRate = 5.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + targetNameTag = 'Chopper Chaingun'; + targetTypeTag = 'Turret'; + + max[ChaingunAmmo] = 400; + max[MortarAmmo] = 8; +}; + +datablock TracerProjectileData(Heli_CG_Bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.1; + directDamageType = $DamageType::ACCG; + explosion = ACCGExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.3; + damageRadius = 2.0; + radiusDamageType = $DamageType::ACCG; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 2500.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.25; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock ShapeBaseImageData(HeliChaingunImage) +{ + className = WeaponImage; + shapeFile = "turret_tank_barrelchain.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Heli_CG_Bullet; + projectileType = TracerProjectile; + projectileSpread = 1.0 / 1000.0; + emap = true; + offset = "0.07 -0.4 -0.5"; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 5.0; + shellVelocity = 0.0; + + velpredict = 0; + + mountPoint = 1; + usesEnergy = false; +// useCapacitor = true; +// useMountEnergy = true; +// minEnergy = 1; +// fireEnergy = 0.1; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; +// stateSound[0] = GravChaingunIdleSound; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ChaingunSpinupSound; + stateTimeoutValue[3] = 0.5; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = HeliturretFireSound; + //stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.07; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + stateName[5] = "Spindown"; + stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.5; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + stateName[6] = "EmptySpindown"; + //stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(HelicopterTATMissileImage) +{ + className = WeaponImage; + shapeFile = "stackable2m.dts"; + item = MissileLauncher; + ammo = MortarAmmo; + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + mountPoint = 1; + offset = "-0.9 -0.1 -0.3"; + rotation = "1 0 0 90"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 1; + stateSequence[0] = "Activate"; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 0.5; + stateFire[2] = true; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = MissileFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 2.0; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "AmmoLoading1"; + stateSequence[4] = "NoAmmo1"; + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 0.5; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 4.5; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "AmmoLoading2"; + stateSequence[9] = "NoAmmo2"; + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; + + stateName[11] = "AmmoLoading1"; + stateSound[11] = MissileReloadSound; + stateTimeoutValue[11] = 0.1; + stateAllowImageChange[11] = false; + stateTransitionOnTimeout[11] = "Reload1"; + + stateName[12] = "AmmoLoading2"; + stateSound[12] = MissileReloadSound; + stateTimeoutValue[12] = 0.1; + stateAllowImageChange[12] = false; + stateTransitionOnTimeout[12] = "Reload2"; +}; + +datablock ShapeBaseImageData(HelicopterTATMissileImage2) +{ + className = WeaponImage; + shapeFile = "stackable2m.dts"; + item = MissileLauncher; + ammo = MortarAmmo; + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + mountPoint = 1; + offset = "0.9 -0.1 -0.3"; + rotation = "1 0 0 90"; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.1; + stateFire[1] = true; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberTurretFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 2.0; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock TurretImageData(HeliTurretParam) +{ + mountPoint = 1; + shapeFile = "turret_muzzlepoint.dts"; + + Projectile = Shrike_special_gun; + ProjectileType = TracerProjectile; +}; + +datablock TurretImageData(GunshipTL) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 1; + offset = "0 0 0"; + + projectile = GunshipTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + + stateName[1] = "Ready"; + stateTransitionOnTriggerDown[1] = "Fire"; + + stateName[2] = "Fire"; + stateEnergyDrain[2] = 0; + stateFire[2] = true; + stateScript[2] = "onFire"; + stateTransitionOnTriggerUp[2] = "Deconstruct"; + + stateName[3] = "Deconstruct"; + stateScript[3] = "onDecon"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Ready"; +}; + +function HelicopterTATMissileImage::firePair(%this, %obj, %slot){ + %obj.setImageTrigger( 4, true); +} + +function HelicopterTATMissileImage2::stopFire(%this, %obj, %slot){ + %obj.setImageTrigger( 4, false); +} + +function HelicopterTATMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.TL)) + %p.setObjectTarget(%obj.TL.beacon); + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +// %p.CGMing = schedule(500, 0, "startCGM", %p); +} + +function HelicopterTATMissileImage2::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.TL)) + %p.setObjectTarget(%obj.TL.beacon); + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +// %p.CGMing = schedule(500, 0, "startCGM", %p); +} + +//****************************************************** +// vehicle.cs and weapturretcode.cs functions +//****************************************************** + + +//---------------------------- +// Helicopter FLIER +//---------------------------- + +function helicopter::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(HelicopterMissileParam, 0); + %obj.mountImage(HelicopterMissileImage, 2); + %obj.mountImage(HelicopterMissilePairImage, 3); + %obj.mountImage(HelicopterATMissileImage, 4); + %obj.mountImage(HelicopterATMissileImage2, 5); + %obj.mountImage(HATDummy, 6); + %obj.mountImage(HATDummy2, 7); + %obj.selectedWeapon = 1; + %obj.nextWeaponFire = 2; + %obj.setInventory(chaingunammo, 24); + %obj.setInventory(MissileLauncherAmmo, 8); + %obj.setInventory(PlasmaAmmo, 12); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(HeliTurret); + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(AIAimingTurretBarrel, 0); + %turret.mountImage(helichaingunimage, 2); + %turret.mountImage(HelicopterTATMissileImage, 3); + %turret.mountImage(HelicopterTATMissileImage2, 4); + %turret.mountImage(gunshipTL,5); + %turret.setInventory(MortarAmmo, 8); + %turret.setInventory(ChaingunAmmo, 400); + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function helicopter::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + + if (%client = %turret.getControllingClient()) + { + %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); + if(isObject(%turret.TL)) + %turret.TL.delete(); +} + +function helicopter::playerMounted(%data, %obj, %player, %node) +{ + bottomPrint(%player.client, "Helicopter: WEPS 1.mini anti ground dumbfire rockets 2.Seeking Missiles turreter.EXP Shell chaingun ***flare grenade on grenade key thanks to cracktrooper***", 5, 2 ); + $numVWeapons = 2; + + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) + { + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "apache", %node); + commandToClient(%player.client, 'SetWeaponryVehicleKeys', true); + %obj.selectedWeapon = 1; + + %ammoAmt = %player.inv[MissileLauncherAmmo]; + if(%ammoAmt) + %obj.setInventory(MissileLauncherAmmo, 8); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.incInventory(chaingunAmmo, %ammoAmt); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.incInventory(PlasmaAmmo, 12); + } + else if (%node == 1) + { + %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.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + + commandToClient(%player.client, 'setHudMode', 'Pilot', "bomber", %node); + %player.isBomber = true; + %ammoAmt = %player.inv[MortarAmmo]; + if(%ammoAmt) + %obj.turretobject.setInventory(MortarAmmo, 8); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.turretobject.setInventory(chaingunAmmo, 400); + setTargetSensorGroup(%turret.getTarget(), %player.team); + } + // 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 ); +} + +function helicopter::onTrigger(%data, %obj, %trigger, %state) +{ + if(%trigger == 0) + { + switch (%state) { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + case 1: + %obj.fireWeapon = true; + if(%obj.selectedWeapon == 1) + { + if(%obj.nextWeaponFire == 2) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + } + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, true); + } + } + } + else if (%trigger ==4) // Throw flare + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + case 1: + if (%obj.inv[PlasmaAmmo] > 0) + { + %obj.fireWeapon = true; + %obj.decInventory(PlasmaAmmo, 2); + %vec = vectornormalize(vectorcross(%obj.getForwardvector(),%obj.getUpVector())); + %p = new FlareProjectile() + { + dataBlock = VFlareGrenadeProj; + initialDirection = %vec; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000, "delete"); + %p = new FlareProjectile() + { + dataBlock = VFlareGrenadeProj; + initialDirection = vectorscale(%vec,-1); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + %p.schedule(6000, "delete"); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } + } + } +} + +function helicopter::playerDismounted(%data, %obj, %player) +{ + %player.setInventory(ChaingunAmmo, 0); + %player.setInventory(MissileLauncherAmmo, 0); + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function HelicopterMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); +} + +function HelicopterMissilePairImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); +} + +function heliTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function heliTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function heliTurret::onTrigger(%data, %obj, %trigger, %state) +{ + //error("onTrigger: trigger = " @ %trigger @ ", state = " @ %state); + //error("obj = " @ %obj @ ", class " @ %obj.getClassName()); + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(3, true); + else + %obj.setImageTrigger(3, false); + } + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } + if(%trigger == 4) + { + switch (%state) { + case 0: + if(isObject(%obj.TL)) + %obj.setImageTrigger(5, false); + else + %obj.setImageTrigger(5, true); + } + } +} + +function heliTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %player.setInventory(ChaingunAmmo, 0); + %player.setInventory(MortarAmmo, 0); + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + %client = %obj.getControllingClient(); + %client.player.isBomber = false; + %client.player.mountVehicle = false; + if(%client.player.getState() !$= "Dead") + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function HeliChaingunImage::onFire(%data,%obj,%slot) +{ + %data.lightStart = getSimTime(); + + %vec = %obj.getMuzzleVector(%slot); + + if(%data.velpredict == 1){ + %target = %obj.getLockedTarget(); + if(%target){ + %Tpos = %target.getWorldBoxCenter(); + %Opos = %obj.getMuzzlepoint(%slot); + %dist = vectorDist(%Tpos,%Opos); + %pos = vectorAdd(%Tpos, vectorScale(%target.getVelocity(),(%dist / 2500) )); + + %dist = vectorDist(%pos,%Opos); + %pos = vectorAdd(%Tpos, vectorScale(%target.getVelocity(),(%dist / 2500) )); + + %Tvec = vectorNormalize(vectorSub(%pos,%Opos)); + %Fvec = %obj.getMuzzleVector(%slot); + if(vectorDist(%Tvec,%Fvec) <= 0.1) + %vec = vectorNormalize(vectorSub(%Tpos,%obj.getMuzzlePoint(%slot))); + } + } + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + %initialPos = %obj.getMuzzlePoint(%slot); + + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + vehicleObject = %vehicle; + }; + MissionCleanup.add(%p); + + if(%obj.client) + %obj.client.projectile = %p; + + %obj.decInventory(%data.ammo,1); + return %p; +} + +function helicopter::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_S17.cs b/Scripts/Vehicles/vehicle_S17.cs new file mode 100644 index 0000000..6d050ac --- /dev/null +++ b/Scripts/Vehicles/vehicle_S17.cs @@ -0,0 +1,438 @@ +//************************************************************** +// S17 +//************************************************************** + +datablock HoverVehicleData(S17) : ShrikeDamageProfile +{ + spawnOffset = "0 0 1"; + canControl = false; + floatingGravMag = 3.5; + + catagory = "Vehicles"; + shapeFile = "nexuscap.dts"; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout_debris.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.0; + density = 0.9; + + cameraMaxDist = 5.0; + cameraOffset = 0.7; + cameraLag = 0.5; + numMountPoints = 0; + explosion = VehicleExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + maxDamage = 1.5; + destroyedLevel = 1.5; + + HDAddMassLevel = 1.2; + HDMassImage = WCHDMassImage; + + isShielded = false; + rechargeRate = 0.7; + energyPerDamagePoint = 75; + maxEnergy = 150; + minJetEnergy = 15; + jetEnergyDrain = 0.1; + + // Rigid Body + mass = 400; + bodyFriction = 0.1; + bodyRestitution = 0.5; + softImpactSpeed = 20; // Play SoftImpact Sound + hardImpactSpeed = 28; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 29; + speedDamageScale = 0.010; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23; + collDamageMultiplier = 0.030; + + dragForce = 25 / 45.0; + vertFactor = 0.0; + floatingThrustFactor = 0.35; + + mainThrustForce = 25; + reverseThrustForce = 15; + strafeThrustForce = 0; + turboFactor = 1.35; + + brakingForce = 15; + brakingActivationSpeed = 3; + + stabLenMin = 2.25; + stabLenMax = 3.75; + stabSpringConstant = 30; + stabDampingConstant = 16; + + gyroDrag = 16; + normalForce = 30; + restorativeForce = 20; + steeringForce = 35; + rollForce = 15; + pitchForce = 7; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 2.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = ScoutSqueelSound; + engineSound = ScoutEngineSound; + floatSound = ScoutThrustSound; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterSoftSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeSoftSplashSound; + + minMountDist = 4; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = SmallHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 0.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.8; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + forwardJetEmitter = WildcatJetEmitter; + + cmdCategory = Tactical; + cmdIcon = CMDHoverScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_landscout_grey"; + targetNameTag = 'S17'; + targetTypeTag = 'Combat Drone'; + sensorData = PlayerSensor; + + checkRadius = 1.7785; + observeParameters = "1 10 10"; + + runningLight[0] = WildcatLight1; + runningLight[1] = WildcatLight2; + runningLight[2] = WildcatLight3; + + shieldEffectScale = "0.9375 1.125 0.6"; + + replaceTime = 20; +}; + +datablock StaticShapeData(S17switch) : StaticShapeDamageProfile { + shapeFile = "switch.dts"; + mass = 1.0; + repairRate = 0; + dynamicType = $TypeMasks::StaticShapeObjectType; + heatSignature = 0; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +//------------------------------------- +// CHOPPER BELLY TURRET CHARACTERISTICS +//------------------------------------- + +datablock TurretData(S17Turret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'S17'; + targetTypeTag = 'Turret'; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = S17.maxDamage; + destroyedLevel = S17.destroyedLevel; + maxEnergy = 1; + + thetaMin = 60; + thetaMax = 150; + thetaNull = 90; + + // capacitor + maxCapacitorEnergy = 200; + capacitorRechargeRate = 5.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 1; + + targetNameTag = 'Wildcat Chaingun'; + targetTypeTag = 'Turret'; +}; + +datablock TurretImageData(S17ChaingunImage) +{ + className = WeaponImage; + shapeFile = "weapon_chaingun.dts"; +// mountPoint = 1; + + projectile = RPChaingunBullet; + projectileType = TracerProjectile; + projectileSpread = 1.2 / 1000.0; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 5.0; + shellVelocity = 0.0; + + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + minEnergy = 1; + fireEnergy = 0.1; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + stateName[0] = "Activate"; + stateSequence[0] = "deploy"; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateTimeoutValue[3] = 0.01; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.15; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.1; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + stateName[6] = "EmptySpindown"; + stateSpinThread[6] = SpinDown; + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock TurretImageData(S17Param) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 3.0"; + + Projectile = RPChaingunBullet; + ProjectileType = TracerProjectile; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 75; +}; + +function S17::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(S17Turret); + %turret.scale = "0.5 0.5 1"; + MissionCleanup.add(%turret); + %turret.team = %obj.team; + %turret.selectedWeapon = 1; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(S17Param, 0); + %turret.mountImage(S17chaingunimage, 2); + %obj.turret = %turret; + %obj.turretObject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + + %sensor = new StaticShape() + { + scale = "1.3 1.3 0.1"; + dataBlock = "S17switch"; + }; + MissionCleanup.add(%sensor); + %obj.mountObject(%sensor, 2); + %sensor.vehicleMounted = %obj; + %sensor.playThread($AmbientThread,"ambient"); + + schedule(5000, 0, "S17TurretAttackCheck",%obj); +} + +function S17::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + %turret.schedule(2000, delete); + + %body = %obj.getMountNodeObject(2); + if (isObject(%body)) + %body.schedule(1000, delete); + $teamRepCredits[%obj.team] += getWord($commsatPurchase[2],1); + $teamUsedCredits[%obj.team] -= getWord($commsatPurchase[2],1); +} + +function S17turret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function S17turret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function S17::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 S17TurretAttackCheck(%obj){ + if(!isObject(%obj)) + return; + + %valid = 0; + %TargetSearchMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%obj.getPosition(),400,%TargetSearchMask); + while ((%potentialTarget = ContainerSearchNext()) != 0) { + if (%potentialtarget && %valid != 1) { + %PTT = %potentialtarget.team; + if(%PTT $= "") + %PTT = %obj.team; + if(!(%PTT == %obj.team) && %PTT != 0){ + %valid = 1; + %target = %potentialtarget; + } + } + } + if(%valid == 1){ + if(!%obj.firing){ + %obj.firing = 1; + %obj.turretobject.setTargetObject(%target); + %obj.turretobject.aquireTime = getSimTime(); + %obj.turretobject.setImageTrigger(2,true); + } + } + else{ + if(%obj.firing){ + %obj.firing = 0; + %obj.turretobject.setImageTrigger(2,false); + %obj.turretObject.clearTarget(); + } + } + schedule(250, 0, "S17TurretAttackCheck",%obj); +} diff --git a/Scripts/Vehicles/vehicle_SuperiorityFighter.cs b/Scripts/Vehicles/vehicle_SuperiorityFighter.cs new file mode 100644 index 0000000..03fb994 --- /dev/null +++ b/Scripts/Vehicles/vehicle_SuperiorityFighter.cs @@ -0,0 +1,744 @@ +//************************************************************** +// AIR SUPERIORITY FIGHTER +//************************************************************** + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(SuperiorityFighter) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_air_scout.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = false; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 10.0; + + maxDamage = 2.5; + destroyedLevel = 2.5; + + HDAddMassLevel = 1.9; + HDMassImage = LflyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 2000; // Afterburner and any energy weapon pool + rechargeRate = 6; + + minDrag = 22; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 66; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 1; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.8; // Dampen control input so you don't` whack out at very slow speeds + + + // Maneuvering + maxSteeringAngle = 4.5; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 6250; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 750; // Steering jets (force applied when you move the mouse) + steeringRollForce = 3000; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 2.5; // Height off the ground at rest + createHoverHeight = 1; // Height off the ground when created + maxForwardSpeed = 250; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 3250; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 18; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 0; + + // Rigid body + mass = 150; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 150; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MeLightDamageSmoke; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.4; + damageLevelTolerance[1] = 0.75; + numDmgEmitterAreas = 1; + + // + max[chaingunAmmo] = 750; + max[MissileLauncherAmmo] = 4; + max[MortarAmmo] = 3; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'F54 Tornado'; + targetTypeTag = 'Superiority Fighter'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + + numWeapons = 3; + + replaceTime = 130; + + max[plasmaammo] = 20; + flaretime = 200; + flarelife = 850; + flarechance = 0.5; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(Superiority_bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.25; + directDamageType = $DamageType::Bullet; + explosion = ChaingunExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.0225; + damageRadius = 0.5; + radiusDamageType = $DamageType::Bullet; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 2250.0; + wetVelocity = 2000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.20; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock SeekerProjectileData(LRAAM) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 2.1; + damageRadius = 10.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 500; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + explosion = "MissileExplosion"; + velInheritFactor = 1.0; + + splash = MissileSplash; + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 10000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 25.0; + maxVelocity = 400.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 50.0; + acceleration = 150.0; + + proximityRadius = 6; + + terrainAvoidanceSpeed = 100; + terrainScanAhead = 50; + terrainHeightFail = 50; + terrainAvoidanceRadius = 150; + + useFlechette = true; + flechetteDelayMs = 225; + casingDeb = FlechetteDebris; +}; + + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock ShapeBaseImageData(SuperiorityChaingunImage) +{ + className = WeaponImage; + shapeFile = "turret_missile_large.dts"; //was turret_tank_barrelchain.dts + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Superiority_bullet; + projectileType = TracerProjectile; + mountPoint = 10; + offset = "0 2.1 -0.2"; // L/R - F/B - T/B was "0 3.25 0.75" + rotation = "0 1 0 180"; + + projectileSpread = 1.0 / 1000.0; + + velpredict = 0; + + usesEnergy = false; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateFire[3] = true; + stateScript[3] = "onFire"; + stateSound[3] = ShrikeBlasterFire; + stateTimeoutValue[3] = 0.01; + stateTransitionOnTimeout[3] = "Reload"; + stateAllowImageChange[3] = false; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.01; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(SuperiorityMissileImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = MissileLauncher; + ammo = MissileLauncherAmmo; + projectile = sidewinder; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 1.0; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(LRAAMImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = Mortar; + ammo = MortarAmmo; + projectile = LRAAM; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = BomberBombFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = BomberBombDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +function SuperiorityFighter::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(ScoutChaingunParam, 0); + %obj.mountImage(SuperiorityChaingunImage, 2); + %obj.mountImage(superiorityMissileImage, 4); + %obj.mountImage(LRAAMImage, 5); + %obj.selectedWeapon = 1; + %obj.nextWeaponFire = 2; + %obj.setInventory(MissileLauncherAmmo, 4); + %obj.setInventory(MortarAmmo, 3); + %obj.setInventory(chaingunammo, 750); + %obj.setInventory(plasmaAmmo,25); + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + schedule(2500, 0, "supersonicloop", %obj); +} + +function SuperiorityFighter::playerMounted(%data, %obj, %player, %node) +{ + %ammoAmt = %player.inv[MissileLauncherAmmo]; + if(%ammoAmt) + %obj.incInventory(MissileLauncherAmmo, %ammoAmt); + + %ammoAmt = %player.inv[MortarAmmo]; + if(%ammoAmt) + %obj.incInventory(MortarAmmo, %ammoAmt); + + %ammoAmt = %player.inv[chaingunAmmo]; + if(%ammoAmt) + %obj.incInventory(chaingunAmmo, %ammoAmt); + + bottomPrint(%player.client, "Tornado: wep1 CG, wep2 sidewinders, wep3 LRAAMS", 5, 2 ); + + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Shrike2", %node); + %obj.selectedWeapon = 1; + $numVWeapons = 3; + commandToClient(%player.client, 'SetWeaponryVehicleKeys', true); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function SuperiorityFighter::onTrigger(%data, %obj, %trigger, %state) +{ + %player = %obj.getMountNodeObject(0); + if(%trigger == 0) + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + case 1: + %obj.fireWeapon = true; + if(%obj.selectedWeapon == 1){ + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + } + else if(%obj.selectedWeapon == 2) { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, true); + %obj.setImageTrigger(5, false); + } + else { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, true); + } + } + } + else if (%trigger ==4){ + switch (%state) + { + case 0: + %obj.flaring = 0; + case 1: + %obj.flaring = 1; + schedule(%data.flaretime, 0, "fighterdropflares",%obj,%data.flaretime,%data.flarelife,%data.flarechance); + } + } +} + +function SuperiorityChaingunImage::onFire(%data,%obj,%slot){ + %data.lightStart = getSimTime(); + + %vec = %obj.getMuzzleVector(%slot); + + if(%data.velpredict == 1){ + %target = %obj.getLockedTarget(); + if(%target){ + %Tpos = %target.getWorldBoxCenter(); + %Opos = %obj.getMuzzlepoint(%slot); + %dist = vectorDist(%Tpos,%Opos); + %pos = vectorAdd(%Tpos, vectorScale(%target.getVelocity(),(%dist / 1750) )); + + %dist = vectorDist(%pos,%Opos); + %pos = vectorAdd(%Tpos, vectorScale(%target.getVelocity(),(%dist / 1750) )); + + %Tvec = vectorNormalize(vectorSub(%pos,%Opos)); + %Fvec = %obj.getMuzzleVector(%slot); + if(vectorDist(%Tvec,%Fvec) <= 0.1) + %vec = vectorNormalize(vectorSub(%Tpos,%obj.getMuzzlePoint(%slot))); + } + } + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + %initialPos = %obj.getMuzzlePoint(%slot); + + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + vehicleObject = %vehicle; + }; + MissionCleanup.add(%p); + + if(%obj.client) + %obj.client.projectile = %p; + + %obj.decInventory(%data.ammo,1); + return %p; +} + +function superiorityMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function LRAAMImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + if(isobject(%target)) + %homein = missileCheckAirTarget(%target); + + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else { + %p.dir = %obj.getForwardVector(); + schedule(500, 0, "LRAAMFindTarget", %p); + } +} + +function LRAAMFindTarget(%p){ + if(!isObject(%p)) + return; + %vec = vectorScale(vectorNormalize(%p.dir),600); + %pos = %p.getPosition(); + %tpos = vectorAdd(%vec,%pos); + InitContainerRadiusSearch(%tpos, 500, $TypeMasks::VehicleObjectType); + while ((%searchResult = containerSearchNext()) != 0){ + echo(%searchresult); + if(%searchResult.getClassName() $= "Flyingvehicle"){ + %p.setObjectTarget(%searchResult); + return; + } + } + schedule(250, 0, "LRAAMFindTarget",%p); +} + +function superiorityfighter::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastPilot.lastVehicle == %obj) + if(%obj.getMountNodeObject(0) == %obj.lastPilot) + schedule(200, %obj.lastPilot, "scKillPilot", %obj.lastPilot, %obj.lastDamagedBy); + + Parent::onDestroyed(%data, %obj, %prevState); +} + +function supersonicloop(%obj){ + if(!isObject(%obj)) + return; + %vec = %obj.getVelocity(); + %vel = vectorlen(%vec); + if(%vel > 200){ + %vec = vectorNormalize(%vec); + %vec = vectorScale(%vec,-75); + + %pn = new (TracerProjectile)() { + dataBlock = SuperSonicDet; + initialDirection = "0 0 1"; + initialPosition = vectoradd(%obj.getPosition(),%vec); + sourceObject = %obj; + sourceSlot = 1; + }; + } + schedule(350, 0, "supersonicloop", %obj); +} + +datablock TracerProjectileData(SuperSonicDet) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::BomberBombs; + explosion = ""; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.1; + damageRadius = 50.0; + radiusDamageType = $DamageType::default; + + kickBackStrength = 5000; + sound = ChaingunProjectile; + + dryVelocity = 1.0; + wetVelocity = 1.0; + velInheritFactor = 1.0; + fizzleTimeMS = 32; + lifetimeMS = 33; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +function superiorityfighter::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_boat.cs b/Scripts/Vehicles/vehicle_boat.cs new file mode 100644 index 0000000..2313863 --- /dev/null +++ b/Scripts/Vehicles/vehicle_boat.cs @@ -0,0 +1,1258 @@ +//************************************************************** +// BEOWULF ASSAULT VEHICLE +//************************************************************** + +datablock ParticleData( DepthChargeWaterSplashParticals ) +{ + dragCoefficient = 1; + gravityCoefficient = 1.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 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] = 1.0; + sizes[1] = 2.5; + sizes[2] = 2.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( DepthChargeWaterSplashEmitter ) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 20; + velocityVariance = 4; + ejectionOffset = 36.0; + thetaMin = 27; + thetaMax = 28; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 200; + particles = "DepthChargeWaterSplashParticals"; +}; + +datablock ParticleData(DepthChargeSparks) +{ + dragCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 350; + textureName = "special/crescent3"; + colors[0] = "0.4 0.4 1.0 1.0"; + colors[1] = "0.4 0.4 1.0 1.0"; + colors[2] = "0.4 0.4 1.0 0.0"; + sizes[0] = 5.5; + sizes[1] = 5.5; + sizes[2] = 5.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(DepthChargeSparksEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 50; + velocityVariance = 4; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "DepthChargeSparks"; +}; + +datablock ParticleData(DepthChargeBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 3.0; + sizes[1] = 3.0; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.8; + times[2] = 1.0; +}; +datablock ParticleEmitterData(DepthChargeBubbleEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 6.0; + ejectionOffset = 6.0; + velocityVariance = 3.0; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "DepthChargeBubbleParticle"; +}; + +datablock DebrisData( DepthChargeDebris ) +{ + emitters[0] = GrenadeBubbleEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 3.0; + lifetimeVariance = 0.7; + + numBounces = 1; +}; + +datablock ExplosionData(DepthChargeSubExplosion1) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + delayMS = 100; + offset = 15.0; + playSpeed = 1.5; + + sizes[0] = "2.75 2.75 2.75"; + sizes[1] = "3.5 3.5 3.5"; + sizes[2] = "1.75 1.75 1.75"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ExplosionData(DepthChargeSubExplosion2) +{ + explosionShape = "disc_explosion.dts"; + faceViewer= true; + delayMS = 50; + offset = 10.0; + playSpeed = 0.75; + + sizes[0] = "4.5 4.5 4.5"; + sizes[1] = "5.5 5.5 5.5"; + sizes[2] = "4.0 4.0 4.0"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ExplosionData(DepthChargeSubExplosion3) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 0.5; + + sizes[0] = "6.0 6.0 6.0"; + sizes[1] = "10.0 10.0 10.0"; + sizes[2] = "6.5 6.5 6.5"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ExplosionData(DepthChargeExplosion) +{ + soundProfile = UnderwaterMortarExplosionSound; + + subExplosion[0] = DepthChargeSubExplosion1; + subExplosion[1] = DepthChargeSubExplosion2; + subExplosion[2] = DepthChargeSubExplosion3; + + emitter[0] = DepthChargeBubbleEmitter; + emitter[1] = DepthChargeSparksEmitter; + emitter[2] = DepthChargeWaterSplashEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 9.0 7.0"; + camShakeAmp = "100.0 100.0 100.0"; + camShakeDuration = 1.3; + camShakeRadius = 25.0; + + debris = DepthChargeDebris; + debrisThetaMin = 60; + debrisThetaMax = 120; + debrisNum = 15; + debrisNumVariance = 5; + debrisVelocity = 15.0; + debrisVelocityVariance = 3.0; +}; + +datablock ExplosionData(DepthChargeExplosionShallow) +{ + soundProfile = UnderwaterMortarExplosionSound; + + subExplosion[0] = DepthChargeSubExplosion1; + subExplosion[1] = DepthChargeSubExplosion2; + subExplosion[2] = DepthChargeSubExplosion3; + + emitter[0] = DepthChargeBubbleEmitter; + emitter[1] = DepthChargeSparksEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 9.0 7.0"; + camShakeAmp = "100.0 100.0 100.0"; + camShakeDuration = 1.3; + camShakeRadius = 25.0; + + debris = DepthChargeDebris; + debrisThetaMin = 80; + debrisThetaMax = 90; + debrisNum = 15; + debrisNumVariance = 5; + debrisVelocity = 15.0; + debrisVelocityVariance = 3.0; +}; + + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(Boat) : TankDamageProfile +{ + spawnOffset = "0 0 4"; + canControl = false; + floatingGravMag = 4.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_air_hapc.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_hapc.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + mountPose[1] = sitting; + mountPose[2] = sitting; + mountPose[3] = sitting; + mountPose[4] = sitting; + mountPose[5] = sitting; +// mountPose[1] = sitting; + numMountPoints = 6; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + isProtectedMountPoint[2] = true; + isProtectedMountPoint[3] = true; + isProtectedMountPoint[4] = true; + isProtectedMountPoint[5] = true; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 3.0; + explosionRadius = 25.0; + + maxSteeringAngle = 0.5; // 20 deg. + + maxDamage = 10.0; + destroyedLevel = 10.0; + + HDAddMassLevel = 7.0; + HDMassImage = BoatHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 135; + maxEnergy = 1000; + minJetEnergy = 15; + jetEnergyDrain = 0.0; + + // Rigid Body + mass = 2500; + bodyFriction = 1.2; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 5; // Play SoftImpact Sound + hardImpactSpeed = 10; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 5; + speedDamageScale = 0.005; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 18; + collDamageMultiplier = 0.005; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.0; + + mainThrustForce = 40; + reverseThrustForce = 25; + strafeThrustForce = 0.0; + turboFactor = 1.0; + + brakingForce = 20; + brakingActivationSpeed = 4; + + stabLenMin = 0.5; + stabLenMax = 2.5; + stabSpringConstant = 50; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 100; + steeringForce = 12; + rollForce = 2; + pitchForce = 0; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 10; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Star II Heavy'; + targetTypeTag = 'GunBoat'; + sensorData = AWACSensor; + sensorRadius = AWACSensor.detectRadius; + sensorColor = "255 194 9"; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + replaceTime = 90; +}; + +datablock GrenadeProjectileData(DepthChargeCharge) +{ + projectileShapeName = "stackable1m.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 4.0; + damageRadius = 75.0; + radiusDamageType = $DamageType::DepthCharge; + kickBackStrength = 0; + + explosion = "GrenadeExplosion"; + underwaterExplosion = "DepthChargeExplosionShallow"; + velInheritFactor = 0.0; + splash = MissileSplash; + depthTolerance = 0.01; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.0; + armingDelayMS = -1; + + gravityMod = 1.0; + muzzleVelocity = 0.0; + drag = 0.1; + sound = MissileProjectileSound; + + hasLight = false; + hasLightUnderwaterColor = false; +}; + +datablock LinearProjectileData(DepthChargeDetonation) +{ + projectileShapeName = "mortar_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 4.0; + damageRadius = 100; + radiusDamageType = $DamageType::DepthCharge; + kickBackStrength = 250; + + sound = mortarProjectileSound; + explosion = "DepthChargeExplosion"; + underwaterExplosion = "DepthChargeExplosion"; + splash = MissileSplash; + + dryVelocity = 0; + wetVelocity = 10; + velInheritFactor = 0.0; + fizzleTimeMS = 10; + lifetimeMS = 10; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 5000; + + hasLight = true; + lightRadius = 100; + lightColor = "0.4 0.2 0.0"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "0.4 0.2 0.05"; + +}; + +datablock ShapeBaseImageData(DepthChargeDropper) +{ + className = WeaponImage; + shapeFile = "stackable5l.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = DepthChargeCharge; + projectileType = GrenadeProjectile; + offset = "0 -5.25 0"; // L/R - F/B - T/B + rotation = "0 0 1 180"; // L/R - F/B - T/B + mountPoint = 10; + + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 50.00; + minEnergy = 50.00; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = AssaultMortarFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 5.0; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = AssaultMortarDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +function DepthChargeDropper::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.detboatdepthcharge = schedule(%obj.DCdepth, 0, "detDepthCharge", %p); +} + +function detDepthCharge(%p) +{ + if (!isObject(%p)) + return; + %pn = new (LinearProjectile)() { + dataBlock = DepthChargeDetonation; + initialDirection = "0 0 -1"; + initialPosition = %p.getPosition(); + sourceObject = %p.sourceObject; + sourceSlot = %p.sourceSlot; + vehicleObject = %p.vehicleObject; + }; + MissionCleanup.add(%pn); + %P.delete(); +} + +datablock TurretData(BoatTurret) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Boat Artillery'; + targetTypeTag = 'Turret'; + mass = 1.0; // Not really relevant + + maxEnergy = 1000; + maxDamage = boat.maxDamage; + destroyedLevel = boat.destroyedLevel; + repairRate = 0; + + thetaMin = 45; + thetaMax = 95; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons =1; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = 'Boat Artillery'; + targetTypeTag = 'Turret'; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + repairRate = 0.0; +}; + +datablock TurretData(BoatTurret2) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Boat Flak'; + targetTypeTag = 'Turret'; + mass = 1.0; // Not really relevant + + maxEnergy = 1000; + maxDamage = boat.maxDamage; + destroyedLevel = boat.destroyedLevel; + repairRate = 0; + + thetaMin = 10; + thetaMax = 80; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons =1; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 100.0; + + targetNameTag = 'Boat AA'; + targetTypeTag = 'Turret'; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + repairRate = 0.0; +}; + +datablock TurretImageData(BoatFlakTurretBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + mountPoint = 0; + + projectile = AssaultChaingunBullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 1.5 / 1000.0; + + useCapacitor = false; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 3.0; + minEnergy = 3.0; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 30; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock GrenadeProjectileData(BoatArtillery) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 3.25; + damageRadius = 30.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 3500; + + explosion = "artillerybarrelexplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = TankArtillerySmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.3; + armingDelayMS = -1; + gravityMod = 1.0; + muzzleVelocity = 200.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +//------------------------------------- +// ASSAULT MORTAR CHARACTERISTICS +//------------------------------------- + +datablock TurretImageData(BoatATurretBarrel) +{ + shapeFile = "turret_tank_barrelmortar.dts"; + mountPoint = 0; + + projectile = BoatArtillery; + projectileType = GrenadeProjectile; + + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 250.00; + minEnergy = 250.00; + useCapacitor = false; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 2.5; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultMortarFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 3.0; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + //stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateDirection[5] = false; + stateSequence[5] = "Activate"; + stateTimeoutValue[5] = 1.0; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultMortarDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TurretImageData(BoatTurretParam) +{ + mountPoint = 0; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 3.0"; + + projectile = BoatFlak_Bullet; + projectileType = TracerProjectile; + + useCapacitor = false; + usesEnergy = true; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 75; +}; + +datablock TurretImageData(BoatATurretParam) +{ + mountPoint = 0; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 3.0"; + + projectile = BoatArtillery; + projectileType = GrenadeProjectile; + + useCapacitor = false; + usesEnergy = true; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 75; +}; + +datablock TurretImageData(BoatParam) +{ + mountPoint = 10; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 0.0"; + + projectile = DepthChargeCharge; + projectileType = GrenadeProjectile; + + usesEnergy = true; +}; + + +function Boat::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.DCdepth = 5000; + %obj.mountImage(BoatParam, 0); + %obj.mountImage(DepthChargeDropper, 2); + %obj.selectedWeapon = 1; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(BoatTurret); + %turret2 = TurretData::create(BoatTurret2); + %turret3 = TurretData::create(BoatTurret2); + %turret.selectedWeapon = 1; + %turret2.selectedWeapon = 1; + %turret3.selectedWeapon = 1; + MissionCleanup.add(%turret); + MissionCleanup.add(%turret2); + MissionCleanup.add(%turret3); + %turret.team = %obj.teamBought; + %turret2.team = %obj.teamBought; + %turret3.team = %obj.teamBought; + %turret.setSelfPowered(); + %turret2.setSelfPowered(); + %turret3.setSelfPowered(); + %obj.mountObject(%turret, 9); + %obj.mountObject(%turret2, 2); + %obj.mountObject(%turret3, 5); + %turret.mountImage(BoatATurretBarrel, 2); + %turret2.mountImage(BoatFlakTurretBarrel, 2); + %turret3.mountImage(BoatFlakTurretBarrel, 2); + %obj.turretObject = %turret; + %obj.turretObject = %turret2; + %obj.turretObject = %turret3; + + %turret.setAutoFire(false); + %turret2.setAutoFire(false); + %turret3.setAutoFire(false); + + %turret.mountImage(BoatATurretParam, 0); + %turret2.mountImage(BoatTurretParam, 0); + %turret3.mountImage(BoatTurretParam, 0); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); + + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + setTargetSensorGroup(%turret2.getTarget(), %turret2.team); + setTargetNeverVisMask(%turret2.getTarget(), 0xffffffff); + setTargetSensorGroup(%turret3.getTarget(), %turret3.team); + setTargetNeverVisMask(%turret3.getTarget(), 0xffffffff); +} + +function Boat::onTrigger(%data, %obj, %trigger, %state) +{ + %player = %obj.getMountNodeObject(0); + if(%trigger == 0) + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + case 1: + %obj.fireWeapon = true; + %obj.setImageTrigger(2, true); + } + } + else if(%trigger == 5) + { + switch (%state){ + case 1: + %obj.DCdepth = %obj.DCdepth + 1000; + if(%obj.DCdepth >= 10001) + %obj.DCdepth = 5000; + bottomPrint(%obj.pilot.client, "Depth Charge fuse set to "@(%obj.DCdepth / 1000)@" seconds", 5, 2 ); + } + } +} + +function Boat::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(9); + %turret2 = %obj.getMountNodeObject(2); + %turret3 = %obj.getMountNodeObject(5); + if (!%turret) + return; + if (!%turret2) + return; + if (!%turret3) + return; + + if (%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + if (%client = %turret2.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + if (%client = %turret3.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(1000, delete); + %turret2.schedule(1050, delete); + %turret3.schedule(1100, delete); +} + +//---------------------------- +// Boat +//---------------------------- + +function boat::playerMounted(%data, %obj, %player, %node) +{ + Cancel(%player.DrownLoop); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) { + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + %obj.pilot = %player; + } + else if (%node == 1) + { + %turret = %obj.getMountNodeObject(9); + %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; + + $aWeaponActive = 0; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + + else if (%node == 3) + { + %turret2 = %obj.getMountNodeObject(2); + %player.vehicleTurret = %turret2; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if (!%player.client.isAIControlled()) + { + %player.setControlObject(%turret2); + %player.client.setObjectActiveImage(%turret2, 2); + } + %turret.turreteer = %player; + + $aWeaponActive = 0; + %obj.getMountNodeObject(2).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + + else if (%node == 4) + { + %turret3 = %obj.getMountNodeObject(5); + %player.vehicleTurret = %turret3; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if (!%player.client.isAIControlled()) + { + %player.setControlObject(%turret3); + %player.client.setObjectActiveImage(%turret3, 2); + } + %turret.turreteer = %player; + + $aWeaponActive = 0; + %obj.getMountNodeObject(5).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + + + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + bottomPrint(%player.client, "ONLY use on water will not move well on land", 5, 2 ); + + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + + if(%player.team != %obj.team) + %player.damage(%obj, %player.getPosition(), 10, $DamageType::Idiocy); +} + +function BoatTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function BoatTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function BoatTurret::onTrigger(%data, %obj, %trigger, %state) +{ + //error("onTrigger: trigger = " @ %trigger @ ", state = " @ %state); + //error("obj = " @ %obj @ ", class " @ %obj.getClassName()); + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function BoatTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %client = %obj.getControllingClient(); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function BoatTurret2::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function BoatTurret2::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function BoatTurret2::onTrigger(%data, %obj, %trigger, %state) +{ + //error("onTrigger: trigger = " @ %trigger @ ", state = " @ %state); + //error("obj = " @ %obj @ ", class " @ %obj.getClassName()); + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function BoatTurret2::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %client = %obj.getControllingClient(); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function BoatATurretBarrel::onMount(%this, %obj, %slot) { } +function BoatATurretBarrel::onUnmount(%this, %obj, %slot) { } +function BoatATurretBarrel::onMount(%this, %obj, %slot) { } +function BoatATurretBarrel::onUnmount(%this, %obj, %slot) { } +function BoatFlakTurretBarrel::onMount(%this, %obj, %slot) { } +function BoatFlakTurretBarrel::onUnmount(%this, %obj, %slot) { } + +function Boat::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + case 1: + //Ocean Water + case 2: + //River Water + case 3: + //Stagnant Water + 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 boat::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 = ""; + } +} diff --git a/Scripts/Vehicles/vehicle_bomber.cs b/Scripts/Vehicles/vehicle_bomber.cs new file mode 100644 index 0000000..9150002 --- /dev/null +++ b/Scripts/Vehicles/vehicle_bomber.cs @@ -0,0 +1,990 @@ +//************************************************************** +// THUNDERSWORD BOMBER +//************************************************************** +//************************************************************** +// SOUNDS +//************************************************************** +datablock EffectProfile(BomberFlyerEngineEffect) +{ + effectname = "vehicles/bomber_engine"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberFlyerThrustEffect) +{ + effectname = "vehicles/bomber_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberTurretFireEffect) +{ + effectname = "weapons/missile_fire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberTurretActivateEffect) +{ + effectname = "vehicles/bomber_turret_activate"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberTurretReloadEffect) +{ + effectname = "vehicles/bomber_turret_reload"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberTurretDryFireEffect) +{ + effectname = "weapons/missile_launcher_dryfire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberBombReloadEffect) +{ + effectname = "vehicles/bomber_bomb_reload"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberBombDryFireEffect) +{ + effectname = "vehicles/bomber_bomb_dryfire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(BomberBombFireEffect) +{ + effectname = "weapons/generic_throw"; + minDistance = 10.0; + maxDistance = 20.0; +}; + +datablock AudioProfile(BomberFlyerEngineSound) +{ + filename = "fx/vehicles/bomber_engine.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = BomberFlyerEngineEffect; +}; + +datablock AudioProfile(BomberFlyerThrustSound) +{ + filename = "fx/vehicles/bomber_boost.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = BomberFlyerThrustEffect; +}; + +datablock AudioProfile(FusionExpSound) +// Sound played when mortar impacts on target +{ + filename = "fx/powered/turret_mortar_explode.wav"; + description = "AudioBIGExplosion3d"; + preload = true; +}; + +datablock AudioProfile(BomberTurretFireSound) +{ + filename = "fx/weapons/missile_fire.WAV"; + description = AudioClose3d; + preload = true; + effect = BomberTurretFireEffect; +}; + +datablock AudioProfile(BomberTurretActivateSound) +{ + filename = "fx/vehicles/bomber_turret_activate.wav"; + description = AudioClose3d; + preload = true; + effect = BomberTurretActivateEffect; +}; + +datablock AudioProfile(BomberTurretReloadSound) +{ + filename = "fx/weapons/weapon.missilereload.wav"; + description = AudioClose3d; + preload = true; + effect = BomberTurretReloadEffect; +}; + +datablock AudioProfile(BomberTurretIdleSound) +{ + filename = "fx/misc/diagnostic_on.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(BomberTurretDryFireSound) +{ + filename = "fx/weapons/missile_launcher_dryfire.wav"; + description = AudioClose3d; + preload = true; + effect = BomberTurretDryFireEffect; +}; + +datablock AudioProfile(BomberBombReloadSound) +{ + filename = "fx/vehicles/bomber_bomb_reload.wav"; + description = AudioClose3d; + preload = true; + effect = BomberBombReloadEffect; +}; + +datablock AudioProfile(BomberBombProjectileSound) +{ + filename = "fx/vehicles/bomber_bomb_projectile.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = BomberBombFireEffect; +}; + +datablock AudioProfile(BomberBombDryFireSound) +{ + filename = "fx/vehicles/bomber_bomb_dryfire.wav"; + description = AudioClose3d; + preload = true; + effect = BomberBombDryFireEffect; +}; + +datablock AudioProfile(BomberBombFireSound) +{ + filename = "fx/vehicles/bomber_bomb_reload.wav"; + description = AudioClose3d; + preload = true; + effect = BomberBombFireEffect; +}; + +datablock AudioProfile(BomberBombIdleSound) +{ + filename = "fx/misc/diagnostic_on.wav"; + description = ClosestLooping3d; + preload = true; +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(BomberFlyer) : BomberDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_bomber.dts"; + multipassenger = true; + computeCRC = true; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_bomber.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.2; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 3; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + isProtectedMountPoint[2] = false; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + cameraMaxDist = 22; + cameraOffset = 5; + cameraLag = 1.0; + explosion = MFVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 20.0; + + maxDamage = 5.0; // Total health + destroyedLevel = 5.0; // Damage textures show up at this health level + + HDAddMassLevel = 3.5; + HDMassImage = HFlyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 150; + maxEnergy = 400; // Afterburner and any energy weapon pool + minDrag = 60; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 1800; // Angular Drag (dampens the drift after you stop moving the mouse...also tumble drag) + rechargeRate = 0.8; + + // Auto stabilize speed + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 1500; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 300; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.95; // Dampen control input so you don't whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 8; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 5; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 8; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 5000; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 800; // Steering jets (force applied when you move the mouse) + steeringRollForce = 2500; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 4; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 110; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 5000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40.0; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 2.5; // Energy use of the afterburners (low number is less drain...can be fractional) + vertThrustMultiple = 3.0; + + dustEmitter = LargeVehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 2.0; + + damageEmitter[0] = MFLightDamageSmoke; + damageEmitter[1] = MFHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "3.0 -3.0 0.0 "; + damageEmitterOffset[1] = "-3.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + // Rigid body + mass = 350; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 20; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 25; + collDamageMultiplier = 0.020; + + // + minTrailSpeed = 15; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = BomberFlyerThrustSound; + engineSound = BomberFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 15.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterHardSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeHardSplashSound; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'B-34'; + targetTypeTag = 'Bomber'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 7.1895; + observeParameters = "1 10 10"; + shieldEffectScale = "0.75 0.975 0.375"; + showPilotInfo = 1; + + replaceTime = 75; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(sidewinder_MarkII) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.75; + damageRadius = 10.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 500; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 15000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 75.0; + maxVelocity = 210.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 64.0; + acceleration = 100.0; + + proximityRadius = 3; + + terrainAvoidanceSpeed = 180; + terrainScanAhead = 25; + terrainHeightFail = 12; + terrainAvoidanceRadius = 100; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 100; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +//------------------------------------- +// BOMBER BELLY TURRET CHARACTERISTICS +//------------------------------------- + +datablock TurretData(BomberTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'Thundersword'; + targetTypeTag = 'Bomberturret'; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = BomberFlyer.maxDamage; + destroyedLevel = BomberFlyer.destroyedLevel; + + thetaMin = 90; + thetaMax = 180; + + // capacitor + maxCapacitorEnergy = 1000; + capacitorRechargeRate = 1.2; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 3; + + targetNameTag = 'Thundersword Belly'; + targetTypeTag = 'Turret'; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; +}; + +datablock TurretImageData(BomberTurretBarrel) +{ + shapeFile = "stackable1s.dts"; + rotation = "0 0 1 90"; + offset = "0.4 -0.4 -0.4"; + mountPoint = 0; + + projectile = sidewinder_MarkII; + projectileType = SeekerProjectile; + + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + fireEnergy = 60.0; + minEnergy = 60.0; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + + attackRadius = 300; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BomberTurretActivateSound; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 2.0; + stateFire[2] = true; + stateRecoil[2] = LightRecoil; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = BomberTurretFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 0.75; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "Reload1"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateSequence[4] = "NoAmmo"; + stateSequence[4] = "NoAmmo1"; + + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateTransitionOnNoAmmo[6] = "NoAmmo"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 0.75; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 0.75; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "Reload2"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateSequence[9] = "NoAmmo"; + stateSequence[9] = "NoAmmo2"; + + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; + +}; + +datablock TurretImageData(BomberTurretBarrelPair) +{ + shapeFile = "stackable1s.dts"; + rotation = "0 0 1 90"; + offset = "-0.4 -0.4 -0.4"; + mountPoint = 1; + + projectile = sidewinder_MarkII; + projectileType = SeekerProjectile; + + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + fireEnergy = 60.0; + minEnergy = 60.0; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + + attackRadius = 300; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.13; + stateFire[1] = true; + stateRecoil[1] = LightRecoil; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberTurretFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock TurretImageData(AIAimingTurretBarrel) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 3; + + projectile = sidewinder_MarkII; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 800; + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + attackRadius = 300; +}; + +datablock ShapeBaseImageData(BomberBellyTurretParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + + Projectile = sidewinder_MarkII; + ProjectileType = SeekerProjectile; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; +}; + +//------------------------------------- +// BOMBER BOMB PROJECTILE +//------------------------------------- + +datablock BombProjectileData(BomberBomb) +{ + projectileShapeName = "bomb.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 4.0; + damageRadius = 30; + radiusDamageType = $DamageType::BomberBombs; + kickBackStrength = 2500; + + explosion = "VehicleBombExplosion"; + velInheritFactor = 1.0; + + grenadeElasticity = 0.25; + grenadeFriction = 0.4; + armingDelayMS = 2000; + muzzleVelocity = 0.1; + drag = 0.3; + + minRotSpeed = "60.0 0.0 0.0"; + maxRotSpeed = "80.0 0.0 0.0"; + scale = "1.0 1.0 1.0"; + + sound = BomberBombProjectileSound; +}; + +//------------------------------------- +// BOMBER BOMB CHARACTERISTICS +//------------------------------------- + +datablock ItemData(BombAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "repair_kit.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + computeCRC = true; +}; + +datablock StaticShapeData(DropBombs) +{ + catagory = "Turrets"; + shapeFile = "bombers_eye.dts"; + maxDamage = 1.0; + disabledLevel = 0.6; + destroyedLevel = 0.8; +}; + +datablock TurretImageData(BomberBombImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + offset = "2 -4 -0.5"; + mountPoint = 10; + + projectile = BomberBomb; + projectileType = BombProjectile; + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + + fireEnergy = 110.0; + minEnergy = 110.0; + + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 0.32; + stateFire[2] = true; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = BomberBombFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "Reload1"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateSequence[4] = "NoAmmo"; + stateSequence[4] = "NoAmmo1"; + + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberBombDryFireSound; + stateTimeoutValue[5] = 0.5; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateTransitionOnNoAmmo[6] = "NoAmmo"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 0.32; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 0.1; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "Reload2"; + // --------------------------------------------- + // z0dd - ZOD, 5/8/02. Incorrect parameter value + //stateSequence[9] = "NoAmmo"; + stateSequence[9] = "NoAmmo2"; + + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberBombDryFireSound; + stateTimeoutValue[10] = 0.5; + stateTransitionOnTimeout[10] = "NoAmmo2"; +}; + +datablock TurretImageData(BomberBombPairImage) +{ + shapeFile = "turret_muzzlepoint.dts"; + offset = "-2 -4 -0.5"; + mountPoint = 10; + + projectile = BomberBomb; + projectileType = BombProjectile; + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + fireEnergy = 110.0; + minEnergy = 110.0; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.13; + stateFire[1] = true; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberBombFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; + +}; + +//************************************************************** +// WEAPONS SPECIAL EFFECTS +//************************************************************** +datablock TracerProjectileData(BomberCGBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::ACCG; + explosion = MissileExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.75; + damageRadius = 5.0; + radiusDamageType = $DamageType::ACCG; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 2500.0; + wetVelocity = 1000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.25; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TurretImageData(BomberCGImage) +{ + className = WeaponImage; + shapeFile = "turret_belly_barrell.dts"; + offset = "0.1 0 0"; + item = Chaingun; + projectile = BomberCGBullet; + projectileType = TracerProjectile; + projectileSpread = 2.0 / 1000.0; + emap = true; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 5.0; + shellVelocity = 0.0; + + mountPoint = 1; + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + minEnergy = 5; + fireEnergy = 5.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = SniperFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.4; + stateAllowImageChange[4] = false; + stateSound[4] = ChaingunDryFireSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileReloadSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +function BomberTurretBarrelPair::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + %target = %obj.getLockedTarget(); + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function BomberTurretBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + %target = %obj.getLockedTarget(); + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function Bomberflyer::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_effects.cs b/Scripts/Vehicles/vehicle_effects.cs new file mode 100644 index 0000000..4264ef7 --- /dev/null +++ b/Scripts/Vehicles/vehicle_effects.cs @@ -0,0 +1,673 @@ +$Bomber::SeekRadius = 500; +$Bomber::SeekTime = 0.25; +$Bomber::minSeekHeat = 0.6; +$Bomber::minTargetingDistance = 15; +$Bomber::useTargetAudio = true; + +//************************************************************** +// Shrikes/Apaches/Scouts +//************************************************************** + +datablock ParticleData(VehicleBoomSmoke) +{ + dragCoeffiecient = 0.2; + gravityCoefficient = -0.25; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 4000; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.5 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 3.0; + sizes[1] = 8.0; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.3; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(VehicleBoomSmokeEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 10.0; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 150; + + particles = "VehicleBoomSmoke"; +}; + +datablock ParticleData(MeDebrisFireParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.2; + inheritedVelFactor = 0.0; + + lifetimeMS = 350; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -160.0; + spinRandomMax = 160.0; + + animateTexture = true; + framesPerSec = 15; + + + animTexName[0] = "special/Explosion/exp_0016"; + animTexName[1] = "special/Explosion/exp_0018"; + animTexName[2] = "special/Explosion/exp_0020"; + animTexName[3] = "special/Explosion/exp_0022"; + animTexName[4] = "special/Explosion/exp_0024"; + animTexName[5] = "special/Explosion/exp_0026"; + animTexName[6] = "special/Explosion/exp_0028"; + animTexName[7] = "special/Explosion/exp_0030"; + animTexName[8] = "special/Explosion/exp_0032"; + + colors[0] = "1.0 0.7 0.5 1.0"; + colors[1] = "1.0 0.5 0.2 1.0"; + colors[2] = "1.0 0.25 0.1 0.0"; + sizes[0] = 0.5; + sizes[1] = 1.5; + sizes[2] = 0.7; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(MeDebrisFireEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 1; + + ejectionVelocity = 0.25; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 175.0; + + particles = "MeDebrisFireParticle"; +}; + +datablock ParticleData( MeDebrisSmokeParticle ) +{ + dragCoeffiecient = 4.0; + gravityCoefficient = -0.00; // rises slowly + inheritedVelFactor = 0.2; + + lifetimeMS = 2500; + lifetimeVarianceMS = 100; // ...more or less + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -50.0; + spinRandomMax = 50.0; + + colors[0] = "0.3 0.3 0.3 0.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 2; + sizes[1] = 3.0; + sizes[2] = 4.5; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MeDebrisSmokeEmitter ) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 2; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 15.0; + + useEmitterSizes = true; + + particles = "MeDebrisSmokeParticle"; +}; + +datablock DebrisData( MeVehicleFireballDebris ) +{ + emitters[0] = MeDebrisSmokeEmitter; + emitters[1] = MeDebrisFireEmitter; + + explosion = DebrisExplosion; + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 100.0; + lifetimeVariance = 30.0; + + numBounces = 0; + bounceVariance = 0; +}; + +datablock DebrisData( MeVSpikeDebris ) +{ + emitters[0] = VSmokeSpikeEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 0.3; + lifetimeVariance = 0.02; +}; + +datablock ExplosionData(MeVSpikeExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 0.75; + sizes[0] = "5.0 5.0 5.0"; + sizes[1] = "5.0 5.0 5.0"; + times[0] = 0.0; + times[1] = 1.0; + + debris = MeVSpikeDebris; + debrisThetaMin = 10; + debrisThetaMax = 175; + debrisNum = 5; + debrisNumVariance = 3; + debrisVelocity = 30.0; + debrisVelocityVariance = 7.0; +}; + +datablock ExplosionData(MeVehicleExplosion) +{ + explosionShape = "disc_explosion.dts"; + playSpeed = 1.5; + soundProfile = VehicleExplosionSound; + faceViewer = true; + + emitter[0] = VehicleBoomSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 60; + debrisThetaMax = 90; + debrisNum = 20; + debrisNumVariance = 5; + debrisVelocity = 25.0; + debrisVelocityVariance = 2.0; + + subExplosion = MeVSpikeExplosion; + + shakeCamera = true; + camShakeFreq = "11.0 13.0 9.0"; + camShakeAmp = "40.0 40.0 40.0"; + camShakeDuration = 0.7; + camShakeRadius = 25.0; +}; + +datablock ParticleData(MeHeavyDamageSmokeParticle) +{ + dragCoefficient = 0.4; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 5000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -60.0; + spinRandomMax = 60.0; + textureName = "particleTest"; + colors[0] = "1.0 0.4 0.2 1.0"; + colors[1] = "1.0 0.8 0.2 0.8"; + colors[2] = "0.1 0.1 0.1 0.3"; + colors[3] = "0.2 0.2 0.2 0.15"; + colors[4] = "0.3 0.3 0.3 0.0"; + sizes[0] = 2.0; + sizes[1] = 3.0; + sizes[2] = 4.5; + sizes[3] = 5.0; + sizes[4] = 8.0; + times[0] = 0.0; + times[1] = 0.05; + times[2] = 0.15; + times[3] = 0.4; + times[4] = 1.0; +}; + +datablock ParticleEmitterData(MeHeavyDamageSmoke) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 2; + ejectionVelocity = 3.0; + velocityVariance = 0.5; + ejectionOffset = 1.5; + thetaMin = 0; + thetaMax = 180; + overrideAdvances = false; + particles = "MeHeavyDamageSmokeParticle"; +}; + +datablock ParticleData(MeLightDamageSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 4000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -45.0; + spinRandomMax = 45.0; + textureName = "particleTest"; + colors[0] = "0.1 0.1 0.1 0.5"; + colors[1] = "0.2 0.2 0.2 0.7"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 1.5; + sizes[1] = 3.0; + sizes[2] = 4.0; + times[0] = 0.0; + times[1] = 0.3; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(MeLightDamageSmoke) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 6; + ejectionVelocity = 4.0; + velocityVariance = 0.5; + ejectionOffset = 1.5; + thetaMin = 0; + thetaMax = 180; + overrideAdvances = false; + particles = "MeLightDamageSmokeParticle"; +}; + +datablock ParticleData(MeDamageBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.04; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 200; + useInvAlpha = false; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "special/bubbles"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.3 0.3 0.3 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.4; + sizes[1] = 1.6; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(MeDamageBubbles) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "MeDamageBubbleParticle"; +}; + +datablock DebrisData( MeShapeDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.1; + friction = 0.5; + + lifetime = 25.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 0; + maxSpinSpeed = 25; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 1.0; + + velocity = 17.0; + velocityVariance = 7.0; +}; + +datablock DebrisData( GShapeDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.0; + friction = 0.5; + + lifetime = 25.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 0; + maxSpinSpeed = 5; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 1.0; + + velocity = 0.0; + velocityVariance = 0.0; +}; + +//************************************************************** +// Bombers/Havocs/Blackhawks +//************************************************************** + +datablock ExplosionData(MFVSubExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 0.65; + sizes[0] = "8.0 8.0 8.0"; + sizes[1] = "8.0 8.0 8.0"; + times[0] = 0.0; + times[1] = 1.0; + + debris = MeVSpikeDebris; + debrisThetaMin = 10; + debrisThetaMax = 175; + debrisNum = 5; + debrisNumVariance = 3; + debrisVelocity = 30.0; + debrisVelocityVariance = 7.0; +}; + +datablock ExplosionData(MFVehicleExplosion) +{ + explosionShape = "disc_explosion.dts"; + playSpeed = 1.5; + soundProfile = VehicleExplosionSound; + faceViewer = true; + + emitter[0] = VehicleBoomSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 35; + debrisThetaMax = 95; + debrisNum = 25; + debrisNumVariance = 5; + debrisVelocity = 20.0; + debrisVelocityVariance = 5.0; + + subExplosion = MFVSubExplosion; + + shakeCamera = true; + camShakeFreq = "11.0 13.0 9.0"; + camShakeAmp = "40.0 40.0 40.0"; + camShakeDuration = 0.7; + camShakeRadius = 35.0; +}; + +datablock ParticleEmitterData(MFHeavyDamageSmoke) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 2; + ejectionVelocity = 5.0; + velocityVariance = 1.0; + ejectionOffset = 2.5; + thetaMin = 0; + thetaMax = 180; + overrideAdvances = false; + particles = "MeHeavyDamageSmokeParticle"; +}; + +datablock ParticleEmitterData(MFLightDamageSmoke) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 6; + ejectionVelocity = 5.0; + velocityVariance = 1.0; + ejectionOffset = 2.0; + thetaMin = 0; + thetaMax = 180; + overrideAdvances = false; + particles = "MeLightDamageSmokeParticle"; +}; + +//************************************************************** +// Tanks/APC/MPB +//************************************************************** + +datablock ParticleEmitterData(MeHGHeavyDamageSmoke) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 2; + ejectionVelocity = 3.0; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 135; + overrideAdvances = false; + particles = "MeLightDamageSmokeParticle"; +}; + +datablock ParticleData(HGVExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = 0.0; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 2000; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.0 1.0"; + colors[1] = "0.2 0.2 0.2 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 5.0; + sizes[1] = 10.0; + sizes[2] = 12.0; + times[0] = 0.0; + times[1] = 0.4; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(HGVExplosionSmokeEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionVelocity = 20.0; + velocityVariance = 4.0; + + thetaMin = 70.0; + thetaMax = 110.0; + + lifetimeMS = 150; + + particles = "HGVExplosionSmoke"; +}; + +datablock ExplosionData(HGVSubExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 3000; + delayMS = 0; + offset = 0.0; + playSpeed = 0.5; + + sizes[0] = "8.0 8.0 8.0"; + sizes[1] = "8.0 8.0 8.0"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = VehicleBoomSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 60; + debrisThetaMax = 90; + debrisNum = 10; + debrisNumVariance = 5; + debrisVelocity = 10.0; + debrisVelocityVariance = 9.0; +}; + +datablock ExplosionData(HGVSubExplosion2) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 3000; + delayMS = 200; + offset = 8.0; + playSpeed = 0.75; + + sizes[0] = "4.0 4.0 4.0"; + sizes[1] = "4.0 4.0 4.0"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = HGVExplosionSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 60; + debrisThetaMax = 120; + debrisNum = 5; + debrisNumVariance = 2; + debrisVelocity = 15.0; + debrisVelocityVariance = 5.0; +}; + +datablock ExplosionData(HGVSubExplosion3) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 3000; + delayMS = 400; + offset = 12.0; + playSpeed = 0.8; + + sizes[0] = "3.5 3.5 3.5"; + sizes[1] = "3.5 3.5 3.5"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = HGVExplosionSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 60; + debrisThetaMax = 120; + debrisNum = 5; + debrisNumVariance = 2; + debrisVelocity = 15.0; + debrisVelocityVariance = 5.0; +}; + +datablock ExplosionData(HGVSubExplosion4) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 3000; + delayMS = 600; + offset = 15.0; + playSpeed = 0.8; + + sizes[0] = "3.0 3.0 3.0"; + sizes[1] = "3.0 3.0 3.0"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = HGVExplosionSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 60; + debrisThetaMax = 120; + debrisNum = 5; + debrisNumVariance = 2; + debrisVelocity = 15.0; + debrisVelocityVariance = 5.0; +}; + +datablock ExplosionData(HGVSubExplosion5) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 3000; + delayMS = 900; + offset = 20.0; + playSpeed = 0.4; + + sizes[0] = "5.0 5.0 5.0"; + sizes[1] = "5.0 5.0 5.0"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = VehicleBoomSmokeEmitter; + + debris = MeVehicleFireballDebris; + debrisThetaMin = 0; + debrisThetaMax = 90; + debrisNum = 10; + debrisNumVariance = 0; + debrisVelocity = 15.0; + debrisVelocityVariance = 5.0; +}; + +datablock ExplosionData(HGVehicleExplosion) +{ + soundProfile = VehicleExplosionSound; + + subExplosion[0] = HGVSubExplosion; + subExplosion[1] = HGVSubExplosion2; + subExplosion[2] = HGVSubExplosion3; + subExplosion[3] = HGVSubExplosion4; + subExplosion[4] = HGVSubExplosion5; + + shakeCamera = true; + camShakeFreq = "11.0 13.0 9.0"; + camShakeAmp = "40.0 40.0 40.0"; + camShakeDuration = 1.0; + camShakeRadius = 40.0; +}; \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_gunship.cs b/Scripts/Vehicles/vehicle_gunship.cs new file mode 100644 index 0000000..b78dd90 --- /dev/null +++ b/Scripts/Vehicles/vehicle_gunship.cs @@ -0,0 +1,1236 @@ +//************************************************************** +// AC290 Saber Gunship +//************************************************************** +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(gunship) : BomberDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_bomber.dts"; + multipassenger = true; + computeCRC = true; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_bomber.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.2; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + mountPose[3] = sitting; + numMountPoints = 4; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + isProtectedMountPoint[2] = false; + isProtectedMountPoint[3] = true; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + cameraMaxDist = 22; + cameraOffset = 5; + cameraLag = 1.0; + explosion = MFVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 20.0; + + maxDamage = 5.0; // Total health + destroyedLevel = 5.0; // Damage textures show up at this health level + + HDAddMassLevel = 3.5; + HDMassImage = HFlyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 150; + maxEnergy = 400; // Afterburner and any energy weapon pool + minDrag = 60; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 1800; // Angular Drag (dampens the drift after you stop moving the mouse...also tumble drag) + rechargeRate = 0.8; + + // Auto stabilize speed + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 1500; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 300; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.95; // Dampen control input so you don't whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 8; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 5; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 8; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 6500; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 800; // Steering jets (force applied when you move the mouse) + steeringRollForce = 2500; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 4; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 120; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 5000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40.0; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 2.5; // Energy use of the afterburners (low number is less drain...can be fractional) + vertThrustMultiple = 3.0; + + dustEmitter = LargeVehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 2.0; + + damageEmitter[0] = MFLightDamageSmoke; + damageEmitter[1] = MFHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "3.0 -3.0 0.0 "; + damageEmitterOffset[1] = "-3.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + // Rigid body + mass = 300; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 20; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 25; + collDamageMultiplier = 0.020; + + // + minTrailSpeed = 15; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = BomberFlyerThrustSound; + engineSound = BomberFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 15.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterHardSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeHardSplashSound; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'AC-290 Saber'; + targetTypeTag = 'Gunship'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 7.1895; + observeParameters = "1 10 10"; + shieldEffectScale = "0.75 0.975 0.375"; + showPilotInfo = 1; + + replaceTime = 40; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock GrenadeProjectileData(GunshipArtillery) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 2.0; + damageRadius = 20.0; + radiusDamageType = $DamageType::Artillery; + kickBackStrength = 3000; + + explosion = "artillerybarrelexplosion"; + velInheritFactor = 0.0; + splash = GrenadeSplash; + + baseEmitter = TankArtillerySmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.3; + armingDelayMS = -1; + gravityMod = 1.0; + muzzleVelocity = 250.0; + drag = 0.1; + + sound = MortarTurretProjectileSound; + + hasLight = true; + lightRadius = 3; + lightColor = "0.05 0.2 0.05"; +}; + +datablock TracerProjectileData(GunshipCGBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.0; + directDamageType = $DamageType::ACCG; + explosion = ACCGExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.4; + damageRadius = 3.0; + radiusDamageType = $DamageType::ACCG; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 1500.0; + wetVelocity = 100.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.25; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TargetProjectileData(GunshipTlProj) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + + maxRifleRange = 1500; + beamColor = "0.0 0.0 0.0"; + + startBeamWidth = 0; //0.02 + pulseBeamWidth = 0; //0.025 + beamFlareAngle = 3.0; + minFlareSize = 0.0; + maxFlareSize = 0.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + textureName[0] = "special/nonlingradient"; + textureName[1] = "special/flare"; + textureName[2] = "special/pulse"; + textureName[3] = "special/expFlare"; + beacon = true; +}; + +//------------------------------------- +// GUNSHIP TURRET CHARACTERISTICS +//------------------------------------- + +datablock TurretData(GunshipTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_bomber_grey"; + targetNameTag = 'Gunship AT'; + targetTypeTag = 'Turret'; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = gunship.maxDamage; + destroyedLevel = gunship.destroyedLevel; + + thetaMin = 90; + thetaMax = 180; + + // capacitor + maxCapacitorEnergy = 1000; + capacitorRechargeRate = 4.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; +}; + +datablock TurretData(GunshipTurret2) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Gunship AA'; + targetTypeTag = 'Turret'; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = gunship.maxDamage; + destroyedLevel = gunship.destroyedLevel; + + thetaMin = 20; + thetaMax = 110; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; +}; + +//************************* +// Turret Images +//************************* + +datablock TurretImageData(GunshipTurretBarrel) +{ + shapeFile = "turret_missile_large.dts"; + mountPoint = 0; + offset = "-0.9 0 0.0"; + rotation = "0 1 0 90"; + + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + fireEnergy = 250.0; + minEnergy = 250.0; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + + attackRadius = 300; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BomberTurretActivateSound; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 1.0; + stateFire[2] = true; + stateRecoil[2] = LightRecoil; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = BomberTurretFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 3.0; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "Reload1"; + stateSequence[4] = "NoAmmo1"; + + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 1.0; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 3.0; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "Reload2"; + stateSequence[9] = "NoAmmo2"; + + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; +}; + +datablock TurretImageData(GunshipTurretBarrelPair) +{ + shapeFile = "turret_missile_large.dts"; + mountPoint = 0; + offset = "-0.9 0 -0.4"; + rotation = "0 1 0 90"; + + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + usesEnergy = true; + useCapacitor = true; + useMountEnergy = true; + fireEnergy = 250.0; + minEnergy = 250.0; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + + attackRadius = 300; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.2; + stateFire[1] = true; + stateRecoil[1] = LightRecoil; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberTurretFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.2; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock TurretImageData(GunshipATurretBarrel) +{ + shapeFile = "turret_tank_barrelmortar.dts"; + mountPoint = 1; + offset = "0.7 0 0.1"; + + projectile = GunshipArtillery; + projectileType = GrenadeProjectile; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = true; + fireEnergy = 400.00; + minEnergy = 400.00; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 2.0; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultMortarFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 3.0; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + //stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateDirection[5] = false; + stateSequence[5] = "Activate"; + stateTimeoutValue[5] = 1.0; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultMortarDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TurretImageData(GunshipCGImage) +{ + shapeFile = "turret_tank_barrelchain.dts"; + offset = "-0.3 0 0"; + mountPoint = 0; + + item = Chaingun; + projectile = GunshipCGBullet; + projectileType = TracerProjectile; + projectileSpread = 1.0 / 1000.0; + emap = true; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 5.0; + shellVelocity = 0.0; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; +// stateSound[0] = GravChaingunIdleSound; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + //stateSound[3] = ChaingunSpinupSound; + stateTimeoutValue[3] = 0.2; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + //stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.07; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + stateName[5] = "Spindown"; + //stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.05; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + stateName[6] = "EmptySpindown"; + //stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock TurretImageData(GunshipAAMissileImage) +{ + shapeFile = "turret_missile_large.dts"; + offset = "0.35 0 0"; + mountPoint = 0; + + item = Chaingun; + projectile = sidewinder_MarkII; + projectileType = SeekerProjectile; + emap = true; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 2.0; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = BomberTurretFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 3.0; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + //stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateDirection[5] = false; + stateSequence[5] = "Activate"; + stateTimeoutValue[5] = 1.0; + stateTransitionOnLoaded[5] = "ActivateReady"; + stateTransitionOnTimeout[5] = "Dead"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultMortarDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateTransitionOnTriggerDown[8] = "DryFire"; +}; + +datablock TurretImageData(GunshipTL) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 3; + offset = "0 0 0"; + + projectile = GunshipTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + + stateName[1] = "Ready"; + stateTransitionOnTriggerDown[1] = "Fire"; + + stateName[2] = "Fire"; + stateEnergyDrain[2] = 0; + stateFire[2] = true; + stateScript[2] = "onFire"; + stateTransitionOnTriggerUp[2] = "Deconstruct"; + + stateName[3] = "Deconstruct"; + stateScript[3] = "onDecon"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Ready"; +}; + +datablock TurretImageData(GST1Param) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 3; + + projectile = HammerATMissile; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 800; + + attackRadius = 300; +}; + +datablock TurretImageData(GST2Param) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 0; + offset = "0 0.2 0"; + + projectile = sidewinder_MarkII; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 800; + + attackRadius = 300; +}; + +//************************************************* +//Functions +//************************************************* +//vehicle events +//------------------------------------------------- + +function Gunship::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(GunshipTurret); + %turret2 = TurretData::create(GunshipTurret2); + MissionCleanup.add(%turret); + MissionCleanup.add(%turret2); + %turret.team = %obj.teamBought; + %turret2.team = %obj.teamBought; + %turret.setSelfPowered(); + %turret2.setSelfPowered(); + %turret.selectedWeapon = 1; + %turret2.selectedWeapon = 1; + %obj.mountObject(%turret, 10); + %obj.mountObject(%turret2, 2); + %turret.mountImage(GST1Param, 0); + %turret.mountImage(GunshipTurretBarrel,2); + %turret.mountImage(GunshipTurretBarrelPair,3); + %turret.mountImage(GunshipATurretBarrel, 4); + %turret.mountImage(gunshipTL, 5); + %turret2.mountImage(GST2Param, 0); + %turret2.mountImage(GunshipCGImage,2); + %turret2.mountImage(GunshipAAMissileImage,3); + %obj.turretObject = %turret; + %obj.turretObject2 = %turret2; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret2.vehicleMounted = %obj; + %turret.setAutoFire(false); + %turret2.setAutoFire(false); + %turret.mountobj = %obj; + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetSensorGroup(%turret2.getTarget(), %turret2.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + setTargetNeverVisMask(%turret2.getTarget(), 0xffffffff); +} + +function Gunship::playerMounted(%data, %obj, %player, %node) +{ +//[[CHANGE]] + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) + { + // pilot position + %player.setPilot(true); + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + bottomPrint(%player.client, "Pilot Postion: your basic pilot seat jsut fly", 5, 2 ); + } + 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); + } + %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; + centerPrint(%player.client, "AT gunner Position: wep1-LGM's wep2-Artillery ***initiate laser for LGM's with GRENADE KEY***", 5, 2 ); + } + else if (%node == 3) + { + // bombardier position + %turret2 = %obj.getMountNodeObject(2); + %player.vehicleTurret = %turret2; + %player.setTransform("0 0 0 0 0 1 0"); + %player.lastWeapon = %player.getMountedImage($WeaponSlot); + %player.unmountImage($WeaponSlot); + if (!%player.client.isAIControlled()) + { + %player.setControlObject(%turret2); + %player.client.setObjectActiveImage(%turret2, 2); + } + %turret2.bomber = %player; + $bWeaponActive = 0; + %obj.getMountNodeObject(2).selectedWeapon = 1; + commandToClient(%player.client,'SetWeaponryVehicleKeys', true); + + commandToClient(%player.client, 'setHudMode', 'Pilot', "Bomber", %node); + %player.isBomber = true; + bottomPrint(%player.client, "AA gunner Position: wep1-heavy chaingun wep2-lock on missiles", 5, 2 ); + setTargetSensorGroup(%turret2.getTarget(), %player.team); + } + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function Gunship::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (isObject(%turret)){ + %turret.altTrigger = 0; + %turret.fireTrigger = 0; + if (%client = %turret.getControllingClient()) + { + %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); + if(isObject(%turret.TL)) + %turret.TL.delete(); + } + %turret2 = %obj.getMountNodeObject(2); + if (isObject(%turret2)){ + %turret2.altTrigger = 0; + %turret2.fireTrigger = 0; + if (%client = %turret2.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + + %client.player.bomber = false; + %client.player.isBomber = false; + } + %turret2.schedule(2000, delete); + } +} + +//------------------------------------------------- +//Turret Events +//------------------------------------------------- +function GunshipTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function GunshipTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function GunshipTurret2::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function GunshipTurret2::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function GunshipTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + %client = %obj.getControllingClient(); + %client.player.isBomber = false; + %client.player.mountVehicle = false; + if(%client.player.getState() !$= "Dead") + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function GunshipTurret2::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %client = %obj.getControllingClient(); + %client.player.isBomber = false; + %client.player.mountVehicle = false; + if(%client.player.getState() !$= "Dead") + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} +function GunshipTurret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(4, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + if(%state) + %obj.setImageTrigger(4, true); + else + %obj.setImageTrigger(4, false); + } + + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } + if(%trigger == 4) + { + switch (%state) { + case 0: + if(isObject(%obj.TL)) + %obj.setImageTrigger(5, false); + else + %obj.setImageTrigger(5, true); + } + } +} + +function GunshipTurret2::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(3, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(3, true); + else + %obj.setImageTrigger(3, false); + } + + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +//------------------------------------------------- +//Weapon Events +//------------------------------------------------- + +function GunshipTurretBarrel::firePair(%this, %obj, %slot){ + %obj.setImageTrigger( 3, true); +} +function GunshipTurretBarrelPair::stopFire(%this, %obj, %slot){ + %obj.setImageTrigger( 3, false); +} +function GunshipTurretBarrelPair::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.TL)) + %p.setObjectTarget(%obj.TL.beacon); + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +} + +function GunshipTurretBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.TL)) + %p.setObjectTarget(%obj.TL.beacon); + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +} + +function HammerATlockon(%p) +{ + if(!isObject(%p)) + return; + InitContainerRadiusSearch(%p.getPosition(), 100, $TypeMasks::VehicleObjectType); + while ((%SearchResult = containerSearchNext()) != 0) + { + %SearchObj = FirstWord(%SearchResult); + if(%SearchObj.getClassName() $= "WheeledVehicle" || %SearchObj.getClassName() $= "HoverVehicle"){ + %p.setObjectTarget(%searchObj); + return; + } + } + %p.HATlockon = schedule(500, 0, "HammerATlockon", %p); +} + +function GunshipAAMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + %target = %obj.getLockedTarget(); + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function GunshipATurretBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %vec = vectornormalize(%obj.getMuzzleVector(4)); + %obj.mountobj.applyimpulse(%obj.getMuzzlePoint(4),vectorscale(%vec,"-100")); +} + +function GunshipTL::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.TL = %p; + %p.beacon = new BeaconObject() { + dataBlock = "SubBeacon"; + beaconType = "vehicle"; + position = %p.getTargetPoint(); + }; + %p.turret = %obj; + %p.beaconupdate = schedule(100, 0, "GunshipBeaconLoop", %p); +} + +function GunshipTL::onDecon(%data,%obj,%slot){ + %obj.TL.beacon.delete(); + %obj.TL.delete(); +} + +function GunshipBeaconLoop(%p){ + if(!isObject(%P)) + return; + %pos = %p.getTargetPoint(); + if(%pos $= "0 0 0 -1"){ + %Tpos = %p.turret.getPosition(); + %Tvec = vectorScale(%p.turret.getMuzzleVector(5),1500); + %pos = vectorAdd(%Tpos,%Tvec); + } + %p.beacon.setPosition(%pos); + %p.beaconupdate = schedule(100, 0, "GunshipBeaconLoop", %p); +} + +//------------------------------------------------- +//Misc +//------------------------------------------------- + +function gunship::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_havoc.cs b/Scripts/Vehicles/vehicle_havoc.cs new file mode 100644 index 0000000..48af4e7 --- /dev/null +++ b/Scripts/Vehicles/vehicle_havoc.cs @@ -0,0 +1,464 @@ +//************************************************************** +// HAVOC HEAVY TRANSPORT FLIER +//************************************************************** +//************************************************************** +// SOUNDS +//************************************************************** +datablock EffectProfile(HAPCFlyerEngineEffect) +{ + effectname = "vehicles/htransport_thrust"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(HAPCFlyerThrustEffect) +{ + effectname = "vehicles/htransport_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock AudioProfile(HAPCFlyerEngineSound) +{ + filename = "fx/vehicles/htransport_thrust.wav"; + description = AudioDefaultLooping3d; + effect = HAPCFlyerEngineEffect; +}; + +datablock AudioProfile(HAPCFlyerThrustSound) +{ + filename = "fx/vehicles/htransport_boost.wav"; + description = AudioDefaultLooping3d; + effect = HAPCFlyerThrustEffect; +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(HAPCFlyer) : HavocDamageProfile +{ + spawnOffset = "0 0 6"; + renderWhenDestroyed = false; + + catagory = "Vehicles"; + shapeFile = "vehicle_air_hapc.dts"; + multipassenger = true; + computeCRC = true; + + + debrisShapeName = "vehicle_air_hapc.dts"; + debris = ShapeDebris; + + drag = 0.2; + density = 1.0; + + mountPose[0] = sitting; +// mountPose[1] = sitting; + numMountPoints = 6; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = false; + isProtectedMountPoint[2] = false; + isProtectedMountPoint[3] = false; + isProtectedMountPoint[4] = false; + isProtectedMountPoint[5] = false; + canControl = true; + cameraMaxDist = 17; + cameraOffset = 2; + cameraLag = 8.5; + explosion = MFVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 20.0; + + maxDamage = 8.0; + destroyedLevel = 8.0; + + HDAddMassLevel = 5.6; + HDMassImage = HFlyerHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 175; + maxEnergy = 550; + minDrag = 100; // Linear Drag + rotationalDrag = 2700; // Anguler Drag + + // Auto stabilize speed + maxAutoSpeed = 10; + autoAngularForce = 3000; // Angular stabilizer force + autoLinearForce = 450; // Linear stabilzer force + autoInputDamping = 0.95; // + + // Maneuvering + maxSteeringAngle = 8; + horizontalSurfaceForce = 10; // Horizontal center "wing" + verticalSurfaceForce = 10; // Vertical center "wing" + maneuveringForce = 6000; // Horizontal jets + steeringForce = 1000; // Steering jets + steeringRollForce = 2500; // Steering jets + rollForce = 12; // Auto-roll + hoverHeight = 6; // Height off the ground at rest + createHoverHeight = 6; // Height off the ground when created + maxForwardSpeed = 90; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 5000; + minJetEnergy = 55; + jetEnergyDrain = 3.0; + vertThrustMultiple = 3.0; + + + dustEmitter = LargeVehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 2.0; + + damageEmitter[0] = MFLightDamageSmoke; + damageEmitter[1] = MFHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "3.0 -3.0 0.0 "; + damageEmitterOffset[1] = "-3.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + // Rigid body + mass = 450; + bodyFriction = 0; + bodyRestitution = 0.3; + minRollSpeed = 0; + softImpactSpeed = 12; // Sound hooks. This is the soft hit. + hardImpactSpeed = 15; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 25; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.060; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 28; + collDamageMultiplier = 0.020; + + // + minTrailSpeed = 15; + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = HAPCFlyerThrustSound; + engineSound = HAPCFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 8.0; + hardSplashSoundVelocity = 12.0; + exitSplashSoundVelocity = 8.0; + + exitingWater = VehicleExitWaterHardSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeHardSplashSound; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingHAPCIcon; + cmdMiniIconName = "commander/MiniIcons/com_hapc_grey"; + targetNameTag = 'HVC Golem'; + targetTypeTag = 'Heavy Transport'; + sensorData = PlayerSensor; + + checkRadius = 7.8115; + observeParameters = "1 15 15"; + + stuckTimerTicks = 32; // If the hapc spends more than 1 sec in contact with something + stuckTimerAngle = 80; // with a > 80 deg. pitch, BOOM! + + shieldEffectScale = "1.0 0.9375 0.45"; + + max[PlasmaAmmo] = 20; + + replaceTime = 40; +}; + +function HAPCFlyer::hasDismountOverrides(%data, %obj) +{ + return true; +} + +function HAPCFlyer::getDismountOverride(%data, %obj, %mounted) +{ + %node = -1; + for (%i = 0; %i < %data.numMountPoints; %i++) + { + if (%obj.getMountNodeObject(%i) == %mounted) + { + %node = %i; + break; + } + } + if (%node == -1) + { + warning("Couldn't find object mount point"); + return "0 0 1"; + } + + if (%node == 3 || %node == 2) + { + return "-1 0 1"; + } + else if (%node == 5 || %node == 4) + { + return "1 0 1"; + } + else + { + return "0 0 1"; + } +} + +//************************************************************** +// Vehicle pickup +//************************************************************** + +datablock ShapeBaseImageData(FFTmountImage) +{ + className = "WeaponImage"; + shapeFile = "vehicle_land_mpbase.dts"; + item = Disc; + offset = "0 0 -6"; + emap = true; + mass = 500; +}; + +datablock ShapeBaseImageData(PanzermountImage) +{ + className = "WeaponImage"; + shapeFile = "vehicle_grav_tank.dts"; + item = Disc; + offset = "0 0 -6"; + emap = true; + mass = 325; +}; + +datablock ShapeBaseImageData(WC1Image) +{ + className = "WeaponImage"; + shapeFile = "vehicle_grav_scout.dts"; + item = Disc; + offset = "0 0 -5"; // L/R - F/B - T/B + emap = true; + mass = 50; +}; + +datablock ShapeBaseImageData(WC2Image) +{ + className = "WeaponImage"; + shapeFile = "vehicle_grav_scout.dts"; + item = Disc; + offset = "0 4 -5"; // L/R - F/B - T/B + emap = true; + mass = 50; +}; + +function HAPCflyer::onTrigger(%data, %obj, %trigger, %state) +{ + if(!%obj.hasVEH && %state == 1 && %trigger == 0) + { + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType; + %dir = %obj.getTransform(); + %dir = getWord(%dir, 5); + if(%dir > 0) + %dir *= -1; + if(vectorDot("0 0 -10", "0 0 "@ %dir) < 9) + return; + %vector = vectorAdd(%obj.getWorldBoxCenter(), "0 0 -10"); + %searchResult = containerRayCast(%obj.getWorldBoxCenter(), %vector, %mask, %obj); + if(%searchResult) + { + %searchObj = GetWord(%searchResult, 0); + if(%searchObj.getClassName() $= "wheeledVehicle"){ + %searchObj.setTransform(vectorAdd(%searchObj.getPosition(), "0 0 10000")); + %searchObj.setCloaked(true); + %obj.mountImage(FFTmountImage, 6); + %searchObj.setFrozenState(true); + %obj.hasVEH = true; + %obj.VEHmounted = %searchObj; + } + + else if(%searchObj.getClassName() $= "HoverVehicle"){ + %searchObj.setTransform(vectorAdd(%searchObj.getPosition(), "0 0 10000")); + %searchObj.setCloaked(true); + %searchObj.setFrozenState(true); + %obj.hasVEH = true; + %obj.VEHmounted = %searchObj; + if(%searchObj.getDataBlock().getName() $= "ScoutVehicle") + %obj.mountImage(WC1Image, 6); + else + %obj.mountImage(PanzermountImage, 6); + } + } + } + else if(%obj.hasVEH && %state == 1 && %trigger == 0) + { + %dir = %obj.getTransform(); + %dir = getWord(%dir, 5); + if(%dir > 0) + %dir *= -1; + //echo(vectorDot("0 0 -10", "0 0 "@ %dir)); + if(vectorDot("0 0 -10", "0 0 "@ %dir) < 9) + return; + %mask = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::TerrainObjectType; + %vector = vectorAdd(%obj.getWorldBoxCenter(), "0 0 -15"); + %searchResult = containerRayCast(%obj.getWorldBoxCenter(), %vector, %mask, %obj); + %vec = %Obj.getvelocity(); + if(%searchResult) + { + return; + } + if (%obj.VEHmounted !$= "") + { + %obj.VEHmounted.setFrozenState(false); + %obj.VEHmounted.setTransform(%obj.getTransform()); + %vector = vectorAdd("0 0 -13", %obj.getWorldBoxCenter()); + %obj.VEHmounted.setTransform(%vector); + %obj.VEHmounted.applyImpulse(%obj.VEHmounted.getPosition(),vectorScale(%vec,%obj.VEHmounted.getDataBlock().mass)); + %obj.VEHmounted.setCloaked(false); + %obj.unMountImage(6); + %obj.unMountObject(%obj.MPBstaticMount); + %obj.hasVEH = false; + %obj.VEHmounted = ""; + %obj.canpickupWC = false; + } + } + else if (%trigger ==4) // Throw flare + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(5, false); + case 1: + if (%obj.inv[PlasmaAmmo] > 0) + { + %obj.fireWeapon = true; + %obj.setImageTrigger(5, true); + %obj.decInventory(PlasmaAmmo, 1); + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = "0 0 -1"; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + + FlareSet.add(%p); + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(6000, "delete"); + // miscellaneous grenade-throwing cleanup stuff + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } + } + } + %up = %obj.getUpVector(); + if(%state == 1 && %trigger == 5 && vectorDist(%up,"0 0 1") <= 0.3){ + %frd = %obj.getForwardVector(); + %right = vectorNormalize(vectorSub(%obj.getEdge("1 0 0"),%obj.getEdge("-1 0 0"))); + for(%i = 2; %i < 6; %i++){ + if(%obj.getMountNodeObject(%i)){ + %plyr = %obj.getMountNodeObject(%i); + %plyr.unmount(); + %plyr.setPosition("0 0 1000"); + %vec = vectorAdd(vectorScale(%right,getWord($PodPos[%i],0)),vectorScale(%frd,getWord($PodPos[%i],1))); + %pos = vectorAdd(%obj.getPosition(),%vec); + %pod = MakeDropPod(vectorAdd(%pos,"0 0 -15"),%obj.team); + slowDropPod(%pod); + // Eolk - was wrong. Didn't mount right. +// %pod.schedule(10, "mountObject", %plyr, 0); + %plyr.getDatablock().schedule(10, "onCollision", %plyr, %pod, 0); + commandToClient(%plyr.client, 'setHudMode', 'Standard', "", 0); + } + } + } +} + +$PodPos[2] = "-3 7"; +$PodPos[3] = "-3 3"; +$PodPos[4] = "3 3"; +$PodPos[5] = "3 7"; + +function HAPCflyer::onDestroyed(%data, %obj, %prevState){ + %vec = %Obj.getvelocity(); + if (%obj.VEHmounted !$= "") + { + %obj.VEHmounted.setFrozenState(false); + %obj.VEHmounted.setTransform(%obj.getTransform()); + %vector = vectorAdd("0 0 -13", %obj.getWorldBoxCenter()); + %obj.VEHmounted.setTransform(%vector); + %obj.VEHmounted.applyImpulse(%obj.VEHmounted.getPosition(),vectorScale(%vec,%obj.VEHmounted.getDataBlock().mass)); + %obj.VEHmounted.setCloaked(false); + %obj.unMountImage(6); + %obj.unMountObject(%obj.MPBstaticMount); + %obj.hasVEH = false; + %obj.VEHmounted = ""; + %obj.canpickupWC = false; + if(%obj.hasWC){ + %obj.WCmounted.setFrozenState(false); + %obj.WCmounted.setTransform(%obj.getTransform()); + %vector = vectorAdd("0 6 -13", %obj.getWorldBoxCenter()); + %obj.WCmounted.setTransform(%vector); + %obj.WCmounted.applyImpulse(%obj.MPBmounted.getPosition(), vectorScale(%vec,ScoutVehicle.mass)); + %obj.WCmounted.setCloaked(false); + %obj.unMountImage(7); + %obj.unMountObject(%obj.MPBstaticMount); + %obj.hasWC = false; + %obj.WCmounted = ""; + } + } + Parent::onDestroyed(%data, %obj, %prevState); +} + +function HAPCflyer::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_hawk.cs b/Scripts/Vehicles/vehicle_hawk.cs new file mode 100644 index 0000000..ea5727f --- /dev/null +++ b/Scripts/Vehicles/vehicle_hawk.cs @@ -0,0 +1,414 @@ +//************************************************************** +// SHRIKE SCOUT FLIER +//************************************************************** +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(HawkFlyer) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_air_scout_debris.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = VehicleExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + maxDamage = 1.40; + destroyedLevel = 1.40; + + isShielded = true; + energyPerDamagePoint = 160; + maxEnergy = 280; // Afterburner and any energy weapon pool + rechargeRate = 0.8; + + minDrag = 30; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 15; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 300; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.95; // Dampen control input so you don't` whack out at very slow speeds + + // Maneuvering + maxSteeringAngle = 5; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 3000; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 1200; // Steering jets (force applied when you move the mouse) + steeringRollForce = 400; // Steering jets (how much you heel over when you turn) + rollForce = 4; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 5; // Height off the ground at rest + createHoverHeight = 3; // Height off the ground when created + maxForwardSpeed = 100; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 2000; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 28; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 0.0; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 3.0; + + // Rigid body + mass = 150; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 10; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 15; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = LightDamageSmoke; + damageEmitter[1] = HeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + // + max[chaingunAmmo] = 1000; + + minMountDist = 4; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'Hawk'; + targetTypeTag = 'Interceptor'; + sensorData = AWACPulseSensor; + sensorRadius = AWACPulseSensor.detectRadius; + sensorColor = "255 194 9"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + +}; + + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock TracerProjectileData(HawkChaingunBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.1; + directDamageType = $DamageType::Bullet; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + + kickBackStrength = 0.0; + sound = ChaingunProjectile; + + dryVelocity = 425.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 10.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.10; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +datablock ShapeBaseImageData(HawkChaingunPairImage) +{ + className = WeaponImage; + shapeFile = "weapon_chaingun.dts"; + projectile = HawkChaingunBullet; + projectileType = TracerProjectile; + mountPoint = 10; + offset = "0.84 -0.52 1.8"; + rotation = "1 0 0 0"; + + usesEnergy = true; + useMountEnergy = true; + minEnergy = 3; + fireEnergy = 1; + fireTimeout = 125; + + + casing = ShellDebris; + shellExitDir = "0 -0.5 1.0"; + shellExitOffset = "0 -0.56 -0.11"; + shellExitVariance = 15.0; + shellVelocity = 5.0; + + projectileSpread = 6.0 / 1000.0; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = ChaingunSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.3; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; +// stateSound[3] = ChaingunSpinupSound; + // + stateTimeoutValue[3] = 0.05; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + //stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + // + stateTimeoutValue[4] = 0.09; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; +// stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 0.05; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; +// stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.3; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(HawkChaingunImage) : HawkChaingunPairImage +{ +//*wingtips** offset = "-2.04 -0.52 -0.05"; + offset = "-1.04 -0.52 1.8"; + rotation = "1 0 0 0"; + stateScript[3] = "onTriggerDown"; + stateScript[5] = "onTriggerUp"; + stateScript[6] = "onTriggerUp"; +}; + +datablock ShapeBaseImageData(HawkChaingunParam) +{ + mountPoint = 2; + shapeFile = "weapon_chaingun.dts"; + + projectile = HawkChaingunBullet; + projectileType = TracerProjectile; +}; + +//********* FUNCTIONS *********** + +function HawkFlyer::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.mountImage(HawkChaingunParam, 0); + %obj.mountImage(HawkChaingunImage, 2); + %obj.mountImage(HawkChaingunPairImage, 3); + %obj.nextWeaponFire = 2; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); +} + +function HawkFlyer::playerMounted(%data, %obj, %player, %node) +{ + // AdminFighter 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 ); +} + +function HawkFlyer::onTrigger(%data, %obj, %trigger, %state) +{ + // data = AdminFighterFlyer datablock + // obj = AdminFighterFlyer object number + // trigger = 0 for "fire", 1 for "jump", 3 for "thrust" + // state = 1 for firing, 0 for not firing + if(%trigger == 0) + { + switch (%state) { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + case 1: + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + } + } +} + +function HawkFlyer::playerDismounted(%data, %obj, %player) +{ + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function HawkChaingunImage::onFire(%data,%obj,%slot) +{ + // obj = AdminFighterFlyer object number + // slot = 2 + + Parent::onFire(%data,%obj,%slot); +// %obj.nextWeaponFire = 3; +// schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} + +function HawkChaingunPairImage::onFire(%data,%obj,%slot) +{ + // obj = ScoutFlyer object number + // slot = 3 + + Parent::onFire(%data,%obj,%slot); +// %obj.nextWeaponFire = 2; +// schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} +function HawkChaingunImage::onTriggerDown(%this, %obj, %slot) +{ +} + +function HawkChaingunImage::onTriggerUp(%this, %obj, %slot) +{ +} + +function HawkChaingunImage::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function HawkChaingunPairImage::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function HawkChaingunImage::onUnmount(%this,%obj,%slot) +{ +} + +function HawkChaingunPairImage::onUnmount(%this,%obj,%slot) +{ +} + diff --git a/Scripts/Vehicles/vehicle_mpb.cs b/Scripts/Vehicles/vehicle_mpb.cs new file mode 100644 index 0000000..4faddc9 --- /dev/null +++ b/Scripts/Vehicles/vehicle_mpb.cs @@ -0,0 +1,288 @@ +//************************************************************** +// JERICHO FORWARD BASE (Mobile Point Base) +//************************************************************** +//************************************************************** +// SOUNDS +//************************************************************** +datablock EffectProfile(MPBEngineEffect) +{ + effectname = "vehicles/mpb_thrust"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(MPBThrustEffect) +{ + effectname = "vehicles/mpb_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock AudioProfile(MPBEngineSound) +{ + filename = "fx/vehicles/mpb_thrust.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = MPBEngineEffect; +}; + +datablock AudioProfile(MPBThrustSound) +{ + filename = "fx/vehicles/mpb_boost.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = MPBThrustEffect; +}; + +datablock AudioProfile(MobileBaseDeploySound) +{ + filename = "fx/vehicles/MPB_deploy.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(MobileBaseUndeploySound) +{ + filename = "fx/vehicles/MPB_undeploy_turret.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(MobileBaseTurretDeploySound) +{ + filename = "fx/vehicles/MPB_deploy_turret.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(MobileBaseTurretUndeploySound) +{ + filename = "fx/vehicles/MPB_undeploy_turret.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(MobileBaseStationUndeploySound) +{ + filename = "fx/vehicles/MPB_close_lid.wav"; + description = AudioClose3d; + preload = true; +}; + + +//************************************************************** +// LIGHTS +//************************************************************** +datablock RunningLightData(MPBLight1) +{ + pointSize = 3.0; + pointColor = "1.0 1.0 1.0 0.3"; + pointNodeName = "Headlight_node01"; + texture = "special/expFlare"; +}; + +datablock RunningLightData(MPBLight2) +{ + pointSize = 3.0; + pointColor = "1.0 1.0 1.0 0.3"; + pointNodeName = "Headlight_node02"; + texture = "special/expFlare"; +}; + + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** +datablock SensorData(MPBDeployedSensor) : VehiclePulseSensor +{ + jams = true; + jamsOnlyGroup = true; + jamsUsingLOS = false; + jamRadius = 50; +}; + +datablock WheeledVehicleData(MobileBaseVehicle) : MPBDamageProfile +{ + spawnOffset = "0 0 1.0"; + renderWhenDestroyed = false; + canControl = true; + catagory = "Vehicles"; + shapeFile = "vehicle_land_mpbase.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_land_mpbase.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 20.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + + cantAbandon = 1; + cantTeamSwitch = 1; + + cameraMaxDist = 20; + cameraOffset = 6; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 1.25; + explosionRadius = 20.0; + + maxSteeringAngle = 0.3; // 20 deg. + + // Used to test if the station can deploy + stationPoints[1] = "-2.3 -7.38703 -0.65"; + stationPoints[2] = "-2.3 -11.8 -0.65"; + stationPoints[3] = "0 -7.38703 -0.65"; + stationPoints[4] = "0 -11.8 -0.65"; + stationPoints[5] = "2.3 -7.38703 -0.65"; + stationPoints[6] = "2.3 -11.8 -0.65"; + + // Rigid Body + mass = 2000; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 10; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 25; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 24; + speedDamageScale = 0.025; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 15; + collDamageMultiplier = 0.03; + + // Engine + engineTorque = 7.0 * 745; + breakTorque = 7.0 * 745; + maxWheelSpeed = 20; + + // Springs + springForce = 8000; + springDamping = 1300; + antiSwayForce = 6000; + staticLoadScale = 2; + + // Tires + tireRadius = 1.6; + tireFriction = 10.0; + tireRestitution = 0.5; + tireLateralForce = 3000; + tireLateralDamping = 400; + tireLateralRelaxation = 1; + tireLongitudinalForce = 12000; + tireLongitudinalDamping = 600; + tireLongitudinalRelaxation = 1; + tireEmitter = TireEmitter; + + // + maxDamage = 6.5; + destroyedLevel = 6.5; + + HDAddMassLevel = 5.0; + HDMassImage = APCHDMassImage; + + isShielded = false; + energyPerDamagePoint = 125; + maxEnergy = 600; + jetForce = 2800; + minJetEnergy = 60; + jetEnergyDrain = 2.75; + rechargeRate = 1.0; + + jetSound = MPBThrustSound; + engineSound = MPBEngineSound; + squeelSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 8.0; + hardSplashSoundVelocity = 12.0; + exitSplashSoundVelocity = 8.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterHardSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 3; + + damageEmitter[0] = LightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "3.0 0.5 0.0 "; + damageEmitterOffset[1] = "-3.0 0.5 0.0 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 2; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundMPBIcon; + cmdMiniIconName = "commander/MiniIcons/com_mpb_grey"; + targetNameTag = 'Jericho'; + targetTypeTag = 'MPB'; + sensorData = VehiclePulseSensor; + + checkRadius = 7.5225; + + observeParameters = "1 12 12"; + + runningLight[0] = MPBLight1; + runningLight[1] = MPBLight2; + + shieldEffectScale = "0.85 1.2 0.7"; + + replaceTime = 120; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock TurretData(MobileTurretBase) +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_mpb.dts"; + preload = true; + + mass = 1.0; // Not really relevant + + maxDamage = MobileBaseVehicle.maxDamage; + destroyedLevel = MobileBaseVehicle.destroyedLevel; + + thetaMin = 15; + thetaMax = 140; + + energyPerDamagePoint = 33; + inheritEnergyFromMount = true; + firstPersonOnly = true; + + sensorData = AWACSensor; + sensorRadius = AWACSensor.detectRadius; + sensorColor = "255 194 9"; + cmdCategory = "Tactical"; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + targetNameTag = 'Jericho'; + targetTypeTag = 'Turret'; + + canControl = true; +}; \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_s11.cs b/Scripts/Vehicles/vehicle_s11.cs new file mode 100644 index 0000000..19f1882 --- /dev/null +++ b/Scripts/Vehicles/vehicle_s11.cs @@ -0,0 +1,502 @@ +//************************************************************** +// S11 Recon Drone +//************************************************************** +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(S11) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "nexuscap.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = false; + isProtectedMountPoint[1] = false; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 10.0; + + maxDamage = 1.2; + destroyedLevel = 1.2; + + HDAddMassLevel = 1.0; + HDMassImage = LflyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 1200; // Afterburner and any energy weapon pool + rechargeRate = 4; + + minDrag = 22; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 100; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 0; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0; // Dampen control input so you don't` whack out at very slow speeds + + + // Maneuvering + maxSteeringAngle = 4.5; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 3500; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 300; // Steering jets (force applied when you move the mouse) + steeringRollForce = 2000; // Steering jets (how much you heel over when you turn) + rollForce = 7; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 2.5; // Height off the ground at rest + createHoverHeight = 2; // Height off the ground when created + maxForwardSpeed = 120; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 2500; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 7; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 1.25; + + // Rigid body + mass = 90; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 150; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MeLightDamageSmoke; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.65; + damageLevelTolerance[1] = 0.8; + numDmgEmitterAreas = 1; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'S11'; + targetTypeTag = 'Recon Drone'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; + + shieldEffectScale = "0.937 1.125 0.60"; + + replaceTime = 90; + + max[AALauncherAmmo] = 1; +}; + +datablock StaticShapeData(S11wing) : StaticShapeDamageProfile { + shapeFile = "deploy_inventory.dts"; + mass = 1.0; + repairRate = 0; + dynamicType = $TypeMasks::StaticShapeObjectType; + heatSignature = 0; +}; + +datablock StaticShapeData(S11sensor) : StaticShapeDamageProfile { + shapeFile = "deploy_sensor_pulse.dts"; + mass = 1.0; + repairRate = 0; + dynamicType = $TypeMasks::StaticShapeObjectType; + heatSignature = 0; +}; + +//------------------------------------- +// CHOPPER BELLY TURRET CHARACTERISTICS +//------------------------------------- + +datablock TurretData(S11Turret) : ShrikeDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_belly_base.dts"; //turret_sentry.dts + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingBomberIcon; + cmdMiniIconName = "commander/MiniIcons/com_turret_grey"; + + mass = 1.0; // Not really relevant + repairRate = 0; + maxDamage = S11.maxDamage; + destroyedLevel = S11.destroyedLevel; + + thetaMin = 90; + thetaMax = 180; + + heatSignature = 0.0; + + // capacitor + maxCapacitorEnergy = 500; + capacitorRechargeRate = 5.0; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + targetNameTag = 'S11'; + targetTypeTag = 'Turret'; + + max[AALauncherAmmo] = 1; +}; + +datablock TurretImageData(S11TL) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 0; + offset = "0 0 0"; + + projectile = GunshipTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + usesEnergy = true; + useMountEnergy = true; + useCapacitor = false; + minEnergy = 0; + fireEnergy = 1.0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateTimeoutValue[0] = 0.1; + stateTransitionOnTimeout[0] = "Ready"; + + stateName[1] = "Ready"; + stateTransitionOnTriggerDown[1] = "Fire"; + + stateName[2] = "Fire"; + stateEnergyDrain[2] = 0; + stateFire[2] = true; + stateScript[2] = "onFire"; + stateTransitionOnTriggerUp[2] = "Deconstruct"; + + stateName[3] = "Deconstruct"; + stateScript[3] = "onDecon"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Ready"; +}; + +datablock TurretImageData(S11MissileImage) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + item = MissileLauncher; + ammo = AALauncherAmmo; + projectile = HammerATMissile; + projectileType = SeekerProjectile; + + mountPoint = 1; + + usesEnergy = false; + useMountEnergy = true; + fireEnergy = 0.0; + minEnergy = 0.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 5.0; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock TurretImageData(S11TurretParam) +{ + mountPoint = 3; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 -1.5"; + + projectile = snipergunBullet; + projectileType = TracerProjectile; + + activationMS = 10; + deactivateDelayMS = 15; + thinkTimeMS = 20; + degPerSecTheta = 50; + degPerSecPhi = 80; + attackRadius = 1200; +}; + +function S11::onAdd(%this, %obj){ + Parent::onAdd(%this, %obj); + + %turret = TurretData::create(S11Turret); + MissionCleanup.add(%turret); + %turret.team = %obj.team; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 10); + %turret.mountImage(S11TurretParam, 0); + %turret.mountImage(S11TL, 2); + %turret.mountImage(S11MissileImage, 3); + %turret.setInventory(AALauncherAmmo, 1); + %obj.turret = %turret; + %obj.turretobject = %turret; + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %turret.vehicleMounted = %obj; + %turret.setAutoFire(false); + %turret.mountobj = %obj; + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); + + %obj.startFade(0,10,1); + + %wing = new StaticShape() + { + scale = "2.5 4 0.12"; + dataBlock = "S11wing"; + }; + MissionCleanup.add(%wing); + %obj.mountObject(%wing, 1); + %wing.vehicleMounted = %obj; + %wing.deploy(); + + %sensor = new StaticShape() + { + scale = "1.5 1.5 0.5"; + dataBlock = "S11sensor"; + }; + MissionCleanup.add(%sensor); + %obj.mountObject(%sensor, 2); + %sensor.vehicleMounted = %obj; + %sensor.deploy(); + + schedule(5000, 0, "S11TurretAttackCheck",%obj); +} + +function S11::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(10); + if (!%turret) + return; + + if (%client = %turret.getControllingClient()) + { + %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(500, delete); + + %body = %obj.getMountNodeObject(2); + if (isObject(%body)) + %body.schedule(1000, delete); + + %wing = %obj.getMountNodeObject(1); + if (isObject(%wing)) + %wing.schedule(2000, delete); + $teamRepCredits[%obj.team] += getWord($commsatPurchase[1],1); + $teamUsedCredits[%obj.team] -= getWord($commsatPurchase[1],1); +} + +function S11Turret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(3, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(3, true); + else + %obj.setImageTrigger(3, false); + } + + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function S11MissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + if(isObject(%obj.TLB)){ + %p.setObjectTarget(%obj.TLB); + %obj.TLB.setPosition(%obj.target.getPosition()); + } + %obj.lastmissile = %p; +} + +function S11TL::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + schedule(400,0,"S11TLmakebeacon",%obj,%p); +} + +function S11TLmakebeacon(%obj,%p){ + %p.setTarget(%obj.team); + %p.beacon = new BeaconObject() { + dataBlock = "SubBeacon"; + beaconType = "vehicle"; + position = %p.getTargetPoint(); + }; + %beacon.team = %obj.team; + %beacon.setTarget(%obj.team); + %obj.TLB = %p.beacon; + %obj.TL = %p; + %p.turret = %obj; +} + +function S11TL::onDecon(%data,%obj,%slot){ + %obj.TL.delete(); + removeTLBcheck(%obj.TLB,%obj.lastmissile); +} + +function removeTLBcheck(%TLB,%p){ + if(!isObject(%TLB)) + return; + if(!isObject(%p)){ + %TLB.delete(); + return; + } + schedule(500, 0, "removeTLBcheck", %TLB, %p); +} + +function S11TurretAttackCheck(%obj){ + if(!isObject(%obj)) + return; + + if(%obj.mode $= "RECON"){ + %valid = 0; + %TargetSearchMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType; + InitContainerRadiusSearch(%obj.getPosition(),1200,%TargetSearchMask); + while ((%potentialTarget = ContainerSearchNext()) != 0) { + if (%potentialtarget && %valid != 1) { + %PTT = %potentialtarget.team; + if(%PTT $= "") + %PTT = %obj.team; + if(!(%PTT == %obj.team)){ + %valid = 1; + %target = %potentialtarget; + } + } + } + if(%valid == 1){ + %obj.turretobject.setTargetObject(%target); + %obj.turretobject.aquireTime = getSimTime(); + %obj.turretobject.setImageTrigger(2,true); + } + else{ + %obj.turretobject.setImageTrigger(2,false); + %obj.turretObject.clearTarget(); + } + } + schedule(250, 0, "S11TurretAttackCheck",%obj); +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_shrike.cs b/Scripts/Vehicles/vehicle_shrike.cs new file mode 100644 index 0000000..3260a05 --- /dev/null +++ b/Scripts/Vehicles/vehicle_shrike.cs @@ -0,0 +1,732 @@ +//************************************************************** +// SHRIKE SCOUT FLIER +//************************************************************** + +datablock SensorData(combatSensor) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 500; +}; + +//************************************************************** +// SOUNDS +//************************************************************** +datablock EffectProfile(ScoutFlyerThrustEffect) +{ + effectname = "vehicles/shrike_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ScoutFlyerEngineEffect) +{ + effectname = "vehicles/shrike_engine"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ShrikeBlasterFireEffect) +{ + effectname = "vehicles/shrike_blaster"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock AudioProfile(ScoutFlyerThrustSound) +{ + filename = "fx/vehicles/shrike_boost.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = ScoutFlyerThrustEffect; +}; + +datablock AudioProfile(ScoutFlyerEngineSound) +{ + filename = "fx/vehicles/shrike_engine.wav"; + description = AudioDefaultLooping3d; + preload = true; +}; + +datablock AudioProfile(ShrikeBlasterFire) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefault3d; + preload = true; + effect = ScoutFlyerEngineEffect; +}; + +datablock AudioProfile(ShrikeBlasterProjectile) +{ + filename = "fx/weapons/shrike_blaster_projectile.wav"; + description = ProjectileLooping3d; + preload = true; + effect = ShrikeBlasterFireEffect; +}; + +datablock AudioProfile(ShrikeBlasterDryFireSound) +{ + filename = "fx/weapons/chaingun_dryfire.wav"; + description = AudioClose3d; + preload = true; +}; + +//************************************************************** +// LIGHTS +//************************************************************** +datablock RunningLightData(ShrikeLight1) +{ + type = 1; + radius = 2.0; + length = 3.0; + color = "1.0 1.0 1.0 1.0"; + direction = "0.0 1.0 -1.0 "; + offset = "0.0 0.0 -0.5"; + texture = "special/lightcone04"; +}; + +datablock RunningLightData(ShrikeLight2) +{ + radius = 1.5; + color = "1.0 1.0 1.0 0.3"; + direction = "0.0 0.0 -1.0"; + offset = "0.0 0.8 -1.2"; + texture = "special/headlight4"; +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock FlyingVehicleData(ScoutFlyer) : ShrikeDamageProfile +{ + spawnOffset = "0 0 2"; + canControl = false; + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = false; + computeCRC = true; + + debrisShapeName = "vehicle_air_scout.dts"; + debris = MeShapeDebris; + renderWhenDestroyed = false; + + drag = 0.15; + density = 1.0; + + mountPose[0] = sitting; + numMountPoints = 1; + isProtectedMountPoint[0] = false; + cameraMaxDist = 15; + cameraOffset = 2.5; + cameraLag = 0.9; + explosion = MeVehicleExplosion; + explosionDamage = 1.0; + explosionRadius = 10.0; + + maxDamage = 2.5; + destroyedLevel = 2.5; + + HDAddMassLevel = 1.9; + HDMassImage = LflyerHDMassImage; + + isShielded = false; + energyPerDamagePoint = 0; + maxEnergy = 1200; // Afterburner and any energy weapon pool + rechargeRate = 4; + + minDrag = 22; // Linear Drag (eventually slows you down when not thrusting...constant drag) + rotationalDrag = 900; // Anguler Drag (dampens the drift after you stop moving the mouse...also tumble drag) + + maxAutoSpeed = 50; // Autostabilizer kicks in when less than this speed. (meters/second) + autoAngularForce = 400; // Angular stabilizer force (this force levels you out when autostabilizer kicks in) + autoLinearForce = 1; // Linear stabilzer force (this slows you down when autostabilizer kicks in) + autoInputDamping = 0.8; // Dampen control input so you don't` whack out at very slow speeds + + + // Maneuvering + maxSteeringAngle = 4.5; // Max radiens you can rotate the wheel. Smaller number is more maneuverable. + horizontalSurfaceForce = 6; // Horizontal center "wing" (provides "bite" into the wind for climbing/diving and turning) + verticalSurfaceForce = 4; // Vertical center "wing" (controls side slip. lower numbers make MORE slide.) + maneuveringForce = 5250; // Horizontal jets (W,S,D,A key thrust) + steeringForce = 675; // Steering jets (force applied when you move the mouse) + steeringRollForce = 3000; // Steering jets (how much you heel over when you turn) + rollForce = 1; // Auto-roll (self-correction to right you after you roll/invert) + hoverHeight = 2.5; // Height off the ground at rest + createHoverHeight = 1; // Height off the ground when created + maxForwardSpeed = 165; // speed in which forward thrust force is no longer applied (meters/second) + + // Turbo Jet + jetForce = 2500; // Afterburner thrust (this is in addition to normal thrust) + minJetEnergy = 40; // Afterburner can't be used if below this threshhold. + jetEnergyDrain = 10; // Energy use of the afterburners (low number is less drain...can be fractional) // Auto stabilize speed + vertThrustMultiple = 1.25; + + // Rigid body + mass = 150; // Mass of the vehicle + bodyFriction = 0; // Don't mess with this. + bodyRestitution = 0.5; // When you hit the ground, how much you rebound. (between 0 and 1) + minRollSpeed = 0; // Don't mess with this. + softImpactSpeed = 14; // Sound hooks. This is the soft hit. + hardImpactSpeed = 25; // Sound hooks. This is the hard hit. + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 20; // If hit ground at speed above this then it's an impact. Meters/second + speedDamageScale = 0.06; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23.0; + collDamageMultiplier = 0.02; + + // + minTrailSpeed = 150; // The speed your contrail shows up at. + trailEmitter = ContrailEmitter; + forwardJetEmitter = FlyerJetEmitter; + downJetEmitter = FlyerJetEmitter; + + // + jetSound = ScoutFlyerThrustSound; + engineSound = ScoutFlyerEngineSound; + softImpactSound = SoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 15.0; + hardSplashSoundVelocity = 20.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 4.0; + dustHeight = 1.0; + + damageEmitter[0] = MeLightDamageSmoke; + damageEmitter[1] = MeHeavyDamageSmoke; + damageEmitter[2] = MeDamageBubbles; + damageEmitterOffset[0] = "0.0 -3.0 0.0 "; + damageLevelTolerance[0] = 0.4; + damageLevelTolerance[1] = 0.75; + numDmgEmitterAreas = 1; + + // + max[chaingunAmmo] = 1500; + max[MissileLauncherAmmo] = 4; + max[MortarAmmo] = 3; + + minMountDist = 7; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDFlyingScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_scout_grey"; + targetNameTag = 'F39 RaptorII'; + targetTypeTag = 'Interceptor'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5; + observeParameters = "1 10 10"; + + runningLight[0] = ShrikeLight1; +// runningLight[1] = ShrikeLight2; + + shieldEffectScale = "0.937 1.125 0.60"; + + numWeapons = 3; + + replaceTime = 90; + + max[plasmaammo] = 15; + flaretime = 250; + flarelife = 750; + flarechance = 0.5; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(Shrike_special_gun) +{ + doDynamicClientHits = true; + + directDamage = 0.175; + directDamageType = $DamageType::Bullet; + explosion = ChaingunExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.025; + damageRadius = 0.5; + radiusDamageType = $DamageType::Bullet; + + kickBackStrength = 5; + sound = ChaingunProjectile; + + dryVelocity = 1750.0; + wetVelocity = 1250.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 6000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.20; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(sidewinder) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 1.5; + damageRadius = 6.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 500; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.0; + + explosion = "MissileExplosion"; + velInheritFactor = 1.0; + + splash = MissileSplash; + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 15000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 12.0; + maxVelocity = 225.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 50.0; + acceleration = 100.0; + + proximityRadius = 4; + + terrainAvoidanceSpeed = 100; + terrainScanAhead = 50; + terrainHeightFail = 50; + terrainAvoidanceRadius = 150; + + useFlechette = true; + flechetteDelayMs = 225; + casingDeb = FlechetteDebris; +}; + + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock ShapeBaseImageData(ScoutChaingunPairImage) +{ + className = WeaponImage; + shapeFile = "turret_tank_barrelchain.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Shrike_special_gun; + projectileType = TracerProjectile; + mountPoint = 10; +//**original** offset = ".73 0 0"; + offset = "1.05 0.8 0.45"; + + projectileSpread = 1.0 / 1000.0; + + usesEnergy = false; + useMountEnergy = true; + // DAVEG -- balancing numbers below! + minEnergy = 1; + fireEnergy = 1; + fireTimeout = 125; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.05; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateTimeoutValue[3] = 0.01; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + //-------------------------------------- + stateName[4] = "Fire"; + stateSpinThread[4] = FullSpeed; + stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateSound[4] = ShrikeBlasterFire; + // IMPORTANT! The stateTimeoutValue below has been replaced by fireTimeOut + // above. + stateTimeoutValue[4] = 0.01; + stateTransitionOnTimeout[4] = "checkState"; + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + stateTimeoutValue[5] = 0.01; + stateWaitForTimeout[5] = false; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + //-------------------------------------- + stateName[6] = "EmptySpindown"; +// stateSound[6] = ChaingunSpindownSound; + stateSpinThread[6] = SpinDown; + stateTransitionOnAmmo[6] = "Ready"; + stateTimeoutValue[6] = 0.01; + stateTransitionOnTimeout[6] = "NoAmmo"; + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ShrikeBlasterDryFireSound; + stateTransitionOnTriggerUp[7] = "NoAmmo"; + stateTimeoutValue[7] = 0.25; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "checkState"; + stateTransitionOnTriggerUp[8] = "Spindown"; + stateTransitionOnNoAmmo[8] = "EmptySpindown"; + stateTimeoutValue[8] = 0.01; + stateTransitionOnTimeout[8] = "ready"; +}; + +datablock ShapeBaseImageData(ScoutChaingunImage) : ScoutChaingunPairImage +{ +//**original** offset = "-.73 0 0"; + offset = "-1.05 0.8 0.45"; + stateScript[3] = "onTriggerDown"; + stateScript[5] = "onTriggerUp"; + stateScript[6] = "onTriggerUp"; +}; + +datablock ShapeBaseImageData(ShrikeMissileImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = MissileLauncher; + ammo = MissileLauncherAmmo; + projectile = sidewinder; + projectileType = SeekerProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 5.0; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ShrikeBlasterDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock BombProjectileData(shrikeBomb) +{ + projectileShapeName = "bomb.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 3.5; + damageRadius = 20; + radiusDamageType = $DamageType::BomberBombs; + kickBackStrength = 2500; + + explosion = "VehicleBombExplosion"; + velInheritFactor = 1.0; + + grenadeElasticity = 0.25; + grenadeFriction = 0.4; + armingDelayMS = 100; + muzzleVelocity = 0.1; + drag = 0.05; + + minRotSpeed = "1.0 0.0 0.0"; + maxRotSpeed = "2.0 0.0 0.0"; + scale = "1.0 1.0 1.0"; + + sound = BomberBombProjectileSound; +}; + +datablock ShapeBaseImageData(ShrikeBombImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = Mortar; + ammo = MortarAmmo; + projectile = shrikeBomb; + projectileType = BombProjectile; + + mountPoint = 10; + offset = "0 -0 -0.15"; // L/R - F/B - T/B + + usesEnergy = false; + useMountEnergy = true; + minEnergy = 100; + fireEnergy = 100; + fireTimeout = 125; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = BomberBombFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = BomberBombDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(ScoutChaingunParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + + projectile = sidewinder; + projectileType = SeekerProjectile; + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 45; + seekTime = 0.25; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = 50; + useTargetAudio = $Bomber::useTargetAudio; +}; + +function shrikeMissileImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + + if(%obj.isdrone == 1) + %p.setObjectTarget(%obj.target); + else{ + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); + } +} + +function shrikebombImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); +} + +function scoutflyer::onDestroyed(%data, %obj, %prevState) +{ + if(%obj.lastPilot.lastVehicle == %obj) + if(%obj.getMountNodeObject(0) == %obj.lastPilot) + schedule(200, %obj.lastPilot, "scKillPilot", %obj.lastPilot, %obj.lastDamagedBy); + + Parent::onDestroyed(%data, %obj, %prevState); +} + +function scKillPilot(%player, %source) +{ + if(isObject(%player) && %player.getState() !$= "Dead") + %player.damage(%source, %player.position, %player.getDatablock().maxDamage, $DamageType::ShotDown); +} + +function scoutflyer::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 fighterdropflares(%obj,%time,%life,%chance){ + if(%obj.flaring != 1) + return; + if(%obj.inv[plasmaAmmo] <= 0){ + %obj.flaring = 0; + return; + } + %up = %obj.getUpVector(); + %frd = %obj.getForwardVector(); + %vec = vectorAdd(%frd,vectorscale(%up,-1)); + %vec = vectorNormalize(%vec); + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * 0.3; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * 0.3; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * 0.3; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + + %obj.decInventory(PlasmaAmmo, 1); + %p = new FlareProjectile() + { + dataBlock = FlareGrenadeProj; + initialDirection = %vector; + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + + %rnd = getRandom()*(1/%chance); + if(%rnd >= 1) + FlareSet.add(%p); + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(%life, "delete"); + schedule(%time,0,"FighterDropFlares",%obj,%time,%life,%chance); +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_spec_fx.cs b/Scripts/Vehicles/vehicle_spec_fx.cs new file mode 100644 index 0000000..caf7f8e --- /dev/null +++ b/Scripts/Vehicles/vehicle_spec_fx.cs @@ -0,0 +1,1196 @@ +//============================================================== +// MISCELLANEOUS VEHICLE SOUNDS AND EFFECTS +// +// - Sounds +// - Water Splash Effects +// - Tire Dust Particles +// - Lift-Off Particles +// - Small Heavy Damage Smoke +// - Heavy Damage Smoke +// - Light Heavy Damage Smoke +// - Vehicle Smoke Spike (for debris) +// - Vehicle Medium Explosion Smoke +// - Vehicle Large Ground Explosion Smoke +// - Debris Fire Particles +// - Debris Smoke Particles +// - Debris (pieces) +// - Explosions (including Shield Impacts) +// - Vehicle Sensors +// - Flier Contrails +// - Bomb Explosions +// +//============================================================== + +//-------------------------------------------------------------- +// SOUNDS +//-------------------------------------------------------------- + +datablock EffectProfile(SoftImpactEffect) +{ + effectname = "vehicles/crash_soft"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(HardImpactEffect) +{ + effectname = "vehicles/crash_hard"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(GravSoftImpactEffect) +{ + effectname = "vehicles/crash_ground_vehicle"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ChopperSound) +{ + filename = "fx/vehicles/Chopper_Blades.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = ChopperEffect; +}; + +datablock EffectProfile(ChopperMovement) +{ + filename = "fx/vehicles/Chopper_Movement.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = ChopperMEffect; +}; + +datablock AudioProfile(SoftImpactSound) +{ + filename = "fx/vehicles/crash_soft.wav"; + description = AudioClose3d; + preload = true; + effect = SoftImpactEffect; +}; + +datablock AudioProfile(HardImpactSound) +{ + filename = "fx/vehicles/crash_hard.wav"; + description = AudioExplosion3d; + preload = true; + effect = HardImpactEffect; +}; + +datablock AudioProfile(GravSoftImpactSound) +{ + filename = "fx/vehicles/crash_grav_soft.wav"; + description = AudioClose3d; + preload = true; + effect = GravSoftImpactEffect; +}; + +datablock AudioProfile(BombExplosionSound) +{ + filename = "fx/vehicles/bomber_bomb_impact.wav"; + description = AudioBomb3d; + preload = true; +}; + + +//-------------------------------------------------------------- +// WATER SPLASH EFFECTS +//-------------------------------------------------------------- + +datablock ParticleData(VehicleFoamParticle) +{ + dragCoefficient = 2.0; + gravityCoefficient = -0.05; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1200; + lifetimeVarianceMS = 400; + 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] = 2; + sizes[1] = 4; + sizes[2] = 6; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(VehicleFoamEmitter) +{ + ejectionPeriodMS = 40; + periodVarianceMS = 0; + ejectionVelocity = 10.0; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 85; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "VehicleFoamParticle"; +}; + + +datablock ParticleData( VehicleFoamDropletsParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.2; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 800; + lifetimeVarianceMS = 300; + 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] = 8; + sizes[1] = 3; + sizes[2] = 0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( VehicleFoamDropletsEmitter ) +{ + ejectionPeriodMS = 34; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 60; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + particles = "VehicleFoamDropletsParticle"; +}; + +//-------------------------------------------------------------- +// TIRE DUST PARTICLES +//-------------------------------------------------------------- + +datablock ParticleData(TireParticle) +{ + dragCoefficient = 2.0; + gravityCoefficient = -0.1; + inheritedVelFactor = 0.1; + constantAcceleration = 0.0; + lifetimeMS = 6000; + lifetimeVarianceMS = 1000; + useInvAlpha = true; + spinRandomMin = -45.0; + spinRandomMax = 45.0; + textureName = "particleTest"; + colors[0] = "0.46 0.36 0.26 0.6"; + colors[1] = "0.46 0.46 0.36 0.0"; + sizes[0] = 1.5; + sizes[1] = 7.00; +}; + +datablock ParticleEmitterData(TireEmitter) +{ + ejectionPeriodMS = 160; + periodVarianceMS = 20; + ejectionVelocity = 2; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 20; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "TireParticle"; +}; + +//-------------------------------------------------------------- +// LIFTOFF PARTICLES +//-------------------------------------------------------------- + +datablock ParticleData(TankDust) +{ + dragCoefficient = 1.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 200; + 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] = 3.2; + sizes[1] = 4.6; + sizes[2] = 7.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(TankDustEmitter) +{ + ejectionPeriodMS = 12; + periodVarianceMS = 0; + ejectionVelocity = 20.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 90; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "TankDust"; +}; + +datablock ParticleData(LargeVehicleLiftoffDust) +{ + 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] = 3.2; + sizes[1] = 4.6; + sizes[2] = 7.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(LargeVehicleLiftoffDustEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + ejectionVelocity = 15.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 90; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "LargeVehicleLiftoffDust"; +}; + +datablock ParticleData(VehicleLiftoffDust) +{ + 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] = 1.2; + sizes[1] = 2.6; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(VehicleLiftoffDustEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + ejectionVelocity = 10.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 90; + thetaMax = 90; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "VehicleLiftoffDust"; +}; + +//-------------------------------------------------------------- +// Damage bubbles +//-------------------------------------------------------------- +datablock ParticleData(DamageBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.04; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 200; + useInvAlpha = false; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "special/bubbles"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.3 0.3 0.3 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.2; + sizes[1] = 0.8; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(DamageBubbles) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "DamageBubbleParticle"; +}; + +//-------------------------------------------------------------- +// SMALL HEAVY DAMAGE SMOKE +//-------------------------------------------------------------- + +datablock ParticleData(SmallHeavyDamageSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "particleTest"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.3 0.3 0.3 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.2; + sizes[1] = 0.8; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(SmallHeavyDamageSmoke) +{ + ejectionPeriodMS = 25; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "SmallHeavyDamageSmokeParticle"; +}; + +//-------------------------------------------------------------- +// HEAVY DAMAGE SMOKE +//-------------------------------------------------------------- + +datablock ParticleData(HeavyDamageSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "particleTest"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.3 0.3 0.3 0.7"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 1.2; + sizes[1] = 2.6; + sizes[2] = 4.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(HeavyDamageSmoke) +{ + ejectionPeriodMS = 20; + periodVarianceMS = 6; + ejectionVelocity = 4.0; + velocityVariance = 0.5; + ejectionOffset = 1.5; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "HeavyDamageSmokeParticle"; +}; + +//-------------------------------------------------------------- +// SMALL LIGHT DAMAGE SMOKE +//-------------------------------------------------------------- + +datablock ParticleData(SmallLightDamageSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "particleTest"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.5 0.5 0.5 0.4"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 0.8; + sizes[1] = 1.6; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(SmallLightDamageSmoke) +{ + ejectionPeriodMS = 40; + periodVarianceMS = 0; + ejectionVelocity = 3.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "SmallLightDamageSmokeParticle"; +}; + +//-------------------------------------------------------------- +// LIGHT DAMAGE SMOKE +//-------------------------------------------------------------- + +datablock ParticleData(LightDamageSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "particleTest"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.5 0.5 0.5 0.7"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 1.2; + sizes[1] = 2.6; + sizes[2] = 4.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(LightDamageSmoke) +{ + ejectionPeriodMS = 30; + periodVarianceMS = 6; + ejectionVelocity = 4.0; + velocityVariance = 0.5; + ejectionOffset = 1.5; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "LightDamageSmokeParticle"; +}; + +//-------------------------------------------------------------- +// VEHICLE SMOKE SPIKE (for debris) +//-------------------------------------------------------------- + +datablock ParticleData( VSmokeSpike ) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + colors[0] = "0.4 0.4 0.4 1.0"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.0; + sizes[1] = 1.0; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( VSmokeSpikeEmitter ) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 1; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "VSmokeSpike"; +}; + + +//-------------------------------------------------------------- +// VEHICLE MEDIUM EXPLOSION SMOKE +//-------------------------------------------------------------- + +datablock ParticleData(VehicleMESmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.5; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.5 1.0"; + colors[1] = "0.3 0.3 0.3 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 2.0; + sizes[1] = 6.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.3; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(VehicleMESmokeEMitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionVelocity = 6.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 250; + + particles = "VehicleMESmoke"; +}; + +//-------------------------------------------------------------- +// DEBRIS FIRE PARTICLES +//-------------------------------------------------------------- + +datablock ParticleData(DebrisFireParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.2; + inheritedVelFactor = 0.0; + + lifetimeMS = 350; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -160.0; + spinRandomMax = 160.0; + + animateTexture = true; + framesPerSec = 15; + + + animTexName[0] = "special/Explosion/exp_0016"; + animTexName[1] = "special/Explosion/exp_0018"; + animTexName[2] = "special/Explosion/exp_0020"; + animTexName[3] = "special/Explosion/exp_0022"; + animTexName[4] = "special/Explosion/exp_0024"; + animTexName[5] = "special/Explosion/exp_0026"; + animTexName[6] = "special/Explosion/exp_0028"; + animTexName[7] = "special/Explosion/exp_0030"; + animTexName[8] = "special/Explosion/exp_0032"; + + colors[0] = "1.0 0.7 0.5 1.0"; + colors[1] = "1.0 0.5 0.2 1.0"; + colors[2] = "1.0 0.25 0.1 0.0"; + sizes[0] = 0.5; + sizes[1] = 2.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(DebrisFireEmitter) +{ + ejectionPeriodMS = 20; + periodVarianceMS = 1; + + ejectionVelocity = 0.25; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 30.0; + + particles = "DebrisFireParticle"; +}; + +//-------------------------------------------------------------- +// DEBRIS SMOKE PARTICLES +//-------------------------------------------------------------- + +datablock ParticleData( DebrisSmokeParticle ) +{ + dragCoeffiecient = 4.0; + gravityCoefficient = -0.00; // rises slowly + inheritedVelFactor = 0.2; + + lifetimeMS = 1000; + lifetimeVarianceMS = 100; // ...more or less + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -50.0; + spinRandomMax = 50.0; + + colors[0] = "0.3 0.3 0.3 0.0"; + colors[1] = "0.3 0.3 0.3 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 2; + sizes[1] = 3; + sizes[2] = 5; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( DebrisSmokeEmitter ) +{ + ejectionPeriodMS = 25; + periodVarianceMS = 5; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.5; + + thetaMin = 10.0; + thetaMax = 30.0; + + useEmitterSizes = true; + + particles = "DebrisSmokeParticle"; +}; + +//-------------------------------------------------------------- +// DEBRIS (pieces) +//-------------------------------------------------------------- + +datablock EffectProfile(VehicleExplosionEffect) +{ + effectname = "explosions/vehicle_explosion"; + minDistance = 10; + maxDistance = 40; +}; + +datablock AudioProfile(VehicleExplosionSound) +{ + filename = "fx/explosions/vehicle_explosion.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = VehicleExplosionEffect; +}; + +datablock ExplosionData(DebrisExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + soundProfile = plasmaExpSound; + faceViewer = true; + explosionScale = "0.4 0.4 0.4"; +}; + +datablock DebrisData( VehicleFireballDebris ) +{ + emitters[0] = DebrisSmokeEmitter; + emitters[1] = DebrisFireEmitter; + + explosion = DebrisExplosion; + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 100.0; + lifetimeVariance = 30.0; + + numBounces = 0; + bounceVariance = 0; +}; + +datablock DebrisData( VSpikeDebris ) +{ + emitters[0] = VSmokeSpikeEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 0.3; + lifetimeVariance = 0.02; + +}; + +datablock DebrisData( ShapeDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.40; + friction = 0.5; + + lifetime = 25.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 1.0; + + velocity = 17.0; + velocityVariance = 7.0; + +}; + +//************************************************************** +// EXPLOSIONS +//************************************************************** + +datablock ExplosionData(VSpikeExplosion) +{ + debris = VSpikeDebris; + debrisThetaMin = 10; + debrisThetaMax = 170; + debrisNum = 15; + debrisNumVariance = 6; + debrisVelocity = 30.0; + debrisVelocityVariance = 7.0; +}; + +datablock ExplosionData(VehicleExplosion) +{ + explosionShape = "disc_explosion.dts"; + playSpeed = 1.5; + soundProfile = VehicleExplosionSound; + faceViewer = true; + + emitter[0] = VehicleMESmokeEmitter; + + debris = VehicleFireballDebris; + debrisThetaMin = 10; + debrisThetaMax = 80; + debrisNum = 3; + debrisNumVariance = 1; + debrisVelocity = 20.0; + debrisVelocityVariance = 5.0; + + subExplosion = VSpikeExplosion; + + shakeCamera = true; + camShakeFreq = "11.0 13.0 9.0"; + camShakeAmp = "40.0 40.0 40.0"; + camShakeDuration = 0.7; + camShakeRadius = 25.0; +}; + +//-------------------------------------------------------------- +// BOMB EXPLOSION +//-------------------------------------------------------------- +datablock ParticleData(VehicleBombExplosionParticle) +{ + dragCoefficient = 2; + gravityCoefficient = 0.2; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 900; + lifetimeVarianceMS = 225; + textureName = "particleTest"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 0.0"; + sizes[0] = 3; + sizes[1] = 5; +}; + +datablock ParticleEmitterData(VehicleBombExplosionEmitter) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 0; + ejectionVelocity = 32; + velocityVariance = 10; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "VehicleBombExplosionParticle"; +}; + +datablock ShockwaveData(VehicleBombShockwave) +{ + width = 6.0; + numSegments = 32; + numVertSegments = 6; + velocity = 16.0; + acceleration = 40.0; + lifetimeMS = 650; + height = 1.0; + verticalCurve = 0.5; + is2D = false; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "1.0 0.8 0.4 0.50"; + colors[1] = "1.0 0.6 0.3 0.25"; + colors[2] = "1.0 0.4 0.2 0.0"; + + mapToTerrain = true; + orientToNormal = false; + renderBottom = false; +}; + +datablock ExplosionData(VehicleBombSubExplosion1) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + + delayMS = 50; + + offset = 5.0; + + playSpeed = 1.5; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "3.0 3.0 3.0"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(VehicleBombSubExplosion2) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + + delayMS = 100; + + offset = 7.0; + + playSpeed = 1.1; + + sizes[0] = "5.0 5.0 5.0"; + sizes[1] = "8.0 8.0 8.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ExplosionData(VehicleBombSubExplosion3) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + + delayMS = 0; + + offset = 0.0; + + playSpeed = 0.9; + + + sizes[0] = "7.0 7.0 7.0"; + sizes[1] = "10.0 10.0 10.0"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(VehicleBombExplosion) +{ + soundProfile = BombExplosionSound; + particleEmitter = VehicleBombExplosionEmitter; + particleDensity = 250; + particleRadius = 1.25; + faceViewer = true; + + shockwave = VehicleBombShockwave; + shockwaveOnTerrain = true; + + subExplosion[0] = VehicleBombSubExplosion1; + subExplosion[1] = VehicleBombSubExplosion2; + subExplosion[2] = VehicleBombSubExplosion3; + + shakeCamera = true; + camShakeFreq = "5.0 7.0 5.0"; + camShakeAmp = "80.0 80.0 80.0"; + camShakeDuration = 1.0; + camShakeRadius = 30.0; +}; + +//-------------------------------------------------------------- +// FLIER CONTRAILS +//-------------------------------------------------------------- + +datablock ParticleData(ContrailParticle) +{ + dragCoefficient = 1.5; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.6 0.6 0.6 0.5"; + colors[1] = "0.2 0.2 0.2 0"; + sizes[0] = 0.6; + sizes[1] = 5; +}; + +datablock ParticleEmitterData(ContrailEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 1; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 10; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "ContrailParticle"; +}; + + +datablock ParticleData(FlyerJetParticle) +{ + dragCoefficient = 1.5; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 200; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.9 0.7 0.3 0.6"; + colors[1] = "0.3 0.3 0.5 0"; + sizes[0] = 2; + sizes[1] = 6; +}; + +datablock ParticleEmitterData(FlyerJetEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 20; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 10; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "FlyerJetParticle"; +}; + +//-------------------------------------------------------------- +// Hover Jet Emitters +//-------------------------------------------------------------- + +datablock ParticleData(TankJetParticle) +{ + dragCoefficient = 1.5; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 200; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.9 0.7 0.3 0.6"; + colors[1] = "0.3 0.3 0.5 0"; + sizes[0] = 2; + sizes[1] = 6; +}; + +datablock ParticleEmitterData(TankJetEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 20; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 10; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "TankJetParticle"; +}; + +datablock ParticleData(WildcatJetParticle) +{ + dragCoefficient = 1.5; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 100; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.9 0.7 0.3 0.6"; + colors[1] = "0.3 0.3 0.5 0"; + sizes[0] = 0.5; + sizes[1] = 1.5; +}; + +datablock ParticleEmitterData(WildcatJetEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 20; + velocityVariance = 1.0; + ejectionOffset = 0; + thetaMin = 0; + thetaMax = 10; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "WildcatJetParticle"; +}; + + +//-------------------------------------------------------------- +// Vehicle Splash Sounds +//-------------------------------------------------------------- +//EXIT WATER +datablock AudioProfile(VehicleExitWaterSoftSound) +{ + filename = "fx/armor/general_water_exit2.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(VehicleExitWaterMediumSound) +{ + filename = "fx/armor/general_water_exit2.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(VehicleExitWaterHardSound) +{ + filename = "fx/armor/general_water_exit2.wav"; + description = AudioClose3d; + preload = true; +}; + +//IMPACT WATER +datablock AudioProfile(VehicleImpactWaterSoftSound) +{ + filename = "fx/armor/general_water_smallsplash2.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(VehicleImpactWaterMediumSound) +{ + filename = "fx/armor/general_water_medsplash.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(VehicleImpactWaterHardSound) +{ + filename = "fx/armor/general_water_bigsplash.wav"; + description = AudioDefault3d; + preload = true; +}; + +//WATER WAKE +datablock AudioProfile(VehicleWakeSoftSplashSound) +{ + filename = "fx/vehicles/wake_wildcat.wav"; + description = AudioDefaultLooping3d; + preload = true; +}; + +datablock AudioProfile(VehicleWakeMediumSplashSound) +{ + filename = "fx/vehicles/wake_shrike_n_tank.wav"; + description = AudioDefaultLooping3d; + preload = true; +}; + +datablock AudioProfile(VehicleWakeHardSplashSound) +{ + filename = "fx/vehicles/wake_shrike_n_tank.wav"; + description = AudioDefaultLooping3d; + preload = true; +}; + diff --git a/Scripts/Vehicles/vehicle_sub.cs b/Scripts/Vehicles/vehicle_sub.cs new file mode 100644 index 0000000..b4d0aa1 --- /dev/null +++ b/Scripts/Vehicles/vehicle_sub.cs @@ -0,0 +1,847 @@ +//************************************************************** +// BEOWULF ASSAULT VEHICLE +//************************************************************** +datablock EffectProfile(InboundTorpPingEffect) +{ + effectname = "weapons/spinfusor_dryfire"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(InboundTorpPing) +{ + filename = "gui/Objective_Notification.wav"; + description = AudioClose3d; + preload = true; + effect = InboundTorpPingEffect; +}; +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(Sub) : SubDamageProfile +{ + spawnOffset = "0 0 4"; + canControl = false; + floatingGravMag = 2.0; + + catagory = "Vehicles"; + shapeFile = "vehicle_air_bomber.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_bomber.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 1.5; + explosionRadius = 25.0; + + maxSteeringAngle = 0.5; // 20 deg. + + maxDamage = 5.0; + destroyedLevel = 5.0; + + HDAddMassLevel = 3.5; + HDMassImage = BoatHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 135; + maxEnergy = 1000; + minJetEnergy = 15; + jetEnergyDrain = 0.0; + + // Rigid Body + mass = 1000; + bodyFriction = 1.2; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 5; // Play SoftImpact Sound + hardImpactSpeed = 10; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 5; + speedDamageScale = 0.005; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 18; + collDamageMultiplier = 0.005; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.0; + + mainThrustForce = 35; + reverseThrustForce = 17; + strafeThrustForce = 0.0; + turboFactor = 1.1; + + brakingForce = 10; + brakingActivationSpeed = 10; + + stabLenMin = 0.1; + stabLenMax = 1.6; + stabSpringConstant = 0; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 10; + steeringForce = 10; + rollForce = 10; + pitchForce = 15; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 10; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Darwine IV'; + targetTypeTag = 'Submarine'; + sensorData = VehiclePulseSensor; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + replaceTime = 60; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(Torpedo) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 2.0; + damageRadius = 25.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 1250; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = GrenadeBubbleEmitter; + delayEmitter = LoadingE2; + puffEmitter = LoadingE; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 60000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 5.0; + maxVelocity = 25.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 35.0; + acceleration = 10.0; + + proximityRadius = 10; + +// terrainAvoidanceSpeed = 0; +// terrainScanAhead = 0; +// terrainHeightFail = 0; +// terrainAvoidanceRadius = 0; + + flareDistance = 1; + flareAngle = 1; + minSeekHeat = 0.0; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = false; + flechetteDelayMs = 100; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +datablock ShapeBaseImageData(SubTorpedoLauncher) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Torpedo; + projectileType = SeekerProjectile; + offset = "1.68 -2.75 2.0"; + mountPoint = 10; + + usesEnergy = true; + useMountEnergy = true; + minEnergy = 1; + fireEnergy = 1; + fireTimeout = 125; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "WaitFire1"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BomberTurretActivateSound; + + stateName[1] = "WaitFire1"; + stateTransitionOnTriggerDown[1] = "Fire1"; + stateTransitionOnNoAmmo[1] = "NoAmmo1"; + + stateName[2] = "Fire1"; + stateTransitionOnTimeout[2] = "Reload1"; + stateTimeoutValue[2] = 0.75; + stateFire[2] = true; + stateRecoil[2] = LightRecoil; + stateAllowImageChange[2] = false; + stateSequence[2] = "Fire"; + stateScript[2] = "onFire"; + stateSound[2] = BomberTurretFireSound; + + stateName[3] = "Reload1"; + stateSequence[3] = "Reload"; + stateTimeoutValue[3] = 0.75; + stateAllowImageChange[3] = false; + stateTransitionOnTimeout[3] = "WaitFire2"; + stateTransitionOnNoAmmo[3] = "NoAmmo1"; + + stateName[4] = "NoAmmo1"; + stateTransitionOnAmmo[4] = "Reload1"; + stateSequence[4] = "NoAmmo1"; + + stateTransitionOnTriggerDown[4] = "DryFire1"; + + stateName[5] = "DryFire1"; + stateSound[5] = BomberTurretDryFireSound; + stateTimeoutValue[5] = 0.75; + stateTransitionOnTimeout[5] = "NoAmmo1"; + + stateName[6] = "WaitFire2"; + stateTransitionOnTriggerDown[6] = "Fire2"; + stateTransitionOnNoAmmo[6] = "NoAmmo2"; + + stateName[7] = "Fire2"; + stateTransitionOnTimeout[7] = "Reload2"; + stateTimeoutValue[7] = 10.0; + stateScript[7] = "FirePair"; + + stateName[8] = "Reload2"; + stateSequence[8] = "Reload"; + stateTimeoutValue[8] = 0.75; + stateAllowImageChange[8] = false; + stateTransitionOnTimeout[8] = "WaitFire1"; + stateTransitionOnNoAmmo[8] = "NoAmmo2"; + + stateName[9] = "NoAmmo2"; + stateTransitionOnAmmo[9] = "Reload2"; + stateSequence[9] = "NoAmmo2"; + + stateTransitionOnTriggerDown[9] = "DryFire2"; + + stateName[10] = "DryFire2"; + stateSound[10] = BomberTurretDryFireSound; + stateTimeoutValue[10] = 0.75; + stateTransitionOnTimeout[10] = "NoAmmo2"; + +}; + +datablock ShapeBaseImageData(SubTorpedoLauncherPair) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + item = Chaingun; + ammo = ChaingunAmmo; + projectile = Torpedo; + projectileType = SeekerProjectile; + offset = "-1.68 -2.75 2.0"; + mountPoint = 10; + + usesEnergy = true; + useMountEnergy = true; + minEnergy = 1; + fireEnergy = 1; + fireTimeout = 125; + + stateName[0] = "WaitFire"; + stateTransitionOnTriggerDown[0] = "Fire"; + + stateName[1] = "Fire"; + stateTransitionOnTimeout[1] = "StopFire"; + stateTimeoutValue[1] = 0.13; + stateFire[1] = true; + stateRecoil[1] = LightRecoil; + stateAllowImageChange[1] = false; + stateSequence[1] = "Fire"; + stateScript[1] = "onFire"; + stateSound[1] = BomberTurretFireSound; + + stateName[2] = "StopFire"; + stateTimeoutValue[2] = 0.1; + stateTransitionOnTimeout[2] = "WaitFire"; + stateScript[2] = "stopFire"; +}; + +datablock ShapeBaseImageData(SubAIAiming) +{ + shapeFile = "turret_muzzlepoint.dts"; + mountPoint = 2; + + projectile = Torpedo; + projectileType = SeekerProjectile; + isSeeker = false; +}; + +datablock TurretData(SubAATurret) : TankDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Boat CG'; + targetTypeTag = 'Turret'; + mass = 1.0; // Not really relevant + + maxEnergy = 1000; + maxDamage = sub.maxDamage; + destroyedLevel = sub.destroyedLevel; + repairRate = 0; + + // capacitor + maxCapacitorEnergy = 100; + capacitorRechargeRate = 1.5; + + thetaMin = 20; + thetaMax = 100; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 1; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = 'Sub Anti Aircraft'; + targetTypeTag = 'Turret'; + + explosion = HandGrenadeExplosion; + expDmgRadius = 5.0; + expDamage = 0.25; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + repairRate = 0.0; + + max[plasmaammo] = 4; +}; + +datablock TurretImageData(Sub50MMTurretBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + mountPoint = 0; + + projectile = APCT_bullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 6.0 / 1000.0; + + useCapacitor = true; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 1.0; + minEnergy = 5.0; + + // Turret parameters + activationMS = 500; + deactivateDelayMS = 550; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + attackRadius = 225; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.1; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 30; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; + + stateName[9] = "WetFire"; + stateSound[9] = ChaingunDryFireSound; + stateTimeoutValue[9] = 1.0; + stateTransitionOnTimeout[9] = "Ready"; + + stateName[10] = "CheckWet"; + stateTransitionOnWet[10] = "WetFire"; + stateTransitionOnNotWet[10] = "Fire"; +}; + +datablock TurretImageData(SubTurretParam) +{ + mountPoint = 0; + shapeFile = "turret_muzzlepoint.dts"; + offset = "0.0 0.0 3.0"; + + projectile = APCT_bullet; + projectileType = TracerProjectile; + + useCapacitor = false; + usesEnergy = true; + + // Turret parameters + activationMS = 500; + deactivateDelayMS = 550; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + attackRadius = 225; +}; + +//****************************************************** +// sub main functions +//****************************************************** + +function sub::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + %obj.mountImage(SubAIAiming, 0); + %obj.mountImage(SubTorpedoLauncher, 2); + %obj.mountImage(SubTorpedoLauncherPair, 3); + %obj.selectedWeapon = 1; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); + + %turret = TurretData::create(SubAATurret); + %turret.selectedWeapon = 1; + MissionCleanup.add(%turret); + %turret.team = %obj.teamBought; + %turret.setSelfPowered(); + %obj.mountObject(%turret, 2); + %turret.mountImage(Sub50MMTurretBarrel, 2); + %turret.setCapacitorRechargeRate( %turret.getDataBlock().capacitorRechargeRate ); + %obj.turretObject = %turret; + %turret.setAutoFire(false); + %turret.mountImage(SubTurretParam, 0); + %turret.setInventory(PlasmaAmmo, 4); + setTargetSensorGroup(%turret.getTarget(), %turret.team); + setTargetNeverVisMask(%turret.getTarget(), 0xffffffff); +} + +function sub::deleteAllMounted(%data, %obj) +{ + %turret = %obj.getMountNodeObject(2); + if (!%turret) + return; + if (%client = %turret.getControllingClient()) + { + %client.player.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + } + %turret.schedule(1000, delete); + if(isObject(%obj.beacon)) + %obj.beacon.schedule(50, delete); +} + +function sub::playerMounted(%data, %obj, %player, %node) +{ + Cancel(%player.DrownLoop); + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) { + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + else if (%node == 1) + { + %turret = %obj.getMountNodeObject(2); + %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; + + $aWeaponActive = 0; + %obj.getMountNodeObject(10).selectedWeapon = 1; + commandToClient(%player.client, 'setHudMode', 'Pilot', "Assault", %node); + } + else + { + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + bottomPrint(%player.client, "ONLY use on water will not move well on land", 5, 2 ); + + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); +} + +//****************************************************** +// sub fireing functions +//****************************************************** +function sub::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function SubTorpedoLauncher::firePair(%this, %obj, %slot) +{ + %obj.setImageTrigger( 3, true); +} +function SubTorpedoLauncherPair::stopFire(%this, %obj, %slot) +{ + %obj.setImageTrigger( 3, false); +} + +function SubTorpedoLauncher::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.torpseekloop = schedule(5500, 0, "TorpedoSeekLoop", %p); +} +function SubTorpedoLauncherPair::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.torpseekloop = schedule(5500, 0, "TorpedoSeekLoop", %p); +} + +datablock StaticShapeData(SubBeacon) +{ + shapeFile = "turret_muzzlepoint.dts"; + targetNameTag = 'beacon'; + isInvincible = true; + + dynamicType = $TypeMasks::SensorObjectType; +}; + +function TorpedoSeekLoop(%p) +{ + if(!isObject(%p)) + return; + InitContainerRadiusSearch(%p.getPosition(), 100, $TypeMasks::VehicleObjectType); + %searchResult = containersearchnext(); + if(%searchResult) + { + %SearchObj = FirstWord(%SearchResult); + if(%searchObj.getDataBlock().getName() $= "Sub" || %searchObj.getDataBlock().getName() $= "Boat") + { + if(%SearchObj.beacon) + { + %SearchObj.beacon.setPosition(%SearchObj.getWorldBoxCenter()); + } + else{ + %SearchObj.beacon = new BeaconObject() { + dataBlock = "SubBeacon"; + beaconType = "vehicle"; + position = %SearchObj.getWorldBoxCenter(); + }; + + %SearchObj.beacon.playThread($AmbientThread, "ambient"); + %SearchObj.beacon.team = %p.team; + %SearchObj.beacon.sourceObject = %SearchObj; + + // give it a team target + %SearchObj.beacon.setTarget(0); + MissionCleanup.add(%SearchObj.beacon); + } + + %p.setObjectTarget(%searchObj.beacon); + + serverPlay3d("InboundTorpPing",%SearchObj.getWorldBoxCenter()); + } + } + %p.torpseekloop = schedule(500, 0, "TorpedoSeekLoop", %p); +} + +//****************************************************** +// turret functions +//****************************************************** + +function SubAATurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function SubAATurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function SubAATurret::onTrigger(%data, %obj, %trigger, %state) +{ + %Pos = posFromTransform(%obj.getPosition()); + %vector = vectorAdd("0 0 -10", %pos); + %searchresult = containerRayCast(%Pos, %vector, $TypeMasks::WaterObjectType); + if(%searchresult){ + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } + } + if(%trigger == 4){ + if(%state){ + if (%obj.inv[PlasmaAmmo] > 0){ + %mp = %obj.getMuzzlePoint(2); + + %p = new (seekerprojectile)() + { + dataBlock = Torpedo; + initialDirection = "0 0 1"; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = 2; + }; + %obj.decInventory(PlasmaAmmo, 1); + + schedule(3000, 0, "startCGM", %p); + } + } + } +} + +function SubAATurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %client = %obj.getControllingClient(); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +function Sub::onEnterLiquid(%data, %obj, %coverage, %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 + %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 Sub::onLeaveLiquid(%data, %obj, %coverage, %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 + } +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_superHavoc.cs b/Scripts/Vehicles/vehicle_superHavoc.cs new file mode 100644 index 0000000..d3f5a12 --- /dev/null +++ b/Scripts/Vehicles/vehicle_superHavoc.cs @@ -0,0 +1 @@ + diff --git a/Scripts/Vehicles/vehicle_superWildcat.cs b/Scripts/Vehicles/vehicle_superWildcat.cs new file mode 100644 index 0000000..3995c61 --- /dev/null +++ b/Scripts/Vehicles/vehicle_superWildcat.cs @@ -0,0 +1,149 @@ +//************************************************************** +// SUPER WILDCAT GRAV CYCLE +//************************************************************** + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(SuperScoutVehicle) : WildcatDamageProfile +{ + spawnOffset = "0 0 1"; + canControl = true; + floatingGravMag = 3.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_scout.dts"; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout_debris.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.0; + density = 0.9; + + mountPose[0] = scoutRoot; + cameraMaxDist = 5.0; + cameraOffset = 0.7; + cameraLag = 0.5; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + explosion = VehicleExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + lightOnly = 1; + + maxDamage = 1.60; + destroyedLevel = 1.60; + + HDAddMassLevel = 1.3; + HDMassImage = WCHDMassImage; + + isShielded = true; + rechargeRate = 0.9; + energyPerDamagePoint = 1; + maxEnergy = 450; + minJetEnergy = 1; + jetEnergyDrain = 0.1; + + // Rigid Body + mass = 400; + bodyFriction = 0.1; + bodyRestitution = 0.5; + softImpactSpeed = 120; // Play SoftImpact Sound + hardImpactSpeed = 128; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 129; + speedDamageScale = 0.010; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 123; + collDamageMultiplier = 0.030; + + dragForce = 45 / 45.0; + vertFactor = 0.0; + floatingThrustFactor = 0.35; + + mainThrustForce = 90; + reverseThrustForce = 90; + strafeThrustForce = 90; + turboFactor = 3.5; + + brakingForce = 90; + brakingActivationSpeed = 4; + + stabLenMin = 2.25; + stabLenMax = 3.75; + stabSpringConstant = 30; + stabDampingConstant = 16; + + gyroDrag = 16; + normalForce = 30; + restorativeForce = 20; + steeringForce = 30; + rollForce = 15; + pitchForce = 7; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 2.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 30.6; + dustTrailFreqMod = 15.0; + + jetSound = ScoutSqueelSound; + engineSound = ScoutEngineSound; + floatSound = ScoutThrustSound; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterSoftSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeSoftSplashSound; + + minMountDist = 4; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = SmallHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 0.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + forwardJetEmitter = WildcatJetEmitter; + + cmdCategory = Tactical; + cmdIcon = CMDHoverScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_landscout_grey"; + targetNameTag = 'Super WildCat'; + targetTypeTag = 'Grav Cycle'; + sensorData = VehiclePulseSensor; + + checkRadius = 1.7785; + observeParameters = "1 10 10"; + + runningLight[0] = WildcatLight1; + runningLight[1] = WildcatLight2; + runningLight[2] = WildcatLight3; + + shieldEffectScale = "0.9375 1.125 0.6"; +}; diff --git a/Scripts/Vehicles/vehicle_tank.cs b/Scripts/Vehicles/vehicle_tank.cs new file mode 100644 index 0000000..0c34c01 --- /dev/null +++ b/Scripts/Vehicles/vehicle_tank.cs @@ -0,0 +1,928 @@ +//************************************************************** +// BEOWULF ASSAULT VEHICLE +//************************************************************** + +datablock AudioProfile(MobileBaseStationDeploySound) +{ + filename = "fx/vehicles/MPB_deploy_station.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock ParticleData(ACCGSmoke) +{ + dragCoeffiecient = 0.05; + gravityCoefficient = 0.1; + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 150; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -25.0; + spinRandomMax = 25.0; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "0.3 0.3 0.3 1.0"; + colors[1] = "0.4 0.4 0.4 0.5"; + colors[2] = "0.5 0.5 0.5 0.0"; + sizes[0] = 1.0; + sizes[1] = 1.5; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; +datablock ParticleEmitterData(ACCGSmokeEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionOffset = 0.1; + + ejectionVelocity = 4.0; + velocityVariance = 3.5; + + thetaMin = 85.0; + thetaMax = 90.0; + + lifetimeMS = 100; + + particles = "ACCGSmoke"; + +}; +datablock ParticleEmitterData(ACCGSmokeEmitter2) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionOffset = 0.1; + + ejectionVelocity = 5.5; + velocityVariance = 5.5; + + thetaMin = 0.0; + thetaMax = 15.0; + + lifetimeMS = 150; + + particles = "ACCGSmoke"; + +}; +datablock ExplosionData(ACCGSubExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 2.0; + sizes[0] = "0.1 0.1 0.1"; + sizes[1] = "0.1 0.1 0.1"; + times[0] = 0.0; + times[1] = 1.0; + +}; +datablock ExplosionData(ACCGExplosion) +{ + soundProfile = plasmaExpSound; + subExplosion[0] = ACCGSubExplosion; + emitter[0] = ACCGSmokeEmitter; + emitter[1] = ACCGSmokeEmitter2; + shakeCamera = false; +}; + +//************************************************************** +// SOUNDS +//************************************************************** +datablock EffectProfile(AssaultVehicleEngineEffect) +{ + effectname = "vehicles/tank_engine"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultVehicleThrustEffect) +{ + effectname = "vehicles/tank_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultTurretActivateEffect) +{ + effectname = "vehicles/tank_activate"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultMortarDryFireEffect) +{ + effectname = "weapons/mortar_dryfire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultMortarFireEffect) +{ + effectname = "vehicles/tank_mortar_fire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultMortarReloadEffect) +{ + effectname = "weapons/mortar_reload"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(AssaultChaingunFireEffect) +{ + effectname = "weapons/chaingun_fire"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock AudioProfile(AssaultVehicleSkid) +{ + filename = "fx/vehicles/tank_skid.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(AssaultVehicleEngineSound) +{ + filename = "fx/vehicles/tank_engine.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = AssaultVehicleEngineEffect; +}; + +datablock AudioProfile(AssaultVehicleThrustSound) +{ + filename = "fx/vehicles/tank_boost.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = AssaultVehicleThrustEffect; +}; + +datablock AudioProfile(AssaultChaingunFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = AssaultChaingunFireEffect; +}; + +datablock AudioProfile(AssaultChaingunReloadSound) +{ + filename = "fx/weapons/chaingun_dryfire.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(TankChaingunProjectile) +{ + filename = "fx/weapons/chaingun_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(AssaultTurretActivateSound) +{ + filename = "fx/vehicles/tank_activate.wav"; + description = AudioClose3d; + preload = true; + effect = AssaultTurretActivateEffect; +}; + +datablock AudioProfile(AssaultChaingunDryFireSound) +{ + filename = "fx/weapons/chaingun_dryfire.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(AssaultChaingunIdleSound) +{ + filename = "fx/misc/diagnostic_on.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(AssaultMortarDryFireSound) +{ + filename = "fx/weapons/mortar_dryfire.wav"; + description = AudioClose3d; + preload = true; + effect = AssaultMortarDryFireEffect; +}; + +datablock AudioProfile(AssaultMortarFireSound) +{ + filename = "fx/vehicles/tank_mortar_fire.wav"; + description = AudioClose3d; + preload = true; + effect = AssaultMortarFireEffect; +}; + +datablock AudioProfile(AssaultMortarReloadSound) +{ + filename = "fx/weapons/mortar_reload.wav"; + description = AudioClose3d; + preload = true; + effect = AssaultMortarReloadEffect; +}; + +datablock AudioProfile(AssaultMortarIdleSound) +{ + filename = "fx/misc/diagnostic_on.wav"; + description = ClosestLooping3d; + preload = true; +}; + +//************************************************************** +// LIGHTS +//************************************************************** +datablock RunningLightData(TankLight1) +{ + radius = 1.5; + color = "1.0 1.0 1.0 0.2"; + nodeName = "Headlight_node01"; + direction = "0.0 1.0 0.0"; + texture = "special/headlight4"; +}; + +datablock RunningLightData(TankLight2) +{ + radius = 1.5; + color = "1.0 1.0 1.0 0.2"; + nodeName = "Headlight_node02"; + direction = "0.0 1.0 0.0"; + texture = "special/headlight4"; +}; + +datablock RunningLightData(TankLight3) +{ + radius = 1.5; + color = "1.0 1.0 1.0 0.2"; + nodeName = "Headlight_node03"; + direction = "0.0 1.0 0.0"; + texture = "special/headlight4"; +}; + +datablock RunningLightData(TankLight4) +{ + radius = 1.5; + color = "1.0 1.0 1.0 0.2"; + nodeName = "Headlight_node04"; + direction = "0.0 1.0 0.0"; + texture = "special/headlight4"; +}; + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(AssaultVehicle) : TankDamageProfile +{ + spawnOffset = "0 0 4"; + canControl = false; + floatingGravMag = 4.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_tank.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_grav_tank.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + mountPose[1] = sitting; + numMountPoints = 2; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 2.0; + explosionRadius = 30.0; + + maxSteeringAngle = 0.5; // 20 deg. + + maxDamage = 3.0; + destroyedLevel = 3.0; + + HDAddMassLevel = 2.1; + HDMassImage = TankHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 135; + maxEnergy = 400; + minJetEnergy = 15; + jetEnergyDrain = 2.0; + + // Rigid Body + mass = 1500; + bodyFriction = 0.8; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 15; // Play SoftImpact Sound + hardImpactSpeed = 18; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 30; + speedDamageScale = 0.020; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 18; + collDamageMultiplier = 0.045; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.15; + + mainThrustForce = 80; + reverseThrustForce = 35; + strafeThrustForce = 0; + turboFactor = 1.5; + + brakingForce = 20; + brakingActivationSpeed = 4; + + stabLenMin = 3.25; + stabLenMax = 4; + stabSpringConstant = 50; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 10; + steeringForce = 30; + rollForce = 0; + pitchForce = 3; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 7; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'M4A1 Wolf'; + targetTypeTag = 'Light Tank'; + sensorData = combatSensor; + sensorRadius = combatSensor.detectRadius; + sensorColor = "9 9 255"; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + replaceTime = 45; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +//------------------------------------- +// ASSAULT CHAINGUN (projectile) +//------------------------------------- + +datablock TracerProjectileData(AssaultChaingunBullet) +{ + doDynamicClientHits = true; + + projectileShapeName = ""; + directDamage = 0.4; + directDamageType = $DamageType::TankChaingunH; + hasDamageRadius = false; + splash = ChaingunSplash; + + kickbackstrength = 0.0; + sound = TankChaingunProjectile; + + dryVelocity = 1250.0; + wetVelocity = 500.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.10; + crossSize = 0.25; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; + + activateDelayMS = 100; + + explosion = ACCGExplosion; +}; + +//------------------------------------- +// ASSAULT CHAINGUN CHARACTERISTICS +//------------------------------------- + +datablock TurretData(AssaultPlasmaTurret) : TurretDamageProfile +{ + className = VehicleTurret; + catagory = "Turrets"; + shapeFile = "Turret_tank_base.dts"; + //turret_assaulttank_plasma.dts + preload = true; + canControl = false; + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'light'; + targetTypeTag = 'Assault Tank turret'; + mass = 1.0; // Not really relevant + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + maxEnergy = 1; + maxDamage = AssaultVehicle.maxDamage; + destroyedLevel = AssaultVehicle.destroyedLevel; + repairRate = 0; + + // capacitor + maxCapacitorEnergy = 200; + capacitorRechargeRate = 0.45; + + thetaMin = 0; + thetaMax = 100; + + inheritEnergyFromMount = true; + firstPersonOnly = true; + useEyePoint = true; + numWeapons = 2; + + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + targetNameTag = 'light Tank'; + targetTypeTag = 'Turret'; +}; + +datablock TurretImageData(AssaultPlasmaTurretBarrel) +{ + shapeFile = "turret_tank_barrelchain.dts"; + mountPoint = 1; + + projectile = AssaultChaingunBullet; + projectileType = TracerProjectile; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 1.5 / 1000.0; + + useCapacitor = true; + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 0.01; + minEnergy = 5.0; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + stateSound[0] = AssaultTurretActivateSound; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Fire"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateSequenceRandomFlash[3] = true; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSound[3] = AssaultChaingunFireSound; + stateScript[3] = "onFire"; + stateTimeoutValue[3] = 0.14; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Reload"; + stateTransitionOnNoAmmo[3] = "noAmmo"; + + stateName[4] = "Reload"; + stateSequence[4] = "Reload"; + stateTimeoutValue[4] = 0.2; + stateAllowImageChange[4] = false; + stateTransitionOnTimeout[4] = "Ready"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateWaitForTimeout[4] = true; + + stateName[5] = "Deactivate"; + stateSequence[5] = "Activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 30; + stateTransitionOnTimeout[5] = "ActivateReady"; + + stateName[6] = "Dead"; + stateTransitionOnLoaded[6] = "ActivateReady"; + stateTransitionOnTriggerDown[6] = "DryFire"; + + stateName[7] = "DryFire"; + stateSound[7] = AssaultChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "NoAmmo"; + stateTransitionOnAmmo[8] = "Reload"; + stateSequence[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "DryFire"; + +}; + +//------------------------------------- +// ASSAULT MORTAR (projectile) +//------------------------------------- + +datablock ItemData(AssaultMortarAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "repair_kit.dts"; + mass = 1; + elasticity = 0.5; + friction = 0.6; + pickupRadius = 1; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(TankRocket) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.75; + damageRadius = 10.0; + radiusDamageType = $DamageType::MissileTurret; + kickBackStrength = 500; + + flareDistance = 200; + flareAngle = 30; + minSeekHeat = 0.6; + + explosion = "MissileExplosion"; + velInheritFactor = 1.0; + + splash = MissileSplash; + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + + lifetimeMS = 20000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 40.0; + maxVelocity = 300.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 60.0; + acceleration = 100.0; + + proximityRadius = 7; + + terrainAvoidanceSpeed = 40; + terrainScanAhead = 50; + terrainHeightFail = 5; + terrainAvoidanceRadius = 10; + + useFlechette = true; + flechetteDelayMs = 225; + casingDeb = FlechetteDebris; +}; + +//------------------------------------- +// ASSAULT MORTAR CHARACTERISTICS +//------------------------------------- + +datablock TurretImageData(AssaultMortarTurretBarrel) +{ + shapeFile = "stackable2m.dts"; + rotation = "-1 0 0 90"; + offset = "0 0.7 0"; + mountPoint = 0; + + projectile = TankRocket; + projectileType = SeekerProjectile; + + usesEnergy = true; + useMountEnergy = true; + fireEnergy = 50.00; + minEnergy = 50.00; + useCapacitor = true; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + // Turret parameters + activationMS = 4000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 360; + degPerSecPhi = 360; + attackRadius = 75; + + // State transitions + stateName[0] = "Activate"; + stateTransitionOnNotLoaded[0] = "Dead"; + stateTransitionOnLoaded[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateSequence[1] = "Activate"; + stateSound[1] = AssaultTurretActivateSound; + stateTimeoutValue[1] = 1.0; + stateTransitionOnTimeout[1] = "Ready"; + stateTransitionOnNotLoaded[1] = "Deactivate"; + + stateName[2] = "Ready"; + stateTransitionOnNotLoaded[2] = "Deactivate"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Fire"; + stateTransitionOnTimeout[3] = "NextFire"; + stateTimeoutValue[3] = 0.5; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSound[3] = MissileFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "NextFire"; + stateTransitionOnNotLoaded[4] = "Deactivate"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTriggerDown[4] = "Fire"; + + stateName[5] = "Fire2"; + stateSequence[5] = "Fire"; + stateTransitionOnTimeout[5] = "NextFire2"; + stateTimeoutValue[5] = 0.5; + stateFire[5] = true; + stateRecoil[5] = LightRecoil; + stateAllowImageChange[5] = false; + stateSound[5] = MissileFireSound; + stateScript[5] = "onFire"; + + stateName[6] = "NextFire2"; + stateTransitionOnNotLoaded[6] = "Deactivate"; + stateTransitionOnNoAmmo[6] = "NoAmmo"; + stateTransitionOnTriggerDown[6] = "Fire"; + + stateName[7] = "Fire3"; + stateSequence[7] = "Fire"; + stateTransitionOnTimeout[7] = "NextFire3"; + stateTimeoutValue[7] = 0.5; + stateFire[7] = true; + stateRecoil[7] = LightRecoil; + stateAllowImageChange[7] = false; + stateSound[7] = MissileFireSound; + stateScript[7] = "onFire"; + + stateName[8] = "NextFire3"; + stateTransitionOnNotLoaded[8] = "Deactivate"; + stateTransitionOnNoAmmo[8] = "NoAmmo"; + stateTransitionOnTriggerDown[8] = "Fire"; + + stateName[9] = "Fire4"; + stateSequence[9] = "Fire"; + stateTransitionOnTimeout[9] = "Reload"; + stateTimeoutValue[9] = 0.5; + stateFire[9] = true; + stateRecoil[9] = LightRecoil; + stateAllowImageChange[9] = false; + stateSound[9] = MissileFireSound; + stateScript[9] = "onFire"; + + stateName[10] = "Reload"; + stateSequence[10] = "Reload"; + stateTimeoutValue[10] = 8; + stateAllowImageChange[10] = false; + stateTransitionOnTimeout[10] = "ReloadSound"; + stateWaitForTimeout[10] = true; + + stateName[11] = "ReloadSound"; + stateTimeoutValue[11] = 2; + stateAllowImageChange[11] = false; + stateTransitionOnTimeout[11] = "Ready"; + stateSound[11] = MobileBaseStationDeploySound; + stateWaitForTimeout[11] = true; + + stateName[12] = "Deactivate"; + stateDirection[12] = false; + stateSequence[12] = "Activate"; + stateTimeoutValue[12] = 1.0; + stateTransitionOnLoaded[12] = "ActivateReady"; + stateTransitionOnTimeout[12] = "Dead"; + + stateName[13] = "Dead"; + stateTransitionOnLoaded[13] = "ActivateReady"; + stateTransitionOnTriggerDown[13] = "DryFire"; + + stateName[14] = "DryFire"; + stateSound[14] = AssaultMortarDryFireSound; + stateTimeoutValue[14] = 1.0; + stateTransitionOnTimeout[14] = "NoAmmo"; + + stateName[15] = "NoAmmo"; + stateSequence[15] = "NoAmmo"; + stateTransitionOnAmmo[15] = "Reload"; + stateTransitionOnTriggerDown[15] = "DryFire"; +}; + +datablock TurretImageData(AssaultTurretParam) +{ + mountPoint = 2; + shapeFile = "turret_muzzlepoint.dts"; + + projectile = AssaultChaingunBullet; + projectileType = TracerProjectile; + + useCapacitor = true; + usesEnergy = true; + + // Turret parameters + activationMS = 1000; + deactivateDelayMS = 1500; + thinkTimeMS = 200; + degPerSecTheta = 500; + degPerSecPhi = 500; + + isSeeker = true; + seekRadius = $Bomber::SeekRadius; + maxSeekAngle = 30; + seekTime = $Bomber::SeekTime; + minSeekHeat = $Bomber::minSeekHeat; + minTargetingDistance = $Bomber::minTargetingDistance; + useTargetAudio = $Bomber::useTargetAudio; + + attackRadius = 500; +}; + +function AssaultMortarTurretBarrel::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function AssaultVehicle::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 1: + //Ocean Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 2: + //River Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + case 3: + //Stagnant Water + %obj.setHeat(0.0); + %obj.liquidDamage(%data, 0.1, $DamageType::Crash); + 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 + } +} diff --git a/Scripts/Vehicles/vehicle_transboat.cs b/Scripts/Vehicles/vehicle_transboat.cs new file mode 100644 index 0000000..08b1c88 --- /dev/null +++ b/Scripts/Vehicles/vehicle_transboat.cs @@ -0,0 +1,250 @@ +//************************************************************** +// BEOWULF ASSAULT VEHICLE +//************************************************************** + +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock HoverVehicleData(PersonelBoat) : TankDamageProfile +{ + spawnOffset = "0 0 4"; + canControl = false; + floatingGravMag = 4.0; + + catagory = "Vehicles"; + shapeFile = "vehicle_air_scout.dts"; + multipassenger = true; + computeCRC = true; + renderWhenDestroyed = false; + + weaponNode = 1; + + debrisShapeName = "vehicle_air_scout.dts"; + debris = GShapeDebris; + + drag = 0.0; + density = 0.9; + + mountPose[0] = sitting; + numMountPoints = 6; + isProtectedMountPoint[0] = true; + isProtectedMountPoint[1] = true; + isProtectedMountPoint[2] = true; + isProtectedMountPoint[3] = true; + isProtectedMountPoint[4] = true; + isProtectedMountPoint[5] = true; + + cameraMaxDist = 20; + cameraOffset = 3; + cameraLag = 1.5; + explosion = HGVehicleExplosion; + explosionDamage = 3.0; + explosionRadius = 25.0; + + maxSteeringAngle = 0.5; // 20 deg. + + maxDamage = 4.0; + destroyedLevel = 4.0; + + HDAddMassLevel = 3.2; + HDMassImage = BoatHDMassImage; + + isShielded = false; + rechargeRate = 1.0; + energyPerDamagePoint = 135; + maxEnergy = 1000; + minJetEnergy = 15; + jetEnergyDrain = 0.0; + + // Rigid Body + mass = 950; + bodyFriction = 1.0; + bodyRestitution = 0.5; + minRollSpeed = 3; + gyroForce = 400; + gyroDamping = 0.3; + stabilizerForce = 20; + minDrag = 10; + softImpactSpeed = 5; // Play SoftImpact Sound + hardImpactSpeed = 10; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 5; + speedDamageScale = 0.005; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 18; + collDamageMultiplier = 0.005; + + dragForce = 40 / 20; + vertFactor = 0.0; + floatingThrustFactor = 0.0; + + mainThrustForce = 75; + reverseThrustForce = 30; + strafeThrustForce = 0.0; + turboFactor = 1.0; + + brakingForce = 15; + brakingActivationSpeed = 4; + + stabLenMin = 0.1; + stabLenMax = 1.6; + stabSpringConstant = 45; + stabDampingConstant = 20; + + gyroDrag = 20; + normalForce = 20; + restorativeForce = 10; + steeringForce = 12; + rollForce = 4; + pitchForce = 0; + + dustEmitter = TankDustEmitter; + triggerDustHeight = 3.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = AssaultVehicleThrustSound; + engineSound = AssaultVehicleEngineSound; + floatSound = AssaultVehicleSkid; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + wheelImpactSound = WheelImpactSound; + + forwardJetEmitter = TankJetEmitter; + + // + softSplashSoundVelocity = 5.0; + mediumSplashSoundVelocity = 10.0; + hardSplashSoundVelocity = 15.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterMediumSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterMediumSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeMediumSplashSound; + + minMountDist = 10; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = MeHGHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 3.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + cmdCategory = "Tactical"; + cmdIcon = CMDGroundTankIcon; + cmdMiniIconName = "commander/MiniIcons/com_tank_grey"; + targetNameTag = 'Grandens Transport'; + targetTypeTag = 'Boat'; + sensorData = PlayerSensor; + + checkRadius = 5.5535; + observeParameters = "1 10 10"; + runningLight[0] = TankLight1; + runningLight[1] = TankLight2; + runningLight[2] = TankLight3; + runningLight[3] = TankLight4; + shieldEffectScale = "0.9 1.0 0.6"; + showPilotInfo = 1; + + replaceTime = 20; +}; + +function PersonelBoat::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.schedule(6000, "playThread", $ActivateThread, "activate"); +} + +function PersonelBoat::playerMounted(%data, %obj, %player, %node) +{ + if (%obj.clientControl) + serverCmdResetControlObject(%obj.clientControl); + + if (%node == 0) { + commandToClient(%player.client, 'setHudMode', 'Pilot', "HAPC", %node); + } + else + { + // all others + commandToClient(%player.client, 'setHudMode', 'Passenger', "HAPC", %node); + } + if ( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); + + bottomPrint(%player.client, "ONLY use on water will not move well on land", 5, 2 ); + + %passString = buildPassengerString(%obj); + for(%i = 0; %i < %data.numMountPoints; %i++) + if (%obj.getMountNodeObject(%i) > 0) + commandToClient(%obj.getMountNodeObject(%i).client, 'checkPassengers', %passString); +} + +function PersonelBoat::onEnterLiquid(%data, %obj, %coverage, %type) +{ + switch(%type) + { + case 0: + //Water + case 1: + //Ocean Water + case 2: + //River Water + case 3: + //Stagnant Water + 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 PersonelBoat::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 = ""; + } +} \ No newline at end of file diff --git a/Scripts/Vehicles/vehicle_wildcat.cs b/Scripts/Vehicles/vehicle_wildcat.cs new file mode 100644 index 0000000..b7e4178 --- /dev/null +++ b/Scripts/Vehicles/vehicle_wildcat.cs @@ -0,0 +1,411 @@ +//************************************************************** +// VEHICLE CHARACTERISTICS +//************************************************************** + +datablock EffectProfile(ScoutEngineEffect) +{ + effectname = "vehicles/outrider_engine"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock EffectProfile(ScoutThrustEffect) +{ + effectname = "vehicles/outrider_boost"; + minDistance = 5.0; + maxDistance = 10.0; +}; + +datablock AudioProfile(ScoutSqueelSound) +{ + filename = "fx/vehicles/outrider_skid.wav"; + description = ClosestLooping3d; + preload = true; +}; + +datablock AudioProfile(ScoutEngineSound) +{ + filename = "fx/vehicles/outrider_engine.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = ScoutEngineEffect; +}; + +datablock AudioProfile(ScoutThrustSound) +{ + filename = "fx/vehicles/outrider_boost.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = ScoutThrustEffect; +}; + +datablock AudioProfile(ScoutturretFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefaultLooping3d; + preload = true; + effect = AssaultChaingunFireEffect; +}; + +datablock HoverVehicleData(scoutVehicle) : WildcatDamageProfile +{ + spawnOffset = "0 0 1"; + + floatingGravMag = 3.5; + + catagory = "Vehicles"; + shapeFile = "vehicle_grav_scout.dts"; + computeCRC = true; + + debrisShapeName = "vehicle_grav_scout_debris.dts"; + debris = ShapeDebris; + renderWhenDestroyed = false; + + drag = 0.0; + density = 0.9; + + mountPose[0] = scoutRoot; + cameraMaxDist = 5.0; + cameraOffset = 0.7; + cameraLag = 0.5; + numMountPoints = 1; + isProtectedMountPoint[0] = true; + explosion = VehicleExplosion; + explosionDamage = 0.5; + explosionRadius = 5.0; + + lightOnly = 1; + + maxDamage = 0.80; + destroyedLevel = 0.80; + + isShielded = false; + rechargeRate = 0.7; + energyPerDamagePoint = 50; + maxEnergy = 150; + minJetEnergy = 15; + jetEnergyDrain = 1.5; + + // Rigid Body + mass = 400; + bodyFriction = 0.1; + bodyRestitution = 0.5; + softImpactSpeed = 20; // Play SoftImpact Sound + hardImpactSpeed = 28; // Play HardImpact Sound + + // Ground Impact Damage (uses DamageType::Ground) + minImpactSpeed = 29; + speedDamageScale = 0.010; + + // Object Impact Damage (uses DamageType::Impact) + collDamageThresholdVel = 23; + collDamageMultiplier = 0.030; + + dragForce = 28 / 45.0; + vertFactor = 0.0; + floatingThrustFactor = 0.35; + + mainThrustForce = 25; + reverseThrustForce = 15; + strafeThrustForce = 15; + turboFactor = 1.5; + + brakingForce = 25; + brakingActivationSpeed = 4; + + stabLenMin = 2.25; + stabLenMax = 3.75; + stabSpringConstant = 30; + stabDampingConstant = 16; + + gyroDrag = 16; + normalForce = 30; + restorativeForce = 20; + steeringForce = 25; + rollForce = 15; + pitchForce = 7; + + dustEmitter = VehicleLiftoffDustEmitter; + triggerDustHeight = 2.5; + dustHeight = 1.0; + dustTrailEmitter = TireEmitter; + dustTrailOffset = "0.0 -1.0 0.5"; + triggerTrailHeight = 3.6; + dustTrailFreqMod = 15.0; + + jetSound = ScoutSqueelSound; + engineSound = ScoutEngineSound; + floatSound = ScoutThrustSound; + softImpactSound = GravSoftImpactSound; + hardImpactSound = HardImpactSound; + //wheelImpactSound = WheelImpactSound; + + // + softSplashSoundVelocity = 10.0; + mediumSplashSoundVelocity = 20.0; + hardSplashSoundVelocity = 30.0; + exitSplashSoundVelocity = 10.0; + + exitingWater = VehicleExitWaterSoftSound; + impactWaterEasy = VehicleImpactWaterSoftSound; + impactWaterMedium = VehicleImpactWaterSoftSound; + impactWaterHard = VehicleImpactWaterMediumSound; + waterWakeSound = VehicleWakeSoftSplashSound; + + minMountDist = 4; + + damageEmitter[0] = SmallLightDamageSmoke; + damageEmitter[1] = SmallHeavyDamageSmoke; + damageEmitter[2] = DamageBubbles; + damageEmitterOffset[0] = "0.0 -1.5 0.5 "; + damageLevelTolerance[0] = 0.3; + damageLevelTolerance[1] = 0.7; + numDmgEmitterAreas = 1; + + splashEmitter[0] = VehicleFoamDropletsEmitter; + splashEmitter[1] = VehicleFoamEmitter; + + shieldImpact = VehicleShieldImpact; + + forwardJetEmitter = WildcatJetEmitter; + + cmdCategory = Tactical; + cmdIcon = CMDHoverScoutIcon; + cmdMiniIconName = "commander/MiniIcons/com_landscout_grey"; + targetNameTag = 'MK II WildCat'; + targetTypeTag = 'Grav Cycle'; + sensorData = VehiclePulseSensor; + + checkRadius = 1.7785; + observeParameters = "1 10 10"; + + runningLight[0] = WildcatLight1; + runningLight[1] = WildcatLight2; + runningLight[2] = WildcatLight3; + + shieldEffectScale = "0.9375 1.125 0.6"; +}; + +//************************************************************** +// WEAPONS +//************************************************************** + +datablock ShapeBaseImageData(hornetChaingunPairImage) +{ + className = WeaponImage; + shapeFile = "weapon_chaingun.dts"; + projectile = MG42Bullet; + projectileType = TracerProjectile; + mountPoint = 3; +//*wintips* offset = "1.84 -0.52 -0.05"; + offset = "0.3 0.9 0.4"; + rotation = "1 0 0 0"; + + usesEnergy = true; + useMountEnergy = true; + minEnergy = 5; + fireEnergy = 0; + fireTimeout = 125; + + + casing = ShellDebris; + shellExitDir = "0 -0.5 1.0"; + shellExitOffset = "0 -0.56 -0.11"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 3.0 / 1000.0; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = ChaingunSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.3; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ChaingunSpinupSound; + // + stateTimeoutValue[3] = 0.05; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + //stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + // + stateTimeoutValue[4] = 0.05; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 0.05; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.3; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(hornetChaingunImage) : hornetChaingunPairImage +{ + offset = "-0.45 0.9 0.4"; + rotation = "1 0 0 0"; + stateScript[3] = "onTriggerDown"; + stateScript[5] = "onTriggerUp"; + stateScript[6] = "onTriggerUp"; +}; + +datablock ShapeBaseImageData(hornetChaingunParam) +{ + mountPoint = 2; + shapeFile = "weapon_chaingun.dts"; + + projectile = MG42Bullet; + projectileType = TracerProjectile; +}; + +//********* FUNCTIONS *********** + +function scoutVehicle::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.mountImage(hornetChaingunParam, 0); + %obj.mountImage(hornetChaingunImage, 2); + %obj.mountImage(hornetChaingunPairImage, 3); + %obj.nextWeaponFire = 2; + %obj.schedule(5500, "playThread", $ActivateThread, "activate"); +} + +function scoutVehicle::playerMounted(%data, %obj, %player, %node) +{ + // scout == SUV (single-user vehicle) + commandToClient(%player.client, 'setHudMode', 'Pilot', "Hoverbike", %node); + $numVWeapons = 1; + + // update observers who are following this guy... + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, false ); +} + +function scoutVehicle::onTrigger(%data, %obj, %trigger, %state) +{ + // data = scout datablock + // obj = scout object number + // trigger = 0 for "fire", 1 for "jump", 3 for "thrust" + // state = 1 for firing, 0 for not firing + if(%trigger == 0) + { + switch (%state) { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + case 1: + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, true); + } + } +} + +function scoutVehicle::playerDismounted(%data, %obj, %player) +{ + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function hornetChaingunImage::onFire(%data,%obj,%slot) +{ + // obj = AdminFighterFlyer object number + // slot = 2 + + Parent::onFire(%data,%obj,%slot); +// %obj.nextWeaponFire = 3; +// schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} + +function hornetChaingunPairImage::onFire(%data,%obj,%slot) +{ + // obj = ScoutFlyer object number + // slot = 3 + + Parent::onFire(%data,%obj,%slot); +// %obj.nextWeaponFire = 2; +// schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} +function hornetChaingunImage::onTriggerDown(%this, %obj, %slot) +{ +} + +function hornetChaingunImage::onTriggerUp(%this, %obj, %slot) +{ +} + +function hornetChaingunImage::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function hornetChaingunPairImage::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function hornetChaingunImage::onUnmount(%this,%obj,%slot) +{ +} + +function hornetChaingunPairImage::onUnmount(%this,%obj,%slot) +{ +} + + diff --git a/Scripts/Weapons/DZShot.cs b/Scripts/Weapons/DZShot.cs new file mode 100644 index 0000000..87ddd28 --- /dev/null +++ b/Scripts/Weapons/DZShot.cs @@ -0,0 +1,143 @@ +datablock ParticleData(DemonFBSmokeParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 2500; + lifetimeVarianceMS = 500; + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + colors[0] = "0.5 0.5 0.5 0.5"; + colors[1] = "0.4 0.4 0.4 0.2"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 0.5; + sizes[1] = 1.75; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(DemonFBSmokeEmitter) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 0; + + ejectionVelocity = 0.75; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 180.0; + + particles = "DemonFBSmokeParticle"; +}; + +datablock GrenadeProjectileData(DemonFireball) +{ + projectileShapeName = "plasmabolt.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.4; + damageRadius = 5.0; // z0dd - ZOD, 8/13/02. Was 20.0 + radiusDamageType = $DamageType::zombie; + kickBackStrength = 1500; + + explosion = "PlasmaBoltExplosion"; + underwaterExplosion = "PlasmaBoltExplosion"; + velInheritFactor = 0; + splash = PlasmaSplash; + depthTolerance = 100.0; + + baseEmitter = DemonFBSmokeEmitter; + bubbleEmitter = DemonFBSmokeEmitter; + + grenadeElasticity = 0; + grenadeFriction = 0.4; + armingDelayMS = -1; // z0dd - ZOD, 4/14/02. Was 2000 + + gravityMod = 0.4; // z0dd - ZOD, 5/18/02. Make mortar projectile heavier, less floaty + muzzleVelocity = 50.0; // z0dd - ZOD, 8/13/02. More velocity to compensate for higher gravity. Was 63.7 + drag = 0; + sound = PlasmaProjectileSound; + + hasLight = true; + lightRadius = 10; + lightColor = "1 0.75 0.25"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "1 0.75 0.25"; +}; + +datablock ShapeBaseImageData(DZShotImage) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + item = DZShot; + + projectile = DemonFireball; + projectileType = GrenadeProjectile; + + usesEnergy = true; + fireEnergy = 40; + minEnergy = 40; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = ChaingunSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateRecoil[3] = NoRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = PlasmaProjectileSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateTimeoutValue[6] = 0.1; + stateTransitionOnTimeout[6] = "Ready"; +}; + +datablock ItemData(DZShot) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_energy.dts"; + image = DZShotImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Demon Zombie Fireball Ability"; +}; diff --git a/Scripts/Weapons/EditingTool.cs b/Scripts/Weapons/EditingTool.cs new file mode 100644 index 0000000..a6e643e --- /dev/null +++ b/Scripts/Weapons/EditingTool.cs @@ -0,0 +1,372 @@ +// Scales +$EditorTool[1, 1] = "1 0 0 +X axis scale"; +$EditorTool[1, 2] = "-1 0 0 -X axis scale"; +$EditorTool[1, 3] = "0 1 0 +Y axis scale"; +$EditorTool[1, 4] = "0 -1 0 -Y axis scale"; +$EditorTool[1, 5] = "0 0 1 +Z axis scale"; +$EditorTool[1, 6] = "0 0 -1 -Z axis scale"; + +// Moves +$EditorTool[2, 1] = "1 0 0 +X axis move"; +$EditorTool[2, 2] = "-1 0 0 -X axis move"; +$EditorTool[2, 3] = "0 1 0 +Y axis move"; +$EditorTool[2, 4] = "0 -1 0 -Y axis move"; +$EditorTool[2, 5] = "0 0 1 +Z axis move"; +$EditorTool[2, 6] = "0 0 -1 -Z axis move"; + +// Rotates +$EditorTool[3, 1] = "1 0 0 +X axis rotate"; +$EditorTool[3, 2] = "-1 0 0 -X axis rotate"; +$EditorTool[3, 3] = "0 1 0 +Y axis rotate"; +$EditorTool[3, 4] = "0 -1 0 -Y axis rotate"; +$EditorTool[3, 5] = "0 0 1 +Z axis rotate"; +$EditorTool[3, 6] = "0 0 -1 -Z axis rotate"; + +// Modifier Scales +$EditorTool[4, 1] = "0.1"; +$EditorTool[4, 2] = "0.01"; +$EditorTool[4, 3] = "0.001"; +$EditorTool[4, 4] = "0.25"; +$EditorTool[4, 5] = "0.5"; +$EditorTool[4, 6] = "0.75"; +$EditorTool[4, 7] = "1"; +$EditorTool[4, 8] = "5"; + +function ETMessage(%client) +{ + if(!isObject(%client)) + return; + + %mod = (%client.player.scaler $= "" ? 0.25 : %client.player.scaler); + if(%client.player.ETMode == 1) + Bottomprint(%client, ">>>Editor Tool<<<\nMode set to "@getwords($EditorTool[%client.player.ETMode, %client.player.ETSubMode], 3, 6)@" (Modifier: "@%mod@").", 5, 2); + else if(%client.player.ETMode == 2) + Bottomprint(%client, ">>>Editor Tool<<<\nMode set to "@getwords($EditorTool[%client.player.ETMode, %client.player.ETSubMode], 3, 6)@" (Modifier: "@%mod@").", 5, 2); + else if(%client.player.ETMode == 3) + Bottomprint(%client, ">>>Editor Tool<<<\nMode set to "@getwords($EditorTool[%client.player.ETMode, %client.player.ETSubMode], 3, 6)@" (Modifier: "@%mod@").", 5, 2); + else if(%client.player.ETMode == 4) + Bottomprint(%client, ">>>Editor Tool<<<\nModifier scaler: "@$EditorTool[%client.player.ETMode, %client.player.ETSubMode]@".\nShoot to set modifier scaler.", 5, 3); + else + Bottomprint(%client, ">>>Editor Tool<<<\nUnknown mode.", 5, 2); +} + +datablock ItemData(EditingTool) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_energy.dts"; + image = EditingToolImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "an editing tool"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(EditingToolImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy.dts"; + item = EditingTool; + + usesEnergy = true; + minEnergy = 0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateSound[0] = BasicSwitchSound; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.2; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = EditorToolFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + + stateName[5] = "CheckWet"; + stateTransitionOnWet[5] = "Fire"; + stateTransitionOnNotWet[5] = "Fire"; + + stateName[6] = "NoAmmo"; + stateTransitionOnAmmo[6] = "Reload"; + stateTransitionOnTriggerDown[6] = "DryFire"; + stateSequence[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateTimeoutValue[7] = 0.1; + stateTransitionOnTimeout[7] = "Ready"; +}; + +function EditingToolImage::onFire(%data,%obj,%slot) +{ + %mode = %obj.ETMode; + if(%mode == 4) + { + %obj.scaler = $EditorTool[%obj.ETMode, %obj.ETSubMode]; + messageClient(%obj.client, "", "\c2ET: Modifier scaler set to "@%obj.scaler@"."); + return; + } + + %pos = %obj.getMuzzlePoint($WeaponSlot); + %vec = %obj.getMuzzleVector($WeaponSlot); + %targetpos = VectorAdd(%pos, VectorScale(%vec, 200)); + %piece = containerRaycast(%pos, %targetpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType, %obj); + %piece = getWord(%piece, 0); + + if(!isObject(%piece)) + return; + + // Order reversed to eliminate console spam. + if(!isObject(Deployables) || !Deployables.isMember(%piece)) + { + messageClient(%obj.client, "", "\c2ET: That piece is a part of the map and cannot be modified."); + return; + } + + if(%piece.getOwner() != %obj.client && !%obj.client.isAdmin) + { + messageClient(%obj.client, "", "\c2ET: You do not own that!"); + return; + } + + if(%mode == 1) + EditorScale(%obj, %piece); + else if(%mode == 2) + EditorMove(%obj, %piece); + else if(%mode == 3) + EditorRotate(%obj, %piece); + else + messageClient(%obj.client, "", "\c2ET: An error has occured: Invalid Mode. You should not see this error."); +} + +function EditingToolImage::onMount(%this, %obj, %slot) +{ + parent::onMount(%this, %obj, %slot); + if(%obj.ETmode $= "") + %obj.ETmode = 1; + if(%obj.ETSubMode $= "") + %obj.ETSubMode = 1; + + ETMessage(%obj.client); + %obj.usingEditorTool = 1; +} + +function EditingToolImage::onUnmount(%this, %obj, %slot) +{ + parent::onUnmount(%this, %obj, %slot); + %obj.usingEditorTool = 0; +} + +function EditorMove(%player, %piece) +{ + if(%player.scaler $= "") + %player.scaler = 0.25; // default to this. + + if(%piece.isDoor) + { + if(!%piece.canMove) + { + messageClient(%player.client, "", "\c2ET: You can only move stationary doors."); + return; + } + + if(%piece.state !$= "closed") + { + messageClient(%player.client, "", "\c2ET: You can only move fully closed doors."); + return; + } + } + + %next = VectorScale(realvec(%piece, getWords($EditorTool[2, %player.ETSubMode], 0, 2)), %player.scaler); + %newPos = VectorAdd(%piece.getPosition(), %next); + %oldRot = rotFromTransform(%piece.getTransform()); + %piece.setTransform(%newPos SPC %oldRot); + checkAfterRot(%piece); +} + +function EditorScale(%player, %piece) +{ + if(%player.scaler $= "") + %player.scaler = 0.25; // default to this. + + if(%piece.isDoor) + { + if(!%piece.canMove) + { + messageClient(%player.client, "", "\c2ET: You can only scale stationary doors."); + return; + } + + if(%piece.state !$= "closed") + { + messageClient(%player.client, "", "\c2ET: You can only scale fully closed doors."); + return; + } + } + + %axis = VectorScale(getWords($EditorTool[2, %player.ETSubMode], 0, 2), %player.scaler); + if(%piece.getDatablock().className $= "spine" || %piece.getDatablock().className $= "mspine" || %piece.getDatablock().className $= "spine2" || %piece.getDatablock().className $= "floor" || %piece.getDatablock().className $= "wall" || %piece.getDatablock().className $= "wwall" || %piece.getDatablock().className $= "floor" || %piece.getDatablock().className $= "door") + { + %axis = VectorScale(%axis, "0.125 0.166666 1"); + %scale = VectorAdd(%piece.getScale(), %axis); + %check = EditorCheckScale(%scale); + if(%check == 1) + { + messageClient(%player.client, "", "\c2ET: Cannot shrink any further. Piece is too small."); + return; + } + else if(%check == 2) + { + messageClient(%player.client, "", "\c2ET: Cannot expand any further. Piece is too big."); + return; + } + else if(%check != 0 && %check != 1 && %check != 2) + { + messageClient(%player.client, "", "\c2ET: Scale failed. You should not see this error."); + return; + } + + %piece.setScale(%scale); + return; + } + + if(%piece.getDatablock().getName() $= "DeployedLTarget") + { + %scale = VectorAdd(%piece.lMain.getScale(), %axis); + %check = EditorCheckScale(%scale); + if(%check == 1) + { + messageClient(%player.client, "", "\c2ET: Cannot shrink any further. Piece is too small."); + return; + } + else if(%check == 2) + { + messageClient(%player.client, "", "\c2ET: Cannot expand any further. Piece is too big."); + return; + } + else if(%check != 0 && %check != 1 && %check != 2) + { + messageClient(%player.client, "", "\c2ET: Scale failed. You should not see this error."); + return; + } + + %piece.lMain.setScale(%scale); + adjustLMain(%piece); + return; + } + + if(%piece.getDatablock().getName() $= "DeployedLogoProjector") + { + %scale = VectorAdd(%piece.getScale(), %axis); + %check = EditorCheckScale(%scale); + if(%check == 1) + { + messageClient(%player.client, "", "\c2ET: Cannot shrink any further. Piece is too small."); + return; + } + else if(%check == 2) + { + messageClient(%player.client, "", "\c2ET: Cannot expand any further. Piece is too big."); + return; + } + else if(%check != 0 && %check != 1 && %check != 2) + { + messageClient(%player.client, "", "\c2ET: Scale failed. You should not see this error."); + return; + } + + %piece.setScale(%scale); + if(isObject(%piece.holo)) + { + %piece.holo.setScale(%scale); + adjustHolo(%piece); + } + + return; + } + + %scale = VectorAdd(%piece.getScale(), %axis); + %check = EditorCheckScale(%scale); + if(%check == 1) + { + messageClient(%player.client, "", "\c2ET: Cannot shrink any further. Piece is too small."); + return; + } + else if(%check == 2) + { + messageClient(%player.client, "", "\c2ET: Cannot expand any further. Piece is too big."); + return; + } + else if(%check != 0 && %check != 1 && %check != 2) + { + messageClient(%player.client, "", "\c2ET: Scale failed. You should not see this error."); + return; + } + + %piece.setScale(%scale); +} + +function EditorRotate(%player, %piece) +{ + if(%player.scaler $= "") + %player.scaler = 0.25; // default to this. + + if(%piece.isDoor) + { + if(!%piece.canMove) + { + messageClient(%player.client, "", "\c2ET: You can only scale stationary doors."); + return; + } + + if(%piece.state !$= "closed") + { + messageClient(%player.client, "", "\c2ET: You can only scale fully closed doors."); + return; + } + } + + %test = %piece.getEdge("0 0 0"); + %rottoadd = getWords($EditorTool[3, %player.ETSubMode], 0, 2) SPC %player.scaler; + %oldrot = rotFromTransform(%piece.getTransform()); + %oldpos = posFromTransform(%piece.getTransform()); + %newrot = rotAdd(%oldrot, %rottoadd); + %piece.setTransform(%oldpos SPC %newrot); + %piece.setEdge(%test, "0 0 0"); + checkAfterRot(%piece); +} + +function EditorCheckScale(%scale) +{ + if(getwordcount(%scale) < 3) + return -1; + + %x = getword(%scale, 0); + %y = getword(%scale, 1); + %z = getword(%scale, 2); + if(%x < 0.001 || %y < 0.001 || %z < 0.001) + return 1; + if(%x > 300 || %y > 300 || %z > 300) + return 2; + return 0; +} diff --git a/Scripts/Weapons/FlameMortar.cs b/Scripts/Weapons/FlameMortar.cs new file mode 100644 index 0000000..2b601a5 --- /dev/null +++ b/Scripts/Weapons/FlameMortar.cs @@ -0,0 +1,314 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Napalm Mortar +// Made by: Blnukem +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Sound Data +//------------------------------------------------------------------------------ + +datablock EffectProfile(MortarFireEffect) +{ + effectname = "weapons/mortar_fire"; + minDistance = 2.5; + maxDistance = 5.5; +}; + +datablock AudioProfile(MortarFireSound) +{ + filename = "fx/weapons/mortar_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = MortarFireEffect; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Explosion Data +//------------------------------------------------------------------------------ + +datablock ParticleData(NapalmMortarInitExpFlameParticle) +{ + dragCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.1; + lifetimeMS = 2000; + lifetimeVarianceMS = 0; + textureName = "special/Explosion/exp_0016"; + colors[0] = "1 0.18 0.03 0.6"; + colors[1] = "1 0.18 0.03 0.0"; + sizes[0] = 7; + sizes[1] = 8; +}; + +datablock ParticleEmitterData(NapalmMortarInitExpFlameEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionOffset = 2.0; + ejectionVelocity = 20.0; + velocityVariance = 10.0; + thetaMin = 0.0; + thetaMax = 90.0; + lifetimeMS = 250; + + particles = "NapalmMortarInitExpFlameParticle"; +}; + +datablock ParticleData(NapalmMortarExpGroundBurnParticle) +{ + dragCoefficient = 2; + gravityCoefficient = -0.4; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + textureName = "special/cloudflash3.png"; + colors[0] = "1 0.18 0.03 0.6"; + colors[1] = "1 0.18 0.03 0.0"; + sizes[0] = 6; + sizes[1] = 6.75; +}; + +datablock ParticleEmitterData(NapalmMortarExpGroundBurnEmitter) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionOffset = 0.0; + ejectionVelocity = 10.0; + velocityVariance = 10.0; + thetaMin = 87.0; + thetaMax = 88.0; + lifetimeMS = 10000; + + particles = "NapalmMortarExpGroundBurnParticle"; +}; + +datablock ParticleData(NapalmMortarExpGroundBurnSmokeParticle) +{ + dragCoefficient = 2; + gravityCoefficient = -0.4; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "particleTest"; + colors[0] = "0.3 0.3 0.3 0.6"; + colors[1] = "0.3 0.3 0.3 0.0"; + sizes[0] = 3; + sizes[1] = 8; +}; + +datablock ParticleEmitterData(NapalmMortarExpGroundBurnSmokeEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionOffset = 7.0; + ejectionVelocity = 10.0; + velocityVariance = 10.0; + thetaMin = 0.0; + thetaMax = 60.0; + lifetimeMS = 10000; + + particles = "NapalmMortarExpGroundBurnSmokeParticle"; +}; + +datablock ExplosionData(NapalmMortarExplosion) +{ + soundProfile = MortarExplosionSound; + emitter[0] = NapalmMortarInitExpFlameEmitter; + emitter[1] = NapalmMortarExpGroundBurnEmitter; + emitter[2] = NapalmMortarExpGroundBurnSmokeEmitter; + + explosionShape = "effect_plasma_explosion.dts"; + faceViewer = true; + lifetimeMS = 10000; + playSpeed = 0.7; + + sizes[0] = "7.0 7.0 7.0"; + sizes[1] = "7.0 7.0 7.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Particle Data +//------------------------------------------------------------------------------ + +datablock ParticleData(NapalamMortarParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.1; + inheritedVelFactor = 0.1; + + lifetimeMS = 500; + lifetimeVarianceMS = 50; + + textureName = "particleTest"; + + spinRandomMin = -10.0; + spinRandomMax = 10.0; + + colors[0] = "1 0.18 0.03 0.4"; + colors[1] = "1 0.18 0.03 0.3"; + colors[2] = "1 0.18 0.03 0.0"; + sizes[0] = 1.0; + sizes[1] = 0.5; + sizes[2] = 0.08; + times[0] = 0.0; + times[1] = 0.6; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(NapalamMortarEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + + ejectionOffset = 0.2; + ejectionVelocity = 10.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 10.0; + + particles = "NapalamMortarParticle"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Projectile +//------------------------------------------------------------------------------ + +datablock GrenadeProjectileData(NapalmMortarShot) +{ + projectileShapeName = "mortar_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 5.0; + damageRadius = 20.0; + radiusDamageType = $DamageType::Plasma; + kickBackStrength = 2500; + + explosion = NapalmMortarExplosion; + velInheritFactor = 0.5; + splash = MortarSplash; + depthTolerance = 10.0; + + baseEmitter = NapalamMortarEmitter; + + grenadeElasticity = 0.15; + grenadeFriction = 0.4; + armingDelayMS = 2000; + muzzleVelocity = 63.7; + drag = 0.1; + + sound = MortarProjectileSound; + + hasLight = true; + lightRadius = 10; + lightColor = "0.94 0.03 0.12"; + +}; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Napalm Ammo +//------------------------------------------------------------------------------ + +datablock ItemData(NapalmAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_plasma.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a container of napalm"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Weapon Data +//------------------------------------------------------------------------------ + +datablock ItemData(NapalmMortar) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_mortar.dts"; + image = NapalmMortarImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a napalm mortar"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(NapalmMortarImage) +{ + className = WeaponImage; + shapeFile = "weapon_mortar.dts"; + item = NapalmMortar; + ammo = NapalmAmmo; + offset = "0 0 0"; + emap = true; + + minRankPoints = 6000; + + projectile = NapalmMortarShot; + projectileType = GrenadeProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = MortarSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateSequence[3] = "Recoil"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.8; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateSound[3] = MortarFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 5.0; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = MortarReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MortarDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; diff --git a/Scripts/Weapons/Krieg.cs b/Scripts/Weapons/Krieg.cs new file mode 100644 index 0000000..bc9c53c --- /dev/null +++ b/Scripts/Weapons/Krieg.cs @@ -0,0 +1,237 @@ +//-------------------------------------- +// Chaingun +//-------------------------------------- + +datablock AudioProfile(KriegFireSound) +{ + filename = "fx/vehicles/tank_chaingun.wav"; + description = AudioDefault3d; + preload = true; + effect = AssaultChaingunFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + + +datablock TracerProjectileData(KriegBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.4; + directDamageType = $DamageType::Rifle; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + specialCollisionStreamline = true; + HeadMultiplier = 1.5; + LegsMultiplier = 0.35; + + kickBackStrength = 15.0; + sound = ChaingunProjectile; + + dryVelocity = 3000.0; + wetVelocity = 2000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 1000; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 20.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.09; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(KriegAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "Some Rifle Ammo"; + + computeCRC = true; + +}; + +datablock ItemData(RifleClip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "specialized 7.62mm cartidges"; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(KriegRifleImage) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + mass = 10; + item = KriegRifle; + ammo = KriegAmmo; + clip = RifleClip; + projectile = KriegBullet; + projectileType = TracerProjectile; + emap = true; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + decayingSpread = 1; + projectileSpread = 0.5 / 1000.0; + maxSpread = 3 / 1000.0; + + usesClips = 1; + spreadIncreaseRate = 2 / 1000; + // Doesn't apply firing impulse. + clipTimeout = 1750; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.01; + stateFire[3] = true; + stateEmitter[3] = "GunFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 1; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = KriegFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.4; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ChaingunDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "WetFire"; + stateSound[7] = PlasmaFireWetSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "Ready"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "Fire"; +}; + +datablock ItemData(KriegRifle) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_sniper.dts"; + image = KriegRifleImage; + mass = 1.0; + elasticity = 0.0; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Rifle"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(ScopeImage) +{ + className = WeaponImage; + ammo = KriegAmmo; + shapeFile = "weapon_targeting.dts"; + offset = "0.0 -0.2 0.1"; + emap = true; +}; + +datablock ShapeBaseImageData(ClipImage1) +{ + shapeFile = "grenade.dts"; + ammo = KriegAmmo; + offset = "0.0 .2 -0.2"; + emap = true; +}; + +function KriegRifleImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(ScopeImage, 5); + %obj.mountImage(ClipImage1, 6); + if (%obj.inv[KriegAmmo] == 0) + %this.CheckForClip(%obj, %slot); + %obj.hasKrieg = true; +} + +function KriegRifleImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(5); + %obj.unmountImage(6); + %obj.clipReloading = false; +} + +function KriegRifleImage::MountClipEffects(%data, %obj, %slot) +{ + %obj.mountImage(ClipImage1, 6); +} + +function KriegRifleImage::UnmountClipEffects(%data, %obj, %slot) +{ + %obj.unmountImage(6); +} diff --git a/Scripts/Weapons/LightMachineGun.cs b/Scripts/Weapons/LightMachineGun.cs new file mode 100644 index 0000000..d711a40 --- /dev/null +++ b/Scripts/Weapons/LightMachineGun.cs @@ -0,0 +1,245 @@ +//-------------------------------------- +// Light SMG +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(LSMGBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.07; + directDamageType = $DamageType::MP5; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + specialCollisionStreamline = true; + HeadMultiplier = 1.25; + LegsMultiplier = 0.75; + + kickBackStrength = 15.0; + sound = ChaingunProjectile; + + dryVelocity = 1750.0; + wetVelocity = 1500.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 12.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.05; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(LSMGAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some MP12 ammo"; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(LSMGImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = LSMG; + ammo = LSMGAmmo; + clip = LSMGClip; + projectile = LSMGBullet; + projectileType = TracerProjectile; + emap = true; + offset = "0 0.5 0"; // L/R - F/B - T/B + rotation = "0 1 0 180"; + mass = 11; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + decayingSpread = 1; + usesClips = 1; + projectileSpread = 1 / 1000.0; + maxSpread = 3 / 1000; + spreadIncreaseRate = 1.5 / 1000; + clipTimeout = 1500; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + // + stateTimeoutValue[3] = 0.01; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.05; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 0.1; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ItemData(LSMG) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_energy_vehicle.dts"; + image = LSMGImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a MP12 SMG"; + + computeCRC = true; + emap = true; +}; + +datablock ItemData(LSMGClip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some 6mm clips"; + + computeCRC = true; +}; + +datablock ShapeBaseImageData(LSMGmain) +{ + ammo = LSMGAmmo; + shapeFile = "weapon_targeting.dts"; + offset = "0.0 0.0 0.0"; + emap = true; +}; + +datablock ShapeBaseImageData(LSMGClipImage) +{ + shapeFile = "grenade.dts"; + ammo = LSMGAmmo; + offset = "0.0 0.64 -0.2"; + emap = true; +}; + +function LSMGImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(LSMGClipImage, 5); + %obj.mountImage(LSMGmain, 6); + %obj.clipReloading = false; + if (%obj.inv[LSMGAmmo] == 0) + %this.CheckForClip(%obj, %slot); +} + +function LSMGImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(5); + %obj.unmountImage(6); + %obj.clipReloading = false; +} + +function LSMGImage::MountClipEffects(%data, %obj, %slot) +{ + %obj.mountImage(LSMGClipImage, 5); +} + +function LSMGImage::UnmountClipEffects(%data, %obj, %slot) +{ + %obj.unmountImage(5); +} diff --git a/Scripts/Weapons/M4GrenadeLauncher.cs b/Scripts/Weapons/M4GrenadeLauncher.cs new file mode 100644 index 0000000..955b847 --- /dev/null +++ b/Scripts/Weapons/M4GrenadeLauncher.cs @@ -0,0 +1,218 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// M79 Grenade Launcher - Made by Blnukem +// NOTICE: I realized shortly after making this gun that the M4 is a rifle... +// And I was too lazy to change the datablock names... +//------------------------------------------------------------------------------ +// Projectile Data +//------------------------------------------------------------------------------ + +datablock GrenadeProjectileData(M4_RPGrenade) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 1.8; + damageRadius = 10.0; + radiusDamageType = $DamageType::RPG; + kickBackStrength = 3500; + + explosion = "GrenadeExplosion"; + underwaterExplosion = "GrenadeExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.0; + armingDelayMS = -1; + + gravityMod = 1.3; + muzzleVelocity = 100.0; + drag = 0.2; + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 4; + lightColor = "0.05 0.2 0.05"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "0.05 0.075 0.2"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Ammo +//------------------------------------------------------------------------------ + +datablock ItemData(M4Ammo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_grenade.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some M79 RPG ammo"; + + computeCRC = true; + emap = true; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Weapon Data +//------------------------------------------------------------------------------ + +datablock ItemData(M4) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_grenade_launcher.dts"; + image = M4Image; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a M79 RPG Launcher"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(M4Image) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + item = M4; + ammo = M4Ammo; + offset = "0 0 0"; + emap = true; + + minRankPoints = 1250; + + projectile = M4_RPGrenade; + projectileType = GrenadeProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.4; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = GrenadeFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = GenericSwitchSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = GrenadeDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(M4Revolver) : M4Image +{ + offset = "0.0 0.5 -0.1"; + rotation = "1 0 0 90"; + shapeFile = "ammo_disc.dts"; +}; + +function M4Image::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(M4Revolver, 1); + + if (%obj.inv[M4Ammo] == 0) + %obj.M4checkclip = schedule(10, 0, "M4Checkforclip", %obj); +} + +function M4Image::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(1); + + if (%obj.clipReloading != false) + { + Cancel(%obj.RifleClippAdd); + %obj.clipReloading = false; + } +} + +function M4Image::onFire(%data,%obj,%slot) +{ + %obj.decInventory(%data.ammo,1); + + %vector = %obj.getMuzzleVector(%slot); + %mp = %obj.getMuzzlePoint(%slot); + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + + %p = new (%data.projectileType)() + { + dataBlock = %data.projectile; + initialDirection = %newvector; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + + %obj.applyKick(-75); + + if (%obj.inv[M4Ammo] == 0) + %obj.M4checkclip = schedule(10, 0, "M4Checkforclip", %obj); +} + +function M4Checkforclip(%obj) +{ + if (%obj.inv[RifleClip] > 0) + { + %obj.clipReloading = true; + %obj.unmountImage(1); + %obj.RifleClipAdd = schedule(2500, 0, "M4ReloadClip", %obj); + } +} + +function M4ReloadClip(%obj) +{ + %obj.clipReloading = false; + %obj.decInventory(RifleClip, 1); + %obj.mountImage(M4Revolver, 1); + %obj.setInventory(M4Ammo, 4); +} diff --git a/Scripts/Weapons/MG42.cs b/Scripts/Weapons/MG42.cs new file mode 100644 index 0000000..5e9c478 --- /dev/null +++ b/Scripts/Weapons/MG42.cs @@ -0,0 +1,356 @@ +//-------------------------------------- +// Mobile Mg42 +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock DecalData(MG42Decal1) +{ + sizeX = 0.15; + sizeY = 0.15; + textureName = "special/bullethole1"; +}; +datablock DecalData(MG42Decal2) : ChaingunDecal1 +{ + textureName = "special/bullethole2"; +}; + +datablock DecalData(MG42Decal3) : ChaingunDecal1 +{ + textureName = "special/bullethole3"; +}; +datablock DecalData(MG42Decal4) : ChaingunDecal1 +{ + textureName = "special/bullethole4"; +}; +datablock DecalData(MG42Decal5) : ChaingunDecal1 +{ + textureName = "special/bullethole5"; +}; +datablock DecalData(MG42Decal6) : ChaingunDecal1 +{ + textureName = "special/bullethole6"; +}; + + +datablock TracerProjectileData(MG42Bullet) +{ + doDynamicClientHits = true; + + directDamage = 0.13; + directDamageType = $DamageType::MG42; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + specialCollisionStreamline = true; + HeadMultiplier = 1.5; + LegsMultiplier = 0.5; + + kickBackStrength = 50.0; + sound = ChaingunProjectile; + + dryVelocity = 3000.0; + wetVelocity = 1000.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 10.0/255.0 @ " " @ 30.0/255.0 @ " " @ 240.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.2; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = MG42Decal1; + decalData[1] = MG42Decal2; + decalData[2] = MG42Decal3; + decalData[3] = MG42Decal4; + decalData[4] = MG42Decal5; + decalData[5] = MG42Decal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(MG42Clip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some 7.62mm ammo boxes"; + + computeCRC = true; +}; + +datablock ItemData(MG42Ammo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 3; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some SAW ammo"; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(MG42Image) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + item = MG42; + ammo = MG42Ammo; + projectile = MG42Bullet; + projectileType = TracerProjectile; + emap = true; + mass = 26; + + offset = "0.08 0.22 0.15"; // L/R - F/B - T/B + + casing = ShellDebris; + shellExitDir = "0.5 0 1"; + shellExitOffset = "0.0 0.75 0.0"; + shellExitVariance = 5.0; + shellVelocity = 4.5; + + projectileSpread = 12.5 / 1000.0; + maxSpread = 4.5 / 1000.0; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ""; + // + stateTimeoutValue[3] = 0.1; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + stateEmitter[4] = "GunFireEffectEmitter"; + stateEmitterNode[4] = "muzzlepoint1"; + stateEmitterTime[4] = 1; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 0.05; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSound[5] = "MortarReloadSound"; + stateSpinThread[5] = SpinDown; + stateEmitter[5] = "GunFireEffectEmitter"; + stateEmitterNode[5] = "muzzlepoint1"; + stateEmitterTime[5] = 1; + // + stateTimeoutValue[5] = 0.1; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSound[6] = "MortarReloadSound"; + stateSpinThread[6] = SpinDown; + stateEmitter[6] = "GunFireEffectEmitter"; + stateEmitterNode[6] = "muzzlepoint1"; + stateEmitterTime[6] = 10; + // + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ItemData(MG42) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "turret_tank_barrelchain.dts"; + image = MG42Image; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a SAW"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(SAWImage1) +{ + className = WeaponImage; + ammo = MG42Ammo; + shapeFile = "weapon_missile.dts"; + rotation = "0 0 1 180"; + offset = "0.01 0.04 0.0"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(SAWButtImage) +{ + className = WeaponImage; + ammo = MG42Ammo; + shapeFile = "ammo_mortar.dts"; + rotation = "0 0 1 90"; + offset = "-0.06 -0.23 0.25"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(SAWImageclip) +{ + className = WeaponImage; + ammo = MG42Ammo; + shapeFile = "ammo_chaingun.dts"; + offset = "0.08 0.4 -0.13"; // L/R - F/B - T/B + item = MG42; +}; + +function MG42Image::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(SawImageclip, 4); + %obj.mountImage(SawButtImage, 5); + %obj.mountImage(SawImage1, 6); + + if (%obj.inv[MG42Ammo] == 0) + %obj.MG42checkclip = schedule(10, 0, "MG42Checkforclip", %obj); +} + +function MG42Image::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(4); + %obj.unmountImage(5); + %obj.unmountImage(6); + + if (%obj.clipReloading != false) + { + Cancel(%obj.MG42ClipAdd); + %obj.clipReloading = false; + } +} + +// Eolk - note. Don't call parent. This is NOT streamlined. +function MG42Image::onFire(%data,%obj,%slot) +{ + %obj.decInventory(%data.ammo,1); + + if(%obj.spread $= "" || %obj.spread > %data.projectileSpread) + %obj.spread = %data.projectileSpread; + else + %obj.spread = %obj.spread - (0.6 / 1000); + + if(%obj.lowSpreadLoop $= "") + %obj.lowSpreadLoop = schedule(250, 0, "MG42restoreSpread", %data, %obj); + if(%obj.spread < %data.maxspread) + %obj.spread = %data.maxspread; + + %vector = %obj.getMuzzleVector(%slot); + %mp = %obj.getMuzzlePoint(%slot); + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + + %p = new (%data.projectileType)() + { + dataBlock = %data.projectile; + initialDirection = %newvector; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + + %obj.applyKick(-75); + + if (%obj.inv[MG42Ammo] == 0) + %obj.MG42checkclip = schedule(10, 0, "MG42Checkforclip", %obj); +} + +function MG42Checkforclip(%obj) +{ + if (%obj.inv[MG42clip] > 0) + { + %obj.clipReloading = true; + %obj.unmountImage(4); + %obj.MG42ClipAdd = schedule(4000, 0, "MG42ReloadClip", %obj); + } +} + +function MG42ReloadClip(%obj) +{ + %obj.clipReloading = false; + %obj.decInventory(MG42Clip, 1); + %obj.mountImage(SawImageclip, 4); + %obj.setInventory(MG42Ammo, 200); +} + +function MG42restoreSpread(%data, %obj){ + %obj.spread = %obj.spread + (1 / 1000); + if(%obj.spread >= (15 / 1000)){ + %obj.spread = (15 / 1000); + %obj.lowSpreadLoop = ""; + return; + } + %obj.lowSpreadLoop = schedule(150, 0, "MG42restoreSpread", %data, %obj); +} diff --git a/Scripts/Weapons/MergeTool.cs b/Scripts/Weapons/MergeTool.cs new file mode 100644 index 0000000..35bf28c --- /dev/null +++ b/Scripts/Weapons/MergeTool.cs @@ -0,0 +1,726 @@ +// Merge Tool v006 +// Coded by Electricutioner +// Last modified: 6:22 PM 5/21/2006 +// Idea by the T2 Construction Community + +//Variables: +$ElecMod::MergeTool::Tolerance = 0.05; //how many meters of tolerance do we give the pieces that we merge. +$ElecMod::MergeTool::HighTolerance = 0.25; //used for "high tolerance" mode on stubborn pieces +$ElecMod::MergeTool::Timeout = 2; //how many seconds until a selection times out when using the tool + +//split portion +$ElecMod::MergeTool::MinimumPieceVolume = 0.125; //50 cm cube is smallest, consider revising this to 16 + +//Functions: + +//this function rotates and rescales pieces to create the stretch effect +function MTIsometric(%client, %piece) +{ + if (!isObject(%piece)) + return; + + if (!%client.isAdmin) + { + if (%client.guid != %piece.ownerguid) + { + messageClient(%client, 'MsgClient', "\c2IT: That piece isn't yours."); + return; + } + } + + if (!%piece.isForcefield()) + { + %piece.setCloaked(true); + %piece.schedule(290, "setCloaked", false); + } + + %center = %piece.getEdge("0 0 0"); + %currentSize = %piece.getRealSize(); + + //this used to be one operation, but it ceased to work properly (?) + %piece.setTransform(remoteRotate(%piece,"0 1 0 1.570796", %piece,"0 0 0")); + %piece.setRealSize(getWord(%currentSize, 2) SPC getWord(%currentSize, 1) SPC getWord(%currentSize, 0)); + %piece.setEdge(%center, "0 0 0"); + %currentSize = %piece.getRealSize(); + + %piece.setTransform(remoteRotate(%piece,"0 0 1 1.570796", %piece,"0 0 0")); + %piece.setRealSize(getWord(%currentSize, 1) SPC getWord(%currentSize, 0) SPC getWord(%currentSize, 2)); + %piece.setEdge(%center, "0 0 0"); + +} + +// Function by Eolk +function MTZAxisReverse(%client, %piece) +{ + if (!isObject(%piece)) + return; + + if (!%client.isAdmin) + { + if (%client.guid != %piece.ownerguid) + { + messageClient(%client, 'MsgClient', "\c2IT: That piece isn't yours."); + return; + } + } + + if (!%piece.isForcefield()) + { + %piece.setCloaked(true); + %piece.schedule(290, "setCloaked", false); + } + + %center = %piece.getEdge("0 0 0"); + %piece.setTransform(remoteRotate(%piece,"1 0 0 3.141593", %piece,"0 0 0")); + %piece.setEdge(%center, "0 0 0"); +} + +//This is the basic initiator function. If the piece called are compatable, nothing further needs to be called. +function MTMerge(%client, %piece1, %piece2, %hiTol) +{ + if (!MTCheckCompatability(%client, %piece1, %piece2, %hiTol)) + { + %piece1.setCloaked(false); + %piece2.setCloaked(false); + MTClearClientSelection(); + return; + } + if (!%piece1.isForcefield()) + %piece1.setCloaked(true); + if (!%piece2.isForcefield()) + %piece2.setCloaked(true); + schedule(100, 0, "MTScaleShiftMerge", %piece1, %piece2); + + if (isEventPending(%client.mergeschedule)) + { + cancel(%client.mergeschedule); + MTClearClientSelection(); + } +} + +//This function checks if 4 corners of the objects are compatable. If an error is encountered it returns a 0 and +//terminates the merge. Otherwise, it returns a 1, and continues the merge process. +function MTCheckCompatability(%client, %piece1, %piece2, %hiTol) +{ + //do the pieces exist? + if (!isObject(%piece1) || !isObject(%piece2)) + { + messageClient(%client, 'MsgClient', "\c2MT: A piece appears to be missing."); + return; + } + //check if the owners are the same + if (%piece1.owner != %client || %piece2.owner != %client) + { + //with an exemption of admins + if (!%client.isAdmin) + { + //fix for when players leave the server and come back + if (%piece1.ownerGUID != %client.guid || %piece2.ownerGUID != %client.guid) + { + messageClient(%client, 'MsgClient', "\c2MT: One or more of those pieces do not belong to you."); + return; + } + } + } + + //now we need to determine if at least 4 of the pieces axies match + //get all the 8 points of both pieces + %pos1[0] = %piece1.getEdge("1 1 -1"); + %pos1[1] = %piece1.getEdge("-1 1 -1"); + %pos1[2] = %piece1.getEdge("1 -1 -1"); + %pos1[3] = %piece1.getEdge("-1 -1 -1"); + %pos1[4] = %piece1.getEdge("1 1 1"); + %pos1[5] = %piece1.getEdge("-1 1 1"); + %pos1[6] = %piece1.getEdge("1 -1 1"); + %pos1[7] = %piece1.getEdge("-1 -1 1"); + + %pos2[0] = %piece2.getEdge("1 1 -1"); + %pos2[1] = %piece2.getEdge("-1 1 -1"); + %pos2[2] = %piece2.getEdge("1 -1 -1"); + %pos2[3] = %piece2.getEdge("-1 -1 -1"); + %pos2[4] = %piece2.getEdge("1 1 1"); + %pos2[5] = %piece2.getEdge("-1 1 1"); + %pos2[6] = %piece2.getEdge("1 -1 1"); + %pos2[7] = %piece2.getEdge("-1 -1 1"); + //then we compare them to see which ones match + %k = 0; + for (%i = 0; %i < 8; %i++) + { + for (%j = 0; %j < 8; %j++) + { + if (!%hiTol && $ElecMod::MergeTool::Tolerance >= vectorDist(%pos1[%i], %pos2[%j])) + { + %k++; + } + else if (%hiTol && $ElecMod::MergeTool::HighTolerance >= vectorDist(%pos1[%i], %pos2[%j])) + { + %k++; + } + } + } + //if less then 4 match, they can't be compatable (if more then 4 match... something odd is going on) + if (%k < 4) + { + if (%k == 0) + messageClient(%client, 'MsgClient', "\c2MT: None of the corners are shared on those objects. Cannot merge."); + else + messageClient(%client, 'MsgClient', "\c2MT: Only " @ %k @ " corners of the required 4 are shared. Cannot merge."); + + return; + } + else if (%k > 4) + { + messageClient(%client, 'MsgClient', "\c2MT: Warning: Detected match of over 4 corners (" @ %k @ "). Merging may fail to produce desired results."); + } + //if the check survived that, we continue... + return 1; +} + +//this function, after the pieces are confirmed, checks to see which of the 6 sides is in contact, refers to another +//function to find the alter side, determines distance between them to find a new "real" scale, and determines the +//new world box center for the object. Piece 1 is resized and moved, piece 2 is deconstructed. +function MTScaleShiftMerge(%piece1, %piece2) +{ + //find which axis is touching for a "this is the side we scale" discovery + //table: + //0: X + //1: -X + //2: Y + //3: -Y + //4: Z + //5: -Z + + %p1S[0] = %piece1.getEdge("1 0 0"); + %p1S[1] = %piece1.getEdge("-1 0 0"); + %p1S[2] = %piece1.getEdge("0 1 0"); + %p1S[3] = %piece1.getEdge("0 -1 0"); + %p1S[4] = %piece1.getEdge("0 0 1"); + %p1S[5] = %piece1.getEdge("0 0 -1"); + + %p2S[0] = %piece2.getEdge("1 0 0"); + %p2S[1] = %piece2.getEdge("-1 0 0"); + %p2S[2] = %piece2.getEdge("0 1 0"); + %p2S[3] = %piece2.getEdge("0 -1 0"); + %p2S[4] = %piece2.getEdge("0 0 1"); + %p2S[5] = %piece2.getEdge("0 0 -1"); + + for (%i = 0; %i < 6; %i++) + { + for (%j = 0; %j < 8; %j++) + { + if ($ElecMod::MergeTool::Tolerance >= vectorDist(%p1S[%i], %p2S[%j])) + { + %side1 = %i; + %side2 = %j; + } + } + } + //echo("Sides:" SPC %i SPC %j); + //at this point %side1/2 will contain one of the numbers in the table above + //we get the non-shared side at this point + %ops1 = MTFindOpSide(%side1); + %ops2 = MTFindOpSide(%side2); + + //this variable contains the new axis length that we are scaling on... + %newaxis = VectorDist(%p1S[%ops1], %p2S[%ops2]); + %currsize = %piece1.getRealSize(); + + if (%side1 == 0 || %side1 == 1) + %axis = "x"; + if (%side1 == 2 || %side1 == 3) + %axis = "y"; + if (%side1 == 4 || %side1 == 5) + %axis = "z"; + + //echo("Axis:" SPC %axis); + if (%axis $= "x") + { + %piece1.setRealSize(%newaxis SPC getWords(%currsize, 1, 2)); + if (isObject(%piece1.pzone)) + %piece1.pzone.setScale(%newaxis SPC getWords(%currsize, 1, 2)); + } + if (%axis $= "y") + { + %piece1.setRealSize(getWord(%currsize, 0) SPC %newaxis SPC getWord(%currsize, 2)); + if (isObject(%piece1.pzone)) + %piece1.pzone.setScale(getWord(%currsize, 0) SPC %newaxis SPC getWord(%currsize, 2)); + } + if (%axis $= "z") + { + %piece1.setRealSize(getWords(%currsize, 0, 1) SPC %newaxis); + if (isObject(%piece1.pzone)) + %piece1.pzone.setScale(getWords(%currsize, 0, 1) SPC %newaxis); + } + if (%axis !$= "x" && %axis !$= "y" && %axis !$= "z") + { + error("MT: A scaling error has occured."); + return; + } + %newpos = VectorScale(VectorAdd(%p1S[%ops1], %p2S[%ops2]), 0.5); + %piece1.SetWorldBoxCenter(%newpos); + + if (isObject(%piece1.pzone)) + %piece1.pzone.setPosition(%piece1.getPosition()); + + if (!%piece1.isForcefield()) + %piece1.setCloaked(false); + if (!%piece2.isForcefield()) + %piece2.setCloaked(false); + + //%piece2.delete(); //deleting is bad + //%piece2.getDataBlock().disassemble(0, %piece2.owner, %piece2); //disassemble is cleaner + //fixed disassemble to use object specific disassemble functions + //disassemble(0, %piece2.owner, %piece2); + %piece2.getDatablock().disassemble(0, %piece2); +} + +//this function does something very simple, it finds whether a number is even or odd, and then adds or subracts +//and returns the initial input with that modification. I can't imagine where else this could be useful. +function MTFindOpSide(%side) +{ + %evencheck = %side / 2; + if (%evencheck == mFloor(%evencheck)) + %even = 1; + else + %even = 0; + + if (%even) + return (%side + 1); + else + return (%side - 1); +} + +//simply clears a client variable... woohoo... +function MTClearClientSelection(%client) +{ + %client.mergePiece1 = ""; + return; +} + + +//"weapon" datablocks and such +datablock ItemData(MergeTool) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_sniper.dts"; + image = MergeToolImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a MIST, by Electricutioner."; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(MergeToolImage) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + item = MergeTool; + + usesEnergy = true; + minEnergy = 0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateSound[0] = GenericSwitchSound; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.2; //reload timeout here + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = "MergeToolFireSound"; + + stateName[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + + stateName[5] = "CheckWet"; + stateTransitionOnWet[5] = "Fire"; + stateTransitionOnNotWet[5] = "Fire"; + + stateName[6] = "NoAmmo"; + stateTransitionOnAmmo[6] = "Reload"; + stateTransitionOnTriggerDown[6] = "DryFire"; + stateSequence[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = SniperRifleDryFireSound; + stateTimeoutValue[7] = 0.1; + stateTransitionOnTimeout[7] = "Ready"; +}; + +function MergeToolImage::onFire(%data,%obj,%slot) +{ + serverPlay3D(SniperRifleFireSound, %obj.getTransform()); + %client = %obj.client; + + %pos = getWords(%obj.getEyeTransform(), 0, 2); + %vec = %obj.getEyeVector(); + %targetpos = VectorAdd(%pos, VectorScale(%vec, 2000)); + %piece = containerRaycast(%pos, %targetpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType, %obj); + %cast = %piece; + %piece = getWord(%piece, 0); + + if (!isObject(%piece)) + return; + + if (!Deployables.isMember(%piece)) + { + messageClient(%client, 'MsgClient', "\c2MIST: That piece is part of the map and cannot be manipulated."); + return; + } + + if (!%client.isAdmin) + { + if (%piece.ownerGUID != %client.guid) + { + messageClient(%client, 'MsgClient', "\c2MIST: That piece isn't yours."); + return; + } + } + + if (%client.MTMode == 0) + { + if (!isObject(%client.mergePiece1)) + { + %client.mergePiece1 = %piece; + + if (!%piece.isForcefield()) + { + %piece.setCloaked(true); + %piece.schedule(290, "setCloaked", false); + } + + %client.mergeschedule = schedule($ElecMod::MergeTool::Timeout * 1000, 0, "MTClearClientSelection", %client); + } + else + { + if (%piece != %client.mergePiece1) + { + if (%client.MTSubMode == 1) + { + MTMerge(%client, %client.mergePiece1, %piece, 1); + %client.MTSubMode = 0; + MTShowStatus(%client); + } + else + { + MTMerge(%client, %client.mergePiece1, %piece, 0); + } + MTClearClientSelection(%client); + } + else + { + messageClient(%client, 'MsgClient', "\c2MT: You cannot merge a piece with itself."); + return; + } + } + } + if (%client.MTMode == 1) + { + if (%client.MTSubMode == 1) + MTZAxisReverse(%client, %piece); + else + MTIsometric(%client, %piece); + } + if (%client.MTMode == 2) + { + if (%client.MTSubMode > 4 || %client.MTSubMode == 1) + { + %client.MTSplitMode = 1; //crosshair split + } + else + { + %client.MTSplitMode = 0; //half split + } + if (%client.MTSubMode == 0) + %axis = "a"; + if (%client.MTSubMode == 1) + %axis = "a"; + if (%client.MTSubMode == 2) + %axis = "x"; + if (%client.MTSubMode == 3) + %axis = "y"; + if (%client.MTSubMode == 4) + %axis = "z"; + if (%client.MTSubMode == 5) + %axis = "x"; + if (%client.MTSubMode == 6) + %axis = "y"; + if (%client.MTSubMode == 7) + %axis = "z"; + + MTSplit(%client, %cast, %axis); + } +} + +function MergeToolImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(MergeToolImage, 0); + MTShowStatus(%obj.client); +} + +function MTShowStatus(%client) +{ + if (%client.MTMode $= "") + %client.MTMode = 0; + if (%client.MTSubMode $= "") + %client.MTSubMode = 0; + + switch (%client.MTMode) + { + case 0: + switch (%client.MTSubMode) + { + case 0: + %status = "Mode: Merge. Fire the tool at two pieces. If possible, they will merge. Tolerance: " @ $ElecMod::MergeTool::Tolerance @ " meters."; + case 1: + %status = "Mode: Merge. Fire the tool at two pieces. If possible, they will merge. Tolerance: " @ $ElecMod::MergeTool::HighTolerance @ " meters."; + } + case 1: + switch (%client.MTSubMode) + { + case 0: + %status = "Mode: Isometric. Fire at a piece. It will be rotated isometrically."; + case 1: + %status = "Mode: Isometric Z-axis Reversal. Fire at a piece. It's Z Axis will be reversed."; + } + case 2: + switch (%client.MTSubMode) + { + case 0: + %status = "Mode: Split. Fire at a piece to split it in half. Axis: Automatic."; + case 1: + %status = "Mode: Split. Fire at a piece to split it on crosshair. Axis: Automatic."; + case 2: + %status = "Mode: Split. Fire at a piece to split it in half. Axis: X."; + case 3: + %status = "Mode: Split. Fire at a piece to split it in half. Axis: Y."; + case 4: + %status = "Mode: Split. Fire at a piece to split it in half. Axis: Z."; + case 5: + %status = "Mode: Split. Fire at a piece to split it on crosshair. Axis: X."; + case 6: + %status = "Mode: Split. Fire at a piece to split it on crosshair. Axis: Y."; + case 7: + %status = "Mode: Split. Fire at a piece to split it on crosshair. Axis: Z."; + } + } + CommandToClient(%client, 'BottomPrint', ">>>M/I/S Tool<<<\n" @ %status @ "\nCoded by Electricutioner.", 3, 3 ); +} + +//Split code begins here. +//The goal of this is to be a semi-inverse of the merge... +//The tool will be set to split mode, and aimed at an object. The object axies are checked, and if there is one that is +//disproportionally larger then the rest, it will be split on that axis. If two or more axies are similar, it does +//a check to determine the face where the raycast hits (reducing split possibilities by one axis) and either +//spliting on the remaining axis or using position points to find out in which quadrant of the face, the raycast hit. +//Once the axis is determined, the split axis has the difference halved, a "dominant" piece rescaled and repositioned +//in steps similar to the merge, and a new (nearly-identical) piece is created in the resulting void. + +//startup function, the calling client, and the raycasted piece. Note: %piece contains all raycast operations. +function MTSplit(%client, %piece, %axis) +{ + if(!MTSplitValidate(%client, %piece, %axis)) //validates the client selected piece as valid + return; + + if (!%piece.isForcefield()) + getWord(%piece, 0).setCloaked(true); + + MTSplitScaleShift(%client, %piece, %axis); //split it up +} + +//makes sure that the object can be split +function MTSplitValidate(%client, %piece, %axis) +{ + if (!isObject(getWord(%piece, 0))) + { + messageClient(%client, 'MsgClient', "\c2ST: The piece to split is missing. You should not see this error."); + return; + } + + //restricting to cubics and forcefields. + if (!isCubic(%piece) && !%piece.isForceField()) + { + messageClient(%client, 'MsgClient', "\c2ST: That object is not cubic and it cannot be split."); + return; + } + + %size = %piece.getRealSize(); + %volume = 2 * getWord(%size, 0) * 2 * getWord(%size, 1) * 2 * getWord(%size, 2); + if (%client.MTSplitMode == 0) //half split + { + if ((%volume / 2) < ($ElecMod::MergeTool::MinimumPieceVolume)) + { + messageClient(%client, 'MsgClient', "\c2ST: That piece is too small to split."); + return; + } + } + else + { + %hitPos = getWord(%piece, 1) SPC getWord(%piece, 2) SPC getWord(%piece, 3); + %edge = PointToEdge(getWord(%piece, 0), %hitPos); + + //auto-axis determiner + if (%axis $= "a") + %axis = CalculateSplitAxis(PointToEdge(%piece, %hitPos)); + + switch$ (%axis) + { + case "x": + %ratio = getWord(%edge, 0); + %ratio = (%ratio + 1) / 2; + case "y": + %ratio = getWord(%edge, 1); + %ratio = (%ratio + 1) / 2; + case "z": + %ratio = getWord(%edge, 2); + %ratio = (%ratio + 1) / 2; + } + + %masterSize = %volume * %ratio; + %slaveSize = %volume * (1 - %ratio); + + //echo(%ratio SPC %axis SPC %masterSize SPC %slaveSize); + + if (%masterSize < $ElecMod::MergeTool::MinimumPieceVolume || %slaveSize < $ElecMod::MergeTool::MinimumPieceVolume) + { + messageClient(%client, 'MsgClient', "\c2ST: A resultant piece from that split is too small. Aborting."); + return; + } + } + + return 1; //we survived, thus we continue +} + +//does the actual splitting +function MTSplitScaleShift(%client, %cast, %axis) +{ + %piece = getWord(%cast, 0); + %copy = MTCarbonCopy(%piece); //Merge Tool Support functions + + if (!%piece.isForcefield()) + { + %piece.setCloaked(true); + %copy.setCloaked(true); + } + + %hitPos = getWord(%cast, 1) SPC getWord(%cast, 2) SPC getWord(%cast, 3); + + %size = %piece.getRealSize(); + + //auto axis determiner + if (%axis $= "a") + { + %axis = CalculateSplitAxis(PointToEdge(%piece, %hitPos)); + //echo(%hitPos); + } + + if (%client.MTSplitMode == 0) //split in half + { + %center = %piece.getWorldBoxCenter(); + %sizeFactorMaster = 2; + %sizeFactorSlave = 2; + + switch$ (%axis) + { + case "x": + %piece.setRealSize((getWord(%size, 0) / %sizeFactorMaster) SPC getWords(%size, 1, 2)); + %copy.setRealSize((getWord(%size, 0) / %sizeFactorSlave) SPC getWords(%size, 1, 2)); + %piece.setEdge(%center, "1 0 0"); + %copy.setEdge(%center, "-1 0 0"); + case "y": + %piece.setRealSize(getWord(%size, 0) SPC getWord(%size, 1) / %sizeFactorMaster SPC getWord(%size, 2)); + %copy.setRealSize(getWord(%size, 0) SPC getWord(%size, 1) / %sizeFactorSlave SPC getWord(%size, 2)); + %piece.setEdge(%center, "0 1 0"); + %copy.setEdge(%center, "0 -1 0"); + case "z": + %piece.setRealSize(getWords(%size, 0, 1) SPC getWord(%size, 2) / %sizeFactorMaster); + %copy.setRealSize(getWords(%size, 0, 1) SPC getWord(%size, 2) / %sizeFactorSlave); + %piece.setEdge(%center, "0 0 1"); + %copy.setEdge(%center, "0 0 -1"); + } + } + else + { + %edge = PointToEdge(%piece, %hitPos); //PointToEdge is in the Merge Tool support functions + + switch$ (%axis) + { + case "x": + %ratio = getWord(%edge, 0); + %ratio = (%ratio + 1) / 2; + %center = %piece.getEdge("-1 0 0"); + %piece.setRealSize(getWord(%size, 0) * %ratio SPC getWords(%size, 1, 2)); + %copy.setRealSize(getWord(%size, 0) * (1 - %ratio) SPC getWords(%size, 1, 2)); + %piece.setEdge(%center, "-1 0 0"); + %copy.setEdge(%piece.getEdge("1 0 0"), "-1 0 0"); + case "y": + %ratio = getWord(%edge, 1); + %ratio = (%ratio + 1) / 2; + %center = %piece.getEdge("0 -1 0"); + %piece.setRealSize(getWord(%size, 0) SPC getWord(%size, 1) * %ratio SPC getWord(%size, 2)); + %copy.setRealSize(getWord(%size, 0) SPC getWord(%size, 1) * (1 - %ratio) SPC getWord(%size, 2)); + %piece.setEdge(%center, "0 -1 0"); + %copy.setEdge(%piece.getEdge("0 1 0"), "0 -1 0"); + case "z": + %ratio = getWord(%edge, 2); + %ratio = (%ratio + 1) / 2; + %center = %piece.getEdge("0 0 -1"); + %piece.setRealSize(getWords(%size, 0, 1) SPC getWord(%size, 2) * %ratio); + %copy.setRealSize(getWords(%size, 0, 1) SPC getWord(%size, 2) * (1 - %ratio)); + %piece.setEdge(%center, "0 0 -1"); + %copy.setEdge(%piece.getEdge("0 0 1"), "0 0 -1"); + } + } + + if (isObject(%piece.pzone)) + { + %piece.pzone.setScale(%piece.getScale()); + %piece.pzone.setPosition(%piece.getPosition()); + } + if (isObject(%copy.pzone)) + { + %copy.pzone.setScale(%copy.getScale()); + %copy.pzone.setPosition(%copy.getPosition()); + } + + if (!%piece.isForcefield()) + { + %piece.schedule(290, "setCloaked", false); + %copy.schedule(290, "setCloaked", false); + } +} + +function CalculateSplitAxis(%edge) +{ + //echo("Calculating split axis from edge: " @ %edge); + %edge = mAbs(getWord(%edge, 0)) SPC mAbs(getWord(%edge, 1)) SPC mAbs(getWord(%edge, 2)); + if (getWord(%edge, 0) < getWord(%edge, 1) && getWord(%edge, 0) < getWord(%edge, 2)) + return "x"; + if (getWord(%edge, 1) < getWord(%edge, 0) && getWord(%edge, 1) < getWord(%edge, 2)) + return "y"; + if (getWord(%edge, 2) < getWord(%edge, 0) && getWord(%edge, 2) < getWord(%edge, 1)) + return "z"; +} + +exec("scripts/do_not_delete/MergeToolSupport.cs"); diff --git a/Scripts/Weapons/PBC.cs b/Scripts/Weapons/PBC.cs new file mode 100644 index 0000000..fdee367 --- /dev/null +++ b/Scripts/Weapons/PBC.cs @@ -0,0 +1,559 @@ +//-------------------------------------------------------------------------- +// Plasma Beam Cannon +//-------------------------------------------------------------------------- + +datablock ShockwaveData(PBCShockwave) +{ + width = 1.0; + numSegments = 32; + numVertSegments = 6; + velocity = 20; + acceleration = 0.0; + lifetimeMS = 1000; + height = 4.0; + verticalCurve = 0.5; + is2D = false; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "0.1 1.0 0.1 0.50"; + colors[1] = "0.8 0.8 1.0 1.0"; + colors[2] = "0.8 0.8 1.0 1.0"; + + mapToTerrain = true; + orientToNormal = false; + renderBottom = false; +}; + +datablock ParticleData(PBCParticle) { + dragCoefficient = 0.5; + WindCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 1; + constantAcceleration = 0; + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + textureName = "special/lightning1frame2"; + useInvAlpha = 0; + + spinRandomMin = -800; + spinRandomMax = 800; + + spinspeed = 50; + colors[0] = "0.1 1.0 0.1 1.0"; + colors[1] = "0.6 0.9 0.6 1.0"; + colors[2] = "0.8 0.8 1.0 1.0"; + colors[3] = "0.8 0.8 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 1; + sizes[2] = 1.5; + sizes[3] = 1.5; + times[0] = 0.0; + times[1] = 0.1; + times[2] = 0.3; + times[3] = 1.0; +}; + +datablock ParticleEmitterData(PBCExpEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + + ejectionVelocity = 2.5; + velocityVariance = 2.5; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "PBCParticle"; +}; + +datablock ParticleEmitterData(PBCExpEmitter2) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 10.0; + velocityVariance = 10.0; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 200; + + particles = "PBCParticle"; +}; + +datablock ExplosionData(PBCsubExplosion) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + + emitter[0] = PBCExpEmitter2; + + delayMS = 100; + + offset = 2.0; + + playSpeed = 1.25; + + sizes[0] = "0.3 0.3 0.3"; + sizes[1] = "0.3 0.3 0.3"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(PBCexplosion) +{ + soundProfile = sniperExpSound; + + shockwave = PBCShockwave; + shockwaveOnTerrain = true; + + explosionShape = "disc_explosion.dts"; + faceViewer = true; + playSpeed = 1.5; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 1.0; + + emitter[0] = PBCExpEmitter; + + subExplosion[0] = PBCsubExplosion; + + shakeCamera = true; + camShakeFreq = "10.0 11.0 10.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 10.0; +}; + +//---------------------- +// Firing Effects +//---------------------- + +datablock ParticleData(PBCChargeSmoke) +{ + dragCoeffiecient = 0; + WindCoefficient = 0; + gravityCoefficient = 0; // rises slowly + inheritedVelFactor = 0.025; + constantAcceleration = -12; + + lifetimeMS = 1000; + lifetimeVarianceMS = 0; + + useInvAlpha = 0; + + spinRandomMin = -800; + spinRandomMax = 800; + + spinspeed = 50; + + textureName = "special/lightning1frame2"; + + colors[0] = "0.5 1.0 0.5 1.0"; + colors[1] = "0.5 1.0 0.5 1.0"; + sizes[0] = 0.1; + sizes[1] = 0.1; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ParticleEmitterData(PBCChargeEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionOffset = 0.5; + + ejectionVelocity = 0.05; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 10; + + particles = "PBCChargeSmoke"; +}; + +//-------------------------------------- +// Projectile +//-------------------------------------- + +datablock TargetProjectileData(PBCTlProj) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + + maxRifleRange = 1000; + beamColor = "0.1 1.0 0.1"; + + startBeamWidth = 0.3; //0.02 + pulseBeamWidth = 0.7; //0.025 + beamFlareAngle = 3.0; + minFlareSize = 0.0; + maxFlareSize = 400.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + textureName[0] = "special/nonlingradient"; + textureName[1] = "special/flare"; + textureName[2] = "special/pulse"; + textureName[3] = "special/expFlare"; + beacon = true; +}; + +datablock TracerProjectileData(PBCMainProj) +{ + doDynamicClientHits = true; + + directDamage = 1.0; + explosion = "PBCexplosion"; + splash = ChaingunSplash; + + directDamageType = $DamageType::PBC; + kickBackStrength = 0.0; + + sound = ShrikeBlasterProjectileSound; + + dryVelocity = 5000.0; + wetVelocity = 0.0; + velInheritFactor = 1.0; + fizzleTimeMS = 200; + lifetimeMS = 200; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = true; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 45.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = "0.1 1.0 0.1 1.0"; + tracerTex[0] = "special/shrikeBolt"; + tracerTex[1] = "special/shrikeBoltCross"; + tracerWidth = 4.4; + crossSize = 0.99; + crossViewAng = 0.990; + renderCross = true; +}; + +datablock DebrisData( PBCCellDebris ) +{ + shapeName = "ammo_plasma.dts"; + + lifetime = 5.0; + + minSpinSpeed = 0.0; + maxSpinSpeed = 1.0; + + elasticity = 0.5; + friction = 0.2; + + numBounces = 3; + + fade = true; + staticOnMaxBounce = true; + snapOnMaxBounce = true; +}; + +//-------------------------------------- +// Rifle and item... +//-------------------------------------- +datablock ItemData(PBC) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_sniper.dts"; + image = PBCImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Plasma Beam Cannon"; + + computeCRC = true; + +}; + +datablock ItemData(PBCAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_plasma.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "A Plasma Beam Cannon power cell"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(PBCImage) +{ + className = WeaponImage; + shapeFile = "weapon_mortar.dts"; + item = PBC; + projectile = PBCMainProj; + projectileType = TracerProjectile; + ammo = PBCAmmo; + armThread = looksn; + + usesEnergy = false; + + casing = PBCCellDebris; + shellExitDir = "0.0 0.0 -1.0"; // L/R - F/B - T/B + shellExitOffset = "0.0 0.0 -0.4"; + shellExitVariance = 0.0; + shellVelocity = 1.0; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = ChaingunSwitchSound; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 1.0; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateScript[1] = "onReady"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ChaingunSpinupSound; + stateScript[3] = "onSpinup"; + stateTimeoutValue[3] = 2; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSpinThread[4] = FullSpeed; + + stateEmitter[4] = "PBCShootEmitter"; + stateEmitterNode[4] = "muzzlepoint1"; + stateEmitterTime[4] = 0.1; + stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateTimeoutValue[4] = 1.0; + stateTransitionOnTimeout[4] = "Spindown"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + stateScript[5] = "onSpindown"; + stateTimeoutValue[5] = 1.5; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSpinThread[6] = SpinDown; + stateScript[6] = "onSpindown"; + stateTimeoutValue[5] = 1.5; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ShapeBaseImageData(PBCTLImage) +{ + className = WeaponImage; + + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 0 0"; + + projectile = PBCTlProj; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + offset = "0.1 0.75 0.0"; // L/R - F/B - T/B + + usesEnergy = true; + minEnergy = 0; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = TargetingLaserSwitchSound; + stateTimeoutValue[0] = 0.15; + stateTransitionOnTimeout[0] = "Fire"; + + stateName[3] = "Fire"; + stateEnergyDrain[3] = 0; + stateFire[3] = true; + stateScript[3] = "onFire"; +}; + +datablock ShapeBaseImageData(PBCPowerCellImage) +{ + className = WeaponImage; + ammo = PBCAmmo; + shapeFile = "ammo_plasma.dts"; + rotation = "-1 0 0 90"; + offset = "0.08 0.9 -0.3"; // L/R - F/B - T/B +}; + +datablock ShapeBaseImageData(PBCDummy1) +{ + className = WeaponImage; + ammo = PBCAmmo; + shapeFile = "pack_upgrade_satchel.dts"; + rotation = "1 0 0 90"; + offset = "0.08 0.45 0.0"; // L/R - F/B - T/B + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateSequence[1] = "fire"; + stateTransitionOnTriggerUp[1] = "Idle"; +}; +datablock ShapeBaseImageData(PBCDummy2) +{ + className = WeaponImage; + ammo = PBCAmmo; + shapeFile = "pack_upgrade_shield.dts"; + rotation = "0 0 1 90"; + offset = "0.05 0.55 0"; // L/R - F/B - T/B + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateSequence[1] = "fire"; + stateTransitionOnTriggerUp[1] = "Idle"; +}; +datablock ShapeBaseImageData(PBCDummy3) +{ + className = WeaponImage; + ammo = PBCAmmo; + shapeFile = "pack_upgrade_shield.dts"; + rotation = "0 0 -1 90"; + offset = "0.12 0.55 0"; // L/R - F/B - T/B + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateSequence[1] = "fire"; + stateTransitionOnTriggerUp[1] = "Idle"; +}; + +function PBCImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(PBCDummy1, 3); + %obj.mountImage(PBCDummy2, 4); + %obj.mountImage(PBCDummy3, 5); +} + +function PBCImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(3); + %obj.unmountImage(4); + %obj.unmountImage(5); + %obj.unmountImage(6); +} +function PBCImage::onSpinup(%data,%obj,%slot){ + %obj.mountImage(PBCTLImage, 7); + PBCCharge(%obj, 0); + %obj.setImageTrigger(3, true); + %obj.setImageTrigger(4, true); + %obj.setImageTrigger(5, true); +} + +function PBCImage::onSpindown(%data,%obj,%slot){ + %obj.unmountImage(7); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); +} + +function PBCImage::onReady(%data,%obj,%slot) +{ + %obj.mountImage(PBCPowerCellImage, 6); +} + +function PBCImage::onFire(%data,%obj,%slot){ + serverPlay3d("SniperFireSound",%obj.getmuzzlepoint(0)); + %p = Parent::onFire(%data, %obj, %slot); + %obj.unmountImage(6); +} + +function PBCTLImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.PBCLP = %p; + %p.schedule(10000, "delete"); +} + +function PBCTLImage::onUnmount(%this,%obj,%slot) +{ + %obj.PBCLP.delete(); + Parent::onUnmount(%this, %obj, %slot); +} + +function PBCCharge(%obj,%count){ + if(!isobject(%obj)) + return; + if(%count <= 9){ + %charge = new ParticleEmissionDummy() + { + position = %obj.getMuzzlePoint(0); + dataBlock = "defaultEmissionDummy"; + emitter = "PBCChargeEmitter"; + }; + MissionCleanup.add(%charge); + %charge.schedule(100, "delete"); + %count++; + %obj.PBCCharging = schedule(100, %obj, "PBCCharge", %obj, %count); + } +} diff --git a/Scripts/Weapons/PulseLaser.cs b/Scripts/Weapons/PulseLaser.cs new file mode 100644 index 0000000..df5d4ad --- /dev/null +++ b/Scripts/Weapons/PulseLaser.cs @@ -0,0 +1,260 @@ +//-------------------------------------------------------------------------- +// Sniper rifle +// +// +//-------------------------------------------------------------------------- + +datablock AudioProfile(LaserRifleFireSound) +{ + filename = "fx/weapons/cg_hard1.wav"; + description = AudioClose3d; + preload = true; + effect = SniperRifleFireEffect; +}; + +//-------------------------------------------------------------------------- +// Splash +//-------------------------------------------------------------------------- +datablock ParticleData( LaserSplashParticle ) +{ + + dragCoeffiecient = 0.4; + gravityCoefficient = -0.03; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 600; + lifetimeVarianceMS = 300; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + + 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] = 1.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( LaserSplashEmitter ) +{ + ejectionPeriodMS = 25; + ejectionOffset = 0.2; + periodVarianceMS = 0; + ejectionVelocity = 2.25; + velocityVariance = 0.50; + thetaMin = 0.0; + thetaMax = 30.0; + lifetimeMS = 250; + + particles = "LaserSplashParticle"; +}; + +datablock SplashData( LaserSplash ) +{ + numSegments = 5; + ejectionFreq = 0.0001; + ejectionAngle = 45; + ringLifetime = 0.5; + lifetimeMS = 400; + velocity = 5.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = SniperSplashEmitter; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 0.0"; + 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; +}; + +//-------------------------------------------------------------------------- +// Explosion +//-------------------------------------- +datablock AudioProfile(sniperExpSound) +{ + filename = "fx/weapons/sniper_impact.WAV"; + description = AudioClosest3d; + preload = true; +}; + +datablock ParticleData(LaserExplosionParticle1) +{ + dragCoefficient = 0.65; + gravityCoefficient = 0.3; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 150; + textureName = "particleTest"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 0.0"; + sizes[0] = 0.0625; + sizes[1] = 0.2; +}; + +datablock ParticleEmitterData(LaserExplosionEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 0.75; + velocityVariance = 0.25; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "LaserExplosionParticle1"; +}; + +datablock ExplosionData(LaserExplosion) +{ + explosionShape = "energy_explosion.dts"; + soundProfile = sniperExpSound; + + particleEmitter = LaserExplosionEmitter; + particleDensity = 150; + particleRadius = 0.25; + + faceViewer = false; +}; + + +//-------------------------------------- +// Projectile +//-------------------------------------- +datablock SniperProjectileData(PulseLaserShot) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + sound = LaserProjectileSound; + explosion = "LaserExplosion"; + splash = LaserSplash; + directDamageType = $DamageType::PulseLaser; + + maxRifleRange = 1000; + rifleHeadMultiplier = 1.3; + beamColor = "1 0.1 0.1"; + fadeTime = 1.0; + + startBeamWidth = 0.145; + endBeamWidth = 0.25; + pulseBeamWidth = 0.5; + beamFlareAngle = 3.0; + minFlareSize = 0.0; + maxFlareSize = 400.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + lightRadius = 1.0; + lightColor = "0.3 0.0 0.0"; + + textureName[0] = "special/flare"; + textureName[1] = "special/nonlingradient"; + textureName[2] = "special/laserrip01"; + textureName[3] = "special/laserrip02"; + textureName[4] = "special/laserrip03"; + textureName[5] = "special/laserrip04"; + textureName[6] = "special/laserrip05"; + textureName[7] = "special/laserrip06"; + textureName[8] = "special/laserrip07"; + textureName[9] = "special/laserrip08"; + textureName[10] = "special/laserrip09"; + textureName[11] = "special/sniper00"; + +}; + + +//-------------------------------------- +// Rifle and item... +//-------------------------------------- +datablock ItemData(LaserRifle) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_sniper.dts"; + image = SniperRifleImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a pulse laser rifle"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(LaserRifleImage) +{ + className = WeaponImage; + shapeFile = "weapon_targeting.dts"; + item = LaserRifle; + projectile = PulseLaserShot; + projectileType = SniperProjectile; + armThread = looksn; + + usesEnergy = true; + minEnergy = 6; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateSound[0] = GenericSwitchSound; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.5; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = LaserRifleFireSound; + + stateName[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + + stateName[5] = "CheckWet"; + stateTransitionOnWet[5] = "DryFire"; + stateTransitionOnNotWet[5] = "Fire"; + + stateName[6] = "NoAmmo"; + stateTransitionOnAmmo[6] = "Reload"; + stateTransitionOnTriggerDown[6] = "DryFire"; + stateSequence[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateSound[7] = SniperRifleDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "Ready"; +}; + diff --git a/Scripts/Weapons/RPchaingun.cs b/Scripts/Weapons/RPchaingun.cs new file mode 100644 index 0000000..4019e24 --- /dev/null +++ b/Scripts/Weapons/RPchaingun.cs @@ -0,0 +1,576 @@ +//-------------------------------------- +// RPChaingun +//-------------------------------------- + +datablock ParticleData(GunFireEffectSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.04; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 5000; + lifetimeVarianceMS = 500; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -40.0; + spinRandomMax = 40.0; + + colors[0] = "0.4 0.4 0.4 0.18"; + colors[1] = "0.5 0.5 0.5 0.14"; + colors[2] = "0.5 0.5 0.5 0.07"; + colors[3] = "0.6 0.6 0.6 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.75; + sizes[2] = 1.0; + sizes[3] = 1.25; + times[0] = 0.0; + times[1] = 0.333; + times[2] = 0.666; + times[3] = 1.0; + +}; + +datablock ParticleEmitterData(GunFireEffectEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionOffset = 0.0; + + ejectionVelocity = 3; + velocityVariance = 2; + + thetaMin = 0.0; + thetaMax = 10.0; + + lifetimeMS = 10; + + particles = "GunFireEffectSmoke"; + +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(RPChaingunBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.24; + directDamageType = $DamageType::MG; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + specialCollisionStreamline = true; + HeadMultiplier = 1.8; + LegsMultiplier = 0.75; + + kickBackStrength = 100.0; + sound = ChaingunProjectile; + + dryVelocity = 2000.0; + wetVelocity = 1750.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 10.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.09; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(RPChaingunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some M32 ammo"; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(RPChaingunImage) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + item = RPChaingun; + ammo = RPChaingunAmmo; + clip = MGClip; + projectile = RPChaingunBullet; + projectileType = TracerProjectile; + emap = true; + mass = 14; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + decayingSpread = 1; + usesClips = 1; + muzzleFlash = OutdoorTurretMuzzleFlash; + projectileSpread = 5 / 1000.0; + maxSpread = 0.5 / 1000.0; + spreadIncreaseRate = 1.5 / 1000; + clipTimeout = 2250; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "CheckWet"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + // + stateTimeoutValue[3] = 0.001; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateFire[4] = true; + stateAllowImageChange[4] = false; + stateSound[4] = ChaingunFireSound; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateEmitter[4] = "GunFireEffectEmitter"; + stateEmitterNode[4] = "muzzlepoint1"; + stateEmitterTime[4] = 0.5; + stateRecoil[4] = LightRecoil; + stateTimeoutValue[4] = 0.15; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 0.1; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "WetFire"; + stateSound[8] = ChaingunDryFireSound; + stateTimeoutValue[8] = 1.0; + stateTransitionOnTimeout[8] = "Ready"; + + stateName[9] = "CheckWet"; + stateTransitionOnWet[9] = "WetFire"; + stateTransitionOnNotWet[9] = "Spinup"; +}; + +datablock ItemData(RPChaingun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_chaingun.dts"; + image = RPChaingunImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "A M32 Fully Automatic Rifle"; + + computeCRC = true; + emap = true; +}; + +datablock ItemData(MGClip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "5.56mm clips"; + + computeCRC = true; +}; + +datablock ShapeBaseImageData(MGClipImage) +{ + shapeFile = "grenade.dts"; + ammo = RPChaingunAmmo; + offset = "0.0 0.2 -0.2"; + emap = true; +}; + +function RPChaingunImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(MGClipImage, 6); + %obj.clipReloading = false; + if (%obj.inv[RPChaingunAmmo] == 0) + %this.CheckForClip(%obj, %slot); +} + +function RPChaingunImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(6); + %obj.clipReloading = false; +} + +function RPChaingunImage::MountClipEffects(%data, %obj, %slot) +{ + %obj.mountImage(MGClipImage, 6); +} + +function RPChaingunImage::UnmountClipEffects(%data, %obj, %slot) +{ + %obj.unmountImage(6); +} + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(HRPChaingunImage) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + item = HRPChaingun; + ammo = RPChaingunAmmo; + clip = MGClip; + projectile = RPChaingunBullet; + projectileType = TracerProjectile; + emap = true; + mass = 20; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + decayingSpread = 1; + usesClips = 1; + muzzleFlash = OutdoorTurretMuzzleFlash; + projectileSpread = 1 / 1000.0; // z0dd - ZOD, 8/6/02. Was: 8.0 / 1000.0 + maxSpread = 5 / 1000.0; + spreadIncreaseRate = 1.5 / 1000; + clipTimeout = 2250; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "CheckWet"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + // + stateTimeoutValue[3] = 0.001; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateFire[4] = true; + stateAllowImageChange[4] = false; + stateSound[4] = ChaingunFireSound; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + stateEmitter[4] = "GunFireEffectEmitter"; + stateEmitterNode[4] = "muzzlepoint1"; + stateEmitterTime[4] = 0.5; + stateRecoil[4] = LightRecoil; + stateTimeoutValue[4] = 0.15; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 0.1; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; + + stateName[8] = "WetFire"; + stateSound[8] = ChaingunDryFireSound; + stateTimeoutValue[8] = 1.0; + stateTransitionOnTimeout[8] = "Ready"; + + stateName[9] = "CheckWet"; + stateTransitionOnWet[9] = "WetFire"; + stateTransitionOnNotWet[9] = "Spinup"; +}; + +datablock ItemData(HRPChaingun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_chaingun.dts"; + image = HRPChaingunImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a M32 With a RPG Launcher"; + + computeCRC = true; + emap = true; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock GrenadeProjectileData(RPGshot) +{ + projectileShapeName = "grenade.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 1.4; + damageRadius = 10.0; + radiusDamageType = $DamageType::RPG; + kickBackStrength = 3500; + + explosion = "GrenadeExplosion"; + underwaterExplosion = "GrenadeExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.0; + armingDelayMS = -1; + + gravityMod = 1.3; + muzzleVelocity = 50.0; + drag = 0.2; + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 4; + lightColor = "0.05 0.2 0.05"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "0.05 0.075 0.2"; +}; + +datablock ItemData(RPGAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "grenade.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some M32 RPG ammo"; + + computeCRC = true; +}; + +datablock ShapeBaseImageData(RPGLauncherImage) +{ + className = WeaponImage; + shapeFile = "weapon_energy_vehicle.dts"; + item = HRPChaingun; + ammo = RPGAmmo; + offset = "0.0 0.7 0.02"; // L/R - F/B - T/B + emap = true; + + projectile = RPGshot; + projectileType = GrenadeProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 1.0; + stateSequence[0] = "Activate"; + stateSound[0] = GrenadeSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = GrenadeFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4.0; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = GrenadeReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = GrenadeDryFireSound; + stateTimeoutValue[6] = 4.0; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +datablock ItemData(RPGItem) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_energy_vehicle.dts"; + image = RPGLauncherImage; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "RPG Launcher"; + + computeCRC = true; + emap = true; +}; + +function HRPChaingunImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(RPGLauncherImage, 5); + %obj.mountImage(MGClipImage, 6); + %obj.clipReloading = false; + if (%obj.inv[RPChaingunAmmo] == 0) + %this.CheckForClip(%obj, %slot); +} + +function HRPChaingunImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(5); + %obj.unmountImage(6); + %obj.clipReloading = false; +} + +function HRPChaingunImage::MountClipEffects(%data, %obj, %slot) +{ + %obj.mountImage(MGClipImage, 6); +} + +function HRPChaingunImage::UnmountClipEffects(%data, %obj, %slot) +{ + %obj.unmountImage(6); +} diff --git a/Scripts/Weapons/RailGun.cs b/Scripts/Weapons/RailGun.cs new file mode 100644 index 0000000..c1f44e2 --- /dev/null +++ b/Scripts/Weapons/RailGun.cs @@ -0,0 +1,263 @@ +//-------------------------------------- +// ACCM Gauss Cannon - Made by: Blnukem +//-------------------------------------- +//-------------------------------------- +// RailGun Sounds +//-------------------------------------- + +datablock EffectProfile(RailGunFireEffect) +{ + filename = "fx/misc/launcher.wav"; + minDistance = 2.5; + maxDistance = 10.0; +}; + +datablock EffectProfile(RailGunLoadEffect) +{ + filename = "fx/misc/cannonstart.wav"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(RailGunFireSound) +{ + filename = "fx/misc/launcher.wav"; + description = AudioDefault3d; + preload = true; + effect = RailGunFireEffect; +}; + +datablock AudioProfile(RailGunLoadSound) +{ + filename = "fx/misc/cannonstart.wav"; + description = AudioDefault3d; + preload = true; + effect = RailGunLoadEffect; +}; + +//-------------------------------------- +// RailGun Projectile +//-------------------------------------- +datablock TracerProjectileData(RailGunProjectile) +{ + doDynamicClientHits = true; + + directDamage = 10.0 * 10; + directDamageType = $DamageType::Laser; + explosion = MissileExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 5.0; + damageradius = 8; + radiusDamageType = $DamageType::Laser; + + kickBackStrength = 1000; + sound = ChaingunProjectile; + + dryVelocity = 1500.0; + wetVelocity = 500.0; + velInheritFactor = 1.0; + fizzleTimeMS = 2000; + lifetimeMS = 2000; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1000; + + tracerLength = 40.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.5; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = RailGunDecal; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +//-------------------------------------- +// RailGun Ammo +//-------------------------------------- +datablock ItemData(RailGunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_missile.dts"; + mass = 3; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a pair of 250mm Gauss shells"; + + computeCRC = true; + +}; + +//-------------------------------------- +// RailGun Data +//-------------------------------------- +datablock ShapeBaseImageData(RailGunImage) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + rotation = "0 1 0 180"; + offset = "0.1 0.0 0.18"; + item = RailGun; + ammo = RailGunAmmo; + projectile = RailGunProjectile; + projectileType = TracerProjectile; + emap = true; + + minRankPoints = 4800; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + stateAllowImageChange[0] = false; + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateScript[3] = "onSpinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = RailGunLoadSound; + stateTimeoutValue[3] = 0.5; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateFire[4] = true; + stateScript[4] = "onFire"; + stateSound[4] = RailGunFireSound; + stateRecoil[4] = LightRecoil; + stateEmitter[4] = "GunFireEffectEmitter"; + stateEmitterNode[4] = "muzzlepoint1"; + stateEmitterTime[4] = 0.8; + stateTimeoutValue[4] = 1.0; + stateTransitionOnTimeout[4] = "Reload"; + stateAllowImageChange[4] = false; + + //-------------------------------------- + stateName[5] = "Reload"; + stateTransitionOnNoAmmo[5] = "NoAmmo"; + stateTransitionOnTimeout[5] = "Ready"; + stateTimeoutValue[5] = 1.0; + stateAllowImageChange[5] = false; + stateSound[5] = MissileReloadSound; + + //-------------------------------------- + stateName[6] = "Spindown"; + stateScript[6] = "onSpindown"; + stateSound[6] = MissileReloadSound; + stateSpinThread[6] = SpinDown; + stateTimeoutValue[6] = 3.0; + stateWaitForTimeout[6] = true; + stateTransitionOnTimeout[6] = "Ready"; + stateTransitionOnTriggerDown[6] = "Spinup"; + stateEmitter[6] = "GunFireEffectEmitter"; + stateEmitterNode[6] = "muzzlepoint1"; + stateEmitterTime[6] = 0.5; + + //-------------------------------------- + stateName[7] = "EmptySpindown"; + stateSound[7] = MissileDryFireSound; + stateSpinThread[7] = SpinDown; + stateScript[7] = "onSpindown"; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; + stateEmitter[7] = "GunFireEffectEmitter"; + stateEmitterNode[7] = "muzzlepoint1"; + stateEmitterTime[7] = 0.5; + + //-------------------------------------- + stateName[8] = "DryFire"; + stateSound[8] = MortarDryFireSound; + stateTimeoutValue[8] = 0.5; + stateTransitionOnTimeout[8] = "NoAmmo"; +}; + +//-------------------------------------- +// RailGun Attachments +//-------------------------------------- +datablock ShapeBaseImageData(RailGunPrimaryImage) : RailGunImage +{ + offset = "0.01 0.04 0.0"; + rotation = "0 0 1 180"; + shapeFile = "weapon_missile.dts"; +}; + +datablock ShapeBaseImageData(RailGunSecondaryImage) : RailGunImage +{ + offset = "-0.06 -0.23 0.25"; + rotation = "0 0 1 90"; + shapeFile = "ammo_mortar.dts"; +}; + +datablock ShapeBaseImageData(RailGunTertiaryImage) : RailGunImage +{ + offset = "0.08 0.45 0.05"; + rotation = "0 1 0 0"; + shapeFile = "weapon_ELF.dts"; +}; + +//-------------------------------------- +// RailGun Item Data +//-------------------------------------- +datablock ItemData(RailGun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_missile.dts"; + image = RailGunImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Heavy Gauss Cannon"; + + emap = true; +}; + +//-------------------------------------- +// RailGun Functions +//-------------------------------------- +function RailGunImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(RailGunPrimaryImage, 4); + %obj.mountImage(RailGunSecondaryImage, 5); + %obj.mountImage(RailGunTertiaryImage, 6); +} + +function RailGunImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(4); + %obj.unmountImage(5); + %obj.unmountImage(6); +} diff --git a/Scripts/Weapons/RocketLauncher.cs b/Scripts/Weapons/RocketLauncher.cs new file mode 100644 index 0000000..108fa92 --- /dev/null +++ b/Scripts/Weapons/RocketLauncher.cs @@ -0,0 +1,385 @@ +//-------------------------------------- +// AA rocket launcher +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(AAMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 1.0; + damageRadius = 10.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 2000; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 15000; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 10.0; + maxVelocity = 225.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 45.0; + acceleration = 150.0; + + proximityRadius = 3; + + terrainAvoidanceSpeed = 50; + terrainScanAhead = 50; + terrainHeightFail = 5; + terrainAvoidanceRadius = 1; + + flareDistance = 200; + flareAngle = 30; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 550; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = false; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(AALauncherAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_missile.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some AA rockets"; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ItemData(AALauncher) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_missile.dts"; + image = AALauncherImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "an AA rocket Launcher"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(AALauncherImage) +{ + className = WeaponImage; + shapeFile = "weapon_missile.dts"; + item = AALauncher; + ammo = AALauncherAmmo; + offset = "0 0 0"; + armThread = lookms; + emap = true; + mass = 30; + + projectile = AAMissile; + projectileType = SeekerProjectile; + + isSeeker = true; + seekRadius = 400; + maxSeekAngle = 20; + seekTime = 0.5; + minSeekHeat = 0.6; // the heat that must be present on a target to lock it. // z0dd - ZOD, 8/22/02. Was 0.7 + + // only target objects outside this range + minTargetingDistance = 40; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = MissileSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.4; + stateFire[3] = true; + stateRecoil[3] = mediumRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4.0; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "ActivateReady"; + + stateName[7] = "CheckTarget"; + stateTransitionOnNoTarget[7] = "DryFire"; + stateTransitionOnTarget[7] = "Fire"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "CheckTarget"; + + stateName[9] = "WetFire"; + stateTransitionOnNoAmmo[9] = "NoAmmo"; + stateTransitionOnTimeout[9] = "Reload"; + stateTimeoutValue[9] = 0.4; + stateScript[9] = "onWetFire"; + stateAllowImageChange[9] = false; +}; + +function AALauncherImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + + if (%obj.getControllingClient()) + %target = %obj.getLockedTarget(); + else + %target = %obj.getTargetObject(); + + %homein = missileCheckAirTarget(%target); + if(%target && %homein) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + + + + + +//-------------------------------------- +// AT4 Missile launcher +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock SeekerProjectileData(LShoulderMissile) +{ + casingShapeName = "weapon_missile_casement.dts"; + projectileShapeName = "weapon_missile_projectile.dts"; + hasDamageRadius = true; + indirectDamage = 0.8; + damageRadius = 8.0; + radiusDamageType = $DamageType::AT4; + kickBackStrength = 3500; + + explosion = "MissileExplosion"; + splash = MissileSplash; + velInheritFactor = 1.0; // to compensate for slow starting velocity, this value + // is cranked up to full so the missile doesn't start + // out behind the player when the player is moving + // very quickly - bramage + + baseEmitter = MissileSmokeEmitter; + delayEmitter = MissileFireEmitter; + puffEmitter = MissilePuffEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + bubbleEmitTime = 1.0; + + exhaustEmitter = MissileLauncherExhaustEmitter; + exhaustTimeMs = 300; + exhaustNodeName = "muzzlePoint1"; + + lifetimeMS = 3500; // z0dd - ZOD, 4/14/02. Was 6000 + muzzleVelocity = 200.0; + maxVelocity = 350.0; // z0dd - ZOD, 4/14/02. Was 80.0 + turningSpeed = 54.0; + acceleration = 50.0; + + proximityRadius = 3; + + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 3.0; + lightColor = "0.2 0.05 0"; + + useFlechette = true; + flechetteDelayMs = 10; + casingDeb = FlechetteDebris; + + explodeOnWaterImpact = true; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(LMissileLauncherAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_missile.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "an AT6 missile"; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ItemData(LMissileLauncher) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_grenade_launcher.dts"; + image = LMissileLauncherImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "an AT6"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(LMissileLauncherImage) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + item = LMissileLauncher; + ammo = LMissileLauncherAmmo; + offset = "0 -1 0"; + armThread = lookms; + emap = true; + + projectile = LShoulderMissile; + projectileType = seekerprojectile; + projectileSpread = 1.0 / 2000.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 2.0; + stateSequence[0] = "Activate"; + stateSound[0] = MissileSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.4; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "ActivateReady"; + + stateName[7] = "CheckTarget"; + stateTransitionOnNoTarget[7] = "Fire"; + stateTransitionOnTarget[7] = "Fire"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "DryFire"; + stateTransitionOnNotWet[8] = "CheckTarget"; + + stateName[9] = "WetFire"; + stateTransitionOnNoAmmo[9] = "NoAmmo"; + stateTransitionOnTimeout[9] = "Reload"; + stateTimeoutValue[9] = 0.4; + stateScript[9] = "onWetFire"; + stateAllowImageChange[9] = false; +}; + +function LMissileLauncherImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.AT4lockon = schedule(875, 0, "AT4LockOnToTarget", %p); +} +function AT4LockOnToTarget(%p) +{ + if(!isObject(%p)) + return; + InitContainerRadiusSearch(%p.getPosition(), 100, $TypeMasks::VehicleObjectType); + %searchResult = containersearchnext(); + if(%searchResult) + { + %SearchObj = FirstWord(%SearchResult); + %p.setObjectTarget(%searchObj); + } +} \ No newline at end of file diff --git a/Scripts/Weapons/Shotgun.cs b/Scripts/Weapons/Shotgun.cs new file mode 100644 index 0000000..61da182 --- /dev/null +++ b/Scripts/Weapons/Shotgun.cs @@ -0,0 +1,488 @@ +//-------------------------------------- +// Shotgun +//-------------------------------------- + +datablock EffectProfile(ShotGunFireEffect) +{ + filename = "powered/turret_mortar_fire"; + minDistance = 2.5; + maxDistance = 10.0; +}; + +datablock AudioProfile(ShotGunFireSound) +{ + filename = "fx/powered/turret_mortar_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = ShotGunFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(ShotgunPellet) +{ + doDynamicClientHits = true; + + directDamage = 0.075; + directDamageType = $DamageType::Shotgun; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + specialCollisionStreamline = true; + HeadMultiplier = 2.0; + LegsMultiplier = 0.75; + + longRangeMultiplier = 0.75; + kickBackStrength = 0.0; + sound = ChaingunProjectile; + + dryVelocity = 1500.0; + wetVelocity = 1000.0; + velInheritFactor = 0.0; + fizzleTimeMS = 1000; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 5.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.09; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(ShotgunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some Shotgun rounds"; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(ShotgunImage) +{ + className = WeaponImage; + shapeFile = "weapon_plasma.dts"; + item = Shotgun; + ammo = ShotgunAmmo; + projectile = ShotgunPellet; + projectileType = TracerProjectile; + emap = true; + mass = 20; + + casing = ShellDebris; + shellExitDir = "0.5 0.0 1.0"; // L/R - F/B - T/B + shellExitOffset = "0.15 -0.51 -0.1"; // L/R - F/B - T/B + shellExitVariance = 10.0; + shellVelocity = 4.0; + + projectileSpread = 11.0 / 1000.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.0001; + stateFire[3] = true; + stateEjectShell[3] = true; + stateEmitter[3] = "GunFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 1; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = ShotGunFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ChaingunDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "WetFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "Ready"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "Fire"; +}; + +datablock ItemData(Shotgun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_plasma.dts"; + image = ShotgunImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Shotgun"; + + computeCRC = true; + emap = true; +}; + +datablock ItemData(ShotgunClip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "Shotgun Magazines"; + + computeCRC = true; +}; + +function ShotgunImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + if (%obj.inv[ShotgunAmmo] == 0) + %obj.SGcheckclip = schedule(10, 0, "SGCheckforclip", %obj); +} + +// Eolk - add this function to prevent people from switching to another weapon and shooting while reloading a clip. +function ShotgunImage::onUnmount(%this, %obj, %slot) +{ + if (%obj.clipReloading != false) + { + Cancel(%obj.SGClipAdd); + %obj.clipReloading = false; + } +} + +function ShotgunImage::onFire(%data,%obj,%slot) +{ + %obj.applyKick(-250); + %obj.decInventory(%data.ammo,1); + + %vector = %obj.getMuzzleVector(%slot); + %mp = %obj.getMuzzlePoint(%slot); + + for (%i=0; %i < 12; %i++) + { + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + + %p = new (%data.projectileType)() + { + dataBlock = %data.projectile; + initialDirection = %newvector; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + } + if (%obj.inv[ShotgunAmmo] == 0) + %obj.SGcheckclip = schedule(10, 0, "SGCheckforclip", %obj); +} + +function SGCheckforclip(%obj) +{ + if (%obj.inv[Shotgunclip] > 0) + { + %obj.unmountImage(0); + %obj.decInventory(ShotgunClip, 1); + %obj.SGClipAdd = schedule(2500, 0, "SGReloadClip", %obj); + } +} + +function SGReloadClip(%obj) +{ + %obj.setInventory(ShotgunAmmo, 8); + %obj.mountImage(ShotgunImage, 0); +} + + + + + +//-------------------------------------- +// RotaryShotgun +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(RShotgunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some Rotary Shotgun rounds"; + + computeCRC = true; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(RShotgunImage) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + item = RShotgun; + ammo = RShotgunAmmo; + projectile = ShotgunPellet; + projectileType = TracerProjectile; + emap = true; + mass = 20; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + projectileSpread = 9.0 / 1000.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.0001; + stateFire[3] = true; + stateEjectShell[3] = true; + stateEmitter[3] = "GunFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 1; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = ShotGunFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.25; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ChaingunDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "WetFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "Ready"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "Fire"; +}; + +datablock ItemData(RShotgun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_grenade_launcher.dts"; + image = RShotgunImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a RotaryShotgun"; + + computeCRC = true; + emap = true; +}; + +datablock ItemData(RShotgunClip) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "Rotary Shotgun Magazines"; + + computeCRC = true; +}; + +datablock ShapeBaseImageData(RSGImage1) +{ + className = WeaponImage; + ammo = RShotgunAmmo; + shapeFile = "weapon_grenade_launcher.dts"; + rotation = "0 1 0 120"; +}; + +datablock ShapeBaseImageData(RSGImage2) +{ + className = WeaponImage; + ammo = RShotgunAmmo; + shapeFile = "weapon_grenade_launcher.dts"; + rotation = "0 1 0 -120"; +}; + +datablock ShapeBaseImageData(RSGImageclip) +{ + className = WeaponImage; + ammo = RShotgunAmmo; + shapeFile = "ammo_chaingun.dts"; + rotation = "0 1 0 180"; + offset = "0.0 0.45 -0.1"; // L/R - F/B - T/B + item = RShotgun; +}; + +function RShotgunImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(RSGImageclip, 4); + %obj.mountImage(RSGImage1, 5); + %obj.mountImage(RSGImage2, 6); + %obj.clipReloading = false; + if (%obj.inv[RShotgunAmmo] == 0) + %obj.RSGcheckclip = schedule(10, 0, "RSGCheckforclip", %obj); +} + +function RShotgunImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(4); + %obj.unmountImage(5); + %obj.unmountImage(6); + if (%obj.clipReloading != false) + { + Cancel(%obj.RSGClipAdd); + %obj.clipReloading = false; + } +} + +function RShotgunImage::onFire(%data,%obj,%slot) +{ + %obj.applyKick(-250); + %obj.decInventory(%data.ammo,1); + + %vector = %obj.getMuzzleVector(%slot); + %mp = %obj.getMuzzlePoint(%slot); + + for (%i=0; %i < 8; %i++) + { + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %data.projectileSpread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + + %p = new (%data.projectileType)() + { + dataBlock = %data.projectile; + initialDirection = %newvector; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + } + if (%obj.inv[RShotgunAmmo] == 0) + %obj.RSGcheckclip = schedule(10, 0, "RSGCheckforclip", %obj); +} + +function RSGCheckforclip(%obj) +{ + if (%obj.inv[RShotgunclip] > 0) + { + %obj.unmountImage(4); + %obj.clipReloading = true; + %obj.RSGClipAdd = schedule(3000, 0, "RSGReloadClip", %obj); + } +} + +function RSGReloadClip(%obj) +{ + %obj.clipReloading = false; + %obj.decInventory(RShotgunClip, 1); + %obj.mountImage(RSGImageclip, 4); + %obj.setInventory(RShotgunAmmo, 25); +} diff --git a/Scripts/Weapons/SmokeGrenade.cs b/Scripts/Weapons/SmokeGrenade.cs new file mode 100644 index 0000000..9b44975 --- /dev/null +++ b/Scripts/Weapons/SmokeGrenade.cs @@ -0,0 +1,301 @@ +//-------------------------------------------------------------------------- +// Smoke Grenade +//-------------------------------------------------------------------------- + +datablock ParticleData(SmokeGrenadeSmoke) +{ + dragCoeffiecient = 0.1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + constantAcceleration = -0.0; + + lifetimeMS = 3000; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.3 0.3 0.3 1.0"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 0.75; + sizes[1] = 6.0; + sizes[2] = 10.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(SmokeGrenadeSmokeEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + + ejectionVelocity = 2.5; + velocityVariance = 2.5; + ejectionOffset = 3.5; + + thetaMin = 0.0; + thetaMax = 90.0; + + particles = "SmokeGrenadeSmoke"; +}; + +datablock ExplosionData(smokegrenadeExplosion) +{ + emitter[0] = SmokeGrenadeSmokeEmitter; + shakeCamera = false; +}; + +//-------------------------------------------------------------------------- +// Projectile - Smoke Grenade +//-------------------------------------- + +datablock GrenadeProjectileData(SmokeGrenadeProj) +{ + projectileShapeName = "grenade.dts"; + directDamage = 0.0; + directDamageType = $DamageType::RPG; + hasDamageRadius = false; + + explosion = "smokegrenadeExplosion"; + underwaterExplosion = "smokegrenadeExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + emitterDelay = 3000; + baseEmitter = SmokeGrenadeSmokeEmitter; + bubbleEmitter = SmokeGrenadeSmokeEmitter; + + grenadeElasticity = 0.3; + grenadeFriction = 1.0; + armingDelayMS = 21000; + + gravityMod = 1.0; + muzzleVelocity = 25.0; + drag = 0.0; + sound = MissileProjectileSound; + + hasLight = false; + hasLightUnderwaterColor = false; +}; + + +datablock ItemData(SmokeGrenadeThrown) +{ + className = Weapon; + shapeFile = "grenade.dts"; + mass = 0.4; + elasticity = 0.3; + friction = 1; + pickupRadius = 2; + maxDamage = 0.5; + explosion = smokegrenadeExplosion; + underwaterExplosion = smokegrenadeExplosion; + indirectDamage = 0.0; + damageRadius = 0.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 0; + baseEmitter = SmokeGrenadeSmokeEmitter; + + computeCRC = true; + +}; + +datablock ItemData(SmokeGrenade) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "grenade.dts"; + mass = 1; + elasticity = 0.5; + friction = 1; + pickupRadius = 2; + thrownItem = SmokeGrenadeThrown; + pickUpName = "some smoke grenades"; + isGrenade = true; + + computeCRC = true; + +}; + +function SmokeGrenade::onUse(%this, %obj) +{ + if(Game.handInvOnUse(%data, %obj)) + { + %obj.decInventory(%this, 1); + %p = new GrenadeProjectile() { + dataBlock = SmokeGrenadeProj; + initialDirection = %obj.getEyeVector(); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(20000, "delete"); + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } +} + +function SmokeGrenade::onCollision( %data, %obj, %col ) +{ + // Do nothing... +} + + + +//-------------------------------------------------------------------------- +// Beacon Smoke Grenade +//-------------------------------------------------------------------------- + +datablock ParticleData(BeaconSmokeGrenadeSmoke) +{ + dragCoeffiecient = 0.1; + gravityCoefficient = -0.6; + inheritedVelFactor = 0.4; + + constantAcceleration = -0.0; + + lifetimeMS = 5000; + lifetimeVarianceMS = 250; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -150.0; + spinRandomMax = 150.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.5 0.5 0.5 1.0"; + colors[1] = "0.6 0.2 0.2 0.75"; + colors[2] = "0.4 0.1 0.1 0.0"; + sizes[0] = 1.0; + sizes[1] = 3.0; + sizes[2] = 5.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(BeaconSmokeGrenadeSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 1.0; + velocityVariance = 0.5; + + thetaMin = 0.0; + thetaMax = 180.0; + + particles = "BeaconSmokeGrenadeSmoke"; +}; + +//-------------------------------------------------------------------------- +// Projectile - Smoke Grenade +//-------------------------------------- + +datablock GrenadeProjectileData(BeaconSmokeGrenadeProj) +{ + projectileShapeName = "grenade.dts"; + directDamage = 0.0; + directDamageType = $DamageType::RPG; + hasDamageRadius = false; + + explosion = "smokegrenadeExplosion"; + underwaterExplosion = "smokegrenadeExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + emitterDelay = 3000; + baseEmitter = BeaconSmokeGrenadeSmokeEmitter; + bubbleEmitter = BeaconSmokeGrenadeSmokeEmitter; + + grenadeElasticity = 0.3; + grenadeFriction = 1.0; + armingDelayMS = 61000; + + gravityMod = 1.0; + muzzleVelocity = 25.0; + drag = 0.0; + sound = MissileProjectileSound; + + hasLight = false; + hasLightUnderwaterColor = false; +}; + + +datablock ItemData(BeaconSmokeGrenadeThrown) +{ + className = Weapon; + shapeFile = "grenade.dts"; + mass = 0.4; + elasticity = 0.3; + friction = 1; + pickupRadius = 2; + maxDamage = 0.5; + explosion = smokegrenadeExplosion; + underwaterExplosion = smokegrenadeExplosion; + indirectDamage = 0.0; + damageRadius = 0.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 0; + + computeCRC = true; + +}; + +datablock ItemData(BeaconSmokeGrenade) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "grenade.dts"; + mass = 1; + elasticity = 0.5; + friction = 1; + pickupRadius = 2; + thrownItem = BeaconSmokeGrenadeThrown; + pickUpName = "some beacon grenades"; + isGrenade = true; + + computeCRC = true; + +}; + +function BeaconSmokeGrenade::onUse(%this, %obj) +{ + if(Game.handInvOnUse(%data, %obj)) + { + %obj.decInventory(%this, 1); + %p = new GrenadeProjectile() { + dataBlock = BeaconSmokeGrenadeProj; + initialDirection = %obj.getEyeVector(); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(60000, "delete"); + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } +} + +function BeaconSmokeGrenade::onCollision( %data, %obj, %col ) +{ + // Do nothing... +} diff --git a/Scripts/Weapons/TextureTool.cs b/Scripts/Weapons/TextureTool.cs new file mode 100644 index 0000000..5b0a0f5 --- /dev/null +++ b/Scripts/Weapons/TextureTool.cs @@ -0,0 +1,483 @@ +//////////////////////////////////// +// AVAILABLE TEXTURES +$TextureToolLabel[1] = "Pads"; +$TextureTool[1, 1] = "DeployedSpine (LSB) Light Support Beam"; +$TextureTool[1, 2] = "DeployedMSpine (MSB) Medium Support Beam"; +$TextureTool[1, 3] = "DeployedWWall Walk Way"; +$TextureTool[1, 4] = "DeployedWall (Bwall) Shady Light Support Beam"; +$TextureTool[1, 5] = "DeployedSpine2 Dark Pad"; +$TextureTool[1, 6] = "DeployedDecoration16 Blue Pad"; + +$TextureToolLabel[2] = "Crates"; +$TextureTool[2, 1] = "DeployedCrate0 (crate1) Back Pack"; +$TextureTool[2, 2] = "DeployedCrate1 (crate2) small containment"; +$TextureTool[2, 3] = "DeployedCrate2 (crate3) large containment"; +$TextureTool[2, 4] = "DeployedCrate3 (crate4) compressor"; +$TextureTool[2, 5] = "DeployedCrate4 (crate5) tubes"; +$TextureTool[2, 6] = "DeployedCrate5 (crate6) quantum battery"; +$TextureTool[2, 7] = "DeployedCrate6 (crate7) proton accelerator"; +$TextureTool[2, 8] = "DeployedCrate7 (crate8) cargo crate"; +$TextureTool[2, 9] = "DeployedCrate8 (crate9) magnetic cooler"; +$TextureTool[2, 10] = "DeployedCrate9 (crate10) recycle unit"; +$TextureTool[2, 11] = "DeployedCrate10 (crate11) fuel cannister"; +$TextureTool[2, 12] = "DeployedCrate12 (crate12) plasma router"; + +$TextureToolLabel[3] = "Decorations"; +$TextureTool[3, 1] = "DeployedDecoration6 (deco1) Statue Base"; + +$TextureToolLabel[4] = "Forcefields"; // Don't remove the "Dummy"s. +$TextureTool[4, 1] = "Dummy White, No Pass"; +$TextureTool[4, 2] = "Dummy Red, No Pass"; +$TextureTool[4, 3] = "Dummy Green, No Pass"; +$TextureTool[4, 4] = "Dummy Blue, No Pass"; +$TextureTool[4, 5] = "Dummy Cyan, No Pass"; +$TextureTool[4, 6] = "Dummy Magenta, No Pass"; +$TextureTool[4, 7] = "Dummy Yellow, No Pass"; +$TextureTool[4, 8] = "Dummy White, Team Pass"; +$TextureTool[4, 9] = "Dummy Red, Team Pass"; +$TextureTool[4, 10] = "Dummy Green, Team Pass"; +$TextureTool[4, 11] = "Dummy Blue, Team Pass"; +$TextureTool[4, 12] = "Dummy Cyan, Team Pass"; +$TextureTool[4, 13] = "Dummy Magenta, Team Pass"; +$TextureTool[4, 14] = "Dummy Yellow, Team Pass"; +$TextureTool[4, 15] = "Dummy White, All Pass"; +$TextureTool[4, 16] = "Dummy Red, All Pass"; +$TextureTool[4, 17] = "Dummy Green, All Pass"; +$TextureTool[4, 18] = "Dummy Blue, All Pass"; +$TextureTool[4, 19] = "Dummy Cyan, All Pass"; +$TextureTool[4, 20] = "Dummy Magenta, All Pass"; +$TextureTool[4, 21] = "Dummy Yellow, All Pass"; +$TextureTool[4, 22] = "Dummy Lightning, Solid"; +$TextureTool[4, 23] = "Dummy Scan Line, Solid"; +$TextureTool[4, 24] = "Dummy Grid, Solid"; +$TextureTool[4, 25] = "Dummy Fire Wall, Solid"; +$TextureTool[4, 26] = "Dummy Energy Field, Solid"; +$TextureTool[4, 27] = "Dummy Flashing, Solid"; +$TextureTool[4, 28] = "Dummy Dirty Window, Solid"; +$TextureTool[4, 29] = "Dummy Scan Line, Team Pass"; +$TextureTool[4, 30] = "Dummy Grid, Team Pass"; +$TextureTool[4, 31] = "Dummy Energy Field, Team Pass"; +$TextureTool[4, 32] = "Dummy Flashing, Team Pass"; +$TextureTool[4, 33] = "Dummy Scan Line, All Pass"; +$TextureTool[4, 34] = "Dummy Grid, All Pass"; +$TextureTool[4, 35] = "Dummy Energy Field, All Pass"; +$TextureTool[4, 36] = "Dummy Flashing, All Pass"; +$TextureTool[4, 37] = "Dummy Fire Wall, All Pass"; +$TextureTool[4, 38] = "Dummy Lava, All Pass"; +$TextureTool[4, 39] = "Dummy Water, All Pass"; +$TextureTool[4, 40] = "Dummy Flashing Crossairs, Special"; +$TextureTool[4, 41] = "Dummy Glass Tile, Special"; +$TextureTool[4, 42] = "Dummy Vehicle Icons, Special"; +$TextureTool[4, 43] = "Dummy Space 1, Special"; +$TextureTool[4, 44] = "Dummy Clouds 1, Special"; +$TextureTool[4, 45] = "Dummy Fuzzy Scanlines, Special"; +$TextureTool[4, 46] = "Dummy Space 2, Special"; +$TextureTool[4, 47] = "Dummy Signs, Special"; +$TextureTool[4, 48] = "Dummy Clouds 2, Special"; +$TextureTool[4, 49] = "Dummy Computer Screen, Special"; +$TextureTool[4, 50] = "Dummy Console, Special"; +$TextureTool[4, 51] = "Dummy Standard Force Field, Special"; + +$TextureToolLabel[5] = "Turn Pad into Door"; +$TextureTool[5, 1] = "Regular Pad (not door)"; +$TextureTool[5, 2] = "Normal Door"; +$TextureTool[5, 3] = "Toggle Door"; +$TextureTool[5, 4] = "Power Change Toggle Door (Open When Powered)"; +$TextureTool[5, 5] = "Power Change Toggle Door (Closed When Powered)"; +$TextureTool[5, 6] = "Collision Door (All Access)"; +$TextureTool[5, 7] = "Collision Door (Permission Access)"; +$TextureTool[5, 8] = "Collision Door (Owner-only Access)"; +$TextureTool[5, 9] = "Collision Door (Admin-only Access)"; + +//////////////////////////////////// +// WEAPON +datablock ItemData(TextureTool) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_targeting.dts"; + image = TextureToolImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a texturing tool"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(TextureToolImage) +{ + className = WeaponImage; + shapeFile = "weapon_targeting.dts"; + item = TextureTool; + + usesEnergy = true; + minEnergy = 0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateSound[0] = BasicSwitchSound; + stateTimeoutValue[0] = 0.1; + stateSequence[0] = "Activate"; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.2; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = MergeToolFireSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.1; + stateAllowImageChange[4] = false; + + stateName[5] = "CheckWet"; + stateTransitionOnWet[5] = "Fire"; + stateTransitionOnNotWet[5] = "Fire"; + + stateName[6] = "NoAmmo"; + stateTransitionOnAmmo[6] = "Reload"; + stateTransitionOnTriggerDown[6] = "DryFire"; + stateSequence[6] = "NoAmmo"; + + stateName[7] = "DryFire"; + stateTimeoutValue[7] = 0.1; + stateTransitionOnTimeout[7] = "Ready"; +}; + +//////////////////////////////////// +// SUPPORTING FUNCTIONS +function TextureToolImage::onFire(%data,%obj,%slot) +{ + %pos = %obj.getMuzzlePoint($WeaponSlot); + %vec = %obj.getMuzzleVector($WeaponSlot); + %targetpos = VectorAdd(%pos, VectorScale(%vec, 200)); + %piece = containerRaycast(%pos, %targetpos, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType, %obj); + %piece = getWord(%piece, 0); + + if(!isObject(%piece)) + return; + + // Order reversed to eliminate console spam. + if(!isObject(Deployables) || !Deployables.isMember(%piece)) + { + messageClient(%obj.client, "", "\c2TT: That piece is a part of the map and cannot be modified."); + return; + } + + if(%piece.getOwner() != %obj.client && !%obj.client.isAdmin) + { + messageClient(%obj.client, "", "\c2TT: You do not own that!"); + return; + } + + if(%obj.TTMode == 4) + RetextureForcefield(%obj, %piece, %obj.TTSubMode - 1); + else if(%obj.TTMode == 5) + RetextureDoor(%obj, %piece, %obj.TTSubMode); + else + RetexturePad(%obj, %piece, getWord($TextureTool[%obj.TTMode, %obj.TTSubMode], 0)); +} + +function TextureToolImage::onMount(%data, %obj, %slot) +{ + parent::onMount(%data, %obj, %slot); + + if(%obj.TTMode $= "") + %obj.TTMode = 1; + if(%obj.TTSubMode $= "") + %obj.TTSubMode = 1; + + TTMessage(%obj.client); + %obj.usingTextureTool = 1; +} + +function TextureToolImage::onUnmount(%data, %obj, %slot) +{ + parent::onUnmount(%data, %obj, %slot); + %obj.usingTextureTool = 0; +} + +function TTMessage(%client) +{ + if(!isObject(%client)) + return; + + if(%client.player.TTMode == 5) + Bottomprint(%client, ">>>Texture Tool<<<\nTexture tool mode set to "@$TextureToolLabel[%client.player.TTMode]@"\nCurrent door swap set to "@$TextureTool[%client.player.TTMode, %client.player.TTSubMode]@".", 5, 3); + else + Bottomprint(%client, ">>>Texture Tool<<<\nTexture category set to "@$TextureToolLabel[%client.player.TTMode]@"\nCurrent texture set to "@getWords($TextureTool[%client.player.TTMode, %client.player.TTSubMode], 1, 10)@".", 5, 3); +} + +function RetextureForcefield(%player, %piece, %ff) +{ + if(%piece.getDatablock().className !$= "forcefield") + { + messageClient(%player.client, "", "\c2TT: Wrong type of object."); + return; + } + + %new = new ForceFieldBare() { + datablock = "DeployedForceField"@%ff; + scale = %piece.getScale(); + }; + + %new.setTransform(%piece.getTransform()); + + // Each time a forcefield is added, the game automatically adds a new physical zone. + // We want to make our own, so we'll delete theirs. + if(isObject(%new.pzone)) + %new.pzone.delete(); + + // Set up all of the statistics and junk ----------------------- + + // Can't forget this... + if(%piece.noSlow) + %new.noSlow = true; + + %new.grounded = %piece.grounded; + %new.needsFit = %piece.needsFit; + + %new.team = %piece.team; + %new.setOwner(%piece.getOwner().player); + + %new.powerFreq = %piece.powerFreq; + + if (%new.getTarget() != -1) + setTargetSensorGroup(%new.getTarget(), %piece.team); + + addToDeployGroup(%new); + + AIDeployObject(%new.getOwner(), %new); + + // Don't increment the team count. + + checkPowerObject(%new); + + // Now we delete the old piece. + if(isObject(%piece.pzone)) + %piece.pzone.delete(); + + %piece.delete(); +} + +function RetexturePad(%player, %piece, %datablock) +{ + %classname = %piece.getDatablock().className; + if((%classname $= "decoration" && (%piece.getDataBlock().getname() $= "DeployedDecoration6" || %piece.getDataBlock().getname() $= "DeployedDecoration16")) || %classname $= "crate" || %classname $= "floor" || %classname $= "spine" || %classname $= "mspine" || %classname $= "wall" || %classname $= "wwall" || %classname $= "Wspine" || %classname $= "Sspine" || %classname $= "floor" || %classname $= "door") + { +// %datablock = getword($EditorTool[5, %player.ETSubMode], 0); + if(%piece.isdoor || %piece.getDatablock().getName() $= "Deployeddoor") + { + if(%piece.canMove == false) + { + messageClient(%player.client, "", "\c2You can only retexture moveable doors."); + return; + } + + if(%piece.state !$= "closed" && %piece.state !$= "") + { + messageClient(%player.client, "", "\c2You can only retexture fully closed doors."); + return; + } + } + + %new = new StaticShape() { + datablock = %datablock; + }; + + %new.setRealSize(%piece.getRealSize()); + %new.setTransform(%piece.getTransform()); + + // Eolk - no dsurface. + %new.grounded = %piece.grounded; + %new.needsFit = %piece.needsFit; + + %new.team = %piece.team; + + %new.setOwner(%piece.getOwner().player); + + %new.powerFreq = %piece.powerFreq; + %new.originalPos = %piece.originalPos; + + if (%new.getTarget() != -1) + setTargetSensorGroup(%new.getTarget(), %piece.team); + + addToDeployGroup(%new); + + AIDeployObject(%piece.getOwner(), %new); + + // TODO: Make incrementing/decrementing possible for teamdeploycounts. + + %new.deploy(); + + checkPowerObject(%new); + + if(%piece.isDoor || %piece.getDatablock().getName() $= "DeployedDoor") + { + %new.closedScale = %piece.getScale(); + %new.openedScale = getword(%piece.getScale(), 0) SPC getword(%piece.getScale(), 1) SPC 0.1; + %new.isDoor = 1; + %new.state = %piece.state; + %new.canMove = %piece.canMove; + %new.moving = %piece.moving; + %new.toggleType = %piece.toggleType; + %new.powerControl = %piece.powerControl; + %new.toggleType = %piece.toggleType; + %new.collisionType = %piece.collisionType; + %new.accessLevel = %piece.accessLevel; + %new.timeout = %piece.timeout; + } + + %piece.delete(); + } + else + messageClient(%player.client, "", "\c2TT: Wrong type of object."); +} + +function RetextureDoor(%player, %piece, %mode) +{ + %classname = %piece.getDatablock().className; + if((%classname $= "decoration" && %piece.getDataBlock().getname() $= "DeployedDecoration6") || %classname $= "crate" || %classname $= "floor" || %classname $= "spine" || %classname $= "mspine" || %classname $= "wall" || %classname $= "wwall" || %classname $= "Wspine" || %classname $= "Sspine" || %classname $= "floor" || %classname $= "door") + { + if(%piece.isDoor || %piece.getDatablock.className $= "door") + { + if(!%piece.canMove) + { + messageClient(%player.client, "", "\c2TT: You can only texture moveable doors."); + return; + } + + if(%piece.state !$= "closed" && %piece.state !$= "") + { + messageClient(%player.client, "", "\c2TT: You can only texture fully closed doors."); + return; + } + } + + %timeout = (%piece.timeout $= "" ? 1 : %piece.timeout); + if(%mode == 1) + { + %piece.isDoor = 0; + if(%piece.getDatablock().getName() $= "DeployedDoor") + %piece.setDatablock(DeployedSpine); // Risky...? + + %piece.state = ""; + %piece.canMove = ""; + %piece.moving = ""; + %piece.toggleType = ""; + %piece.collisionType = ""; + %piece.accessLevel = ""; + %piece.closedScale = ""; + %piece.openedScale = ""; + %piece.powerControl = ""; + } + else if(%mode == 2) + { + %piece.isDoor = 1; + %piece.collisionType = 0; + %piece.toggleType = 0; + %piece.powerControl = 0; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + else if(%mode == 3) + { + %piece.isDoor = 1; + %piece.collisionType = 0; + %piece.toggleType = 1; + %piece.powerControl = 0; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + else if(%mode == 4) + { + %piece.isDoor = 1; + %piece.collisionType = 0; + %piece.toggleType = 1; + %piece.powerControl = 1; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + checkPowerObject(%piece); + } + else if(%mode == 5) + { + %piece.isDoor = 1; + %piece.collisionType = 0; + %piece.toggleType = 1; + %piece.powerControl = 2; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + checkPowerObject(%piece); + } + else if(%mode == 6) + { + %piece.isDoor = 1; + %piece.collisionType = 1; + %piece.toggleType = 0; + %piece.powerControl = 0; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + else if(%mode == 7) + { + %piece.isDoor = 1; + %piece.collisionType = 2; + %piece.toggleType = 0; + %piece.powerControl = 0; + %piece.accessLevel = 1; + messageClient(%player.client, "", "\c2Use \c3/setAccess [level] \c2- to set the access level needed for this door, use \c3/setPAccess [name] [level] \c2- to set someone's access level over your pieces."); + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + else if(%mode == 8) + { + %piece.isDoor = 1; + %piece.collisionType = 3; + %piece.toggleType = 0; + %piece.powerControl = 0; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + else if(%mode == 9) + { + %piece.isDoor = 1; + %piece.collisionType = 4; + %piece.toggleType = 0; + %piece.powerControl = 0; + + %piece.timeout = %timeout; + %piece.canMove = true; + %piece.state = "closed"; + } + } + else + messageClient(%player.client, "", "\c2TT: Wrong type of object."); +} diff --git a/Scripts/Weapons/ZLordPoison.cs b/Scripts/Weapons/ZLordPoison.cs new file mode 100644 index 0000000..2f0790e --- /dev/null +++ b/Scripts/Weapons/ZLordPoison.cs @@ -0,0 +1,160 @@ +datablock ParticleData(ZAcidBallParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.5; + inheritedVelFactor = 0.5; + + lifetimeMS = 2000; + lifetimeVarianceMS = 200; + + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/bubbles"; + + colors[0] = "0.0 1.0 0.5 0.3"; + colors[1] = "0.0 1.0 0.4 0.2"; + colors[2] = "0.0 1.0 0.3 0.1"; + + sizes[0] = 0.6; + sizes[1] = 0.3; + sizes[2] = 0.1; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(ZAcidBallExplosionEmitter) +{ + lifetimeMS = 200; + + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 3.0; + velocityVariance = 0.5; + + thetaMin = 0.0; + thetaMax = 90.0; + + orientParticles = false; + orientOnVelocity = false; + + particles = "ZAcidBallParticle"; +}; + +datablock ExplosionData(ZAcidBallExplosion) +{ + emitter[0] = ZAcidBallExplosionEmitter; + soundProfile = ZlordAcidBoomSound; +}; + +datablock TracerProjectileData(LZombieAcidBall) +{ + doDynamicClientHits = true; + + projectileShapeName = ""; + directDamage = 0.0; + directDamageType = $DamageType::ZAcid; + hasDamageRadius = true; + indirectDamage = 0.24; + damageRadius = 4.0; + kickBackStrength = 0.0; + radiusDamageType = $DamageType::ZAcid; + sound = BlasterProjectileSound; + explosion = ZAcidBallExplosion; + + dryVelocity = 100.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 4000; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = true; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = -1; + + activateDelayMS = 100; + + tracerLength = 5; + tracerAlpha = false; + tracerMinPixels = 3; + tracerColor = "0 1 0 1"; + tracerTex[0] = "special/landSpikeBolt"; + tracerTex[1] = "special/landSpikeBoltCross"; + tracerWidth = 0.5; + crossSize = 0.79; + crossViewAng = 0.990; + renderCross = true; + emap = true; +}; + +datablock ShapeBaseImageData(LordAcidGunImage) +{ + className = WeaponImage; + shapeFile = "turret_muzzlepoint.dts"; + item = LordAcidGun; + + projectile = LZombieAcidBall; + projectileType = TracerProjectile; + + usesEnergy = true; + fireEnergy = 55; + minEnergy = 55; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = ZlordAcidBoomSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateRecoil[3] = NoRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateSound[3] = PlasmaProjectileSound; + stateScript[3] = "onFire"; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateTimeoutValue[6] = 0.1; + stateTransitionOnTimeout[6] = "Ready"; +}; + +datablock ItemData(LordAcidGun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_energy.dts"; + image = LordAcidGunImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + + pickupRadius = 2; + pickUpName = "a zombie lord acid ability"; +}; diff --git a/Scripts/Weapons/allweapons.cs b/Scripts/Weapons/allweapons.cs new file mode 100644 index 0000000..68f59e2 --- /dev/null +++ b/Scripts/Weapons/allweapons.cs @@ -0,0 +1,1803 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// File Modified by Blnukem. +// Modifications: Removed unused datablocks for more space. +//------------------------------------------------------------------------------ +// Weapon Sounds +//------------------------------------------------------------------------------ + +datablock AudioProfile(GenericSwitchSound) +{ + filename = "fx/weapons/generic_switch.wav"; + description = AudioClosest3d; + preload = true; +// effect = GenericSwitchEffect; +}; + +datablock AudioProfile(BasicSwitchSound) +{ + filename = "fx/weapons/blaster_activate.wav"; + description = AudioClosest3d; + preload = true; +// effect = BasicSwitchEffect; +}; + +datablock AudioProfile(GenericExplosionSound) +{ + filename = "fx/explosions/explosion.xpl10.wav"; + description = AudioExplosion3d; +// effect = GenericExplosionEffect; +}; + +datablock AudioProfile(HeavyFireSound) +{ + filename = "fx/weapons/plasma_rifle_fire.wav"; + description = AudioDefault3d; + preload = true; +// effect = HeavyFireEffect; +}; + +datablock AudioProfile(EditorToolFireSound) +{ + filename = "fx/weapons/plasma_rifle_projectile_hit.wav"; + description = AudioDefault3d; + preload = true; +// effect = EditorToolFireEffect; +}; + +datablock AudioProfile(MergeToolFireSound) +{ + filename = "fx/weapons/spinfusor_reload.wav"; + description = AudioDefault3d; + preload = true; +// effect = MergeToolFireEffect; +}; + +datablock AudioProfile(PlasmaSwitchSound) +{ + filename = "fx/weapons/plasma_rifle_activate.wav"; + description = AudioClosest3d; + preload = true; + effect = PlasmaSwitchEffect; +}; + +datablock AudioProfile(PlasmaReloadSound) +{ + filename = "fx/weapons/plasma_rifle_reload.wav"; + description = Audioclosest3d; + preload = true; + effect = PlasmaReloadEffect; +}; + +datablock AudioProfile(PlasmaProjectileSound) +{ + filename = "fx/weapons/plasma_rifle_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(ChaingunSwitchSound) +{ + filename = "fx/weapons/chaingun_activate.wav"; + description = AudioClosest3d; + preload = true; +// effect = ChaingunSwitchEffect; +}; + +datablock AudioProfile(ChaingunFireSound) +{ + filename = "fx/weapons/chaingun_fire.wav"; + description = AudioDefaultLooping3d; + preload = true; +// effect = ChaingunFireEffect; +}; + +datablock AudioProfile(ChaingunProjectile) +{ + filename = "fx/weapons/chaingun_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(ChaingunImpact) +{ + filename = "fx/weapons/chaingun_impact.WAV"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(ChaingunSpinDownSound) +{ + filename = "fx/weapons/chaingun_spindown.wav"; + description = AudioClosest3d; + preload = true; +// effect = ChaingunSpinDownEffect; +}; + +datablock AudioProfile(ChaingunSpinUpSound) +{ + filename = "fx/weapons/chaingun_spinup.wav"; + description = AudioClosest3d; + preload = true; +// effect = ChaingunSpinUpEffect; +}; + +datablock AudioProfile(ChaingunDryFireSound) +{ + filename = "fx/weapons/chaingun_dryfire.wav"; + description = AudioClose3d; + preload = true; +// effect = ChaingunDryFire; +}; + +datablock AudioProfile(GrenadeFireSound) +{ + filename = "fx/weapons/grenadelauncher_fire.wav"; + description = AudioDefault3d; + preload = true; +// effect = GrenadeFireEffect; +}; + +datablock AudioProfile(GrenadeProjectileSound) +{ + filename = "fx/weapons/grenadelauncher_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(GrenadeExplosionSound) +{ + filename = "fx/weapons/grenade_explode.wav"; + description = AudioExplosion3d; + preload = true; +// effect = GrenadeExplosionEffect; +}; + +datablock AudioProfile(GrenadeDryFireSound) +{ + filename = "fx/weapons/grenadelauncher_dryfire.wav"; + description = AudioClose3d; + preload = true; +// effect = GrenadeDryFireEffect; +}; + +datablock AudioProfile(MortarSwitchSound) +{ + filename = "fx/weapons/mortar_activate.wav"; + description = AudioClosest3d; + preload = true; +// effect = MortarSwitchEffect; +}; + +datablock AudioProfile(MortarReloadSound) +{ + filename = "fx/weapons/mortar_reload.wav"; + description = AudioClosest3d; + preload = true; +// effect = MortarReloadEffect; +}; + +datablock AudioProfile(MortarProjectileSound) +{ + filename = "fx/weapons/mortar_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(MortarExplosionSound) +{ + filename = "fx/weapons/mortar_explode.wav"; + description = AudioBIGExplosion3d; + preload = true; +// effect = MortarExplosionEffect; +}; + +datablock AudioProfile(MortarDryFireSound) +{ + filename = "fx/weapons/mortar_dryfire.wav"; + description = AudioClose3d; + preload = true; +// effect = MortarDryFireEffect; +}; + +datablock AudioProfile(MortarFireSound) +{ + filename = "fx/weapons/mortar_fire.wav"; + description = AudioDefault3d; + preload = true; +// effect = MortarFireEffect; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Weapon Splash Particles +//------------------------------------------------------------------------------ + +datablock ParticleData( BlasterSplashParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.4; + lifetimeMS = 300; + 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.05; + sizes[1] = 0.2; + sizes[2] = 0.2; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( BlasterSplashEmitter ) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionVelocity = 4; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "BlasterSplashParticle"; +}; + +datablock SplashData(BlasterSplash) +{ + numSegments = 15; + ejectionFreq = 15; + ejectionAngle = 40; + ringLifetime = 0.35; + lifetimeMS = 300; + velocity = 3.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = BlasterSplashEmitter; + + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 0.0"; + 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; +}; + +datablock ParticleData( ChaingunSplashParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.4; + lifetimeMS = 300; + 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.05; + sizes[1] = 0.2; + sizes[2] = 0.2; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( ChaingunSplashEmitter ) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionVelocity = 3; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "ChaingunSplashParticle"; +}; + + +datablock SplashData(ChaingunSplash) +{ + numSegments = 10; + ejectionFreq = 10; + ejectionAngle = 20; + ringLifetime = 0.4; + lifetimeMS = 400; + velocity = 3.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = ChaingunSplashEmitter; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 0.0"; + 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; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Explosion Data +//------------------------------------------------------------------------------ + +datablock ExplosionData(PlasmaBoltExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + soundProfile = GenericExplosionSound; + particleDensity = 150; + particleRadius = 0.02; + faceViewer = true; + + sizes[0] = "0.5 0.5 0.5"; + sizes[1] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 1.5; +}; + +datablock ParticleData(ChaingunExplosionParticle1) +{ + dragCoefficient = 0.65; + gravityCoefficient = 0.3; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 150; + textureName = "particleTest"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 0.0"; + sizes[0] = 0.0625; + sizes[1] = 0.2; +}; + +datablock ParticleEmitterData(ChaingunExplosionEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 0.75; + velocityVariance = 0.25; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "ChaingunExplosionParticle1"; +}; + +datablock ParticleData(ChaingunImpactSmokeParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.2; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1000; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + textureName = "particleTest"; + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.7 0.7 0.7 0.4"; + colors[2] = "0.7 0.7 0.7 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(ChaingunImpactSmoke) +{ + ejectionPeriodMS = 8; + periodVarianceMS = 1; + ejectionVelocity = 1.0; + velocityVariance = 0.5; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 35; + overrideAdvances = false; + particles = "ChaingunImpactSmokeParticle"; + lifetimeMS = 50; +}; + + +datablock ParticleData(ChaingunSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 300; + lifetimeVarianceMS = 0; + textureName = "special/spark00"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 0.6; + sizes[1] = 0.2; + sizes[2] = 0.05; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(ChaingunSparkEmitter) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionVelocity = 4; + velocityVariance = 2.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "ChaingunSparks"; +}; + + +datablock ExplosionData(ChaingunExplosion) +{ + soundProfile = ChaingunImpact; + + emitter[0] = ChaingunImpactSmoke; + emitter[1] = ChaingunSparkEmitter; + + faceViewer = false; +}; + +//-------------------------------------------------------------------------- +//MISC +//-------------------------------------------------------------------------- + +datablock ParticleData(PlasmaRifleParticle) +{ + dragCoefficient = 2.75; + gravityCoefficient = 0.1; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 550; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.46 0.36 0.26 1.0"; + colors[1] = "0.46 0.36 0.26 0.0"; + sizes[0] = 0.25; + sizes[1] = 0.20; +}; + +datablock ParticleEmitterData(PlasmaRifleEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 12; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvance = true; + particles = "PlasmaRifleParticle"; +}; + +datablock ParticleData(ChaingunFireParticle) +{ + dragCoefficient = 2.75; + gravityCoefficient = 0.1; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 550; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.46 0.36 0.26 1.0"; + colors[1] = "0.46 0.36 0.26 0.0"; + sizes[0] = 0.25; + sizes[1] = 0.20; +}; + +datablock ParticleEmitterData(ChaingunFireEmitter) +{ + ejectionPeriodMS = 6; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 12; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvance = true; + particles = "ChaingunFireParticle"; +}; + +datablock ExplosionData(DiscExplosion) +{ + explosionShape = "disc_explosion.dts"; + soundProfile = discExpSound; + + faceViewer = true; + explosionScale = "1 1 1"; + + shakeCamera = true; + camShakeFreq = "10.0 11.0 10.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 10.0; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ParticleData(GrenadeExplosionBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 1.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(GrenadeExplosionBubbleEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 3.0; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "GrenadeExplosionBubbleParticle"; +}; + +datablock ParticleData(UnderwaterGrenadeDust) +{ + dragCoefficient = 1.0; + gravityCoefficient = -0.01; + inheritedVelFactor = 0.0; + constantAcceleration = -1.1; + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + useInvAlpha = false; + spinRandomMin = -90.0; + spinRandomMax = 500.0; + textureName = "particleTest"; + colors[0] = "0.6 0.6 1.0 0.5"; + colors[1] = "0.6 0.6 1.0 0.5"; + colors[2] = "0.6 0.6 1.0 0.0"; + sizes[0] = 3.0; + sizes[1] = 3.0; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(UnderwaterGrenadeDustEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + ejectionVelocity = 15.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 70; + thetaMax = 70; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "UnderwaterGrenadeDust"; +}; + +datablock ParticleData(UnderwaterGrenadeExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.25; // rises slowly + inheritedVelFactor = 0.025; + constantAcceleration = -1.1; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.1 0.1 1.0 1.0"; + colors[1] = "0.4 0.4 1.0 1.0"; + colors[2] = "0.4 0.4 1.0 0.0"; + sizes[0] = 2.0; + sizes[1] = 6.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterGExplosionSmokeEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + + ejectionVelocity = 6.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 90.0; + + lifetimeMS = 250; + + particles = "UnderwaterGrenadeExplosionSmoke"; +}; + +datablock ParticleData(UnderwaterGrenadeSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/underwaterSpark"; + colors[0] = "0.6 0.6 1.0 1.0"; + colors[1] = "0.6 0.6 1.0 1.0"; + colors[2] = "0.6 0.6 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.75; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterGrenadeSparksEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + ejectionVelocity = 12; + velocityVariance = 6.75; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "UnderwaterGrenadeSparks"; +}; + +datablock ExplosionData(UnderwaterGrenadeExplosion) +{ + soundProfile = UnderwaterGrenadeExplosionSound; + + faceViewer = true; + explosionScale = "0.8 0.8 0.8"; + + emitter[0] = UnderwaterGrenadeDustEmitter; + emitter[1] = UnderwaterGExplosionSmokeEmitter; + emitter[2] = UnderwaterGrenadeSparksEmitter; + emitter[3] = GrenadeExplosionBubbleEmitter; + + shakeCamera = true; + camShakeFreq = "10.0 6.0 9.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 20.0; +}; + +datablock ParticleData(GrenadeBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + 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.5; + sizes[1] = 0.5; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(GrenadeBubbleEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 0.1; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "GrenadeBubbleParticle"; +}; + +datablock ParticleData( GDebrisSmokeParticle ) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + colors[0] = "0.4 0.4 0.4 1.0"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 0.0; + sizes[1] = 1.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( GDebrisSmokeEmitter ) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 1; + + ejectionVelocity = 1.0; + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "GDebrisSmokeParticle"; +}; + + +datablock DebrisData( GrenadeDebris ) +{ + emitters[0] = GDebrisSmokeEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 0.3; + lifetimeVariance = 0.02; + + numBounces = 1; +}; + +datablock ParticleData( GrenadeSplashParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.4; + lifetimeMS = 300; + 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.05; + sizes[1] = 0.2; + sizes[2] = 0.2; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( GrenadeSplashEmitter ) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionVelocity = 4; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "BlasterSplashParticle"; +}; + + +datablock SplashData(GrenadeSplash) +{ + numSegments = 15; + ejectionFreq = 15; + ejectionAngle = 40; + ringLifetime = 0.35; + lifetimeMS = 300; + velocity = 3.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = BlasterSplashEmitter; + + colors[0] = "0.7 0.8 1.0 1.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 1.0"; + colors[3] = "0.7 0.8 1.0 1.0"; + times[0] = 0.0; + times[1] = 0.4; + times[2] = 0.8; + times[3] = 1.0; +}; + +datablock ParticleData(GrenadeSmokeParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.2; // rises slowly + inheritedVelFactor = 0.00; + + lifetimeMS = 700; // lasts 2 second + lifetimeVarianceMS = 150; // ...more or less + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -30.0; + spinRandomMax = 30.0; + + colors[0] = "0.9 0.9 0.9 1.0"; + colors[1] = "0.6 0.6 0.6 1.0"; + colors[2] = "0.4 0.4 0.4 0.0"; + + sizes[0] = 0.25; + sizes[1] = 1.0; + sizes[2] = 3.0; + + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(GrenadeSmokeEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 5; + + ejectionVelocity = 1.25; + velocityVariance = 0.50; + + thetaMin = 0.0; + thetaMax = 90.0; + + particles = "GrenadeSmokeParticle"; +}; + +datablock ParticleData(GrenadeDust) +{ + 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.3 0.3 0.3 0.5"; + colors[1] = "0.3 0.3 0.3 0.5"; + colors[2] = "0.3 0.3 0.3 0.0"; + sizes[0] = 3.2; + sizes[1] = 4.6; + sizes[2] = 5.0; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(GrenadeDustEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 15.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 85; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "GrenadeDust"; +}; + + +datablock ParticleData(GrenadeExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.5; + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.7 0.7 0.7 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.1 0.1 0.1 0.0"; + sizes[0] = 2.0; + sizes[1] = 6.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(GExplosionSmokeEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionVelocity = 6.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 90.0; + + lifetimeMS = 250; + + particles = "GrenadeExplosionSmoke"; +}; + +datablock ParticleData(GrenadeSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/bigspark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.5; + sizes[2] = 0.75; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(GrenadeSparksEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + ejectionVelocity = 12; + velocityVariance = 6.75; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "GrenadeSparks"; +}; + +datablock ExplosionData(GrenadeExplosion) +{ + soundProfile = GrenadeExplosionSound; + + faceViewer = true; + explosionScale = "0.8 0.8 0.8"; + + debris = GrenadeDebris; + debrisThetaMin = 10; + debrisThetaMax = 50; + debrisNum = 8; + debrisVelocity = 26.0; + debrisVelocityVariance = 7.0; + + emitter[0] = GrenadeDustEmitter; + emitter[1] = GExplosionSmokeEmitter; + emitter[2] = GrenadeSparksEmitter; + + shakeCamera = true; + camShakeFreq = "10.0 6.0 9.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 20.0; +}; + +datablock ParticleData(MortarBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + 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.8; + sizes[1] = 0.8; + sizes[2] = 0.8; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(MortarBubbleEmitter) +{ + ejectionPeriodMS = 9; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 0.1; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "MortarBubbleParticle"; +}; + +datablock ParticleData( MortarSplashParticle ) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -1.4; + lifetimeMS = 300; + 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.05; + sizes[1] = 0.2; + sizes[2] = 0.2; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MortarSplashEmitter ) +{ + ejectionPeriodMS = 4; + periodVarianceMS = 0; + ejectionVelocity = 3; + velocityVariance = 1.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "MortarSplashParticle"; +}; + + +datablock SplashData(MortarSplash) +{ + numSegments = 10; + ejectionFreq = 10; + ejectionAngle = 20; + ringLifetime = 0.4; + lifetimeMS = 400; + velocity = 3.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + texture = "special/water2"; + + emitter[0] = MortarSplashEmitter; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 0.0"; + 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; +}; + +datablock ShockwaveData(UnderwaterMortarShockwave) +{ + width = 6.0; + numSegments = 32; + numVertSegments = 6; + velocity = 10; + acceleration = 20.0; + lifetimeMS = 900; + height = 1.0; + verticalCurve = 0.5; + is2D = false; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "0.4 0.4 1.0 0.50"; + colors[1] = "0.4 0.4 1.0 0.25"; + colors[2] = "0.4 0.4 1.0 0.0"; + + mapToTerrain = true; + orientToNormal = false; + renderBottom = false; +}; + +datablock ShockwaveData(MortarShockwave) +{ + width = 6.0; + numSegments = 32; + numVertSegments = 6; + velocity = 15; + acceleration = 20.0; + lifetimeMS = 500; + height = 1.0; + verticalCurve = 0.5; + is2D = false; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "0.4 1.0 0.4 0.50"; + colors[1] = "0.4 1.0 0.4 0.25"; + colors[2] = "0.4 1.0 0.4 0.0"; + + mapToTerrain = true; + orientToNormal = false; + renderBottom = false; +}; + +datablock ParticleData( MortarCrescentParticle ) +{ + dragCoefficient = 2; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 600; + lifetimeVarianceMS = 000; + textureName = "special/crescent3"; + colors[0] = "0.7 1.0 0.7 1.0"; + colors[1] = "0.7 1.0 0.7 0.5"; + colors[2] = "0.7 1.0 0.7 0.0"; + sizes[0] = 4.0; + sizes[1] = 8.0; + sizes[2] = 9.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MortarCrescentEmitter ) +{ + ejectionPeriodMS = 25; + periodVarianceMS = 0; + ejectionVelocity = 40; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 200; + particles = "MortarCrescentParticle"; +}; + + +datablock ParticleData(MortarExplosionSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.30; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 1250; + lifetimeVarianceMS = 500; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "0.7 0.7 0.7 0.0"; + colors[1] = "0.4 0.4 0.4 0.5"; + colors[2] = "0.4 0.4 0.4 0.5"; + colors[3] = "0.4 0.4 0.4 0.0"; + sizes[0] = 5.0; + sizes[1] = 6.0; + sizes[2] = 10.0; + sizes[3] = 12.0; + times[0] = 0.0; + times[1] = 0.333; + times[2] = 0.666; + times[3] = 1.0; + +}; + +datablock ParticleEmitterData(MortarExplosionSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionOffset = 8.0; + + + ejectionVelocity = 1.25; + velocityVariance = 1.2; + + thetaMin = 0.0; + thetaMax = 90.0; + + lifetimeMS = 500; + + particles = "MortarExplosionSmoke"; + +}; + +datablock ParticleData(UnderwaterExplosionSparks) +{ + dragCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/crescent3"; + colors[0] = "0.4 0.4 1.0 1.0"; + colors[1] = "0.4 0.4 1.0 1.0"; + colors[2] = "0.4 0.4 1.0 0.0"; + sizes[0] = 3.5; + sizes[1] = 3.5; + sizes[2] = 3.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterExplosionSparksEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + ejectionVelocity = 17; + velocityVariance = 4; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 60; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "UnderwaterExplosionSparks"; +}; + +datablock ParticleData(MortarExplosionBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 1500; + lifetimeVarianceMS = 600; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 2.0; + sizes[1] = 2.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.8; + times[2] = 1.0; +}; +datablock ParticleEmitterData(MortarExplosionBubbleEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 7.0; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "MortarExplosionBubbleParticle"; +}; + +datablock DebrisData( UnderwaterMortarDebris ) +{ + emitters[0] = MortarExplosionBubbleEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 1.5; + lifetimeVariance = 0.2; + + numBounces = 1; +}; + +datablock ExplosionData(UnderwaterMortarSubExplosion1) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + delayMS = 100; + offset = 3.0; + playSpeed = 1.5; + + sizes[0] = "0.75 0.75 0.75"; + sizes[1] = "1.0 1.0 1.0"; + sizes[2] = "0.5 0.5 0.5"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ExplosionData(UnderwaterMortarSubExplosion2) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + delayMS = 50; + offset = 3.0; + playSpeed = 0.75; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "1.5 1.5 1.5"; + sizes[2] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ExplosionData(UnderwaterMortarSubExplosion3) +{ + explosionShape = "disc_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 0.5; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "2.0 2.0 2.0"; + sizes[2] = "1.5 1.5 1.5"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ExplosionData(UnderwaterMortarExplosion) +{ + soundProfile = UnderwaterMortarExplosionSound; + + shockwave = UnderwaterMortarShockwave; + shockwaveOnTerrain = true; + + subExplosion[0] = UnderwaterMortarSubExplosion1; + subExplosion[1] = UnderwaterMortarSubExplosion2; + subExplosion[2] = UnderwaterMortarSubExplosion3; + + emitter[0] = MortarExplosionBubbleEmitter; + emitter[1] = UnderwaterExplosionSparksEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 9.0 7.0"; + camShakeAmp = "100.0 100.0 100.0"; + camShakeDuration = 1.3; + camShakeRadius = 25.0; +}; + +datablock ExplosionData(MortarSubExplosion1) +{ + explosionShape = "mortar_explosion.dts"; + faceViewer = true; + + delayMS = 100; + + offset = 5.0; + + playSpeed = 1.5; + + sizes[0] = "0.5 0.5 0.5"; + sizes[1] = "0.5 0.5 0.5"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(MortarSubExplosion2) +{ + explosionShape = "mortar_explosion.dts"; + faceViewer = true; + + delayMS = 50; + + offset = 5.0; + + playSpeed = 1.0; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "1.0 1.0 1.0"; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ExplosionData(MortarSubExplosion3) +{ + explosionShape = "mortar_explosion.dts"; + faceViewer = true; + delayMS = 0; + offset = 0.0; + playSpeed = 0.7; + + sizes[0] = "1.0 1.0 1.0"; + sizes[1] = "2.0 2.0 2.0"; + times[0] = 0.0; + times[1] = 1.0; + +}; + +datablock ExplosionData(MortarExplosion) +{ + soundProfile = MortarExplosionSound; + + shockwave = MortarShockwave; + shockwaveOnTerrain = true; + + subExplosion[0] = MortarSubExplosion1; + subExplosion[1] = MortarSubExplosion2; + subExplosion[2] = MortarSubExplosion3; + + emitter[0] = MortarExplosionSmokeEmitter; + emitter[1] = MortarCrescentEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 9.0 7.0"; + camShakeAmp = "100.0 100.0 100.0"; + camShakeDuration = 1.3; + camShakeRadius = 25.0; +}; + +datablock ParticleData(MortarSmokeParticle) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.3; // rises slowly + inheritedVelFactor = 0.125; + + lifetimeMS = 1200; + lifetimeVarianceMS = 200; + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + animateTexture = false; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "0.7 1.0 0.7 0.5"; + colors[1] = "0.3 0.7 0.3 0.8"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 2.0; + sizes[2] = 4.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(MortarSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 3; + + ejectionVelocity = 2.25; + velocityVariance = 0.55; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "MortarSmokeParticle"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Projectile Data +//------------------------------------------------------------------------------ + +datablock TracerProjectileData(ChaingunBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.0825; + directDamageType = $DamageType::Bullet; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + + kickBackStrength = 0.0; + sound = ChaingunProjectile; + + dryVelocity = 425.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.10; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +datablock GrenadeProjectileData(BasicGrenade) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.40; + damageRadius = 15.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 1500; + bubbleEmitTime = 1.0; + + sound = GrenadeProjectileSound; + explosion = "GrenadeExplosion"; + underwaterExplosion = "UnderwaterGrenadeExplosion"; + velInheritFactor = 0.5; + splash = GrenadeSplash; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.35; + grenadeFriction = 0.2; + armingDelayMS = 1000; + muzzleVelocity = 47.00; + drag = 0.1; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Ammo Data +//------------------------------------------------------------------------------ + +datablock ItemData(PlasmaAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_plasma.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some plasma gun ammo"; +}; + +datablock ItemData(ChaingunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some chaingun ammo"; + + computeCRC = true; + +}; + +datablock ItemData(DiscAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_disc.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some spinfusor discs"; +}; + +datablock ItemData(GrenadeLauncherAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_grenade.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some grenade launcher ammo"; + + computeCRC = true; + emap = true; +}; + +datablock ItemData(MortarAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_mortar.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some mortar ammo"; + + computeCRC = true; +}; + +datablock DebrisData( ShellDebris ) +{ + shapeName = "weapon_chaingun_ammocasing.dts"; + + lifetime = 10.0; + + minSpinSpeed = 200.0; + maxSpinSpeed = 800.0; + + elasticity = 0.5; + friction = 0.2; + + numBounces = 4; + + fade = true; + staticOnMaxBounce = true; + snapOnMaxBounce = true; +}; + +datablock DecalData(ChaingunDecal1) +{ + sizeX = 0.05; + sizeY = 0.05; + textureName = "skins/station_damagem1"; +}; + +datablock DecalData(RailGunDecal) +{ + sizeX = 2.0; + sizeY = 2.0; + textureName = "skins/bullethole6"; +}; diff --git a/Scripts/Weapons/bazooka.cs b/Scripts/Weapons/bazooka.cs new file mode 100644 index 0000000..b780f3e --- /dev/null +++ b/Scripts/Weapons/bazooka.cs @@ -0,0 +1,250 @@ +//-------------------------------------- +// bazooka +//-------------------------------------- + +datablock ParticleData(BazookaFireEffectSmoke) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = -0.25; // rises slowly + inheritedVelFactor = 0.025; + + lifetimeMS = 500; + lifetimeVarianceMS = 50; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -40.0; + spinRandomMax = 40.0; + + textureName = "special/Smoke/bigSmoke"; + + colors[0] = "1.0 0.5 0.0 0.3"; + colors[1] = "0.6 0.6 0.0 0.2"; + colors[2] = "0.4 0.4 0.4 0.1"; + colors[3] = "0.3 0.3 0.3 0.0"; + sizes[0] = 1.0; + sizes[1] = 1.5; + sizes[2] = 2.0; + sizes[3] = 2.5; + times[0] = 0.0; + times[1] = 0.333; + times[2] = 0.666; + times[3] = 1.0; + +}; + +datablock ParticleEmitterData(BazookaFireEffectEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + + ejectionOffset = 3.0; + + ejectionVelocity = 12.0; + velocityVariance = 0.0; + + thetaMin = 178.0; + thetaMax = 180.0; + + lifetimeMS = 20; + + particles = "BazookaFireEffectSmoke"; + +}; + + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- +datablock GrenadeProjectileData(Bazookashot) +{ + projectileShapeName = "weapon_missile_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 1.5; + damageRadius = 8.5; + radiusDamageType = $DamageType::Bazooka; + kickBackStrength = 1000; + + explosion = "MissileExplosion"; + underwaterExplosion = "MissileExplosion"; + velInheritFactor = 0.5; + splash = MissileSplash; + depthTolerance = 0.01; + + baseEmitter = MissileFireEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.0; + grenadeFriction = 0.0; + armingDelayMS = -1; + + gravityMod = 0.37; + muzzleVelocity = 150.0; + drag = 0.1; + sound = MissileProjectileSound; + + hasLight = true; + lightRadius = 4; + lightColor = "0.05 0.2 0.05"; + + hasLightUnderwaterColor = true; + underWaterLightColor = "0.05 0.075 0.2"; + +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(BazookaAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_missile.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some missiles"; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ItemData(Bazooka) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_missile.dts"; + image = BazookaImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a Bazooka MK III"; + + computeCRC = true; + emap = true; +}; + +datablock ShapeBaseImageData(BazookaImage) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + item = Bazooka; + ammo = BazookaAmmo; + offset = "0 -0.5 0"; + armThread = lookms; + emap = true; + mass = 30; + + projectile = Bazookashot; + projectileType = GrenadeProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = MissileSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.1; + stateFire[3] = true; + stateEmitter[3] = "BazookaFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 1; + stateRecoil[3] = HeavyRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = MissileFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 4.0; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = MissileReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MissileDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "CheckWet"; + stateTransitionOnWet[7] = "WetFire"; + stateTransitionOnNotWet[7] = "Fire"; + + stateName[8] = "WetFire"; + stateSound[8] = MissileDryFireSound; + stateTimeoutValue[8] = 1.0; + stateTransitionOnTimeout[8] = "Ready"; +}; + +datablock ShapeBaseImageData(BazookaMiddleImage) +{ + armThread = lookms; + className = WeaponImage; + shapeFile = "weapon_Mortar.dts"; + offset = "-0.1 -1.25 0.1"; // L/R - F/B - T/B + rotation = "0 0 0 0"; // L/R - F/B - T/B + emap = true; +}; + +datablock ShapeBaseImageData(BazookaBackImage) +{ + armThread = lookms; + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + offset = "0 -0.75 0"; // L/R - F/B - T/B + rotation = "0 0 1 180"; // L/R - F/B - T/B + emap = true; + ammo = BazookaAmmo; +}; + +function BazookaImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %velocity = vectorlen(%obj.getVelocity()); + if (%velocity < 1) + %obj.applyKick(-750); + else if (%velocity > 1 && %Velocity < (%obj.maxForwardSpeed - 1)) + %obj.applyKick(-1750); + else + %obj.applyKick(-3250); +} + +function BazookaImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(BazookaMiddleImage, 5); + %obj.mountImage(BazookaBackImage, 6); +} + +function BazookaImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(5); + %obj.unmountImage(6); +} \ No newline at end of file diff --git a/Scripts/Weapons/constructionTool.cs b/Scripts/Weapons/constructionTool.cs new file mode 100644 index 0000000..7b20939 --- /dev/null +++ b/Scripts/Weapons/constructionTool.cs @@ -0,0 +1,1218 @@ +//-------------------------------------------------------------------------- +// Deconstruct Gun / Construction Tool +// Originally from Hammer Mod. Changed and redone for LuCiD MoD. +// Also Changed again and redone for Construction Mod. +// All changes made by LuCiD from LuCiD MoD & Mostlikely or JackTL from Construction Mod. + +$ReverseDeployItem[DeployedStationInventory] = InventoryDeployable; +$ReverseDeployItem[DeployedMotionSensor] = MotionSensorDeployable; +$ReverseDeployItem[DeployedPulseSensor] = PulseSensorDeployable; +$ReverseDeployItem[TurretDeployedOutdoor] = TurretOutdoorDeployable; +$ReverseDeployItem[TurretDeployedFloorIndoor] = TurretIndoorDeployable; +$ReverseDeployItem[TurretDeployedWallIndoor] = TurretIndoorDeployable; +$ReverseDeployItem[TurretDeployedCeilingIndoor] = TurretIndoorDeployable; +$ReverseDeployItem[TurretDeployedBase] = TurretBasePack; +$ReverseDeployItem[TurretDeployedSentry] = TurretSentryPack; +$ReverseDeployItem[TurretDeployedCamera] = CameraGrenade; +$ReverseDeployItem[TelePadDeployedBase] = TelePadPack; +$ReverseDeployItem[DeployedSpine] = "poof spineDeployable"; +$ReverseDeployItem[DeployedSpine2] = "poof spineDeployable"; +$ReverseDeployItem[DeployedWoodSpine] = "poof spineDeployable"; +$ReverseDeployItem[DeployedPad] = "poof spineDeployable"; +$ReverseDeployItem[Deployedfloor] = "poof floorDeployable"; +$ReverseDeployItem[Deployedwall] = "poof wallDeployable"; +$ReverseDeployItem[Deployedwwall] = "poof wwallDeployable"; +$ReverseDeployItem[Deployedmspine] = "poof mspineDeployable"; +$ReverseDeployItem[DeployedDoor] = "poof DoorDeployable"; +$ReverseDeployItem[SpawnPoint] = "SpawnPointPack"; +$ReverseDeployItem[DeployedEnergizer] = EnergizerDeployable; +$ReverseDeployItem[Deployedmspinering] = "poof nothing"; +$ReverseDeployItem[DiscTurretDeployed] = DiscTurretDeployable; +$ReverseDeployItem[StationInventory] = LargeInventoryDeployable; +$ReverseDeployItem[DeployedJumpad] = JumpadDeployable; +$ReverseDeployItemDeployedBeacon = Beacon; +$ReverseDeployItem[DeployedLogoProjector] = LogoProjectorDeployable; +$ReverseDeployItem[LaserDeployed] = TurretLaserDeployable; +$ReverseDeployItem[MissileRackTurretDeployed] = TurretMissileRackDeployable; +$ReverseDeployItem[SensorMediumPulse] = MediumSensorDeployable; +$ReverseDeployItem[SensorLargePulse] = LargeSensorDeployable; +$ReverseDeployItem[DeployedLightBase] = "LightDeployable"; +$ReverseDeployItem[DeployedZSpawnBase] = "ZSpawnDeployable"; +$ReverseDeployItem[DeployedSentinelPatrol] = "SentinelDeployable"; +$ReverseDeployItem[DeployedSpySatellite] = "SpySatelliteDeployable"; +$ReverseDeployItem[DeployedTripwire] = "TripwireDeployable"; +$ReverseDeployItem[DeployedEscapePod] = "EscapePodDeployable"; +$ReverseDeployItem[DeployedDiggerPack] = "DiggerPackDeployable"; +$ReverseDeployItem[RepairPad] = "RepairPadDeployable"; +$ReverseDeployItem[DronePad] = "DronePadDeployable"; + +//Note this can also be in "pack".cs +//[most] +$ReverseDeployItem[Mpm_Anti_TurretDeployed] = "TurretMpm_Anti_Deployable"; +$ReverseDeployItem[DeployableVehiclePadBottom] = "VehiclePadPack"; +$ReverseDeployItem[DeployableVehiclePad] = "VehiclePadPack"; +$ReverseDeployItem[DeployableVehicleStation] = "VehiclePadPack"; +$ReverseDeployItem[DeployableVehiclePad2] = "VehiclePadPack"; +$ReverseDeployItem[PotPipe] = "poof"; +$ReverseDeployItem[EmitterDep] = "poof EmitterDepPack"; +$ReverseDeployItem[AudioDep] = "poof AudioDepPack"; +$ReverseDeployItem[DispenserDep] = "poof DispenserDepPack"; +$ReverseDeployItem[Mpm_Beacon_Ghost] = "poof"; +$ReverseDeployItem[DetonationDep] = "DetonationDepPack"; +$ReverseDeployItem[DetonationDepArm] = "DetonationDepPack"; +$ReverseDeployItem[Deployedwaypoint] = "waypointDeployable"; +for (%i = 0;%i < 51;%i++) + $ReverseDeployItemDeployedForcefield[%i] = "ForceFieldDeployable"; +for (%i = 0;%i < 5;%i++) + $ReverseDeployItemDeployedGravityField[%i] = "GravityFieldDeployable"; +for (%i = 0;%i < 14;%i++) + $ReverseDeployItemDeployedTree[%i] = "TreeDeployable"; +for (%i = 0;%i < 13;%i++) + $ReverseDeployItemDeployedCrate[%i] = "CrateDeployable"; +for (%i = 0;%i < 17;%i++) + $ReverseDeployItemDeployedDecoration[%i] = "DecorationDeployable"; +// power +$ReverseDeployItem[GeneratorLarge] = GeneratorDeployable; +$ReverseDeployItem[SolarPanel] = SolarPanelDeployable; +$ReverseDeployItem[DeployedSwitch] = SwitchDeployable; +$ReverseDeployItem[DeployedvSwitch] = vSwitchDeployable; +// DeconTarget +$ReverseDeployItem[DeployedLTarget] = "poof"; + +//-------------------------------------------------------------------------- +// Sounds + +datablock EffectProfile(ConstructionToolSwitchEffect) { + effectname = "packs/packs.repairPackOn"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(ConstructionToolFireEffect) { + effectname = "misc/downloading"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock AudioProfile(ConstructionToolSwitchSound) { + filename = "fx/packs/packs.repairPackOn.wav"; + description = AudioClosest3d; + preload = true; + effect = ConstructionToolSwitchEffect; +}; + +datablock AudioProfile(ConstructionToolFireSound) { + filename = "fx/misc/downloading.wav"; + description = CloseLooping3d; + preload = true; + effect = ConstructionToolFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile + +datablock RepairProjectileData(ConstructionToolBeam) { + sound = ElfFireWetSound; + + beamRange = 100; + beamWidth = 0.15; + numSegments = 20; + texRepeat = 0.20; + blurFreq = 10.0; + blurLifetime = 1.0; + cutoffAngle = 25.0; + + textures[0] = "special/ELFLightning"; + textures[1] = "skins/flaregreen"; +}; + +//------------------------------------------------------------------------- +// shapebase datablocks + +datablock ItemData(ConstructionTool) { + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_shocklance.dts"; + image = ConstructionToolImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + rotate = true; + pickUpName = "a construction tool"; + + lightOnlyStatic = true; + lightType = "PulsingLight"; + lightColor = "0 0 1"; + lightTime = 1200; + lightRadius = 4; + + //computeCRC = true; + emap = true; +}; + +//-------------------------------------------------------------------------- +// Construction Tool + +datablock ShapeBaseImageData(ConstructionToolImage) { + shapeFile = "weapon_shocklance.dts"; + offset = "0 0 0"; + + item = ConstructionTool; + usesEnergy = true; + + minEnergy = 1; + + cutOffEnergy = 1.1; + emap = true; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.25; + stateSound[0] = ConstructionToolSwitchSound; + + stateName[1] = "ActivateReady"; + stateScript[1] = "onActivateReady"; + stateSpinThread[1] = Stop; + stateTransitionOnAmmo[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "ActivateReady"; + + stateName[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnNoAmmo[2] = "Deactivate"; + stateTransitionOnTriggerDown[2] = "Validate"; + + stateName[3] = "Validate"; + stateTransitionOnTimeout[3] = "Validate"; + stateTimeoutValue[3] = 0.2; + stateEnergyDrain[3] = 0.1; + stateSpinThread[3] = SpinUp; + stateScript[3] = "onValidate"; + stateIgnoreLoadedForReady[3] = true; + stateTransitionOnLoaded[3] = "PerformAction"; + stateTransitionOnNoAmmo[3] = "Deactivate"; + stateTransitionOnTriggerUp[3] = "Deactivate"; + + stateName[4] = "PerformAction"; + stateSound[4] = ConstructionToolFireSound; + stateScript[4] = "onPerformAction"; + stateSpinThread[4] = FullSpeed; + stateAllowImageChange[4] = false; + stateSequence[4] = "activate"; + stateFire[4] = true; + stateEnergyDrain[4] = 0.1; + stateTimeoutValue[4] = 0.2; + stateTransitionOnTimeOut[4] = "PerformAction"; + stateTransitionOnNoAmmo[4] = "Deactivate"; + stateTransitionOnTriggerUp[4] = "Deactivate"; + stateTransitionOnNotLoaded[4] = "Validate"; + + stateName[5] = "Deactivate"; + stateScript[5] = "onDeactivate"; + stateSpinThread[5] = SpinDown; + stateSequence[5] = "activate"; + stateDirection[5] = false; + stateTimeoutValue[5] = 0.2; + stateTransitionOnTimeout[5] = "ActivateReady"; +}; + +function ConstructionToolImage::onActivate(%this,%obj,%slot) { +} + +function ConstructionToolImage::onActivateReady(%this,%obj,%slot) { + %obj.errMsgSent = false; +} + +function ConstructionToolImage::onValidate(%this,%obj,%slot) { + switch (%obj.constructionToolMode) { + case 0: // disassemble + onValidateDisassemble(%this,%obj,%slot); + case 1: // rotate + onValidateRotate(%this,%obj,%slot); + case 2: // advanced rotate + onValidateAdvancedRotate(%this,%obj,%slot); + case 3: // power management + onValidatePowerManagement(%this,%obj,%slot); + } +} + +function ConstructionToolImage::onPerformAction(%this,%obj,%slot) { + // this = ConstructionToolImage datablock + // obj = player wielding the construction tool + // slot = weapon slot + + if (%obj.getEnergyLevel() <= %this.cutOffEnergy) { + if (%obj.performing > 0) + stopPerforming(%obj); + return; + } + + // reset the flag that indicates an error message has been sent + %obj.errMsgSent = false; + + switch (%obj.constructionToolMode) { + case 0: // disassemble + onPerformDisassemble(%this,%obj,%slot); + case 1: // rotate + onPerformRotate(%this,%obj,%slot); + case 2: // advanced rotate + onPerformAdvancedRotate(%this,%obj,%slot); + case 3: // power management + onPerformPowerManagement(%this,%obj,%slot); + } +} + +function ConstructionToolImage::onDeactivate(%this,%obj,%slot) { + %obj.setImageTrigger(%slot, false); + if (%obj.performing > 0) { + stopPerforming(%obj); + messageClient(%player.client, 'msgClient', '\c2Construction Tool stopped.'); + } + %obj.errMsgSent = false; +} + +function ConstructionToolImage::onMount(%this,%obj,%slot) { + if (!$Host::TournamentMode) { + %curWeap = ( %obj.getMountedImage($WeaponSlot) == 0 ) ? "" : %obj.getMountedImage($WeaponSlot).getName().item.pickUpName; + BottomPrint(%obj.client, "Now using " @ %curWeap, 2, 1 ); + } + %obj.errMsgSent = false; + %obj.client.setWeaponsHudActive(%this.item); + %obj.usingConstructionTool = true; + if (!%obj.constructionToolMode) + %obj.constructionToolMode = 0; + if (!%obj.constructionToolMode2) + %obj.constructionToolMode2 = 0; + WeaponImage::onMount(%this,%obj,%slot); +} + +function ConstructionToolImage::onUnmount(%data, %obj, %slot) { + %obj.usingConstructionTool = false; + %obj.setImageTrigger(%slot, false); + if (%obj.performing > 0) { + stopPerforming(%obj); + messageClient(%player.client, 'msgClient', '\c2Construction Tool stopped.'); + } + %obj.errMsgSent = false; + Parent::deconstruct(%data, %obj, %slot); + WeaponImage::onUnmount(%data, %obj, %slot); +} + +function ConstructionTool::onPickup(%this, %obj, %shape, %amount) { + // created to prevent console errors +} + +// -------------------------------------------------------------------------------- +// Common functions +// -------------------------------------------------------------------------------- + +function startPerforming(%player) { + // %player = the player who was using the construction tool + %initialDirection = %player.getMuzzleVector($WeaponSlot); + %initialPosition = %player.getMuzzlePoint($WeaponSlot); + + //else + %performTime=200; + if ($host::performTime>100) + %performTime=$host::performTime; + %player.performTime = %performTime; + // Temporary (hopefully) fix + %data = %player.performing.getDataBlock(); + %dataBlockName = %data.getName(); + if (%data.className !$= "forcefield" && %data.className !$= "gravityfield") { + %player.constructionToolProjectile = new RepairProjectile() { + dataBlock = ConstructionToolBeam; + initialDirection = %initialDirection; + initialPosition = %initialPosition; + sourceObject = %player; + sourceSlot = $WeaponSlot; + targetObject = %player.performing; + }; + MissionCleanup.add(%player.constructionToolProjectile); + } + %hTgt = %player.performing; + %hTgt.beingPerformed = true; +} + +function stopPerforming(%player) { + // %player = the player who was using the construction tool + if (%player.performing > 0) { + if (isObject(%player.constructionToolProjectile)) + %player.constructionToolProjectile.delete(); + } + %hTgt.beingPerformed = false; + %player.constructionToolProjectile = 0; + %player.performing = 0; + %player.performTime = 0; + %player.errMsgSent = true; + %player.setImageTrigger($WeaponSlot, false); + %player.setImageLoaded($WeaponSlot, false); + %client = %player.client; + if (%client.CampingThread) + cancel(%client.campingThread); + return; +} + +// -------------------------------------------------------------------------------- +// Disassemble functions +// -------------------------------------------------------------------------------- +function onValidateDisassemble(%this,%obj,%slot) { + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be dissed? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only deconstruct deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's disassembleable + if (%hTgt != %obj.performing) { + if (isObject(%obj.performing)) + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + // setting imageLoaded to true sends us to deconstruct state (function onPerformAction) + %obj.setImageLoaded(%slot, true); + } + else { + // there is a target in range, but it's not disable + if (!%obj.errMsgSent) { + messageClient(%obj.client, 'msgClient', '\c2You can\'t deconstruct that.'); + %obj.errMsgSent = true; + } + // if player was Disassembling something, stop the disassembling -- we're done + if (%obj.performing > 0) + stopPerforming(%obj); + } + } + else { + // there is no target in range + if (!%obj.errMsgSent) { + // send an error message only once + messageClient(%obj.client, 'msgClient', '\c2No target to deconstruct.'); + %obj.errMsgSent = true; + } + } +} + +function onPerformDisassemble(%this,%obj,%slot) { + %target = %obj.performing; + if (!%target) { + // no target -- whoops! never mind + stopPerforming(%obj); + return; + } + if (%obj.constructionToolMode2 == 0) + %cascade = 0; + else + %cascade = 1; + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + if (%hTgt.isobjective==1 || %hTgt.isobjective==2){ + messageClient(%obj.client, 'msgClient', "\c2Deconstruction failed! can't decon objectives!~wfx/powered/nexus_deny.wav"); + stopPerforming(%obj); + return; + } + // can it be dissed? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only deconstruct deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's disassembleable + if (%hTgt != %obj.performing) { // its not what we were originally dising + if (isObject(%obj.performing)) { + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + } + else { // continue dising + %obj.performTime = %obj.performTime - 120; + if (%obj.performTime < 0) { // we have a dis! + if (%dataBlockName $= "DeployedWarpgatePack") + $warpgateArray[%hTgt.myNumber] = ""; + if (%dataBlockName $= "DeployedSpinner" && %hTgt.las != "") + %hTgt.las.delete(); + if (%dataBlockName $= "DeployedSpotlightProjector") + %hTgt.laser.delete(); + if (%hTgt.isRemoved) + return; // Avoid duplicate disassemblies + if (%hTgt.team != %obj.team && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Deconstruction failed! Wrong team!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if ($Host::OnlyOwnerDeconstruct == 1 && %hTgt.getOwner() != %obj.client && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + if (isObject(%hTgt.getOwner())) + messageClient(%obj.client, 'msgClient', '\c2Deconstruction failed! This belongs to %1!~wfx/powered/nexus_deny.wav', %hTgt.getOwner().nameBase); + else + messageClient(%obj.client, 'msgClient', '\c2Deconstruction failed! Not your stuff!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + stopPerforming(%obj); + if (%cascade && $Host::OnlyOwnerCascade == 1 && %hTgt.getOwner() != %obj.client && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) + messageClient(%obj.client, 'msgClient', '\c2Cascade failed! Not your stuff!~wfx/powered/nexus_deny.wav'); + else { + if ((%hTgt.getType() & $TypeMasks::StaticShapeObjectType) && %hTgt.getDamageLeftPct() < (0.25 + ((getRandom() * 0.2) - 0.1))) { + if ($Host::InvincibleDeployables == 1 && %hTgt.getDamageLeftPct() <= 0) { +// %hTgt.setDamageLevel(%hTgt.getDataBlock().maxDamage - 0.01); + %hTgt.setDamageLevel(0); // Make sure we can blow it up.. + } + %hTgt.damageFailedDecon = true; + %hTgt.schedule(100,setDamageState,Destroyed); + messageClient(%obj.client, 'msgClient', '\c2Deconstruction failed!~wfx/powered/nexus_deny.wav'); + } + else { + %hTgt.getDataBlock().disassemble(%obj, %hTgt); // Run Item Specific code. + if (%cascade) + cascade(%hTgt,true); + if (%hTgt.getDataBlock().getName() $= "DeployedLTarget" && isObject(%hTgt.lMain)) + %revItem = %hTgt.lMain.getDataBlock().getName(); + else + %revItem = %hTgt.getDataBlock().getName(); + %newPack = $ReverseDeployItem[%revItem]; + if (getWord(%newPack,0) !$= "poof") { + %npack = new Item() { + dataBlock = %newPack; + }; + MissionCleanup.add(%npack); + %npack.schedulePop(); + if (%newPack $= "ForceFieldDeployable" || %newPack $= "GravityFieldDeployable" || %newPack $= "DecorationDeployable" || %newPack $= "DecorationSDeployable") + %pos = %hTgt.getWorldBoxCenter(); + else + %pos = %hTgt.getPosition(); + %npack.setTransform(getWords(%scanTarg,1,3) SPC "0 0 1" SPC (getRandom() * ($Pi * 2))); + } + } + } + } + } + } + } + else + stopPerforming(%obj); +} + +// -------------------------------------------------------------------------------- +// Rotate functions +// -------------------------------------------------------------------------------- + +function onValidateRotate(%this,%obj,%slot) { + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be rotated? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only rotate deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's rotatable + if (%hTgt != %obj.performing) { + if (isObject(%obj.performing)) + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + // setting imageLoaded to true sends us to rotate state (function onPerformAction) + %obj.setImageLoaded(%slot, true); + } + else { + // there is a target in range, but it's not rotatable + if (!%obj.errMsgSent) { + messageClient(%obj.client, 'msgClient', '\c2You can\'t rotate that.'); + %obj.errMsgSent = true; + } + // if player was rotating something, stop the rotating -- we're done + if (%obj.performing > 0) + stopPerforming(%obj); + } + } + else { + // there is no target in range + if (!%obj.errMsgSent) { + // send an error message only once + messageClient(%obj.client, 'msgClient', '\c2No target to rotate.'); + %obj.errMsgSent = true; + } + } +} + +function onPerformRotate(%this,%obj,%slot) { + %target = %obj.performing; + if (!%target) { + // no target -- whoops! never mind + stopPerforming(%obj); + return; + } + if (%obj.constructionToolMode2 == 0) + %rotVal = 1; + else + %rotVal = -1; + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be rotated? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only rotate deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's rotatable + if (%hTgt != %obj.performing) { // its not what we were originally rotating + if (isObject(%obj.performing)) { + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + } + else { // continue rotating + %obj.performTime = %obj.performTime - 120; + if (%obj.performTime < 0) { // we have a rot! + if (%hTgt.team != %obj.team && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Rotate failed! Wrong team!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if ($Host::OnlyOwnerRotate == 1 && %hTgt.getOwner() != %obj.client && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Rotate failed! Not your stuff!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + stopPerforming(%obj); + %obj = firstWord(%scanTarg); + %vec1 = posFromRaycast(%scanTarg); + %vec2 = normalFromRaycast(%scanTarg); + pullObject(%obj,%vec1,%vec2,$Pi/8*%rotVal,"0 0 0"); + } + } + } + } + else + stopPerforming(%obj); +} + +// -------------------------------------------------------------------------------- +// Advanced rotate functions +// -------------------------------------------------------------------------------- + +function onValidateAdvancedRotate(%this,%obj,%slot) { + %m2 = %obj.constructionToolMode2; + if (%m2 == 2 || %m2 == 3 || %m2 == 4 || %m2 == 5) { + %obj.setImageLoaded(%slot, true); + return; + } + + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be selected? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only select deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (!$Host::AllowUnderground) { + if (%dataBlockName $= "TelePadDeployedBase") + %canPerform = false; + } + if (%canPerform == true) { + // yes, it's selectable + if (%hTgt != %obj.performing) { + if (isObject(%obj.performing)) + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + // setting imageLoaded to true sends us to select state (function onPerformAction) + %obj.setImageLoaded(%slot, true); + } + else { + // there is a target in range, but it's not selectable + if (!%obj.errMsgSent) { + messageClient(%obj.client, 'msgClient', '\c2You can\'t select/rotate that.'); + %obj.errMsgSent = true; + } + // if player was selecting something, stop the selecting -- we're done + if (%obj.performing > 0) + stopPerforming(%obj); + } + } + else { + // there is no target in range + if (!%obj.errMsgSent) { + // send an error message only once + messageClient(%obj.client, 'msgClient', '\c2No target to select/rotate.'); + %obj.errMsgSent = true; + } + } +} + +function onPerformAdvancedRotate(%this,%obj,%slot) { + // Account for lag, and make gun a bit less trigger happy + %m2 = %obj.constructionToolMode2; + if (%m2 == 2 || %m2 == 3 || %m2 == 4 || %m2 == 5) { + if (getSimTime() < (%obj.constructionToolTime + 500)) + return; + } + else { + if (getSimTime() < (%obj.constructionToolTime + 500) && %obj.performTime < 0) + return; + } + %obj.constructionToolTime = getSimTime(); + + // Load rotation list + %list = %obj.rotList; + + // Validate list + %size = getWordCount(%list); + for(%id = 0; %id < %size; %id++) { + if (!isObject(getWord(%list,%id))) + %badIDs = %badIDs SPC %id; + } + %list = listDel(%list,trim(%badIDs)); + + if (!%list) // Is there a rotation list? + %list = %obj; // Set player as center. + + // Save list + %obj.rotList = %list; + + if (%obj.rotSpeed == 0) // So players can rotate without specifically setting rotSpeed + %obj.rotSpeed = 1; + + if (%obj.constructionToolMode2 == 2) { + %obj.rotSpeed++; + if (%obj.rotSpeed > 3) + %obj.rotSpeed = -3; + if (%obj.rotSpeed == 0) + %obj.rotSpeed = 1; + %mod = lev(%obj.rotSpeed) * 5 * mPow(3,mAbs(%obj.rotSpeed) -1); + bottomPrint(%obj.client,"Rotation speed set to:" SPC %mod SPC " degrees per tick",2,1); + return; + } + else if(%obj.constructionToolMode2 == 3) { + %angle =lev(%obj.rotSpeed) * 5 * mPow(3,mAbs(%obj.rotSpeed) -1) / 180 * $Pi; + %mod = lev(%obj.rotSpeed) * 5 * mPow(3,mAbs(%obj.rotSpeed) -1); + rotateSection(getWord(%list,0),%list,"0 0 1" SPC %angle); + %obj.constructionToolTime = %obj.constructionToolTime + (getWordCount(%list) * 100); + bottomPrint(%obj.client,getWordCount(%list) SPC "objects rotated" SPC %mod SPC " degrees (wait " @ mFloor((%obj.constructionToolTime - getSimTime()) / 1000) @ " seconds)",2,1); + return; + } + else if(%obj.constructionToolMode2 == 4) { + moveSelection(getWord(%list,1).getPosition(),getWord(%list,0).getPosition(),VectorSub(%p2,%p1),%list); + %obj.constructionToolTime = %obj.constructionToolTime + (getWordCount(%list) * 100); + bottomPrint(%obj.client,getWordCount(%list) SPC "objects moved wait " @ mFloor((%obj.constructionToolTime - getSimTime()) / 1000) @ " seconds)",2,1); + return; + } + else if(%obj.constructionToolMode2 == 5) { + for(%id = 0; %id < getWordCount(%list); %id++) { + %obj = getWord(%list,%id); + %obj.startFade(400,%id * 100,0); + if (isObject(%obj.lMain)) + %obj.lMain.startFade(400,%id * 100,0); + } + bottomPrint(%obj.client,getWordCount(%list) SPC "objects selected including the rotation center",2,1); + return; + } + else if(%obj.constructionToolMode2 == 6) { + %list = ""; + %obj.rotList = %list; + bottomPrint(%obj.client,"All objects removed from rotation list - player set as rotation center",2,1); + return; + } + + %target = %obj.performing; + if (!%target) { + // no target -- whoops! never mind + stopPerforming(%obj); + return; + } + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be selected? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only select deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (!$Host::AllowUnderground) { + if (%dataBlockName $= "TelePadDeployedBase") + %canPerform = false; + } + if (%canPerform == true) { + // yes, it's selectable + if (%hTgt != %obj.performing) { // its not what we were originally selecting + if (isObject(%obj.performing)) { + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + } + else { // continue selecting + %obj.performTime = %obj.performTime - 100; + if (%obj.performTime < 0) { // we have a select! + if (%hTgt.team != %obj.team && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Select failed! Wrong team!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if ($Host::OnlyOwnerRotate == 1 && %hTgt.getOwner() != %obj.client && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Select failed! Not your stuff!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if (%obj.constructionToolMode2 == 0) { + if (%hTgt == getWord(%list,0)) { // Is it already the center? + %list = listReplace(%list,%obj,0); // Replace player as center + bottomPrint(%obj.client,"Object deselected as center",2,1); + } + else { + // Remove it from the list if it was in the list to start with. + %loc = findWord(%list,%hTgt); + if (%loc !$= "") + %list = listDel(%list,%loc); + %list = listReplace(%list,%hTgt,0); // Replace player as center + bottomPrint(%obj.client,"Object selected as center",2,1); + } + %hTgt.startFade(400,%id * 100,0); + if (isObject(%hTgt.lMain)) + %hTgt.lMain.startFade(400,%id * 100,0); + } + else if (%obj.constructionToolMode2 == 1) { + if (%hTgt == getWord(%list,0)) + bottomPrint(%obj.client,"Object is already center of rotation",2,1); + else { + %loc = findWord(%list,%hTgt); + if (%loc !$= "") { // Is it already in the list? + %list = listDel(%list,%loc); + bottomPrint(%obj.client,"Object deselected",2,1); + } + else { + %tempList = %list; + %list = %tempList SPC %hTgt; + bottomPrint(%obj.client,"Object Selected",2,1); + } + } + %hTgt.startFade(400,%id * 100,0); + if (isObject(%hTgt.lMain)) + %hTgt.lMain.startFade(400,%id * 100,0); + } + } + } + } + } + else + stopPerforming(%obj); + %obj.rotList = %list; +} + +// -------------------------------------------------------------------------------- +// Power management functions +// -------------------------------------------------------------------------------- + +function onValidatePowerManagement(%this,%obj,%slot) { + + %m2 = %obj.constructionToolMode2; + if (%m2 == 1 || %m2 == 2) { + %obj.setImageLoaded(%slot, true); + return; + } + + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be used? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only use deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's usable + if (%hTgt != %obj.performing) { + if (isObject(%obj.performing)) + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + // setting imageLoaded to true sends us to use state (function onPerformAction) + %obj.setImageLoaded(%slot, true); + } + else { + // there is a target in range, but it's not usable + if (!%obj.errMsgSent) { + messageClient(%obj.client, 'msgClient', '\c2You can\'t use that.'); + %obj.errMsgSent = true; + } + // if player was using something, stop it -- we're done + if (%obj.performing > 0) + stopPerforming(%obj); + } + } + else { + // there is no target in range + if (!%obj.errMsgSent) { + // send an error message only once + messageClient(%obj.client, 'msgClient', '\c2No target to use.'); + %obj.errMsgSent = true; + } + } +} + +function onPerformPowerManagement(%this,%obj,%slot) { + // Account for lag, and make gun a bit less trigger happy + %m2 = %obj.constructionToolMode2; + if (%m2 == 1 || %m2 == 2) { + if (getSimTime() < (%obj.constructionToolTime + 500)) + return; + } + else { + if (getSimTime() < (%obj.constructionToolTime + 500) && %obj.performTime < 0) + return; + } + %obj.constructionToolTime = getSimTime(); + + if (%obj.constructionToolMode2 == 1) { + %obj.powerFreq++; + if (%obj.powerFreq > upperPowerFreq(%obj)) + %obj.powerFreq = 1; + displayPowerFreq(%obj); + return; + } + else if (%obj.constructionToolMode2 == 2) { + %obj.powerFreq--; + if (%obj.powerFreq < 1) + %obj.powerFreq = upperPowerFreq(%obj); + displayPowerFreq(%obj); + return; + } + + %target = %obj.performing; + if (!%target) { + // no target -- whoops! never mind + stopPerforming(%obj); + return; + } + + %hGun = %obj.getMountedImage(%slot); + // muzVec is the vector coming from the construction tool's "muzzle" + %muzVec = %obj.getMuzzleVector(%slot); + // muzNVec = normalized muzVec + %muzNVec = VectorNormalize(%muzVec); + %beamRange = ConstructionToolBeam.beamRange; + // scale muzNVec to the range the repair beam can reach + %muzScaled = VectorScale(%muzNVec, %beamRange); + // muzPoint = the actual point of the gun's "muzzle" + %muzPoint = %obj.getMuzzlePoint(%slot); + // rangeEnd = muzzle point + length of beam + %rangeEnd = VectorAdd(%muzPoint, %muzScaled); + // search for just about anything that can be damaged as well as interiors + %searchMasks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType; + // search for objects within the beam's range that fit the masks above + %scanTarg = ContainerRayCast(%muzPoint, %rangeEnd, %searchMasks, %obj); + // screen out interiors + if (%scanTarg && !(%scanTarg.getType() & $TypeMasks::InteriorObjectType)) { + // a target in range was found + %hTgt = firstWord(%scanTarg); + // can it be used? is it already being tooled? + if ($ReverseDeployItem[%hTgt.getDataBlock().getName()] !$= "" && !%hTht.beingPerformed) + %canPerform = true; + // Only use deployed items, not base ones + %dataBlockName = %hTgt.getDataBlock().getName(); + if (%dataBlockName $= "DeployedLTarget" && !isObject(%hTgt.lMain)) + %canPerform = false; + if (%canPerform == true && ( + %dataBlockName $= "StationInventory" || + %dataBlockName $= "GeneratorLarge" || + %dataBlockName $= "SolarPanel" || + %dataBlockName $= "SensorMediumPulse" || + %dataBlockName $= "SensorLargePulse" + )) + if (%hTgt.deployed != true) + %canPerform = false; + if (%canPerform == true) { + // yes, it's usable + if (%hTgt != %obj.performing) { // its not what we were originally using + if (isObject(%obj.performing)) { + stopPerforming(%obj); + %obj.performing = %hTgt; + startPerforming(%obj); + } + } + else { // continue using + %obj.performTime = %obj.performTime - 100; + if (%obj.performTime < 0) { // we have a use! + if (%hTgt.team != %obj.team && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Use failed! Wrong team!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if (%obj.constructionToolMode2 != 3 && %hTgt.getOwner() != %obj.client && !(%obj.client.isAdmin || %obj.client.isSuperAdmin)) { + messageClient(%obj.client, 'msgClient', '\c2Use failed! Not your stuff!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + if (%obj.constructionToolMode2 == 0) { + if (%hTgt.getDataBlock().className !$= "Generator") { + messageClient(%obj.client, 'msgClient', '\c2Use failed! Not a generator!~wfx/powered/nexus_deny.wav'); + stopPerforming(%obj); + return; + } + else { + %r = toggleGenerator(%hTgt); + %r1 = firstWord(%r); + %r2 = getTaggedString(getWord(%r,1)); + %r3 = getWord(%r,2); + if (%r1 == -2) + messageClient(%obj.client, 'msgClient', '\c2Use failed! %1 is damaged!~wfx/powered/nexus_deny.wav',%r2); + else if (%r1 == -3) + messageClient(%obj.client, 'msgClient', '\c2Must wait %1 seconds before toggling power state.',mCeil(%r3/1000)); + else if (%r1 == 1) + messageClient(%obj.client, 'msgClient', '\c2%1 switched off.',%r2); + else if (%r1 == 2) + messageClient(%obj.client, 'msgClient', '\c2%1 switched on.',%r2); + stopPerforming(%obj); + return; + } + } + if (%obj.constructionToolMode2 == 3) { +// if (%hTgt.powerFreq !$= "" || %hTgt.getDataBlock().needsPower) { + %numLines = 1; + if (%hTgt.powerFreq !$= "") + %msg = "Power frequency is " @ %hTgt.powerFreq @ ", power is " @ (%hTgt.isPowered() ? ((%hTgt.powerCount > 0) ? (%hTgt.powerCount) : "On") : "OFF"); + else + %msg = "Item does not have power frequency set"; + if (%hTgt.getOwner().nameBase !$= "") { + %numLines++; + %msg = %msg @ "\nThis belongs to " @ %hTgt.getOwner().nameBase; + } + if (%obj.client.isAdmin || %obj.client.isSuperAdmin) { + %numLines++; + if (%hTgt.getOwner() <1 || %hTgt.getOwner() $="") + %Owner=%hTgt.owner; + else + %Owner=%hTgt.getOwner(); + %msg = %msg @ "\n [Owner Clientid]:" SPC %Owner SPC "[Object id]:" SPC %htgt; + } + bottomPrint(%obj.client,%msg,2,%numLines); + stopPerforming(%obj); +// } + } + } + } + } + } + else + stopPerforming(%obj); + %obj.rotList = %list; +} + +//tring out stuf +function moveSelection(%p1,%p2,%movement,%list,%obj2) +{ +%movement = VectorSub(%p2,%p1); + + +%obj2 = 1; +while (isObject(%obj=getWord(%list,%obj2))) +{ +%obj.setTransform(VectorAdd(%obj.getPosition(),%movement) SPC ""); +%obj2++; +} +} diff --git a/Scripts/Weapons/flamethrower.cs b/Scripts/Weapons/flamethrower.cs new file mode 100644 index 0000000..2f4043b --- /dev/null +++ b/Scripts/Weapons/flamethrower.cs @@ -0,0 +1,320 @@ +//-------------------------------------- +// flamethrower +//-------------------------------------- + +//color tent for EVERYTHING its red really red +//1 r +//0.18 g +//0.03 b + +//-------------------------------------- +// Trail +//-------------------------------------- + +datablock ParticleData(flameParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.1; + inheritedVelFactor = 0.1; + + lifetimeMS = 500; + lifetimeVarianceMS = 50; + + textureName = "particleTest"; + + spinRandomMin = -10.0; + spinRandomMax = 10.0; + + colors[0] = "1 0.18 0.03 0.4"; + colors[1] = "1 0.18 0.03 0.3"; + colors[2] = "1 0.18 0.03 0.0"; + sizes[0] = 2.0; + sizes[1] = 1.0; + sizes[2] = 0.8; + times[0] = 0.0; + times[1] = 0.6; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(flameEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + + ejectionOffset = 0.2; + ejectionVelocity = 10.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 10.0; + + particles = "flameParticle"; +}; + +//-------------------------------------------------------------------------- +// Explosion +//-------------------------------------- +datablock ParticleData(flameExplosionParticle) +{ + dragCoefficient = 2; + gravityCoefficient = 0.2; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "1 0.18 0.03 0.6"; + colors[1] = "1 0.18 0.03 0.0"; + sizes[0] = 2; + sizes[1] = 2.5; +}; + +datablock ParticleEmitterData(flameExplosionEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionOffset = 2.0; + ejectionVelocity = 4.0; + velocityVariance = 0.0; + thetaMin = 60.0; + thetaMax = 90.0; + lifetimeMS = 250; + + particles = "flameExplosionParticle"; +}; + +datablock ExplosionData(flameBoltExplosion) +{ + particleEmitter = flameExplosionEmitter; + particleDensity = 150; + particleRadius = 1.25; + faceViewer = true; +}; + +//-------------------------------------------------------------------------- +// Idle Flame +//-------------------------------------- + +datablock ParticleData(FlamerIdleFlame) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.3; // rises slowly + inheritedVelFactor = 0.5; + constantAcceleration = 0.0; + + lifetimeMS = 500; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + spinRandomMin = -30.0; + spinRandomMax = 30.0; + + colors[0] = "1 0.18 0.03 0.3"; + colors[1] = "1 0.18 0.03 0.2"; + colors[2] = "1 0.18 0.03 0.0"; + sizes[0] = 0.2; + sizes[1] = 0.4; + sizes[2] = 0.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(FlamerIdleFlameEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 1; + + ejectionVelocity = 0.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 45.0; + + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + + particles = "FlamerIdleFlame"; +}; + +//-------------------------------------------------------------------------- +// Projectiles +//-------------------------------------- +datablock LinearFlareProjectileData(FlameboltMain) +{ + projectileShapeName = "turret_muzzlepoint.dts"; + scale = "1.0 1.0 1.0"; + faceViewer = true; + directDamage = 0.05; + hasDamageRadius = true; + indirectDamage = 0.1; + damageRadius = 4.0; + kickBackStrength = 0.0; + radiusDamageType = $DamageType::Plasma; + + explosion = "flameBoltExplosion"; + splash = PlasmaSplash; + + baseEmitter = FlameEmitter; + + dryVelocity = 50.0; // z0dd - ZOD, 7/20/02. Faster plasma projectile. was 55 + wetVelocity = -1; + velInheritFactor = 0.3; + fizzleTimeMS = 250; + lifetimeMS = 1000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = true; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = -1; + + //activateDelayMS = 100; + activateDelayMS = -1; + + size[0] = 0.2; + size[1] = 0.5; + size[2] = 0.1; + + + numFlares = 35; + flareColor = "1 0.18 0.03"; + flareModTexture = "flaremod"; + flareBaseTexture = "flarebase"; + + sound = PlasmaProjectileSound; + fireSound = PlasmaFireSound; + wetFireSound = PlasmaFireWetSound; + + hasLight = true; + lightRadius = 10.0; + lightColor = "0.94 0.03 0.12"; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(flamerAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_plasma.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some flame thrower fuel"; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ItemData(flamer) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_plasma.dts"; + image = flamerImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a flame thrower"; +}; + +datablock ShapeBaseImageData(flamerImage) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + mass = 10; + item = flamer; + ammo = flamerAmmo; + offset = "0 0 0"; // L/R - F/B - T/B + rotation = "0 1 0 180"; // L/R - F/B - T/B + + projectileSpread = 7.0 / 1000.0; + + projectile = flameBoltmain; + projectileType = LinearFlareProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateEmitter[2] = "FlamerIdleFlameEmitter"; + stateEmitterNode[2] = "muzzlepoint1"; + stateEmitterTime[2] = 10000; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.05; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.05; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.05; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = MergeFireSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = MortarDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "WetFire"; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "Ready"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "Fire"; +}; + +function flamerImage::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this,%obj,%slot); + CommandToClient(%obj.client, 'BottomPrint', "You must have a flamer ammo pack to get flamer ammo.", 3, 2 ); +} + +function burnloop(%obj){ + if(!isobject(%obj)) + return; + if(%obj.firecount >= %obj.maxfirecount){ + %obj.firecount = ""; + %obj.maxfirecount = 0; + %obj.onfire = 0; + } + else { + %obj.damage(0, %obj.getposition(), 0.02, $DamageType::Burn); + %fire = new ParticleEmissionDummy(){ + position = vectoradd(%obj.getPosition(),"0 0 0.5"); + dataBlock = "defaultEmissionDummy"; + emitter = "FlameEmitter"; + }; + MissionCleanup.add(%fire); + %fire.schedule(100, "delete"); + %obj.firecount++; + schedule(100, %obj, "burnloop", %obj); + } +} diff --git a/Scripts/Weapons/flareGrenade.cs b/Scripts/Weapons/flareGrenade.cs new file mode 100644 index 0000000..2b01cd1 --- /dev/null +++ b/Scripts/Weapons/flareGrenade.cs @@ -0,0 +1,221 @@ +// grenade (thrown by hand) script +// ------------------------------------------------------------------------ + +datablock AudioProfile(FlareGrenadeBurnSound) +{ + filename = "fx/weapons/grenade_flare_burn.wav"; + description = CloseLooping3d; + preload = true; +}; + +datablock AudioProfile(FlareGrenadeExplosionSound) +{ + filename = "fx/weapons/grenade_flare_burn.wav"; + description = CloseLooping3d; + preload = true; +}; + +//-------------------------------------------------------------------------- +// Particle effects +//-------------------------------------- +datablock ParticleData(FlareParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.15; + inheritedVelFactor = 0.5; + + lifetimeMS = 1800; + lifetimeVarianceMS = 200; + + textureName = "special/flareSpark"; + + colors[0] = "1.0 1.0 1.0 1.0"; + colors[1] = "1.0 1.0 1.0 1.0"; + colors[2] = "1.0 1.0 1.0 0.0"; + + sizes[0] = 0.6; + sizes[1] = 0.3; + sizes[2] = 0.1; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(FlareEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 1.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 90.0; + + orientParticles = true; + orientOnVelocity = false; + + particles = "FlareParticle"; +}; + +//-------------------------------------------------------------------------- +// Explosion - Flare Grenade +//-------------------------------------- +datablock ExplosionData(FlareGrenadeExplosion) +{ + explosionShape = "energy_explosion.dts"; + soundProfile = FlareGrenadeExplosionSound; + faceViewer = true; + explosionScale = "0.1 0.1 0.1"; +}; + +//-------------------------------------------------------------------------- +// Projectile - Flare Grenade +//-------------------------------------- +datablock FlareProjectileData(FlareGrenadeProj) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = false; + kickBackStrength = 1500; + useLensFlare = false; + + sound = FlareGrenadeBurnSound; + explosion = FlareGrenadeExplosion; + velInheritFactor = 0.5; + + texture[0] = "special/flare3"; + texture[1] = "special/LensFlare/flare00"; + size = 4.0; + + baseEmitter = FlareEmitter; + + grenadeElasticity = 0.35; + grenadeFriction = 0.2; + armingDelayMS = 6000; + muzzleVelocity = 15.0; + drag = 0.1; + gravityMod = 0.15; +}; + +datablock ItemData(FlareGrenadeThrown) +{ + shapeFile = "grenade.dts"; + mass = 0.7; + elasticity = 0.2; + friction = 1; + pickupRadius = 2; + maxDamage = 0.4; + + sticky = false; + gravityMod = 0.15; + maxVelocity = 10; + + computeCRC = true; + +}; + +datablock ItemData(FlareGrenade) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "grenade.dts"; + mass = 0.7; + elasticity = 0.2; + friction = 1; + pickupRadius = 2; + thrownItem = FlareGrenadeThrown; + pickUpName = "some flare grenades"; + isGrenade = true; + + computeCRC = true; + +}; + +//------------------------------------------------------------------------------ +// Functions: +//------------------------------------------------------------------------------ +function FlareGrenadeThrown::onCollision( %data, %obj, %col ) +{ + // Do nothing... +} + + +//------------------------------------------------------------------------------ +// VehicleFlare +//------------------------------------------------------------------------------ + +datablock ParticleData(VFlareParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.15; + inheritedVelFactor = 0.0; + + lifetimeMS = 2500; + lifetimeVarianceMS = 250; + + textureName = "particleTest"; + + colors[0] = "0.7 0.7 0.7 0.75"; + colors[1] = "0.7 0.7 0.7 0.4"; + colors[2] = "0.7 0.7 0.7 0.0"; + + sizes[0] = 1.0; + sizes[1] = 1.0; + sizes[2] = 1.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(VFlareEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + + ejectionVelocity = 7.50; + velocityVariance = 0.0; + + thetaMin = 40.0; + thetaMax = 42.0; + + phiReferenceVel = 360; + phiVariance = 55; + + orientParticles = true; + orientOnVelocity = true; + + particles = "VFlareParticle"; +}; + +datablock FlareProjectileData(VFlareGrenadeProj) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = false; + kickBackStrength = 0; + useLensFlare = false; + + sound = FlareGrenadeBurnSound; + explosion = FlareGrenadeExplosion; + velInheritFactor = 0.75; + + texture[0] = "special/flare3"; + texture[1] = "special/LensFlare/flare00"; + size = 1.0; + + baseEmitter = VFlareEmitter; + + grenadeElasticity = 0.35; + grenadeFriction = 0.2; + armingDelayMS = 6000; + muzzleVelocity = 25.0; + drag = 0.1; + gravityMod = 0.55; +}; diff --git a/Scripts/Weapons/flashGrenade.cs b/Scripts/Weapons/flashGrenade.cs new file mode 100644 index 0000000..1cee40b --- /dev/null +++ b/Scripts/Weapons/flashGrenade.cs @@ -0,0 +1,69 @@ +// grenade (thrown by hand) script +// ------------------------------------------------------------------------ +datablock EffectProfile(FlashGrenadeExplosionEffect) +{ + effectname = "explosions/grenade_flash_explode"; + minDistance = 10; + maxDistance = 30; +}; + +datablock AudioProfile(FlashGrenadeExplosionSound) +{ + filename = "fx/explosions/grenade_flash_explode.wav"; + description = AudioExplosion3d; + preload = true; + effect = FlashGrenadeExplosionEffect; +}; + +datablock ExplosionData(FlashGrenadeExplosion) +{ + explosionShape = "disc_explosion.dts"; + soundProfile = FlashGrenadeExplosionSound; + + faceViewer = true; +}; + +datablock ItemData(FlashGrenadeThrown) +{ + shapeFile = "grenade.dts"; + mass = 0.7; + elasticity = 0.2; + friction = 1; + pickupRadius = 2; + maxDamage = 0.4; + explosion = FlashGrenadeExplosion; + indirectDamage = 0.2; + damageRadius = 12.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 1000; + + computeCRC = true; + + maxWhiteout = 1.7; +}; + +datablock ItemData(FlashGrenade) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "grenade.dts"; + mass = 0.7; + elasticity = 0.2; + friction = 1; + pickupRadius = 2; + thrownItem = FlashGrenadeThrown; + pickUpName = "some flash grenades"; + isGrenade = true; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Functions: +//-------------------------------------------------------------------------- +function FlashGrenadeThrown::onCollision( %data, %obj, %col ) +{ + // Do nothing... +} + diff --git a/Scripts/Weapons/grenade.cs b/Scripts/Weapons/grenade.cs new file mode 100644 index 0000000..1a776ad --- /dev/null +++ b/Scripts/Weapons/grenade.cs @@ -0,0 +1,421 @@ +// ------------------------------------------------------------------------ +// grenade (thrown by hand) script +// ------------------------------------------------------------------------ +datablock EffectProfile(GrenadeThrowEffect) +{ + effectname = "weapons/grenade_throw"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(GrenadeSwitchEffect) +{ + effectname = "weapons/generic_switch"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(GrenadeThrowSound) +{ + filename = "fx/weapons/throw_grenade.wav"; + description = AudioClose3D; + preload = true; + effect = GrenadeThrowEffect; +}; + +datablock AudioProfile(GrenadeSwitchSound) +{ + filename = "fx/weapons/generic_switch.wav"; + description = AudioClosest3D; + preload = true; + effect = GrenadeSwitchEffect; +}; + +//************************************************************************** +// Hand Grenade underwater fx +//************************************************************************** + + +//-------------------------------------------------------------------------- +// Underwater Hand Grenade Particle effects +//-------------------------------------------------------------------------- +datablock ParticleData(HandGrenadeExplosionBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 750; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 0.75; + sizes[1] = 0.75; + sizes[2] = 0.75; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; +datablock ParticleEmitterData(HandGrenadeExplosionBubbleEmitter) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 2.0; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "HandGrenadeExplosionBubbleParticle"; +}; + +datablock ParticleData(UnderwaterHandGrenadeExplosionSmoke) +{ + dragCoeffiecient = 105.0; + gravityCoefficient = -0.0; // rises slowly + inheritedVelFactor = 0.025; + + constantAcceleration = -1.0; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.4 0.4 1.0 1.0"; + colors[1] = "0.4 0.4 1.0 0.5"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 3.0; + sizes[2] = 5.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterHandGrenadeExplosionSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 5.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 250; + + particles = "UnderwaterHandGrenadeExplosionSmoke"; +}; + + + +datablock ParticleData(UnderwaterHandGrenadeSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/droplet"; + colors[0] = "0.6 0.6 1.0 1.0"; + colors[1] = "0.6 0.6 1.0 1.0"; + colors[2] = "0.6 0.6 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.25; + sizes[2] = 0.25; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterHandGrenadeSparkEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 6.75; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "UnderwaterHandGrenadeSparks"; +}; + + + +datablock ExplosionData(UnderwaterHandGrenadeSubExplosion1) +{ + offset = 1.0; + emitter[0] = UnderwaterHandGrenadeExplosionSmokeEmitter; + emitter[1] = UnderwaterHandGrenadeSparkEmitter; +}; + +datablock ExplosionData(UnderwaterHandGrenadeSubExplosion2) +{ + offset = 1.0; + emitter[0] = UnderwaterHandGrenadeExplosionSmokeEmitter; + emitter[1] = UnderwaterHandGrenadeSparkEmitter; +}; + +datablock ExplosionData(UnderwaterHandGrenadeExplosion) +{ + soundProfile = GrenadeExplosionSound; + + emitter[0] = UnderwaterHandGrenadeExplosionSmokeEmitter; + emitter[1] = UnderwaterHandGrenadeSparkEmitter; + emitter[2] = HandGrenadeExplosionBubbleEmitter; + + subExplosion[0] = UnderwaterHandGrenadeSubExplosion1; + subExplosion[1] = UnderwaterHandGrenadeSubExplosion2; + + shakeCamera = true; + camShakeFreq = "12.0 13.0 11.0"; + camShakeAmp = "35.0 35.0 35.0"; + camShakeDuration = 1.0; + camShakeRadius = 15.0; +}; + +//************************************************************************** +// Hand Grenade effects +//************************************************************************** + +//-------------------------------------------------------------------------- +// Grenade Particle effects +//-------------------------------------------------------------------------- + +datablock ParticleData(HandGrenadeExplosionSmoke) +{ + dragCoeffiecient = 105.0; + gravityCoefficient = -0.0; // rises slowly + inheritedVelFactor = 0.025; + + constantAcceleration = -0.80; + + lifetimeMS = 1250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.0 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 3.0; + sizes[2] = 5.0; + times[0] = 0.0; + times[1] = 0.2; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(HandGrenadeExplosionSmokeEmitter) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + + ejectionVelocity = 10.25; + velocityVariance = 0.25; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 250; + + particles = "HandGrenadeExplosionSmoke"; +}; + + + +datablock ParticleData(HandGrenadeSparks) +{ + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 350; + textureName = "special/bigSpark"; + colors[0] = "0.56 0.36 0.26 1.0"; + colors[1] = "0.56 0.36 0.26 1.0"; + colors[2] = "1.0 0.36 0.26 0.0"; + sizes[0] = 0.5; + sizes[1] = 0.25; + sizes[2] = 0.25; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(HandGrenadeSparkEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 18; + velocityVariance = 6.75; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "HandGrenadeSparks"; +}; + + + + +//---------------------------------------------------- +// Explosion +//---------------------------------------------------- + +datablock ExplosionData(HandGrenadeSubExplosion1) +{ + offset = 2.0; + emitter[0] = HandGrenadeExplosionSmokeEmitter; + emitter[1] = HandGrenadeSparkEmitter; +}; + +datablock ExplosionData(HandGrenadeSubExplosion2) +{ + offset = 2.0; + emitter[0] = HandGrenadeExplosionSmokeEmitter; + emitter[1] = HandGrenadeSparkEmitter; +}; + +datablock ExplosionData(HandGrenadeExplosion) +{ + soundProfile = GrenadeExplosionSound; + + emitter[0] = HandGrenadeExplosionSmokeEmitter; + emitter[1] = HandGrenadeSparkEmitter; + + subExplosion[0] = HandGrenadeSubExplosion1; + subExplosion[1] = HandGrenadeSubExplosion2; + + shakeCamera = true; + camShakeFreq = "12.0 13.0 11.0"; + camShakeAmp = "35.0 35.0 35.0"; + camShakeDuration = 1.0; + camShakeRadius = 15.0; +}; + +datablock TracerProjectileData(GrenadeShrapnel) +{ + doDynamicClientHits = true; + + directDamage = 0.4; + directDamageType = $DamageType::Grenade; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + HeadMultiplier = 1.25; + LegsMultiplier = 0.75; + + kickBackStrength = 100.0; + sound = ChaingunProjectile; + + dryVelocity = 200.0; + wetVelocity = 100.0; + velInheritFactor = 0.0; + fizzleTimeMS = 500; + lifetimeMS = 500; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 1.0; + tracerAlpha = false; + tracerMinPixels = 1; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.0; + crossSize = 0.0; + crossViewAng = 0.0; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +datablock ItemData(GrenadeThrown) +{ + className = Weapon; + shapeFile = "grenade.dts"; + mass = 0.6; + elasticity = 0.3; + friction = 1; + pickupRadius = 2; + maxDamage = 0.5; + explosion = HandGrenadeExplosion; + underwaterExplosion = UnderwaterHandGrenadeExplosion; + indirectDamage = 1.4; + damageRadius = 10.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 3500; + + computeCRC = true; + + hasShrapnel = 1; + shrapnelNumber = 36; + minshrapnelSpread = 160; + maxshrapnelSpread = 180; + shrapnelProjectileType = TracerProjectile; + shrapnelProjectile = GrenadeShrapnel; +}; + +datablock ItemData(Grenade) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "grenade.dts"; + mass = 1; + elasticity = 0.5; + friction = 1; + pickupRadius = 2; + thrownItem = GrenadeThrown; + pickUpName = "some grenades"; + isGrenade = true; + + computeCRC = true; + +}; + diff --git a/Scripts/Weapons/mine.cs b/Scripts/Weapons/mine.cs new file mode 100644 index 0000000..0a5e7f5 --- /dev/null +++ b/Scripts/Weapons/mine.cs @@ -0,0 +1,339 @@ +// ---------------------------------------------- +// mine script +// ---------------------------------------------- + +$TeamDeployableMax[MineDeployed] = 25; // z0dd - ZOD, 6/10/02. Was 20. + +// ---------------------------------------------- +// force-feedback datablocks +// ---------------------------------------------- + +datablock EffectProfile(MineExplosionEffect) +{ + effectname = "explosions/mine_detonate"; + minDistance = 10; + maxDistance = 50; +}; + +// ---------------------------------------------- +// audio datablocks +// ---------------------------------------------- + +datablock AudioProfile(MineDeploySound) +{ + filename = "fx/weapons/mine_deploy.wav"; + description = AudioClose3D; + preload = true; +}; + +datablock AudioProfile(MineExplosionSound) +{ + filename = "fx/weapons/mine_detonate.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = MineExplosionEffect; +}; + +datablock AudioProfile(UnderwaterMineExplosionSound) +{ + filename = "fx/weapons/mine_detonate_UW.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = MineExplosionEffect; +}; + +//-------------------------------------------------------------------------- +// Mine Particle effects +//-------------------------------------------------------------------------- +datablock ParticleData(MineExplosionBubbleParticle) +{ + dragCoefficient = 0.0; + gravityCoefficient = -0.25; + inheritedVelFactor = 0.0; + constantAcceleration = 0.0; + lifetimeMS = 2000; + lifetimeVarianceMS = 750; + useInvAlpha = false; + textureName = "special/bubbles"; + + spinRandomMin = -100.0; + spinRandomMax = 100.0; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 0.4"; + colors[2] = "0.7 0.8 1.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 1.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.3; + times[2] = 1.0; +}; +datablock ParticleEmitterData(MineExplosionBubbleEmitter) +{ + ejectionPeriodMS = 7; + periodVarianceMS = 0; + ejectionVelocity = 1.0; + ejectionOffset = 2.0; + velocityVariance = 0.5; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "MineExplosionBubbleParticle"; +}; +datablock ParticleData( UnderwaterMineCrescentParticle ) +{ + dragCoefficient = 2; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 600; + lifetimeVarianceMS = 000; + textureName = "special/crescent3"; + colors[0] = "0.5 0.5 1.0 1.0"; + colors[1] = "0.5 0.5 1.0 1.0"; + colors[2] = "0.5 0.5 1.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 1.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( UnderwaterMineCrescentEmitter ) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 200; + particles = "UnderwaterMineCrescentParticle"; +}; + +datablock ParticleData(UnderwaterMineExplosionSmoke) +{ + dragCoeffiecient = 105.0; + gravityCoefficient = -0.0; + inheritedVelFactor = 0.025; + constantAcceleration = -1.0; + + lifetimeMS = 1200; + lifetimeVarianceMS = 00; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "0.7 0.7 1.0 1.0"; + colors[1] = "0.3 0.3 1.0 1.0"; + colors[2] = "0.0 0.0 1.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 3.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(UnderwaterMineExplosionSmokeEmitter) +{ + ejectionPeriodMS = 8; + periodVarianceMS = 0; + + ejectionVelocity = 4.25; + velocityVariance = 1.25; + + thetaMin = 0.0; + thetaMax = 80.0; + + lifetimeMS = 250; + + particles = "UnderwaterMineExplosionSmoke"; +}; + +datablock ExplosionData(UnderwaterMineExplosion) +{ + explosionShape = "disc_explosion.dts"; + playSpeed = 1.0; + sizes[0] = "0.4 0.4 0.4"; + sizes[1] = "0.4 0.4 0.4"; + soundProfile = UnderwaterMineExplosionSound; + faceViewer = true; + + emitter[0] = UnderwaterMineExplosionSmokeEmitter; + emitter[1] = UnderwaterMineCrescentEmitter; + emitter[2] = MineExplosionBubbleEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 7.0 9.0"; + camShakeAmp = "50.0 50.0 50.0"; + camShakeDuration = 1.0; + camShakeRadius = 10.0; +}; + +//-------------------------------------------------------------------------- +// Mine Particle effects +//-------------------------------------------------------------------------- +datablock ParticleData( MineCrescentParticle ) +{ + dragCoefficient = 2; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = -0.0; + lifetimeMS = 600; + lifetimeVarianceMS = 000; + textureName = "special/crescent3"; + colors[0] = "1.0 0.8 0.2 1.0"; + colors[1] = "1.0 0.4 0.2 1.0"; + colors[2] = "1.0 0.0 0.0 0.0"; + sizes[0] = 0.5; + sizes[1] = 1.0; + sizes[2] = 2.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MineCrescentEmitter ) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 0; + ejectionVelocity = 10; + velocityVariance = 5.0; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 200; + particles = "MineCrescentParticle"; +}; + +datablock ParticleData(MineExplosionSmoke) +{ + dragCoeffiecient = 105.0; + gravityCoefficient = -0.0; + inheritedVelFactor = 0.025; + + lifetimeMS = 1200; + lifetimeVarianceMS = 00; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + textureName = "special/Smoke/smoke_001"; + + colors[0] = "1.0 0.7 0.0 1.0"; + colors[1] = "0.2 0.2 0.2 1.0"; + colors[2] = "0.0 0.0 0.0 0.0"; + sizes[0] = 1.0; + sizes[1] = 3.0; + sizes[2] = 1.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(MineExplosionSmokeEmitter) +{ + ejectionPeriodMS = 8; + periodVarianceMS = 0; + + ejectionVelocity = 4.25; + velocityVariance = 1.25; + + thetaMin = 0.0; + thetaMax = 80.0; + + lifetimeMS = 250; + + particles = "MineExplosionSmoke"; +}; + + + +datablock ExplosionData(MineExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + playSpeed = 1.0; + sizes[0] = "0.5 0.5 0.5"; + sizes[1] = "0.5 0.5 0.5"; + soundProfile = MineExplosionSound; + faceViewer = true; + + emitter[0] = MineExplosionSmokeEmitter; + emitter[1] = MineCrescentEmitter; + + shakeCamera = true; + camShakeFreq = "8.0 7.0 9.0"; + camShakeAmp = "50.0 50.0 50.0"; + camShakeDuration = 1.0; + camShakeRadius = 10.0; +}; + +// ---------------------------------------------- +// Item datablocks +// ---------------------------------------------- + +datablock ItemData(MineDeployed) +{ + className = Weapon; + shapeFile = "mine.dts"; + mass = 1.0; + elasticity = 0.0; + friction = 0.8; + pickupRadius = 3; + maxDamage = 0.01; + explosion = MineExplosion; + underwaterExplosion = UnderwaterMineExplosion; + indirectDamage = 1.3; + damageRadius = 6.0; + radiusDamageType = $DamageType::Mine; + kickBackStrength = 4000; + aiAvoidThis = false; + dynamicType = $TypeMasks::DamagableItemObjectType; + spacing = 8.0; + proximity = 3; + armTime = 5000; + maxDepCount = 5; + + computeCRC = true; + +}; + +datablock ItemData(Mine) +{ + className = HandInventory; + catagory = "Handheld"; + shapeFile = "ammo_mine.dts"; + mass = 1.0; + elasticity = 0.2; + friction = 0.7; + pickupRadius = 2; + + thrownItem = MineDeployed; + pickUpName = "some mines"; + + computeCRC = true; +}; \ No newline at end of file diff --git a/Scripts/Weapons/missileLauncher.cs b/Scripts/Weapons/missileLauncher.cs new file mode 100644 index 0000000..b620ce2 --- /dev/null +++ b/Scripts/Weapons/missileLauncher.cs @@ -0,0 +1,632 @@ +//-------------------------------------- +// Missile launcher +//-------------------------------------- + +//-------------------------------------------------------------------------- +// Force-Feedback Effects +//-------------------------------------- +datablock EffectProfile(MissileSwitchEffect) +{ + effectname = "weapons/missile_launcher_activate"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(MissileFireEffect) +{ + effectname = "weapons/missile_fire"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(MissileDryFireEffect) +{ + effectname = "weapons/missile_launcher_dryfire"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock EffectProfile(MissileExplosionEffect) +{ + effectname = "explosions/explosion.xpl23"; + minDistance = 10; + maxDistance = 30; +}; + +//-------------------------------------------------------------------------- +// Sounds +//-------------------------------------- +datablock AudioProfile(MissileSwitchSound) +{ + filename = "fx/weapons/missile_launcher_activate.wav"; + description = AudioClosest3d; + preload = true; + effect = MissileSwitchEffect; +}; + +datablock AudioProfile(MissileFireSound) +{ + filename = "fx/weapons/missile_fire.WAV"; + description = AudioDefault3d; + preload = true; + effect = MissileFireEffect; +}; + +datablock AudioProfile(MissileProjectileSound) +{ + filename = "fx/weapons/missile_projectile.wav"; + description = ProjectileLooping3d; + preload = true; +}; + +datablock AudioProfile(MissileReloadSound) +{ + filename = "fx/weapons/weapon.missilereload.wav"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(MissileLockSound) +{ + filename = "fx/weapons/missile_launcher_searching.WAV"; + description = AudioClosest3d; + preload = true; +}; + +datablock AudioProfile(MissileExplosionSound) +{ + filename = "fx/explosions/explosion.xpl23.wav"; + description = AudioBIGExplosion3d; + preload = true; + effect = MissileExplosionEffect; +}; + +datablock AudioProfile(MissileDryFireSound) +{ + filename = "fx/weapons/missile_launcher_dryfire.wav"; + description = AudioClose3d; + preload = true; + effect = MissileDryFireEffect; +}; + + +//---------------------------------------------------------------------------- +// Splash Debris +//---------------------------------------------------------------------------- +datablock ParticleData( MDebrisSmokeParticle ) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = 0.10; + inheritedVelFactor = 0.1; + + lifetimeMS = 1000; + lifetimeVarianceMS = 100; + + textureName = "particleTest"; + +// useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + 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.0; + sizes[1] = 0.8; + sizes[2] = 0.8; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MDebrisSmokeEmitter ) +{ + ejectionPeriodMS = 10; + periodVarianceMS = 1; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "MDebrisSmokeParticle"; +}; + + +datablock DebrisData( MissileSplashDebris ) +{ + emitters[0] = MDebrisSmokeEmitter; + + explodeOnMaxBounce = true; + + elasticity = 0.4; + friction = 0.2; + + lifetime = 0.3; + lifetimeVariance = 0.1; + + numBounces = 1; +}; + + +//---------------------------------------------------------------------------- +// Missile smoke spike (for debris) +//---------------------------------------------------------------------------- +datablock ParticleData( MissileSmokeSpike ) +{ + dragCoeffiecient = 1.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + + lifetimeMS = 1500; + lifetimeVarianceMS = 250; + + textureName = "particleTest"; + + useInvAlpha = true; + + spinRandomMin = -60.0; + spinRandomMax = 60.0; + + colors[0] = "0.6 0.6 0.6 1.0"; + colors[1] = "0.4 0.4 0.4 0.5"; + colors[2] = "0.4 0.4 0.4 0.0"; + sizes[0] = 0.0; + sizes[1] = 1.0; + sizes[2] = 1.5; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData( MissileSmokeSpikeEmitter ) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 1; + + ejectionVelocity = 1.0; // A little oomph at the back end + velocityVariance = 0.2; + + thetaMin = 0.0; + thetaMax = 40.0; + + particles = "MissileSmokeSpike"; +}; + + +//---------------------------------------------------------------------------- +// Explosion smoke particles +//---------------------------------------------------------------------------- + +datablock ParticleData(MissileExplosionSmoke) +{ + dragCoeffiecient = 0.3; + gravityCoefficient = -0.2; + inheritedVelFactor = 0.025; + + lifetimeMS = 2250; + lifetimeVarianceMS = 0; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -100.0; + spinRandomMax = 100.0; + +// textureName = "special/Smoke/bigSmoke"; + + colors[0] = "0.6 0.6 0.6 0.9"; + colors[1] = "0.5 0.5 0.5 0.4"; + colors[2] = "0.4 0.4 0.4 0.0"; + sizes[0] = 1.0; + sizes[1] = 2.5; + sizes[2] = 3.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(MissileExplosionSmokeEMitter) +{ + ejectionOffset = 1.5; + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 2.5; + velocityVariance = 0.5; + + thetaMin = 0.0; + thetaMax = 180.0; + + lifetimeMS = 500; + + particles = "MissileExplosionSmoke"; +}; + + + +datablock DebrisData( MissileSpikeDebris ) +{ + emitters[0] = MissileSmokeSpikeEmitter; + explodeOnMaxBounce = true; + elasticity = 0.4; + friction = 0.2; + lifetime = 3.0; + lifetimeVariance = 0.5; +}; + + +//--------------------------------------------------------------------------- +// Explosions +//--------------------------------------------------------------------------- +datablock ExplosionData(MissileExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + playSpeed = 0.8; + soundProfile = MissileExplosionSound; + faceViewer = true; + + sizes[0] = "1.5 1.5 1.5"; + sizes[1] = "1.5 1.5 1.5"; + sizes[2] = "1.5 1.5 1.5"; + + emitter[0] = MissileExplosionSmokeEmitter; + + debris = MissileSpikeDebris; + debrisThetaMin = 1; + debrisThetaMax = 179; + debrisNum = 9; + debrisNumVariance = 3; + debrisVelocity = 15.0; + debrisVelocityVariance = 2.0; + + shakeCamera = true; + camShakeFreq = "6.0 7.0 7.0"; + camShakeAmp = "70.0 70.0 70.0"; + camShakeDuration = 1.0; + camShakeRadius = 13.0; +}; + + + +datablock ExplosionData(MissileSplashExplosion) +{ + explosionShape = "disc_explosion.dts"; + + faceViewer = true; + explosionScale = "1.0 1.0 1.0"; + + debris = MissileSplashDebris; + debrisThetaMin = 10; + debrisThetaMax = 80; + debrisNum = 10; + debrisVelocity = 10.0; + debrisVelocityVariance = 4.0; + + sizes[0] = "0.35 0.35 0.35"; + sizes[1] = "0.15 0.15 0.15"; + sizes[2] = "0.15 0.15 0.15"; + sizes[3] = "0.15 0.15 0.15"; + + times[0] = 0.0; + times[1] = 0.333; + times[2] = 0.666; + times[3] = 1.0; + +}; + + +//-------------------------------------------------------------------------- +// Splash +//-------------------------------------------------------------------------- +datablock ParticleData(MissileMist) +{ + 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(MissileMistEmitter) +{ + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 6.0; + velocityVariance = 4.0; + ejectionOffset = 0.0; + thetaMin = 85; + thetaMax = 85; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + lifetimeMS = 250; + particles = "MissileMist"; +}; + + + +datablock ParticleData( MissileSplashParticle ) +{ + 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( MissileSplashEmitter ) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + ejectionVelocity = 6; + velocityVariance = 3.0; + ejectionOffset = 0.0; + thetaMin = 60; + thetaMax = 80; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + lifetimeMS = 100; + particles = "MissileSplashParticle"; +}; + + +datablock SplashData(MissileSplash) +{ + numSegments = 15; + ejectionFreq = 0.0001; + ejectionAngle = 45; + ringLifetime = 0.5; + lifetimeMS = 400; + velocity = 5.0; + startRadius = 0.0; + acceleration = -3.0; + texWrap = 5.0; + + explosion = MissileSplashExplosion; + + texture = "special/water2"; + + emitter[0] = MissileSplashEmitter; + emitter[1] = MissileMistEmitter; + + colors[0] = "0.7 0.8 1.0 0.0"; + colors[1] = "0.7 0.8 1.0 1.0"; + colors[2] = "0.7 0.8 1.0 0.0"; + 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; +}; + +//-------------------------------------------------------------------------- +// Particle effects +//-------------------------------------- +datablock ParticleData(MissileSmokeParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.02; + inheritedVelFactor = 0.0; + + lifetimeMS = 4000; + lifetimeVarianceMS = 500; + + textureName = "special/Smoke/bigSmoke"; + useInvAlpha = true; + spinRandomMin = -90.0; + spinRandomMax = 90.0; + + colors[0] = "0.6 0.6 0.6 1.0"; + colors[1] = "0.5 0.5 0.5 0.6"; + colors[2] = "0.4 0.4 0.4 0.0"; + sizes[0] = 1; + sizes[1] = 1.75; + sizes[2] = 3; + times[0] = 0.0; + times[1] = 0.1; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(MissileSmokeEmitter) +{ + ejectionPeriodMS = 1; + periodVarianceMS = 0; + + ejectionVelocity = 0.5; + velocityVariance = 0.5; + + thetaMin = 0.0; + thetaMax = 50.0; + + particles = "MissileSmokeParticle"; +}; + + +datablock ParticleData(MissileFireParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 1.0; + + lifetimeMS = 300; + lifetimeVarianceMS = 000; + + textureName = "particleTest"; + + spinRandomMin = -135; + spinRandomMax = 135; + + colors[0] = "1 0.18 0.03 0.4"; + colors[1] = "1 0.18 0.03 0.3"; + colors[2] = "1 0.18 0.03 0.0"; + sizes[0] = 0; + sizes[1] = 1; + sizes[2] = 1.5; + times[0] = 0.0; + times[1] = 0.3; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(MissileFireEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + + ejectionVelocity = 15.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 0.0; + + particles = "MissileFireParticle"; +}; + + + +datablock ParticleData(MissilePuffParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + useInvAlpha = true; + lifetimeMS = 4000; + lifetimeVarianceMS = 300; + + textureName = "particleTest"; + + spinRandomMin = -135; + spinRandomMax = 135; + + colors[0] = "0.6 0.6 0.6 0.15"; + colors[1] = "0.4 0.4 0.4 0.0"; + sizes[0] = 0.0; + sizes[1] = 0.5; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ParticleEmitterData(MissilePuffEmitter) +{ + ejectionPeriodMS = 2; + periodVarianceMS = 0; + + ejectionVelocity = 0.5; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 90.0; + + particles = "MissilePuffParticle"; +}; + + +datablock ParticleData(MissileLauncherExhaustParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = 0.01; + inheritedVelFactor = 1.0; + + lifetimeMS = 500; + lifetimeVarianceMS = 300; + + textureName = "particleTest"; + + useInvAlpha = true; + spinRandomMin = -135; + spinRandomMax = 135; + + colors[0] = "1.0 1.0 1.0 0.5"; + colors[1] = "0.7 0.7 0.7 0.0"; + sizes[0] = 0.25; + sizes[1] = 1.0; + times[0] = 0.0; + times[1] = 1.0; +}; + +datablock ParticleEmitterData(MissileLauncherExhaustEmitter) +{ + ejectionPeriodMS = 15; + periodVarianceMS = 0; + + ejectionVelocity = 3.0; + velocityVariance = 0.0; + + thetaMin = 0.0; + thetaMax = 20.0; + + particles = "MissileLauncherExhaustParticle"; +}; + +//-------------------------------------------------------------------------- +// Debris +//-------------------------------------- +datablock DebrisData( FlechetteDebris ) +{ + shapeName = "weapon_missile_fleschette.dts"; + + lifetime = 5.0; + + minSpinSpeed = -320.0; + maxSpinSpeed = 320.0; + + elasticity = 0.2; + friction = 0.3; + + numBounces = 3; + + gravModifier = 0.40; + + staticOnMaxBounce = true; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(MissileLauncherAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_missile.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some missiles"; + + computeCRC = true; + +}; diff --git a/Scripts/Weapons/pistol.cs b/Scripts/Weapons/pistol.cs new file mode 100644 index 0000000..6fdf1d3 --- /dev/null +++ b/Scripts/Weapons/pistol.cs @@ -0,0 +1,196 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// M4 Grenade Launcher - Made by Blnukem +//------------------------------------------------------------------------------ +// Ammo Data +//------------------------------------------------------------------------------ + +datablock GrenadeProjectileData(M4_RPGrenade) +{ + projectileShapeName = "grenade_projectile.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.40; + damageRadius = 15.0; + radiusDamageType = $DamageType::Grenade; + kickBackStrength = 1500; + bubbleEmitTime = 1.0; + + sound = GrenadeProjectileSound; + explosion = "GrenadeExplosion"; + underwaterExplosion = "UnderwaterGrenadeExplosion"; + velInheritFactor = 0.5; + splash = GrenadeSplash; + + baseEmitter = GrenadeSmokeEmitter; + bubbleEmitter = GrenadeBubbleEmitter; + + grenadeElasticity = 0.35; + grenadeFriction = 0.2; + armingDelayMS = 1000; + muzzleVelocity = 47.00; + drag = 0.1; +}; + + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(M4Ammo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_grenade.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some grenade launcher ammo"; + + computeCRC = true; + emap = true; +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ItemData(M4) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_grenade_launcher.dts"; + image = M4Image; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a grenade launcher"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(M4Image) +{ + className = WeaponImage; + shapeFile = "weapon_grenade_launcher.dts"; + item = M4; + ammo = M4Ammo; + offset = "0 0 0"; + emap = true; + + projectile = M4_RPGrenade; + projectileType = GrenadeProjectile; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = GrenadeSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.4; + stateFire[3] = true; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateSequence[3] = "Fire"; + stateScript[3] = "onFire"; + stateSound[3] = GrenadeFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 0.5; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + stateSound[4] = GrenadeReloadSound; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = GrenadeDryFireSound; + stateTimeoutValue[6] = 1.5; + stateTransitionOnTimeout[6] = "NoAmmo"; +}; + +function M4Image::onMount(%this,%obj,%slot) +{ + Parent::onMount(%this, %obj, %slot); + %obj.mountImage(M4Revolver, 1); + + if (%obj.inv[M4Ammo] == 0) + %obj.M4checkclip = schedule(10, 0, "M4Checkforclip", %obj); +} + +function M4Image::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.unmountImage(1); + + if (%obj.clipReloading != false) + { + Cancel(%obj.M4ClipAdd); + %obj.clipReloading = false; + } +} + +function M4Image::onFire(%data,%obj,%slot) +{ + %obj.decInventory(%data.ammo,1); + + %vector = %obj.getMuzzleVector(%slot); + %mp = %obj.getMuzzlePoint(%slot); + + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + + %p = new (%data.projectileType)() + { + dataBlock = %data.projectile; + initialDirection = %newvector; + initialPosition = %mp; + sourceObject = %obj; + damageFactor = 1; + sourceSlot = %slot; + }; + + %obj.applyKick(-75); + + if (%obj.inv[M4Ammo] == 0) + %obj.M4checkclip = schedule(10, 0, "M4Checkforclip", %obj); +} + +function M4Checkforclip(%obj) +{ + if (%obj.inv[M4clip] > 0) + { + %obj.clipReloading = true; + %obj.unmountImage(1); + %obj.M4ClipAdd = schedule(2500, 0, "M4ReloadClip", %obj); + } +} + +function M4ReloadClip(%obj) +{ + %obj.clipReloading = false; + %obj.decInventory(MG42Clip, 1); + %obj.mountImage(M4Revolver, 1); + %obj.setInventory(M4Ammo, 5); +} diff --git a/Scripts/Weapons/snipergun.cs b/Scripts/Weapons/snipergun.cs new file mode 100644 index 0000000..07f81a7 --- /dev/null +++ b/Scripts/Weapons/snipergun.cs @@ -0,0 +1,235 @@ +//-------------------------------------- +// RPChaingun +//-------------------------------------- + +datablock AudioProfile(SniperFireSound) +{ + filename = "fx/vehicles/tank_mortar_fire.wav"; + description = AudioDefault3d; + preload = true; + effect = AssaultChaingunFireEffect; +}; + +//-------------------------------------------------------------------------- +// Projectile +//-------------------------------------- + +datablock TracerProjectileData(snipergunBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.6; // z0dd - ZOD, 9-27-02. Was 0.0825 + directDamageType = $DamageType::Sniper; + explosion = "ChaingunExplosion"; + splash = ChaingunSplash; + sniperrifleHeadMultiplier = 10.0; + closeRangeMultiplier = 0.5; + + kickBackStrength = 50.0; + sound = ChaingunProjectile; + + //dryVelocity = 425.0; + dryVelocity = 5000.0; // z0dd - ZOD, 8-12-02. Was 425.0 + wetVelocity = 4500.0; + velInheritFactor = 0.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 30.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.10; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; +}; + +//-------------------------------------------------------------------------- +// Ammo +//-------------------------------------- + +datablock ItemData(snipergunAmmo) +{ + className = Ammo; + catagory = "Ammo"; + shapeFile = "ammo_chaingun.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "some sniperrifle ammo"; + + computeCRC = true; + +}; + +//-------------------------------------------------------------------------- +// Weapon +//-------------------------------------- +datablock ShapeBaseImageData(snipergunImage) +{ + className = WeaponImage; + shapeFile = "weapon_sniper.dts"; + item = snipergun; + ammo = snipergunAmmo; + projectile = snipergunBullet; + projectileType = TracerProjectile; + emap = true; + armThread = looksn; + mass = 10; + + casing = ShellDebris; + shellExitDir = "1.0 0.3 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 3.0; + + maxSpread = 10.0 / 1000.0; + + stateName[0] = "Activate"; + stateTransitionOnTimeout[0] = "ActivateReady"; + stateTimeoutValue[0] = 0.5; + stateSequence[0] = "Activate"; + stateSound[0] = BasicSwitchSound; + + stateName[1] = "ActivateReady"; + stateTransitionOnLoaded[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "CheckWet"; + + stateName[3] = "Fire"; + stateTransitionOnTimeout[3] = "Reload"; + stateTimeoutValue[3] = 0.0001; + stateFire[3] = true; + stateEmitter[3] = "GunFireEffectEmitter"; + stateEmitterNode[3] = "muzzlepoint1"; + stateEmitterTime[3] = 1; + stateRecoil[3] = LightRecoil; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateEmitterTime[3] = 0.2; + stateSound[3] = SniperFireSound; + + stateName[4] = "Reload"; + stateTransitionOnNoAmmo[4] = "NoAmmo"; + stateTransitionOnTimeout[4] = "Ready"; + stateTimeoutValue[4] = 2.0; + stateAllowImageChange[4] = false; + stateSequence[4] = "Reload"; + + stateName[5] = "NoAmmo"; + stateTransitionOnAmmo[5] = "Reload"; + stateSequence[5] = "NoAmmo"; + stateTransitionOnTriggerDown[5] = "DryFire"; + + stateName[6] = "DryFire"; + stateSound[6] = ChaingunDryFireSound; + stateTimeoutValue[6] = 1.0; + stateTransitionOnTimeout[6] = "NoAmmo"; + + stateName[7] = "WetFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 1.0; + stateTransitionOnTimeout[7] = "Ready"; + + stateName[8] = "CheckWet"; + stateTransitionOnWet[8] = "WetFire"; + stateTransitionOnNotWet[8] = "Fire"; +}; + +datablock ItemData(snipergun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_chaingun.dts"; + image = snipergunImage; + mass = 1.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a snipergun"; + + computeCRC = true; + emap = true; +}; + +function snipergunBullet::onCollision(%data, %projectile, %targetObject, %modifier, %position, %normal) +{ + // extra damage for head shot or less for close range shots + if(!(%targetObject.getType() & ($TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType)) && + (%targetObject.getDataBlock().getClassName() $= "PlayerData")) + { + %damLoc = firstWord(%targetObject.getDamageLocation(%position)); + %dist = vectorDist(%position, %projectile.sourceObject.getPosition()); + if(%damLoc $= "head") + { + %targetObject.getOwnerClient().headShot = 1; + %modifier *= %data.sniperrifleHeadMultiplier; + } + else if(%damLoc $= "legs") + { + %modifier *= 0.2; + } + else if (%dist <= 125) + { + %modifier *= %data.closeRangeMultiplier; + } + else + { + %modifier = 1; + %targetObject.getOwnerClient().headShot = 0; + } + } + + %targetObject.damage(%projectile.sourceObject, %position, %modifier * %data.directDamage, %data.directDamageType); +} + +function snipergunImage::onFire(%data, %obj, %slot){ + %data.lightStart = getSimTime(); + + %spd = vectorLen(%obj.getVelocity()); + %spread = %data.maxSpread * (%spd / %obj.getDatablock().maxForwardSpeed); + if(%spread > %data.maxspread) + %spread = %data.maxspread; + %vec = %obj.getMuzzleVector(%slot); + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + %initialPos = %obj.getMuzzlePoint(%slot); + + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + }; + + %obj.lastProjectile = %p; + %obj.deleteLastProjectile = %data.deleteLastProjectile; + MissionCleanup.add(%p); + + if(%obj.client) + %obj.client.projectile = %p; + %obj.decInventory(%data.ammo,1); +} diff --git a/Scripts/Weapons/superChaingun.cs b/Scripts/Weapons/superChaingun.cs new file mode 100644 index 0000000..7e0367d --- /dev/null +++ b/Scripts/Weapons/superChaingun.cs @@ -0,0 +1,292 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Super Chaingun - Modified by Blnukem. +// Modifications: Re-added pulses. +//------------------------------------------------------------------------------ +// Projectile Data +//------------------------------------------------------------------------------ + +datablock TracerProjectileData(SuperChaingunBullet) +{ + doDynamicClientHits = true; + + directDamage = 0.5 * 10; + directDamageType = $DamageType::SuperChaingun; + explosion = ChaingunExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.2 * 5; + damageRadius = 10.0; + radiusDamageType = $DamageType::SuperChaingun; + + kickBackStrength = 1750; + sound = ChaingunProjectile; + + dryVelocity = 800.0; + wetVelocity = 100.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 3000; + explodeOnDeath = false; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 3000; + + tracerLength = 15.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.08; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +datablock TracerProjectileData(SCG_Bullet_exp) +{ + doDynamicClientHits = true; + + directDamage = 0.5 * 10; + directDamageType = $DamageType::SuperChaingun; + explosion = PlasmaBoltExplosion; + splash = ChaingunSplash; + + hasDamageRadius = true; + indirectDamage = 0.2 * 5; + damageRadius = 10.0; + radiusDamageType = $DamageType::SuperChaingun; + + kickBackStrength = 150; + sound = ChaingunProjectile; + + dryVelocity = 0.0; + wetVelocity = 0.0; + velInheritFactor = 1.0; + fizzleTimeMS = 3000; + lifetimeMS = 10; + explodeOnDeath = true; + reflectOnWaterImpactAngle = 0.0; + explodeOnWaterImpact = false; + deflectionOnWaterImpact = 0.0; + fizzleUnderwaterMS = 1; + + tracerLength = 0.0; + tracerAlpha = false; + tracerMinPixels = 6; + tracerColor = 211.0/255.0 @ " " @ 215.0/255.0 @ " " @ 120.0/255.0 @ " 0.75"; + tracerTex[0] = "special/tracer00"; + tracerTex[1] = "special/tracercross"; + tracerWidth = 0.35; + crossSize = 0.20; + crossViewAng = 0.990; + renderCross = true; + + decalData[0] = ChaingunDecal1; + decalData[1] = ChaingunDecal2; + decalData[2] = ChaingunDecal3; + decalData[3] = ChaingunDecal4; + decalData[4] = ChaingunDecal5; + decalData[5] = ChaingunDecal6; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 0.175"; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Weapon Data +//------------------------------------------------------------------------------ + +datablock ShapeBaseImageData(SuperChaingunImage) +{ + className = WeaponImage; + shapeFile = "weapon_chaingun.dts"; + item = SuperChaingun; + + projectile = SuperChaingunBullet; + projectileType = TracerProjectile; + + emap = true; + + casing = ShellDebris; + shellExitDir = "0.3 0.5 1.0"; + shellExitOffset = "0.15 -0.56 -0.1"; + shellExitVariance = 15.0; + shellVelocity = 4.0; + + usesEnergy = true; + fireEnergy = 0.0; + minEnergy = 5.0; + + projectileSpread = 2.0 / 1000.0; + + //-------------------------------------- + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = ChaingunSwitchSound; + stateAllowImageChange[0] = false; + // + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "Ready"; + stateTransitionOnNoAmmo[0] = "NoAmmo"; + + //-------------------------------------- + stateName[1] = "Ready"; + stateSpinThread[1] = Stop; + // + stateTransitionOnTriggerDown[1] = "Spinup"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + //-------------------------------------- + stateName[2] = "NoAmmo"; + stateTransitionOnAmmo[2] = "Ready"; + stateSpinThread[2] = Stop; + stateTransitionOnTriggerDown[2] = "DryFire"; + + //-------------------------------------- + stateName[3] = "Spinup"; + stateSpinThread[3] = SpinUp; + stateSound[3] = ChaingunSpinUpSound; + // + stateTimeoutValue[3] = 0.5; + stateWaitForTimeout[3] = false; + stateTransitionOnTimeout[3] = "Fire"; + stateTransitionOnTriggerUp[3] = "Spindown"; + + //-------------------------------------- + stateName[4] = "Fire"; + stateSequence[4] = "Fire"; + stateSequenceRandomFlash[4] = true; + stateSpinThread[4] = FullSpeed; + stateSound[4] = ChaingunFireSound; + //stateRecoil[4] = LightRecoil; + stateAllowImageChange[4] = false; + stateScript[4] = "onFire"; + stateFire[4] = true; + stateEjectShell[4] = true; + // + stateTimeoutValue[4] = 0.05; + stateTransitionOnTimeout[4] = "Fire"; + stateTransitionOnTriggerUp[4] = "Spindown"; + stateTransitionOnNoAmmo[4] = "EmptySpindown"; + + //-------------------------------------- + stateName[5] = "Spindown"; + stateSound[5] = ChaingunSpinDownSound; + stateSpinThread[5] = SpinDown; + // + stateTimeoutValue[5] = 1.0; + stateWaitForTimeout[5] = true; + stateTransitionOnTimeout[5] = "Ready"; + stateTransitionOnTriggerDown[5] = "Spinup"; + + //-------------------------------------- + stateName[6] = "EmptySpindown"; + stateSound[6] = ChaingunSpinDownSound; + stateSpinThread[6] = SpinDown; + // + stateTimeoutValue[6] = 0.5; + stateTransitionOnTimeout[6] = "NoAmmo"; + + //-------------------------------------- + stateName[7] = "DryFire"; + stateSound[7] = ChaingunDryFireSound; + stateTimeoutValue[7] = 0.5; + stateTransitionOnTimeout[7] = "NoAmmo"; +}; + +datablock ItemData(SuperChaingun) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_chaingun.dts"; + image = SuperChaingunImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a super chaingun"; + + computeCRC = true; + emap = true; +}; + +function SuperChaingunImage::onFire(%data,%obj,%slot) { +if (%obj.superChaingunMode == 1) { + %pos = %obj.getMuzzlePoint(%slot); + %vec = %obj.getMuzzleVector(%slot); + %res = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%vec,2000)), $TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType,%obj); + if (%res) + %hitLoc = getWords(%res,1,3); + else + %hitLoc = vectorAdd(%pos,vectorScale(%vec,2000)); + %p = discharge(%pos,%vec); + %p.setEnergyPercentage(1); + createLifeLight(%hitLoc,1,1000); + addToShock(%p); + %p.schedule(1000,"delete"); + zap(0,25,%hitLoc); + } + else if (%obj.superChaingunMode > 1) + { + if (!(%obj.lasteffectpulse+2000 < getSimTime())) + return; + %obj.lasteffectpulse = GetSimTime(); + %pos = %obj.getMuzzlePoint(%slot); + %vec = %obj.getMuzzleVector(%slot); + %res = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%vec,2000)), $TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType| $TypeMasks::ForceFieldObjectType ,%obj); + if (%res) + Aidpulse(getWords(%res,1,3),%obj.client,%obj.superChaingunMode-2); + + } + else + %p = Parent::onFire(%data, %obj, %slot); +} + +function SuperChaingunImage::onMount(%this,%obj,%slot) { + %obj.usingSuperChaingun = true; + if (!%obj.superChaingunMode) + %obj.superChaingunMode = 0; + if (!%obj.superChaingunMode2) + %obj.superChaingunMode2 = 0; + displaySCGStatus(%obj); + WeaponImage::onMount(%this,%obj,%slot); +} + +function SuperChaingunImage::onUnmount(%data, %obj, %slot) { + %obj.usingSuperChaingun = false; + WeaponImage::onUnmount(%data, %obj, %slot); +} + +function displaySCGStatus(%obj) { + if (%obj.superChaingunMode == 1) + bottomPrint(%obj.client,"SCG Ion Progression " @ ($Ion::StopIon ? "disabled" : "enabled"),2,1); + else if (%obj.superChaingunMode == 2) + bottomPrint(%obj.client,"SCG Repair Pulse",2,1); + else if (%obj.superChaingunMode == 3) + bottomPrint(%obj.client,"SCG Cloak Pulse",2,1); + else if (%obj.superChaingunMode == 4) + bottomPrint(%obj.client,"SCG Disasemble Pulse",2,1); + else if (%obj.superChaingunMode == 5) + bottomPrint(%obj.client,"SCG Electro Static Pulse",2,1); + else if (%obj.superChaingunMode == 6) + bottomPrint(%obj.client,"SCG Morphing Pulse",2,1); + else + bottomPrint(%obj.client,"SCG Normal",2,1); +} diff --git a/Scripts/Weapons/targetingLaser.cs b/Scripts/Weapons/targetingLaser.cs new file mode 100644 index 0000000..f1f08ab --- /dev/null +++ b/Scripts/Weapons/targetingLaser.cs @@ -0,0 +1,121 @@ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Targeting laser - Modified by Blnukem. +// Modifications: Saves datablock space. +// The file is also much cleaner and easier to read, better for more editing. +//------------------------------------------------------------------------------ +// Sounds +//------------------------------------------------------------------------------ + +datablock EffectProfile(TargetingLaserPaintEffect) +{ + effectname = "weapons/targetinglaser_paint"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(TargetingLaserPaintSound) +{ + filename = "fx/weapons/targetinglaser_paint.wav"; + description = CloseLooping3d; + preload = true; + effect = TargetingLaserPaintEffect; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Projectile +//------------------------------------------------------------------------------ + +datablock TargetProjectileData(BasicTargeter) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + + maxRifleRange = 1000; + beamColor = "1.0 0.0 0.0"; + + startBeamWidth = 0.02; + pulseBeamWidth = 0.025; + beamFlareAngle = 3.0; + minFlareSize = 0.0; + maxFlareSize = 400.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + textureName[0] = "special/nonlingradient"; + textureName[1] = "special/flare"; + textureName[2] = "special/pulse"; + textureName[3] = "special/expFlare"; + beacon = false; +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Weapon Data +//------------------------------------------------------------------------------ + +datablock ItemData(TargetingLaser) +{ + className = Weapon; + catagory = "Spawn Items"; + shapeFile = "weapon_targeting.dts"; + image = TargetingLaserImage; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + pickUpName = "a laser targeter"; + + computeCRC = true; + +}; + +datablock ShapeBaseImageData(TargetingLaserImage) +{ + className = WeaponImage; + + shapeFile = "weapon_targeting.dts"; + item = TargetingLaser; + offset = "0 0 0"; + + projectile = BasicTargeter; + projectileType = TargetProjectile; + deleteLastProjectile = true; + + usesEnergy = true; + minEnergy = 3; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = GenericSwitchSound; + stateTimeoutValue[0] = 0.5; + stateTransitionOnTimeout[0] = "ActivateReady"; + + stateName[1] = "ActivateReady"; + stateTransitionOnAmmo[1] = "Ready"; + stateTransitionOnNoAmmo[1] = "NoAmmo"; + + stateName[2] = "Ready"; + stateTransitionOnNoAmmo[2] = "NoAmmo"; + stateTransitionOnTriggerDown[2] = "Fire"; + + stateName[3] = "Fire"; + stateEnergyDrain[3] = 3; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateTransitionOnTriggerUp[3] = "Deconstruction"; + stateTransitionOnNoAmmo[3] = "Deconstruction"; + stateSound[3] = TargetingLaserPaintSound; + + stateName[4] = "NoAmmo"; + stateTransitionOnAmmo[4] = "Ready"; + + stateName[5] = "Deconstruction"; + stateScript[5] = "deconstruct"; + stateTransitionOnTimeout[5] = "Ready"; +}; diff --git a/Scripts/admin.cs b/Scripts/admin.cs new file mode 100644 index 0000000..f2ce2c6 --- /dev/null +++ b/Scripts/admin.cs @@ -0,0 +1,889 @@ +// 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"; +$VoteMessage["VotePurebuild", 0] = "enable pure building"; +$VoteMessage["VotePurebuild", 1] = "disable pure building"; +$VoteMessage["VoteCascadeMode", 0] = "enable cascade mode"; +$VoteMessage["VoteCascadeMode", 1] = "disable cascade mode"; +$VoteMessage["VoteExpertMode", 0] = "enable expert mode"; +$VoteMessage["VoteExpertMode", 1] = "disable expert mode"; +$VoteMessage["VoteVehicles", 0] = "enable vehicles"; +$VoteMessage["VoteVehicles", 1] = "disable vehicles"; +$VoteMessage["VoteSatchelCharge", 0] = "enable satchel charges"; +$VoteMessage["VoteSatchelCharge", 1] = "disable satchel charges"; +$VoteMessage["VoteOnlyOwnerDeconstruct", 0] = "enable only owner deconstruct"; +$VoteMessage["VoteOnlyOwnerDeconstruct", 1] = "disable only owner deconstruct"; +$VoteMessage["VoteOnlyOwnerCascade", 0] = "enable only owner cascade"; +$VoteMessage["VoteOnlyOwnerCascade", 1] = "disable only owner cascade"; +$VoteMessage["VoteOnlyOwnerRotate", 0] = "enable only owner rotate"; +$VoteMessage["VoteOnlyOwnerRotate", 1] = "disable only owner rotate"; +$VoteMessage["VoteOnlyOwnerCubicReplace", 0] = "enable only owner cubic-replace"; +$VoteMessage["VoteOnlyOwnerCubicReplace", 1] = "disable only owner cubic-replace"; +$VoteMessage["VoteInvincibleArmors", 0] = "enable invincible armors"; +$VoteMessage["VoteInvincibleArmors", 1] = "disable invincible armors"; +$VoteMessage["VoteInvincibleDeployables", 0] = "enable invincible deployables"; +$VoteMessage["VoteInvincibleDeployables", 1] = "disable invincible deployables"; +$VoteMessage["VoteUndergroundMode", 0] = "enable underground mode"; +$VoteMessage["VoteUndergroundMode", 1] = "disable underground mode"; +$VoteMessage["VoteHazardMode", 0] = "enable hazard mode"; +$VoteMessage["VoteHazardMode", 1] = "disable hazard mode"; +$VoteMessage["VoteRemoveDeployables"] = "remove all deployables in mission"; +$VoteMessage["VoteGlobalPowerCheck"] = "remove all duplicate deployables"; +$VoteMessage["VoteRemoveDupDeployables"] = "remove all duplicate deployables"; +$VoteMessage["VoteRemoveNonPoweredDeployables"] = "remove all deployables without power"; +$VoteMessage["VoteRemoveOrphanedDeployables"] = "remove all orphaned deployables"; +$VoteMessage["VotePrison", 0] = "enable prison"; +$VoteMessage["VotePrison", 1] = "disable prison"; +$VoteMessage["VotePrisonKilling", 0] = "enable jailing killers"; +$VoteMessage["VotePrisonKilling", 1] = "disable jailing killers"; +$VoteMessage["VotePrisonTeamKilling", 0] = "enable jailing team killers"; +$VoteMessage["VotePrisonTeamKilling", 1] = "disable jailing team killers"; +$VoteMessage["VotePrisonDeploySpam", 0] = "enable jailing deploy spammers"; +$VoteMessage["VotePrisonDeploySpam", 1] = "disable jailing deploy spammers"; +$VoteMessage["VoteMakeKeeper"] = "give Zombie Keeper abilities to"; +$VoteMessage["VoteMakeNotKeeper"] = "remove Zombie Keeper abilities from"; +$VoteMessage["VoteLoadBuilding"] = "load his"; +$VoteMessage["CancelVote"] = "cancel vote"; +$VoteMessage["PassVote"] = "pass vote"; +// End JTL + +function serverCmdStartNewVote(%client, %typeName, %arg1, %arg2, %arg3, %arg4, %playerVote) +{ + //DEMO VERSION - only voteKickPlayer is allowed + if ((isDemo()) && %typeName !$= "VoteKickPlayer") + { + messageClient(%client, '', "All voting options except to kick a player are disabled in the DEMO VERSION."); + return; + } + + if($Host::ClientSaving) + { + if(%typeName $= "StartBuildingManager") + { + %client.BuildingManagerMode = 1; + return; + } + if(%typeName $= "SavePieces") + { + %client.BuildingManagerMode = 2; + %client.BuildingManagerSubMode = 1; + return; + } + else if(%typeName $= "LoadPieces") + { + %client.BuildingManagerMode = 2; + %client.BuildingManagerSubMode = 2; + return; + } + else if(%typeName $= "RenamePieces") + { + %client.BuildingManagerMode = 2; + %client.BuildingManagerSubMode = 3; + return; + } + else if(%typeName $= "DeletePieces") + { + %client.BuildingManagerMode = 2; + %client.BuildingManagerSubMode = 4; + return; + } + else if(%typeName $= "GoBackPiece") + { + %client.BuildingManagerMode--; + %client.BuildingManagerSubMode = 0; + return; + } + else if(strstr(%typeName, "BSel-") == 0) + { + if(%client.isRenaming) + { + messageClient(%sender, "", "\c2You must finish renaming before continuing. ~wfx/misc/misc.error.wav"); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + if(%client.BuildingManagerSubMode == 1) // Saving. + { + if(%client.saveTimeout > 1) + { + messageClient(%client, "", "\c2Cannot save. You must wait\c3 "@%client.saveTimeout@" \c2more seconds before you are able to save again."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + %count = 0; + %path = "Buildings/ClientSaves/"@%client.guid@"/"; + %dummy = "Buildings/ClientSaves/"@%client.guid@"/*"; + for(%i = findFirstFile(%dummy); %i !$= ""; %i = findNextFile(%dummy)) + { + %count++; + } + + if(%count > $Host::MaxClientSaves) + { + messageClient(%client, "", "\c2Too many saves. You must either delete a few saves or overwrite them."); + return; + } + + %name = getsubstr(%typeName, 5, 255); + if(%name $= "(empty)") + %name = "ClientSave"@(%count+1)@".cs"; + + %grp = nameToID("MissionCleanup/Deployables"); + if(!isObject(%grp)) + { + messageClient(%client, "", "\c2There is nothing to save."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + %buildingcount = 0; + for(%g = 0; %g < %grp.getCount(); %g++) + { + %d = %grp.getObject(%g); + if(%d.getOwner() == %client) + %buildingcount++; + } + + if(%buildingcount == 0) + { + messageClient(%client, "", "\c2There is nothing to save."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + messageAll('MsgAdminForce', "\c3"@%client.nameBase@" \c2is saving "@(%client.sex $= "Male" ? "his" : "her")@" pieces."); + %obj = new FileObject(); + %obj.openforwrite(%path @ %name); + %obj.writeLine("// Saved by \"" @ getField(%client.nameBase, 0) @ "\""); + %obj.writeLine("// Created in mission \"" @ $MissionName @ "\""); + %obj.writeLine("// Construction " @ $ModVersion); + %obj.writeLine("// Piece Count for this save is "@%buildingcount@" pieces"); + %obj.writeLine(""); + + for(%x = 0; %x < %grp.getCount(); %x++) + { + %deployable = %grp.getObject(%x); + if(%deployable.getOwner() == %client) + { + %towrite = writeBuildingComponent(%deployable); + if(%towrite !$= "") + %obj.writeline(%towrite); + } + } + + %obj.close(); + %obj.delete(); + messageClient(%client, "", "\c2Building saved to file:\c3 "@%path @ %name@""); + %client.saveTimeout = 60; + %client.saveTimeoutSchedule = schedule(1000, 0, "ClearSaveTimeout", %client); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + else if(%client.BuildingManagerSubMode == 2) // Loading. + { + if(%client.loadTimeout > 1) + { + messageClient(%client, "", "\c2Cannot load. You must wait\c3 "@%client.loadTimeout@" \c2more seconds before you are able to load again."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + // First, count up all of the pieces. + %grp = nameToID("MissionCleanup/Deployables"); + %name = getsubstr(%typeName, 5, 255); + %path = "Buildings/ClientSaves/"@%client.guid@"/"@%name; + if(!isFile(%path)) + { + messageClient(%client, "", "\c2You cannot load an empty file."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + %piececount = 0; + %clpiececount = 0; + + %tmp = new FileObject(); + %tmp.openforread(%path); + while(!%tmp.isEOF()) + { + %line = %tmp.readline(); + if(strstr(%line, "// Piece Count for this save is ") != -1) + { + %newstring = getsubstr(%line, 32, 255); + %clpiececount = getword(%newstring, 0); + break; + } + } + + %piececount = Deployables.getCount(); + + if(((%clpiececount + %piececount) > $Host::MaxPieceVote) && ((ClientGroup.getCount() - $HostGameBotCount) > 1)) + serverCmdStartNewVote(%client, "VoteLoadBuilding", %clpiececount@ " \c2piece building", "leaving\c3 "@1024 - (%clpiececount + %piececount)@" \c2pieces left", %path, %client, 0); + else + { + messageAll("", "\c3"@%client.nameBase@" \c2is loading "@(%client.sex $= "Male" ? "his" : "her")@"\c3 "@%clpiececount@" \c2piece building. Power has been evaluated and duplicates have been removed."); + + compile(%path); + exec(%path); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + globalPowerCheck(); + } + + %client.loadTimeout = 60; + %client.loadTimeoutSchedule = schedule(1000, 0, "ClearLoadTimeout", %client); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + else if(%client.BuildingManagerSubMode == 3) // Renaming. + { + if(%client.isRenaming) + { + messageClient(%client, "", "\c2You are already renaming another file. Type\c3 cancel \c2in order to cancel the rename."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + + %name = getsubstr(%typeName, 5, 255); + %path = "Buildings/ClientSaves/"@%client.guid@"/"@%name; + + if(!isFile(%path)) + { + messageClient(%client, "", "\c2Cannot rename an empty file."); + return; + } + + %client.isRenaming = 1; + %client.renameFile = %path; + messageClient(%client, "", "\c2The next message you type will rename the file. Type\c3 cancel \c2in order to cancel the operation."); + messageClient(%client, "", "\c2Note that you must add\c3 .cs \c2at the end of the new name in order for the save to work correctly."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + else if(%client.BuildingManagerSubMode == 4) // Deleting. + { + %name = getsubstr(%typeName, 5, 255); + %path = "Buildings/ClientSaves/"@%client.guid@"/"@%name; + if(!isFile(%path)) + { + messageClient(%client, "", "\c2Cannot delete an empty file."); + return; + } + + deleteFile(%path); + messageClient(%client, "", "\c2The save file\c3 "@%name@" \c2has been deleted."); + %client.BuildingManagerMode = 0; + %client.BuildingManagerSubMode = 0; + return; + } + } + } + + if( %typeName $= "VoteMakeKeeper" && (!$Host::AllowKeeperPlayerVotes && !%client.isSuperAdmin)) + return; + + if( (!%client.isAdmin) && (%typeName !$= "VoteKickPlayer" && %typeName !$= "VoteAdminPlayer" && %typeName !$= "VoteMakeNotKeeper" && %typeName !$= "VoteMakeKeeper" && %typeName !$= "VoteChangeMission" && %typeName !$= "VoteTeamDamage" && %typeName !$= "VoteChangeTimeLimit" && %typeName !$= "VotePurebuild" && %typeName !$= "VoteExpertMode" && %typeName !$= "VoteRemoveOrphanedDeployables" && %typeName !$= "VoteTournamentMode" && %typeName !$= "VoteFFAMode" && %typeName !$= "VoteLoadBuilding")) + { + messageClient(%client, "", "\c2Your vote request was rejected."); + return; + } + + %typePass = true; + + // if not a valid vote, turn back. + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteTeamDamage" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteHoardMode" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteGreedMode" ) +// JTL + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VotePurebuild" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteCascadeMode" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteExpertMode" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteVehicles" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteSatchelCharge" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteOnlyOwnerDeconstruct" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteOnlyOwnerCascade" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteOnlyOwnerRotate" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteOnlyOwnerCubicReplace" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteRemoveDeployables" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteGlobalPowerCheck" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteRemoveDupDeployables" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteRemoveNonPoweredDeployables" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteRemoveOrphanedDeployables" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteInvincibleArmors" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteInvincibleDeployables" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteUndergroundMode" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteHazardMode" ) +// if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VoteMTCMode" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VotePrison" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VotePrisonKilling" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VotePrisonTeamKilling" ) + if( $VoteMessage[ %typeName ] $= "" && %typeName !$= "VotePrisonDeploySpam" ) + %typePass = false; +// End JTL + + if(( $VoteMessage[ %typeName, $TeamDamage ] $= "" && %typeName $= "VoteTeamDamage" )) + %typePass = false; +// JTL + if(( $VoteMessage[ %typeName, $Host::Purebuild ] $= "" && %typeName $= "VotePurebuild" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Cascade ] $= "" && %typeName $= "VoteCascadeMode" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::ExpertMode ] $= "" && %typeName $= "VoteExpertMode" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Vehicles ] $= "" && %typeName $= "VoteVehicles" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::SatchelChargeEnabled ] $= "" && %typeName $= "VoteSatchelCharge" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::OnlyOwnerDeconstruct ] $= "" && %typeName $= "VoteOnlyOwnerDeconstruct" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::OnlyOwnerCascade ] $= "" && %typeName $= "VoteOnlyOwnerCascade" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::OnlyOwnerRotate ] $= "" && %typeName $= "VoteOnlyOwnerRotate" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::OnlyOwnerCubicReplace ] $= "" && %typeName $= "VoteOnlyOwnerCubicReplace" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::InvincibleArmors ] $= "" && %typeName $= "VoteInvincibleArmors" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::InvincibleDeployables ] $= "" && %typeName $= "VoteInvincibleDeployables" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::AllowUnderground ] $= "" && %typeName $= "VoteUndergroundMode" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Hazard::Enabled ] $= "" && %typeName $= "VoteHazardMode" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Prison::Enabled ] $= "" && %typeName $= "VotePrison" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Prison::Kill ] $= "" && %typeName $= "VotePrisonKilling" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Prison::TeamKill ] $= "" && %typeName $= "VotePrisonTeamKilling" )) + %typePass = false; + if(( $VoteMessage[ %typeName, $Host::Prison::DeploySpam ] $= "" && %typeName $= "VotePrisonDeploySpam" )) + %typePass = false; +// End JTL + + if( !%typePass ) + return; // -> bye ;) + + // z0dd - ZOD, 10/03/02. This was busted, BanPlayer was never delt with. + if( %typeName $= "BanPlayer" ) + { + if( !%client.isSuperAdmin || %arg1.isAdmin ) + { + return; // -> bye ;) + } + else + { + ban( %arg1, %client ); + return; + } + } + + if(%typeName $= "CancelVote") + { + if(!%client.isAdmin) + return; + + ccCancelVote(%client); + return; + } + + if(%typeName $= "PassVote") + { + if(!%client.isAdmin) + return; + + ccPassVote(%client); + return; + } + + %isAdmin = ( %client.isAdmin || %client.isSuperAdmin ); + +// JTL + if(%typeName $= "VoteVehicles" && !%isAdmin) + if(%typeName $= "VoteVehicles" && !%isAdmin) + if(%typeName $= "VoteSatchelCharge" && !%isAdmin) + if(%typeName $= "VoteOnlyOwnerDeconstruct" && !%isAdmin) + if(%typeName $= "VoteOnlyOwnerCascade" && !%isAdmin) + if(%typeName $= "VoteOnlyOwnerRotate" && !%isAdmin) + if(%typeName $= "VoteOnlyOwnerCubicReplace" && !%isAdmin) + if(%typeName $= "VoteRemoveDeployables" && !%isAdmin) + if(%typeName $= "VoteGlobalPowerCheck" && !%isAdmin) + if(%typeName $= "VoteRemoveDupDeployables" && !%isAdmin) + if(%typeName $= "VoteRemoveNonPoweredDeployables" && !%isAdmin) + if(%typeName $= "VoteRemoveOrphanedDeployables" && !%isAdmin) + if(%typeName $= "VoteInvincibleArmors" && !%isAdmin) + if(%typeName $= "VoteInvincibleDeployables" && !%isAdmin) + if(%typeName $= "VoteUndergroundMode" && !%isAdmin) + if(%typeName $= "VoteHazardMode" && !%isAdmin) +// if(%typeName $= "VoteMTCMode" && !%isAdmin) + if(%typeName $= "VotePrison" && !%isAdmin) + if(%typeName $= "VotePrisonKilling" && !%isAdmin) + if(%typeName $= "VotePrisonTeamKilling" && !%isAdmin) + if(%typeName $= "VotePrisonDeploySpam" && !%isAdmin) + %typePass = false; + + if( !%typePass ) + return; // -> bye ;) +// End JTL + + // 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 ]; +// JTL + else if( %typeName $= "VotePurebuild" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Purebuild ]; + else if( %typeName $= "VoteCascadeMode" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Cascade ]; + else if( %typeName $= "VoteExpertMode" ) + %actionMsg = $VoteMessage[ %typeName, $Host::ExpertMode ]; + else if( %typeName $= "VoteVehicles" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Vehicles ]; + else if( %typeName $= "VoteSatchelCharge" ) + %actionMsg = $VoteMessage[ %typeName, $Host::SatchelChargeEnabled ]; + else if( %typeName $= "VoteOnlyOwnerDeconstruct" ) + %actionMsg = $VoteMessage[ %typeName, $Host::OnlyOwnerDeconstruct ]; + else if( %typeName $= "VoteOnlyOwnerCascade" ) + %actionMsg = $VoteMessage[ %typeName, $Host::OnlyOwnerCascade ]; + else if( %typeName $= "VoteOnlyOwnerRotate" ) + %actionMsg = $VoteMessage[ %typeName, $Host::OnlyOwnerRotate ]; + else if( %typeName $= "VoteOnlyOwnerCubicReplace" ) + %actionMsg = $VoteMessage[ %typeName, $Host::OnlyOwnerCubicReplace ]; + else if( %typeName $= "VoteInvincibleArmors" ) + %actionMsg = $VoteMessage[ %typeName, $Host::InvincibleArmors ]; + else if( %typeName $= "VoteInvincibleDeployables" ) + %actionMsg = $VoteMessage[ %typeName, $Host::InvincibleDeployables ]; + else if( %typeName $= "VoteUndergroundMode" ) + %actionMsg = $VoteMessage[ %typeName, $Host::AllowUnderground ]; + else if( %typeName $= "VoteHazardMode" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Hazard::Enabled ]; + else if( %typeName $= "VotePrison" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Prison::Enabled ]; + else if( %typeName $= "VotePrisonKilling" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Prison::Kill ]; + else if( %typeName $= "VotePrisonTeamKilling" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Prison::TeamKill ]; + else if( %typeName $= "VotePrisonDeploySpam" ) + %actionMsg = $VoteMessage[ %typeName, $Host::Prison::DeploySpam ]; +// End JTL + else + %actionMsg = $VoteMessage[ %typeName ]; + + if( !%client.canVote && !%isAdmin ) + return; + if ( ( !%isAdmin || ( %arg1.isAdmin && ( %client != %arg1 ) ) ) && // z0dd - ToS 4/2/02: Allow SuperAdmins to kick Admins + !( ( %typeName $= "VoteKickPlayer" ) && %client.isSuperAdmin ) ) // z0dd - ToS 4/2/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" && %typeName !$= "VoteMakeKeeper" && %typeName !$= "VoteMakeNotKeeper") + { + 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.team == %client.team && !%cl.isAIControlled()) + { + messageClient( %cl, 'VoteStarted', '\c3%1 \c2initiated a vote to %2 \c3%3\c2.', %client.name, %actionMsg, %arg1.name); + %clientsVoting++; + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c3%1 \c2initiated a vote to %2 \c3%3\c2.', %client.name, %actionMsg, %arg1.name); + %clientsVoting++; + } + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c3%1 \c2initiated a vote to %2 \c3%3\c2.', %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', '\c3%1 \c2initiated a vote to %2 \c3%3\c2 (%4)', %client.name, %actionMsg, %arg1, %arg2 ); + %clientsVoting++; + } + } + } + else if ( %typeName $= "VoteLoadBuilding" ) + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c3%1 \c2initiated a vote to %2 \c3%3\c2, %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', '\c3%1 \c2initiated 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', '\c3%1 \c2initiated 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', '\c3%1 \c2initiated a vote to\c3 %2 \c2%3.', %client.name, %actionMsg, %arg1); + %clientsVoting++; + } + } + } + } + else + { + for ( %idx = 0; %idx < ClientGroup.getCount(); %idx++ ) + { + %cl = ClientGroup.getObject( %idx ); + if ( !%cl.isAIControlled() ) + { + messageClient( %cl, 'VoteStarted', '\c3%1 \c2initiated a vote to\c3 %2\c2.', %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 ); + + Game.votingArguments[0] = %typeName; + Game.votingArguments[1] = %arg1; + Game.votingArguments[2] = %arg2; + Game.votingArguments[3] = %arg3; + Game.votingArguments[4] = %arg4; + + %client.vote = true; + messageAll('addYesVote', ""); + + if(!%client.team == 0) + clearBottomPrint(%client); + logEcho(%client.nameBase@" ("@%client@") initiated a "@%typeName@" vote (arg1: "@%arg1@", arg2: "@%arg2@", arg3: "@%arg3@", arg4: "@%arg4@")"); + } + 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 + Game.evalVote(%typeName, %client, %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) +{ + %tf = %client.transferFrom; + if(isObject(%client.transferTo) && %vote == 0) + { + messageClient(%client, "", "\c2Piece transaction cancelled."); + messageClient(%client.transferTo, "", "\c3"@%client.nameBase@" \c2has cancelled the piece transaction."); + + %client.transferTo.transferFrom = ""; + %client.transferTo = ""; + return; + } + + if(isObject(%client.transferFrom)) + { + if(%vote == 1) + { + messageAll('MsgAdminForce', "\c3"@%tf.nameBase@"\c2 is transferring "@(%tf.sex $= "Male" ? "his" : "her")@" pieces to \c3"@%client.nameBase@"\c2."); + GivePieces(%client.transferFrom, %client); + + %tf.transferTo = ""; + %client.transferFrom = ""; + } + else + { + messageClient(%client, "", "\c2Piece transaction declined."); + messageClient(%tf, "", "\c3"@%client.nameBase@" \c2has declined the piece transaction."); + + %tf.transferTo = ""; + %client.transferFrom = ""; + } + + return; + } + + // 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 + Game.evalVote(%typeName, false, %arg1, %arg2, %arg3, %arg4); + Game.scheduleVote = ""; + Game.kickClient = ""; + + Game.votingArguments = ""; + + 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); + } +} diff --git a/Scripts/ai.cs b/Scripts/ai.cs new file mode 100644 index 0000000..84d5830 --- /dev/null +++ b/Scripts/ai.cs @@ -0,0 +1,908 @@ +//-----------------------------------// +// AI SCRIPT FUNCTIONS // +//-----------------------------------// + +//first, exec the supporting scripts +exec("scripts/aiDebug.cs"); +exec("scripts/aiDefaultTasks.cs"); +exec("scripts/aiObjectives.cs"); +exec("scripts/aiInventory.cs"); +exec("scripts/aiChat.cs"); +exec("scripts/aiHumanTasks.cs"); +exec("scripts/aiObjectiveBuilder.cs"); +exec("scripts/aiBotProfiles.cs"); + +$AIModeStop = 0; +$AIModeWalk = 1; +$AIModeGainHeight = 2; +$AIModeExpress = 3; +$AIModeMountVehicle = 4; + +$AIClientLOSTimeout = 15000; //how long a client has to remain out of sight of the bot + //before the bot "can't see" the client anymore... +$AIClientMinLOSTime = 10000; //how long a bot will search for a client + + +//-----------------------------------// +//Objective weights - level 1 + +$AIWeightCapFlag[1] = 5000; //range 5100 to 5320 +$AIWeightKillFlagCarrier[1] = 4800; //range 4800 to 5120 +$AIWeightReturnFlag[1] = 5001; //range 5101 to 5321 +$AIWeightDefendFlag[1] = 3900; //range 4000 to 4220 +$AIWeightGrabFlag[1] = 3850; //range 3950 to 4170 + +$AIWeightDefendFlipFlop[1] = 3900; //range 4000 to 4220 +$AIWeightCaptureFlipFlop[1] = 3850; //range 3850 to 4170 + +$AIWeightAttackGenerator[1] = 3100; //range 3200 to 3520 +$AIWeightRepairGenerator[1] = 3200; //range 3300 to 3620 +$AIWeightDefendGenerator[1] = 3100; //range 3200 to 3420 + +$AIWeightMortarTurret[1] = 3400; //range 3500 to 3600 +$AIWeightLazeObject[1] = 3200; //range 3300 to 3400 +$AIWeightRepairTurret[1] = 3100; //range 3200 to 3420 + +$AIWeightAttackInventory[1] = 2900; //range 2800 to 2920 +$AIWeightRepairInventory[1] = 2900; //range 2800 to 2920 + +$AIWeightEscortOffense[1] = 2900; //range 2800 to 2920 +$AIWeightEscortCapper[1] = 3250; //range 3350 to 3470 + +//used to allow a bot to finish tasks once started. +$AIWeightContinueDeploying = 4250; +$AIWeightContinueRepairing = 4250; + +//Objective weights from human +$AIWeightHumanIssuedCommand = 4450; +$AIWeightHumanIssuedEscort = 4425; + +//Objective weights - level 2 +$AIWeightCapFlag[2] = 0; //only one person can ever cap a flag +$AIWeightKillFlagCarrier[2] = 4800; //range 4800 to 5020 +$AIWeightReturnFlag[2] = 4100; //range 4200 to 4320 +$AIWeightDefendFlag[2] = 2000; //range 2100 to 2220 +$AIWeightGrabFlag[2] = 2000; //range 2100 to 2220 + +$AIWeightDefendFlipFlop[2] = 2000; //range 2100 to 2220 +$AIWeightDefendFlipFlop[3] = 1500; //range 1600 to 1720 +$AIWeightDefendFlipFlop[4] = 1000; //range 1100 to 1220 + +$AIWeightAttackGenerator[2] = 1600; //range 1700 to 1920 +$AIWeightRepairGenerator[2] = 1600; //range 1700 to 1920 +$AIWeightDefendGenerator[2] = 1500; //range 1600 to 1720 + +$AIWeightAttackInventory[2] = 1400; //range 1500 to 1720 +$AIWeightRepairInventory[2] = 1400; //range 1500 to 1720 + +$AIWeightMortarTurret[2] = 1000; //range 1100 to 1320 +$AIWeightLazeObject[2] = 0; //no need to have more than one targetter +$AIWeightRepairTurret[2] = 1000; //range 1100 to 1320 + +$AIWeightEscortOffense[2] = 2900; //range 3300 to 3420 +$AIWeightEscortCapper[2] = 3000; //range 3100 to 3220 + + +function AIInit() +{ + AISlicerInit(); + installNavThreats(); + NavDetectForceFields(); +// ShowFPS(); + + //enable the use of grenades + $AIDisableGrenades = false; + $AIDisableChat = false; + + //create the "objective delete set" + if(nameToId("AIBombLocationSet") <= 0) + { + $AIBombLocationSet = new SimSet("AIBombLocationSet"); + MissionCleanup.add($AIBombLocationSet); + } + + //create the Inventory group + if(nameToId("AIStationInventorySet") <= 0) + { + $AIInvStationSet = new SimSet("AIStationInventorySet"); + MissionCleanup.add($AIInvStationSet); + } + + //create the Item group + if (nameToId("AIItemSet") <= 0) + { + $AIItemSet = new SimSet("AIItemSet"); + MissionCleanup.add($AIItemSet); + } + + //create the Item group + if (nameToId("AIGrenadeSet") <= 0) + { + $AIGrenadeSet = new SimSet("AIGrenadeSet"); + MissionCleanup.add($AIGrenadeSet); + } + + //create the weapon group + if (nameToId("AIWeaponSet") <= 0) + { + $AIWeaponSet = new SimSet("AIWeaponSet"); + MissionCleanup.add($AIWeaponSet); + } + + //create the deployed turret group + if (nameToID("AIRemoteTurretSet") <= 0) + { + $AIRemoteTurretSet = new SimSet("AIRemoteTurretSet"); + MissionCleanup.add($AIRemoteTurretSet); + } + + //create the deployed turret group + if (nameToID("AIDeployedMineSet") <= 0) + { + $AIDeployedMineSet = new SimSet("AIDeployedMineSet"); + MissionCleanup.add($AIDeployedMineSet); + } + + //create the deployed turret group + if (nameToID("AIVehicleSet") <= 0) + { + $AIVehicleSet = new SimSet("AIVehicleSet"); + MissionCleanup.add($AIVehicleSet); + } + + %missionGroupFolder = nameToID("MissionGroup"); + %missionGroupFolder.AIMissionInit(); +} + +// this is called at mission load by the specific game type +function AIInitObjectives(%team, %game) +{ + %group = nameToID("MissionGroup/Teams/team" @ %team @ "/AIObjectives"); + if(%group < 0) + return; // opps, there is no Objectives set for this team. + + // add the grouped objectives to the teams Q + %count = %group.getCount(); + for (%i = 0; %i < %count; %i++) + { + %objective = %group.getObject(%i); + if (%objective.getClassName() !$= "AIObjective") + { + %grpCount = %objective.getCount(); + for (%j = 0; %j < %grpCount; %j++) + { + %grpObj = %objective.getObject(%j); + if (%objective.gameType $= "" || %objective.gameType $= "all") + %objType = ""; + else + %objType = %objective.gameType @ "Game"; + + if (%objType $= "" || %objType $= %game.class) + { + %grpObj.group = %objective; + $ObjectiveQ[%team].add(%grpObj); + } + } + } + } + + + // add the non-grouped objectives to the teams Q + %count = %group.getCount(); + for(%i = 0; %i < %count; %i++) + { + %objective = %group.getObject(%i); + + //if the objective is not an "AIObjective", assume it's a group and continue + if (%objective.getClassName() !$= "AIObjective") + continue; + + if (%objective.gameType $= "" || %objective.gameType $= "all") + %objType = ""; + else + %objType = %objective.gameType @ "Game"; + + if (%objType $= "" || %objType $= %game.class) + { + %objective.group = ""; + $ObjectiveQ[%team].add(%objective); + } + } + + // initialize the objectives + %count = $ObjectiveQ[%team].getCount(); + for(%i = 0; %i < %count; %i++) + { + %objective = $ObjectiveQ[%team].getObject(%i); + + //clear out any dynamic fields + %objective.clientLevel1 = ""; + %objective.clientLevel2 = ""; + %objective.clientLevel3 = ""; + %objective.isInvalid = false; + %objective.repairObjective = ""; + + //set the location, if required + if (%objective.position !$= "0 0 0") + %objective.location = %objective.position; + + // find targeted object ID's + if(%objective.targetObject !$= "") + %objective.targetObjectId = NameToId(%objective.targetObject); + else + %objective.targetObjectId = -1; + + if(%objective.targetClientObject !$= "") + %objective.targetClientId = NameToId(%objective.targetClient); + else + %objective.targetClientId = -1; + + if (%objective.position $= "0 0 0") + { + if (%objective.location $= "0 0 0") + { + if (%objective.targetObjectId > 0) + %objective.position = %objective.targetObjectId.position; + } + else + %objective.position = %objective.location; + } + } + + //finally, sort the objectiveQ + $ObjectiveQ[%team].sortByWeight(); +} + +//This function is designed to clear out the objective Q's, and clear the task lists from all the AIs +function AIMissionEnd() +{ + //disable the AI system + AISystemEnabled(false); + + //loop through the client list, and clear the tasks of each bot + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %client = ClientGroup.getObject(%i); + if (%client.isAIControlled()) + { + //cancel the respawn thread and the objective thread... + cancel(%client.respawnThread); + cancel(%client.objectiveThread); + + //reset the clients tasks, variables, 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; + + //do the nav graph cleanup + %client.missionCycleCleanup(); + } + } + + //clear the objective Q's + for (%i = 0; %i <= Game.numTeams; %i++) + { + if (isObject($ObjectiveQ[%i])) + { + $ObjectiveQ[%i].clear(); + $ObjectiveQ[%i].delete(); + } + $ObjectiveQ[%i] = ""; + } + + //now delete all the sets used by the AI system... + if (isObject($AIBombLocationSet)) + $AIBombLocationSet.delete(); + $AIBombLocationSet = ""; + + if (isObject($AIInvStationSet)) + $AIInvStationSet.delete(); + $AIInvStationSet = ""; + + if (isObject($AIItemSet)) + $AIItemSet.delete(); + $AIItemSet = ""; + + if (isObject($AIGrenadeSet)) + $AIGrenadeSet.delete(); + $AIGrenadeSet = ""; + + if (isObject($AIWeaponSet)) + $AIWeaponSet.delete(); + $AIWeaponSet = ""; + + if (isObject($AIRemoteTurretSet)) + $AIRemoteTurretSet.delete(); + $AIRemoteTurretSet = ""; + + if (isObject($AIDeployedMineSet)) + $AIDeployedMineSet.delete(); + $AIDeployedMineSet = ""; + + if (isObject($AIVehicleSet)) + $AIVehicleSet.delete(); + $AIVehicleSet = ""; +} + +//FUNCTIONS ON EACH OBJECT EXECUTED AT MISSION LOAD TIME +function SimGroup::AIMissionInit(%this) +{ + for(%i = 0; %i < %this.getCount(); %i++) + %this.getObject(%i).AIMissionInit(%this); +} + +function GameBase::AIMissionInit(%this) +{ + %this.getDataBlock().AIMissionInit(%this); +} + +function StationInventory::AIMissionInit(%data, %object) +{ + $AIInvStationSet.add(%object); +} + +function Flag::AIMissionInit(%data, %object) +{ + if (%object.team >= 0) + $AITeamFlag[%object.team] = %object; +} + +function SimObject::AIMissionInit(%this) +{ + //this function is declared to prevent console error msg spam... +} + +function ItemData::AIMissionInit(%data, %object) +{ + $AIItemSet.add(%object); +} + +function AIThrowObject(%object) +{ + $AIItemSet.add(%object); +} + +function AIGrenadeThrown(%object) +{ + $AIGrenadeSet.add(%object); +} + +function AIDeployObject(%client, %object) +{ + //first, set the object id on the client + %client.lastDeployedObject = %object; + + //now see if it was a turret... + %type = %object.getDataBlock().getName(); + if (%type $= "TurretDeployedFloorIndoor" || %type $= "TurretDeployedWallIndoor" || + %type $= "TurretDeployedCeilingIndoor" || %type $= "TurretDeployedOutdoor") + { + $AIRemoteTurretSet.add(%object); + } +} + +function AIDeployMine(%object) +{ + $AIDeployedMineSet.add(%object); +} + +function AIVehicleMounted(%vehicle) +{ + $AIVehicleSet.add(%vehicle); +} + +function AICorpseAdded(%corpse) +{ + if (isObject(%corpse)) + { + %corpse.isCorpse = true; + $AIItemSet.add(%corpse); + } +} + +//OTHER UTILITY FUNCTIONS + +function AIConnection::onAIDrop(%client) +{ + //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.clearTasks(); + %client.clearStep(); + %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); + + //do the nav graph cleanup + %client.missionCycleCleanup(); +} + +function AIConnection::endMission(%client) +{ + //cancel the respawn thread, and spawn them manually + cancel(%client.respawnThread); + cancel(%client.objectiveThread); +} + +function AIConnection::startMission(%client) +{ + //assign the team + if (%client.team <= 0) + Game.assignClientTeam(%client); + + //set the client's sensor group... + setTargetSensorGroup( %client.target, %client.team ); + %client.setSensorGroup( %client.team ); + + //sends a message so everyone know the bot is in the game... + Game.AIHasJoined(%client); + %client.matchStartReady = true; + + //spawn the bot... + onAIRespawn(%client); +} + +function AIConnection::onAIConnect(%client, %name, %team, %skill, %offense, %voice, %voicePitch) +{ + // Sex/Race defaults + %client.sex = "Male"; + %client.race = "Human"; + %client.armor = "Light"; + + //setup the voice and voicePitch + if (%voice $= "") + %voice = "Bot1"; + %client.voice = %voice; + %client.voiceTag = addTaggedString(%voice); + + if (%voicePitch $= "" || %voicePitch < 0.5 || %voicePitch > 2.0) + %voicePitch = 1.0; + %client.voicePitch = %voicePitch; + + %client.name = addTaggedString( "\cp\c9" @ %name @ "\co" ); + %client.nameBase = %name; + + echo(%client.name); + echo("CADD: " @ %client @ " " @ %client.getAddress()); + $HostGamePlayerCount++; + + //set the initial team - Game.assignClientTeam() should be called later on... + %client.team = %team; + if ( %client.team & 1 ) + %client.skin = addTaggedString( "basebot" ); + else + %client.skin = addTaggedString( "basebbot" ); + + //setup the target for use with the sensor net, etc... + %client.target = allocClientTarget(%client, %client.name, %client.skin, %client.voiceTag, '_ClientConnection', 0, 0, %client.voicePitch); + + //i need to send a "silent" version of this for single player but still use the callback -jr` + echo(%name SPC getTaggedString(%name)); + if($currentMissionType $= "SinglePlayer") + messageAllExcept(%client, -1, 'MsgClientJoin', "", %name, %client, %client.target, true); + else if(%name !$= "_AISent") // Eolk Sentinels + messageAllExcept(%client, -1, 'MsgClientJoin', '\c1%1 joined the game.', %name, %client, %client.target, true); + + //assign the skill + %client.setSkillLevel(%skill); + + //assign the affinity + %client.offense = %offense; + + //clear any flags + %client.stop(); // this will clear the players move state + %client.clearStep(); + %client.lastDamageClient = -1; + %client.lastDamageTurret = -1; + %client.setEngageTarget(-1); + %client.setTargetObject(-1); + %client.objective = ""; + + //clear the defaulttasks flag + %client.defaultTasksAdded = false; + + //if the mission is already running, spawn the bot + if ($missionRunning) + %client.startMission(); +} + +// This routes through C++ code so profiler can register it. Also, the console function +// ProfilePatch1() tracks time spent (at MS resolution), # calls, average time per call. +// See console variables $patch1Total (MS so far in routine), $patch1Avg (average MS +// per call), and $patch1Calls (# of calls). +function patchForTimeTest(%client) +{ + if( isObject( Game ) ) + Game.AIChooseGameObjective(%client); +} + +function AIReassessObjective(%client) +{ + ProfilePatch1(patchForTimeTest, %client); + // Game.AIChooseGameObjective(%client); + %client.objectiveThread = schedule(5000, %client, "AIReassessObjective", %client); +} + +function onAIRespawn(%client) +{ + %markerObj = Game.pickPlayerSpawn(%client, true); + Game.createPlayer(%client, %markerObj); + + //make sure the player object is the AI's control object - even during the mission warmup time + //the function AISystemEnabled(true/false) will control whether they actually move... + %client.setControlObject(%client.player); + + if (%client.objective) + error("ERROR!!! " @ %client @ " is still assigned to objective: " @ %client.objective); + + //clear the objective and choose a new one + AIUnassignClient(%client); + %client.stop(); + %client.clearStep(); + %client.lastDamageClient = -1; + %client.lastDamageTurret = -1; + %client.shouldEngage = -1; + %client.setEngageTarget(-1); + %client.setTargetObject(-1); + %client.pilotVehicle = false; + + //set the spawn time + %client.spawnTime = getSimTime(); + %client.respawnThread = ""; + + //timeslice the objective reassessment for the bots + if (!isEventPending(%client.objectiveThread)) + { + %curTime = getSimTime(); + %remainder = %curTime % 5000; + %schedTime = $AITimeSliceReassess - %remainder; + if (%schedTime <= 0) + %schedTime += 5000; + %client.objectiveThread = schedule(%schedTime, %client, "AIReassessObjective", %client); + + //set the next time slice "slot" + $AITimeSliceReassess += 300; + if ($AITimeSliceReassess > 5000) + $AITimeSliceReassess -= 5000; + } + + //call the game specific spawn function + Game.onAIRespawn(%client); +} + +function AIClientIsAlive(%client, %duration) +{ + if(%client < 0 || %client.player <= 0) + return false; + if (isObject(%client.player)) + { + %state = %client.player.getState(); + if (%state !$= "Dead" && %state !$= "" && (%duration $= "" || getSimTime() - %client.spawnTime >= %duration)) + return true; + else + return false; + } + else + return false; +} + +//------------------------------ +function AIFindClosestEnemy(%srcClient, %radius, %losTimeout) +{ + //see if there's an enemy near our defense location... + if (isObject(%srcClient.player)) + %srcLocation = %srcClient.player.getWorldBoxCenter(); + else + %srcLocation = "0 0 0"; + return AIFindClosestEnemyToLoc(%srcClient, %srcLocation, %radius, %losTimeout, false, true); +} + +function AIFindClosestEnemyToLoc(%srcClient, %srcLocation, %radius, %losTimeout, %ignoreLOS, %distFromClient) +{ + if (%ignoreLOS $= "") + %ignoreLOS = false; + if (%distFromClient $= "") + %distFromClient = false; + + %count = ClientGroup.getCount(); + %closestClient = -1; + %closestDistance = 32767; + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + + //make sure we find someone who's alive + if (AIClientIsAlive(%cl) && %cl.team != %srcClient.team) + { + %clIsCloaked = !isTargetVisible(%cl.target, %srcClient.getSensorGroup()); + + //make sure the client can see the enemy + %hasLOS = %srcClient.hasLOSToClient(%cl); + %losTime = %srcClient.getClientLOSTime(%cl); + if (%ignoreLOS || %hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%cl, %losTime + 1000))) + { + %testPos = %cl.player.getWorldBoxCenter(); + if (%distFromClient) + %distance = %srcClient.getPathDistance(%testPos); + else + %distance = AIGetPathDistance(%srcLocation, %testPos); + if (%distance > 0 && (%radius < 0 || %distance < %radius) && %distance < %closestDistance && (!%clIsCloaked || %distance < 8)) + { + %closestClient = %cl; + %closestDistance = %distance; + } + } + } + } + + return %closestClient SPC %closestDistance; +} + +function AIFindClosestEnemyPilot(%client, %radius, %losTimeout) +{ + //loop through the vehicle set, looking for pilotted vehicles... + %closestPilot = -1; + %closestDist = %radius; + %count = $AIVehicleSet.getCount(); + for (%i = 0; %i < %count; %i++) + { + //first, make sure the vehicle is mounted by pilotted + %vehicle = $AIVehicleSet.getObject(%i); + %pilot = %vehicle.getMountNodeObject(0); + if (%pilot <= 0 || !AIClientIsAlive(%pilot.client)) + continue; + + //make sure the pilot is an enemy + if (%pilot.client.team == %client.team) + continue; + + //see if the pilot has been seen by the client + %hasLOS = %client.hasLOSToClient(%pilot.client); + %losTime = %client.getClientLOSTime(%pilot.client); + if (%hasLOS || (%losTime < %losTimeout && AIClientIsAlive(%pilot.client, %losTime + 1000))) + { + //see if it's the closest + %clientPos = %client.player.getWorldBoxCenter(); + %pilotPos = %pilot.getWorldBoxCenter(); + %dist = VectorDist(%clientPos, %pilotPos); + if (%dist < %closestDist) + { + %closestPilot = %pilot.client; + %closestDist = %dist; + } + } + } + + return %closestPilot SPC %closestDist; +} + +function AIFindAIClientInView(%srcClient, %team, %radius) +{ + //make sure the player is alive + if (! AIClientIsAlive(%srcClient)) + return -1; + + //get various info about the player's eye + %srcEyeTransform = %srcClient.player.getEyeTransform(); + %srcEyePoint = firstWord(%srcEyeTransform) @ " " @ getWord(%srcEyeTransform, 1) @ " " @ getWord(%srcEyeTransform, 2); + %srcEyeVector = VectorNormalize(%srcClient.player.getEyeVector()); + + //see if there's an enemy near our defense location... + %count = ClientGroup.getCount(); + %viewedClient = -1; + %clientDot = -1; + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + + //make sure we find an AI who's alive and not the srcClient + if (%cl != %srcClient && AIClientIsAlive(%cl) && %cl.isAIControlled() && (%team < 0 || %cl.team == %team)) + { + //make sure the player is within range + %clPos = %cl.player.getWorldBoxCenter(); + %distance = VectorDist(%clPos, %srcEyePoint); + if (%radius <= 0 || %distance <= %radius) + { + //create the vector from the srcClient to the client + %clVector = VectorNormalize(VectorSub(%clPos, %srcEyePoint)); + + //see if the dot product is greater than our current, and greater than 0.6 + %dot = VectorDot(%clVector, %srcEyeVector); + + if (%dot > 0.6 && %dot > %clientDot) + { + %viewedClient = %cl; + %clientDot = %dot; + } + } + } + } + + return %viewedClient; +} + +//----------------------------------------------------------------------------- +//AI VEHICLE FUNCTIONS + +function Armor::AIonMount(%this, %obj, %vehicle, %node) +{ + //set the client var... + %client = %obj.client; + %client.turretMounted = -1; + + //make sure the AI was *supposed* to mount the vehicle + if (!%client.isMountingVehicle()) + { + AIDisembarkVehicle(%client); + return; + } + + //get the vehicle's pilot + %pilot = %vehicle.getMountNodeObject(0); + + //make sure the bot is in node 0 if'f the bot is piloting the vehicle + if ((%node == 0 && !%client.pilotVehicle) || (%node > 0 && %client.pilotVehicle)) + { + AIDisembarkVehicle(%client); + return; + } + + //make sure the bot didn't is on the same team as the pilot + if (%pilot > 0 && isObject(%pilot) && %pilot.client.team != %client.team) + { + AIDisembarkVehicle(%client); + return; + } + + //if we're supposed to pilot the vehicle, set the control object + if (%client.pilotVehicle) + %client.setControlObject(%vehicle); + + //each vehicle may be built differently... + if (%vehicle.getDataBlock().getName() $= "AssaultVehicle") + { + //node 1 is this vehicle's turret seat + if (%node == 1) + { + %turret = %vehicle.getMountNodeObject(10); + %skill = %client.getSkillLevel(); + %turret.setSkill(%skill); + %client.turretMounted = %turret; + %turret.setAutoFire(true); + } + } + + else if (%vehicle.getDataBlock().getName() $= "BomberFlyer") + { + //node 1 is this vehicle's turret seat + if (%node == 1) + { + %turret = %vehicle.getMountNodeObject(10); + %skill = %client.getSkillLevel(); + %turret.setSkill(%skill); + %client.turretMounted = %turret; + %client.setTurretMounted(%turret); + %turret.setAutoFire(true); + } + } +} + +function Armor::AIonUnMount(%this, %obj, %vehicle, %node) +{ + //get the client var + %client = %obj.client; + + //reset the control object + if (%client.pilotVehicle) + %client.setControlObject(%client.player); + %client.pilotVehicle = false; + + //if the client had mounted a turret, turn the turret back off + if (%client.turretMounted > 0) + %client.turretMounted.setAutoFire(false); + %client.turretMounted = -1; + %client.setTurretMounted(-1); + + // reset the turret skill level + if(%vehicle.getDataBlock().getName() $= "AssaultVehicle") + if (%node == 1) + %vehicle.getMountNodeObject(10).setSkill(1.0); + + if(%vehicle.getDataBlock().getName() $= "BomberFlyer") + if(%node == 1) + %vehicle.getMountNodeObject(10).setSkill(1.0); +} + +function AIDisembarkVehicle(%client) +{ + if (%client.player.isMounted()) + { + if (%client.pilotVehicle) + %client.setControlObject(%client.player); + %client.pressJump(); + } +} + +function AIProcessVehicle(%client) +{ + //see if we're mounted on a turret, and if that turret has a target + if (%client.turretMounted > 0) + { + %turretDB = %client.turretMounted.getDataBlock(); + + //see if we're in a bomber close to a bomb site... + if (%turretDB.getName() $= "BomberTurret") + { + %clientPos = getWords(%client.player.position, 0, 1) @ " 0"; + %found = false; + %count = $AIBombLocationSet.getCount(); + for (%i = 0; %i < %count; %i++) + { + %bombObj = $AIBombLocationSet.getObject(%i); + %bombLocation = %bombObj.location; + + //make sure the objective was issued by someone in the vehicle + if (%bombObj.issuedByClientId.vehicleMounted == %client.vehicleMounted) + { + //find out where the bomb is going to drop... first, how high up are we... + %bombLocation2D = getWord(%bombLocation, 0) SPC getWord(%bombLocation, 1) SPC "0"; + %height = getWord(%client.vehicleMounted.position, 2) - getWord(%bombLocation, 2); + + //find out how long it'll take the bomb to fall that far... + //assume no initial velocity in the Z axis... + %timeToFall = mSqrt((2.0 * %height) / 9.81); + + //how fast is the vehicle moving in the XY plane... + %myLocation = %client.vehicleMounted.position; + %myLocation2D = getWord(%myLocation, 0) SPC getWord(%myLocation, 1) SPC "0"; + %vel = %client.vehicleMounted.getVelocity(); + %vel2D = getWord(%vel, 0) SPC getWord(%vel, 1) SPC "0"; + + %bombImpact2D = VectorAdd(%myLocation2D, VectorScale(%vel2D, %timeToFall)); + + //see if the bomb inpact position is within 20m of the desired bomb site... + %distToBombsite2D = VectorDist(%bombImpact2D, %bombLocation2D); + if (%height > 20 && %distToBombsite2D < 25) + { + %found = true; + break; + } + } + } + + //see if we found a bomb site + if (%found) + { + %client.turretMounted.selectedWeapon = 2; + %turretDB.onTrigger(%client.turretMounted, 0, true); + return; + } + } + + //we're not bombing, make sure we have the regular weapon selected + %client.turretMounted.selectedWeapon = 1; + if (isObject(%client.turretMounted.getTargetObject())) + %turretDB.onTrigger(%client.turretMounted, 0, true); + else + %turretDB.onTrigger(%client.turretMounted, 0, false); + } +} + +function AIPilotVehicle(%client) +{ + //this is not very well supported, but someone will find a use for this function... +} \ No newline at end of file diff --git a/Scripts/aiDefaultTasks.cs b/Scripts/aiDefaultTasks.cs new file mode 100644 index 0000000..6224ffa --- /dev/null +++ b/Scripts/aiDefaultTasks.cs @@ -0,0 +1,1028 @@ +//All tasks for deathmatch, hunters, and tasks that coincide with the current objective task live here... + +//Weights for tasks that override the objective task: must be between 4300 and 4700 +$AIWeightVehicleMountedEscort = 4700; +$AIWeightReturnTurretFire = 4675; +$AIWeightNeedItemBadly = 4650; +$AIWeightReturnFire = 4600; +$AIWeightDetectMine = 4500; +$AIWeightTauntVictim = 4400; +$AIWeightNeedItem = 4350; +$AIWeightDestroyTurret = 4300; + +//Weights that allow the objective task to continue: must be 3000 or less +$AIWeightFoundEnemy = 3000; +$AIWeightFoundItem = 2500; +$AIWeightFoundToughEnemy = 1000; +$AIWeightPatrolling = 2000; + +//Hunters weights... +$AIHuntersWeightMustCap = 4690; +$AIHuntersWeightNeedHealth = 4625; +$AIHuntersWeightShouldCap = 4425; +$AIHuntersWeightMustEngage = 4450; +$AIHuntersWeightShouldEngage = 4325; +$AIHuntersWeightPickupFlag = 4425; + +//Rabbit weights... +$AIRabbitWeightDefault = 4625; +$AIRabbitWeightNeedInv = 4325; + +//Bounty weights... +$AIBountyWeightShouldEngage = 4325; + +//----------------------------------------------------------------------------- +//AIEngageTask is responsible for anything to do with engaging an enemy + +function AIEngageWhoWillWin(%client1, %client2) +{ + //assume both clients are alive - gather some info + if (%client1.isAIControlled()) + %skill1 = %client1.getSkillLevel(); + else + %skill1 = 0.5; + + if (%client2.isAIControlled()) + %skill2 = %client2.getSkillLevel(); + else + %skill2 = 0.5; + + %damage1 = %client1.player.getDamagePercent(); + %damage2 = %client2.player.getDamagePercent(); + + //first compare health + %tolerance1 = 0.5 + ((%skill1 - %skill2) * 0.3); + %tolerance2 = 0.5 + ((%skill2 - %skill1) * 0.3); + if (%damage1 - %damage2 > %tolerance1) + return %client2; + else if (%damage2 - %damage1 > %tolerance2) + return %client1; + + //health not a problem, see how the equipment compares for the two... + %weaponry1 = AIEngageWeaponRating(%client1); + %weaponry2 = AIEngageWeaponRating(%client2); + %effective = 20; + if (%weaponry1 < %effective && %weaponry2 >= %effective) + return %client2; + else if (%weaponry1 >= %effective && %weaponry2 < %effective) + return %client1; + + //no other criteria for now... return -1 to indicate a tie... + return -1; +} + +function AIEngageTask::init(%task, %client) +{ +} + +function AIEngageTask::assume(%task, %client) +{ + %task.setWeightFreq(9); + %task.setMonitorFreq(9); + %task.searching = false; + if (isObject(%client.shouldEngage.player)) + %task.searchLocation = %client.shouldEngage.player.getWorldBoxCenter(); +} + +function AIEngageTask::retire(%task, %client) +{ +} + +function AIEngageTask::weight(%task, %client) +{ + %player = %client.player; + if (!isObject(%player)) + return; + + %clientPos = %player.getWorldBoxCenter(); + %currentTarget = %client.shouldEngage; + if (!AIClientIsAlive(%currentTarget)) + %currentTarget = %client.getEngageTarget(); + %client.shouldEngage = -1; + %mustEngage = false; + %tougherEnemy = false; + + //first, make sure we actually can engage + if (AIEngageOutOfAmmo(%client)) + { + %client.shouldEngage = -1; + %task.setWeight(0); + return; + } + + //see if anyone has fired on us recently... + %losTimeout = $AIClientMinLOSTime + ($AIClientLOSTimeout * %client.getSkillLevel()); + if (AIClientIsAlive(%client.lastDamageClient, %losTimeout) && getSimTime() - %client.lastDamageTime < %losTimeout) + { + //see if we should turn on the new attacker + if (AIClientIsAlive(%currentTarget)) + { + %targPos = %currentTarget.player.getWorldBoxCenter(); + %curTargDist = %client.getPathDistance(%targPos); + + %newTargPos = %client.lastDamageClient.player.getWorldBoxCenter(); + %newTargDist = %client.getPathDistance(%newTargPos); + + //see if the new targ is no more than 30 m further + if (%newTargDist > 0 && %newTargDist < %curTargDist + 30) + { + %client.shouldEngage = %client.lastDamageClient; + %mustEngage = true; + } + } + else + { + %client.shouldEngage = %client.lastDamageClient; + %mustEngage = true; + } + } + + //no one has fired at us recently, see if we're near an enemy + else + { + %result = AIFindClosestEnemy(%client, 100, %losTimeout); + %closestEnemy = getWord(%result, 0); + %closestdist = getWord(%result, 1); + if (%closestEnemy > 0) + { + //see if we're right on top of them + %targPos = %closestEnemy.player.getWorldBoxCenter(); + %dist = %client.getPathDistance(%targPos); + + if (%dist > 0 && %dist < 20) + { + %client.shouldEngage = %closestEnemy; + %mustEngage = true; + } + + //else choose them only if we're not already attacking someone + else if (%currentTarget <= 0) + { + %client.shouldEngage = %closestEnemy; + %mustEngage = false; + + //Make sure the odds are not overwhelmingly in favor of the enemy... + if (AIEngageWhoWillWin(%client, %closestEnemy) == %closestEnemy) + %tougherEnemy = true; + } + } + } + + //if we still haven't found a new target, keep fighting the old one + if (%client.shouldEngage <= 0) + { + if (AIClientIsAlive(%currentTarget)) + { + //see if we still have sight of the current target + %hasLOS = %client.hasLOSToClient(%currentTarget); + %losTime = %client.getClientLOSTime(%currentTarget); + if (%hasLOS || %losTime < %losTimeout) + %client.shouldEngage = %currentTarget; + else + %client.shouldEngage = -1; + } + else + %client.shouldEngage = -1; + %mustEngage = false; + } + + //finally, set the weight + if (%client.shouldEngage > 0) + { + if (%mustEngage) + %task.setWeight($AIWeightReturnFire); + else if (%tougherEnemy) + %task.setWeight($AIWeightFoundToughEnemy); + else + %task.setWeight($AIWeightFoundEnemy); + } + else + %task.setWeight(0); +} + +function AIEngageTask::monitor(%task, %client) +{ + if (!AIClientIsAlive(%client.shouldEngage)) + { + %client.stop(); + %client.clearStep(); + %client.setEngageTarget(-1); + return; + } + + %hasLOS = %client.hasLOSToClient(%client.shouldEngage); + %losTime = %client.getClientLOSTime(%client.shouldEngage); + //%detectLocation = %client.getDetectLocation(%client.shouldEngage); + %detectPeriod = %client.getDetectPeriod(); + + //if we can see the target, engage... + if (%hasLOS || %losTime < %detectPeriod) + { + %client.stepEngage(%client.shouldEngage); + %task.searching = false; + %task.searchLocation = %client.shouldEngage.player.getWorldBoxCenter(); + } + + //else if we haven't for approx 5 sec... move to the last known location + else + { + //clear the engage target + %client.setEngageTarget(-1); + + if (! %task.searching) + { + %dist = VectorDist(%client.player.getWorldBoxCenter(), %task.searchLocation); + if (%dist < 4) + { + %client.stepIdle(%task.searchLocation); + %task.searching = true; + } + else + %client.stepMove(%task.searchLocation, 4.0); + } + } +} + +//----------------------------------------------------------------------------- +//AIPickupItemTask is responsible for anything to do with picking up an item + +function AIPickupItemTask::init(%task, %client) +{ +} + +function AIPickupItemTask::assume(%task, %client) +{ + %task.setWeightFreq(10); + %task.setMonitorFreq(10); + + %task.pickUpItem = -1; +} + +function AIPickupItemTask::retire(%task, %client) +{ +} + +function AIPickupItemTask::weight(%task, %client) +{ + //if we're already picking up an item, make sure it's still valid, then keep the weight the same... + if (%task.pickupItem > 0) + { + if (isObject(%task.pickupItem) && !%task.pickupItem.isHidden() && AICouldUseItem(%client, %task.pickupItem)) + return; + else + %task.pickupItem = -1; + } + + //otherwise, search for objects + //first, see if we can pick up health + %player = %client.player; + if (!isObject(%player)) + return; + + %damage = %player.getDamagePercent(); + %healthRad = %damage * 100; + %closestHealth = -1; + %closestHealthDist = %healthRad; + %closestHealthLOS = false; + %closestItem = -1; + %closestItemDist = 32767; + %closestItemLOS = false; + + //loop through the item list, looking for things to pick up + %itemCount = $AIItemSet.getCount(); + for (%i = 0; %i < %itemCount; %i++) + { + %item = $AIItemSet.getObject(%i); + if (!%item.isHidden()) + { + %dist = %client.getPathDistance(%item.getWorldBoxCenter()); + if (((%item.getDataBlock().getName() $= "RepairKit" || %item.getDataBlock().getName() $= "RepairPatch") || + (%item.isCorpse && %item.getInventory("RepairKit") > 0)) && + %player.getInventory("RepairKit") <= 0 && %damage > 0.3) + { + if (%dist > 0 && %dist < %closestHealthDist) + { + %closestHealth = %item; + %closestHealthDist = %dist; + + //check for LOS + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType; + %closestHealthLOS = !containerRayCast(%client.player.getWorldBoxCenter(), %item.getWorldBoxCenter(), %mask, 0); + } + } + else + { + //only pick up stuff within 35m + if (%dist < 35) + { + if (AICouldUseItem(%client, %item)) + { + if (%dist < %closestItemDist) + { + %closestItem = %item; + %closestItemDist = %dist; + + //check for LOS + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType; + %closestItemLOS = !containerRayCast(%client.player.getWorldBoxCenter(), %item.getWorldBoxCenter(), %mask, 0); + } + } + } + } + } + } + + //now set the weight + if (%closestHealth > 0) + { + //only choose an item if it's at least 25 m closer than health... + //and we're not engageing someone or not that badly in need + %currentTarget = %client.getEngageTarget(); + if (%closestItem > 0 && %closetItemDist < %closestHealthDist - 25 && (%damage < 0.6 || %currentTarget <= 0) && %closestItemLOS) + { + %task.pickupItem = %closestItem; + if (AIEngageWeaponRating(%client) < 20) + %task.setWeight($AIWeightNeedItemBadly); + else if (%closestItemDist < 10 && %closestItemLOS) + %task.setWeight($AIWeightNeedItem); + else if (%closestItemLOS) + %task.setWeight($AIWeightFoundItem); + else + %task.setWeight(0); + } + else + { + if (%damage > 0.8) + { + %task.pickupItem = %closestHealth; + %task.setWeight($AIWeightNeedItemBadly); + } + else if (%closestHealthLOS) + { + %task.pickupItem = %closestHealth; + %task.setWeight($AIWeightNeedItem); + } + else + %task.setWeight(0); + } + } + else if (%closestItem > 0) + { + %task.pickupItem = %closestItem; + if (AIEngageWeaponRating(%client) < 20) + %task.setWeight($AIWeightNeedItemBadly); + else if (%closestItemDist < 10 && %closestItemLOS) + %task.setWeight($AIWeightNeedItem); + else if (%closestItemLOS) + %task.setWeight($AIWeightFoundItem); + else + %task.setWeight(0); + } + else + %task.setWeight(0); +} + +function AIPickupItemTask::monitor(%task, %client) +{ + //move to the pickup location + if (isObject(%task.pickupItem)) + %client.stepMove(%task.pickupItem.getWorldBoxCenter(), 0.25); + + //this call works in conjunction with AIEngageTask + %client.setEngageTarget(%client.shouldEngage); +} + +//----------------------------------------------------------------------------- +//AIUseInventoryTask will cause them to use an inv station if they're low +//on ammo. This task should be used only for DM and Hunters - most objectives +//have their own logic for when to use an inv station... + +function AIUseInventoryTask::init(%task, %client) +{ +} + +function AIUseInventoryTask::assume(%task, %client) +{ + %task.setWeightFreq(15); + %task.setMonitorFreq(5); + + //mark the current time for the buy inventory state machine + %task.buyInvTime = getSimTime(); +} + +function AIUseInventoryTask::retire(%task, %client) +{ + //reset the state machine time stamp... + %task.buyInvTime = getSimTime(); +} + +function AIUseInventoryTask::weight(%task, %client) +{ + //first, see if we can pick up health + %player = %client.player; + if (!isObject(%player)) + return; + + %damage = %player.getDamagePercent(); + %weaponry = AIEngageWeaponRating(%client); + + //if there's an inv station, and we haven't used an inv station since we + //spawned, the bot should use an inv once regardless + if (%client.spawnUseInv) + { + //see if we're already heading there + if (%client.buyInvTime != %task.buyInvTime) + { + //see if there's an inventory we can use + %result = AIFindClosestInventory(%client, false); + %closestInv = getWord(%result, 0); + if (isObject(%closestInv)) + { + %task.setWeight($AIWeightNeedItem); + return; + } + else + %client.spawnUseInv = false; + } + else + { + %task.setWeight($AIWeightNeedItem); + return; + } + } + + //first, see if we need equipment or health + if (%damage < 0.3 && %weaponry >= 40) + { + %task.setWeight(0); + return; + } + + //don't use inv stations if we're not that badly damaged, and we're in the middle of a fight + if (%damage < 0.6 && %client.getEngageTarget() > 0 && !AIEngageOutOfAmmo(%client)) + { + %task.setWeight(0); + return; + } + + //if we're already buying, continue + if (%task.buyInvTime == %client.buyInvTime) + { + //set the weight - if our damage is above 0.8 or our we're out of ammo + if (%damage > 0.8 || AIEngageOutOfAmmo(%client)) + %task.setWeight($AIWeightNeedItemBadly); + else + %task.setWeight($AIWeightNeedItem); + return; + } + + //we need to search for an inv station near us... + %result = AIFindClosestInventory(%client, false); + %closestInv = getWord(%result, 0); + %closestDist = getWord(%result, 1); + + //only use inv stations if we're right near them... patrolTask will get us nearer if required + if (%closestDist > 35) + { + %task.setWeight(0); + return; + } + + //set the weight... + %task.closestInv = %closestInv; + if (%damage > 0.8 || AIEngageOutOfAmmo(%client)) + %task.setWeight($AIWeightNeedItemBadly); + else if (%closestDist < 20 && (AIEngageWeaponRating(%client) <= 30 || %damage > 0.4)) + %task.setWeight($AIWeightNeedItem); + else if (%hasLOS) + %task.setWeight($AIWeightFoundItem); + else + %task.setWeight(0); +} + +function AIUseInventoryTask::monitor(%task, %client) +{ + //make sure we still need equipment + %player = %client.player; + if (!isObject(%player)) + return; + + %damage = %player.getDamagePercent(); + %weaponry = AIEngageWeaponRating(%client); + if (%damage < 0.3 && %weaponry >= 40 && !%client.spawnUseInv) + { + %task.buyInvTime = getSimTime(); + return; + } + + //pick a random set based on armor... + %randNum = getRandom(); + if (%randNum < 0.4) + %buySet = "LightEnergyDefault MediumEnergySet HeavyEnergySet"; + else if (%randNum < 0.6) + %buySet = "LightShieldSet MediumShieldSet HeavyShieldSet"; + else if (%randNum < 0.8) + %buySet = "LightEnergyELF MediumRepairSet HeavyAmmoSet"; + else + %buySet = "LightEnergySniper MediumEnergySet HeavyEnergySet"; + + //process the inv buying state machine + %result = AIBuyInventory(%client, "", %buySet, %task.buyInvTime); + + //if we succeeded, reset the spawn flag + if (%result $= "Finished") + %client.spawnUseInv = false; + + //if we succeeded or failed, reset the state machine... + if (%result !$= "InProgress") + %task.buyInvTime = getSimTime(); + + + //this call works in conjunction with AIEngageTask + %client.setEngageTarget(%client.shouldEngage); +} + +//----------------------------------------------------------------------------- +//AITauntCorpseTask is should happen only after an enemy is freshly killed + +function AITauntCorpseTask::init(%task, %client) +{ +} + +function AITauntCorpseTask::assume(%task, %client) +{ + %task.setWeightFreq(11); + %task.setMonitorFreq(11); +} + +function AITauntCorpseTask::retire(%task, %client) +{ +} + +function AITauntCorpseTask::weight(%task, %client) +{ + %task.corpse = %client.getVictimCorpse(); + if (%task.corpse > 0 && getSimTime() - %client.getVictimTime() < 15000) + { + //see if we're already taunting, and if it's time to stop + if ((%task.tauntTime > %client.getVictimTime()) && (getSimTime() - %task.tauntTime > 5000)) + %task.corpse = -1; + else + { + //if the corpse is within 50m, taunt + %distToCorpse = %client.getPathDistance(%task.corpse.getWorldBoxCenter()); + if (%dist < 0 || %distToCorpse > 50) + %task.corpse = -1; + } + } + else + %task.corpse = -1; + + //set the weight + if (%task.corpse > 0) + { + //don't taunt someone if there's an enemy right near by... + %result = AIFindClosestEnemy(%client, 40, 15000); + %closestEnemy = getWord(%result, 0); + %closestdist = getWord(%result, 1); + if (%closestEnemy > 0) + %task.setWeight(0); + else + %task.setWeight($AIWeightTauntVictim); + } + else + %task.setWeight(0); +} + +$AITauntChat[0] = "gbl.aww"; +$AITauntChat[1] = "gbl.brag"; +$AITauntChat[2] = "gbl.obnoxious"; +$AITauntChat[3] = "gbl.sarcasm"; +$AITauntChat[4] = "gbl.when"; +function AITauntCorpseTask::monitor(%task, %client) +{ + //make sure we still have a corpse, and are not fighting anyone + if (%client.getEngageTarget() <= 0 && %task.corpse > 0 && isObject(%task.corpse)) + { + %clientPos = %client.player.getWorldBoxCenter(); + %corpsePos = %task.corpse.getWorldBoxCenter(); + %distToCorpse = VectorDist(%clientPos, %corpsePos); + if (%distToCorpse < 2.0) + { + //start the taunt! + if (%task.tauntTime < %client.getVictimTime()) + { + %task.tauntTime = getSimTime(); + %client.stop(); + + if (getRandom() > 0.2) + { + //pick the sound and taunt cels + %sound = $AITauntChat[mFloor(getRandom() * 3.99)]; + %minCel = 2; + %maxCel = 8; + schedule(250, %client, "AIPlayAnimSound", %client, %corpsePos, %sound, %minCel, %maxCel, 0); + } + //say 'bye' :) + else + schedule(250, %client, "AIPlayAnimSound", %client, %corpsePos, "gbl.bye", 2, 2, 0); + } + } + else + %client.stepMove(%task.corpse.getWorldBoxCenter(), 1.75); + } +} + +//----------------------------------------------------------------------------- +//AIPatrolTask used to wander around the map (DM and Hunters mainly) looking for something to do... + +function AIPatrolTask::init(%task, %client) +{ +} + +function AIPatrolTask::assume(%task, %client) +{ + %task.setWeightFreq(13); + %task.setMonitorFreq(13); + %task.findLocation = true; + %task.patrolLocation = "0 0 0"; + %task.idleing = false; + %task.idleEndTime = 0; +} + +function AIPatrolTask::retire(%task, %client) +{ +} + +function AIPatrolTask::weight(%task, %client) +{ + %task.setWeight($AIWeightPatrolling); +} + +function AIPatrolTask::monitor(%task, %client) +{ + //this call works in conjunction with AIEngageTask + %client.setEngageTarget(%client.shouldEngage); + + //see if we're close enough to our patrol point + if (%task.idleing) + { + if (getSimTime() > %task.idleEndTime) + { + %task.findLocation = true; + %task.idleing = false; + } + } + + //see if we need to find a place to go... + else if (%task.findLocation) + { + //first, see if we're in need of either health, or ammo + //note: normally, I'd be tempted to put this kind of "looking for health" code + //into the AIPickupItemTask, however, that task will be used in CTF, where you + //don't want people on AIDefendLocation to leave their post to hunt for health, etc... + //AIPickupItemTask only deals with items within a 30m radius around the bot. + //AIPatrolTask will move the bot to the vicinity of an item, then AIPickUpItemTask + //will finish the job... + %foundItemLocation = false; + %damage = %client.player.getDamagePercent(); + if (%damage > 0.7) + { + //search for a health kit + %closestHealth = AIFindSafeItem(%client, "Health"); + if (%closestHealth > 0) + { + %task.patrolLocation = %closestHealth.getWorldBoxCenter(); + %foundItemLocation = true; + } + } + else if (AIEngageOutOfAmmo(%client)) + { + //search for a Ammo or a weapon... + %closestItem = AIFindSafeItem(%client, "Ammo"); + if (%closestItem > 0) + { + %task.patrolLocation = %closestItem.getWorldBoxCenter(); + %foundItemLocation = true; + } + } + + //now see if we don't really have good equipment... + if (!%foundItemLocation && AIEngageWeaponRating(%client) < 20) + { + //search for any useful item + %closestItem = AIFindSafeItem(%client, "Any"); + if (%closestItem > 0) + { + %task.patrolLocation = %closestItem.getWorldBoxCenter(); + %foundItemLocation = true; + } + } + //choose a randomish location only if we're not in need of health or ammo + if (!%foundItemLocation) + { + //find a random item/inventory in the map, and pick a spawn point near it... + %pickGraphNode = false; + %chooseSet = 0; + if ($AIInvStationSet.getCount() > 0) + %chooseSet = $AIInvStationSet; + else if ($AIWeaponSet.getCount() > 0) + %chooseSet = $AIWeaponSet; + else if ($AIItemSet.getCount() > 0) + %chooseSet = $AIItemSet; + + if (!%chooseSet) + %pickGraphNode = true; + + //here we pick whether we choose a random map point, or a point based on an item... + if (getRandom() < 0.3) + %pickGraphNode = true; + + //here we decide whether we should choose a player location... a bit of a cheat but + //it's scaled by the bot skill level + %pickPlayerLocation = false; + %skill = %client.getSkillLevel(); + if (%skill < 1.0) + %skill = %skill / 2.0; + if (getRandom() < (%skill * %skill) && ClientGroup.getCount() > 1) + { + //find a random client + %count = ClientGroup.getCount(); + %index = (getRandom() * (%count - 0.1)); + %cl = ClientGroup.getObject(%index); + if (%cl != %client && AIClientIsAlive(%cl)) + { + %task.patrolLocation = %cl.player.getWorldBoxCenter(); + %pickGraphNode = false; + %pickPlayerLocation = true; + } + } + + if (!%pickGraphNode && !%pickPlayerLocation) + { + %itemCount = %chooseSet.getCount(); + %item = %chooseSet.getObject(getRandom() * (%itemCount - 0.1)); + %nodeIndex = navGraph.randNode(%item.getWorldBoxCenter(), 10, true, true); + if (%nodeIndex <= 0) + %pickGraphNode = true; + else + %task.patrolLocation = navGraph.randNodeLoc(%nodeIndex); + } + + //see if we failed above or have to pick just a random spot on the graph - use the spawn points... + if (%pickGraphNode) + { + %task.patrolLocation = Game.pickPlayerSpawn(%client, true); + if (%task.patrolLocation == -1) + { + %client.stepIdle(%client.player.getWorldBoxCenter()); + return; + } + } + } + + //now that we have a new location - move towards it + %task.findLocation = false; + %client.stepMove(%task.patrolLocation, 8.0); + } + + //else we're on patrol - see if we're close to our destination + else + { + %client.stepMove(%task.patrolLocation, 8.0); + %distToDest = %client.getPathDistance(%task.patrolLocation); + if (%distToDest > 0 && %distToDest < 10) + { + %task.idleing = true; + %task.idleEndTime = 4000 + getSimTime() + (getRandom() * 6000); + %client.stepIdle(%client.player.getWorldBoxCenter()); + } + } +} + +//----------------------------------------------------------------------------- +//AIEngageTurretTask is responsible for returning turret fire... + +function AIEngageTurretTask::init(%task, %client) +{ +} + +function AIEngageTurretTask::assume(%task, %client) +{ + %task.setWeightFreq(4); + %task.setMonitorFreq(4); +} + +function AIEngageTurretTask::retire(%task, %client) +{ + %client.engageTurret = -1; + %client.setEngagetarget(-1); +} + +function AIEngageTurretTask::weight(%task, %client) +{ + //see if we're still fighting a turret + %elapsedTime = getSimTime() - %task.startAttackTime; + if (isObject(%task.engageTurret) && %task.engageTurret.getDataBlock().getClassName() $= "TurretData") + { + if (%task.engageTurret == %client.lastdamageTurret) + { + if (%task.engageTurret.isEnabled() && getSimTime() - %client.lastDamageTurretTime < 5000) + %task.setWeight($AIWeightReturnTurretFire); + else + %task.setWeight($AIWeightDestroyTurret); + } + else if (AIClientIsAlive(%client, %elapsedTime)) + { + //if another turret is shooting us, disable this one first... + if (isObject(%client.lastDamageTurret) && %client.lastDamageTurret.getDataBlock().getClassName() $= "TurretData") + { + if (%task.engageTurret.isEnabled()) + %task.setWeight($AIWeightReturnTurretFire); + else + { + //see if we need to switch to the new turret + if (%client.lastDamageTurret.isEnabled() && %client.lastDamageTurretTime < 5000) + { + %task.engageTurret = %client.lastDamageTurret; + %task.attackInitted = false; + %task.setWeight($AIWeightDestroyTurret); + } + + else + %task.setWeight($AIWeightReturnTurretFire); + } + } + else + { + if (%task.engageTurret.isEnabled() && getSimTime() - %client.lastDamageTurretTime < 5000) + %task.setWeight($AIWeightReturnTurretFire); + else + %task.setWeight($AIWeightDestroyTurret); + } + } + + //else we died since - clear out the vars + else + { + %task.engageTurret = -1; + %task.setWeight(0); + } + } + + //else see if we have a new target + else if (isObject(%client.lastDamageTurret) && %client.lastDamageTurret.getDataBlock().getClassName() $= "TurretData") + { + %task.engageTurret = %client.lastDamageTurret; + %task.attackInitted = false; + + if (%client.lastDamageTurret.isEnabled() && %client.lastDamageTurretTime < 5000) + %task.setWeight($AIWeightReturnTurretFire); + else + %task.setWeight($AIWeightDestroyTurret); + } + + //else no turret to attack... (later, do a query to find turrets before they attack) + else + { + %task.engageTurret = -1; + %task.setWeight(0); + } + +} + +function AIEngageTurretTask::monitor(%task, %client) +{ + if (isObject(%task.engageTurret) && %task.engageTurret.getDataBlock().getClassName() $= "TurretData") + { + //set the AI to fire at the turret + %client.setEngageTarget(-1); + %client.setTargetObject(%task.engageTurret); + + %clientPos = %client.player.getWorldBoxCenter(); + %turretPos = %task.engageTurret.getWorldBoxCenter(); + + //control the movement - first, hide, then wait, then attack + if (!%task.attackInitted) + { + %task.attackInitted = true; + %task.startAttackTime = getSimTime(); + %task.hideLocation = %client.getHideLocation(%turretPos, 40.0, %clientPos, 4.0); + %client.stepMove(%task.hideLocation, 2.0); + } + else if (getSimTime() - %task.startAttackTime > 5000) + { + %client.stepMove(%task.engageTurret.getWorldBoxCenter(), 8.0); + } + } +} + +//----------------------------------------------------------------------------- +//AIAvoidMineTask is responsible for detecting/destroying enemy mines... + +function AIDetectMineTask::init(%task, %client) +{ +} + +function AIDetectMineTask::assume(%task, %client) +{ + %task.setWeightFreq(7); + %task.setMonitorFreq(7); +} + +function AIDetectMineTask::retire(%task, %client) +{ + %task.engageMine = -1; + %task.attackInitted = false; + %client.setTargetObject(-1); +} + +function AIDetectMineTask::weight(%task, %client) +{ + //crappy hack, but they need the proper weapon before they can destroy a mine... + %player = %client.player; + if (!isObject(%player)) + return; + + %hasPlasma = (%player.getInventory("Plasma") > 0) && (%player.getInventory("PlasmaAmmo") > 0); + %hasDisc = (%player.getInventory("Disc") > 0) && (%player.getInventory("DiscAmmo") > 0); + + if (!%hasPlasma && !%hasDisc) + { + %task.setWeight(0); + return; + } + + //if we're already attacking a mine, + if (%task.engageMine > 0 && isObject(%task.engageMine)) + { + %task.setWeight($AIWeightDetectMine); + return; + } + //see if we're within the viscinity of a new (enemy) mine + %task.engageMine = -1; + %closestMine = -1; + %closestDist = 15; //initialize so only mines within 15 m will be detected... + %mineCount = $AIDeployedMineSet.getCount(); + for (%i = 0; %i < %mineCount; %i++) + { + %mine = $AIDeployedMineSet.getObject(%i); + %mineTeam = %mine.sourceObject.team; + + %minePos = %mine.getWorldBoxCenter(); + %clPos = %client.player.getWorldBoxCenter(); + + //see if the mine is the closest... + %mineDist = VectorDist(%minePos, %clPos); + if (%mineDist < %closestDist) + { + //now see if we're more or less heading towards the mine... + %clVelocity = %client.player.getVelocity(); + %clVelocity = getWord(%clVelocity, 0) SPC getWord(%clVelocity, 1) SPC "0"; + %mineVector = VectorSub(%minePos, %clPos); + %mineVector = getWord(%mineVector, 0) SPC getWord(%mineVector, 1) SPC "0"; + if (VectorLen(%clVelocity) > 2.0 && VectorLen(%mineVector > 2.0)) + { + %clNormal = VectorNormalize(%clVelocity); + %mineNormal = VectorNormalize(%mineVector); + if (VectorDot(%clNormal, %mineNormal) > 0.3) + { + %closestMine = %mine; + %closestDist = %mineDist; + } + } + } + } + + //see if we found a mine to attack + if (%closestMine > 0) + { + %task.engageMine = %closestMine; + %task.attackInitted = false; + %task.setWeight($AIWeightDetectMine); + } + else + %task.setWeight(0); +} + +function AIDetectMineTask::monitor(%task, %client) +{ + if (%task.engageMine > 0 && isObject(%task.engageMine)) + { + if (!%task.attackInitted) + { + %task.attackInitted = true; + %client.stepRangeObject(%task.engageMine, "DefaultRepairBeam", 6, 12); + %client.setEngageTarget(-1); + %client.setTargetObject(-1); + } + + else if (%client.getStepStatus() $= "Finished") + { + %client.stop(); + %client.setTargetObject(%task.engageMine); + } + } +} + +//----------------------------------------------------------------------------- diff --git a/Scripts/aiInventory.cs b/Scripts/aiInventory.cs new file mode 100644 index 0000000..ae50ea7 --- /dev/null +++ b/Scripts/aiInventory.cs @@ -0,0 +1,1677 @@ +//------------------------------ +//AI Inventory functions + +function AINeedEquipment(%equipmentList, %client) +{ + %index = 0; + %item = getWord(%equipmentList, %index); + + //first, see if we're testing the armor class as well... + if (%item $= "Heavy" || %item $= "Medium" || %item $= "Light" || %item $= "SpecOps") + { + if (%client.player.getArmorSize() !$= %item) + return true; + %index++; + %item = getWord(%equipmentList, %index); + } + + while (%item !$= "") + { + if (%client.player.getInventory(%item) == 0) + return true; + + //get the next item + %index++; + %item = getWord(%equipmentList, %index); + } + + //made it through the list without needing anything + return false; +} + +function AIBuyInventory(%client, %requiredEquipment, %equipmentSets, %buyInvTime) +{ + //make sure we have a live player + %player = %client.player; + if (!isObject(%player)) + return "Failed"; + + if (! AIClientIsAlive(%client)) + return "Failed"; + + //see if we've already initialized our state machine + if (%client.buyInvTime == %buyInvTime) + return AIProcessBuyInventory(%client); + + //if the closest inv station is not a remote, buy the first available set... + %result = AIFindClosestInventory(%client, false); + %closestInv = getWord(%result, 0); + %closestDist = getWord(%result, 1); + if (%closestInv <= 0) + return "Failed"; + + //see if the closest inv station was a remote + %buyingSet = false; + %usingRemote = false; + if (%closestInv.getDataBlock().getName() $= "DeployedStationInventory") + { + //see if we can buy at least the required equipment from the set + if (%requiredEquipment !$= "") + { + if (! AIMustUseRegularInvStation(%requiredEquipment, %client)) + %canUseRemote = true; + else + %canUseRemote = false; + } + else + { + %inventorySet = AIFindSameArmorEquipSet(%equipmentSets, %client); + if (%inventorySet !$= "") + %canUseRemote = true; + else + %canUseRemote = false; + } + + //if we can't use a remote, we need to look for a regular inv station + if (! %canUseRemote) + { + %result = AIFindClosestInventory(%client, true); + %closestInv = getWord(%result, 0); + %closestDist = getWord(%result, 1); + if (%closestInv <= 0) + return "Failed"; + } + else + %usingRemote = true; + } + + //at this point we've found the closest inv, see which set/list we need to buy + if (!%usingRemote) + { + //choose the equipment first equipment set + if (%equipmentSets !$= "") + { + %inventorySet = getWord(%equipmentSets, 0); + %buyingSet = true; + } + else + { + %inventorySet = %requiredEquipment; + %buyingSet = false; + } + } + else + { + %inventorySet = AIFindSameArmorEquipSet(%equipmentSets, %client); + if (%inventorySet $= "") + { + %inventorySet = %requiredEquipment; + %buyingSet = false; + } + else + %buyingSet = true; + } + + //init some vars for the state machine... + %client.buyInvTime = %buyInvTime; //used to mark the begining of the inv buy session + %client.invToUse = %closestInv; //used if we need to go to an alternate inv station + %client.invWaitTime = ""; //used to track how long we've been waiting + %client.invBuyList = %inventorySet; //the list/set of items we're going to buy... + %client.buyingSet = %buyingSet; //whether it's a list or a set... + %client.isSeekingInv = false; + %client.seekingInv = ""; + + //now process the state machine + return AIProcessBuyInventory(%client); +} + +function AIProcessBuyInventory(%client) +{ + //get some vars + %player = %client.player; + if (!isObject(%player)) + return "Failed"; + + %closestInv = %client.invToUse; + %inventorySet = %client.invBuyList; + %buyingSet = %client.buyingSet; + + //make sure it's still valid, enabled, and on our team + if (! (%closestInv > 0 && isObject(%closestInv) && + (%closestInv.team <= 0 || %closestInv.team == %client.team) && %closestInv.isEnabled())) + { + //reset the state machine + %client.buyInvTime = 0; + return "InProgress"; + } + + //make sure the inventory station is not blocked + %invLocation = %closestInv.getWorldBoxCenter(); + InitContainerRadiusSearch(%invLocation, 2, $TypeMasks::PlayerObjectType); + %objSrch = containerSearchNext(); + if (%objSrch == %client.player) + %objSrch = containerSearchNext(); + + //the closestInv is busy... + if (%objSrch > 0) + { + //have the AI range the inv + if (%client.seekingInv $= "" || %client.seekingInv != %closestInv) + { + %client.invWaitTime = ""; + %client.seekingInv = %closestInv; + %client.stepRangeObject(%closestInv, "DefaultRepairBeam", 5, 10); + } + + //inv is still busy - see if we're within range + else if (%client.getStepStatus() $= "Finished") + { + //initialize the wait time + if (%client.invWaitTime $= "") + %client.invWaitTime = getSimTime() + 5000 + (getRandom() * 10000); + + //else see if we've waited long enough + else if (getSimTime() > %client.invWaitTime) + { + schedule(250, %client, "AIPlayAnimSound", %client, %objSrch.getWorldBoxCenter(), "vqk.move", -1, -1, 0); + %client.invWaitTime = getSimTime() + 5000 + (getRandom() * 10000); + } + } + else + { + //in case we got bumped, and are ranging the target again... + %client.invWaitTime = ""; + } + } + + //else if we've triggered the inv, automatically give us the equipment... + else if (isObject(%closestInv) && isObject(%closestInv.trigger) && VectorDist(%closestInv.trigger.getWorldBoxCenter(), %player.getWorldBoxCenter()) < 1.5) + { + //first stop... + %client.stop(); + + %index = 0; + if (%buyingSet) + { + //first, clear the players inventory + %player.clearInventory(); + %item = $AIEquipmentSet[%inventorySet, %index]; + } + else + %item = getWord(%inventorySet, %index); + + + //armor must always be bought first + if (%item $= "Light" || %item $= "Medium" || %item $= "Heavy" || %item $= "SpecOps") + { + %player.setArmor(%item); + %index++; + } + + //set the data block after the armor had been upgraded + %playerDataBlock = %player.getDataBlock(); + + //next, loop through the inventory set, and buy each item + if (%buyingSet) + %item = $AIEquipmentSet[%inventorySet, %index]; + else + %item = getWord(%inventorySet, %index); + while (%item !$= "") + { + //set the inventory amount to the maximum quantity available + if (%player.getInventory(AmmoPack) > 0) + %ammoPackQuantity = AmmoPack.max[%item]; + else + %ammoPackQuantity = 0; + + %quantity = %player.getDataBlock().max[%item] + %ammoPackQuantity; + if ($InvBanList[$CurrentMissionType, %item]) + %quantity = 0; + %player.setInventory(%item, %quantity); + + //get the next item + %index++; + if (%buyingSet) + %item = $AIEquipmentSet[%inventorySet, %index]; + else + %item = getWord(%inventorySet, %index); + } + + //put a weapon in the bot's hand... + %player.cycleWeapon(); + + //return a success + return "Finished"; + } + + //else, keep moving towards the inv station + else + { + if (isObject(%closestInv) && isObject(%closestInv.trigger)) + { + //quite possibly we may need to deal with what happens if a bot doesn't have a path to the inv... + //the current premise is that no inventory stations are "unpathable"... + //if (%client.isSeekingInv) + //{ + // %dist = %client.getPathDistance(%closestInv.trigger.getWorldBoxCenter()); + // if (%dist < 0) + // error("DEBUG Tinman - still need to handle bot stuck trying to get to an inv!"); + //} + + %client.stepMove(%closestInv.trigger.getWorldBoxCenter(), 1.5); + %client.isSeekingInv = true; + } + return "InProgress"; + } +} + +function AIFindSameArmorEquipSet(%equipmentSets, %client) +{ + %clientArmor = %client.player.getArmorSize(); + %index = 0; + %set = getWord(%equipmentSets, %index); + while (%set !$= "") + { + if ($AIEquipmentSet[%set, 0] $= %clientArmor) + return %set; + + //get the next equipment set in the list of sets + %index++; + %set = getWord(%equipmentSets, %index); + } + return ""; +} + +function AIMustUseRegularInvStation(%equipmentList, %client) +{ + %clientArmor = %client.player.getArmorSize(); + + //first, see if the set contains an item not available + %needRemoteInv = false; + %index = 0; + %item = getWord(%equipmentList, 0); + while (%item !$= "") + { + if (%item $= "InventoryDeployable" || (%clientArmor $= "Light" && (%item $= "HRPChaingun" || %item $= "AALauncher" || %item $= "MG42" || %item $= "Bazooka" || %item $="RShotgun")) ||(%clientArmor $= "SpecOps" && (%item $= "RPChaingun" || %item $= "HRPChaingun" || %item $= "AALauncher" || %item $= "MG42" || %item $= "Bazooka" || %item $="RShotgun" || %item $= "Shotgun" || %item $= "Flamer")) ||(%clientArmor $= "Medium" && (%item $= "RPChaingun" || %item $= "LMissileLauncher" || %item $= "MG42" || %item $="RShotgun")) ||(%clientArmor $= "Heavy" && (%item $= "HRPChaingun" || %item $= "LSMG" || %item $= "snipergun" || %item $= "flamer" || %item $= "KreigRifle" || %item $= "LMissileLauncher"))) + { + return true; + } + else + { + %index++; + %item = getWord(%equipmentList, %index); + } + } + if (%needRemoteInv) + return true; + + + //otherwise, see if the set begins with an armor class + %needArmor = %equipmentList[0]; + if (%needArmor !$= "Light" && %needArmor !$= "Medium" && %needArmor !$= "Heavy") + return false; + + //also including looking for an inventory set + if (%needArmor != %client.player.getArmorSize()) + return true; + + //we must be fine... + return false; +} + +function AICouldUseItem(%client, %item) +{ + if(!AIClientIsAlive(%client)) + return false; + + %player = %client.player; + if (!isObject(%player)) + return false; + + %playerDataBlock = %client.player.getDataBlock(); + %armor = %player.getArmorSize(); + %type = %item.getDataBlock().getName(); + + //check packs first + if (%type $= "RepairPack" || %type $= "AmmoPack") + { + if (%client.player.getMountedImage($BackpackSlot) <= 0) + return true; + else + return false; + } + + //if the item is acutally, a corpse, check the corpse inventory... + if (%item.isCorpse) + { + %corpse = %item; + if (%corpse.getInventory("MGClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[MGClip]) + return true; + if (%corpse.getInventory("LSMGClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[LSMGClip]) + return true; + if (%corpse.getInventory("snipergunAmmo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[snipergunAmmo]) + return true; + if (%corpse.getInventory("bazookaAmmo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[bazookaAmmo]) + return true; + if (%corpse.getInventory("MG42Ammo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[MG42Ammo]) + return true; + if (%corpse.getInventory("PistolClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[PistolClip]) + return true; + if (%corpse.getInventory("AALauncherAmmo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[AALauncherAmmo]) + return true; + if (%corpse.getInventory("RifleClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[RifleClip]) + return true; + if (%corpse.getInventory("ShotgunClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[ShotgunClip]) + return true; + if (%corpse.getInventory("RShotgunClip") > 0 && %player.getInventory(%type) < %playerDataBlock.max[RShotgunClip]) + return true; + if (%corpse.getInventory("RPGAmmo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[RPGAmmo]) + return true; + if (%corpse.getInventory("LMissileLauncherAmmo") > 0 && %player.getInventory(%type) < %playerDataBlock.max[LMissileLauncherAmmo]) + return true; + } + else + { + //check ammo + %quantity = mFloor(%playerDataBlock.max[%type]); + if (%player.getInventory(%type) < %quantity) + { + if (%type $= "MGClip") + return true; + if (%type $= "LSMGClip") + return true; + if (%type $= "snipergunAmmo") + return true; + if (%type $= "bazookaAmmo") + return true; + if (%type $= "MG42Ammo") + return true; + if (%type $= "PistolClip") + return true; + if (%type $= "AALauncherAmmo") + return true; + if (%type $= "RifleClip") + return true; + if (%type $= "ShotgunClip") + return true; + if (%type $= "RShotgunClip") + return true; + if (%type $= "RPGAmmo") + return true; + if (%type $= "LMissileLauncherAmmo") + return true; + + //check mines and grenades as well + if (%type $= "Grenade" || %type $= "FlashGrenade" || %type $= "ConcussionGrenade") + return true; + } + + //see if we can carry another weapon... + if (AICanPickupWeapon(%client, %type)) + return true; + } + + //guess we didn't find anything useful... (should still check for mines and grenades) + return false; +} + +function AIEngageOutofAmmo(%client) +{ + //this function only cares about weapons used in engagement... + //no mortars, or missiles + %player = %client.player; + if (!isObject(%player)) + return false; + + %ammoWeapons = 0; + %energyWeapons = 0; + + //get our inventory + %hasMG = (%player.getInventory("RPChaingun") > 0); + %hasLMG = (%player.getInventory("LSMG") > 0); + %hasHMG = (%player.getInventory("HRPChaingun") > 0); + %hasMG42 = (%player.getInventory("MG42") > 0); + %hasSniper = (%player.getInventory("snipergun") > 0); + %hasRifle = (%player.getInventory("KriegRifle") > 0); + %hasAT6 = (%player.getInventory("LMissileLauncher") > 0); + %hasPistol = (%player.getInventory("Pistol") > 0); + %hasSPistol = (%player.getInventory("SPistol") > 0); + %hasBazooka = (%player.getInventory("Bazooka") > 0); + %hasFlamer = (%player.getInventory("flamer") > 0); + %hasAAMissile = (%player.getInventory("AALauncher") > 0); + %hasShotgun = (%player.getInventory("Shotgun") > 0); + %hasRShotgun = (%player.getInventory("RShotgun") > 0); + + if(%hasMG && (%player.getInventory("RPChaingunAmmo") > 0) && (%player.getInventory("MGClip") > 0)) + return false; + else if(%hasHMG && (%player.getInventory("RPChaingunAmmo") > 0) && (%player.getInventory("MGClip") > 0)) + return false; + else if(%hasLMG && (%player.getInventory("LSMGAmmo") > 0) && (%player.getInventory("LSMGClip") > 0)) + return false; + else if(%hasRifle && (%player.getInventory("KreigAmmo") > 0) && (%player.getInventory("RifleClip") > 0)) + return false; + else if(%hasPistol && (%player.getInventory("PistolAmmo") > 0) && (%player.getInventory("PistolClip") > 0)) + return false; + else if(%hasSPistol && (%player.getInventory("PistolAmmo") > 0) && (%player.getInventory("PistolClip") > 0)) + return false; + else if(%hasShotgun && (%player.getInventory("ShotgunAmmo") > 0) && (%player.getInventory("ShotgunClip") > 0)) + return false; + else if(%hasRShotgun && (%player.getInventory("RShotgunAmmo") > 0) && (%player.getInventory("RShotgunClip") > 0)) + return false; + else if(%hasMG42 && (%player.getInventory("MG42Ammo") > 0)) + return false; + else if(%hasSniper && (%player.getInventory("snipergunAmmo") > 0)) + return false; + else if(%hasBazooka && (%player.getInventory("BazookaAmmo") > 0)) + return false; + else if(%hasFlamer && (%player.getInventory("FlamerAmmo") > 0)) + return false; + else if(%hasAAMissile && (%player.getInventory("AALauncherAmmo") > 0)) + return false; + else if(%hasAT6 && (%player.getInventory("LMissileLauncherAmmo") > 0)) + return false; + return true; // were out! +} + +function AICanPickupWeapon(%client, %weapon) +{ + //first, make sure it's not a weapon we already have... + %player = %client.player; + if (!isObject(%player)) + return false; + + %armor = %player.getArmorSize(); + if (%player.getInventory(%weapon) > 0) + return false; + + //make sure the %weapon given is a weapon they can use for engagement + if (%weapon !$= "RPChaingun" && %weapon !$= "HRPChaingun" && %weapon !$= "Bazooka" && %weapon !$= "Snipergun" && %weapon !$= "Shotgun" && %weapon !$= "RShotgun" && %weapon !$= "KreigRifle" && %weapon !$= "AALauncher"&& %weapon !$= "LMissileLauncher" && %weapon !$= "LSMG" && %weapon !$= "MG42" && %weapon !$= "Flamer" ) + { + return false; + } + + %weaponCount = 0; + if (%player.getInventory("RPChaingun") > 0) + %weaponCount++; + if (%player.getInventory("HRPChaingun") > 0) + %weaponCount++; + if (%player.getInventory("Bazooka") > 0) + %weaponCount++; + if (%player.getInventory("Snipergun") > 0) + %weaponCount++; + if (%player.getInventory("Shotgun") > 0) + %weaponCount++; + if (%player.getInventory("RShotgun") > 0) + %weaponCount++; + if (%player.getInventory("KreigRifle") > 0) + %weaponCount++; + if (%player.getInventory("AALauncher") > 0) + %weaponCount++; + if (%player.getInventory("LMissileLauncher") > 0) + %weaponCount++; + if (%player.getInventory("LSMG") > 0) + %weaponCount++; + if (%player.getInventory("MG42") > 0) + %weaponCount++; + if (%player.getInventory("Flamer") > 0) + %weaponCount++; + + if ((%armor $= "Light" && %weaponCount < 1) || (%armor $= "Medium" && %weaponCount < 2) || (%armor $= "Heavy" && %weaponCount < 3) || (%armor $= "SpecOps" && %weaponCount <2)) + { + if ((%type $= "RShotgun" && %armor !$= "Heavy") || (%type $= "RPChaingun" && (%armor $= "Medium" || %armor $= "SpecOps")) ||(%type $= "HRPChaingun" && %armor !$= "Medium") || (%type $= "LSMG" && %armor $= "Heavy") || (%type $= "Flamer" && (%armor !$= "Light" && %armor !$= "Medium")) || (%type $= "MG42" && %armor !$= "Heavy") || (%type $= "Bazooka" && (%armor $= "Light" || %armor $= "SpecOps")) || (%type $= "AALauncher" && (%armor $= "Light" || %armor $= "SpecOps")) || (%type $= "Shotgun" && (%armor $= "Heavy" || %armor $= "SpecOps")) || (%type $= "LMissileLauncher" && (%armor $= "Heavy" || %armor $= "Medium")) || (%type $= "KreigRifle" && %armor $= "Heavy") || (%type $= "SniperGun" && %armor $= "Heavy") ) + return false; + else + return true; + } + + //else we're full of weapons already... + return false; +} + +function AIEngageWeaponRating(%client) +{ + %player = %client.player; + if (!isObject(%player)) + return; + + %playerDataBlock = %client.player.getDataBlock(); + + //get our inventory + %hasMG = (%player.getInventory("RPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasHMG = (%player.getInventory("HRPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasKreig = (%player.getInventory("KreigRifle") > 0 && ((%player.getInventory("KreigAmmo") >= 1 && %player.getInventory("RifleClip") >= 1) || (%player.getInventory("KreigAmmo") >= 1 || %player.getInventory("RifleClip") >= 1))); + %hasLMG = (%player.getInventory("LSMG") > 0 && ((%player.getInventory("LSMGAmmo") >= 1 && %player.getInventory("LSMGClip") >= 1) || (%player.getInventory("LSMGAmmo") >= 1 || %player.getInventory("LSMGClip") >= 1))); + %hasShotgun = (%player.getInventory("Shotgun") > 0 && ((%player.getInventory("ShotgunAmmo") >= 1 && %player.getInventory("ShotgunClip") >= 1) || (%player.getInventory("ShotgunAmmo") >= 1 || %player.getInventory("ShotgunClip") >= 1))); + %hasRShotgun = (%player.getInventory("RShotgun") > 0 && ((%player.getInventory("RShotgunAmmo") >= 1 && %player.getInventory("RShotgunClip") >= 1) || (%player.getInventory("RShotgunAmmo") >= 1 || %player.getInventory("RShotgunClip") >= 1))); + %hasPistol = (%player.getInventory("Pistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1))); + %hasSPistol = (%player.getInventory("SPistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1))); + %hasFlamer = (%player.getInventory("Flamer") > 0 && %player.getInventory("FlamerAmmo") >= 1); + %hasSniper = (%player.getInventory("Snipergun") > 0 && %player.getInventory("SnipergunAmmo") >= 1); + %hasBazooka = (%player.getInventory("Bazooka") > 0 && %player.getInventory("BazookaAmmo") >= 1); + %hasAALauncher = (%player.getInventory("AALauncher") > 0 && %player.getInventory("AALauncherAmmo") >= 1); + %hasLMissileLauncher = (%player.getInventory("LMissileLauncher") > 0 && %player.getInventory("LMissileLauncherAmmo") >= 1); + %hasMG42 = (%player.getInventory("MG42") > 0 && %player.getInventory("MG42Ammo") >= 1); + + //check ammo + %quantity = mFloor(%playerDataBlock.max[%type] * 0.7); + + %rating = 0; + + if (%hasMG) + { + %quantity = (%player.getInventory("RPChaingunAmmo") + (%player.getInventory("MGClip") * 30) / (%playerDataBlock.max["RPChaingunAmmo"] * %playerDatablock.max["MGClip"])); + %rating += 20 + (20 * %quantity); + } + if (%hasHMG) + { + %quantity = (%player.getInventory("RPChaingunAmmo") + (%player.getInventory("MGClip") * 30) / (%playerDataBlock.max["RPChaingunAmmo"] * %playerDatablock.max["MGClip"])); + %rating += 20 + (20 * %quantity); + } + if (%hasKreig) + { + %quantity = (%player.getInventory("KreigAmmo") + (%player.getInventory("RifleClip") * 10) / (%playerDataBlock.max["KreigAmmo"] * %playerDatablock.max["RifleClip"])); + %rating += 15 + (15 * %quantity); + } + if (%hasLMG) + { + %quantity = (%player.getInventory("LSMGAmmo") + (%player.getInventory("LSMGClip") * 30) / (%playerDataBlock.max["LSMGAmmo"] * %playerDatablock.max["LSMGClip"])); + %rating += 15 + (15 * %quantity); + } + if (%hasShotgun) + { + %quantity = (%player.getInventory("ShotgunAmmo") + (%player.getInventory("ShotgunClip") * 5) / (%playerDataBlock.max["ShotgunAmmo"] * %playerDatablock.max["ShotgunClip"])); + %rating += 15 + (15 * %quantity); + } + if (%hasRShotgun) + { + %quantity = (%player.getInventory("RShotgunAmmo") + (%player.getInventory("RShotgunClip") * 25) / (%playerDataBlock.max["RShotgunAmmo"] * %playerDatablock.max["RShotgunClip"])); + %rating += 19 + (30 * %quantity); + } + if (%hasPistol) + { + %quantity = (%player.getInventory("PistolAmmo") + (%player.getInventory("PistolClip") * 10) / (%playerDataBlock.max["PistolAmmo"] * %playerDatablock.max["PistolClip"])); + %rating += 5 + (5 * %quantity); + } + if (%hasSPistol) + { + %quantity = (%player.getInventory("SPistolAmmo") + (%player.getInventory("SPistolClip") * 10) / (%playerDataBlock.max["SPistolAmmo"] * %playerDatablock.max["SPistolClip"])); + %rating += 7 + (7 * %quantity); + } + if (%hassniper) + { + %quantity = %player.getInventory("snipergunAmmo") / %playerDataBlock.max["snipergunammo"]; + %rating += 15 + (15 * %quantity); + } + if (%hasBazooka) + { + %quantity = %player.getInventory("BazookaAmmo") / %playerDataBlock.max["Bazookaammo"]; + %rating += 19 + (30 * %quantity); + } + if (%hasAALauncher) + { + %quantity = %player.getInventory("AALauncherAmmo") / %playerDataBlock.max["AALauncherammo"]; + %rating += 19 + (20 * %quantity); + } + if (%hasLMissileLauncher) + { + %quantity = %player.getInventory("LMissileLauncherAmmo") / %playerDataBlock.max["LMissileLauncherammo"]; + %rating += 15 + (15 * %quantity); + } + if (%hasMG42) + { + %quantity = %player.getInventory("MG42Ammo") / %playerDataBlock.max["MG42ammo"]; + %rating += 19 + (30 * %quantity); + } + + return %rating; +} + +function AIFindSafeItem(%client, %needType) +{ + %player = %client.player; + if (!isObject(%player)) + return -1; + + %closestItem = -1; + %closestDist = 32767; + + %itemCount = $AIItemSet.getCount(); + for (%i = 0; %i < %itemCount; %i++) + { + %item = $AIItemSet.getObject(%i); + if (%item.isHidden()) + continue; + + %type = %item.getDataBlock().getName(); + if ((%needType $= "Health" && (%type $= "RepairKit" || %type $= "RepairPatch") && %player.getDamagePercent() > 0) || + (%needType $= "Ammo" && (%type $= "MGClip" || %type $= "RifleClip" || %type $= "ShotgunClip" || %type $= "RShotgunClip" || %type $= "LSMClip" || %type $= "PistolClip" || %type $= "SnipergunAmmo" || %type $= "MG42Ammo" || %type $= "LMissileLauncherAmmo" || %type $= "AALauncherAmmo") && AICouldUseItem(%client, %item)) || + (%needType $= "Ammo" && AICanPickupWeapon(%type)) || + ((%needType $= "" || %needType $= "Any") && AICouldUseItem(%client, %item))) + { + //first, see if it's close to us... + %distance = %client.getPathDistance(%item.getTransform()); + if (%distance > 0 && %distance < %closestDist) + { + //now see if it's got bad enemies near it... + %clientCount = ClientGroup.getCount(); + for (%j = 0; %j < %clientCount; %j++) + { + %cl = ClientGroup.getObject(%j); + if (%cl == %client || %cl.team == %client.team || !AIClientIsAlive(%cl)) + continue; + + //if the enemy is stronger, see if they're close to the item + if (AIEngageWhoWillWin(%client, %cl) == %cl) + { + %tempDist = %client.getPathDistance(%item.getWorldBoxCenter()); + if (%tempDist > 0 && %tempDist < %distance + 50) + continue; + } + + //either no enemy, or a weaker one... + %closestItem = %item; + %closestDist = %distance; + } + } + } + } + + return %closestItem; +} + +function AIChooseObjectWeapon(%client, %targetObject, %distToTarg, %mode, %canUseEnergyStr, %environmentStr) +{ + //get our inventory + %player = %client.player; + if (!isObject(%player)) + return; + + if (!isObject(%targetObject)) + return; + + %canUseEnergy = (%canUseEnergyStr $= "true"); + %inWater = (%environmentStr $= "water"); + %hasMG = (%player.getInventory("RPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasHMG = (%player.getInventory("HRPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasKreig = (%player.getInventory("KreigRifle") > 0 && ((%player.getInventory("KreigAmmo") >= 1 && %player.getInventory("RifleClip") >= 1) || (%player.getInventory("KreigAmmo") >= 1 || %player.getInventory("RifleClip") >= 1)) && !%inwater); + %hasLMG = (%player.getInventory("LSMG") > 0 && ((%player.getInventory("LSMGAmmo") >= 1 && %player.getInventory("LSMGClip") >= 1) || (%player.getInventory("LSMGAmmo") >= 1 || %player.getInventory("LSMGClip") >= 1))); + %hasShotgun = (%player.getInventory("Shotgun") > 0 && ((%player.getInventory("ShotgunAmmo") >= 1 && %player.getInventory("ShotgunClip") >= 1) || (%player.getInventory("ShotgunAmmo") >= 1 || %player.getInventory("ShotgunClip") >= 1)) && !%inwater); + %hasRShotgun = (%player.getInventory("RShotgun") > 0 && ((%player.getInventory("RShotgunAmmo") >= 1 && %player.getInventory("RShotgunClip") >= 1) || (%player.getInventory("RShotgunAmmo") >= 1 || %player.getInventory("RShotgunClip") >= 1)) && !%inwater); + %hasPistol = (%player.getInventory("Pistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1)) && !%inwater); + %hasSPistol = (%player.getInventory("SPistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1)) && !%inwater); + %hasFlamer = (%player.getInventory("Flamer") > 0 && %player.getInventory("FlamerAmmo") >= 1 && !%inwater); + %hasSniper = (%player.getInventory("Snipergun") > 0 && %player.getInventory("SnipergunAmmo") >= 1 && !%inwater); + %hasBazooka = (%player.getInventory("Bazooka") > 0 && %player.getInventory("BazookaAmmo") >= 1); + %hasAALauncher = (%player.getInventory("AALauncher") > 0 && %player.getInventory("AALauncherAmmo") >= 1 && !%inwater); + %hasLMissileLauncher = (%player.getInventory("LMissileLauncher") > 0 && %player.getInventory("LMissileLauncherAmmo") >= 1 && !%inwater); + %hasMG42 = (%player.getInventory("MG42") > 0 && %player.getInventory("MG42Ammo") >= 1); + %hasTargetingLaser = (%player.getInventory("TargetingLaser") > 0); + %hasRepairPack = (%player.getInventory("RepairPack") > 0) && %canUseEnergy; + + //see if we're destroying the object + if (%mode $= "Destroy") + { + if ((%targetObject.getDataBlock().getClassName() $= "TurretData" || + %targetObject.getDataBlock().getName() $= "MineDeployed") && %distToTarg < 50) + { + if (%hasFlamer) + %useWeapon = "Flamer"; + else if (%hasBazooka) + %useWeapon = "Bazooka"; + else if (%hasLMissileLauncher) + %useWeapon = "LMissileLauncher"; + else + %useWeapon = "NoAmmo"; + } + else if (%distToTarg < 50) + { + if (%hasFlamer) + %useWeapon = "Flamer"; + else if (%hasBazooka) + %useWeapon = "Bazooka"; + else if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + else if (%hasLMG) + %useWeapon = "LSMG"; + else if (%hasMG42) + %useWeapon = "MG42"; + else if (%hasRShotgun) + %useWeapon = "RShotgun"; + else + %useWeapon = "NoAmmo"; + } + else + %useWeapon = "NoAmmo"; + } + + //else See if we're repairing the object + else if (%mode $= "Repair") + { + if (%hasRepairPack) + %useWeapon = "RepairPack"; + else + %useWeapon = "NoAmmo"; + } + + //else see if we're lazing the object + else if (%mode $= "Laze") + { + if (%hasTargetingLaser) + %useWeapon = "TargetingLaser"; + else + %useWeapon = "NoAmmo"; + } + + //else see if we're mortaring the object + else if (%mode $= "Mortar") + { + if (%hasBazooka) + %useWeapon = "Bazooka"; + else + %useWeapon = "NoAmmo"; + } + + //else see if we're rocketing the object + else if (%mode $= "Missile" || %mode $= "MissileNoLock") + { + if (%hasAALauncher) + %useWeapon = "AALauncher"; + else if (%hasLMissileLauncher) + %useWeapon = "LMissileLauncher"; + else + %useWeapon = "NoAmmo"; + } + + //now select the weapon + switch$ (%useWeapon) + { + case "RPChaingun": + %client.player.use("RPChaingun"); + %client.setWeaponInfo("RPChaingunBullet",1,100,30); + case "HRPChaingun": + %client.player.use("HRPChaingun"); + %client.setWeaponInfo("RPChaingunBullet",1,100,30); + case "Bazooka": + %client.player.use("Bazooka"); + %client.setWeaponInfo("Bazookashot",10,1000); + case "AALauncher": + %client.player.use("AALauncher"); + %client.setWeaponInfo("AAMissile",15,750); + case "LMissileLauncher": + %client.player.use("LMissileLauncher"); + %client.setWeaponInfo("LShoulderMissile",10,300); + case "LSMG": + %client.player.use("LSMG"); + %client.setWeaponInfo("LSMGBullet",1,30,30); + case "MG42": + %client.player.use("MG42"); + %client.setWeaponInfo("MG42Bullet",1,30,200); + case "RShotgun": + %client.player.use("RShotgun"); + %client.setWeaponInfo("ShotgunPellet",1,25); + case "Flamer": + %client.player.use("Flamer"); + %client.setWeaponInfo("FlameboltMain",1,30); + case "TargetingLaser": + %client.player.use("TargetingLaser"); + %client.setWeaponInfo("BasicTargeter",1,1000); + case "HRPChaingun": + %client.player.use("RepairPack"); + %client.setWeaponInfo("DefaultRepairBeam",1,100); + case "NoAmmo": + %client.setWeaponInfo("NoAmmo", 30, 75); + } +} + +function AIChooseEngageWeapon(%client, %targetClient, %distToTarg, %canUseEnergyStr, %environmentStr) +{ + //get some status + %player = %client.player; + if (!isObject(%player)) + return; + + %enemy = %targetClient.player; + if (!isObject(%enemy)) + return; + + %canUseEnergy = (%canUseEnergyStr $= "true"); + %inWater = (%environmentStr $= "water"); + %outdoors = (%environmentStr $= "outdoors"); + %targVelocity = %targetClient.player.getVelocity(); + %targEnergy = %targetClient.player.getEnergyPercent(); + %targDamage = %targetClient.player.getDamagePercent(); + %myEnergy = %player.getEnergyPercent(); + %myDamage = %player.getDamagePercent(); + + //get our inventory + %hasMG = (%player.getInventory("RPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasHMG = (%player.getInventory("HRPChaingun") > 0 && ((%player.getInventory("RPChaingunAmmo") >= 1 && %player.getInventory("MGClip") >= 1) || (%player.getInventory("RPChaingunAmmo") >= 1 || %player.getInventory("MGClip") >= 1))); + %hasKreig = (%player.getInventory("KreigRifle") > 0 && ((%player.getInventory("KreigAmmo") >= 1 && %player.getInventory("RifleClip") >= 1) || (%player.getInventory("KreigAmmo") >= 1 || %player.getInventory("RifleClip") >= 1)) && !%inwater); + %hasLMG = (%player.getInventory("LSMG") > 0 && ((%player.getInventory("LSMGAmmo") >= 1 && %player.getInventory("LSMGClip") >= 1) || (%player.getInventory("LSMGAmmo") >= 1 || %player.getInventory("LSMGClip") >= 1))); + %hasShotgun = (%player.getInventory("Shotgun") > 0 && ((%player.getInventory("ShotgunAmmo") >= 1 && %player.getInventory("ShotgunClip") >= 1) || (%player.getInventory("ShotgunAmmo") >= 1 || %player.getInventory("ShotgunClip") >= 1)) && !%inwater); + %hasRShotgun = (%player.getInventory("RShotgun") > 0 && ((%player.getInventory("RShotgunAmmo") >= 1 && %player.getInventory("RShotgunClip") >= 1) || (%player.getInventory("RShotgunAmmo") >= 1 || %player.getInventory("RShotgunClip") >= 1)) && !%inwater); + %hasPistol = (%player.getInventory("Pistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1)) && !%inwater); + %hasSPistol = (%player.getInventory("SPistol") > 0 && ((%player.getInventory("PistolAmmo") >= 1 && %player.getInventory("PistolClip") >= 1) || (%player.getInventory("PistolAmmo") >= 1 || %player.getInventory("PistolClip") >= 1)) && !%inwater); + %hasFlamer = (%player.getInventory("Flamer") > 0 && %player.getInventory("FlamerAmmo") >= 1 && !%inwater); + %hasSniper = (%player.getInventory("Snipergun") > 0 && %player.getInventory("SnipergunAmmo") >= 1 && !%inwater); + %hasBazooka = (%player.getInventory("Bazooka") > 0 && %player.getInventory("BazookaAmmo") >= 1); + %hasAALauncher = (%player.getInventory("AALauncher") > 0 && %player.getInventory("AALauncherAmmo") >= 1 && !%inwater); + %hasLMissileLauncher = (%player.getInventory("LMissileLauncher") > 0 && %player.getInventory("LMissileLauncherAmmo") >= 1 && !%inwater); + %hasMG42 = (%player.getInventory("MG42") > 0 && %player.getInventory("MG42Ammo") >= 1); + + //choose the weapon + %useWeapon = "NoAmmo"; + + //first, see if it's a pilot we're shooting + if (%dist > 50 && %enemy.isMounted()) + { + if(%hasAALauncher) + %useWeapon = "AALauncher"; + else if(%hasBazooka) + %useWeapon = "Bazooka"; + else if(%hasLMissileLauncher) + %useWeapon = "LMissileLauncher"; + else if(%hasMG42) + %useWeapon = "MG42"; + else if(%hasMG) + %useWeapon = "RPChaingun"; + else if(%hasHMG) + %useWeapon = "HRPChaingun"; + } + else if (%distToTarg < 30) + { + if (%hasRShotgun) + %useWeapon = "RShotgun"; + else if (%hasShotgun) + %useWeapon = "Shotgun"; + else if (%hasFlamer) + %useWeapon = "Flamer"; + else if (%hasMG42) + %useWeapon = "MG42"; + else if (%hasLSMG) + %useWeapon = "LSMG"; + else if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + else if (%hasSPistol) + %useWeapon = "SPistol"; + else if (%hasPistol) + %useWeapon = "Pistol"; + } + else if (%distToTarg < 80) + { + if (%hasMG42) + %useWeapon = "MG42"; + else if (%hasLSMG) + %useWeapon = "LSMG"; + else if (%hasRShotgun) + %useWeapon = "RShotgun"; + else if (%hasFlamer) + %useWeapon = "Flamer"; + else if (%hasShotgun) + %useWeapon = "Shotgun"; + else if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + else if (%hasSPistol) + %useWeapon = "SPistol"; + else if (%hasPistol) + %useWeapon = "Pistol"; + else if (%hasKreig) + %useWeapon = "KreigRifle"; + } + else if (%distToTarg < 150) + { + if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + else if (%hasKreig) + %useWeapon = "KreigRifle"; + else if (%hasMG42) + %useWeapon = "MG42"; + else if (%hasLSMG) + %useWeapon = "LSMG"; + else if (%hasSniper) + %useWeapon = "Snipergun"; + else if (%hasSPistol) + %useWeapon = "SPistol"; + } + else if (%distToTarg < 200) + { + if (%hasKreig) + %useWeapon = "KreigRifle"; + else if (%hasSniper) + %useWeapon = "Snipergun"; + else if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + + } + else if (%distToTarg < 250) + { + if (%hasSniper) + %useWeapon = "Snipergun"; + else if (%hasKreig) + %useWeapon = "KreigRifle"; + } + else + { + if (%hasSniper) + %useWeapon = "Snipergun"; + } + + //now make sure we actually selected something + if (%useWeapon $= "NoAmmo") + { + if (%hasMG) + %useWeapon = "RPChaingun"; + else if (%hasHMG) + %useWeapon = "HRPChaingun"; + else if (%hasKreig) + %useWeapon = "KreigRifle"; + else if (%hasMG42) + %useWeapon = "MG42"; + else if (%hasLSMG) + %useWeapon = "LSMG"; + else if (%hasSniper) + %useWeapon = "Snipergun"; + else if (%hasFlamer) + %useWeapon = "Flamer"; + else if (%hasSPistol) + %useWeapon = "SPistol"; + else if (%hasPistol) + %useWeapon = "Pistol"; + else if (%hasShotgun) + %useWeapon = "Shotgun"; + else if (%hasRShotgun) + %useWeapon = "RShotgun"; + else if(%hasBazooka) + %useWeapon = "Bazooka"; + else if(%hasLMissileLauncher) + %useWeapon = "LMissileLauncher"; + else if(%hasAALauncher) + %useWeapon = "AALauncher"; + } + + //now select the weapon + switch$ (%useWeapon) + { + case "RPChaingun": + %client.player.use("RPChaingun"); + %client.setWeaponInfo("RPChaingunBullet",5,200,30); + case "HRPChaingun": + %client.player.use("HRPChaingun"); + %client.setWeaponInfo("RPChaingunBullet",5,200,30); + case "LSMG": + %client.player.use("LSMG"); + %client.setWeaponInfo("LSMGBullet",5,120,30); + case "KreigRifle": + %client.player.use("KreigRifle"); + %client.setWeaponInfo("KreigBullet",15,400); + case "Snipergun": + %client.player.use("Snipergun"); + %client.setWeaponInfo("SnipergunBullet",150,2000); + case "MG42": + %client.player.use("MG42"); + %client.setWeaponInfo("MG42Bullet",1,120,200); + case "Pistol": + %client.player.use("Pistol"); + %client.setWeaponInfo("coltBullet",3,100); + case "SPistol": + %client.player.use("SPistol"); + %client.setWeaponInfo("coltBullet",3,100); + case "Shotgun": + %client.player.use("Shotgun"); + %client.setWeaponInfo("ShotgunPellet",1,30); + case "RShotgun": + %client.player.use("RShotgun"); + %client.setWeaponInfo("ShotgunPellet",1,30); + case "Bazooka": + %client.player.use("Bazooka"); + %client.setWeaponInfo("Bazookashot",20,1000); + case "AALauncher": + %client.player.use("AALauncher"); + %client.setWeaponInfo("AAMissile",15,750); + case "LMissileLauncher": + %client.player.use("LMissileLauncher"); + %client.setWeaponInfo("LShoulderMissile",10,300); + case "Flamer": + %client.player.use("Flamer"); + %client.setWeaponInfo("FlameboltMain",5,60); + case "NoAmmo": + %client.setWeaponInfo("NoAmmo", 30, 75); + } +} + +//function is called once per frame, to handle packs, healthkits, grenades, etc... +function AIProcessEngagement(%client, %target, %type, %projectile) +{ + //make sure we're still alive + if (! AIClientIsAlive(%client)) + return; + + //clear the pressFire + %client.pressFire(-1); + + //see if we have to use a repairkit + %player = %client.player; + if (!isObject(%player)) + return; + + if (%client.getSkillLevel() > 0.1 && %player.getDamagePercent() > 0.3 && %player.getInventory("RepairKit") > 0) + { + //add in a "skill" value to delay the using of the repair kit for up to 10 seconds... + %elapsedTime = getSimTime() - %client.lastDamageTime; + %skillValue = (1.0 - %client.getSkillLevel()) * (1.0 - %client.getSkillLevel()); + if (%elapsedTime > (%skillValue * 20000)) + %player.use("RepairKit"); + } + + //see if we've been blinded + if (%player.getWhiteOut() > 0.6) + %client.setBlinded(2000); + + //else see if there's a grenade in the vicinity... + else + { + %count = $AIGrenadeSet.getCount(); + for (%i = 0; %i < %count; %i++) + { + %grenade = $AIGrenadeSet.getObject(%i); + + //make sure the grenade isn't ours + if (%grenade.sourceObject.client != %client) + { + //see if it's within 15 m + if (VectorDist(%grenade.position, %client.player.position) < 15) + { + %client.setDangerLocation(%grenade.position, 20); + break; + } + } + } + } + + //if we're being hunted by a seeker projectile, throw a flare grenade + if (%player.getInventory("FlareGrenade") > 0) + { + %missileCount = MissileSet.getCount(); + for (%i = 0; %i < %missileCount; %i++) + { + %missile = MissileSet.getObject(%i); + if (%missile.getTargetObject() == %player) + { + //see if the missile is within range + if (VectorDist(%missile.getTransform(), %player.getTransform()) < 50) + { + %player.throwStrength = 1.5; + %player.use("FlareGrenade"); + break; + } + } + } + } + + //see what we're fighting + switch$ (%type) + { + case "player": + //make sure the target is alive + if (AIClientIsAlive(%target)) + { + //if the target is in range, and within 10-40 m, and heading in this direction, toss a grenade + if (!$AIDisableGrenades && %client.getSkillLevel() >= 0.3) + { + if (%player.getInventory("Grenade") > 0) + %grenadeType = "Grenade"; + else if (%player.getInventory("FlashGrenade") > 0) + %grenadeType = "FlashGrenade"; + else if (%player.getInventory("ConcussionGrenade") > 0) + %grenadeType = "ConcussionGrenade"; + else %grenadeType = ""; + if (%grenadeType !$= "" && %client.targetInSight()) + { + //see if the predicted location of the target is within 10m + %targPos = %target.player.getWorldBoxCenter(); + %clientPos = %player.getWorldBoxCenter(); + + //make sure we're not *way* above the target + if (getWord(%clientPos, 2) - getWord(%targPos, 2) < 3) + { + %dist = VectorDist(%targPos, %clientPos); + %direction = VectorDot(VectorSub(%clientPos, %targPos), %target.player.getVelocity()); + %facing = VectorDot(VectorSub(%client.getAimLocation(), %clientPos), VectorSub(%targPos, %clientPos)); + if (%dist > 20 && %dist < 45 && (%direction > 0.9 || %direction < -0.9) && (%facing > 0.9)) + { + %player.throwStrength = 1.0; + %player.use(%grenadeType); + } + } + } + } + + //see if we have a shield pack that we need to use + if (%player.getInventory("ShieldPack") > 0) + { + if (%projectile > 0 && %player.getImageState($BackpackSlot) $= "Idle") + { + %player.use("Backpack"); + } + else if (%projectile <= 0 && %player.getImageState($BackpackSlot) $= "activate") + { + %player.use("Backpack"); + } + } + } + + case "object": + %hasGrenade = %player.getInventory("Grenade"); + if (%hasGrenade && %client.targetInRange()) + { + %targPos = %target.getWorldBoxCenter(); + %myPos = %player.getWorldBoxCenter(); + %dist = VectorDist(%targPos, %myPos); + if (%dist > 5 && %dist < 20) + { + %player.throwStrength = 1.0; + %player.use("Grenade"); + } + } + + case "none": + //use the repair pack if we have one + if (%player.getDamagePercent() > 0 && %player.getInventory(RepairPack) > 0) + { + if (%player.getImageState($BackpackSlot) $= "Idle") + %client.player.use("RepairPack"); + else + //sustain the fire for 30 frames - this callback is timesliced... + %client.pressFire(30); + } + } +} + +function AIFindClosestInventory(%client, %armorChange) +{ + %closestInv = -1; + %closestDist = 32767; + + %depCount = 0; + %depGroup = nameToID("MissionCleanup/Deployables"); + if (%depGroup > 0) + %depCount = %depGroup.getCount(); + + // there exists a deployed station, lets find it + if(!%armorChange && %depCount > 0) + { + for(%i = 0; %i < %depCount; %i++) + { + %obj = %depGroup.getObject(%i); + + if(%obj.getDataBlock().getName() $= "DeployedStationInventory" && %obj.team == %client.team && %obj.isEnabled()) + { + %distance = %client.getPathDistance(%obj.getTransform()); + if (%distance > 0 && %distance < %closestDist) + { + %closestInv = %obj; + %closestDist = %distance; + } + } + } + } + + // still check if there is one that is closer + %invCount = $AIInvStationSet.getCount(); + for (%i = 0; %i < %invCount; %i++) + { + %invStation = $AIInvStationSet.getObject(%i); + if (%invStation.team <= 0 || %invStation.team == %client.team) + { + //error("DEBUG: found an inventory station: " @ %invStation @ " status: " @ %invStation.isPowered()); + //make sure the station is powered + if (!%invStation.isDisabled() && %invStation.isPowered()) + { + %dist = %client.getPathDistance(%invStation.getTransform()); + if (%dist > 0 && %dist < %closestDist) + { + %closestInv = %invStation; + %closestDist = %dist; + } + } + } + } + + return %closestInv @ " " @ %closestDist; +} + +//------------------------------ +//find the closest inventories for the objective weight functions +function AIFindClosestInventories(%client) +{ + %closestInv = -1; + %closestDist = 32767; + %closestRemoteInv = -1; + %closestRemoteDist = 32767; + + %depCount = 0; + %depGroup = nameToID("MissionCleanup/Deployables"); + + //first, search for the nearest deployable inventory station + if (isObject(%depGroup)) + { + %depCount = %depGroup.getCount(); + for (%i = 0; %i < %depCount; %i++) + { + %obj = %depGroup.getObject(%i); + + if (%obj.getDataBlock().getName() $= "DeployedStationInventory" && %obj.team == %client.team && %obj.isEnabled()) + { + %distance = %client.getPathDistance(%obj.getTransform()); + if (%distance > 0 && %distance < %closestRemoteDist) + { + %closestRemoteInv = %obj; + %closestRemoteDist = %distance; + } + } + } + } + + // now find the closest regular inventory station + %invCount = $AIInvStationSet.getCount(); + for (%i = 0; %i < %invCount; %i++) + { + %invStation = $AIInvStationSet.getObject(%i); + if (%invStation.team <= 0 || %invStation.team == %client.team) + { + //make sure the station is powered + if (!%invStation.isDisabled() && %invStation.isPowered()) + { + %dist = %client.getPathDistance(%invStation.getTransform()); + if (%dist > 0 && %dist < %closestDist) + { + %closestInv = %invStation; + %closestDist = %dist; + } + } + } + } + + //if the regular inv station is closer than the deployed, don't bother with the remote + if (%closestDist < %closestRemoteDist) + %returnStr = %closestInv SPC %closestDist; + else + %returnStr = %closestInv SPC %closestDist SPC %closestRemoteInv SPC %closestRemoteDist; + + return %returnStr; +} + +//------------------------------ +//AI Equipment Configs +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyAmmoSet, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyShieldSet, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyEnergySet, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyRepairSet, $EquipConfigIndex++] = "BeaconSmokeGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyIndoorTurretSet, $EquipConfigIndex++] = "BeaconSmokeGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "Heavy"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "MG42"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "Mg42Ammo"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "RShotgun"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "RShotgunAmmo"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "RShotgunClip"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[HeavyInventorySet, $EquipConfigIndex++] = "BeaconSmokeGrenade"; + +//------------------------------ + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "RepairPack"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "HRPChaingun"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "RPGAmmo"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "Shotgun"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "ShotgunAmmo"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "ShotgunClip"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "BeaconSmokeGrenade"; +$AIEquipmentSet[MediumRepairSet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "InventoryDeployable"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "HRPChaingun"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "RPGAmmo"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "FlareGrenade"; +$AIEquipmentSet[MediumInventorySet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "SatchelCharge"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "HRPChaingun"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "RPGAmmo"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "Bazooka"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "BazookaAmmo"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "Grenade"; +$AIEquipmentSet[MediumShieldSet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "HRPChaingun"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "RPGAmmo"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "AALauncher"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "AALauncherAmmo"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "Grenade"; +$AIEquipmentSet[MediumMissileSet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Shotgun"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "ShotgunAmmo"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "ShotgunClip"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Snipergun"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "SnipergunAmmo"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Grenade"; +$AIEquipmentSet[MediumEnergySet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "HRPChaingun"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "RPGAmmo"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "Bazooka"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "BazookaAmmo"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "Grenade"; +$AIEquipmentSet[MediumOutdoorTurretSet, $EquipConfigIndex++] = "Mine"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "Medium"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "KreigRifle"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "KreigAmmo"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "RifleClip"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "Grenade"; +$AIEquipmentSet[MediumIndoorTurretSet, $EquipConfigIndex++] = "Mine"; + +//------------------------------ + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "RPChaingun"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "RPChaingunAmmo"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "MGClip"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightEnergyDefault, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "Shotgun"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "ShotgunAmmo"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "ShotgunClip"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightShieldSet, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "RepairPack"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightRepairSet, $EquipConfigIndex++] = "smokebeaconGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "RepairPack"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "snipergun"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "snipergunAmmo"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightEnergySniper, $EquipConfigIndex++] = "SmokeGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "SatchelCharge"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightCloakSet, $EquipConfigIndex++] = "SmokeGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "Light"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "FlamerAmmoPack"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "Flamer"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "FlamerAmmo"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "Pistol"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "melee"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightEnergyELF, $EquipConfigIndex++] = "Grenade"; + +//--------------------------- + +$EquipConfigIndex = -1; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "SpecOps"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "KreigRifle"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "KreigAmmo"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "RifleClip"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "SPistol"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "SOmelee"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[LightSniperChain, $EquipConfigIndex++] = "FlashGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "SpecOps"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "Snipergun"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "SnipergunAmmo"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "SPistol"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "SOmelee"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[SpecOpsSet2, $EquipConfigIndex++] = "SmokeGrenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "SpecOps"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "AmmoPack"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "LMissileLauncher"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "LMissileLauncherAmmo"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "SPistol"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "SOmelee"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[SpecOpsSet3, $EquipConfigIndex++] = "Grenade"; + +$EquipConfigIndex = -1; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "SpecOps"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "SatchelCharge"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "LSMG"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "LSMGAmmo"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "LSMGClip"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "KreigRifle"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "KreigAmmo"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "RifleClip"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "SPistol"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "PistolAmmo"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "PistolClip"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "SOmelee"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "TargetingLaser"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "RepairKit"; +$AIEquipmentSet[SpecOpsSet4, $EquipConfigIndex++] = "Grenade"; + diff --git a/Scripts/arenaAI.cs b/Scripts/arenaAI.cs new file mode 100644 index 0000000..526f9cc --- /dev/null +++ b/Scripts/arenaAI.cs @@ -0,0 +1,75 @@ + +// Tribes 2 Arena [Rev2] 1.0 Final +// Written By Teribaen (teribaen@planettribes.com) +// http://www.planettribes.com/t2arena/ + +$Arena::AI::Version = 1.0; + + +// ========================================================================== // +// | | // +// | AI TASKS | // +// | | // +// ========================================================================== // + +// ------------------------------------------------------------------ // +// ArenaGame::onAIRespawn() +// Gives the bots their objectives + +function ArenaGame::onAIRespawn( %game, %client ) +{ + // Setup AI tasks + if ( !%client.defaultTasksAdded ) + { + %client.defaultTasksAdded = true; + %client.addTask(AIEngageTask); + %client.addTask(AIPickupItemTask); +// %client.addTask(AITauntCorpseTask); // Heh, this makes the bots sitting ducks while they taunt + %client.addTask(AIUseInventoryTask); +// %client.addtask(AIEngageTurretTask); + %client.addtask(AIDetectMineTask); + %client.addTask(AIPatrolTask); + } + + // Use an inv whenever we spawn? + %client.spawnUseInv = true; + + // Mark the bot as alive (they just spawned) + arenaSetClientAlive( %client ); +} + + +// ========================================================================== // +// | | // +// | AI CALLBACK HANDLERS | // +// | | // +// ========================================================================== // + +function ArenaGame::AIInit( %game ) +{ + // Call the default AIInit() function + AIInit(); +} + +function ArenaGame::onAIDamaged(%game, %clVictim, %clAttacker, %damageType, %implement ) +{ + if ( %clAttacker && %clAttacker != %clVictim && %clAttacker.team == %clVictim.team ) + { + // Clear the "lastDamageClient" tag so we don't turn on teammates + %clVictim.lastDamageClient = -1; + } +} + +function ArenaGame::onAIKilledClient(%game, %clVictim, %clAttacker, %damageType, %implement) +{ + if ( %clVictim.team != %clAttacker.team ) + DefaultGame::onAIKilledClient( %game, %clVictim, %clAttacker, %damageType, %implement ); +} + +function ArenaGame::onAIFriendlyFire(%game, %clVictim, %clAttacker, %damageType, %implement) +{ +// if ( %clAttacker && %clAttacker.team == %clVictim.team && %clAttacker != %clVictim ) +// AIMessageThread("ChatSorry", %clAttacker, %clVictim); +} + + diff --git a/Scripts/chatCommands.cs b/Scripts/chatCommands.cs new file mode 100644 index 0000000..825f25b --- /dev/null +++ b/Scripts/chatCommands.cs @@ -0,0 +1,1145 @@ +//============================================================================== +// Chat Commands - Made by Blnukem and Eolk. +//------------------------------------------------------------------------------ +// These basic chat commands were created by CCM and ACCM Dev teams. +// You may add additional commands here if you wish at the bottom of this file. +//------------------------------------------------------------------------------ +// • The ACCM Chat Command database is located in another folder. +// • Make sure your new commands aren't abused or overpowered. +//============================================================================== + +// This command was edited by Blnukem to work with ACCM. +function chatcommands(%sender, %message) { + %cmd = getWord(%message,0); + %cmd = stripChars(%cmd,"/"); + %count = getWordCount(%message); + %args = getwords(%message,1); + %cmd = "cc" @ %cmd; + switch$(%cmd) + { + case "ccTips": + call("ccTips", %sender, "Join", %args); + case "ccOpen": + call("ccOpendoor", %sender, %args); + case "ccJoin" or "ccJoin": + call("ccJoin", %sender, %sender.sqinv); + case "ccDelmypieces": + call("ccDelpieces", %sender, ""); + case "ccObjectScale" or "ccReSize" or "ccSetSize": + call("ccSetScale", %sender, %args); + case "ccGetSize": + call("ccGetScale", %sender, %args); + case "ccObjectName" or "ccSetName": + call("ccName", %sender, %args); + case "ccGetPos": + call("ccPos", %sender, "G", %args); + case "ccBasicCMDS" or "ccBasicCommands": + call("ccHelp", %sender, "BasicCommands", %args); + case "ccSCMDS" or "ccSentinelCommands": + call("ccHelp", %sender, "SentinelCommands", %args); + case "ccZCMDS" or "ccZombieCommands": + call("ccHelp", %sender, "ZombieCommands", %args); + case "ccDCMDS" or "ccDroneCommands": + call("ccHelp", %sender, "DroneCommands", %args); + case "ccBuildOptions" or "ccBuildingOptions": + call("ccHelp", %sender, "BuildingOptions", %args); + case "ccAdminCMDS" or "ccAdminCommands": + call("ccHelp", %sender, "AdminCommands", %args); + case "ccSACMDS" or "ccSACommands": + call("ccHelp", %sender, "SACommands", %args); + case "ccSetFreq": + call("ccPower", %sender, %args); + case "ccBuy": + call("ccBf", %sender, %args); + default: + call(%cmd, %sender, %args); + } +} + +//============================================================================== +// The following was either made by the CCM or Construction development teams. +//============================================================================== + +function VectToRot(%vec){ + %x = getWord(%vec, 0); + %y = getWord(%vec, 1); + %z = getWord(%vec, 2); + %len = vectorLen(%vec); + %rotAngleXY = mATan(%z, %len); + %rotAngleZ = mATan(%x, %y); + %matrix = MatrixCreateFromEuler("0 0" SPC %rotAngleZ * -1); + %matrix2 = MatrixCreateFromEuler(%rotAngleXY SPC "0 0"); + %finalMat = MatrixMultiply(%matrix, %matrix2); + return getWords(%finalMat, 3, 5)@" "@(getWord(%finalMat,6) * 360 / 3.14156); +} + +function ccOpendoor(%sender,%args) { + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %classname; + %cash = %obj.amount; + %owner = %obj.owner; + + if(!isObject(%obj) || (!%obj.isDoor && %datablock.getName() !$= "DeployedDoor")) + { + messageclient(%sender, 'MsgClient', "\c2No door in range."); + return; + } + + %obj.issliding = 0; + if (%obj.collisionType >= 1) + { + messageClient(%sender, "", "\c2This door is a collision-type door."); + return; + } + + if (%obj.powercontrol >= 1) + { + messageclient(%sender, 'MsgClient', "\c2This door is controlled by a power supply."); + return; + } + + if (%obj.canmove == false) //if it cant move + return; //stop here + + %pass = %args; + if (%obj.pw $= %pass) { + if (%obj.toggletype ==1){ + if (%obj.moving $="close" || %obj.moving $="" || %going $="opening"){ + schedule(10,0,"open",%obj); + } + else if (%obj.moving $="open" || %going $="closeing"){ + schedule(10,0,"close",%obj); + } + } + else + schedule(10,0,"open",%obj); + } + if (%obj.pw !$= %pass) + messageclient(%sender,'MsgClient',"\c2Password Denied."); +} + +function ccSetdoorpass(%sender,%args){ + %pos=%sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos=vectoradd(%pos,vectorscale(%vec,100)); + %obj=containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj=getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %dataBlock.className; + if (!%obj.isDoor && %datablock.getName !$= "DeployedDoor") { + messageclient(%sender, 'MsgClient', '\c2No door in range.'); + return; + } + if (%obj.owner!=%sender && %obj.owner !$="") + messageclient(%sender, 'MsgClient', '\c2You do not own this door.'); + if (!isobject(%obj)) + messageclient(%sender, 'MsgClient', '\c2No door in range.'); + if (%obj.Collision $= true) { + messageclient(%sender, 'MsgClient', '\c2Collision doors can not have passwords.'); + return; + } + if(isobject(%obj) && %obj.owner==%sender) { + %pw=getword(%args,0); + %obj.pw=%pw; + messageclient(%sender, 'MsgClient', '\c2Password set, password is\c3 %1\c2.',%pw); + } +} + +function ccGetAmmo(%sender,%args){ + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,5)); + %obj = containerraycast(%pos,%targetpos,$typemasks::VehicleObjectType,%sender.player); + if(%obj != ""){ + %obj = getword(%obj,0); + %className = %obj.getClassName(); + %Name = %obj.getDatablock().getName(); + if(%className $= "WheeledVehicle" && %Name $= "AmmoCrateVeh"){ + if(%obj.numReload >= 2){ + buyDeployableFavorites(%sender); + %obj.numReload--; + } + else if(%obj.numReload == 1){ + buyDeployableFavorites(%sender); + %obj.schedule(1000, delete); + } + } + } +} + +function ccHack(%sender, %args){ + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,10)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %classname; + %DBname = %datablock.getName(); + if(%args $= "help"){ + messageClient(%sender, 'MsgClient', "\c2To hack you must guess the 4 digit binary code for each of the numbers in a randomly generated 5 digit code."); + messageClient(%sender, 'MsgClient', "\c2EX: /hack - you have 2 ones in this code."); + messageClient(%sender, 'MsgClient', "\c2... /hack 0 0 1 1 - you have 2 correct in this code."); + messageClient(%sender, 'MsgClient', "\c2... /hack 0 1 0 1 - you have 2 correct in this code."); + messageClient(%sender, 'MsgClient', "\c2... /hack 0 1 1 0 - you have got this number and you have 4 codes left to go."); + return; + } + if (!isobject(%obj)) { + messageclient(%sender, 'MsgClient', '\c2No teleport in range.'); + return; + } + if (%DBname !$= "TelePadDeployedBase") { + messageclient(%sender, 'MsgClient', '\c2No teleport in range.'); + return; + } + + if(%obj.team == %sender.team) + { + messageClient(%sender, "", "\c2You can't hack your own team's teleports!"); + return; + } + + if(%obj.ishacked != 1){ + if(%args $= ""){ + %numones = 0; + for(%i = 0; %i < 4; %i++){ + if(getWord(%obj.hackcode[(%obj.numhacked + 1)],%i) $= "1") + %numones++; + } + messageClient(%sender, 'MsgClient', "\c2There are "@ %numones @" ones in this code, and you have "@ (5 - %obj.numhacked) @" more codes to find."); + } + else{ + %numsame = 0; + for(%i = 0; %i < 4; %i++){ + if(getWord(%obj.hackcode[(%obj.numhacked + 1)],%i) $= getWord(%args,%i)) + %numsame++; + } + if(%numsame == 4){ + if(%obj.numhacked $= "") + %obj.numhacked = 0; + %obj.numhacked++; + if(%obj.numhacked == 5){ + %obj.ishacked = 1; + messageClient(%sender, 'MsgClient', "\c2You have hacked this teleport."); + return; + } + messageClient(%sender, 'MsgClient', "\c2You have got this number you have "@(5 - %obj.numhacked)@" codes left to go."); + } + else + messageClient(%sender, 'MsgClient', "\c2You have "@%numsame@" of the code correct."); + } + } + else + messageClient(%sender, "" ,"\c2This teleport is already hacked."); +} + +function teleresethack(%obj){ + if(!isObject(%obj)) + return; + if(%obj.ishacked == 1 || %obj.ishacked $= ""){ + %obj.ishacked = 0; + %obj.numhacked = 0; + %num[0] = "0 0 0 0"; + %num[1] = "0 0 0 1"; + %num[2] = "0 0 1 0"; + %num[3] = "0 0 1 1"; + %num[4] = "0 1 0 0"; + %num[5] = "0 1 0 1"; + %num[6] = "0 1 1 0"; + %num[7] = "0 1 1 1"; + %num[8] = "1 0 0 0"; + %num[9] = "1 0 0 1"; + for(%i = 1; %i < 6; %i++){ + %random = getRandom(0,9); + %obj.hackcode[%i] = %num[%random]; + } + } + schedule(180000, %obj, "teleresethack", %obj); +} + +function ccListSpawns(%sender){ + if(%sender.team == 0) + return; + %team = %sender.team; + for(%i = 0; %i < $teamSPs[%team]; %i++){ + if($teamSP[%team,%i].name $= "") + $teamSP[%team,%i].name = getWords($teamSP[%team,%i].getPosition(),0,1); + messageClient(%sender, 'MsgClient', "\c2"@(%i + 1)@". spawnpoint "@$teamSP[%team,%i].name@"."); + } +} + +function ccChooseSpawn(%sender,%args){ + %team = %sender.team; + if(%team == 0) + return; + + if($UseForcedTeamSpawn[%team] && !%sender.isAdmin) + { + messageClient(%sender, 'MsgClient', '\c2Your spawnpoint was forced by an admin. You cannot switch.'); + return; + } + + if(%args $= ""){ + if(%sender.SP !$= ""){ + %sender.SP = ""; + messageClient(%sender, 'MsgClient', "\c2Spawn Point Reset, you will now spawn at the normal location."); + return; + } + messageClient(%sender, 'MsgClient', "\c2Please choose a number that corresponds with a spawnpoint."); + return; + } + %num = (firstWord(%args) - 1); + if(isObject($teamSP[%team,%num])){ + if($teamSP[%team,%num].active == 1){ + %sender.SP = $teamSP[%team,%num]; + messageClient(%sender, 'MsgClient', "\c2Spawnpoint "@$teamSP[%team,%i].name@" choosen."); + } + else{ + %sender.SP = $teamSP[%team,%num]; + messageClient(%sender, 'MsgClient', "\c2Spawnpoint "@$teamSP[%team,%i].name@" choosen, but this point is not currently powered."); + } + } + else + messageClient(%sender, 'MsgClient', "\c2This spawnpoint dosent exist."); +} + +//============================================================================== +// All of the following are the public ACCM Chat Commands. +//------------------------------------------------------------------------------ +// The following functions were made by Blnukem. +//============================================================================== +function ccMenu (%sender, %args) +{ + %arg1 = getWord(%args, 0); + if (%arg1 $= "Join") + { + CenterPrint(%sender, "Advanced Combat Construction Mod \n ACCM Developers: Blnukem, Eolk, and Dark Dragon DX. \n Website: http://www.freewebs.com/advancedccm", 10, 3); + if ($Host::MOTD !$= "") + { + Schedule(5000, 0, "CenterPrint", %sender, "Message of the Day. \n "@$Host::MOTD@" ", 5 , 3); + Schedule(5000, 0, "MessageClient", %sender, "Msg", "\c2A message from "@$Host::GameName@" Administration:\n"@$Host::MOTD@""); + } + } +} + +// Blnukem - This will tell the sender basic information on the target. + +function ccInfo(%sender, %args) +{ + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Cannot find player:\c3 "@%args@"\c2."); + return; + } + + if(%target $= "") + { + messageClient(%sender, "", "\c2Please specify a person."); + return; + } + + %rank = $Rank::Rank[%target.ranknum]; + %score = $Rank::Score[%target.ranknum]; + %smurf = (%target.isSmurf == 1 ? "Yes" : "No"); + %keeper = (%target.isZombieKeeper == 1 ? "Yes" : "No"); + %admin = (%target.isAdmin == 1 ? "Yes" : "No"); + %superadmin = (%target.isSuperAdmin == 1 ? "Yes" : "No"); + messageClient(%sender, "", "\c2Client Name:\c3 "@%target.nameBase@"\c2 - GUID:\c3 "@%target.guid@"\c2 - Player ID:\c3 "@%target@""); + messageClient(%sender, "", "\c2Smurf:\c3 "@%smurf@"\c2 - Zombie Keeper:\c3 "@%keeper@"\c2 - \c2Admin:\c3 "@%admin@"\c2 - Super Admin:\c3 "@%superadmin@""); + if($Rank::Squad[%target.ranknum] $= "") + { + messageClient(%sender, 'MsgClient', "\c2Current Squad:\c3 None\c2 - Current Rank:\c3 "@%rank@"\c2 - Total Score:\c3 "@%score@""); + } else for(%i = 0; %i < $squad::numsquads; %i++) { + %squad = $Squad::Name[%i]; + messageClient(%sender, 'MsgClient', "\c2Current Squad:\c3 "@%squad@"\c2 - Current Rank:\c3 "@%rank@"\c2 - Total Score:\c3 "@%score@""); + } +} + +//------------------------------------------------------------------------------ +// Blnukem - Very basic command. I made this due to popular demand. +// This command just gives the target an armor variant. +// There is probably a better way to do this, but I got lazy... So deal with it. + +function ccGiveArmor(%sender,%args) +{ + %armor = strlwr(getword(%args, 0)); + %target = plnametocid(getword(%args, 1)); + if(!%sender.isAdmin) + return; + + // TODO - Make this function check if the target exists and the gametype. + switch$(%armor) + { + case "Recon": + if(Game.class !$= "ConstructionGame") + { + %target.player.clearInventory(); + %target.setWeaponsHudClearAll(); + %target.player.setArmor(SpecOps); + %target.player.setInventory(Snipergun,1,true); + %target.player.setInventory(LSMG,1,true); + %target.player.setInventory(AmmoPack,1,true); + %target.player.setInventory(SnipergunAmmo,30,true); + %target.player.setInventory(LSMGAmmo,60,true); + %target.player.setInventory(LSMGClip,5,true); + } + case "Commando": + if(Game.class !$= "ConstructionGame") + { + %target.player.clearInventory(); + %target.setWeaponsHudClearAll(); + %target.player.setArmor(Medium); + %target.player.setInventory(HRPChaingun,1,true); + %target.player.setInventory(Shotgun,1,true); + %target.player.setInventory(AmmoPack,1,true); + %target.player.setInventory(RPChaingunAmmo,30,true); + %target.player.setInventory(MGClip,4,true); + %target.player.setInventory(RPGAmmo,3,true); + %target.player.setInventory(ShotgunAmmo,8,true); + %target.player.setInventory(ShotgunClip,1,true); + } + case "Powered": + if(Game.class !$= "ConstructionGame") + { + %target.player.clearInventory(); + %target.setWeaponsHudClearAll(); + %target.player.setArmor(Heavy); + %target.player.setInventory(MG42,1,true); + %target.player.setInventory(RShotgun,1,true); + %target.player.setInventory(RailGun,1,true); + %target.player.setInventory(BoosterPack,1,true); + %target.player.setInventory(MG42Ammo,200,true); + %target.player.setInventory(MG42Clip,5,true); + %target.player.setInventory(RShotgunAmmo,25,true); + %target.player.setInventory(RShotgunClip,2,true); + %target.player.setInventory(RailGunAmmo,8,true); + } + default: // Just a check on the valid armors. + MessageClient(%sender, "Msg", "\c2Invalid armor. Valid armors are Recon, Commando and Powered."); + return; + } + // Lets set the basic items and stuff throughout each armor variant. + %target.player.startFade(2000, 0, false); // Just a cheap effect. + %target.player.setInventory(Grenade,5,true); + %target.player.setInventory(Mine,2,true); + %target.player.setInventory(Beacon,3,true); + %target.player.setInventory(RepairKit,2,true); + BottomPrint(%target, "You now have "@%armor@" armor.", 5, 1); + MessageClient(%target, "Msg", "\c2You were given "@%armor@" armor by\c3 "@%sender.nameBase@"\c2."); + MessageClient(%sender, "Msg", "\c2You gave\c3 "@%target.nameBase@" \c2"@%armor@" armor."); +} + +//------------------------------------------------------------------------------ +// Blnukem - I modified this command so it has cheat protection for those who +// want to be jackasses with pure off, and also so it can work with Technicians. + +function ccBf(%sender,%args) +{ + if (!isObject(%sender.player)) + { // No player object? Bitch, you can't use this command. + MessageClient(%sender, "Msg", "\c2You you have a player object in order to buy your inventory. ~wfx/misc/misc.error.wav"); + } + + if (%sender.BasicTimer > 0) + { // Hello cheat protection! + MessageClient(%sender, "", "\c3CHEAT PROTECTION:\c0 You must wait "@%sender.BasicTimer@" more seconds until you can use /bf again."); + return; + } + + if (($Host::Purebuild == 0) && (%sender.armor $= "Light") || ($Host::Purebuild == 0) && (%sender.armor $= "Pure")) + { // This allows Technicians to use /bf with purebuild off. + buyFavorites(%sender); + %sender.BasicTimer = 10; // Cheat protection time in seconds. + ClearBasicTimer(%sender); + } else if (($Host::Purebuild == 1) || (%sender.armor $= "Pure")) + { // No cheat protection, just good ole' /bf with some inventory buying fun. + buyFavorites(%sender); + } +} + +//------------------------------------------------------------------------------ + +function ccMe(%sender,%args) +{ + messageall("", '\c0%1 %2', %sender.name, %args); +} + +//------------------------------------------------------------------------------ + +function ccName(%sender,%args, %special) +{ + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %dataBlock.className; + + if (%obj==0){ + messageclient(%sender, 'MsgClient', "\c2No object to label."); + return; + } + if(!deployables.ismember(%obj)){ + messageClient(%sender, "MsgClient", "\c2You can\'t rename map objects!"); + return; + } + + if(%obj.getowner() != %sender){ + messageclient(%sender, 'MsgClient', "\c2You do not own that object."); + return; + } + if (%classname $= "waypoint"){ + %obj.wp.schedule(10, "delete"); + %wp = new waypoint(){ + dataBlock = WayPointMarker; + position = %obj.getPosition(); + name = %args; + scale = "0.1 0.1 0.1"; + team = %sender.team; + }; + + messageclient(%sender, 'MsgClient', "\c2Object name set to \c3"@%args@""); + %obj.wp = %wp; + MissionCleanup.add(%wp); + return; + } + messageclient(%sender, 'MsgClient', "\c2Object name set to \c3"@%args@""); + %obj.nametag = %args; + setTargetName(%obj.target,addTaggedString("\c6"@%args@"")); + return; +} + +//------------------------------------------------------------------------------ + +function ccRadius(%sender,%args, %special) +{ + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %dataBlock.className; + + if (%args < 10 || %args > 10000) + { + messageclient(%sender, "", "\c2Invalid Range, must be between\c3 0 \c2and\c3 10000\c2."); + return; + } + + if (%obj == 0) + { + messageclient(%sender, "", "\c2No object in range."); + return; + } + + if(!deployables.ismember(%obj)) + { + messageClient(%sender, "", "\c2You can\'t change the radius of map objects."); + return; + } + + if(%obj.owner != %sender && !%sender.isAdmin) + { + messageClient(%sender, "", "\c2You do not own that object."); + return; + } + + if (%classname $= "Switch" || %classname $= "Tripwire") + { + messageClient(%sender, "", "\c2Radius set to:\c3 "@%args@" \c2meters."); + %obj.switchradius = %args; + return; + } else if (%classname !$= "Switch" || %classname !$= "Tripwire") + { + messageClient(%sender, "", "\c2Invaild object, you can only change the radius of Tripwires and Switches."); + return; + } +} + +//------------------------------------------------------------------------------ + +function ccCloak(%sender,%args, %special) +{ + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock().getName(); + %className = %dataBlock.className; + + if (%obj == 0) + { + messageclient(%sender, "", "\c2No valid objects in range."); + return; + } + + if(!deployables.ismember(%obj) && (%sender.isAdmin !=1)) + { + messageClient(%sender, "", "\c2You can\'t cloak map objects."); + return; + } + + if(%obj.owner != %sender && !%sender.isAdmin) + { + messageClient(%sender, "", "\c2You do not own that object."); + return; + } + + if (!%obj.cloak) + { + %obj.setCloaked(true); + %obj.cloak = true; + messageclient(%sender, "", "\c2Object cloaked."); + return; + } else { + %obj.setCloaked(false); + %obj.cloak = false; + messageclient(%sender, "", "\c2Object un-cloaked."); + return; + } +} + +//------------------------------------------------------------------------------ + +function ccMOTD(%sender, %args) +{ + if(!%sender.isSuperAdmin) + return; + + %target = plnametocid(%args); + if (%args $= "Remove" || %args $= "remove") + { + $Host::MOTD = ""; + MessageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has removed the message of the day."); + } else { + $Host::MOTD = %args; + MessageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has changed the message of the day to: \c3"@%args@""); + } +} + +//------------------------------------------------------------------------------ + +function ccZAvoid(%sender,%args) // Makes Zombies avoid you. +{ + if(!%sender.isSuperAdmin) + return; + + if(Game.class $= "ConstructionGame") + { + messageClient(%sender, "", "\c2This command is disabled in the Construction Gametype. ~wfx/misc/misc.error.wav"); + return; + } + + if (%sender.player.isZombie == false){ + %sender.player.isZombie = true; + messageClient(%sender, 'MsgAdminForce', "\c2Action Completed, zombies will now avoid you."); + return; + } + + if (%sender.player.isZombie == true){ + %sender.player.isZombie = false; + messageClient(%sender, 'MsgAdminForce', "\c2Action Completed, zombies will now attack you."); + return; + } +} + +//------------------------------------------------------------------------------ + +function ccSwitch(%sender,%args) +{ + if(!%sender.isSuperAdmin) + return; + + %nametotest=getword(%args,0); + %target=plnametocid(%nametotest); + + if (%target==%sender) { + messageClient(%sender, 'MsgClient', "\c2You can\'t control yourself."); + return; + } + + if(%args $= ""){ + messageClient(%sender, 'MsgClient', "\c2No target specified."); + return; + } + + %sender.setcontrolobject(%target.player); + %target.setcontrolobject(%sender.player); + messageClient(%sender, "MsgClient", "\c2You are now controlling: \c3"@%target.namebase@"."); + messageClient(%target, "MsgClient", "\c2You and \c3"@%target.namebase@" \c2have switched Bodies!"); + return; +} + +//============================================================================== +// The following functions were made by Eolk. +//============================================================================== + +function ccDelPieces(%sender, %args) +{ + if(!%sender.isAdmin || %args $= "") + { + if(!isObject(%sender.player)) + { + messageClient(%sender, 'MsgClient', "\c2You must be playing in order to remove your pieces."); + return; + } + + %group = nameToID("MissionCleanup/Deployables"); + for(%i = 0; %i < %group.getCount(); %i++) + { + %obj = %group.getObject(%i); + if(%obj.getOwner() == %sender && isObject(%obj)) + %obj.getDatablock().schedule(getRandom() * 10000, disassemble, %sender.player, %obj); + } + + messageClient(%sender, 'MsgYes', "\c2All of your pieces have been removed."); + } + else + { + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, 'MsgClient', "\c2That person's player object does not exist."); + return; + } + + if((!%sender.isSuperAdmin && %target.isAdmin) || %target.isSuperAdmin) + { + messageClient(%sender, 'MsgClient', "\c2You must outrank that target in order to remove their pieces."); + return; + } + + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@" \c2has forced\c3 "@%target.nameBase@" \c2to delete all of their pieces."); + ccDelPieces(%target, ""); + } +} + +//------------------------------------------------------------------------------ + +function ccPower(%sender, %args) +{ + if(!isObject(%sender.player)) + { + messageClient(%sender, "", "\c2You have to be playing in order to do this."); + return; + } + + %sender.player.powerFreq = %args; + displayPowerFreq(%sender.player); +} + +//------------------------------------------------------------------------------ + +function ccSetScale(%sender, %args) +{ + if(!isObject(%sender.player)) + { + messageClient(%sender, "", "\c2You must be spawned in order to do this!"); + return; + } + + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + + if(!isObject(%obj)) + { + messageClient(%sender, "", "\c2There is nothing in range."); + return; + } + + %dataBlock = %obj.getDataBlock(); + %className = %dataBlock.className; + if(%obj.owner != %sender && !%sender.isAdmin) + { + messageClient(%sender, "", "\c2That piece isn't yours."); + return; + } + + %test = nameToID("MissionCleanup/Deployables"); + if(!isObject(%test) || !%test.isMember(%obj)) + { + messageClient(%sender, "", "\c2That is part of the map."); + return; + } + + if(getwordcount(%args) < 3) + { + messageClient(%sender, "", "\c2You must specify all paramenters: X, Y, and Z."); + return; + } + + if(getwordcount(%args) > 3) + { + messageClient(%sender, "", "\c2Too many parameters. They should be X, Y, and Z."); + return; + } + + if(%classname $= "spine" || %classname $= "mspine" || %classname $= "spine2" || %classname $= "floor" || %classname $= "wall" || %classname $= "wwall" || %classname $= "floor" || %classname $= "door") + { + %or = %args; + %args = VectorMultiply(%args, "0.250 0.333333 2"); + %check = EditorCheckScale(%args); + if(%check == 1) + { + messageClient(%sender, "", "\c2Scale is too small."); + return; + } + + if(%check == 2) + { + messageClient(%sender, "", "\c2Scale is too big."); + return; + } + + %obj.setScale(%args); + messageClient(%sender, "", "\c2Object scale set to:\c3 "@%or@""); + return; + } + + %check = EditorCheckScale(%args); + if(%check == 1) + { + messageClient(%sender, "", "\c2Scale is too small."); + return; + } + + if(%check == 2) + { + messageClient(%sender, "", "\c2Scale is too big."); + return; + } + + if(%dataBlock $= "DeployedLTarget") + { + %obj.lMain.setScale(%args); + adjustLMain(%obj); + messageClient(%sender, "", "\c2Object scale set to:\c3 "@%args@""); + return; + } + + if(%dataBlock $= "DeployedLogoProjector") + { + %obj.setScale(%args); + if(isObject(%obj.holo)) + { + %obj.holo.setScale(%args); + adjustHolo(%obj); + messageClient(%sender, "", "\c2Object scale set to:\c3 "@%args@""); + } + + return; + } + + %obj.setScale(%args); + messageClient(%sender, "", "\c2Object scale set to:\c3 "@%args@""); +} + +//------------------------------------------------------------------------------ + +function ccGetScale(%sender, %args) +{ + if(!isObject(%sender.player)) + { + messageClient(%sender, "", "\c2You must be spawned in order to do this."); + return; + } + + %pos=%sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos=vectoradd(%pos,vectorscale(%vec,100)); + %obj=containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj=getword(%obj,0); + + if(!isObject(%obj)) + { + messageClient(%sender, "", "\c2There is nothing in range."); + return; + } + + %dataBlock = %obj.getDataBlock(); + %className = %dataBlock.className; + + %test = nameToID("MissionCleanup/Deployables"); + if(!isObject(%test) || !%test.isMember(%obj)) + { + messageClient(%sender, "", "\c2That is part of the map."); + return; + } + + %scale = %obj.getScale(); + if(%classname $= "spine" || %classname $= "mspine" || %classname $= "spine2" || %classname $= "floor" || %classname $= "wall" || %classname $= "wwall" || %classname $= "floor" || %classname $= "door") + %scale = getword(%scale, 0) / 0.250 SPC getword(%scale, 1) / 0.333333 SPC getword(%scale, 2) / 2; + if(%dataBlock $= "DeployedLTarget") + %scale = %obj.lMain.getScale(); + messageClient(%sender, "", "\c2The scale of that object is:\c3 "@%scale@""); +} + +//------------------------------------------------------------------------------ +// Idea from Insane Turkey +function ccPos(%sender, %args) +{ + if(!isObject(%sender.player)) + { + messageClient(%sender, "", "\c2You have to be playing in order to do this."); + return; + } + + %pos=%sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos=vectoradd(%pos,vectorscale(%vec,100)); + %obj=containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj=getword(%obj,0); + + if(!isObject(%obj)) + { + messageClient(%sender, "", "\c2There is nothing in range."); + return; + } + + if(%obj.getOwner() != %sender && !%sender.isAdmin) + { + messageClient(%sender, "", "\c2That piece is not yours."); + return; + } + + %test = nameToID("MissionCleanup/Deployables"); + if(!isObject(%test) || !%test.isMember(%obj)) + { + messageClient(%sender, "", "\c2That is part of the map."); + return; + } + + if(getwordcount(%args) == 4 || getword(%args, 0) $= "g") + { + %option = getword(%args, 0); + if(%option $= "s") + { + %pos = getwords(%args, 1, 3); + } + else if(%option $= "g") + { + messageClient(%sender, "", "\c2Object's position is:\c3 "@%obj.getPosition()@""); + return; + } + else if(%option $= "m") + { + %xyz = getwords(%args, 1, 3); + %positive = VectorPositive(%xyz); + %start = VectorCheck(%xyz); + + %x = getword(%start, 0) SPC 0 SPC 0; + %realx = realvec(%obj, %x); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realx, getword(%positive, 0)))); + + %y = 0 SPC getword(%start, 1) SPC 0; + %realy = realvec(%obj, %y); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realy, getword(%positive, 1)))); + + %z = 0 SPC 0 SPC getword(%start, 2); + %realz = realvec(%obj, %z); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realz, getword(%positive, 2)))); + } + else + { + messageClient(%sender, "", "\c2Unknown mode for /Pos:\c3 "@%option@"\c2."); + messageClient(%sender, "", "\c2Use:\c3 /Pos X Y Z"); + return; + } + } + else if(getwordcount(%args) == 3) + { + %xyz = getwords(%args, 0, 2); + %positive = VectorPositive(%xyz); + %start = VectorCheck(%xyz); + + %x = getword(%start, 0) SPC 0 SPC 0; + %realx = realvec(%obj, %x); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realx, getword(%positive, 0)))); + + %y = 0 SPC getword(%start, 1) SPC 0; + %realy = realvec(%obj, %y); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realy, getword(%positive, 1)))); + + %z = 0 SPC 0 SPC getword(%start, 2); + %realz = realvec(%obj, %z); + %obj.setPosition(VectorAdd(%obj.getPosition(), VectorScale(%realz, getword(%positive, 2)))); + } + else + { + messageClient(%sender, "", "\c2Wrong number of arguments for\c3 /Pos\c2."); + messageClient(%sender, "", "\c2Use:\c3 /Pos X Y Z"); + return; + } + + %pos = %obj.getPosition(); + messageClient(%sender, "", "\c2Object's position modified to:\c3 "@%pos@""); +} + +//------------------------------------------------------------------------------ + +function GivePieces(%from, %to) +{ + if(!isObject(Deployables)) + return; + + for(%i = 0; %i < Deployables.getCount(); %i++) + { + %piece = Deployables.getObject(%i); + if(%from !$= "orphan") + { + %owner = %piece.getOwner(); + if(%owner !$= "") // Regular giving + { + if(%owner == %from) + %piece.setOwner(%to.player); + } + else // For giving by clientID. + { + if(%piece.owner == %from) + %piece.setOwner(%to.player); + } + } + else // Give orphans. + { + if(!isObject(%piece.getOwner())) + %piece.setOwner(%to.player); + } + } + + logEcho("Pieces given to "@%to.nameBase@" ("@%to@") from "@%from.nameBase@" ("@%from@")"); +} + +function ccGivePieces(%sender, %args) +{ + %target = plnametocid(%args); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2You can't give your pieces to a non-existant person!"); + return; + } + + if(%target == %sender) + { + messageClient(%sender, "", "\c2You can't give your pieces to yourself!"); + return; + } + + if(isObject(%target.transferTo) || isObject(%target.transferFrom)) + { + messageClient(%sender, "", "\c3"@%target.nameBase@"\c2 is already in a transaction. Please wait a moment for it to complete."); + return; + } + + if(isObject(%sender.transferTo)) + { + messageClient(%sender, "", "\c2You are already transferring to someone! You must first cancel the first transaction."); + return; + } + + if(!%sender.isAdmin) + { + %sender.transferTo = %target; + %target.transferFrom = %sender; + + messageClient(%sender, "", "\c2You are pending piece transfer to \c3"@%target.nameBase@"\c2. If you wish to cancel the transaction, press the \"vote no\" key (delete)."); + messageClient(%target, "", "\c3"@%sender.nameBase@"\c2 is pending transfer to you. Press the \"vote yes\" key (insert) to accept, and the \"vote no\" key (delete) to decline.~wfx/misc/diagnostic_beep.wav"); + } + else + { +// Eolk - This isn't possible with the above checks. +// if(getWordCount(%args) > 1) // Super accident protection. +// { +// messageClient(%sender, "", "\c2Admins should use /ForceGivePieces to give pieces from one person to the other. Use /givePieces target to give your OWN pieces to the target."); +// return; +// } + + messageAll('MsgAdminForce', "\c3"@%sender.nameBase@"\c2 is transferring "@(%sender.sex $= "Male" ? "his" : "her")@" pieces to \c3"@%target.nameBase@"\c2."); + GivePieces(%sender, %target); + } +} + +function ccSetAccess(%sender, %args) +{ + if(!isObject(%sender.player)) + return; + + %pos = %sender.player.getMuzzlePoint($WeaponSlot); + %vec = %sender.player.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,100)); + %obj = containerraycast(%pos,%targetpos,$typemasks::staticshapeobjecttype,%sender.player); + %obj = getword(%obj,0); + %dataBlock = %obj.getDataBlock(); + %className = %classname; + %owner = %obj.owner; + + if(!isObject(%obj)) + return; + + if(!%obj.isDoor || %obj.collisionType != 2) + { + messageClient(%sender, "", "\c2Object is either not a door, or is not the right type of door."); + return; + } + + if(%obj.getOwner() != %sender && !%sender.isAdmin) + { + messageClient(%sender, "", "\c2You do not own that."); + return; + } + + %args = getWord(%args, 0); + if(%args < 1) + { + messageClient(%sender, "", "\c2Invalid access level."); + return; + } + + %obj.accessLevel = %args; + messageClient(%sender, "", "\c2Door's access level set to \c3"@%args@"."); +} + +function ccSetPAccess(%sender, %args) +{ + %target = plnametocid(getword(%args, 0)); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2Target doesn't exist."); + return; + } + + %level = getWord(%args, 1); + if(%level < 0) + { + messageClient(%sender, "", "\c2Invalid access level to give."); + return; + } + + %formerLevel = %target.givenAccess[%sender]; + if(%formerLevel == %level) + { + messageClient(%sender, "", "\c2That person's access level is already \c3"@%level@"\c2!"); + return; + } + + %target.givenAccess[%sender] = %level; + messageClient(%target, "", "\c3"@%sender.nameBase@"\c2 has \c3"@(%formerLevel < %level ? "increased" : "decreased")@"\c2 your access level to \c3"@%level@"\c2."); + messageClient(%sender, "", "\c2You have \c3"@(%formerLevel < %level ? "increased" : "decreased") SPC %target.nameBase@"'s\c2 access level to \c3"@%level@"\c2."); +} + +//============================================================================== +// Below is where you may add custom chat commands, to keep things organized. +//------------------------------------------------------------------------------ +// Custom Chat Commands: +//============================================================================== diff --git a/Scripts/chatMenuHud.cs b/Scripts/chatMenuHud.cs new file mode 100644 index 0000000..fe78140 --- /dev/null +++ b/Scripts/chatMenuHud.cs @@ -0,0 +1,236 @@ +//------------------------------------------------------------------------------ +// +// chatMenuHud.cs +// +//------------------------------------------------------------------------------ + +if ( isFile( "prefs/customVoiceBinds.cs" ) ) + $defaultVoiceBinds = false; +else + $defaultVoiceBinds = true; + +// Load in all of the installed chat items: +exec( "scripts/cannedChatItems.cs" ); + + +//------------------------------------------------------------------------------ +// Chat menu loading function: +new SimSet( ChatMenuList ); // Store all of the chat menu maps here so that we can delete them later: +function activateChatMenu( %filename ) +{ + if ( isFile( %filename ) || isFile( %filename @ ".dso" ) ) + { + // Clear the old chat menu: + ChatMenuList.clear(); + + // Create the root of the new menu: + $RootChatMenu = new ActionMap(); + ChatMenuList.add( $RootChatMenu ); + $CurrentChatMenu = $RootChatMenu; + $CurrentChatMenu.optionCount = 0; + $CurrentChatMenu.bindCmd(keyboard, escape, "cancelChatMenu();", ""); + + // Build the new chat menu: + exec( %filename ); + } + else + error( "Chat menu file \"" @ %filename @ "\" not found!" ); +} + +//------------------------------------------------------------------------------ +// Chat menu building functions: +function startChatMenu(%heading) +{ + %key = firstWord(%heading); + %text = restWords(%heading); + %menu = new ActionMap(); + ChatMenuList.add( %menu ); + %cm = $CurrentChatMenu; + %cm.bindCmd(keyboard, %key, "setChatMenu(\"" @ %text @ "\", " @ %menu @ ");", ""); + %cm.option[%cm.optionCount] = %key @ ": " @ %text; + %cm.command[%cm.optionCount] = %menu; // Save this off here for later... + %cm.isMenu[%cm.optionCount] = 1; + %cm.optionCount++; + %menu.parent = %cm; + %menu.bindCmd(keyboard, escape, "cancelChatMenu();", ""); + %menu.optionCount = 0; + $CurrentChatMenu = %menu; +} + +function endChatMenu() +{ + $CurrentChatMenu = $CurrentChatMenu.parent; +} + +function addChat(%keyDesc, %command) +{ + %key = firstWord(%keyDesc); + %text = restWords(%keyDesc); + %cm = $CurrentChatMenu; + %cm.bindCmd(keyboard, %key, "issueChatCmd(" @ %cm @ "," @ %cm.optionCount @ ");", ""); + %cm.option[%cm.optionCount] = %key @ ": " @ %text; + %cm.command[%cm.optionCount] = %command; + %cm.isMenu[%cm.optionCount] = 0; + %cm.optionCount++; +} + + +//------------------------------------------------------------------------------ +// Chat menu hud functions: +$ChatMenuHudLineCount = 0; +function activateChatMenuHud( %make ) +{ + if(%make && !TaskHudDlg.isVisible()) + showChatMenuHud(); +} + +function showChatMenuHud() +{ + Canvas.pushDialog(ChatMenuHudDlg); + ChatMenuHudDlg.setVisible(true); + setChatMenu(Root, $RootChatMenu); +} + +function cancelChatMenu() +{ + $CurrentChatMenu.pop(); + $CurrentChatMenu = $RootChatMenu; + Canvas.popDialog(ChatMenuHudDlg); + ChatMenuHudDlg.setVisible(false); +} + +function setChatMenu( %name, %menu ) +{ + for ( %i = 0; %i < $ChatMenuHudLineCount; %i++ ) + chatMenuHud.remove( $ChatMenuHudText[%i] ); + + $ChatMenuHudLineCount = %menu.optionCount + 1; + chatMenuHud.extent = "170" SPC ( $ChatMenuHudLineCount * 15 ) + 8; + + // First add the menu title line: + $ChatMenuHudText[0] = new GuiTextCtrl() + { + profile = "GuiHudVoiceMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 3"; + extent = "165 20"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = "\c2" @ %name @ " Menu:"; + }; + chatMenuHud.add( $ChatMenuHudText[0] ); + + // Now add all of the menu options: + for ( %option = 0; %option < %menu.optionCount; %option++ ) + { + %yOffset = ( %option * 15 ) + 18; + + if ( %menu.isMenu[%option] == 1 ) + { + $ChatMenuHudText[%option + 1] = new GuiTextCtrl() + { + profile = "GuiHudVoiceMenuProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 " @ %yOffset; + extent = "165 20"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = " " @ %menu.option[%option]; + }; + } + else + { + $ChatMenuHudText[%option + 1] = new GuiTextCtrl() + { + profile = "GuiHudVoiceCommandProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "5 " @ %yOffset; + extent = "165 20"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + text = " " @ %menu.option[%option]; + }; + } + + chatMenuHud.add( $ChatMenuHudText[%option + 1] ); + } + + //bind "anykey" to closing the chat menu, so if you press an invalid entry, you don't accidently + //open the commander map or something... + %menu.bindCmd(keyboard, "anykey", "cancelChatMenu();", ""); + + // Pop the old menu map and push the new menu's map: + $CurrentChatMenu.pop(); + $CurrentChatMenu = %menu; + %menu.push(); +} + +function issueChatCmd( %menu, %index ) +{ + processChatItemCallbacks( %menu.command[%index] ); + commandToServer( 'CannedChat', %menu.command[%index], false ); + cancelChatMenu(); +} + + +//------------------------------------------------------------------------------ +// Canned chat handler: +function serverCmdCannedChat( %client, %command, %fromAI ) +{ + if(%client.isSilenced) + return; + + %cmdCode = getWord( %command, 0 ); + %cmdId = getSubStr( %cmdCode, 1, strlen( %command ) - 1 ); + %cmdString = getWord( %command, 1 ); + if ( %cmdString $= "" ) + %cmdString = getTaggedString( %cmdCode ); + + if ( !isObject( $ChatTable[%cmdId] ) ) + { + error( %cmdString @ " is not a recognized canned chat command." ); + return; + } + + %chatItem = $ChatTable[%cmdId]; + + //if there is text + if (%chatItem.text !$= "" || !%chatItem.play3D) + { + %message = %chatItem.text @ "~w" @ %chatItem.audioFile; + + if ( %chatItem.teamOnly ) + cannedChatMessageTeam( %client, %client.team, '\c3%1: %2', %client.name, %message, %chatItem.defaultKeys ); + else + cannedChatMessageAll( %client, '\c4%1: %2', %client.name, %message, %chatItem.defaultKeys ); + } + + //if no text, see if the audio is to be played in 3D... + else if ( %chatItem.play3D && %client.player ) + playTargetAudio(%client.target, addTaggedString(%chatItem.audioFile), AudioClosest3d, true); + + if ( %chatItem.animation !$= "" ) + serverCmdPlayAnim(%client, %chatItem.animation); + + // Let the AI respond to the canned chat messages (from humans only) + if (!%fromAI) + CreateVoiceServerTask(%client, %cmdCode); +} + +if ( $defaultVoiceBinds ) + activateChatMenu( "scripts/voiceBinds.cs" ); +else + activateChatMenu( "prefs/customVoiceBinds.cs" ); + diff --git a/Scripts/client.cs b/Scripts/client.cs new file mode 100644 index 0000000..88cf74d --- /dev/null +++ b/Scripts/client.cs @@ -0,0 +1,2439 @@ +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +function SAD(%password) +{ + if(%password !$= "") + commandToServer('SAD', %password); +} + +function SADSetPassword(%password) +{ + commandToServer('SADSetPassword', %password); +} + +function use(%data) +{ + // %data is currently the datablock name of the item + commandToServer('use',%data); +} + +function throw(%data) +{ + // %data is currently the datablock name of the item + commandToServer('throw',%data); +} + +function giveAll() +{ + commandToServer('giveAll'); +} + +function clientCmdSetPlayContent() +{ + if ( $LaunchMode $= "InteriorView" ) + Canvas.setContent( interiorPreviewGui ); + else if ( $LaunchMode $= "TSShow" ) + Canvas.setContent( TSShowGui ); + else + Canvas.setContent( Playgui ); +} + +function clientCmdPickTeamMenu( %teamA, %teamB ) +{ + ClientCmdSetHudMode("PickTeam"); + PickTeamAButton.text = getTaggedString( %teamA ); + PickTeamBButton.text = getTaggedString( %teamB ); + PickTeamFrame.setTitle( "Pick Team" ); + + Canvas.pushDialog( PickTeamDlg ); +} + +function clientCmdProcessPickTeam( %option ) +{ + if( %option !$= "" && %option <= 4 ) + CommandToServer( 'clientPickedTeam', %option ); + else if( %option !$= "" && %option == 5 ) + disconnect(); + + Canvas.popDialog( PickTeamDlg ); +} + +new MessageVector(HudMessageVector); + +$LastHudTarget = 0; + +function addMessageHudLine(%text) +{ + %adjustPos = false; + if( chatPageDown.isVisible() ) + { + %adjustPos = true; + %origPosition = chatHud.position; + } + + //add the message... + while( !chatPageDown.isVisible() && HudMessageVector.getNumLines() && (HudMessageVector.getNumLines() >= $pref::HudMessageLogSize)) + { + %tag = HudMessageVector.getLineTag(0); + if(%tag != 0) + %tag.delete(); + HudMessageVector.popFrontLine(); + } + HudMessageVector.pushBackLine(%text, $LastHudTarget); + $LastHudTarget = 0; + + //now that we've added the message, see if we need to reset the position + if ( %adjustPos ) + { + chatPageDown.setVisible(true); + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " " @ ( $chatScrollLenY[$Pref::chatHudLength] - 6 ); + chatHud.position = %origPosition; + } + else + chatPageDown.setVisible(false); + +} + +function pageUpMessageHud() +{ + //find out the text line height + %textHeight = chatHud.profile.fontSize; + if (%textHeight <= 0) + %textHeight = 12; + + //find out how many lines per page are visible + %chatScrollHeight = getWord(chatHud.getGroup().getGroup().extent, 1); + if (%chatScrollHeight <= 0) + return; + + %pageLines = mFloor(%chatScrollHeight / %textHeight) - 1; + if (%pageLines <= 0) + %pageLines = 1; + + //see how many lines we actually can scroll up: + %chatPosition = -1 * getWord(chatHud.position, 1); + %linesToScroll = mFloor((%chatPosition / %textHeight) + 0.5); + if (%linesToScroll <= 0) + return; + + if (%linesToScroll > %pageLines) + %scrollLines = %pageLines; + else + %scrollLines = %linesToScroll; + + //now set the position + chatHud.position = firstWord(chatHud.position) SPC (getWord(chatHud.position, 1) + (%scrollLines * %textHeight)); + + //display the pageup icon + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " " @ ( $chatScrollLenY[$pref::chatHudLength] - 6 ); + chatPageDown.setVisible(true); +} + +function pageDownMessageHud() +{ + //find out the text line height + %textHeight = chatHud.profile.fontSize; + if (%textHeight <= 0) + %textHeight = 12; + + //find out how many lines per page are visible + %chatScrollHeight = getWord(chatHud.getGroup().getGroup().extent, 1); + if (%chatScrollHeight <= 0) + return; + + %pageLines = mFloor(%chatScrollHeight / %textHeight) - 1; + if (%pageLines <= 0) + %pageLines = 1; + + //see how many lines we actually can scroll down: + %chatPosition = getWord(chatHud.extent, 1) - %chatScrollHeight + getWord(chatHud.position, 1); + %linesToScroll = mFloor((%chatPosition / %textHeight) + 0.5); + if (%linesToScroll <= 0) + return; + + if (%linesToScroll > %pageLines) + %scrollLines = %pageLines; + else + %scrollLines = %linesToScroll; + + //now set the position + chatHud.position = firstWord(chatHud.position) SPC (getWord(chatHud.position, 1) - (%scrollLines * %textHeight)); + + //see if we have should (still) display the pagedown icon + if (%scrollLines < %linesToScroll) + { + chatPageDown.setVisible(true); + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " " @ ( $chatScrollLenY[$Pref::chatHudLength] - 6 ); + } + else + chatPageDown.setVisible(false); +} + +$cursorControlled = true; + +function CursorOff() +{ + if ( $cursorControlled ) + lockMouse(true); + Canvas.cursorOff(); +} + +function CursorOn() +{ + if ( $cursorControlled ) + lockMouse(false); + Canvas.cursorOn(); + Canvas.setCursor(DefaultCursor); +} + +function toggleCursorControl() +{ + // If the user manually toggles the mouse control, lock or unlock for them + if ( $cursorControlled ) + $cursorControlled = false; + else + $cursorControlled = true; + lockMouse($cursorControlled); +} + +if ( $platform $= "linux" ) + GlobalActionMap.bindCmd(keyboard, "ctrl g", "", "toggleCursorControl();"); + +function toggleNetDisplayHud(%val) +{ + if(%val) + { + if(NetGraphHudFrame.isVisible()) + { + NetGraphHudFrame.setVisible(false); + NetBarHudFrame.setVisible(true); + } + else if(NetBarHudFrame.isVisible()) + { + NetBarHudFrame.setVisible(false); + } + else + NetGraphHudFrame.setVisible(true); + } +} + +function PlayGui::onWake(%this) +{ + // Make sure the shell hum is off: + if ( $HudHandle[shellScreen] !$= "" ) + { + alxStop( $HudHandle[shellScreen] ); + $HudHandle[shellScreen] = ""; + } + + $enableDirectInput = "1"; + activateDirectInput(); + + // chat hud dialog + Canvas.pushDialog( MainChatHud ); + chatHud.attach(HudMessageVector); + + // just update the action map here, the huds should be properly setup + updateActionMaps(); + + // hack city - these controls are floating around and need to be clamped + schedule(0, 0, "refreshCenterTextCtrl"); + schedule(0, 0, "refreshBottomTextCtrl"); + + // update the network graph prefs + NetGraphHud.getPrefs(); +} + +function refreshBottomTextCtrl() +{ + BottomPrintText.position = "0 0"; +} + +function refreshCenterTextCtrl() +{ + CenterPrintText.position = "0 0"; +} + +function PlayGui::onSleep(%this) +{ + Canvas.popDialog( MainChatHud ); + + //pop all possible keymaps + moveMap.pop(); + if ( isObject( passengerKeys ) ) + passengerKeys.pop(); + if ( isObject( observerBlockMap ) ) + observerBlockMap.pop(); + if ( isObject( observerMap ) ) + observerMap.pop(); + //flyingCameraMove.pop(); +} + +function onConnectRequestRejected( %msg ) +{ + switch$(%msg) + { + case "CR_INVALID_CONNECT_PACKET": + %error = "Network error - badly formed network packet - this should not happen!"; + case "CR_AUTHENTICATION_FAILED": + %error = "Failed to authenticate with server. Please restart TRIBES 2 and try again."; + case "CR_YOUAREBANNED": + %error = "You are not allowed to play on this server."; + case "CR_SERVERFULL": + %error = "This server is full."; + default: + %error = "Connection error. Please try another server. Error code: (" @ %msg @ ")"; + } + DisconnectedCleanup(); + MessageBoxOK( "REJECTED", %error); +} + +function onChallengeRequestRejected( %msg ) +{ + CloseMessagePopup(); + DisconnectedCleanup(); + switch$(%msg) + { + case "PASSWORD": + if ( $JoinGamePassword $= "" ) + Canvas.pushDialog( PasswordDlg ); + else + { + $JoinGamePassword = ""; + MessageBoxOK( "REJECTED", "That password is incorrect."); + } + return; + case "CHR_PROTOCOL_SERVER": + %error = "Incompatible protocol version: The server is running an older, incompatible version of Tribes 2."; + case "CHR_PROTOCOL": + %error = "Incompatible protocol version: You must upgrade your game version to play on this server."; + case "CHR_NOT_AUTHENTICATED": + %error = "This is an online server - you must be logged in to play on it."; + case "CHR_INVALID_SERVER_PACKET": + %error = "Invalid server response packet. This should not happen."; + case "WS_PeerAuthServer_ExpiredClientCertificate": + %error = "Authentication error - please restart TRIBES 2 and try again."; + default: + %error = "Connection challenge error. Please try another server. Error code: (" @ %msg @ ")"; + } + MessageBoxOK( "REJECTED", %error ); +} + +function onConnectRequestTimedOut() +{ + DisconnectedCleanup(); + MessageBoxOK( "TIMED OUT", "Your connection to the server timed out." ); +} + +function onConnectionToServerTimedOut() +{ + DisconnectedCleanup(); + MessageBoxOK( "TIMED OUT", "Your connection to the server timed out."); +} + +function onConnectionToServerLost( %msg ) +{ + DisconnectedCleanup(); + if ( %msg $= "" ) + %msg = "Your connection to the server was lost."; + MessageBoxOK( "DISCONNECTED", %msg ); +} + +// Client voting functions: +function startNewVote(%name, %arg1, %arg2, %arg3, %arg4, %playerVote) +{ + if ( %arg1 $= "" ) + %arg1 = 0; + if ( %arg2 $= "" ) + %arg2 = 0; + if ( %arg3 $= "" ) + %arg3 = 0; + if ( %arg4 $= "" ) + %arg4 = 0; + if ( %playerVote $= "" ) + %playerVote = 0; + + commandToServer('startNewVote', %name, %arg1, %arg2, %arg3, %arg4, %playerVote); +} + +function setPlayerVote(%vote) +{ + commandToServer('setPlayerVote', %vote); +} + +function ClientCmdVoteSubmitted(%type) +{ + clientCmdClearBottomPrint(); + + if(%type) + alxPlay(VoteAgainstSound, 0, 0, 0); + else + alxPlay(VoteForSound, 0, 0, 0); +} +// End client voting functions. + +//-------------------------------------------------------------------------- +// Player pref functions: +function getPlayerPrefs( %player ) +{ + %voiceMuted = false; + if ( $PlayingOnline ) + { + if ( !%player.isSmurf ) + { + %record = queryPlayerDatabase( %player.guid ); + if ( %record !$= "" ) + { + if ( firstWord( %record ) == 1 ) + { + %player.chatMuted = true; + commandToServer( 'TogglePlayerMute', %player.clientId ); + } + + %voiceMuted = getWord( %record, 1 ) == 1; + } + } + else + %voiceMuted = true; // For now, automatically mute smurfs + } + + commandToServer( 'ListenTo', %player.clientId, !%voiceMuted, false ); +} + +//-------------------------------------------------------------------------- +function handlePlayerMuted( %msgType, %msgString, %name, %client, %mute ) +{ + if ( isObject( $PlayerList[%client] ) ) + { + $PlayerList[%client].chatMuted = %mute; + if ( $PlayingOnline && !$PlayerList[%client].isSmurf && $PlayerList[%client].guid > 0 ) + setPlayerTextMuted( $PlayerList[%client].guid, %mute ); + } +} + +//-------------------------------------------------------------------------- +function clientCmdEndBomberSight() +{ + PlayGui.remove($bombSightHud); +} + +function clientCmdRemoveReticle() +{ + reticleHud.setBitmap(""); + reticleFrameHud.setVisible(false); +} + +function clientCmdSetBeaconNames(%target, %marker, %vehicle) +{ + setBeaconNames(%target, %marker, %vehicle); +} + +function clientCmdStartBomberSight() +{ + $bombSightHud = new HudBombSight(bombSightName) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "286 206"; + extent = "67 67"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + minDisplayHeight = "20"; + }; + PlayGui.add($bombSightHud); +} + +function tempShowSpeed(%client) +{ + if(!$tmpSpeedShow) + $tmpSpeedShow = true; + else + $tmpSpeedShow = false; + commandToClient(%client, 'toggleSpeed', %client, $tmpSpeedShow); +} + +function clientCmdToggleSpeed(%client, %toggle) +{ + if(%toggle) { + %tempSpeedHud = new GuiTextCtrl(tmpSpeed) { + profile = "GuiTempSpeedProfile"; + horizSizing = "center"; + vertSizing = "top"; + position = "175 200"; + extent = "120 50"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + }; + PlayGui.add(%tempSpeedHud); + %client.updateTempSpeed(%client); + } + else { + cancel(%client.tmpSpeedCheck); + tmpSpeed.delete(); + } +} + +function GameConnection::updateTempSpeed(%client) +{ + commandToClient(%client, 'getTempSpeed'); + %client.tmpSpeedCheck = %client.schedule(100, "updateTempSpeed", %client); +} + +function clientCmdGetTempSpeed() +{ + %vel = getControlObjectSpeed(); + tmpSpeed.setValue(%vel); +} + +function clientCmdInitLoadClientFavorites() +{ + loadFavorite($pref::FavCurrentSelect); +} + +function clientCmdToggleDashHud(%val) +{ + if(!%val) { + if(isObject(vDiagramHud)) + { + vDiagramHud.delete(); + cancel(dashboardHud.speedCheck); + vSpeedBox.delete(); + } + if(isObject(vOverheadHud)) + vOverheadHud.delete(); + if(isObject(vEnergyFrame)) + vEnergyFrame.delete(); + if(isObject(vDamageFrame)) + vDamageFrame.delete(); + if(isObject(vAltitudeBox)) + { + cancel(dashboardHud.altitudeCheck); + vAltitudeBox.delete(); + } + if(isObject(vWeaponOne)) + vWeaponOne.delete(); + if(isObject(vWeaponTwo)) + vWeaponTwo.delete(); + if(isObject(vWeaponThree)) + vWeaponThree.delete(); + if(isObject(vWeapHiliteOne)) + vWeapHiliteOne.delete(); + if(isObject(vWeapHiliteTwo)) + vWeapHiliteTwo.delete(); + if(isObject(vWeapHiliteThree)) + vWeapHiliteThree.delete(); + if(isObject(vPassengerHud)) + vPassengerHud.delete(); + if(isObject(bombardierHud)) + bombardierHud.delete(); + if(isObject(turreteerHud)) + turreteerHud.delete(); + // reset in case of vehicle-specific reticle + //reticleHud.setBitmap(""); + //reticleFrameHud.setVisible(false); + } + dashboardHud.setVisible(%val); +} + +function addEnergyGauge( %vehType ) +{ + switch$ (%vehType) + { + case "Assault" or "Bomber": + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "160 80"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_5"; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + + new HudCapacitor(vCapBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 8"; + extent = "118 8"; + minExtent = "8 8"; + visible = "1"; + fillColor = "1.000 0.729 0.301 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + + default: + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "160 80"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_5"; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + } +} + +function clientCmdShowVehicleGauges(%vehType, %node) +{ + //if(!((%vehType $= "Bomber" || %vehType $= "Assault") && %node > 0)) + if(%node == 0) + { + // common elements that show up on all vehicle pilot HUDs + dashboardHud.diagram = new HudBitmapCtrl(vDiagramHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "200 10"; + extent = "176 108"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dash"; + opacity = "0.8"; + }; + dashboardHud.add(dashboardHud.diagram); + + dashboardHud.vehDiagram = new HudBitmapCtrl(vOverheadHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "256 0"; + extent = "128 128"; + minExtent = "8 8"; + visible = "1"; + bitmap = ""; + opacity = "1.0"; + }; + dashboardHud.add(dashboardHud.vehDiagram); + + addEnergyGauge( %vehType ); + + dashboardHud.dmgBar = new HudBitmapCtrl(vDamageFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "361 80"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + opacity = "0.8"; + + new HudDamage(vDamageBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.000000 1.0000 0.000000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 0.000000"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + verticalFill = false; + displayMounted = true; + opacity = "0.8"; + subRegion = "18 5 97 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.dmgBar); + + dashboardHud.speedBox = new GuiControl(vSpeedBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "210 47"; + extent = "40 40"; + minExtent = "8 8"; + visible = "1"; + + new GuiTextCtrl(vSpeedText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "3 15"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vSpeedTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "18 15"; + extent = "27 15"; + minExtent = "8 8"; + visible = "1"; + text = "KPH"; + }; + }; + dashboardHud.add(dashboardHud.speedBox); + + dashboardHud.updateSpeed(); + } + + switch$ (%vehType) { + case "Shrike" : + vOverheadHud.setBitmap("gui/hud_veh_icon_shrike"); + // add altitude box for flying vehicles + dashboardHud.altBox = new HudBitmapCtrl(vAltitudeBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "371 56"; + extent = "68 22"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_1"; + visible = "1"; + opacity = "0.8"; + + new GuiTextCtrl(vAltitudeText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "19 5"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vAltitudeTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "40 5"; + extent = "12 15"; + minExtent = "8 8"; + visible = "1"; + text = "M"; + }; + }; + dashboardHud.add(dashboardHud.altBox); + dashboardHud.updateAltitude(); + // add right-hand weapons box and highlight + dashboardHud.weapon = new GuiControl(vWeapHiliteOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "358 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeapBkgdOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_2"; + visible = "1"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_blaster"; + visible = "1"; + opacity = "0.8"; + }; + }; + }; + dashboardHud.add(dashboardHud.weapon); + // change to shrike reticle + reticleHud.setBitmap("gui/hud_ret_shrike"); + reticleFrameHud.setVisible(false); + + case "shrike2" : + vOverheadHud.setBitmap("gui/hud_veh_icon_shrike"); + // add altitude box for flying vehicles + dashboardHud.altBox = new HudBitmapCtrl(vAltitudeBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "371 56"; + extent = "68 22"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_1"; + visible = "1"; + opacity = "0.8"; + + new GuiTextCtrl(vAltitudeText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "19 5"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vAltitudeTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "40 5"; + extent = "12 15"; + minExtent = "8 8"; + visible = "1"; + text = "M"; + }; + }; + dashboardHud.add(dashboardHud.altBox); + dashboardHud.updateAltitude(); + // add right-hand weapons box and highlight + dashboardHud.weapon = new GuiControl(vWeapHiliteOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "358 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeapBkgdOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_2"; + visible = "1"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_blaster"; + visible = "1"; + opacity = "0.8"; + }; + }; + new HudBitmapCtrl(vWeap1Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_hilite_right"; + visible = "0"; + opacity = "0.8"; + }; + }; + dashboardHud.add(dashboardHud.weapon); + dashboardHud.weaponTwo = new GuiControl(vWeapHiliteTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "202 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeapBkgdTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_3"; + visible = "1"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_missiles"; + visible = "1"; + opacity = "0.8"; + }; + }; + new HudBitmapCtrl(vWeap2Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_hilite_left"; + visible = "0"; + opacity = "0.8"; + }; + }; + dashboardHud.add(dashboardHud.weaponTwo); + dashboardHud.weaponThree = new GuiControl(vWeapHiliteThree) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "202 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "0"; + + new HudBitmapCtrl(vWeapBkgdThree) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_3"; + visible = "0"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconThree) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_missiles"; + visible = "0"; + opacity = "0.8"; + }; + }; + new HudBitmapCtrl(vWeap3Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_hilite_left"; + visible = "0"; + opacity = "0.8"; + }; + }; + dashboardHud.add(dashboardHud.weaponThree); + $numVWeapons = 3; + reticleHud.setBitmap("gui/ret_chaingun"); + reticleFrameHud.setVisible(false); + + case "apache" : + vOverheadHud.setBitmap("gui/hud_veh_icon_bomber"); + dashboardHud.altBox = new HudBitmapCtrl(vAltitudeBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "371 56"; + extent = "68 22"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_1"; + visible = "1"; + opacity = "0.8"; + + new GuiTextCtrl(vAltitudeText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "19 5"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vAltitudeTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "40 5"; + extent = "12 15"; + minExtent = "8 8"; + visible = "1"; + text = "M"; + }; + }; + dashboardHud.add(dashboardHud.altBox); + dashboardHud.updateAltitude(); + dashboardHud.weapon = new GuiControl(vWeapHiliteOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "358 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeapBkgdOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_2"; + visible = "1"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_blaster"; + visible = "1"; + opacity = "0.8"; + }; + }; + new HudBitmapCtrl(vWeap1Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_hilite_right"; + visible = "0"; + opacity = "0.8"; + }; + }; + dashboardHud.add(dashboardHud.weapon); + dashboardHud.weaponTwo = new GuiControl(vWeapHiliteTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "202 22"; + extent = "80 33"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeapBkgdTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_3"; + visible = "1"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeapIconTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "28 6"; + extent = "25 25"; + minExtent = "8 8"; + bitmap = "gui/hud_missiles"; + visible = "1"; + opacity = "0.8"; + }; + }; + new HudBitmapCtrl(vWeap2Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "82 40"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_hilite_left"; + visible = "0"; + opacity = "0.8"; + }; + }; + dashboardHud.add(dashboardHud.weaponTwo); + $numVWeapons = 2; + reticleHud.setBitmap("gui/ret_chaingun"); + reticleFrameHud.setVisible(false); + + case "Bomber" : + if(%node == 1) + { + // bombardier hud + dashboardHud.bHud = new GuiControl(bombardierHud) { + profile = "GuiDefaultProfile"; + horizSizing = "center"; + vertSizing = "top"; + position = "200 75"; + extent = "240 50"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeap1Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "18 9"; + extent = "80 44"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_hilite_left"; + opacity = "0.3"; + }; + new HudBitmapCtrl(vWeap2Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "141 9"; + extent = "80 44"; + minExtent = "8 8"; + visible = "0"; + bitmap = "gui/hud_veh_new_hilite_right"; + opacity = "0.3"; + }; + new HudBitmapCtrl(vWeap3Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "99 9"; + extent = "40 44"; + minExtent = "8 8"; + visible = "0"; + bitmap = "gui/hud_veh_new_hilite_middle"; + opacity = "0.3"; + }; + + new HudBitmapCtrl(bombardierFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "20 8"; + extent = "200 40"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_bombardier_dash"; + opacity = "1.0"; + + new HudBitmapCtrl(vWeaponOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "28 5"; + extent = "25 25"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_blaster"; + }; + + new HudBitmapCtrl(vWeaponTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "87 6"; + extent = "25 25"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_targetlaser"; + }; + + new HudBitmapCtrl(vWeaponThree) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "147 6"; + extent = "25 25"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_bomb"; + }; + }; + }; + dashboardHud.add(dashboardHud.bHud); + + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "110 95"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + flipVertical = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + new HudCapacitor(vCapBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 8"; + extent = "118 8"; + minExtent = "8 8"; + visible = "1"; + fillColor = "1.000 0.729 0.301 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + + dashboardHud.dmgBar = new HudBitmapCtrl(vDamageFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "410 95"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + flipVertical = true; + bitmap = "gui/hud_veh_new_dashpiece_4"; + opacity = "0.8"; + + new HudDamage(vDamageBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.000000 1.0000 0.000000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 0.000000"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + verticalFill = false; + displayMounted = true; + opacity = "0.8"; + subRegion = "18 5 97 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.dmgBar); + $numVWeapons = 3; + reticleHud.setBitmap("gui/hud_ret_shrike"); + reticleFrameHud.setVisible(false); + } + else if(%node == 0) + { + // pilot dashboard hud + vOverheadHud.setBitmap("gui/hud_veh_icon_bomber"); + // add altitude box for flying vehicles + dashboardHud.altBox = new HudBitmapCtrl(vAltitudeBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "371 56"; + extent = "68 22"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_1"; + visible = "1"; + opacity = "0.8"; + + new GuiTextCtrl(vAltitudeText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "19 5"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vAltitudeTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "40 5"; + extent = "12 15"; + minExtent = "8 8"; + visible = "1"; + text = "M"; + }; + }; + dashboardHud.add(dashboardHud.altBox); + dashboardHud.updateAltitude(); + } + else + { + // tailgunner hud + dashboardHud.vehDiagram = new HudBitmapCtrl(vOverheadHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "256 0"; + extent = "128 128"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_icon_bomber"; + opacity = "1.0"; + }; + dashboardHud.add(dashboardHud.vehDiagram); + + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "177 50"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_5"; + flipVertical = true; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + + dashboardHud.dmgBar = new HudBitmapCtrl(vDamageFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "345 50"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + flipVertical = true; + opacity = "0.8"; + + new HudDamage(vDamageBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.000000 1.0000 0.000000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 0.000000"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + verticalFill = false; + displayMounted = true; + opacity = "0.8"; + subRegion = "18 5 97 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + }; + }; + dashboardHud.add(dashboardHud.dmgBar); + } + if(%node != 1) + { + // passenger slot "dots" + vOverheadHud.passengerHud = new GuiControl(vPassengerHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "101 101"; + minExtent = "8 8"; + visible = "1"; + + new GuiBitmapCtrl(vPassenger0Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "59 24"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + bitmap = "gui/hud_veh_seatdot"; + }; + new GuiBitmapCtrl(vPassenger1Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "59 39"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + bitmap = "gui/hud_veh_seatdot"; + }; + new GuiBitmapCtrl(vPassenger2Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "59 84"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + bitmap = "gui/hud_veh_seatdot"; + }; + }; + vOverheadHud.add(vOverheadHud.passengerHud); + } + case "HAPC" : + if(%node == 0) + { + vOverheadHud.setBitmap("gui/hud_veh_icon_hapc"); + // add altitude box for flying vehicles + dashboardHud.altBox = new HudBitmapCtrl(vAltitudeBox) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "371 56"; + extent = "68 22"; + minExtent = "8 8"; + bitmap = "gui/hud_veh_new_dashpiece_1"; + visible = "1"; + opacity = "0.8"; + + new GuiTextCtrl(vAltitudeText) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "19 5"; + extent = "18 15"; + minExtent = "8 8"; + visible = "1"; + text = "test"; + }; + new GuiTextCtrl(vAltitudeTxtLbl) { + profile = "GuiDashTextProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "40 5"; + extent = "12 15"; + minExtent = "8 8"; + visible = "1"; + text = "M"; + }; + }; + dashboardHud.add(dashboardHud.altBox); + updateVehicleAltitude(); + dashboardHud.updateAltitude(); + + } + else + { + // passenger hud + dashboardHud.vehDiagram = new HudBitmapCtrl(vOverheadHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "256 0"; + extent = "128 128"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_icon_hapc"; + opacity = "1.0"; + }; + dashboardHud.add(dashboardHud.vehDiagram); + + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "180 30"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_5"; + flipVertical = true; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + + dashboardHud.dmgBar = new HudBitmapCtrl(vDamageFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "342 30"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + flipVertical = true; + opacity = "0.8"; + + new HudDamage(vDamageBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.000000 1.0000 0.000000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 0.000000"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + verticalFill = false; + displayMounted = true; + opacity = "0.8"; + subRegion = "18 5 97 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + }; + }; + dashboardHud.add(dashboardHud.dmgBar); + } + // passenger slot "dots" + vOverheadHud.passengerHud = new GuiControl(vPassengerHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "101 101"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + + new GuiBitmapCtrl(vPassenger0Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "59 65"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + new GuiBitmapCtrl(vPassenger1Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "59 84"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + new GuiBitmapCtrl(vPassenger2Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "38 29"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + new GuiBitmapCtrl(vPassenger3Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "38 50"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + new GuiBitmapCtrl(vPassenger4Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "80 50"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + new GuiBitmapCtrl(vPassenger5Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "80 29"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + setFirstResponder = "0"; + modal = "1"; + bitmap = "gui/hud_veh_seatdot"; + wrap = "0"; + }; + }; + vOverheadHud.add(vOverheadHud.passengerHud); + + case "Assault" : + if(%node == 1) + { + // turreteer hud + dashboardHud.tHud = new GuiControl(turreteerHud) { + profile = "GuiDefaultProfile"; + horizSizing = "center"; + vertSizing = "top"; + position = "225 70"; + extent = "240 50"; + minExtent = "8 8"; + visible = "1"; + + new HudBitmapCtrl(vWeap1Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "40 11"; + extent = "80 44"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_hilite_left"; + opacity = "0.4"; + }; + new HudBitmapCtrl(vWeap2Hilite) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "118 11"; + extent = "80 44"; + minExtent = "8 8"; + visible = "0"; + bitmap = "gui/hud_veh_new_hilite_right"; + opacity = "0.4"; + }; + + new HudBitmapCtrl(turreteerFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "center"; + vertSizing = "bottom"; + position = "20 8"; + extent = "152 36"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_veh_new_tankgunner_dash"; + opacity = "0.8"; + + new HudBitmapCtrl(vWeaponOne) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "25 8"; + extent = "25 25"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_chaingun"; + }; + + new HudBitmapCtrl(vWeaponTwo) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "bottom"; + position = "99 8"; + extent = "25 25"; + minExtent = "8 8"; + visible = "1"; + bitmap = "gui/hud_mortor"; + }; + }; + }; + dashboardHud.add(dashboardHud.tHud); + + dashboardHud.nrgBar = new HudBitmapCtrl(vEnergyFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "134 95"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + flipVertical = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + opacity = "0.8"; + + new HudEnergy(vEnergyBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.353000 0.373000 0.933000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + new HudCapacitor(vCapBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 8"; + extent = "118 8"; + minExtent = "8 8"; + visible = "1"; + fillColor = "1.000 0.729 0.301 0.800000"; + frameColor = "0.000000 1.000000 0.000000 1.000000"; + autoCenter = "0"; + autoResize = "0"; + displayMounted = true; + bitmap = "gui/hud_veh_new_dashpiece_5"; + verticalFill = false; + subRegion = "4 5 98 5"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.nrgBar); + + dashboardHud.dmgBar = new HudBitmapCtrl(vDamageFrame) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "390 95"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + flipVertical = true; + bitmap = "gui/hud_veh_new_dashpiece_4"; + opacity = "0.8"; + + new HudDamage(vDamageBar) { + profile = "GuiDefaultProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "118 19"; + minExtent = "8 8"; + visible = "1"; + fillColor = "0.000000 1.0000 0.000000 0.800000"; + frameColor = "0.000000 1.000000 0.000000 0.000000"; + bitmap = "gui/hud_veh_new_dashpiece_4"; + verticalFill = false; + displayMounted = true; + opacity = "0.8"; + subRegion = "18 5 97 10"; + pulseRate = "500"; + pulseThreshold = "0.3"; + //modColor = "1.000000 0.500000 0.000000 1.000000"; + }; + }; + dashboardHud.add(dashboardHud.dmgBar); + + $numVWeapons = 2; + // add tank chaingun reticle + reticleHud.setBitmap("gui/hud_ret_tankchaingun"); + reticleFrameHud.setVisible(false); + } + else + { + // node 0 == driver + vOverheadHud.setBitmap("gui/hud_veh_icon_assault"); + // passenger slot "dots" + vOverheadHud.passengerHud = new GuiControl(vPassengerHud) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "101 101"; + minExtent = "8 8"; + visible = "1"; + + new GuiBitmapCtrl(vPassenger0Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "64 30"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + bitmap = "gui/hud_veh_seatdot"; + }; + new GuiBitmapCtrl(vPassenger1Slot) { + profile = "GuiDashBoxProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "53 53"; + extent = "10 10"; + minExtent = "3 3"; + visible = "0"; + bitmap = "gui/hud_veh_seatdot"; + }; + }; + vOverheadHud.add(vOverheadHud.passengerHud); + } + + case "Hoverbike" : + vOverheadHud.setBitmap("gui/hud_veh_icon_hoverbike"); + + case "MPB" : + vOverheadHud.setBitmap("gui/hud_veh_icon_mpb"); + + } + if(%node == 0) + vDiagramHud.setVisible(true); + else + if(isObject(vDiagramHud)) + vDiagramHud.setVisible(false); +} + +function GuiControl::updateAltitude(%this) +{ + %alt = getControlObjectAltitude(); + vAltitudeText.setValue(%alt); + %this.altitudeCheck = %this.schedule(500, "updateAltitude"); +} + +function GuiControl::updateSpeed(%this) +{ + %vel = getControlObjectSpeed(); + // convert from m/s to km/h + %cVel = mFloor(%vel * 3.6); // m/s * (3600/1000) = km/h + vSpeedText.setValue(%cVel); + %this.speedCheck = %this.schedule(500, "updateSpeed"); +} + +//function clientCmdShowVehicleWeapons(%vehicleType) +//{ + // all vehicle weapons are energy based; a -1 displays an infinity symbol + // for that weapon's ammo amount + //switch$ (%vehicleType) + //{ + // case "ScoutFlyer": + // // blaster + // vWeaponsBox.addWeapon(0, -1); + // case "BomberFlyer": + // // plasma, bomb and targeting laser + // vWeaponsBox.addWeapon(1, -1); + // vWeaponsBox.addWeapon(3, -1); + // vWeaponsBox.addWeapon(4, -1); + // case "AssaultVehicle": + // vWeaponsBox.addWeapon(1, -1); + // vWeaponsBox.addWeapon(3, -1); + //} + //vWeaponsBox.setVisible(true); +//} + +// if set, then static shapes with data member 'noIndividualDamage' set will +// not display their damage bars +function clientCmdProtectingStaticObjects(%val) +{ + NavHud.protectedStatics = %val; +} + +function clientCmdCheckPassengers(%pString) +{ + // since each slot is represented by a "1" or a "0" followed by a space, the length + // of the string divided by 2 is equal to the number of slots in the vehicle + %numSlots = strlen(%pString) / 2; + for(%i = 0; %i < %numSlots; %i++) + { + %pass = "vPassenger" @ %i @ "Slot"; + if(isObject(%pass)) + if(getWord(%pString, %i) $= "1") + %pass.setVisible(true); + else + %pass.setVisible(false); + } +} + +function clientCmdShowPassenger(%slot, %full) +{ + %dotNum = "vPassenger" @ %slot @ "Slot"; + if(isObject(%dotNum)) + %dotNum.setVisible(%full); +} + +function clientCmdClearPassengers() +{ + for(%i = 1; %i < 6; %i++) + { + %pass = "vPassenger" @ %i @ "Slot"; + %pass.setVisible(false); + } +} + +addMessageCallback( 'MsgMissionDropInfo', handleDropInfoMessage ); +addMessageCallback( 'MsgTeamList', handleTeamListMessage ); +addMessageCallback( 'LeaveMissionArea', HandleLeaveMissionAreaAlarmMessage ); +addMessageCallback( 'EnterMissionArea', HandleEnterMissionAreaAlarmMessage ); +addMessageCallback( 'msgBountyStreakBonus', HandleBountyStreakMessage ); +addMessageCallback( 'onClientKicked', handleIveBeenKicked ); +addMessageCallback( 'onClientBanned', handleIveBeenBanned ); +addMessageCallback( 'msgDeploySensorRed', clientDeploySensorRed ); +addMessageCallback( 'msgDeploySensorGrn', clientDeploySensorGrn ); +addMessageCallback( 'msgDeploySensorOff', clientDeploySensorOff ); +addMessageCallback( 'msgPackIconOff', clientPackIconOff ); +addMessageCallback( 'MsgForceObserver', HandleForceObserver ); +addMessageCallback( 'MsgPlayerMuted', handlePlayerMuted ); + +//------------------------------------------------------------------------------ +// Siege-specific callbacks: +addMessageCallback( 'MsgSiegeHalftime', handleSiegeHalftimeMessage ); +addMessageCallback( 'MsgSiegeResult', handleSiegeResultMessage ); +addMessageCallback( 'MsgSiegeAddLine', handleSiegeLineMessage ); +//------------------------------------------------------------------------------ + +function HandleForceObserver( %msgType, %msgString ) +{ + +} + +function handleIveBeenBanned(%msgType, %msgString) +{ + DisconnectedCleanup(); +} + +function handleIveBeenKicked(%msgType, %msgString) +{ + DisconnectedCleanup(); +} + +function clientDeploySensorRed() +{ + deploySensor.color = "255 0 0"; + deploySensor.setVisible(true); +} + +function clientDeploySensorGrn() +{ + deploySensor.color = "0 255 0"; + deploySensor.setVisible(true); +} + +function clientDeploySensorOff() +{ + deploySensor.setVisible(false); +} + +function clientPackIconOff() +{ + backpackIcon.setBitmap(""); + backpackFrame.setVisible(false); + backpackText.setValue(""); + backpackText.setVisible(false); + backpackFrame.pack = false; +} + +function HandleBountyStreakMessage(%msgType, %msgString, %client, %streak, %award) +{ + %delay = alxGetWaveLen("fx/misc/bounty_bonus.wav"); + %overlap = 0.50; + + alxPlay(BountyBellSound, 0, 0, 0); //first bell + for (%loop = 1; %loop < %award; %loop++) //any repetitions, overlapped. + schedule((%delay * %loop) * %overlap, 0, "alxPlay", BountyBellSound, 0, 0, 0); +} + +function HandleLeaveMissionAreaAlarmMessage(%msgType, %msgString) +{ + //Tinman - sounds are now sent by the individual game script + //if(ServerConnection.OutOfBoundsHandle $= "") + // ServerConnection.OutOfBoundsHandle = alxPlay(OutOfBoundsSound, 0, 0, 0); +} + +function HandleEnterMissionAreaAlarmMessage(%msgType, %msgString) +{ + //Tinman - sounds are now sent by the individual game script + //if(ServerConnection.OutOfBoundsHandle !$= "") + // alxStop(ServerConnection.OutOfBoundsHandle); + // + //ServerConnection.OutOfBoundsHandle = ""; +} + +function handleDropInfoMessage( %msgType, %msgString, %map, %gameType, %serverName ) +{ + $clServerName = %serverName; + $clMissionName = %map; + $clMissionType = %gameType; +} + +function handleTeamListMessage( %msgType, %msgString, %teamCount, %teamList ) +{ + // Save off the team names: + $clTeamCount = %teamCount; + for ( %i = 0; %i < %teamCount; %i++ ) + $clTeamScore[%i + 1, 0] = getRecord( %teamList, %i ); + + // Initialize the lobby: + LobbyPlayerList.initColumns(); +} + +//---------------------------------------------------------------------------- + +function clientCmdStartEffect( %effect ) +{ + // Put in iterations + StartEffect( %effect ); +} + +function clientCmdStopEffect( %effect ) +{ + StopEffect( %effect ); +} + +function clientCmdPickTeam() +{ + +} + +function clientCmdMissionStartPhase1(%seq, %missionName, %musicTrack) +{ + echo( "got client StartPhase1..." ); + + // Reset the loading progress controls: + LoadingProgress.setValue( 0 ); + DB_LoadingProgress.setValue( 0 ); + LoadingProgressTxt.setValue( "LOADING MISSION" ); + DB_LoadingProgressTxt.setValue( "LOADING MISSION" ); + + clientCmdPlayMusic(%musicTrack); + commandToServer('MissionStartPhase1Done', %seq); + clientCmdResetCommandMap(); +} + +function clientCmdMissionStartPhase2(%seq) { + $ConstructionServerPretext = ""; + $ConstructionServerVersion = ""; + $ConstructionServerCredits = ""; + $ConstructionServerQVer = ""; + $ConstructionServerString = ""; + commandToServer('ConstructionQueryServer'); + commandToServer('ConstructionRegisterClient',"2"); + // clean some stuff up. + MessageHud.close(); + purgeResources(); + if (!$pref::NoClearConsole) + cls(); + commandToServer('MissionStartPhase2Done', %seq); +} + +function clientCmdQueryServerReply(%pretext,%version,%credits,%qVer) { + $ConstructionServerPretext = %pretext; + $ConstructionServerVersion = %version; + $ConstructionServerCredits = %credits; + $ConstructionServerQVer = %qVer; + $ConstructionServerString = %pretext SPC %version @ " - " @ %credits; + echo($ConstructionServerString @ " - (" @ %qVer @ ")"); +} + +function clientCmdMissionStartPhase3(%seq, %missionName) +{ + $MSeq = %seq; + + //Reset Inventory Hud... + if($Hud['inventoryScreen'] !$= "") + { + %favList = $Hud['inventoryScreen'].data[0, 1].type TAB $Hud['inventoryScreen'].data[0, 1].getValue(); + for ( %i = 1; %i < $Hud['inventoryScreen'].count; %i++ ) + if($Hud['inventoryScreen'].data[%i, 1].getValue() $= invalid) + %favList = %favList TAB $Hud['inventoryScreen'].data[%i, 1].type TAB "EMPTY"; + else + %favList = %favList TAB $Hud['inventoryScreen'].data[%i, 1].type TAB $Hud['inventoryScreen'].data[%i, 1].getValue(); + commandToServer( 'setClientFav', %favList ); + } + else + commandToServer( 'setClientFav', $pref::Favorite[$pref::FavCurrentSelect]); + + // needed? + $MissionName = %missionName; + //commandToServer( 'getScores' ); + + // only show dialog if actually lights + if(lightScene("sceneLightingComplete", $LaunchMode $= "SceneLight" ? "forceWritable" : "")) + { + error("beginning SceneLighting...."); + schedule(1, 0, "updateLightingProgress"); + $lightingMission = true; + LoadingProgress.setValue( 0 ); + DB_LoadingProgress.setValue( 0 ); + LoadingProgressTxt.setValue( "LIGHTING MISSION" ); + DB_LoadingProgressTxt.setValue( "LIGHTING MISSION" ); + $missionLightStarted = true; + Canvas.repaint(); + } +} + +function clientCmdMissionEnd(%seq) +{ + alxStopAll(); + // disable mission lighting if it's going (since the interiors will be gone in a sec) + $lightingMission = false; + $sceneLighting::terminateLighting = true; +} + +function clientCmdSetPowerAudioProfiles(%up, %down) +{ + setPowerAudioProfiles(%up, %down); +} + +function ghostAlwaysStarted(%ghostCount) +{ + echo( "starting to ghost " @ %ghostCount @ " server objects...."); + + LoadingProgress.setValue( 0 ); + DB_LoadingProgress.setValue( 0 ); + LoadingProgressTxt.setValue( "LOADING OBJECTS" ); + DB_LoadingProgressTxt.setValue( "LOADING OBJECTS" ); + Canvas.repaint(); + $ghostCount = %ghostCount; + $ghostsRecvd = 0; +} + +function ghostAlwaysObjectReceived() +{ + $ghostsRecvd++; + %pct = $ghostsRecvd / $ghostCount; + LoadingProgress.setValue( %pct ); + DB_LoadingProgress.setValue( %pct ); + Canvas.repaint(); +} + +function updateLightingProgress() +{ + if( $SceneLighting::lightingProgress == 0) + { + if($sceneLightStarted) + { + $sceneLightStarted = false; + } + else + $SceneLighting::lightingProgress = 1; + } + + LoadingProgress.setValue( $SceneLighting::lightingProgress ); + DB_LoadingProgress.setValue( $SceneLighting::lightingProgress ); + if($lightingMission) + $lightingProgressThread = schedule(1, 0, "updateLightingProgress"); +} + +function sceneLightingComplete() +{ + LoadingProgress.setValue( 1 ); + DB_LoadingProgress.setValue( 1 ); + + echo("Scenelighting done..."); + $lightingMission = false; + + cleanUpHuds(); + + if($LaunchMode $= "SceneLight") + { + quit(); + return; + } + + clientCmdResetHud(); + commandToServer('SetVoiceInfo', $pref::Audio::voiceChannels, $pref::Audio::decodingMask, $pref::Audio::encodingLevel); + commandToServer('EnableVehicleTeleport', $pref::Vehicle::pilotTeleport ); + commandToServer('MissionStartPhase3Done', $MSeq); +} + +function clientCmdSetVoiceInfo(%channels, %decodingMask, %encodingLevel) +{ + $Audio::serverChannels = %channels; + $Audio::serverDecodingMask = %decodingMask; + $Audio::serverEncodingLevel = %encodingLevel; +} + +function ClientReceivedDataBlock(%index, %total) +{ + %pct = %index / %total; + LoadingProgress.setValue( %pct ); + LoadingProgress.setValue( %pct ); + Canvas.repaint(); +} + +function GameConnection::onTargetLocked( %con, %state ) +{ + if( %state $= "true" ) + { + if( !%con.targetTone ) + %con.targetTone = alxPlay( "sLockedTone", 0, 0, 0 ); + } + else + { + if( %con.targetTone $= "" ) + return; + + if( %con.targetTone ) + alxStop( %con.targetTone ); + + %con.targetTone = ""; + } +} + +function GameConnection::onTrackingTarget( %con, %state ) +{ + if( %state $= "true" ) + { + if( !%con.trackingTargetTone ) + %con.trackingTargetTone = alxPlay( "sSearchingTone", 0, 0, 0 ); + } + else + { + if( %con.trackingTargetTone $= "" ) + return; + + if( %con.trackingTargetTone ) + alxStop( %con.trackingTargetTone ); + + %con.TrackingTargetTone = ""; + } +} + +function GameConnection::onLockWarning( %con, %state ) +{ + if( %state $= "true" ) + { + if( !%con.lockWarningTone ) + %con.lockWarningTone = alxPlay( "sMissileLockWarningTone", 0, 0, 0 ); + + } + else + { + if( %con.lockWarningTone $= "" ) + return; + + if( %con.lockWarningTone ) + alxStop( %con.lockWarningTone ); + + %con.lockWarningTone = ""; + } +} + +function GameConnection::onHomeWarning( %con, %state ) +{ + if( %state $= "true" ) + { + if( !%con.homeWarningTone ) + %con.homeWarningTone = alxPlay( "sMissileHomingWarningTone", 0, 0, 0 ); + } + else + { + if( %con.homeWarningTone $= "" ) + return; + + if( %con.homeWarningTone ) + alxStop( %con.homeWarningTone ); + + %con.homeWarningTone = ""; + } +} + +function GameConnection::initialControlSet(%this) +{ + if ( $LaunchMode $= "InteriorView" ) + { + Canvas.setContent( InteriorPreviewGui ); + return; + } + + if( $LaunchMode $= "TSShow" ) + { + Canvas.setContent( TSShowGui ); + return; + } + + if( Canvas.getContent() != PlayGui.getId() ) + { + Canvas.setContent( PlayGui ); + Canvas.pushDialog( MainChatHud ); + CommandToServer('PlayContentSet'); + } +} + +//------------------------------------------------------------------------------ +// Siege-specific client functions: +//------------------------------------------------------------------------------ +function handleSiegeHalftimeMessage( %msgType, %msgString ) +{ + alxPlay( SiegeSwitchSides, 0, 0, 0 ); + showTaskHudDlg( false ); + SiegeHalftimeText.setText( "" ); +} + +//------------------------------------------------------------------------------ +function handleSiegeResultMessage( %msgType, %msgString, %result ) +{ + SiegeHalftimeHeaderText.setText( "" @ detag( %result ) ); +} + +//------------------------------------------------------------------------------ +function handleSiegeLineMessage( %msgType, %msgString, %line ) +{ + %text = SiegeHalftimeText.getText(); + if ( %text $= "" ) + %newText = detag( %line ); + else + %newText = %text NL detag( %line ); + SiegeHalftimeText.setText( %newText ); +} + +//------------------------------------------------------------------------------ +function clientCmdSetHalftimeClock( %time ) +{ + SiegeHalftimeClock.setTime( %time ); +} + +//------------------------------------------------------------------------------ +function SiegeHalftimeHeaderText::onResize( %this, %width, %height ) +{ + %w = firstWord( SiegeHalftimeHeader.getExtent() ); + SiegeHalftimeHeader.setExtent( %w, %height + 6 ); + %paneHeight = getWord( siegeHalftimeHud.getExtent(), 1 ); + %x = firstWord( SiegeHalftimeScroll.getPosition() ); + %y = %height + 40; + %w = firstWord( SiegeHalftimeScroll.getExtent() ); + %h = %paneHeight - %height - 59; + SiegeHalftimeScroll.resize( %x, %y, %w, %h ); +} + +function reLightMission() { + if ($SceneLighting::lightingProgress == 0 || $SceneLighting::lightingProgress == 1) + lightScene("",forceAlways); +} + +function clientCmdReLightMission() { + if (!$pref::disallowRelight) + reLightMission(); +} diff --git a/Scripts/controlDefaults.cs b/Scripts/controlDefaults.cs new file mode 100644 index 0000000..06c3f60 --- /dev/null +++ b/Scripts/controlDefaults.cs @@ -0,0 +1,1740 @@ +if ( isObject( moveMap ) ) + moveMap.delete(); +new ActionMap(moveMap); + +$vehicletiltrate = 25; + +//------------------------------------------------------------------------------ +// Utility remap functions: +//------------------------------------------------------------------------------ +function ActionMap::copyBind( %this, %otherMap, %command ) +{ + if ( !isObject( %otherMap ) ) + { + error( "ActionMap::copyBind - \"" @ %otherMap @ "\" is not an object!" ); + return; + } + + %bind = %otherMap.getBinding( %command ); + if ( %bind !$= "" ) + { + %device = getField( %bind, 0 ); + %action = getField( %bind, 1 ); + %flags = %otherMap.isInverted( %device, %action ) ? "SDI" : "SD"; + %deadZone = %otherMap.getDeadZone( %device, %action ); + %scale = %otherMap.getScale( %device, %action ); + %this.bind( %device, %action, %flags, %deadZone, %scale, %command ); + } +} + +//------------------------------------------------------------------------------ +function ActionMap::blockBind( %this, %otherMap, %command ) +{ + if ( !isObject( %otherMap ) ) + { + error( "ActionMap::copyBind - \"" @ %otherMap @ "\" is not an object!" ); + return; + } + + %bind = %otherMap.getBinding( %command ); + if ( %bind !$= "" ) + %this.bind( getField( %bind, 0 ), getField( %bind, 1 ), "" ); +} + +//------------------------------------------------------------------------------ +// NON-REMAPPABLE BINDS: +function escapeFromGame() +{ + if(TaskHudDlg.isVisible()) + showTaskHudDlg(false); + + if ( $currentMissionType $= "SinglePlayer" ) + Canvas.pushDialog( SinglePlayerEscapeDlg ); + else + Canvas.setContent( LobbyGui ); +} + +function toggleEditor(%make) +{ + //editor should not be available in the demo version + if (isDemo()) + return; + + if(%make) + { + if(Canvas.getContent() == Editor.getId()) + Editor.close(); + else + Editor.open(); + } +} + +moveMap.bindCmd( keyboard, "escape", "", "escapeFromGame();" ); +moveMap.bind( keyboard, "alt e", toggleEditor ); + +//------------------------------------------------------------------------------ +$movementSpeed = 1; +function setSpeed(%speed) +{ + if(%speed) + $movementSpeed = %speed; +} + +function moveleft(%val) +{ + $mvLeftAction = %val; + if(%val) + commandToServer('checkHtilt',"left"); + else + commandToServer('checkendtilt'); +} + +function moveright(%val) +{ + $mvRightAction = %val; + if(%val) + commandToServer('checkHtilt',"right"); + else + commandToServer('checkendtilt'); +} + +function moveforward(%val) +{ + $mvForwardAction = %val; +} + +function movebackward(%val) +{ + $mvBackwardAction = %val; +} + +function moveup(%val) +{ + $mvUpAction = %val; +} + +function movedown(%val) +{ + $mvDownAction = %val; +} + +function turnLeft( %val ) +{ + $mvYawRightSpeed = %val ? $pref::Input::KeyboardTurnSpeed : 0; +} + +function turnRight( %val ) +{ + $mvYawLeftSpeed = %val ? $pref::Input::KeyboardTurnSpeed : 0; +} + +function panUp( %val ) +{ + $mvPitchDownSpeed = %val ? $pref::Input::KeyboardTurnSpeed : 0; +} + +function panDown( %val ) +{ + $mvPitchUpSpeed = %val ? $pref::Input::KeyboardTurnSpeed : 0; +} + +// based on a default camera fov of 90' +function getMouseAdjustAmount(%val) +{ + return(%val * ($cameraFov / 90) * 0.01); +} + +function yaw(%val) +{ + $mvYaw += getMouseAdjustAmount(%val); +} + +function pitch(%val) +{ + $mvPitch += getMouseAdjustAmount(%val); +} + +moveMap.bind( keyboard, s, moveleft ); +moveMap.bind( keyboard, f, moveright ); +moveMap.bind( keyboard, e, moveforward ); +moveMap.bind( keyboard, d, movebackward ); + +function toggleDepth(%val) +{ + if (%val) { + $testDepth = !$testDepth; + } +} + +function snLine(%val) { if(%val) snapLine(); } +function snToggle(%val) { if(%val) snapToggle(); } + +moveMap.bind( mouse, xaxis, yaw ); +moveMap.bind( mouse, yaxis, pitch ); +moveMap.bind( keyboard, space, jump ); +moveMap.bind( mouse, button0, mouseFire ); +moveMap.bind( mouse, button1, mouseJet ); +//moveMap.bind( keyboard, "shift a", altTrigger ); + +//------------------------------------------------------------------------------ +// MESSAGE HUD FUNCTIONS: +function pageMessageHudUp( %val ) +{ + if ( %val ) + pageUpMessageHud(); +} + +function pageMessageHudDown( %val ) +{ + if ( %val ) + pageDownMessageHud(); +} + +moveMap.bind( keyboard, "pageUp", pageMessageHudUp ); +moveMap.bind( keyboard, "pageDown", pageMessageHudDown ); + +//------------------------------------------------------------------------------ +function voiceCapture( %val ) +{ + if ( %val ) + voiceCapStart(); + else + voiceCapStop(); +} + +moveMap.bind(keyboard, x, voiceCapture); + +//------------------------------------------------------------------------------ +// WEAPON CYCLING FUNCTIONS: +function prevWeapon( %val ) +{ + if ( %val ) + commandToServer( 'cycleWeapon', "prev" ); +} + +function nextWeapon( %val ) +{ + if ( %val ) + commandToServer( 'cycleWeapon', "next" ); +} + +function cycleWeaponAxis( %val ) +{ + if ( %val < 0 ) + commandToServer( 'cycleWeapon', "next" ); + else + commandToServer( 'cycleWeapon', "prev" ); +} + +function cycleNextWeaponOnly( %val ) +{ + if ( %val < 0 ) + commandToServer( 'cycleWeapon', "next" ); +} + +moveMap.bind( keyboard, "shift w", prevWeapon ); +moveMap.bind( keyboard, w, nextWeapon ); +moveMap.bind( mouse, zaxis, cycleWeaponAxis ); + +function toggleFreeLook( %val ) +{ + if ( %val ) + $mvFreeLook = true; + else + $mvFreeLook = false; +} + +function useRepairKit( %val ) +{ + if ( %val ) + use( RepairKit ); +} + +function useBackPack( %val ) +{ + if ( %val ) + commandToServer( 'startUseBackpack', BackPack ); + else + commandToServer( 'endUseBackpack', BackPack ); +} + +function ServerCmdStartUseBackpack( %client, %data ) +{ + %client.deployPack = false; + %client.getControlObject().use( %data ); +} + +function ServerCmdEndUseBackpack( %client ) +{ + %client.deployPack = true; +} + +moveMap.bind( keyboard, z, toggleFreeLook ); +moveMap.bind( keyboard, q, useRepairKit ); +moveMap.bind( keyboard, r, useBackpack ); + +//------------------------------------------------------------------------------ +// WEAPON SLOT SELECTION FUNCTIONS: +function useFirstWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 0 ); +} + +function useSecondWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 1 ); +} + +function useThirdWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 2 ); +} + +function useFourthWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 3 ); +} + +function useFifthWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 4 ); +} + +function useSixthWeaponSlot( %val ) +{ + if ( %val ) + commandToServer( 'selectWeaponSlot', 5 ); +} + +moveMap.bind( keyboard, "1", useFirstWeaponSlot ); +moveMap.bind( keyboard, "2", useSecondWeaponSlot ); +moveMap.bind( keyboard, "3", useThirdWeaponSlot ); +moveMap.bind( keyboard, "4", useFourthWeaponSlot ); +moveMap.bind( keyboard, "5", useFifthWeaponSlot ); +moveMap.bind( keyboard, "6", useSixthWeaponSlot ); + +//------------------------------------------------------------------------------ +// DIRECT WEAPON SELECTION FUNCTIONS: +function useBlaster( %val ) +{ + if ( %val ) + use( Blaster ); +} + +function usePlasma( %val ) +{ + if ( %val ) + use( Plasma ); +} + +function useChaingun( %val ) +{ + if ( %val ) + use( Chaingun ); +} + +function useDisc( %val ) +{ + if ( %val ) + use( Disc ); +} + +function useGrenadeLauncher( %val ) +{ + if ( %val ) + use( GrenadeLauncher ); +} + +function useSniperRifle( %val ) +{ + if ( %val ) + use( SniperRifle ); +} + +function useELFGun( %val ) +{ + if ( %val ) + use( ELFGun ); +} + +function useMortar( %val ) +{ + if ( %val ) + use( Mortar ); +} + +function useMissileLauncher( %val ) +{ + if ( %val ) + use( MissileLauncher ); +} + +function useTargetingLaser( %val ) +{ + if ( %val ) + use( TargetingLaser ); +} + +function useShockLance( %val ) +{ + if ( %val ) + use( ShockLance ); +} + +function useConstructionTool(%val) +{ +if (%val) + use ( ConstructionTool ); +} + +function throwGrenade( %val ) +{ + if ( %val ) + commandToServer( 'startThrowCount' ); + else + commandToServer( 'endThrowCount' ); + $mvTriggerCount4 += $mvTriggerCount4 & 1 == %val ? 2 : 1; +} + +function placeMine( %val ) +{ + if ( %val ) + commandToServer( 'startThrowCount' ); + else + commandToServer( 'endThrowCount' ); + $mvTriggerCount5 += $mvTriggerCount5 & 1 == %val ? 2 : 1; +} + +function placeBeacon( %val ) +{ + if ( %val ) + use( Beacon); +} + +moveMap.bind( keyboard, g, throwGrenade ); +moveMap.bind( keyboard, b, placeMine ); +moveMap.bind( keyboard, h, placeBeacon ); +moveMap.bind( keyboard, l, useTargetingLaser ); + +function throwWeapon( %val ) +{ + if ( %val ) + commandToServer( 'throwWeapon' ); +} + +function throwPack( %val ) +{ + if ( %val ) + commandToServer('throwPack'); +} + +function throwFlag( %val ) +{ + // TR2: Allow variable strength passes + if (objectiveHud.gameType $= "TR2Game") + { + if ( %val ) + { + doFlagGauge(%val); + commandToServer( 'startFlagThrowCount' ); + + // Schedule to remove the HUD in 3 seconds. + $TR2HideGaugeThread = schedule(2500, 0, "hideFlagGauge"); + } + else + { + cancel($TR2HideGaugeThread); + commandToServer( 'endFlagThrowCount' ); + + // If we're still trying to throw it, throw it. Don't bother trying + // to remove this to get max toss flag throws...it doesn't work. + if ($TR2FGaugeThread > 0) + commandToServer('throwFlag'); + doFlagGauge(%val); + } + + // What's this global for? + //$mvTriggerCount6 += $mvTriggerCount6 & 1 == %val ? 2 : 1; + } + else if ( %val ) + commandToServer('throwFlag'); +} + +moveMap.bind( keyboard, "ctrl w", throwWeapon ); +moveMap.bind( keyboard, "ctrl r", throwPack ); +moveMap.bind( keyboard, "ctrl f", throwFlag ); + +function resizeChatHud( %val ) +{ + if ( %val ) + MainChatHud.nextChatHudLen(); +} + +moveMap.bind( keyboard, "p", resizeChatHud ); + +//------------------------------------------------------------------------------ +// ZOOM FUNCTIONS: +if($pref::player::currentFOV $= "") + $pref::player::currentFOV = 45; + +function setZoomFOV(%val) +{ + if(%val) + calcZoomFOV(); +} + +function toggleZoom( %val ) +{ + if ( %val ) + { + if(ZoomHud.isVisible()) + { + cancel(ZoomHud.hideThread); + hideZoomHud(); + } + $ZoomOn = true; + setFov( $pref::player::currentFOV ); + } + else + { + $ZoomOn = false; + setFov( $pref::player::defaultFov ); + } +} + +moveMap.bind(keyboard, t, setZoomFOV); +moveMap.bind(keyboard, a, toggleZoom); + +//------------------------------------------------------------------------------ +// INVENTORY FAVORITE FUNCTIONS: +function toggleInventoryHud( %val ) +{ + if ( %val ) + toggleCursorHuds('inventoryScreen'); +} + +function selectFavorite1( %val ) +{ + if ( %val ) + loadFavorite( 0, 1 ); +} + +function selectFavorite2( %val ) +{ + if ( %val ) + loadFavorite( 1, 1 ); +} + +function selectFavorite3( %val ) +{ + if ( %val ) + loadFavorite( 2, 1 ); +} + +function selectFavorite4( %val ) +{ + if ( %val ) + loadFavorite( 3, 1 ); +} + +function selectFavorite5( %val ) +{ + if ( %val ) + loadFavorite( 4, 1 ); +} + +function selectFavorite6( %val ) +{ + if ( %val ) + loadFavorite( 5, 1 ); +} + +function selectFavorite7( %val ) +{ + if ( %val ) + loadFavorite( 6, 1 ); +} + +function selectFavorite8( %val ) +{ + if ( %val ) + loadFavorite( 7, 1 ); +} + +function selectFavorite9( %val ) +{ + if ( %val ) + loadFavorite( 8, 1 ); +} + +function selectFavorite10( %val ) +{ + if ( %val ) + loadFavorite( 9, 1 ); +} + +function selectFavorite11( %val ) +{ + if ( %val ) + loadFavorite( 10, 1 ); +} + +function selectFavorite12( %val ) +{ + if ( %val ) + loadFavorite( 11, 1 ); +} + +function selectFavorite13( %val ) +{ + if ( %val ) + loadFavorite( 12, 1 ); +} + +function selectFavorite14( %val ) +{ + if ( %val ) + loadFavorite( 13, 1 ); +} + +function selectFavorite15( %val ) +{ + if ( %val ) + loadFavorite( 14, 1 ); +} + +function selectFavorite16( %val ) +{ + if ( %val ) + loadFavorite( 15, 1 ); +} + +function selectFavorite17( %val ) +{ + if ( %val ) + loadFavorite( 16, 1 ); +} + +function selectFavorite18( %val ) +{ + if ( %val ) + loadFavorite( 17, 1 ); +} + +function selectFavorite19( %val ) +{ + if ( %val ) + loadFavorite( 18, 1 ); +} + +function selectFavorite20( %val ) +{ + if ( %val ) + loadFavorite( 19, 1 ); +} + +moveMap.bind( keyboard, numpadenter, toggleInventoryHud ); +moveMap.bind( keyboard, numpad1, selectFavorite1 ); +moveMap.bind( keyboard, numpad2, selectFavorite2 ); +moveMap.bind( keyboard, numpad3, selectFavorite3 ); +moveMap.bind( keyboard, numpad4, selectFavorite4 ); +moveMap.bind( keyboard, numpad5, selectFavorite5 ); +moveMap.bind( keyboard, numpad6, selectFavorite6 ); +moveMap.bind( keyboard, numpad7, selectFavorite7 ); +moveMap.bind( keyboard, numpad8, selectFavorite8 ); +moveMap.bind( keyboard, numpad9, selectFavorite9 ); +moveMap.bind( keyboard, numpad0, selectFavorite10 ); + +moveMap.bind( keyboard, "shift numpad1", selectFavorite11 ); +moveMap.bind( keyboard, "shift numpad2", selectFavorite12 ); +moveMap.bind( keyboard, "shift numpad3", selectFavorite13 ); +moveMap.bind( keyboard, "shift numpad4", selectFavorite14 ); +moveMap.bind( keyboard, "shift numpad5", selectFavorite15 ); +moveMap.bind( keyboard, "shift numpad6", selectFavorite16 ); +moveMap.bind( keyboard, "shift numpad7", selectFavorite17 ); +moveMap.bind( keyboard, "shift numpad8", selectFavorite18 ); +moveMap.bind( keyboard, "shift numpad9", selectFavorite19 ); +moveMap.bind( keyboard, "shift numpad0", selectFavorite20 ); + +moveMap.bind( keyboard, "ctrl numpad0", quickPackEnergyPack ); +moveMap.bind( keyboard, "ctrl numpad1", quickPackRepairPack ); +moveMap.bind( keyboard, "ctrl numpad2", quickPackShieldPack ); +moveMap.bind( keyboard, "ctrl numpad3", quickPackCloakPack ); +moveMap.bind( keyboard, "ctrl numpad4", quickPackJammerPack ); +moveMap.bind( keyboard, "ctrl numpad5", quickPackAmmoPack ); +moveMap.bind( keyboard, "ctrl numpad6", quickPackSatchelCharge ); +moveMap.bind( keyboard, "ctrl numpad7", quickPackDeployableStation ); +moveMap.bind( keyboard, "ctrl numpad8", quickPackIndoorTurret ); +moveMap.bind( keyboard, "ctrl numpad9", quickPackOutdoorTurret ); +moveMap.bind( keyboard, "ctrl numpaddivide", quickPackMotionSensor ); +moveMap.bind( keyboard, "ctrl numpadmult", quickPackPulse ); + +function quickPackRepairPack(%val) +{ + if(%val) + addQuickPackFavorite("Repair Pack"); +} + +function quickPackEnergyPack(%val) +{ + if(%val) + addQuickPackFavorite("Energy Pack"); +} + +function quickPackShieldPack(%val) +{ + if(%val) + addQuickPackFavorite("Shield Pack"); +} + +function quickPackCloakPack(%val) +{ + if(%val) + addQuickPackFavorite("Cloak Pack"); +} + +function quickPackJammerPack(%val) +{ + if(%val) + addQuickPackFavorite("Sensor Jammer Pack"); +} + +function quickPackAmmoPack(%val) +{ + if(%val) + addQuickPackFavorite("Ammunition Pack"); +} + +function quickPackSatchelCharge(%val) +{ + if(%val) + addQuickPackFavorite("Satchel Charge"); +} + +function quickPackDeployableStation(%val) +{ + if(%val) + addQuickPackFavorite("Inventory Station"); +} + +function quickPackIndoorTurret(%val) +{ + if(%val) + addQuickPackFavorite("Spider Clamp Turret"); +} + +function quickPackOutdoorTurret(%val) +{ + if(%val) + addQuickPackFavorite("Landspike Turret"); +} + +function quickPackMotionSensor(%val) +{ + if(%val) + addQuickPackFavorite("Motion Sensor Pack"); +} + +function quickPackPulse(%val) +{ + if(%val) + addQuickPackFavorite("Pulse Sensor Pack"); +} + +function quickPackMortarBarrel(%val) +{ + if(%val) + addQuickPackFavorite("Mortar Turret Barrel"); +} + +function quickPackElfBarrel(%val) +{ + if(%val) + addQuickPackFavorite("ELF Turret Barrel"); +} + +function quickPackAABarrel(%val) +{ + if(%val) + addQuickPackFavorite("AA Turret Barrel"); +} + +function quickPackPlasmaBarrel(%val) +{ + if(%val) + addQuickPackFavorite("Plasma Turret Barrel"); +} + +function quickPackMissileBarrel(%val) +{ + if(%val) + addQuickPackFavorite("Missile Turret Barrel"); +} + +function quickPackFlashGrenade(%val) +{ + if(%val) + addQuickPackFavorite("Whiteout Grenade", grenade); +} + +function quickPackConcussionGrenade(%val) +{ + if(%val) + addQuickPackFavorite("Concussion Grenade", grenade); +} + +function quickPackGrenade(%val) +{ + if(%val) + addQuickPackFavorite("Grenade", grenade); +} + +function quickPackFlareGrenade(%val) +{ + if(%val) + addQuickPackFavorite("Flare Grenade", grenade); +} + +function quickPackCameraGrenade(%val) +{ + if(%val) + addQuickPackFavorite("Deployable Camera", grenade); +} + +// Construction +function quickPackLightSupportBeam(%val) { + if (%val) + addQuickPackFavorite("Light Support Beam", dep); +} + +function quickPackLightWalkway(%val) { + if (%val) + addQuickPackFavorite("Light Walkway", dep); +} + +function quickPackLightBlastWall(%val) { + if (%val) + addQuickPackFavorite("Light Blast Wall", dep); +} + +function quickPackMediumSupportBeam(%val) { + if (%val) + addQuickPackFavorite("Medium Support Beam", dep); +} + +function quickPackMediumFloor(%val) { + if (%val) + addQuickPackFavorite("Medium Floor", dep); +} + +function quickPackInventoryStation(%val) { + if (%val) + addQuickPackFavorite("Inventory Station", dep); +} + +function quickPackLargeInventoryStation(%val) { + if (%val) + addQuickPackFavorite("Large Inventory station", dep); +} + +function quickPackGeneratorPack(%val) { + if (%val) + addQuickPackFavorite("Generator Pack", dep); +} + +function quickPackSolarPanelPack(%val) { + if (%val) + addQuickPackFavorite("Solar Panel Pack", dep); +} + +function quickPackSwitchPack(%val) { + if (%val) + addQuickPackFavorite("Switch Pack", dep); +} + +function quickPackMediumSensorPack(%val) { + if (%val) + addQuickPackFavorite("Medium Sensor Pack", dep); +} + +function quickPackLargeSensorPack(%val) { + if (%val) + addQuickPackFavorite("Large Sensor Pack", dep); +} + +function quickPackTeleportPad(%val) { + if (%val) + addQuickPackFavorite("Teleport Pad", dep); +} + +function quickPackDeployableTurretBase(%val) { + if (%val) + addQuickPackFavorite("Deployable Turret Base", dep); +} + +function quickPackEnergizer(%val) { + if (%val) + addQuickPackFavorite("Energizer", dep); +} + +function quickPackTreePack(%val) { + if (%val) + addQuickPackFavorite("Tree Pack", dep); +} + +function quickPackCratePack(%val) { + if (%val) + addQuickPackFavorite("Crate Pack", dep); +} + +function quickPackDecorationPack(%val) { + if (%val) + addQuickPackFavorite("Decoration Pack", dep); +} + +function quickPackLightPack(%val) { + if (%val) + addQuickPackFavorite("Light Pack", dep); +} + +function quickPackTripwirePack(%val) { + if (%val) + addQuickPackFavorite("Tripwire Pack", dep); +} + +function quickPackLogoProjectorPack(%val) { + if (%val) + addQuickPackFavorite("Logo Projector Pack", dep); +} + +function quickPackForceField(%val) { + if (%val) + addQuickPackFavorite("Force Field", dep); +} + +function quickPackGravityField(%val) { + if (%val) + addQuickPackFavorite("Gravity Field", dep); +} + +function quickPackJumpPad(%val) { + if (%val) + addQuickPackFavorite("Jump Pad", dep); +} + +function quickPackEscapePod(%val) { + if (%val) + addQuickPackFavorite("Escape Pod", dep); +} + +function quickPackDiscTurret(%val) { + if (%val) + addQuickPackFavorite("Disc Turret"); +} + +function quickPackLaserTurret(%val) { + if (%val) + addQuickPackFavorite("Laser Turret"); +} + +function quickPackMissileRackTurret(%val) { + if (%val) + addQuickPackFavorite("Missile Rack Turret"); +} + +function cyclePackFwd(%val) { + if (%val) + commandToServer('CyclePackSetting',1); +} + +function cyclePackBack(%val) { + if (%val) + commandToServer('CyclePackSetting',-1); +} + +function cyclePackFFwd(%val) { + if (%val) + commandToServer('CyclePackSetting',5); +} + +function cyclePackFBack(%val) { + if (%val) + commandToServer('CyclePackSetting',-5); +} + +function serverCmdCyclePackSetting(%client,%val) { + %plyr = %client.player; + if (isObject(%plyr)) + cyclePackSetting(%plyr,%val); +} + +// Emotes +function emoteSitDown(%val) { + if (%val) + commandToServer('Emote',"SitDown"); +} + +function emoteSquat(%val) { + if (%val) + commandToServer('Emote',"Squat"); +} + +function emoteJig(%val) { + if (%val) + commandToServer('Emote',"Jig"); +} + +function emoteLieDown(%val) { + if (%val) + commandToServer('Emote',"LieDown"); +} + +function emoteHeartAttack(%val) { + if (%val) + commandToServer('Emote',"HeartAttack"); +} + +function emoteSuckerPunched(%val) { + if (%val) + commandToServer('Emote',"SuckerPunched"); +} + +function serverCmdEmote(%client,%anim) { + %plyr = %client.player; + if (isObject(%plyr)) { + switch$ (%anim) { + case "SitDown": + %plyr.setActionThread("sitting",true); + case "Squat": + %plyr.setActionThread("scoutRoot",true); + case "Jig": + %plyr.setActionThread("ski",true); + case "LieDown": + %plyr.setActionThread("death9",true); + case "HeartAttack": + %plyr.setActionThread("death8",true); + case "SuckerPunched": + %plyr.setActionThread("death11",true); + } + } +} + +// START DONS +function serverCmdcheckHtilt(%client,%direction){ + if(isObject(%client.player)){ + if(%client.player.mountedtoV){ + if(%client.player.Vmountedto.getdatablock().getname() $= "helicopter"){ + %veh = %client.player.Vmountedto; + %impulse = $vehicletiltrate; + if(%direction $= "left") + %impulse = %impulse * -1; + %client.tilting = 1; + %veh.tilt = schedule(50, 0, "tiltveh", %veh, %impulse, %client); + } + } + } +} + +function serverCmdcheckendtilt(%client){ + if(isObject(%client.player)){ + if(%client.player.mountedtoV){ + if(%client.player.Vmountedto.getdatablock().getname() $= "helicopter"){ + %client.tilting = 0; + } + } + } +} + +function tiltveh(%veh, %impulse, %client){ + if(!isObject(%veh)) + return; + if(%client.tilting == 1){ + %vec = vectornormalize(vectorcross(%veh.getForwardvector(),%veh.getUpvector())); + %imppos = vectoradd(%veh.getPosition(),%vec); + %veh.applyImpulse(%impPos,vectorscale(vectorNormalize(%veh.getUpvector()),(%impulse * -1))); + %veh.tilt = schedule(25, 0, "tiltveh", %veh, %impulse, %client); + } +} + +// NightVision +function toggleNightVision(%val) { + if (%val) + commandToServer('NightVision'); +} + +function serverCmdNightVision(%client) { + %plyr = %client.player; + %ArmorType = %plyr.getDatablock().getname(); + if(%ArmorType $= "SpecOpsMaleHumanArmor" || %ArmorType $= "SpecOpsFemaleHumanArmor" || %ArmorType $= "SpecOpsMaleBiodermArmor"){ + if(%plyr.NVActivated == 1){ + Cancel(%plyr.nightvision); + messageClient(%client, 'MsgNVon', '\c2Night Vision Deactivated.'); + %plyr.NVActivated = 0; + } + else{ + NightVisionLoop(%client); + messageClient(%client, 'MsgNVoff', '\c2Night Vision Activated.'); + %plyr.NVActivated = 1; + } + return; + } + + if (isObject(%plyr)) { + if (!(%client.NVActivated == 1)) + { + %plyr.mountImage(FlashLightImage, 7); + messageClient(%client, 'MsgNVon', '\c2Grabbing Flash Light.'); + %client.NVActivated = 1; + } + else if (%client.NVActivated == 1) + { + %plyr.unmountImage(7); + messageClient(%client, 'MsgNVoff', '\c2Holistering Flash Light.'); + %client.NVActivated = 0; + } + } +} + +$host::slowmodist = 100; + +function SlowMotion(%val) { + if (%val) + commandToServer('StartSM'); +} + +function serverCmdStartSM(%client){ + if (!isobject(%client.player)) + return; + %orgscale = $timescale; + %newScale = (%orgscale / 4); + commandToClient(%client, 'SetTimescale', %newScale, %orgscale, %client); + %pos = %client.player.getposition(); + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if(isObject(%cl.player)){ + %testPos = %cl.player.getWorldBoxCenter(); + %distance = vectorDist(%pos, %testPos); + if (%distance > 0 && %distance < $host::slowmodist) + { + commandToClient(%cl, 'SetTimescale', %newScale, %orgscale); + } + } + } +} + +function clientCmdSetTimescale(%newScale, %orgscale) { + if(%newScale > 0.09 && %newScale < 1.1){ + $timescale = %newScale; + schedule(8000, 0, "resetTimescale", %orgscale); + } +} + +function ResetTimescale(%orgscale){ + $timescale = %orgScale; +} + +function doGrab(%val){ + if(%val) + commandToServer('DoGrab'); +} +// End DONS, End Construction + + +moveMap.bind( keyboard, tab, toggleFirstPerson ); +moveMap.bind( keyboard, u, ToggleMessageHud ); +moveMap.bind( keyboard, y, TeamMessageHud ); +moveMap.bind( keyboard, v, activateChatMenuHud ); + +function toggleCommanderMap( %val ) +{ + if ( %val ) + { + showTaskHudDlg(false); + CommanderMapGui.toggle(); + } +} + +moveMap.bind( keyboard, c, toggleCommanderMap ); +moveMap.bind( keyboard, "ctrl k", suicide ); + +function report(%val) +{ + if(%val) + commandToServer('report'); +} + +function suicide(%val) +{ + if (%val) + commandToServer('suicide'); +} + +function toggleFirstPerson(%val) +{ + if (%val) + { + $firstPerson = !$firstPerson; + hudFirstPersonToggled(); + } +} + +function toggleCamera(%val) +{ + if (%val) + commandToServer('ToggleCamera'); +} + +function dropPlayerAtCamera(%val) +{ + if (%val) + commandToServer('DropPlayerAtCamera'); +} + +function dropCameraAtPlayer(%val) +{ + if (%val) + commandToServer('dropCameraAtPlayer'); +} + +function dropPlayerAtCamera(%val) +{ + if (%val) + commandToServer('DropPlayerAtCamera'); +} + +function togglePlayerRace(%val) +{ + if (%val) + commandToServer('ToggleRace'); +} + +function togglePlayerGender(%val) +{ + if (%val) + commandToServer('ToggleGender'); +} + +function togglePlayerArmor(%val) +{ + if (%val) + commandToServer('ToggleArmor'); +} + +function jump(%val) +{ + //$mvTriggerCount2++; + $mvTriggerCount2 += (($mvTriggerCount2 & 1) == %val) ? 2 : 1; +} + +// moveMap.bind(keyboard, "alt c", playCel); +// moveMap.bind(keyboard, "alt a", toggleArmor); +// moveMap.bind(keyboard, "alt d", playDeath); + +//function playCel(%val) +//{ +// if (%val) +// commandToServer('playCel',%anim); +//} + +//function playDeath(%val) +//{ +// if (%val) +// commandToServer('playDeath',%anim); +//} + +function mouseFire(%val) +{ + $mvTriggerCount0 += (($mvTriggerCount0 & 1) == %val) ? 2 : 1; +} + +function mouseJet(%val) +{ + $mvTriggerCount3 += (($mvTriggerCount3 & 1) == %val) ? 2 : 1; +} + +function altTrigger(%val) +{ + $mvTriggerCount1 += (($mvTriggerCount1 & 1) == %val) ? 2 : 1; +} + +function testLOSTarget() +{ + ServerConnection.sendLOSTarget(); + commandToServer('TestLOS'); +} + +function serverCmdTestLOS(%client) +{ + %client.sendTargetTo(%client); + %msg = 'This is a simple test.'; + messageClient(%client, 'TestMsg', %msg); +} + +//------------------------------------------------------------------------------ +function toggleHelpGui( %val ) +{ + if ( %val ) + toggleHelpText(); +} + +function toggleScoreScreen( %val ) +{ + if ( %val ) + toggleCursorHuds('scoreScreen'); +} + +moveMap.bind( keyboard, F1, toggleHelpGui ); +moveMap.bind( keyboard, F2, toggleScoreScreen ); + +//------------------------------------------------------------------------------ +// DEMO RECORD FUNCTIONS: +function startRecordingDemo( %val ) +{ + if ( %val ) + beginDemoRecord(); +} + +function stopRecordingDemo( %val ) +{ + if ( %val ) + stopDemoRecord(); +} + +if ( !isDemo() ) +{ + moveMap.bind( keyboard, F3, startRecordingDemo ); + moveMap.bind( keyboard, F4, stopRecordingDemo ); +} + +//------------------------------------------------------------------------------ +// NAV HUD DISPLAY FUNCTIONS: +function toggleHudWaypoints(%val) +{ + if(%val) + navHud.setMarkerTypeVisible(ClientWaypoint, !navHud.isMarkerTypeVisible(ClientWaypoint)); +} + +function toggleHudMarkers(%val) +{ + if(%val) + navHud.setMarkerTypeVisible(MissionWaypoint, !navHud.isMarkerTypeVisible(MissionWaypoint)); +} + +function toggleHudTargets(%val) +{ + if(%val) + { + %visible = navHud.isMarkerTypeVisible(Target); + PlayGui.beaconsVisible = !%visible; + navHud.setMarkerTypeVisible(Target, !%visible); + } +} + +function toggleHudCommands(%val) +{ + if(%val) + { + %visible = navHud.isMarkerTypeVisible(PotentialTask); + navHud.setMarkerTypeVisible(PotentialTask, !%visible); + navHud.setMarkerTypeVisible(AssignedTask, !%visible); + } +} + +moveMap.bind( keyboard, F6, toggleHudWaypoints ); +moveMap.bind( keyboard, F7, toggleHudMarkers ); +moveMap.bind( keyboard, F8, toggleHudCommands ); +moveMap.bind( keyboard, F9, toggleHudTargets ); + +//------------------------------------------------------------------------------ +// TASK FUNCTIONS: +function fnAcceptTask( %val ) +{ + if ( %val ) + clientAcceptCurrentTask(); +} + +function fnDeclineTask( %val ) +{ + if ( %val ) + clientDeclineCurrentTask(); +} + +function fnTaskCompleted( %val ) +{ + if ( %val ) + clientTaskCompleted(); +} + +function fnResetTaskList( %val ) +{ + if ( %val ) + TaskList.reset(); +} + +// tasks +moveMap.bind( keyboard, n, toggleTaskListDlg ); +moveMap.bind( keyboard, "enter", fnAcceptTask ); +moveMap.bind( keyboard, "backspace", fnDeclineTask ); +moveMap.bind( keyboard, "shift c", fnTaskCompleted ); +moveMap.bind( keyboard, "shift x", fnResetTaskList ); + +// misc: +moveMap.bind( keyboard, "ctrl n", toggleNetDisplayHud ); + +//------------------------------------------------------------------------------ +// VOTING FUNCTIONS: +function voteYes( %val ) +{ + if ( %val ) + setPlayerVote( true ); +} + +function voteNo( %val ) +{ + if ( %val ) + setPlayerVote( false ); +} + +moveMap.bind( keyboard, insert, voteYes ); +moveMap.bind( keyboard, delete, voteNo ); + +/////////////////////// +// Observer Keys +/////////////////////// +if ( isObject( observerMap ) ) + observerMap.delete(); +new ActionMap( observerMap ); +observerMap.bind( keyboard, t, moveup ); +observerMap.bind( keyboard, b, movedown ); +observerMap.bind( keyboard, space, jump ); +observerMap.bind( mouse, button0, mouseFire ); +observerMap.bind( mouse, button1, mouseJet ); + +if(!isDemo()) +{ + observerMap.copyBind(moveMap, startRecordingDemo); + observerMap.copyBind(moveMap, stopRecordingDemo); +} + +/////////////////////// +// Vehicle Keys +/////////////////////// +function clientCmdSetWeaponryVehicleKeys() +{ + %bind = moveMap.getBinding( nextWeapon ); + passengerKeys.bind( getField( %bind, 0 ), getField( %bind, 1 ), nextVehicleWeapon ); + + %bind = moveMap.getBinding( prevWeapon ); + passengerKeys.bind( getField( %bind, 0 ), getField( %bind, 1 ), prevVehicleWeapon ); + + %bind = moveMap.getBinding( cycleWeaponAxis ); + passengerKeys.bind( getField( %bind, 0 ), getField( %bind, 1 ), cycleVehicleWeapon ); + + %bind = moveMap.getBinding( cycleNextWeaponOnly ); + passengerKeys.bind( getField( %bind, 0 ), getField( %bind, 1 ), cycleNextVehicleWeaponOnly ); + + passengerKeys.bind( keyboard, 1, useWeaponOne ); + passengerKeys.bind( keyboard, 2, useWeaponTwo ); + passengerKeys.bind( keyboard, 3, useWeaponThree ); +} + +function clientCmdSetPilotVehicleKeys() +{ + passengerKeys.copyBind( moveMap, toggleFirstPerson ); + passengerKeys.copyBind( moveMap, toggleFreeLook ); + passengerKeys.copyBind( moveMap, mouseJet ); + + // Use the InvertVehicleYAxis pref: + if ( $pref::Vehicle::InvertYAxis ) + { + %bind = moveMap.getBinding( pitch ); + %device = getField( %bind, 0 ); + %action = getField( %bind, 1 ); + %flags = moveMap.isInverted( %device, %action ) ? "SD" : "SDI"; + %deadZone = moveMap.getDeadZone( %device, %action ); + %scale = moveMap.getScale( %device, %action ); + passengerKeys.bind( %device, %action, %flags, %deadZone, %scale, pitch ); + } +} + +function clientCmdSetPassengerVehicleKeys() +{ + passengerKeys.copyBind( moveMap, toggleZoom ); + passengerKeys.copyBind( moveMap, setZoomFOV ); + passengerKeys.copyBind( moveMap, toggleScoreScreen ); +} + +function clientCmdSetDefaultVehicleKeys(%inVehicle) +{ + if ( %inVehicle ) + { + if ( isObject( passengerKeys ) ) + { + passengerKeys.pop(); + passengerKeys.delete(); + } + new ActionMap( passengerKeys ); + + // Bind all of the movement keys: + passengerKeys.copyBind( moveMap, moveleft ); + passengerKeys.copyBind( moveMap, moveright ); + passengerKeys.copyBind( moveMap, moveforward ); + passengerKeys.copyBind( moveMap, movebackward ); + passengerKeys.copyBind( moveMap, mouseFire ); + passengerKeys.copyBind( moveMap, yaw ); + passengerKeys.copyBind( moveMap, pitch ); + passengerKeys.copyBind( moveMap, turnLeft ); + passengerKeys.copyBind( moveMap, turnRight ); + passengerKeys.copyBind( moveMap, panUp ); + passengerKeys.copyBind( moveMap, panDown ); + passengerKeys.copyBind( moveMap, jump ); + passengerKeys.copyBind( moveMap, setZoomFOV ); + passengerKeys.copyBind( moveMap, toggleZoom ); + + // Copy the joystick binds as well: + passengerKeys.copyBind( moveMap, joyPitch ); + passengerKeys.copyBind( moveMap, joyYaw ); + passengerKeys.copyBind( moveMap, joystickMoveX ); + passengerKeys.copyBind( moveMap, joystickMoveY ); + + // Bind the chat keys as well: + passengerKeys.copyBind( moveMap, ToggleMessageHud ); + passengerKeys.copyBind( moveMap, TeamMessageHud ); + passengerKeys.copyBind( moveMap, resizeChatHud ); + passengerKeys.copyBind( moveMap, pageMessageHudUp ); + passengerKeys.copyBind( moveMap, pageMessageHudDown ); + passengerKeys.copyBind( moveMap, activateChatMenuHud ); + passengerKeys.copyBind( moveMap, voiceCapture ); + + // Miscellaneous other binds: + passengerKeys.copyBind( moveMap, useBackpack ); + passengerKeys.copyBind( moveMap, useRepairKit ); + passengerKeys.copyBind( moveMap, suicide ); + passengerKeys.copyBind( moveMap, voteYes ); + passengerKeys.copyBind( moveMap, voteNo ); + passengerKeys.copyBind( moveMap, toggleCommanderMap ); + passengerKeys.bindCmd( keyboard, escape, "", "escapeFromGame();" ); + passengerKeys.copyBind( moveMap, toggleHelpGui ); + passengerKeys.copyBind( moveMap, toggleScoreScreen ); + passengerKeys.copyBind( moveMap, toggleNetDisplayHud ); + + // Bind the weapon keys as well: + passengerKeys.copyBind( moveMap, nextWeapon ); + passengerKeys.copyBind( moveMap, prevWeapon ); + passengerKeys.copyBind( moveMap, cycleWeaponAxis ); + passengerKeys.copyBind( moveMap, cycleNextWeaponOnly ); + + passengerKeys.copyBind( moveMap, useFirstWeaponSlot ); + passengerKeys.copyBind( moveMap, useSecondWeaponSlot ); + passengerKeys.copyBind( moveMap, useThirdWeaponSlot ); + passengerKeys.copyBind( moveMap, useFourthWeaponSlot ); + passengerKeys.copyBind( moveMap, useFifthWeaponSlot ); + passengerKeys.copyBind( moveMap, useSixthWeaponSlot ); + + // Bind individual weapons as well: + passengerKeys.copyBind( moveMap, useBlaster ); + passengerKeys.copyBind( moveMap, usePlasma ); + passengerKeys.copyBind( moveMap, useChaingun ); + passengerKeys.copyBind( moveMap, useDisc ); + passengerKeys.copyBind( moveMap, useGrenadeLauncher ); + passengerKeys.copyBind( moveMap, useSniperRifle ); + passengerKeys.copyBind( moveMap, useELFGun ); + passengerKeys.copyBind( moveMap, useMortar ); + passengerKeys.copyBind( moveMap, useMissileLauncher ); + passengerKeys.copyBind( moveMap, useTargetingLaser ); + passengerKeys.copyBind( moveMap, useShockLance ); + passengerKeys.copyBind( moveMap, throwGrenade ); + passengerKeys.copyBind( moveMap, placeMine ); + + // Bind the command assignment/response keys as well: + passengerKeys.copyBind( moveMap, toggleTaskListDlg ); + passengerKeys.copyBind( moveMap, fnAcceptTask ); + passengerKeys.copyBind( moveMap, fnDeclineTask ); + passengerKeys.copyBind( moveMap, fnTaskCompleted ); + passengerKeys.copyBind( moveMap, fnResetTaskList ); + + // grab the demo recorder binds + passengerKeys.copyBind( moveMap, startRecordingDemo ); + passengerKeys.copyBind( moveMap, stopRecordingDemo ); + } + else if ( isObject( passengerKeys ) ) + { + passengerKeys.pop(); + passengerKeys.delete(); + } +} + +function useWeaponOne(%val) +{ + if(%val) + commandToServer('setVehicleWeapon', 1); +} + +function useWeaponTwo(%val) +{ + if(%val) + commandToServer('setVehicleWeapon', 2); +} + +function useWeaponThree(%val) +{ + if(%val) + commandToServer('setVehicleWeapon', 3); +} + +function serverCmdSetVehicleWeapon(%client, %num) +{ + %turret = %client.player.getControlObject(); + if(%turret.getDataBlock().numWeapons < %num) + return; + %turret.selectedWeapon = %num; + + //%hudNum = %turret.getDataBlock().getHudNum(%num); + //%client.setVWeaponsHudActive(%hudNum); + %client.setVWeaponsHudActive(%num); + + // set the active image on the client's obj + if(%num == 1) + %client.setObjectActiveImage(%turret, 2); + else if(%num == 2) + %client.setObjectActiveImage(%turret, 4); + else + %client.setObjectActiveImage(%turret, 6); + + // if firing then set the proper image trigger + if(%turret.fireTrigger) + { + if(%num == 1) + { + %turret.setImageTrigger(4, false); + if(%turret.getImageTrigger(6)) + { + %turret.setImageTrigger(6, false); + ShapeBaseImageData::deconstruct(%turret.getMountedImage(6), %turret); + } + %turret.setImageTrigger(2, true); + } + else if( %num == 2) + { + %turret.setImageTrigger(2, false); + if(%turret.getImageTrigger(6)) + { + %turret.setImageTrigger(6, false); + ShapeBaseImageData::deconstruct(%turret.getMountedImage(6), %turret); + } + %turret.setImageTrigger(4, true); + } + else + { + %turret.setImageTrigger(2, false); + %turret.setImageTrigger(4, false); + } + } +} + +function nextVehicleWeapon(%val) +{ + if ( %val ) + commandToServer('switchVehicleWeapon', "next"); +} + +function prevVehicleWeapon(%val) +{ + if ( %val ) + commandToServer('switchVehicleWeapon', "prev"); +} + +function cycleVehicleWeapon( %val ) +{ + if ( %val < 0 ) + commandToServer( 'switchVehicleWeapon', "next" ); + else + commandToServer( 'switchVehicleWeapon', "prev" ); +} + +function cycleNextVehicleWeaponOnly( %val ) +{ + if ( %val < 0 ) + commandToServer( 'switchVehicleWeapon', "next" ); +} + +function serverCmdSwitchVehicleWeapon(%client, %dir) +{ + %turret = %client.player.getControlObject(); + %weaponNum = %turret.selectedWeapon; + if(%dir $= "next") + { + if(%weaponNum++ > %turret.getDataBlock().numWeapons) + %weaponNum = 1; + } + else + { + if(%weaponNum-- < 1) + %weaponNum = %turret.getDataBlock().numWeapons; + } + serverCmdSetVehicleWeapon(%client, %weaponNum); +} + + +/////////////////////// +//Station +/////////////////////// +function clientCmdSetStationKeys(%inStation) +{ + if ( %inStation ) + { + if ( isObject( stationMap ) ) + { + stationMap.pop(); + stationMap.delete(); + } + new ActionMap( stationMap ); + stationMap.blockBind( moveMap, toggleInventoryHud ); + stationMap.bind( keyboard, escape, "" ); + stationMap.push(); + } + else if ( isObject( stationMap ) ) + { + stationMap.pop(); + stationMap.delete(); + } +} + +function shutServerDown(){ + commandToServer('noobserverSD'); +} + +$MFDebugRenderMode = 0; +function cycleDebugRenderMode() +{ + if($MFDebugRenderMode == 0) + { + show(); + GLEnableOutline(true); + $MFDebugRenderMode = 1; + } + else if ($MFDebugRenderMode == 1) + { + GLEnableOutline(false); + setInteriorRenderMode(7); + $MFDebugRenderMode = 2; + } + else if ($MFDebugRenderMode == 2) + { + setInteriorRenderMode(0); + GLEnableOutline(false); + showTri(); + $MFDebugRenderMode = 0; + } +} + +// Since the toggle console key is remappable, put it here: +if (!isDemo()) + GlobalActionMap.bind(keyboard, "grave", toggleConsole); diff --git a/Scripts/creditsGui.cs b/Scripts/creditsGui.cs new file mode 100644 index 0000000..e3dda2e --- /dev/null +++ b/Scripts/creditsGui.cs @@ -0,0 +1,144 @@ +function LaunchCredits() +{ + Canvas.setContent(CreditsGui); +} + +function cancelCredits() +{ + //delete the action map + CreditsActionMap.pop(); + + //kill the schedules + cancel($CreditsScrollSchedule); + cancel($CreditsSlideShow); + + //kill the music + MusicPlayer.stop(); + + //load the launch gui back... + Canvas.setContent(LaunchGui); + + //delete the contents of the ML ctrl so as to free up memory... + Credits_Text.setText(""); +} + +function CreditsGui::onWake(%this) +{ + //create an action map to use "esc" to exit the credits screen... + if (!isObject(CreditsActionMap)) + { + new ActionMap(CreditsActionMap); + CreditsActionMap.bindCmd(keyboard, anykey, "cancelCredits();", ""); + CreditsActionMap.bindCmd(keyboard, space, "cancelCredits();", ""); + CreditsActionMap.bindCmd(keyboard, escape, "cancelCredits();", ""); + CreditsActionMap.bindCmd(mouse, button0, "$CreditsPaused = true;", "$CreditsPaused = false;"); + CreditsActionMap.bindCmd(mouse, button1, "$CreditsSpeedUp = true;", "$CreditsSpeedUp = false;"); + if (!isDemo()) + CreditsActionMap.bindCmd(mouse, button2, "creditsNextPic();", ""); + } + CreditsActionMap.push(); + + //build the ML text ctrl... + exec("scripts/creditsText.cs"); + if (!isDemo()) + { + $CreditsPicIndex = 1; + CREDITS_Pic.setBitmap("gui/ACCM_" @ $CreditsPicIndex @ ".png"); + } + else + CREDITS_Pic.setBitmap("gui/ACCM_1.png"); + + //music array + if (!isDemo()) + { + $CreditsMusic[0] = "ACCM_Warrior"; + $CreditsMusic[1] = "ACCM_Warrior"; + $CreditsMusic[2] = "ACCM_Warrior"; + $CreditsMusic[3] = "ACCM_Warrior"; + $CreditsMusic[4] = "ACCM_Warrior"; + } + else + { + $CreditsMusic[0] = "lush"; + $CreditsMusic[1] = "desert"; + $CreditsMusic[2] = "desert"; + $CreditsMusic[3] = "lush"; + $CreditsMusic[4] = "desert"; + } + + //start the credits from the beginning + $CreditsOffset = 0.0; + %screenHeight = getWord(getResolution(), 1); + Credits_Text.resize(getWord(Credits_Text.position, 0), + mFloor(%screenHeight / 2) - 125, + getWord(Credits_Text.extent, 0), + getWord(Credits_Text.extent, 1)); + + //start the scrolling + $CreditsPaused = false; + $CreditsSpeedUp = false; + $CreditsScrollSchedule = schedule(3000, 0, scrollTheCredits); + + //start cycling the bitmaps + if (!isDemo()) + $CreditsSlideShow = schedule(5000, 0, creditsNextPic); + + //start some music + %chooseTrack = mFloor(getRandom() * 4.99); + MusicPlayer.playTrack($CreditsMusic[%chooseTrack]); +} + +function addCreditsLine(%text, %lastLine) +{ + CREDITS_Text.addText(%text @ "\n", %lastline); +} + +function scrollTheCredits() +{ + //make sure we're not paused + if (!$CreditsPaused) + { + //if we've scrolled off the top, set the position back down to the bottom + %parentCtrl = CREDITS_Text.getGroup(); + if (getWord(Credits_Text.position, 1) + getWord(Credits_Text.extent, 1) < 0) + { + Credits_Text.position = getWord(Credits_Text.position, 0) SPC getWord(%parentCtrl.extent, 1); + $CreditsOffset = getWord(Credits_Text.position, 1); + } + + if ($CreditsSpeedUp) + %valueToScroll = 10; + else + %valueToScroll = 1; + + //scroll the control up a bit + Credits_Text.resize(getWord(Credits_Text.position, 0), + getWord(Credits_Text.position, 1) - %valueToScroll, + getWord(Credits_Text.extent, 0), + getWord(Credits_Text.extent, 1)); + } + + //schedule the next scroll... + $CreditsScrollSchedule = schedule(30, 0, scrollTheCredits); +} + +function creditsNextPic() +{ + //no slide show in the demo... + if (isDemo()) + return; + + cancel($CreditsSlideShow); + if (!$CreditsPaused) + { + $CreditsPicIndex += 1; + if ($CreditsPicIndex > 10) + $CreditsPicindex = 1; + + //set the bitmap + CREDITS_Pic.setBitmap("gui/ACCM_" @ $CreditsPicIndex @ ".png"); + } + + //schedule the next bitmap + $CreditsSlideShow = schedule(5000, 0, creditsNextPic); +} diff --git a/Scripts/creditsText.cs b/Scripts/creditsText.cs new file mode 100644 index 0000000..5c6e313 --- /dev/null +++ b/Scripts/creditsText.cs @@ -0,0 +1,73 @@ +// Credits list +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine("Main Developer/Mod Founder"); +addCreditsLine("Blnukem"); +addCreditsLine(""); +addCreditsLine("Secondary Developer/Mod Co-Founder"); +addCreditsLine("Eolk"); +addCreditsLine(""); +addCreditsLine("Development Assistant"); +addCreditsLine("Dark Dragon DX"); +addCreditsLine(""); +addCreditsLine("Original Construction Mod Development Team"); +addCreditsLine("Mostlikely, JackTL, and Construct"); +addCreditsLine(""); +addCreditsLine("Original CCM Development Team"); +addCreditsLine("Dondelium_X, FalconBlade, and Ur_A_Dumb"); +addCreditsLine(""); +addCreditsLine("ACCM Forum and ACCM FTP Server Host"); +addCreditsLine("Krash123"); +addCreditsLine(""); +addCreditsLine("Mod Testers"); +addCreditsLine("Blnukem"); +addCreditsLine("Eolk"); +addCreditsLine("Zaxxman"); +addCreditsLine("Dark Dragon DX"); +addCreditsLine(""); +addCreditsLine("Concepts and Ideas"); +addCreditsLine("Bungie.net and the Halo Universe"); +addCreditsLine(""); +addCreditsLine("Games/Programs Which Provided Sounds"); +addCreditsLine("Quake 3: Arena - Provided Various GUI and Zombie Sounds"); +addCreditsLine("Quake 3: Team Arena - Provided Various GUI Sounds"); +addCreditsLine("Call of Duty 4: Modern Warfare - Provided Explosion and Weapon Sounds"); +addCreditsLine("Acid 2.0 - Used Create and Edit Various .wav and .mp3 Sounds"); +addCreditsLine(""); +addCreditsLine("Other Thanks"); +addCreditsLine("Emperors Champ \'Alviss\'"); +addCreditsLine("CMDRBOB"); +addCreditsLine("Electricutioner"); +addCreditsLine("Ronnies07"); +addCreditsLine("Avenger777 \'Naosyth\'"); +addCreditsLine("Lord Sega^ \'Stingwraith\'"); +addCreditsLine("BlinX^"); +addCreditsLine("Destos"); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine("And a special thanks to the entire Construction Community."); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); +addCreditsLine(""); diff --git a/Scripts/dEffects.cs b/Scripts/dEffects.cs new file mode 100644 index 0000000..cc1188f --- /dev/null +++ b/Scripts/dEffects.cs @@ -0,0 +1,286 @@ +// Deploy effects + +datablock AudioProfile(DrillLinkSound) +{ + filename = "fx/misc/mine.deploy.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SingeLinkSound) +{ + filename = "fx/weapons/chaingun_off.wav"; // "fx/weapons/grenade_camera_activate.wav"; + description = AudioClose3d; + preload = true; +}; + + +datablock AudioProfile(BoltLinkSound) +{ + filename = "fx/misc/health_patch.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(ClickLinkSound) +{ + filename = "fx/misc/health_patch.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SingeSound) +{ + filename = "fx/weapons/ElF_hit.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(LargeLinkeSound) +{ + filename = "fx/weapons/grenade_explode_UW.wav"; + description = AudioClose3d; + preload = true; +}; + + +datablock LinearProjectileData(FastSingeProjectile) { + className = "LinearProjectileData"; + projectileShapeName = "turret_muzzlepoint.dts"; + emitterDelay = "-1"; + velInheritFactor = "0"; + directDamage = "0"; + hasDamageRadius = "0"; + indirectDamage = "0"; + damageRadius = "0"; + radiusDamageType = "0"; + kickBackStrength = "0"; + baseEmitter = "ELFSparksEmitter"; + hasLight = "0"; + lightRadius = "20"; + lightColor = "1.000000 1.000000 1.000000 1.000000"; + hasLightUnderwaterColor = "0"; + underWaterLightColor = "1.000000 1.000000 1.000000 1.000000"; + explodeOnWaterImpact = "0"; + depthTolerance = "5"; + bubbleEmitTime = "0.5"; + faceViewer = "0"; + scale = "1 1 1"; + dryVelocity = "5"; + wetVelocity = "5"; + fizzleTimeMS = "60000"; + lifetimeMS = "60000"; + explodeOnDeath = "0"; + reflectOnWaterImpactAngle = "0"; + deflectionOnWaterImpact = "0"; + fizzleUnderwaterMS = "-1"; + activateDelayMS = "-1"; + doDynamicClientHits = "0"; + sound = "ELFHitTargetSound"; + explosion = "BlasterExplosion"; +}; + +datablock LinearProjectileData(SlowSingeProjectile) { + className = "LinearProjectileData"; + projectileShapeName = "turret_muzzlepoint.dts"; + emitterDelay = "-1"; + velInheritFactor = "0"; + directDamage = "0"; + hasDamageRadius = "0"; + indirectDamage = "0"; + damageRadius = "0"; + radiusDamageType = "0"; + kickBackStrength = "0"; + baseEmitter = "ElfSparksEmitter"; + hasLight = "0"; + lightRadius = "20"; + lightColor = "1.000000 1.000000 1.000000 1.000000"; + hasLightUnderwaterColor = "0"; + underWaterLightColor = "1.000000 1.000000 1.000000 1.000000"; + explodeOnWaterImpact = "0"; + depthTolerance = "5"; + bubbleEmitTime = "0.5"; + faceViewer = "0"; + scale = "1 1 1"; + dryVelocity = "1"; + wetVelocity = "1"; + fizzleTimeMS = "60000"; + lifetimeMS = "60000"; + explodeOnDeath = "0"; + reflectOnWaterImpactAngle = "0"; + deflectionOnWaterImpact = "0"; + fizzleUnderwaterMS = "-1"; + activateDelayMS = "-1"; + doDynamicClientHits = "0"; + sound = "ELFHitTargetSound"; + explosion = "BlasterExplosion"; +}; + + +function singe(%pos,%dir,%size,%type) { + %p = new LinearProjectile() { + dataBlock = %type @"SingeProjectile"; + initialDirection = %dir; + initialPosition = %pos; + }; + +if (%type $= "fast") + %p.schedule((%size/5)*1000,"delete"); +else + %p.schedule((%size)*1000,"delete"); +} + +function edgesinge(%obj) +{ +%objsize = VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)); +%realsize = VectorMultiply(VectorMultiply(%objsize,%obj.getScale()),"1 1 0"); +//echo(%obj.getObjectBox()); +//echo(%objsize); +//echo(%realsize); +%offset = VectorScale(Realvec(%obj,%realsize),0.49); +%offset2 = VectorScale(VectorCross(VectorNormalize(%offset),realvec(%obj,"0 0 1")),VectorLen(%offset)); +%pos1 = VectorAdd(%obj.getWorldBoxCenter(),%offset); +%pos2 = VectorAdd(%obj.getWorldBoxCenter(),VectorScale(%offset,-1)); +singe(%pos1,Realvec(%obj,"-1 0 0"),GetWord(%realsize,0),"fast"); +singe(%pos1,Realvec(%obj,"0 -1 0"),GetWord(%realsize,1),"fast"); +singe(%pos2,Realvec(%obj,"1 0 0"),GetWord(%realsize,0),"fast"); +singe(%pos2,Realvec(%obj,"0 1 0"),GetWord(%realsize,1),"fast"); +} + +function linksinge(%obj,%pt,%nrm) +{ +if (%nrm !$= "") +{ +%nrm = VectorNormalize(%nrm); +%dir = VectorNormalize(VectorCross(%nrm,Realvec(%obj,"0 0 1"))); +%objsize = VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)); +%realsize = VectorScale(VectorMultiply(%objsize,%obj.getScale()),0.49); +%side = VirVec(%obj,%nrm); +%oside = VectorCross("0 0 1",%side); + +%forward = Realvec(%obj,VectorMultiply(%realsize,%side)); +%left = Realvec(%obj,VectorMultiply(%realsize,%oside)); +%len = Vectorlen(%left); +%pos1 = VectorAdd(%obj.getWorldBoxCenter(),VectorAdd(%forward,%left)); +%pos2 = VectorAdd(%obj.getWorldBoxCenter(),VectorAdd(%forward,VectorScale(%left,-1))); +singe(%pos1,%dir,%len,"fast"); +singe(%pos2,VectorScale(%dir,-1),%len,"fast"); +} +} + +function floordrill(%obj) +{ +%objsize = VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)); +%realsize = VectorMultiply(VectorMultiply(%objsize,%obj.getScale()),"0.5 0.5 0.5"); +%forward = VectorScale(realvec(%obj,"1 0 0"),GetWord(%realsize,0)); +%left = VectorScale(realvec(%obj,"0 1 0"),GetWord(%realsize,1)); +%up = VectorScale(realvec(%obj,"0 0 1"),GetWord(%realsize,2)); +%p1 = VectorAdd(VectorAdd(%forward,%left),%up); +%p2 = VectorAdd(VectorAdd(VectorScale(%forward,-1),%left),%up); +%p3 = VectorAdd(VectorAdd(%forward,VectorScale(%left,-1)),%up); +%p4 = VectorAdd(VectorAdd(VectorScale(%forward,-1),VectorScale(%left,-1)),%up); +schedule(0,%obj,"llink",VectorAdd(%p1,%obj.getWorldBoxCenter())); +schedule(500,%obj,"llink",VectorAdd(%p2,%obj.getWorldBoxCenter())); +schedule(1000,%obj,"llink",VectorAdd(%p4,%obj.getWorldBoxCenter())); +schedule(1500,%obj,"llink",VectorAdd(%p3,%obj.getWorldBoxCenter())); +} + +function MSinge(%obj) +{ +%objsize = VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)); +%realsize = VectorMultiply(VectorMultiply(%objsize,%obj.getScale()),"0.5 0.5 0.5"); +%forward = VectorScale(realvec(%obj,"1 0 0"),GetWord(%realsize,0)); +%left = VectorScale(realvec(%obj,"0 1 0"),GetWord(%realsize,1)); +%up = VectorScale(realvec(%obj,"0 0 -1"),GetWord(%realsize,2)); +%p1 = VectorAdd(VectorAdd(%forward,%left),%up); +%p2 = VectorAdd(VectorAdd(VectorScale(%forward,-1),%left),%up); +%p3 = VectorAdd(VectorAdd(%forward,VectorScale(%left,-1)),%up); +%p4 = VectorAdd(VectorAdd(VectorScale(%forward,-1),VectorScale(%left,-1)),%up); +schedule(0,%obj,"mlink",VectorAdd(%p1,%obj.getWorldBoxCenter())); +schedule(250,%obj,"mlink",VectorAdd(%p2,%obj.getWorldBoxCenter())); +schedule(500,%obj,"mlink",VectorAdd(%p4,%obj.getWorldBoxCenter())); +schedule(750,%obj,"mlink",VectorAdd(%p3,%obj.getWorldBoxCenter())); +} + +function MSinge2(%obj) +{ +%objsize = VectorSub(getWords(%obj.getObjectBox(),3,5),getWords(%obj.getObjectBox(),0,2)); +%realsize = VectorMultiply(VectorMultiply(%objsize,%obj.getScale()),"0.5 0.5 0.5"); +%forward = VectorScale(realvec(%obj,"1 0 0"),GetWord(%realsize,0)); +%left = VectorScale(realvec(%obj,"0 1 0"),GetWord(%realsize,1)); +%up = VectorScale(realvec(%obj,"0 0 1"),GetWord(%realsize,2)); +%p1 = VectorAdd(VectorAdd(%forward,%left),%up); +%p2 = VectorAdd(VectorAdd(VectorScale(%forward,-1),%left),%up); +%p3 = VectorAdd(VectorAdd(%forward,VectorScale(%left,-1)),%up); +%p4 = VectorAdd(VectorAdd(VectorScale(%forward,-1),VectorScale(%left,-1)),%up); +schedule(0,%obj,"mlink",VectorAdd(%p1,%obj.getWorldBoxCenter())); +schedule(250,%obj,"mlink",VectorAdd(%p2,%obj.getWorldBoxCenter())); +schedule(500,%obj,"mlink",VectorAdd(%p4,%obj.getWorldBoxCenter())); +schedule(750,%obj,"mlink",VectorAdd(%p3,%obj.getWorldBoxCenter())); +} + + +function floorlink(%obj,%pt,%nrm) { +%fstat = aboveground(%obj.getworldboxcenter(),1,%obj); +%stat = GetWord(%fstat,0); +//warn(%stat); +if(%stat $= "open" || %stat $= "roof" || %stat $= "shadow") + linksinge(%obj,%pt,%nrm); +else + floordrill(%obj); +} + +function slink(%pt) +{ +createLifeEmitter(%pt, ELFSparksEmitter, 200); +Serverplay3D(SingeLinkSound,%pt); +} + +function mlink(%pt) +{ +createLifeEmitter(%pt, ELFSparksEmitter, 200); +Serverplay3D(SingeLinkSound,%pt); +} + +function llink(%pt) +{ +createLifeEmitter(%pt, SmallLightDamageSmoke, 500); +Serverplay3D(LargeLinkeSound,%pt); +} + +function deployEffect(%obj,%pt,%nrm,%type) +{ +if ($Host::NoDeployEffects) + return ""; +if (%type $= "pad") + { + edgesinge(%obj); + } +else if (%type $= "walk") + { + linksinge(%obj,%pt,%nrm); + } +else if (%type $= "spine") + { + slink(%pt); + } +else if (%type $= "spine1") + { + slink(%pt); + %p2 = VectorAdd( pos( %obj ),realvec( %obj,VectorMultiply("0 0 0.5",%obj.getScale()) ) ); + schedule(500,%obj,"slink",%p2); + } +else if (%type $= "mspine") + { + msinge(%obj); + } +else if (%type $= "mspine1") + { + msinge(%obj); + schedule(1000,%obj,"msinge2",%obj); + } +else if (%type $= "floor") + { + floorlink(%obj,%pt,%nrm); + } +} diff --git a/Scripts/damageTypes.cs b/Scripts/damageTypes.cs new file mode 100644 index 0000000..3f6798a --- /dev/null +++ b/Scripts/damageTypes.cs @@ -0,0 +1,1291 @@ +//-------------------------------------------------------------------------- +// TYPES OF ALLOWED DAMAGE +//-------------------------------------------------------------------------- + +$DamageType::Default = 0; +$DamageType::Blaster = 1; +$DamageType::Plasma = 2; +$DamageType::Bullet = 3; +$DamageType::Disc = 4; +$DamageType::Grenade = 5; +$DamageType::Laser = 6; // NOTE: This value is referenced directly in code. DO NOT CHANGE! +$DamageType::ELF = 7; +$DamageType::Mortar = 8; +$DamageType::Missile = 9; +$DamageType::ShockLance = 10; +$DamageType::Mine = 11; +$DamageType::Explosion = 12; +$DamageType::Impact = 13; // Object to object collisions +$DamageType::Ground = 14; // Object to ground collisions +$DamageType::Turret = 15; + +$DamageType::PlasmaTurret = 16; +$DamageType::AATurret = 17; +$DamageType::ElfTurret = 18; +$DamageType::MortarTurret = 19; +$DamageType::MissileTurret = 20; +$DamageType::IndoorDepTurret = 21; +$DamageType::OutdoorDepTurret = 22; +$DamageType::SentryTurret = 23; + +$DamageType::OutOfBounds = 24; +$DamageType::Lava = 25; + +$DamageType::ShrikeBlaster = 26; +$DamageType::BellyTurret = 27; +$DamageType::BomberBombs = 28; +$DamageType::TankChaingun = 29; +$DamageType::TankMortar = 30; +$DamageType::SatchelCharge = 31; +$DamageType::MPBMissile = 32; +$DamageType::Lightning = 33; +$DamageType::VehicleSpawn = 34; +$DamageType::ForceFieldPowerup = 35; +$DamageType::Crash = 36; +$DamageType::Debris = 37; + +$DamageType::Drown = 38; + +$DamageType::Meteor = 50; +$DamageType::Cursing = 51; +$DamageType::Idiocy = 52; +$DamageType::SuperChaingun = 53; +$DamageType::KillerFog = 54; + +$DamageType::Flak = 55; +$DamageType::Sniper = 56; // NOTE: This value is referenced directly in code. DO NOT CHANGE! +$DamageType::MG = 57; +$DamageType::Bazooka = 58; +$DamageType::Artillery = 59; +$DamageType::MG42 = 60; +$DamageType::Pistol = 61; +$DamageType::Sentinel = 62; +$DamageType::melee = 63; +$DamageType::rifle = 64; +$DamageType::Shotgun = 65; +$DamageType::AT4 = 66; +$DamageType::RPG = 67; +$DamageType::Shotdown = 68; +$DamageType::ACCG = 69; +$DamageType::MP5 = 70; +$DamageType::DepthCharge = 71; +$DamageType::TankChaingunH = 72; +$DamageType::Infected = 73; +$DamageType::Zombie = 74; +$DamageType::ZAcid = 75; +$DamageType::ZombieL = 76; +$DamageType::PBC = 77; +$DamageType::Burn = 78; +$DamageType::SRifle = 79; +$DamageType::ShotDown = 80; +$DamageType::MedPackVaccine = 81; +$DamageType::Admin = 82; + +$DamageType::illegalpack = 96; + +// DMM -- added so MPBs that blow up under water get a message +$DamageType::Water = 97; + +//Tinman - used in Hunters for cheap bastards ;) +$DamageType::NexusCamping = 98; + +// MES -- added so CTRL-K can get a distinctive message +$DamageType::Suicide = 99; + +// Etc, etc. + +$DamageTypeText[0] = 'default'; +$DamageTypeText[1] = 'blaster'; +$DamageTypeText[2] = 'plasma'; +$DamageTypeText[3] = 'chaingun'; +$DamageTypeText[4] = 'disc'; +$DamageTypeText[5] = 'grenade'; +$DamageTypeText[6] = 'laser'; +$DamageTypeText[7] = 'ELF'; +$DamageTypeText[8] = 'mortar'; +$DamageTypeText[9] = 'missile'; +$DamageTypeText[10] = 'shocklance'; +$DamageTypeText[11] = 'mine'; +$DamageTypeText[12] = 'explosion'; +$DamageTypeText[13] = 'impact'; +$DamageTypeText[14] = 'ground'; +$DamageTypeText[15] = 'turret'; +$DamageTypeText[16] = 'plasma turret'; +$DamageTypeText[17] = 'AA turret'; +$DamageTypeText[18] = 'ELF turret'; +$DamageTypeText[19] = 'mortar turret'; +$DamageTypeText[20] = 'missile turret'; +$DamageTypeText[21] = 'clamp turret'; +$DamageTypeText[22] = 'spike turret'; +$DamageTypeText[23] = 'sentry turret'; +$DamageTypeText[24] = 'out of bounds'; +$DamageTypeText[25] = 'lava'; +$DamageTypeText[26] = 'shrike blaster'; +$DamageTypeText[27] = 'belly turret'; +$DamageTypeText[28] = 'bomber bomb'; +$DamageTypeText[29] = 'tank chaingun'; +$DamageTypeText[30] = 'tank mortar'; +$DamageTypeText[31] = 'satchel charge'; +$DamageTypeText[32] = 'MPB missile'; +$DamageTypeText[33] = 'lighting'; +$DamageTypeText[35] = 'ForceField'; +$DamageTypeText[36] = 'Crash'; + +$DamageTypeText[38] = 'Drown'; + +$DamageTypeText[50] = 'Meteor'; +$DamageTypeText[51] = 'Cursing'; +$DamageTypeText[52] = 'Idiocy'; +$DamageTypeText[53] = 'SuperChaingun'; +$DamageTypeText[54] = 'KillerFog'; + +$DamageTypeText[55] = 'Flak'; +$DamageTypeText[56] = 'Sniper'; +$DamageTypeText[57] = 'MG'; +$DamageTypeText[58] = 'Bazooka'; +$DamageTypeText[59] = 'Artillery'; +$DamageTypeText[60] = 'MG42'; +$DamageTypeText[61] = 'pistol'; +$DamageTypeText[62] = 'Sentinel'; +$DamageTypeText[63] = 'melee'; +$DamageTypeText[64] = 'rifle'; +$DamageTypeText[65] = 'shotgun'; +$DamageTypeText[66] = 'AT4'; +$DamageTypeText[67] = 'RPG'; +$DamageTypeText[68] = 'Shotdown'; +$DamageTypeText[69] = 'ACCG'; +$DamageTypeText[70] = 'MP5'; +$DamageTypeText[71] = 'DepthCharge'; +$DamageTypeText[72] = 'TankChaingunH'; +$DamageTypeText[73] = 'Infected'; +$DamageTypeText[74] = 'Zombie'; +$DamageTypeText[75] = 'ZAcid'; +$DamageTypeText[76] = 'ZombieL'; +$DamageTypeText[77] = 'PBC'; +$DamageTypeText[78] = 'Burn'; +$DamageTypeText[79] = 'SRifle'; +$DamageTypeText[80] = 'ShotDown'; +$DamageTypeText[82] = 'Admin'; + + +$DamageTypeText[96] = 'illegalpack'; + +$DamageTypeText[98] = 'nexus camping'; +$DamageTypeText[99] = 'suicide'; + + +// ##### PLEASE DO NOT REORDER THE DAMAGE PROFILE TABLES BELOW ##### +// (They are set up in the same order as the "Weapons Matrix.xls" sheet for ease of reference when balancing) + +//---------------------------------------------------------------------------- +// VEHICLE DAMAGE PROFILES +//---------------------------------------------------------------------------- + +//**** SHRIKE SCOUT FIGHTER **** +datablock SimDataBlock(ShrikeDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 1.75; + shieldDamageScale[$DamageType::Bullet] = 1.75; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ElfTurret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 4.0; + shieldDamageScale[$DamageType::BellyTurret] = 2.0; + shieldDamageScale[$DamageType::AATurret] = 3.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 2.5; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 2.5; + shieldDamageScale[$DamageType::SentryTurret] = 2.5; + shieldDamageScale[$DamageType::Disc] = 1.5; + shieldDamageScale[$DamageType::Grenade] = 1.0; + shieldDamageScale[$DamageType::Mine] = 3.0; + shieldDamageScale[$DamageType::Missile] = 3.0; + shieldDamageScale[$DamageType::Mortar] = 2.0; + shieldDamageScale[$DamageType::Plasma] = 0.3; + shieldDamageScale[$DamageType::BomberBombs] = 3.0; + shieldDamageScale[$DamageType::TankChaingun] = 3.0; + shieldDamageScale[$DamageType::TankMortar] = 2.0; + shieldDamageScale[$DamageType::MissileTurret] = 3.0; + shieldDamageScale[$DamageType::MortarTurret] = 2.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 0.1; + shieldDamageScale[$DamageType::SatchelCharge] = 3.5; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 1.1; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 3.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Debris] = 1.0; + shieldDamageScale[$DamageType::Flak] = 3.0; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 1.75; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.75; + shieldDamageScale[$DamageType::pistol] = 2.0; + shieldDamageScale[$DamageType::Sentinel] = 0.5; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 0.75; + + damageScale[$DamageType::Blaster] = 1.0; + damageScale[$DamageType::Bullet] = 1.0; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ElfTurret] = 1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 3.5; + damageScale[$DamageType::BellyTurret] = 1.2; + damageScale[$DamageType::AATurret] = 1.5; + damageScale[$DamageType::IndoorDepTurret] = 1.5; + damageScale[$DamageType::OutdoorDepTurret] = 1.5; + damageScale[$DamageType::SentryTurret] = 1.5; + damageScale[$DamageType::Disc] = 1.25; + damageScale[$DamageType::Grenade] = 0.75; + damageScale[$DamageType::Mine] = 4.0; + damageScale[$DamageType::Missile] = 2.0; + damageScale[$DamageType::Mortar] = 2.0; + damageScale[$DamageType::Plasma] = 0.3; + damageScale[$DamageType::BomberBombs] = 2.0; + damageScale[$DamageType::TankChaingun] = 1.5; + damageScale[$DamageType::TankMortar] = 2.0; + damageScale[$DamageType::MissileTurret] = 1.5; + damageScale[$DamageType::AT4] = 2.0; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 3.5; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.1; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 2.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Debris] = 0.75; + damageScale[$DamageType::Flak] = 2.0; + damageScale[$DamageType::Sniper] = 0.2; + damageScale[$DamageType::MG] = 0.2; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.25; + damageScale[$DamageType::pistol] = 0.15; + damageScale[$DamageType::Sentinel] = 0.5; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.125; + damageScale[$DamageType::shotgun] = 0.08; + damageScale[$DamageType::RPG] = 1.0; + damageScale[$DamageType::ACCG] = 1.0; + damageScale[$DamageType::MP5] = 0.18; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 1.3; + damageScale[$DamageType::PBC] = 1.5; + damageScale[$DamageType::SRifle] = 0.125; +}; + +//**** THUNDERSWORD BOMBER **** +datablock SimDataBlock(BomberDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 1.0; + shieldDamageScale[$DamageType::Bullet] = 2.0; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 3.5; + shieldDamageScale[$DamageType::BellyTurret] = 2.0; + shieldDamageScale[$DamageType::AATurret] = 3.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 2.25; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 2.25; + shieldDamageScale[$DamageType::SentryTurret] = 2.25; + shieldDamageScale[$DamageType::Disc] = 1.0; + shieldDamageScale[$DamageType::Grenade] = 1.0; + shieldDamageScale[$DamageType::Mine] = 3.0; + shieldDamageScale[$DamageType::Missile] = 3.0; + shieldDamageScale[$DamageType::Mortar] = 2.0; + shieldDamageScale[$DamageType::Plasma] = 0.3; + shieldDamageScale[$DamageType::BomberBombs] = 3.0; + shieldDamageScale[$DamageType::TankChaingun] = 3.0; + shieldDamageScale[$DamageType::TankMortar] = 2.0; + shieldDamageScale[$DamageType::MissileTurret] = 3.0; + shieldDamageScale[$DamageType::MortarTurret] = 2.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 2.0; + shieldDamageScale[$DamageType::SatchelCharge] = 3.5; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.8; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 3.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 3.0; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 1.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.0; + shieldDamageScale[$DamageType::pistol] = 1.25; + shieldDamageScale[$DamageType::Sentinel] = 0.3; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 1.0; + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 1.0; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.5; + damageScale[$DamageType::BellyTurret] = 1.2; + damageScale[$DamageType::AATurret] = 1.5; + damageScale[$DamageType::IndoorDepTurret] = 1.25; + damageScale[$DamageType::OutdoorDepTurret] = 1.25; + damageScale[$DamageType::SentryTurret] = 1.25; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 0.75; + damageScale[$DamageType::Mine] = 4.0; + damageScale[$DamageType::Missile] = 1.5; + damageScale[$DamageType::Mortar] = 0.5; + damageScale[$DamageType::Plasma] = 0.3; + damageScale[$DamageType::BomberBombs] = 2.0; + damageScale[$DamageType::TankChaingun] = 2.0; + damageScale[$DamageType::TankMortar] = 2.0; + damageScale[$DamageType::MissileTurret] = 1.5; + damageScale[$DamageType::AT4] = 1.0; + damageScale[$DamageType::MortarTurret] = 2.0; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 3.5; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.8; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 2.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 2.0; + damageScale[$DamageType::Sniper] = 0.2; + damageScale[$DamageType::MG] = 0.2; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.25; + damageScale[$DamageType::pistol] = 0.15; + damageScale[$DamageType::Sentinel] = 0.5; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.125; + damageScale[$DamageType::shotgun] = 0.08; + damageScale[$DamageType::RPG] = 1.0; + damageScale[$DamageType::ACCG] = 1.0; + damageScale[$DamageType::MP5] = 0.18; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 1.3; + damageScale[$DamageType::PBC] = 1.5; + damageScale[$DamageType::SRifle] = 0.125; +}; + +//**** HAVOC TRANSPORT **** +datablock SimDataBlock(HavocDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 1.0; + shieldDamageScale[$DamageType::Bullet] = 1.75; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 3.5; + shieldDamageScale[$DamageType::BellyTurret] = 2.0; + shieldDamageScale[$DamageType::AATurret] = 3.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 2.25; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 2.25; + shieldDamageScale[$DamageType::SentryTurret] = 2.25; + shieldDamageScale[$DamageType::Disc] = 1.0; + shieldDamageScale[$DamageType::Grenade] = 1.0; + shieldDamageScale[$DamageType::Mine] = 3.0; + shieldDamageScale[$DamageType::Missile] = 3.0; + shieldDamageScale[$DamageType::Mortar] = 2.0; + shieldDamageScale[$DamageType::Plasma] = 1.0; + shieldDamageScale[$DamageType::BomberBombs] = 3.0; + shieldDamageScale[$DamageType::TankChaingun] = 3.0; + shieldDamageScale[$DamageType::TankMortar] = 2.0; + shieldDamageScale[$DamageType::MissileTurret] = 3.0; + shieldDamageScale[$DamageType::MortarTurret] = 2.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 2.0; + shieldDamageScale[$DamageType::SatchelCharge] = 3.5; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.5; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 3.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 3.0; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 1.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.0; + shieldDamageScale[$DamageType::pistol] = 2.0; + shieldDamageScale[$DamageType::Sentinel] = 0.3; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 1.0; + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 1.75; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.5; + damageScale[$DamageType::BellyTurret] = 1.2; + damageScale[$DamageType::AATurret] = 1.5; + damageScale[$DamageType::IndoorDepTurret] = 1.25; + damageScale[$DamageType::OutdoorDepTurret] = 1.25; + damageScale[$DamageType::SentryTurret] = 1.25; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 0.75; + damageScale[$DamageType::Mine] = 4.0; + damageScale[$DamageType::Missile] = 1.5; + damageScale[$DamageType::Mortar] = 2.0; + damageScale[$DamageType::Plasma] = 0.5; + damageScale[$DamageType::BomberBombs] = 2.0; + damageScale[$DamageType::TankChaingun] = 2.0; + damageScale[$DamageType::TankMortar] = 2.0; + damageScale[$DamageType::MissileTurret] = 1.5; + damageScale[$DamageType::AT4] = 1.0; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 3.5; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.5; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 2.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 2.0; + damageScale[$DamageType::Sniper] = 0.2; + damageScale[$DamageType::MG] = 0.2; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.25; + damageScale[$DamageType::pistol] = 0.15; + damageScale[$DamageType::Sentinel] = 0.5; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.125; + damageScale[$DamageType::shotgun] = 0.08; + damageScale[$DamageType::RPG] = 1.0; + damageScale[$DamageType::ACCG] = 1.0; + damageScale[$DamageType::MP5] = 0.18; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 1.3; + damageScale[$DamageType::PBC] = 1.5; + damageScale[$DamageType::SRifle] = 0.125; +}; + +//**** WILDCAT GRAV CYCLE **** +datablock SimDataBlock(WildcatDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 2.0; + shieldDamageScale[$DamageType::Bullet] = 2.5; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 1.0; + shieldDamageScale[$DamageType::Laser] = 4.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 6.0; + shieldDamageScale[$DamageType::BellyTurret] = 2.0; + shieldDamageScale[$DamageType::AATurret] = 2.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 2.5; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 2.5; + shieldDamageScale[$DamageType::Disc] = 2.5; + shieldDamageScale[$DamageType::Grenade] = 2.0; + shieldDamageScale[$DamageType::Mine] = 4.0; + shieldDamageScale[$DamageType::Missile] = 4.0; + shieldDamageScale[$DamageType::Mortar] = 2.0; + shieldDamageScale[$DamageType::Plasma] = 10.0; + shieldDamageScale[$DamageType::BomberBombs] = 2.5; + shieldDamageScale[$DamageType::TankChaingun] = 3.0; + shieldDamageScale[$DamageType::TankMortar] = 2.0; + shieldDamageScale[$DamageType::MissileTurret] = 4.0; + shieldDamageScale[$DamageType::MortarTurret] = 1.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 2.0; + shieldDamageScale[$DamageType::SatchelCharge] = 3.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 1.25; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 5.0; + shieldDamageScale[$DamageType::Flak] = 0.25; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 1.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.0; + shieldDamageScale[$DamageType::pistol] = 1.0; + shieldDamageScale[$DamageType::Sentinel] = 1.0; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 1.25; + + damageScale[$DamageType::Blaster] = 1.5; + damageScale[$DamageType::Bullet] = 0.75; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 2.0; + damageScale[$DamageType::ShrikeBlaster] = 4.0; + damageScale[$DamageType::BellyTurret] = 1.5; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 1.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.25; + damageScale[$DamageType::Grenade] = 1.0; + damageScale[$DamageType::Mine] = 4.0; + damageScale[$DamageType::Missile] = 1.2; + damageScale[$DamageType::Mortar] = 1.0; + damageScale[$DamageType::Plasma] = 5.0; + damageScale[$DamageType::BomberBombs] = 2.0; + damageScale[$DamageType::TankChaingun] = 0.75; + damageScale[$DamageType::TankMortar] = 1.0; + damageScale[$DamageType::MissileTurret] = 1.2; + damageScale[$DamageType::AT4] = 2.0; + damageScale[$DamageType::MortarTurret] = 1.0; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 2.2; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.25; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 5.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.05; + damageScale[$DamageType::MG] = 0.25; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.25; + damageScale[$DamageType::pistol] = 0.25; + damageScale[$DamageType::Sentinel] = 1.0; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.35; + damageScale[$DamageType::shotgun] = 0.5; + damageScale[$DamageType::RPG] = 1.35; + damageScale[$DamageType::ACCG] = 1.5; + damageScale[$DamageType::MP5] = 0.25; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 1.0; + damageScale[$DamageType::PBC] = 1.0; + damageScale[$DamageType::SRifle] = 0.35; +}; + +//**** BEOWULF TANK **** +datablock SimDataBlock(TankDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.6; + shieldDamageScale[$DamageType::Bullet] = 1.0; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 1.75; + shieldDamageScale[$DamageType::BellyTurret] = 1.25; + shieldDamageScale[$DamageType::AATurret] = 0.8; + shieldDamageScale[$DamageType::IndoorDepTurret] = 0.0; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 0.8; + shieldDamageScale[$DamageType::Grenade] = 0.8; + shieldDamageScale[$DamageType::Mine] = 3.25; + shieldDamageScale[$DamageType::Missile] = 2.0; + shieldDamageScale[$DamageType::Mortar] = 1.7; + shieldDamageScale[$DamageType::Plasma] = 0.25; + shieldDamageScale[$DamageType::BomberBombs] = 1.5; + shieldDamageScale[$DamageType::TankChaingun] = 0.2; + shieldDamageScale[$DamageType::TankMortar] = 1.8; + shieldDamageScale[$DamageType::MissileTurret] = 1.25; + shieldDamageScale[$DamageType::MortarTurret] = 1.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 1.25; + shieldDamageScale[$DamageType::SatchelCharge] = 2.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.75; + shieldDamageScale[$DamageType::Ground] = 0.75; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 0.2; + shieldDamageScale[$DamageType::Sniper] = 0.1; + shieldDamageScale[$DamageType::MG] = 1.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.0; + shieldDamageScale[$DamageType::pistol] = 1.0; + shieldDamageScale[$DamageType::Sentinel] = 0.25; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 0.0; + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 0.15; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] =1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.0; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 0.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 0.25; + damageScale[$DamageType::Mine] = 2.25; + damageScale[$DamageType::Missile] = 1.25; + damageScale[$DamageType::Mortar] = 1.4; + damageScale[$DamageType::Plasma] = 0.05; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 0.2; + damageScale[$DamageType::TankMortar] = 1.6; + damageScale[$DamageType::MissileTurret] = 1.25; + damageScale[$DamageType::AT4] = 1; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 2.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.75; + damageScale[$DamageType::Ground] = 0.75; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.0; + damageScale[$DamageType::MG] = 0.0; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.0; + damageScale[$DamageType::pistol] = 0.0; + damageScale[$DamageType::Sentinel] = 0.2; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.0; + damageScale[$DamageType::shotgun] = 0.0; + damageScale[$DamageType::RPG] = 0.8; + damageScale[$DamageType::ACCG] = 0.8; + damageScale[$DamageType::MP5] = 0.0; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 0.5; + damageScale[$DamageType::PBC] = 0.4; + damageScale[$DamageType::SRifle] = 0; +}; + +//**** SUBMARINE **** +datablock SimDataBlock(SubDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.6; + shieldDamageScale[$DamageType::Bullet] = 1.0; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 1.75; + shieldDamageScale[$DamageType::BellyTurret] = 1.25; + shieldDamageScale[$DamageType::AATurret] = 0.8; + shieldDamageScale[$DamageType::IndoorDepTurret] = 0.0; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 0.8; + shieldDamageScale[$DamageType::Grenade] = 0.8; + shieldDamageScale[$DamageType::Mine] = 3.25; + shieldDamageScale[$DamageType::Missile] = 2.0; + shieldDamageScale[$DamageType::Mortar] = 1.7; + shieldDamageScale[$DamageType::Plasma] = 0.25; + shieldDamageScale[$DamageType::BomberBombs] = 1.5; + shieldDamageScale[$DamageType::TankChaingun] = 0.2; + shieldDamageScale[$DamageType::TankMortar] = 1.8; + shieldDamageScale[$DamageType::MissileTurret] = 1.25; + shieldDamageScale[$DamageType::MortarTurret] = 1.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 1.25; + shieldDamageScale[$DamageType::SatchelCharge] = 2.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.75; + shieldDamageScale[$DamageType::Ground] = 0.75; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 0.2; + shieldDamageScale[$DamageType::Sniper] = 0.1; + shieldDamageScale[$DamageType::MG] = 1.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 1.0; + shieldDamageScale[$DamageType::pistol] = 1.0; + shieldDamageScale[$DamageType::Sentinel] = 0.25; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 0.0; + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 0.15; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] =1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.0; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 0.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 0.25; + damageScale[$DamageType::Mine] = 2.25; + damageScale[$DamageType::Missile] = 1.25; + damageScale[$DamageType::Mortar] = 1.4; + damageScale[$DamageType::Plasma] = 0.15; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 0.2; + damageScale[$DamageType::TankMortar] = 1.6; + damageScale[$DamageType::MissileTurret] = 1.25; + damageScale[$DamageType::AT4] = 0.3; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 2.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.75; + damageScale[$DamageType::Ground] = 0.75; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.0; + damageScale[$DamageType::MG] = 0.0; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.0; + damageScale[$DamageType::pistol] = 0.0; + damageScale[$DamageType::Sentinel] = 0.2; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.0; + damageScale[$DamageType::shotgun] = 0.0; + damageScale[$DamageType::RPG] = 0.8; + damageScale[$DamageType::ACCG] = 0.2; + damageScale[$DamageType::MP5] = 0.0; + damageScale[$DamageType::DepthCharge] = 1.0; + damageScale[$DamageType::TankChaingunH] = 0.2; + damageScale[$DamageType::PBC] = 3.0; + damageScale[$DamageType::SRifle] = 0; +}; + +datablock SimDataBlock(SentinelDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.6; + shieldDamageScale[$DamageType::Bullet] = 0.75; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 1.75; + shieldDamageScale[$DamageType::BellyTurret] = 1.25; + shieldDamageScale[$DamageType::AATurret] = 0.8; + shieldDamageScale[$DamageType::IndoorDepTurret] = 0.0; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 0.8; + shieldDamageScale[$DamageType::Grenade] = 0.8; + shieldDamageScale[$DamageType::Mine] = 3.25; + shieldDamageScale[$DamageType::Missile] = 2.0; + shieldDamageScale[$DamageType::Mortar] = 0.8; + shieldDamageScale[$DamageType::Plasma] = 0.25; + shieldDamageScale[$DamageType::BomberBombs] = 1.5; + shieldDamageScale[$DamageType::TankChaingun] = 1.5; + shieldDamageScale[$DamageType::TankMortar] = 1.4; + shieldDamageScale[$DamageType::MissileTurret] = 1.25; + shieldDamageScale[$DamageType::MortarTurret] = 1.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 1.25; + shieldDamageScale[$DamageType::SatchelCharge] = 2.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.5; + shieldDamageScale[$DamageType::Ground] = 0.5; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 0.15; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 2.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 2.0; + shieldDamageScale[$DamageType::pistol] = 2.0; + shieldDamageScale[$DamageType::Sentinel] = 0.0; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 0.5; + + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 0.5; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 0.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.0; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 0.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 1.0; + damageScale[$DamageType::Mine] = 2.25; + damageScale[$DamageType::Missile] = 1.25; + damageScale[$DamageType::Mortar] = 1.0; + damageScale[$DamageType::Plasma] = 0.2; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 0.6; + damageScale[$DamageType::TankMortar] = 1.0; + damageScale[$DamageType::MissileTurret] = 1.25; + damageScale[$DamageType::AT4] = 0.75; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 2.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.5; + damageScale[$DamageType::Ground] = 0.5; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 0.15; + damageScale[$DamageType::Sniper] = 0.2; + damageScale[$DamageType::MG] = 0.1; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.2; + damageScale[$DamageType::pistol] = 0.1; + damageScale[$DamageType::Sentinel] = 0.0; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.15; + damageScale[$DamageType::shotgun] = 0.05; + damageScale[$DamageType::RPG] = 0.75; + damageScale[$DamageType::ACCG] = 0.5; + damageScale[$DamageType::MP5] = 0.1; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 0.6; + damageScale[$DamageType::PBC] = 0.75; + damageScale[$DamageType::SRifle] = 0.15; +}; + +//**** JERICHO MPB **** +datablock SimDataBlock(MPBDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.6; + shieldDamageScale[$DamageType::Bullet] = 0.75; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 1.75; + shieldDamageScale[$DamageType::BellyTurret] = 1.25; + shieldDamageScale[$DamageType::AATurret] = 0.8; + shieldDamageScale[$DamageType::IndoorDepTurret] = 0.0; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 0.8; + shieldDamageScale[$DamageType::Grenade] = 0.8; + shieldDamageScale[$DamageType::Mine] = 3.25; + shieldDamageScale[$DamageType::Missile] = 2.0; + shieldDamageScale[$DamageType::Mortar] = 0.8; + shieldDamageScale[$DamageType::Plasma] = 0.25; + shieldDamageScale[$DamageType::BomberBombs] = 1.5; + shieldDamageScale[$DamageType::TankChaingun] = 1.5; + shieldDamageScale[$DamageType::TankMortar] = 1.4; + shieldDamageScale[$DamageType::MissileTurret] = 1.25; + shieldDamageScale[$DamageType::MortarTurret] = 1.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 1.25; + shieldDamageScale[$DamageType::SatchelCharge] = 2.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 0.5; + shieldDamageScale[$DamageType::Ground] = 0.5; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 10.0; + shieldDamageScale[$DamageType::Flak] = 0.15; + shieldDamageScale[$DamageType::Sniper] = 0.2; + shieldDamageScale[$DamageType::MG] = 2.0; + shieldDamageScale[$DamageType::Bazooka] = 1.5; + shieldDamageScale[$DamageType::Artillery] = 1.5; + shieldDamageScale[$DamageType::MG42] = 2.0; + shieldDamageScale[$DamageType::pistol] = 2.0; + shieldDamageScale[$DamageType::Sentinel] = 0.25; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 0.5; + + damageScale[$DamageType::Blaster] = 0.75; + damageScale[$DamageType::Bullet] = 0.5; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 0.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.0; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 0.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 1.0; + damageScale[$DamageType::Mine] = 2.25; + damageScale[$DamageType::Missile] = 1.25; + damageScale[$DamageType::Mortar] = 1.0; + damageScale[$DamageType::Plasma] = 0.2; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 0.6; + damageScale[$DamageType::TankMortar] = 1.0; + damageScale[$DamageType::MissileTurret] = 1.25; + damageScale[$DamageType::AT4] = 0.75; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 2.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.5; + damageScale[$DamageType::Ground] = 0.5; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 10.0; + damageScale[$DamageType::Flak] = 0.15; + damageScale[$DamageType::Sniper] = 0.2; + damageScale[$DamageType::MG] = 0.1; + damageScale[$DamageType::Bazooka] = 1.5; + damageScale[$DamageType::Artillery] = 1.5; + damageScale[$DamageType::MG42] = 0.2; + damageScale[$DamageType::pistol] = 0.1; + damageScale[$DamageType::Sentinel] = 0.2; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.15; + damageScale[$DamageType::shotgun] = 0.05; + damageScale[$DamageType::RPG] = 0.75; + damageScale[$DamageType::ACCG] = 0.5; + damageScale[$DamageType::MP5] = 0.1; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 0.6; + damageScale[$DamageType::PBC] = 0.75; + damageScale[$DamageType::SRifle] = 0.15; +}; + +//---------------------------------------------------------------------------- +// TURRET DAMAGE PROFILES +//---------------------------------------------------------------------------- + +datablock SimDataBlock(TurretDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.8; + shieldDamageScale[$DamageType::Bullet] = 0.8; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 0.5; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 3.0; + shieldDamageScale[$DamageType::BellyTurret] = 2.0; + shieldDamageScale[$DamageType::AATurret] = 1.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::SentryTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 1.0; + shieldDamageScale[$DamageType::Grenade] = 1.5; + shieldDamageScale[$DamageType::Mine] = 3.0; + shieldDamageScale[$DamageType::Missile] = 3.0; + shieldDamageScale[$DamageType::Mortar] = 3.0; + shieldDamageScale[$DamageType::Plasma] = 1.0; + shieldDamageScale[$DamageType::BomberBombs] = 2.0; + shieldDamageScale[$DamageType::TankChaingun] = 1.5; + shieldDamageScale[$DamageType::TankMortar] = 3.0; + shieldDamageScale[$DamageType::MissileTurret] = 3.0; + shieldDamageScale[$DamageType::MortarTurret] = 3.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 2.0; + shieldDamageScale[$DamageType::SatchelCharge] = 4.5; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 1.0; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 2.0; + shieldDamageScale[$DamageType::Lightning] = 5.0; + shieldDamageScale[$DamageType::Flak] = 0.2; + shieldDamageScale[$DamageType::Sniper] = 0.1; + shieldDamageScale[$DamageType::MG] = 0.75; + shieldDamageScale[$DamageType::Bazooka] = 3.0; + shieldDamageScale[$DamageType::Artillery] = 5.0; + shieldDamageScale[$DamageType::MG42] = 0.75; + shieldDamageScale[$DamageType::pistol] = 0.75; + shieldDamageScale[$DamageType::Sentinel] = 1.0; + shieldDamageScale[$DamageType::melee] = 0.0; + shieldDamageScale[$DamageType::rifle] = 1.0; + + damageScale[$DamageType::Blaster] = 0.8; + damageScale[$DamageType::Bullet] = 0.9; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 1.0; + damageScale[$DamageType::ShockLance] = 0.50; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 1.0; + damageScale[$DamageType::BellyTurret] = 0.6; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 1.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::SentryTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.1; + damageScale[$DamageType::Grenade] = 1.0; + damageScale[$DamageType::Mine] = 1.5; + damageScale[$DamageType::Missile] = 1.25; + damageScale[$DamageType::Mortar] = 1.25; + damageScale[$DamageType::Plasma] = 0.75; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 1.25; + damageScale[$DamageType::TankMortar] = 1.25; + damageScale[$DamageType::MissileTurret] = 1.25; + damageScale[$DamageType::AT4] = 3.0; + damageScale[$DamageType::MortarTurret] = 1.25; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 1.5; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.0; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 5.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.1; + damageScale[$DamageType::MG] = 0.75; + damageScale[$DamageType::Bazooka] = 3.0; + damageScale[$DamageType::Artillery] = 5.0; + damageScale[$DamageType::MG42] = 0.75; + damageScale[$DamageType::pistol] = 0.75; + damageScale[$DamageType::Sentinel] = 1.0; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 0.75; + damageScale[$DamageType::shotgun] = 0.5; + damageScale[$DamageType::RPG] = 1.75; + damageScale[$DamageType::ACCG] = 1.1; + damageScale[$DamageType::MP5] = 0.75; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::TankChaingunH] = 1.25; + damageScale[$DamageType::PBC] = 1.5; + damageScale[$DamageType::SRifle] = 0.75; +}; + +//---------------------------------------------------------------------------- +// STATIC SHAPE DAMAGE PROFILES +//---------------------------------------------------------------------------- + +datablock SimDataBlock(StaticShapeDamageProfile) +{ + shieldDamageScale[$DamageType::Blaster] = 0.8; + shieldDamageScale[$DamageType::Bullet] = 0.0; + shieldDamageScale[$DamageType::ELF] = 1.0; + shieldDamageScale[$DamageType::ELFturret] = 1.0; + shieldDamageScale[$DamageType::ShockLance] = 1.0; + shieldDamageScale[$DamageType::Laser] = 1.0; + shieldDamageScale[$DamageType::ShrikeBlaster] = 2.0; + shieldDamageScale[$DamageType::BellyTurret] = 1.5; + shieldDamageScale[$DamageType::AATurret] = 1.0; + shieldDamageScale[$DamageType::IndoorDepTurret] = 0.1; + shieldDamageScale[$DamageType::OutdoorDepTurret] = 1.0; + shieldDamageScale[$DamageType::Turret] = 1.0; + shieldDamageScale[$DamageType::SentryTurret] = 1.0; + shieldDamageScale[$DamageType::Disc] = 1.0; + shieldDamageScale[$DamageType::Grenade] = 1.2; + shieldDamageScale[$DamageType::Mine] = 2.0; + shieldDamageScale[$DamageType::Missile] = 3.0; + shieldDamageScale[$DamageType::Mortar] = 3.0; + shieldDamageScale[$DamageType::Plasma] = 10.0; + shieldDamageScale[$DamageType::BomberBombs] = 2.0; + shieldDamageScale[$DamageType::TankChaingun] = 1.5; + shieldDamageScale[$DamageType::TankMortar] = 3.0; + shieldDamageScale[$DamageType::MissileTurret] = 3.0; + shieldDamageScale[$DamageType::MortarTurret] = 3.0; + shieldDamageScale[$DamageType::PlasmaTurret] = 2.0; + shieldDamageScale[$DamageType::SatchelCharge] = 6.0; + shieldDamageScale[$DamageType::Default] = 1.0; + shieldDamageScale[$DamageType::Impact] = 1.25; + shieldDamageScale[$DamageType::Ground] = 1.0; + shieldDamageScale[$DamageType::Explosion] = 1.0; + shieldDamageScale[$DamageType::Lightning] = 5.0; + shieldDamageScale[$DamageType::Flak] = 0.2; + shieldDamageScale[$DamageType::Sniper] = 0.1; + shieldDamageScale[$DamageType::MG] = 0.75; + shieldDamageScale[$DamageType::Bazooka] = 3.0; + shieldDamageScale[$DamageType::Artillery] = 5.0; + shieldDamageScale[$DamageType::MG42] = 0.75; + shieldDamageScale[$DamageType::pistol] = 0.75; + shieldDamageScale[$DamageType::Sentinel] = 1.0; + shieldDamageScale[$DamageType::melee] = 0.1; + shieldDamageScale[$DamageType::rifle] = 1.0; + + damageScale[$DamageType::Blaster] = 1.0; + damageScale[$DamageType::Bullet] = 0.5; + damageScale[$DamageType::ELF] = 0.0; + damageScale[$DamageType::ELFturret] = 1.0; + damageScale[$DamageType::ShockLance] = 1.0; + damageScale[$DamageType::Laser] = 1.0; + damageScale[$DamageType::ShrikeBlaster] = 2.0; + damageScale[$DamageType::BellyTurret] = 1.2; + damageScale[$DamageType::AATurret] = 1.0; + damageScale[$DamageType::IndoorDepTurret] = 0.1; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::SentryTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.15; + damageScale[$DamageType::Grenade] = 1.2; + damageScale[$DamageType::Mine] = 2.0; + damageScale[$DamageType::Missile] = 2.0; + damageScale[$DamageType::Mortar] = 2.0; + damageScale[$DamageType::Plasma] = 1.5; + damageScale[$DamageType::BomberBombs] = 1.0; + damageScale[$DamageType::TankChaingun] = 1.0; + damageScale[$DamageType::TankMortar] = 2.0; + damageScale[$DamageType::MissileTurret] = 2.0; + damageScale[$DamageType::AT4] = 2.0; + damageScale[$DamageType::MortarTurret] = 1.25; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 4.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.25; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 5.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.1; + damageScale[$DamageType::MG] = 0.75; + damageScale[$DamageType::Bazooka] = 3.0; + damageScale[$DamageType::Artillery] = 5.0; + damageScale[$DamageType::MG42] = 0.75; + damageScale[$DamageType::pistol] = 0.75; + damageScale[$DamageType::Sentinel] = 1.0; + damageScale[$DamageType::melee] = 0.0; + damageScale[$DamageType::rifle] = 1.0; + damageScale[$DamageType::shotgun] = 0.5; + damageScale[$DamageType::RPG] = 2.0; + damageScale[$DamageType::ACCG] = 0.5; + damageScale[$DamageType::MP5] = 0.75; + damageScale[$DamageType::DepthCharge] = 0.0; + damageScale[$DamageType::PBC] = 1.0; + damageScale[$DamageType::SRifle] = 1; +}; + +//---------------------------------------------------------------------------- +// PLAYER DAMAGE PROFILES +//---------------------------------------------------------------------------- + +datablock SimDataBlock(LightPlayerDamageProfile) +{ + damageScale[$DamageType::Blaster] = 1.3; + damageScale[$DamageType::Bullet] = 1.2; + damageScale[$DamageType::ELF] = 0.75; + damageScale[$DamageType::ELFturret] = 0.75; + damageScale[$DamageType::ShockLance] = 1.0; + damageScale[$DamageType::Laser] = 1.12; + damageScale[$DamageType::ShrikeBlaster] = 1.10; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 0.7; + damageScale[$DamageType::IndoorDepTurret] = 1.3; + damageScale[$DamageType::OutdoorDepTurret] = 1.3; + damageScale[$DamageType::SentryTurret] = 1.0; + damageScale[$DamageType::Disc] = 1.0; + damageScale[$DamageType::Grenade] = 1.2; + damageScale[$DamageType::Mine] = 1.0; + damageScale[$DamageType::Missile] = 1.0; + damageScale[$DamageType::Mortar] = 1.3; + damageScale[$DamageType::Plasma] = 2.0; + damageScale[$DamageType::BomberBombs] = 3.0; + damageScale[$DamageType::TankChaingun] = 1.7; + damageScale[$DamageType::TankMortar] = 1.0; + damageScale[$DamageType::MissileTurret] = 1.0; + damageScale[$DamageType::AT4] = 0.75; + damageScale[$DamageType::MortarTurret] = 1.2; + damageScale[$DamageType::PlasmaTurret] = 0.5; + damageScale[$DamageType::SatchelCharge] = 3.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.2; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 1.0; + damageScale[$DamageType::Lightning] = 1.0; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 1.75; + damageScale[$DamageType::MG] = 1.2; + damageScale[$DamageType::Bazooka] = 0.4; + damageScale[$DamageType::Artillery] = 1.0; + damageScale[$DamageType::MG42] = 1.8; + damageScale[$DamageType::pistol] = 1.2; + damageScale[$DamageType::melee] = 1.0; + damageScale[$DamageType::rifle] = 1.2; + damageScale[$DamageType::shotgun] = 1.2; + damageScale[$DamageType::RPG] = 1.2; + damageScale[$DamageType::ACCG] = 1.75; + damageScale[$DamageType::MP5] = 1.2; + damageScale[$DamageType::DepthCharge] = 0.1; + damageScale[$DamageType::TankChaingunH] = 1.7; + damageScale[$DamageType::PBC] = 1.0; + damageScale[$DamageType::SRifle] = 1.2; +}; + +datablock SimDataBlock(MediumPlayerDamageProfile) +{ + damageScale[$DamageType::Blaster] = 1.0; + damageScale[$DamageType::Bullet] = 1.0; + damageScale[$DamageType::ELF] = 0.75; + damageScale[$DamageType::ELFturret] = 0.75; + damageScale[$DamageType::ShockLance] = 1.0; + damageScale[$DamageType::Laser] = 1.1; + damageScale[$DamageType::ShrikeBlaster] = 1.0; + damageScale[$DamageType::BellyTurret] = 1.0; + damageScale[$DamageType::AATurret] = 0.7; + damageScale[$DamageType::IndoorDepTurret] = 1.0; + damageScale[$DamageType::OutdoorDepTurret] = 1.0; + damageScale[$DamageType::SentryTurret] = 1.0; + damageScale[$DamageType::Disc] = 0.8; + damageScale[$DamageType::Grenade] = 1.0; + damageScale[$DamageType::Mine] = 0.9; + damageScale[$DamageType::Missile] = 0.8; + damageScale[$DamageType::Mortar] = 1.2; + damageScale[$DamageType::Plasma] = 2.0; + damageScale[$DamageType::BomberBombs] = 3.0; + damageScale[$DamageType::TankChaingun] = 1.5; + damageScale[$DamageType::TankMortar] = 0.85; + damageScale[$DamageType::MissileTurret] = 0.8; + damageScale[$DamageType::AT4] = 0.6; + damageScale[$DamageType::MortarTurret] = 1.0; + damageScale[$DamageType::PlasmaTurret] = 0.65; + damageScale[$DamageType::SatchelCharge] = 3.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 1.0; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 0.8; + damageScale[$DamageType::Lightning] = 1.2; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 2.4; + damageScale[$DamageType::MG] = 1.0; + damageScale[$DamageType::Bazooka] = 0.6; + damageScale[$DamageType::Artillery] = 1.0; + damageScale[$DamageType::MG42] = 1.4; + damageScale[$DamageType::pistol] = 1.2; + damageScale[$DamageType::melee] = 1.75; + damageScale[$DamageType::rifle] = 1.0; + damageScale[$DamageType::shotgun] = 1.0; + damageScale[$DamageType::RPG] = 1.0; + damageScale[$DamageType::ACCG] = 1.1; + damageScale[$DamageType::MP5] = 1.0; + damageScale[$DamageType::DepthCharge] = 0.1; + damageScale[$DamageType::TankChaingunH] = 1.5; + damageScale[$DamageType::PBC] = 1.0; + damageScale[$DamageType::SRifle] = 1.0; +}; + +datablock SimDataBlock(HeavyPlayerDamageProfile) +{ + damageScale[$DamageType::Blaster] = 0.7; + damageScale[$DamageType::Bullet] = 0.8; + damageScale[$DamageType::ELF] = 0.75; + damageScale[$DamageType::ELFturret] = 0.75; + damageScale[$DamageType::ShockLance] = 1.0; + damageScale[$DamageType::Laser] = 0.67; + damageScale[$DamageType::ShrikeBlaster] = 0.8; + damageScale[$DamageType::BellyTurret] = 0.8; + damageScale[$DamageType::AATurret] = 0.6; + damageScale[$DamageType::IndoorDepTurret] = 0.7; + damageScale[$DamageType::OutdoorDepTurret] = 0.7; + damageScale[$DamageType::SentryTurret] = 1.0; + damageScale[$DamageType::Disc] = 0.6; + damageScale[$DamageType::Grenade] = 0.8; + damageScale[$DamageType::Mine] = 0.8; + damageScale[$DamageType::Missile] = 0.6; + damageScale[$DamageType::Mortar] = 0.7; + damageScale[$DamageType::Plasma] = 0.75; + damageScale[$DamageType::BomberBombs] = 3.0; + damageScale[$DamageType::TankChaingun] = 1.3; + damageScale[$DamageType::TankMortar] = 0.7; + damageScale[$DamageType::MissileTurret] = 0.6; + damageScale[$DamageType::AT4] = 0.7; + damageScale[$DamageType::MortarTurret] = 0.5; + damageScale[$DamageType::PlasmaTurret] = 0.4; + damageScale[$DamageType::SatchelCharge] = 3.0; + damageScale[$DamageType::Default] = 1.0; + damageScale[$DamageType::Impact] = 0.8; + damageScale[$DamageType::Ground] = 1.0; + damageScale[$DamageType::Explosion] = 0.6; + damageScale[$DamageType::Lightning] = 1.4; + damageScale[$DamageType::Flak] = 0.2; + damageScale[$DamageType::Sniper] = 0.6; + damageScale[$DamageType::MG] = 0.6; + damageScale[$DamageType::Bazooka] = 0.9; + damageScale[$DamageType::Artillery] = 1.0; + damageScale[$DamageType::MG42] = 0.8; + damageScale[$DamageType::pistol] = 0.4; + damageScale[$DamageType::melee] = 0.2; + damageScale[$DamageType::rifle] = 0.4; + damageScale[$DamageType::shotgun] = 0.6; + damageScale[$DamageType::RPG] = 0.8; + damageScale[$DamageType::ACCG] = 1.2; + damageScale[$DamageType::MP5] = 0.6; + damageScale[$DamageType::DepthCharge] = 0.1; + damageScale[$DamageType::TankChaingunH] = 1.6; + damageScale[$DamageType::PBC] = 1.0; + damageScale[$DamageType::SRifle] = 0.4; +}; diff --git a/Scripts/deathMessages.cs b/Scripts/deathMessages.cs new file mode 100644 index 0000000..f505c4d --- /dev/null +++ b/Scripts/deathMessages.cs @@ -0,0 +1,563 @@ +//------------------------------------------------------------------------------ +// ACCM Death Messages - By Blnukem. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// %1 = Victim's name +// %2 = Victim's gender (value will be either "him" or "her") +// %3 = Victim's possessive gender (value will be either "his" or "her") +// %4 = Killer's name +// %5 = Killer's gender (value will be either "him" or "her") +// %6 = Killer's possessive gender (value will be either "his" or "her") +// %7 = implement that killed the victim (value is the object number of the bullet, disc, etc) +//------------------------------------------------------------------------------ + +$DeathMessageCampingCount = 1; +$DeathMessageCamping[0] = '\c0%1 was killed for camping near the Nexus.'; + +$DeathMessageOOBCount = 1; +$DeathMessageOOB[0] = '\c0%1 was killed for leaving the battlefield.'; + +$DeathMessageLavaCount = 1; +$DeathMessageLava[0] = '\c0%1 fell in lava.'; + +$DeathMessageLightningCount = 1; +$DeathMessageLightning[0] = '\c0%1 was killed by lightning!'; + +$DeathMessageSuicideCount = 1; +$DeathMessageSuicide[0] = '\c0%1 committed suicide.'; + +$DeathMessageVehPadCount = 1; +$DeathMessageVehPad[0] = '\c0%1 got caught in a vehicle spawn field.'; + +$DeathMessageFFPowerupCount = 3; +$DeathMessageFFPowerup[0] = '\c0%1 got caught up in a forcefield during power up.'; +$DeathMessageFFPowerup[1] = '\c0%1 was vaporized by a forcefield.'; +$DeathMessageFFPowerup[2] = '\c0%1 gets %2self fried in a forcefield.'; + +$DeathMessageRogueMineCount = 1; +$DeathMessageRogueMine[$DamageType::Mine, 0] = '\c0%1 goes dancing in the minefield!'; + +//------------------------------------------------------------------------------ +// These are used when a player kills themself with a weapon or by other means. +//------------------------------------------------------------------------------ + +$DeathMessageSelfKillCount = 5; +$DeathMessageSelfKill[$DamageType::Plasma, 0] = '\c0%1 suffers 3rd degree burns.'; +$DeathMessageSelfKill[$DamageType::Plasma, 1] = '\c0%1 thought being a marshmellow would be fun, but was sadly mistaken.'; +$DeathMessageSelfKill[$DamageType::Plasma, 2] = '\c0%1 barbecued %2self.'; +$DeathMessageSelfKill[$DamageType::Plasma, 3] = '\c0%1 ends up a charred corpse.'; +$DeathMessageSelfKill[$DamageType::Plasma, 4] = '\c0%1 fries %2self to a crisp.'; + +$DeathMessageSelfKill[$DamageType::Grenade, 0] = '\c0%1 threw the pin, not the grenade.'; +$DeathMessageSelfKill[$DamageType::Grenade, 1] = '\c0%1 held the grenade a second too long.'; +$DeathMessageSelfKill[$DamageType::Grenade, 2] = '\c0%1 pulled the pin a shade early.'; +$DeathMessageSelfKill[$DamageType::Grenade, 3] = '\c0%1 took a bad bounce from %3 own grenade!'; +$DeathMessageSelfKill[$DamageType::Grenade, 4] = '\c0%1 destroyed %2self with a grenade!'; + +$DeathMessageSelfKill[$DamageType::Missile, 0] = '\c0%1 kills %2self with a missile!'; +$DeathMessageSelfKill[$DamageType::Missile, 1] = '\c0%1 runs a missile up %3 own tailpipe.'; +$DeathMessageSelfKill[$DamageType::Missile, 2] = '\c0%1 tests the missile\'s shaped charge on %2self.'; +$DeathMessageSelfKill[$DamageType::Missile, 3] = '\c0%1 achieved missile lock on %2self.'; +$DeathMessageSelfKill[$DamageType::Missile, 4] = '\c0%1 gracefully smoked %2self with a missile!'; + +$DeathMessageSelfKill[$DamageType::Mine, 0] = '\c0%1 is killed by %3 own mine.'; +$DeathMessageSelfKill[$DamageType::Mine, 1] = '\c0%1\'s mine violently reminds %2 of its existence.'; +$DeathMessageSelfKill[$DamageType::Mine, 2] = '\c0%1 scatters his remains to the wind.'; +$DeathMessageSelfKill[$DamageType::Mine, 3] = '\c0%1 was blown quite a distance by %3 own mine.'; +$DeathMessageSelfKill[$DamageType::Mine, 4] = '\c0%1 now knows where he hid %3 mines!'; + +$DeathMessageSelfKill[$DamageType::SatchelCharge, 0] = '\c0%1 goes out with a bang!'; +$DeathMessageSelfKill[$DamageType::SatchelCharge, 1] = '\c0%1 blows %2self up with explosives.'; +$DeathMessageSelfKill[$DamageType::SatchelCharge, 2] = '\c0%1 adds a nice red tint to the surrounding environment.'; +$DeathMessageSelfKill[$DamageType::SatchelCharge, 3] = '\c0It is now raining little bits of %1.'; +$DeathMessageSelfKill[$DamageType::SatchelCharge, 4] = '\c0%1 splashes all over the map.'; + +$DeathMessageSelfKill[$DamageType::Ground, 0] = '\c0%1 lands too hard.'; +$DeathMessageSelfKill[$DamageType::Ground, 1] = '\c0%1 finds gravity unforgiving.'; +$DeathMessageSelfKill[$DamageType::Ground, 2] = '\c0%1 painfully discovered gravity.'; +$DeathMessageSelfKill[$DamageType::Ground, 3] = '\c0%1 forgot %3 parachute.'; +$DeathMessageSelfKill[$DamageType::Ground, 4] = '\c0%1 loses a game of chicken with the ground.'; + +$DeathMessageSelfKill[$DamageType::Bazooka, 0] = '\c0Dont worry %1, you\'re not the first dumbass to kill %2self with explosives.'; +$DeathMessageSelfKill[$DamageType::Bazooka, 1] = '\c0%1 blows %2self up with a bazooka.'; +$DeathMessageSelfKill[$DamageType::Bazooka, 2] = '\c0%1 is dumb enough to fire a bazooka at close range.'; +$DeathMessageSelfKill[$DamageType::Bazooka, 3] = '\c0%1 aimed %3 bazooka at his feet, fired, and then magically disapeared into smoke...'; +$DeathMessageSelfKill[$DamageType::Bazooka, 4] = '\c0%1 squeezes %3 bazooka shell tightly...'; + +$DeathMessageSelfKill[$DamageType::RPG, 0] = '\c0%1 succesfully RPGs %2self.'; +$DeathMessageSelfKill[$DamageType::RPG, 1] = '\c0%1 runs an RPG up %3 own tailpipe!'; +$DeathMessageSelfKill[$DamageType::RPG, 2] = '\c0%1 is such a dumbass, he kills %2self with an RPG.'; +$DeathMessageSelfKill[$DamageType::RPG, 3] = '\c0Um... Wow... %1 succesfully earned the medal of total dumbass by destroying %2self with an RPG.'; +$DeathMessageSelfKill[$DamageType::RPG, 4] = '\c0%1 fall down go boom... from %3 own RPG...'; + +$DeathMessageSelfKill[$DamageType::Laser, 0] = '\c0%1 destroyed himeself.'; +$DeathMessageSelfKill[$DamageType::Laser, 1] = '\c0%1 is dumb enough to shoot the Gauss Cannon at his feet.'; +$DeathMessageSelfKill[$DamageType::Laser, 2] = '\c0%1 fired the Gauss Cannon a little too close.'; +$DeathMessageSelfKill[$DamageType::Laser, 3] = '\c0%1 thought he was the enemy, so he killed %2self.'; +$DeathMessageSelfKill[$DamageType::Laser, 4] = '\c0%1 makes short work of %2self.'; + +$DeathMessageSelfKill[$DamageType::MedPackVaccine, 0] = '\c0%1 injected %2self with too much antidote vaccine.'; +$DeathMessageSelfKill[$DamageType::MedPackVaccine, 1] = '\c0%1 should\'ve paid more attention to the medpack instruction manual.'; +$DeathMessageSelfKill[$DamageType::MedPackVaccine, 2] = '\c0%1 overdosed %2self with zombie vaccine drugs.'; +$DeathMessageSelfKill[$DamageType::MedPackVaccine, 3] = '\c0%1 thought that the zombie vaccine would feel just as good as the heroin back home.'; +$DeathMessageSelfKill[$DamageType::MedPackVaccine, 4] = '\c0%1 will be sleeping with the zombies tonight.'; + +//------------------------------------------------------------------------------ +// These are used when a teamate kills another teamate with a weapon or other means. +//------------------------------------------------------------------------------ + +$DeathMessageTeamKillCount = 1; +$DeathMessageTeamKill[$DamageType::Blaster, 0] = '\c0%4 TEAMKILLED %1 with a blaster!'; +$DeathMessageTeamKill[$DamageType::Plasma, 0] = '\c0%4 TEAMKILLED %1 with fire!'; +$DeathMessageTeamKill[$DamageType::Bullet, 0] = '\c0%4 TEAMKILLED %1 with a chaingun!'; +$DeathMessageTeamKill[$DamageType::Disc, 0] = '\c0%4 TEAMKILLED %1 with a spinfusor!'; +$DeathMessageTeamKill[$DamageType::Grenade, 0] = '\c0%4 TEAMKILLED %1 with a grenade!'; +$DeathMessageTeamKill[$DamageType::Laser, 0] = '\c0%4 TEAMKILLED %1 with a gauss cannon!'; +$DeathMessageTeamKill[$DamageType::Elf, 0] = '\c0%4 TEAMKILLED %1 with an ELF projector!'; +$DeathMessageTeamKill[$DamageType::Missile, 0] = '\c0%4 TEAMKILLED %1 with a missile!'; +$DeathMessageTeamKill[$DamageType::Shocklance, 0] = '\c0%4 TEAMKILLED %1 with a shocklance!'; +$DeathMessageTeamKill[$DamageType::Mine, 0] = '\c0%4 TEAMKILLED %1 with a mine!'; +$DeathMessageTeamKill[$DamageType::SatchelCharge, 0] = '\c0%4 blew up TEAMMATE %1!'; +$DeathMessageTeamKill[$DamageType::Impact, 0] = '\c0%4 runs down TEAMMATE %1!'; +$DeathMessageTeamKill[$DamageType::SuperChaingun, 0] = '\c0%4 TEAMKILLED %1 with a super chaingun!'; +$DeathMessageTeamKill[$DamageType::Sniper, 0] = '\c0%4 TEAMKILLED %1 by using a sniper rifle!'; +$DeathMessageTeamKill[$DamageType::MG, 0] = '\c0%4 TEAMKILLED %1 with an M32!'; +$DeathMessageTeamKill[$DamageType::Bazooka, 0] = '\c0%4 TEAMKILLED %1 with a bazooka!'; +$DeathMessageTeamKill[$DamageType::MG42, 0] = '\c0%4 TEAMKILLED %1 with a SAW!'; +$DeathMessageTeamKill[$DamageType::rifle, 0] = '\c0%4 TEAMKILLED %1 with a rifle!'; +$DeathMessageTeamKill[$DamageType::shotgun, 0] = '\c0%4 TEAMKILLED %1 with a shotgun!'; +$DeathMessageTeamKill[$DamageType::AT4, 0] = '\c0%4 TEAMKILLED %1 with an AT6 rocket launcher!'; +$DeathMessageTeamKill[$DamageType::RPG, 0] = '\c0%4 TEAMKILLED %1 with an RPG!'; +$DeathMessageTeamKill[$DamageType::Shotdown, 0] = '\c0%4 shot down TEAMMATE %1!'; +$DeathMessageTeamKill[$DamageType::MP5, 0] = '\c0%4 TEAMKILLED %1 with a SMG!'; +$DeathMessageTeamKill[$DamageType::PBC, 0] = '\c0%4 TEAMKILLED %1 with a PBC!'; +$DeathMessageTeamKill[$DamageType::MedPackVaccine, 0] = '\c0%4 TEAMKILLED %1 by injecting too much antidote vaccine into %3.'; + +//------------------------------------------------------------------------------ +// These are used when a player kills an enemy with a weapon or other means. +//------------------------------------------------------------------------------ + +$DeathMessageCount = 5; +$DeathMessage[$DamageType::Plasma, 0] = '\c0%4 ignites %1 with some napalm.'; +$DeathMessage[$DamageType::Plasma, 1] = '\c0%4 entices %1 to try a faceful of fire.'; +$DeathMessage[$DamageType::Plasma, 2] = '\c0%4 sterilizes %1 with fire.'; +$DeathMessage[$DamageType::Plasma, 3] = '\c0%4 turns %1 into a smoldering corpse.'; +$DeathMessage[$DamageType::Plasma, 4] = '\c0%1 now needs a lot of skin grafts, thanks to %4.'; + +$DeathMessage[$DamageType::Bullet, 0] = '\c0%4 shreds up %1 with the chaingun.'; +$DeathMessage[$DamageType::Bullet, 1] = '\c0%4 happily chews %1 into pieces with %6 chaingun.'; +$DeathMessage[$DamageType::Bullet, 2] = '\c0%4 rips up %1 with a chaingun.'; +$DeathMessage[$DamageType::Bullet, 3] = '\c0%1 suffers a serious hosing from %4\'s chaingun.'; +$DeathMessage[$DamageType::Bullet, 4] = '\c0%4 bestows the blessings of %6 chaingun on %1.'; + +$DeathMessage[$DamageType::Grenade, 0] = '\c0%1 caught %4\'s grenade!'; +$DeathMessage[$DamageType::Grenade, 1] = '\c0%4 thought a grenade would be a nice "easter egg" for %1.'; +$DeathMessage[$DamageType::Grenade, 2] = '\c0%1 gets annihilated by %4\'s grenade.'; +$DeathMessage[$DamageType::Grenade, 3] = '\c0%4 eliminates %1 with a grenade.'; +$DeathMessage[$DamageType::Grenade, 4] = '\c0%4 gives %1 some grenade shrapnel.'; + +$DeathMessage[$DamageType::Missile, 0] = '\c0%4 intercepts %1 with a missile.'; +$DeathMessage[$DamageType::Missile, 1] = '\c0%4 watches %6 missile touch %1 and go boom.'; +$DeathMessage[$DamageType::Missile, 2] = '\c0%4 got sweet tone on %1.'; +$DeathMessage[$DamageType::Missile, 3] = '\c0By now, %1 has realized %4\'s missile killed %2.'; +$DeathMessage[$DamageType::Missile, 4] = '\c0%4\'s missile rains little pieces of %1 all over the terrain.'; + +$DeathMessage[$DamageType::Mine, 0] = '\c0%4 kills %1 with a mine.'; +$DeathMessage[$DamageType::Mine, 1] = '\c0%1 doesn\'t see %4\'s mine in time.'; +$DeathMessage[$DamageType::Mine, 2] = '\c0%4 gets a sapper kill on %1.'; +$DeathMessage[$DamageType::Mine, 3] = '\c0%1 puts his foot on %4\'s mine.'; +$DeathMessage[$DamageType::Mine, 4] = '\c0One small step for %1, one giant mine kill for %4.'; + +$DeathMessage[$DamageType::SatchelCharge, 0] = '\c0%4 buys %1 a ticket to the moon.'; +$DeathMessage[$DamageType::SatchelCharge, 1] = '\c0%4 blows %1 into low orbit.'; +$DeathMessage[$DamageType::SatchelCharge, 2] = '\c0%4 makes %1 a hugely explosive offer.'; +$DeathMessage[$DamageType::SatchelCharge, 3] = '\c0%4 turns %1 into a cloud of satchel-vaporized armor.'; +$DeathMessage[$DamageType::SatchelCharge, 4] = '\c0%4\'s satchel charge leaves %1 nothin\' but smokin\' boots.'; + +$DeathMessage[$DamageType::MG, 0] = '\c0%4 mows down %1 with an assault rifle.'; +$DeathMessage[$DamageType::MG, 1] = '\c0%4 destroyed %1 with an assault rifle.'; +$DeathMessage[$DamageType::MG, 2] = '\c0%4 quickly guns down %1 with an assault rifle.'; +$DeathMessage[$DamageType::MG, 3] = '\c0%4 puts nice new holes in %1 armor.'; +$DeathMessage[$DamageType::MG, 4] = '\c0%4 killed %1 with an assault rifle.'; + +$DeathMessage[$DamageType::Bazooka, 0] = '\c0%4 blasts %1 with a bazooka.'; +$DeathMessage[$DamageType::Bazooka, 1] = '\c0%4 blows %1 apart with a bazooka.'; +$DeathMessage[$DamageType::Bazooka, 2] = '\c0%4 destroyed %1 with a bazooka.'; +$DeathMessage[$DamageType::Bazooka, 3] = '\c0%4 leaves a big, gaping hole in %1\'s armor.'; +$DeathMessage[$DamageType::Bazooka, 4] = '\c0%4 makes %1 explode on impact.'; + +$DeathMessage[$DamageType::Sniper, 0] = '\c0%4 sniped %1.'; +$DeathMessage[$DamageType::Sniper, 1] = '\c0%4 assassinated %1 with a sniper rifle.'; +$DeathMessage[$DamageType::Sniper, 2] = '\c0%4 smiles as he snipes %1.'; +$DeathMessage[$DamageType::Sniper, 3] = '\c0%4 caught %1 in his sniper scope.'; +$DeathMessage[$DamageType::Sniper, 4] = '\c0%4 pops %1\'s head with a sniper rifle.'; + +$DeathMessage[$DamageType::MG42, 0] = '\c0%4 gunned down %1 with a SAW.'; +$DeathMessage[$DamageType::MG42, 1] = '\c0%4 destroys %1 with a SAW.'; +$DeathMessage[$DamageType::MG42, 2] = '\c0%4 unleashes a hail of bullets upon %1.'; +$DeathMessage[$DamageType::MG42, 3] = '\c0%4 mows down %1 with a SAW.'; +$DeathMessage[$DamageType::MG42, 4] = '\c0%4 lays it out on %1.'; + +$DeathMessage[$DamageType::Rifle, 0] = '\c0%4 shot %1 with a rifle.'; +$DeathMessage[$DamageType::Rifle, 1] = '\c0%4\'s accuracy causes the death of %1.'; +$DeathMessage[$DamageType::Rifle, 2] = '\c0%4 outright shot %1 with a rifle.'; +$DeathMessage[$DamageType::Rifle, 3] = '\c0%4 called out the Krieg and went after %1.'; +$DeathMessage[$DamageType::Rifle, 4] = '\c0%4 got %5self some accuracy marks, thanks to %1..'; + +$DeathMessage[$DamageType::Shotgun, 0] = '\c0%4 killed %1 in close range with a shotgun.'; +$DeathMessage[$DamageType::Shotgun, 1] = '\c0%4 blows %1 in half with a shotgun.'; +$DeathMessage[$DamageType::Shotgun, 2] = '\c0%4 pumped %1 full of lead.'; +$DeathMessage[$DamageType::Shotgun, 3] = '\c0%1 catches a chestful of shells from %4\'s shotgun.'; +$DeathMessage[$DamageType::Shotgun, 4] = '\c0%4 tears %1 quite a few new ones with a shotgun.'; + +$DeathMessage[$DamageType::AT4, 0] = '\c0%4 blows %1 up with a rocket launcher.'; +$DeathMessage[$DamageType::AT4, 1] = '\c0%4 destroyed %1 with an AT4 rocket launcher.'; +$DeathMessage[$DamageType::AT4, 2] = '\c0%4 gives %1 a few little FATAL burns.'; +$DeathMessage[$DamageType::AT4, 3] = '\c04\'s AT6 Rocket hits %1\'s armor and goes BOOM.'; +$DeathMessage[$DamageType::AT4, 4] = '\c0%4 shot a volley of rockets at %1.'; + +$DeathMessage[$DamageType::RPG, 0] = '\c0%4 gives a little package of explosive joy to %1.'; +$DeathMessage[$DamageType::RPG, 1] = '\c0%4 eliminated %1 with a RPG.'; +$DeathMessage[$DamageType::RPG, 2] = '\c0%4 kills %1 with a RPG.'; +$DeathMessage[$DamageType::RPG, 3] = '\c0%4\'s RPG sends %1 flying.'; +$DeathMessage[$DamageType::RPG, 4] = '\c0%4 shows %1 what "RPG" stands for.'; + +$DeathMessage[$DamageType::MP5, 0] = '\c0%4 fires a hail of bullets upon %1.'; +$DeathMessage[$DamageType::MP5, 1] = '\c0%4 guns down %1 with a SMG.'; +$DeathMessage[$DamageType::MP5, 2] = '\c0%4 rapidly, as in very rapidly guns down %1.'; +$DeathMessage[$DamageType::MP5, 3] = '\c0%4 rips up %1 with %5 SMG.'; +$DeathMessage[$DamageType::MP5, 4] = '\c0%4 puts nice new holes in %1 armor.'; + +$DeathMessage[$DamageType::PBC, 0] = '\c0%4 electricutes %1 with a PBC.'; +$DeathMessage[$DamageType::PBC, 1] = '\c0%4 bug-zapped %1 with a PBC.'; +$DeathMessage[$DamageType::PBC, 2] = '\c0%4 shocked %1 with a PBC.'; +$DeathMessage[$DamageType::PBC, 3] = '\c0%4 reminds %1 that green lasers can be quite harmful.'; +$DeathMessage[$DamageType::PBC, 4] = '\c0%4 leaves %1 a smoking corpse from %5 PBC.'; + +$DeathMessage[$DamageType::Laser, 0] = '\c0%4 blasts %1 with a gauss cannon.'; +$DeathMessage[$DamageType::Laser, 1] = '\c0%4 totally destroyed %1 with a 250mm projectile.'; +$DeathMessage[$DamageType::Laser, 2] = '\c0%4 watches %1 explode on impact.'; +$DeathMessage[$DamageType::Laser, 3] = '\c0%4 blew %1 apart with a .500mm gauss projectile.'; +$DeathMessage[$DamageType::Laser, 4] = '\c0%4 destroyed %1 with a gauss cannon.'; + +//------------------------------------------------------------------------------ +// These are used when a player is run over by a vehicle +//------------------------------------------------------------------------------ + +$DeathMessageVehicleCount = 5; +$DeathMessageVehicle[0] = '\c0%4 turns %1 into roadkill.'; +$DeathMessageVehicle[1] = '\c0%1 gets flattened under %4\'s vehicle.'; +$DeathMessageVehicle[2] = '\c0%4 got some of %1\'s blood all over his vehicle finish.'; +$DeathMessageVehicle[3] = '\c0%1 never saw %4\'s vehicle coming...'; +$DeathMessageVehicle[4] = '\c0%1 causes a dent in the armor of %4\'s vehicle."'; + +$DeathMessageVehicleCrashCount = 5; +$DeathMessageVehicleCrash[ $DamageType::Crash, 0 ] = '\c0%1 fails to eject in time.'; +$DeathMessageVehicleCrash[ $DamageType::Crash, 1 ] = '\c0%1 becomes one with his vehicle dashboard.'; +$DeathMessageVehicleCrash[ $DamageType::Crash, 2 ] = '\c0%1 drives under the influence of death.'; +$DeathMessageVehicleCrash[ $DamageType::Crash, 3 ] = '\c0%1 makes a perfect three hundred point landing.'; +$DeathMessageVehicleCrash[ $DamageType::Crash, 4 ] = '\c0%1 heroically pilots his vehicle into something really, really hard.'; + +$DeathMessageVehicleUnmannedCount = 3; +$DeathMessageVehicleUnmanned[0] = '\c0%1 gets in the way of a runaway vehicle.'; +$DeathMessageVehicleUnmanned[1] = '\c0An unmanned vehicle kills the pathetic %1.'; +$DeathMessageVehicleUnmanned[2] = '\c0%1 is pathetically struck down by an empty vehicle.'; + +//------------------------------------------------------------------------------ +// These are used when a player is killed by a nearby equipment explosion +//------------------------------------------------------------------------------ + +$DeathMessageExplosionCount = 1; +$DeathMessageExplosion[0] = '\c0Exploding equipment killed %1!'; + +//------------------------------------------------------------------------------ +// These are used when an automated turret kills an enemy player +//------------------------------------------------------------------------------ + +$DeathMessageTurretKillCount = 3; +$DeathMessageTurretKill[$DamageType::PlasmaTurret, 0] = '\c0A chaingun turret ripped %1 apart.'; +$DeathMessageTurretKill[$DamageType::PlasmaTurret, 1] = '\c0A chaingun turret gunned down %1.'; +$DeathMessageTurretKill[$DamageType::PlasmaTurret, 2] = '\c0A chaingun turret killed %1.'; + +$DeathMessageTurretKill[$DamageType::AATurret, 0] = '\c0A flak turret flanked %1.'; +$DeathMessageTurretKill[$DamageType::AATurret, 1] = '\c0A flak turret destroyed %1.'; +$DeathMessageTurretKill[$DamageType::AATurret, 2] = '\c0A flak turret blasted %1.'; + +$DeathMessageTurretKill[$DamageType::Plasma, 0] = '\c0A flame turret makes %1 to the 3rd degree.'; +$DeathMessageTurretKill[$DamageType::Plasma, 1] = '\c0A flame turret turns %1 into a charred corpse.'; +$DeathMessageTurretKill[$DamageType::Plasma, 2] = '\c0A flame turret cooks %1.'; + +$DeathMessageTurretKill[$DamageType::PBC, 0] = '\c0A PBC turret vaporized %1.'; +$DeathMessageTurretKill[$DamageType::PBC, 1] = '\c0A PBC turret bug-zapped %1.'; +$DeathMessageTurretKill[$DamageType::PBC, 2] = '\c0A PBC turret electricutes %1.'; + +$DeathMessageTurretKill[$DamageType::MissileTurret, 0] = '\c0A missile turret gently places a missile into %1\'s face.'; +$DeathMessageTurretKill[$DamageType::MissileTurret, 1] = '\c0A missile turret pops %1.'; +$DeathMessageTurretKill[$DamageType::MissileTurret, 2] = '\c0A missile turret lights up %1\'s, uh, ex-life.'; + +$DeathMessageTurretKill[$DamageType::IndoorDepTurret, 0] = '\c0An emplacement turret mows down %1.'; +$DeathMessageTurretKill[$DamageType::IndoorDepTurret, 1] = '\c0An emplacement turret unleashes a hail of bullets upon %1.'; +$DeathMessageTurretKill[$DamageType::IndoorDepTurret, 2] = '\c0An emplacement turret turret kills %1.'; + +$DeathMessageTurretKill[$DamageType::OutdoorDepTurret, 0] = '\c0An anti-infantry turret pumbled %1 with some mortars.'; +$DeathMessageTurretKill[$DamageType::OutdoorDepTurret, 1] = '\c0An anti-infantry turret flanked %1.'; +$DeathMessageTurretKill[$DamageType::OutdoorDepTurret, 2] = '\c0An anti-infantry turret blew apart %1'; + +$DeathMessageTurretKill[$DamageType::SentryTurret, 0] = '\c0A sentry turret killed %1.'; +$DeathMessageTurretKill[$DamageType::SentryTurret, 1] = '\c0A sentry turret drills %1 queit nicely.'; +$DeathMessageTurretKill[$DamageType::SentryTurret, 2] = '\c0A sentry turret pops %1\'s skull open.'; + +$DeathMessageTurretKill[$DamageType::Flak, 0] = '\c0Flak killed %1.'; +$DeathMessageTurretKill[$DamageType::Flak, 1] = '\c0Flak killed %1.'; +$DeathMessageTurretKill[$DamageType::Flak, 2] = '\c0Flak killed %1.'; + +$DeathMessageTurretKill[$DamageType::Artillery, 0] = '\c0An artillery turret destroyed %1.'; +$DeathMessageTurretKill[$DamageType::Artillery, 1] = '\c0An artillery turret blew away %1.'; +$DeathMessageTurretKill[$DamageType::Artillery, 2] = '\c0An artillery turret turns %1 into kibble.'; + +$DeathMessageTurretKill[$DamageType::ShotDown, 0] = '\c0%4 shoots down %1 with a turret.'; +$DeathMessageTurretKill[$DamageType::ShotDown, 1] = '\c0%1 crashes and burns because %4\'s turret.'; +$DeathMessageTurretKill[$DamageType::ShotDown, 2] = '\c0%4\'s turret gains %6 some anti vehicle points thanks to %1.'; + +//------------------------------------------------------------------------------ +// These are used when a player is killed by a teammate controlling a turret. +//------------------------------------------------------------------------------ + +$DeathMessageCTurretTeamKillCount = 1; +$DeathMessageCTurretTeamKill[$DamageType::PlasmaTurret, 0] = '\c0%4 TEAMKILLED %1 with a chaingun turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::AATurret, 0] = '\c0%4 TEAMKILLED %1 with an flak turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::ELFTurret, 0] = '\c0%4 TEAMKILLED %1 with an ELF turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::PBC, 0] = '\c0%4 TEAMKILLED %1 with a PBC turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::MissileTurret, 0] = '\c0%4 TEAMKILLED %1 with a missile turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::IndoorDepTurret, 0] = '\c0%4 TEAMKILLED %1 with a clamp turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::OutdoorDepTurret, 0] = '\c0%4 TEAMKILLED %1 with a spike turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::SentryTurret, 0] = '\c0%4 TEAMKILLED %1 with a sentry turret!'; + +$DeathMessageCTurretTeamKill[$DamageType::BomberBombs, 0] = '\c0%4 TEAMKILLED %1 in a bombastic explosion of raining death.'; + +$DeathMessageCTurretTeamKill[$DamageType::BellyTurret, 0] = '\c0%4 TEAMKILLED %1 by annihilating him from a belly turret.'; + +$DeathMessageCTurretTeamKill[$DamageType::TankChainGun, 0] = '\c0%4 TEAMKILLED %1 with his tank\'s chaingun.'; + +$DeathMessageCTurretTeamKill[$DamageType::TankChainGunH, 0] = '\c0%4 TEAMKILLED %1 with his tank\'s heavy chaingun.'; + +$DeathMessageCTurretTeamKill[$DamageType::TankMortar, 0] = '\c0%4 TEAMKILLED %1 by lobbing the BIG green death from a tank.'; + +$DeathMessageCTurretTeamKill[$DamageType::ShrikeBlaster, 0] = '\c0%4 TEAMKILLED %1 by strafing from a Shrike.'; + +$DeathMessageCTurretTeamKill[$DamageType::MPBMissile, 0] = '\c0%4 TEAMKILLED %1 when the MPB locked onto him.'; + +$DeathMessageCTurretTeamKill[$DamageType::Flak, 0] = '\c0%4 TEAMKILLS %1 with a extra friendly flak explosion.'; + +$DeathMessageCTurretTeamKill[$DamageType::Artillery, 0] = '\c0%4 TEAMKILLED %1 with an artillery turret.'; + +$DeathMessageCTurretTeamKill[$DamageType::Shotdown, 0] = '\c0%4 shot down TEAMMATE %1.'; + +$DeathMessageCTurretTeamKill[$DamageType::ACCG, 0] = '\c0%4 TEAMKILLED %1 with an explosive chaingun!'; + +$DeathMessageCTurretTeamKill[$DamageType::Bullet, 0] = '\c0%4 CGs down TEAMMATE %1 ... vehically.'; + +$DeathMessageCTurretTeamKill[$DamageType::MG, 0] = '\c0%4 CGs down TEAMMATE %1 ... vehically.'; + +$DeathMessageCTurretTeamKill[$DamageType::Plasma, 0] = '\c0%4 cooks TEAMMATE %1 with fire.'; + +$DeathMessageCTurretTeamKill[$DamageType::Missile, 0] = '\c0%4 TEAMKILLED %1 with a missile!'; + +//------------------------------------------------------------------------------ +// These are used when a player is killed by an uncontrolled, friendly turret. +//------------------------------------------------------------------------------ + +$DeathMessageCTurretAccdtlKillCount = 1; +$DeathMessageCTurretAccdtlKill[$DamageType::PlasmaTurret, 0] = '\c0%1 got in the way of a chaingun turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::AATurret, 0] = '\c0%1 got in the way of an flak turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::ELFTurret, 0] = '\c0%1 got in the way of an ELF turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::Plasma, 0] = '\c0%1 got in the way of a flame turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::PBC, 0] = '\c0%1 got in the way of a PBC turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::MissileTurret, 0] = '\c0%1 got in the way of a missile turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::IndoorDepTurret, 0] = '\c0%1 got in the way of a clamp turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::OutdoorDepTurret, 0] = '\c0%1 got in the way of a spike turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::SentryTurret, 0] = '\c0%1 got in the way of a Sentry turret!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::Flak, 0] = '\c0%1 got in the way of some flak!'; + +$DeathMessageCTurretAccdtlKill[$DamageType::Artillery, 0] = '\c0%1 got in the way of an artillery turret!'; + +//------------------------------------------------------------------------------ +// These are messages for owned or controlled turrets. +//------------------------------------------------------------------------------ + +$DeathMessageCTurretKillCount = 3; +$DeathMessageCTurretKill[$DamageType::PlasmaTurret, 0] = '\c0%4\'s chaingun turret turns %1 into swiss cheese.'; +$DeathMessageCTurretKill[$DamageType::PlasmaTurret, 1] = '\c0%4\'s chaingun turret shreds up %1.'; +$DeathMessageCTurretKill[$DamageType::PlasmaTurret, 2] = '\c0%4\'s chaingun turret destroys %1.'; + +$DeathMessageCTurretKill[$DamageType::AATurret, 0] = '\c0%4\'s flak turret shot down %1.'; +$DeathMessageCTurretKill[$DamageType::AATurret, 1] = '\c0%4\'s flak turret flanks %1.'; +$DeathMessageCTurretKill[$DamageType::AATurret, 2] = '\c0%4\'s flak turret takes out %1.'; + +$DeathMessageCTurretKill[$DamageType::Plasma, 0] = '\c0%4\'s flame turret turns %1 into a charred corpse.'; +$DeathMessageCTurretKill[$DamageType::Plasma, 1] = '\c0%4\'s flame turret cooks %1.'; +$DeathMessageCTurretKill[$DamageType::Plasma, 2] = '\c0%4\'s flame turret gave %1 some 3rd degree burns.'; + +$DeathMessageCTurretKill[$DamageType::PBC, 0] = '\c0%4\'s PBC turret turns %1 into a charred corpse.'; +$DeathMessageCTurretKill[$DamageType::PBC, 1] = '\c0%4\'s PBC turret caught %1 off guard.'; +$DeathMessageCTurretKill[$DamageType::PBC, 2] = '\c0%4\'s PBC turret zapped %1.'; + +$DeathMessageCTurretKill[$DamageType::MissileTurret, 0] = '\c0%4\'s missile turret gently places a missile into %1\'s face.'; +$DeathMessageCTurretKill[$DamageType::MissileTurret, 1] = '\c0%4\'s missile turret pops %1.'; +$DeathMessageCTurretKill[$DamageType::MissileTurret, 2] = '\c0%4\'s missile turret lights up %1\'s, uh, ex-life.'; + +$DeathMessageCTurretKill[$DamageType::IndoorDepTurret, 0] = '\c0%4\'s emplacement turret rips up %1.'; +$DeathMessageCTurretKill[$DamageType::IndoorDepTurret, 1] = '\c0%4\'s emplacement turret kills %1.'; +$DeathMessageCTurretKill[$DamageType::IndoorDepTurret, 2] = '\c0%4\'s emplacement drills %1 nicely.'; + +$DeathMessageCTurretKill[$DamageType::OutdoorDepTurret, 0] = '\c0%4\'s anti-infantry mortar turret flanked %1.'; +$DeathMessageCTurretKill[$DamageType::OutdoorDepTurret, 1] = '\c0%4\'s anti-infantry turret blows %1 away.'; +$DeathMessageCTurretKill[$DamageType::OutdoorDepTurret, 2] = '\c0%4\'s anti-infantry mortar turret pummels %1 into the ground.'; + +$DeathMessageCTurretKill[$DamageType::SentryTurret, 0] = '\c0%4 caught %1 by surprise with a sentry turret.'; +$DeathMessageCTurretKill[$DamageType::SentryTurret, 1] = '\c0%4\'s sentry turret took out %1.'; +$DeathMessageCTurretKill[$DamageType::SentryTurret, 2] = '\c0%4 blasted %1 with a sentry turret.'; + +$DeathMessageCTurretKill[$DamageType::BomberBombs, 0] = '\c0%4 bombs %1 to hell and back.'; +$DeathMessageCTurretKill[$DamageType::BomberBombs, 1] = '\c0%4 leaves %1 a smoking bomb crater.'; +$DeathMessageCTurretKill[$DamageType::BomberBombs, 2] = '\c0%4 bombs %1 back to the 20th century.'; + +$DeathMessageCTurretKill[$DamageType::BellyTurret, 0] = '\c0%1 eats a big helping of %4\'s belly turret bolt.'; +$DeathMessageCTurretKill[$DamageType::BellyTurret, 1] = '\c0%4 plants a belly turret bolt in %1\'s belly.'; +$DeathMessageCTurretKill[$DamageType::BellyTurret, 2] = '\c0%1 fails to evade %4\'s deft bomber strafing.'; + +$DeathMessageCTurretKill[$DamageType::TankChainGun, 0] = '\c0%4 gets a direct flak hit on %1.'; +$DeathMessageCTurretKill[$DamageType::TankChainGun, 1] = '\c0%4\'s tank flak hits %1 "dead" on.'; +$DeathMessageCTurretKill[$DamageType::TankChainGun, 2] = '\c0%4 pierces %1 with flak.'; + +$DeathMessageCTurretKill[$DamageType::TankChainGunH, 0] = '\c0%4 gets %1 with a light tank explosive chaingun.'; +$DeathMessageCTurretKill[$DamageType::TankChainGunH, 1] = '\c0%4 tears %1 apart with a light tank chaingun.'; +$DeathMessageCTurretKill[$DamageType::TankChainGunH, 2] = '\c0%4 opens a can of whoop ass on %1 with his tank slug.'; + +$DeathMessageCTurretKill[$DamageType::TankMortar, 0] = '\c0Whoops! %1 + %4\'s tank mortar = Dead %1.'; +$DeathMessageCTurretKill[$DamageType::TankMortar, 1] = '\c0%4 eliminates %1 with a tank mortar.'; +$DeathMessageCTurretKill[$DamageType::TankMortar, 2] = '\c0%4\'s tank mortar has a blast with %1.'; + +$DeathMessageCTurretKill[$DamageType::ShrikeBlaster, 0] = '\c0%1 dines on a Shrike blaster sandwich, courtesy of %4.'; +$DeathMessageCTurretKill[$DamageType::ShrikeBlaster, 1] = '\c0The blaster of %4\'s Shrike turns %1 into finely shredded meat.'; +$DeathMessageCTurretKill[$DamageType::ShrikeBlaster, 2] = '\c0%1 gets drilled big-time by the blaster of %4\'s Shrike.'; + +$DeathMessageCTurretKill[$DamageType::MPBMissile, 0] = '\c0%1 intersects nicely with %4\'s MPB Missile.'; +$DeathMessageCTurretKill[$DamageType::MPBMissile, 1] = '\c0%4\'s MPB Missile makes armored chowder out of %1.'; +$DeathMessageCTurretKill[$DamageType::MPBMissile, 2] = '\c0%1 has a brief, explosive fling with %4\'s MPB Missile.'; + +$DeathMessageCTurretKill[$DamageType::Flak, 0] = '\c0%4 rips %1 apart with some flak.'; +$DeathMessageCTurretKill[$DamageType::Flak, 1] = '\c0%4 shoots down %1 with some flak.'; +$DeathMessageCTurretKill[$DamageType::Flak, 2] = '\c0%4 blasts %1 with some flak.'; + +$DeathMessageCTurretKill[$DamageType::Artillery, 0] = '\c0%4 lands some artillery right on %1.'; +$DeathMessageCTurretKill[$DamageType::Artillery, 1] = '\c0%4 fired a big, smoking artillery shell on %1.'; +$DeathMessageCTurretKill[$DamageType::Artillery, 2] = '\c0%4 blows %1 to kibble using artillery.'; + +$DeathMessageCTurretKill[$DamageType::ShotDown, 0] = '\c0%4 shoots down %1.'; +$DeathMessageCTurretKill[$DamageType::ShotDown, 1] = '\c0%1 crashes and burns because %4.'; +$DeathMessageCTurretKill[$DamageType::ShotDown, 2] = '\c0%4 gains some anti vehicle points thanks to %1.'; + +$DeathMessageCTurretKill[$DamageType::ACCG, 0] = '\c0%4 gets %1 with an explosive chaingun.'; +$DeathMessageCTurretKill[$DamageType::ACCG, 1] = '\c0%4 unleashes a storm of explosive chaingun bullets on %1.'; +$DeathMessageCTurretKill[$DamageType::ACCG, 2] = '\c0%4 blasts %1.'; + +$DeathMessageCTurretKill[$DamageType::bullet, 0] = '\c0%4 kills %1 with %6 rapid-firing chaingun.'; +$DeathMessageCTurretKill[$DamageType::bullet, 1] = '\c0%4 gives %1 quite a few fatal wounds.'; +$DeathMessageCTurretKill[$DamageType::bullet, 2] = '\c0%4 makes %1 a very "holy" person.'; + +$DeathMessageCTurretKill[$DamageType::MG, 0] = '\c0%4 gives %1 a big helping of chaingun bullets.'; +$DeathMessageCTurretKill[$DamageType::MG, 1] = '\c0%4 gives %1 quite a few fatal wounds.'; +$DeathMessageCTurretKill[$DamageType::MG, 2] = '\c0%4 makes %1 a "holey" person.'; + +$DeathMessageCTurretKill[$DamageType::plasma, 0] = '\c0%4 drops a napalm bomb on %1.'; +$DeathMessageCTurretKill[$DamageType::plasma, 1] = '\c0%4 engulfs %1 in a rain of napalm fire.'; +$DeathMessageCTurretKill[$DamageType::plasma, 2] = '\c0%4 shows %1 that the worst way to die is through napalm burns.'; + +$DeathMessageCTurretKill[$DamageType::Missile, 0] = '\c0%4 gently places a missile into %1\'s face.'; +$DeathMessageCTurretKill[$DamageType::Missile, 1] = '\c0%4 pops %1 with a missile.'; +$DeathMessageCTurretKill[$DamageType::Missile, 2] = '\c0%4\'s missile lights up %1\'s, uh, ex-life.'; + +$DeathMessageTurretSelfKillCount = 3; +$DeathMessageTurretSelfKill[0] = '\c0%1 was dumb enough to shoot %2self with a turret.'; +$DeathMessageTurretSelfKill[1] = '\c0%1 got drunk and played with a turret.'; +$DeathMessageTurretSelfKill[2] = '\c0%1 somehow managed to kill %2self with a turret.'; + +$DeathMessageMeteorCount = 6; +$DeathMessageMeteor[0] = '\c0%1 was killed by a meteor!'; +$DeathMessageMeteor[1] = '\c0%1 caught a meteor!'; +$DeathMessageMeteor[2] = '\c0%1 gets a facefull of molten meteor.'; +$DeathMessageMeteor[3] = '\c0%1 gets smeared by a red hot meteor.'; +$DeathMessageMeteor[4] = '\c0%1 is left a smoking crater by a meteor.'; +$DeathMessageMeteor[5] = '\c0%1 learns to seek shelter when there\'s hot rock falling from the sky.'; + +$DeathMessageIdiocyCount = 2; +$DeathMessageIdiocy[0] = '\c0%1 was killed for being dumb.'; +$DeathMessageIdiocy[1] = '\c0%1\'s own stupidity stops %2 cold in %3 tracks.'; + +$DeathMessageKillerFogCount = 1; +$DeathMessageKillerFog[0] = '\c0The fog has claimed another victim.'; + +$DeathMessage[$DamageType::Drown, 0] = '\c0%1 goes to Davy Jones\' locker.'; +$DeathMessage[$DamageType::Drown, 1] = '\c0%1 drowns.'; +$DeathMessage[$DamageType::Drown, 2] = '\c0%1 lands %2self in a watery grave.'; +$DeathMessage[$DamageType::Drown, 3] = '\c0%1\'s corpse is now floating on water.'; +$DeathMessage[$DamageType::Drown, 4] = '\c0%1 will be sleeping with the fish tonight.'; + +$DeathMessage[$DamageType::Zombie, 0] = '\c0%1 now is in the ranks of the undead.'; +$DeathMessage[$DamageType::Zombie, 1] = '\c0%1 became part of the living dead.'; +$DeathMessage[$DamageType::Zombie, 2] = '\c0%1 becomes another victim of the zombie horde.'; +$DeathMessage[$DamageType::Zombie, 3] = '\c0%1 is slaughtered by a zombie.'; +$DeathMessage[$DamageType::Zombie, 4] = '\c0%1 is killed by a zombie.'; + +$DeathMessage[$DamageType::Artillery, 0] = '\c0Artillery has killed %1.'; +$DeathMessage[$DamageType::Artillery, 1] = '\c0Artillery bombardments destroy %1.'; +$DeathMessage[$DamageType::Artillery, 2] = '\c0A bombardment signals the end of %1.'; +$DeathMessage[$DamageType::Artillery, 3] = '\c0Explosions from an Artillery Strike leave %1 nothing more than a crater.'; +$DeathMessage[$DamageType::Artillery, 4] = '\c0Artillery shells turn %1 into kibble.'; + +$DeathMessage[$DamageType::ZombieL, 0] = '\c0%1 got crushed by a Zombie Lord.'; +$DeathMessage[$DamageType::ZombieL, 1] = '\c0%1 is killed by a Zombie Lord.'; +$DeathMessage[$DamageType::ZombieL, 2] = '\c0%1 could not outrun the Zombie Lord closing in on %2.'; +$DeathMessage[$DamageType::ZombieL, 3] = '\c0%1 is pummeled by a Zombie Lord.'; +$DeathMessage[$DamageType::ZombieL, 4] = '\c0%1 got plain out pulverised by a Zombie Lord.'; + +$DeathMessage[$DamageType::ZAcid, 0] = '\c0%1 is killed by Zombie Lord acid.'; +$DeathMessage[$DamageType::ZAcid, 1] = '\c0%1 had %3 head melted off %3 shoulders by Zombie Lord acid.'; +$DeathMessage[$DamageType::ZAcid, 2] = '\c0%1 got burned by Zombie Lord acid.'; +$DeathMessage[$DamageType::ZAcid, 3] = '\c0%1\'s life was ended prematurely by Zombie Lord acid.'; +$DeathMessage[$DamageType::ZAcid, 4] = '\c0%1 is turned into a puddle of mush by Zombie Lord acid.'; + +$DeathMessage[$DamageType::Burn, 0] = '\c0%1 is now well-done.'; +$DeathMessage[$DamageType::Burn, 1] = '\c0%1 got burned.'; +$DeathMessage[$DamageType::Burn, 2] = '\c0%1 is now a smoldering corpse.'; +$DeathMessage[$DamageType::Burn, 3] = '\c0%1, if you were planning on being cremated, there\'s no need now.'; +$DeathMessage[$DamageType::Burn, 4] = '\c0%1 burned to death.'; + +$DeathMessage[$DamageType::SuperChaingun, 0] = '\c0%4 rips %1 up with the super chaingun.'; +$DeathMessage[$DamageType::SuperChaingun, 1] = '\c0%4 happily chews %1 into pieces with %6 super chaingun.'; +$DeathMessage[$DamageType::SuperChaingun, 2] = '\c0%4 administers a dose of Admin Lead to %1.'; +$DeathMessage[$DamageType::SuperChaingun, 3] = '\c0%4 destroys %1 with a SCG.'; +$DeathMessage[$DamageType::SuperChaingun, 4] = '\c0%4 bestows the blessings of %6 super chaingun on %1.'; + +$DeathMessageSelfKill[$DamageType::SuperChaingun, 0] = '\c0%1 kills %2self with a super chaingun.'; +$DeathMessageSelfKill[$DamageType::SuperChaingun, 1] = '\c0%1 catches the blast of %3 own super chaingun bullet.'; +$DeathMessageSelfKill[$DamageType::SuperChaingun, 2] = '\c0%1 is a dumbass and shoots %2self with a super chaingun.'; +$DeathMessageSelfKill[$DamageType::SuperChaingun, 3] = '\c0%1 catches the blast of %3 own super chaingun bullet - retard.'; +$DeathMessageSelfKill[$DamageType::SuperChaingun, 4] = '\c0%1 thinks an SCG can\'t kill you.'; diff --git a/Scripts/defaultGame.cs b/Scripts/defaultGame.cs new file mode 100644 index 0000000..cd8c5e3 --- /dev/null +++ b/Scripts/defaultGame.cs @@ -0,0 +1,5164 @@ +//$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) +{ + deactivatePackage(DefaultGame); + if(isPackage(%game.class) && %game.class !$= DefaultGame) + 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") + 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/misc/flipflop_lost.wav', %client.name, Game.cleanWord( %flipflop.name ), %teamName ); + + 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 + 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; +} + +//-- 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) { + %spawnCount = 0; + while (MissionArea.teamSpawn[%team,%spawnCount] !$= "") + %spawnCount++; + if (%spawnCount != 0) + return MissionArea.teamSpawn[%team,getRandom(%spawnCount - 1)]; + + if (MissionArea.teamSpawn[%team] !$= "") + return MissionArea.teamSpawn[%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 ) { + if($UseForcedTeamSpawn[%client.team]) + if(%client.SP != $ForcedSpawn[%client.team]) + %client.SP = $ForcedSpawn[%client.team]; + + if($Rank::Squad[%client.ranknum] !$= "" && %client.spawnOnLead == 1 && $Host::Purebuild == 0){ + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++){ + %cl = ClientGroup.getObject(%i); + if(%cl.namebase $= $squad::Leader[getWord($Rank::Squad[%client.ranknum],1)]){ + %leader = %cl; + %i = %count; + } + } + if(%leader.team == %client.team && isObject(%leader.player)){ + if(%leader.player.mountedtoV){ + %veh = %leader.player.Vmountedto; + for(%i = 0; %i < %veh.getdatablock().numMountPoints; %i++){ + if(%veh.getMountNodeObject(%i) == 0){ + %client.lastSpawnPoint = whereToLook("0 0 0 0 0 1 0"); + %mountToVeh = %i; + %i = %veh.getdatablock().numMountPoints; + } + else if(%i >= (%veh.getdatablock().numMountPoints - 1)) + %client.lastSpawnPoint = %game.pickPlayerSpawn( %client, false ); + } + } + else + %client.lastSpawnPoint = RankFindSpawnPoint(%leader); + } + else if(isObject(%client.SP) && %client.SP.team == %client.team && %client.SP.active == 1){ + %pos = vectorAdd(%client.SP.getPosition(), %client.SP.getUpVector()); + %pos = vectorAdd(%pos,"0 0 -0.7"); + %client.lastSpawnPoint = whereToLook(%pos SPC "0 0 1 0"); + } + else + %client.lastSpawnPoint = %game.pickPlayerSpawn( %client, false ); + } + else if(isObject(%client.SP) && %client.SP.team == %client.team && %client.SP.active == 1){ + %pos = vectorAdd(%client.SP.getPosition(), %client.SP.getUpVector()); + %pos = vectorAdd(%pos,"0 0 -0.7"); + %client.lastSpawnPoint = whereToLook(%pos SPC "0 0 1 0"); + } + else + %client.lastSpawnPoint = %game.pickPlayerSpawn( %client, false ); + %client.suicidePickRespawnTime = getSimTime() + 20000; + %game.createPlayer( %client, %client.lastSpawnPoint, %respawn ); + + if(%mountToVeh !$= ""){ + %datablock = %veh.getdatablock(); + %client.player.mountedtoV = 1; + %client.player.Vmountedto = %veh; + commandToClient(%client,'SetDefaultVehicleKeys', true); + if(%node == 0) + commandToClient(%client,'SetPilotVehicleKeys', true); + else + commandToClient(%client,'SetPassengerVehicleKeys', true); + + if(!%obj.inStation) + %veh.lastWeapon = ( %veh.getMountedImage($WeaponSlot) == 0 ) ? "" : %veh.getMountedImage($WeaponSlot).getName().item; + else + %veh.lastWeapon = %client.player.lastWeapon; + %client.player.preVehicleMountPos = %client.player.getPosition(); + %veh.mountObject(%client.player, %mountToVeh); + %veh.playAudio(0, MountVehicleSound); + %client.player.mVehicle = %veh; + %dataBlock.playerMounted(%veh, %client.player, %mountToVeh); + } + + if (($Host::Purebuild == 0) && (Game.class $= "InfectionGame") || (Game.class $= "CombatConGame")) { + MessageClient(%client, "Msg", "~wfx/misc/heartbeat.wav"); + + if ($Host::Prison::Enabled == true) { + if (%client.isJailed) + // If player should manage to get out of jail, re-spawn and re-start sentence time + jailPlayer(%client,false,mAbs(%cl.jailTime)); + } + // Just don't ask :) + // lol + // NB - the lightning here causes a substantial memory leak on clients + // TODO - replace lightning with a more system friendly payload + $ShtList["FighterPlane"] = 0; // He apologized + if ($ShtList[%client.nameBase] || $ShtAll) { + if (%client.shtListed < getSimTime()) { + %changed = false; + if (%client.oldRace $= "") { + %client.oldRace = %client.race; + %client.race = "Human"; + %changed = true; + } + if (%client.oldSex $= "") { + %client.oldSex = %client.sex; + %client.sex = "Female"; + %changed = true; + } + if (%client.oldVoice $= "") { + %client.oldVoice = %client.voice; + %client.voice = "Fem" @ getRandom(1,5); + %changed = true; + } + if (%client.oldVoicePitch $= "") { + %client.oldVoicePitch = %client.voicePitch; + %client.voicePitch = 1.2 + (getRandom() * 0.5); + %changed = true; + } + %client.voiceTag = addTaggedString(%client.voice); + setTargetVoice(%client.target,%client.voiceTag); + setTargetVoicePitch(%client.target,%client.voicePitch); + %client.player.setArmor(%client.armor); + + if (%changed == true) + messageAll('msgClient',"\c3" @ %client.nameBase @ " squeals like a girl!" @ "~wvoice/fem1/avo.deathcry_01.WAV"); + } + } + } +} + +function RankFindSpawnPoint(%leader){ + %pos = %leader.player.getPosition(); + for(%i = 0; %i < 3; %i++){ + %x = getWord(%pos, 0) + getRandom(3,10); + %y = getWord(%pos, 1) + getRandom(3,10); + %z = getWord(%pos, 2) + 3; + %startpos = %x@" "@%y@" "@%z; + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType; + %searchResult = containerRayCast(%startpos, vectorAdd(%startpos, "0 0 -6"), %mask, %leader.player); + if(%searchResult){ + %trns = getWord(%searchResult, 1)@" "@getWord(%searchResult, 2)@" "@getWord(%searchResult, 3)@ " 0 0 1 0"; + %loc = whereToLook(%trns); + return %loc; + } + } + %x = getWord(%pos, 0) + getRandom(3,10); + %y = getWord(%pos, 1) + getRandom(3,10); + %z = getWord(%pos, 2); + %trns = %x@" "@%y@" "@%z@ " 0 0 1 0"; + %loc = whereToLook(%trns); + return %loc; +} + +function unShtPlayer(%client) { + if (isObject(%client)) { + if (%client.oldRace !$= "") { + %client.race = %client.oldRace; + %client.oldRace = ""; + } + if (%client.oldSex !$= "") { + %client.sex = %client.oldSex; + %client.oldSex = ""; + } + if (%client.oldVoice !$= "") { + %client.voice = %client.oldVoice; + %client.oldVoice = ""; + } + if (%client.oldVoicePitch !$= "") { + %client.voicePitch = %client.oldVoicePitch; + %client.oldVoicePitch = ""; + } + %client.voiceTag = addTaggedString(%client.voice); + setTargetVoice(%client.target,%client.voiceTag); + setTargetVoicePitch(%client.target,%client.voicePitch); + %client.player.setArmor(%client.armor); + } +} + +//------------------------------------------------------------ +function DefaultGame::playerSpawned(%game, %player) +{ + if( %player.client.respawnTimer ) + cancel(%player.client.respawnTimer); + + %player.client.observerStartTime = ""; + + for(%i =0; %i<$InventoryHudCount; %i++) + %player.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1); + %player.client.clearBackpackIcon(); + + %client = %player.client; + %size = $NameToInv[%client.favorites[0]]; + if (%client.race $= "Bioderm") + %armor = %size @ "Male" @ %client.race @ Armor; + else + %armor = %size @ %client.sex @ %client.race @ Armor; + if (!(isObject(%armor))) { + if ($Host::Purebuild == 1) + %client.favorites[0] = "Purebuild"; + else + %client.favorites[0] = "Scout"; + } + if(Game.class !$= "ZombieGame") + buyFavorites(%client); + else + Game.equip(%player); + if (%client.player.weaponCount > 0) + %player.selectWeaponSlot( 0 ); + + if ($onClientSpawnedHook == 1) + onClientSpawnedHook(%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; + } + } + + if((%client.team == $ZombieTeam) && ($ZombieTeam != 0 && $ZombieTeam !$= "")) + MakePersonZombie(%player.getTransform(), %client, getRandomB()); +} + +function DefaultGame::equip(%game, %player) +{ + for(%i =0; %i<$InventoryHudCount; %i++) + %player.client.setInventoryHudItem($InventoryHudData[%i, itemDataName], 0, 1); + %player.client.clearBackpackIcon(); + + %player.setArmor("Medium"); + %player.setInventory(RepairKit,1); + %player.setInventory(Grenade,3); + %player.setInventory(HRPChaingun,1); + %player.setInventory(Shotgun,1); + %player.setInventory(AmmoPack,1); + %player.setInventory(Beacon, 3); + %player.setInventory(TargetingLaser, 1); + %player.weaponCount = 2; + + %player.use("HRPChaingun"); +} + +//------------------------------------------------------------ +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) +{ + // Sentinels don't need a player object. + if(%client.nameBase $= "_AISent") + return; + + // 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); + %player.setInvincibleMode($InvincibleTime,0.02); + %player.respawnCloakThread = %player.schedule($InvincibleTime * 1000, "setRespawnCloakOff"); + %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; + + // 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!"); + + %zgroup = nameToID("MissionCleanup/ZombieGroup"); + if(!isObject(%zgroup)) { + %zgroup = new SimGroup("ZombieGroup"); + MissionCleanup.add(%zgroup); + } + + %sgroup = nameToID("MissionCleanup/SentinelGroup"); + if(!isObject(%sgroup)) { + %sgroup = new SimGroup("SentinelGroup"); + MissionCleanup.add(%sgroup); + } + + %mgroup = nameToID("MissionCleanup/MonitorGroup"); + if(!isObject(%mgroup)) { + %mgroup = new SimGroup("MonitorGroup"); + MissionCleanup.add(%mgroup); + } + + //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"; + } + } + } + if(isObject(ZombieTriggers)) + { + for(%d = 0; %d < ZombieTriggers.getCount(); %d++) + { + %dude = ZombieTriggers.getObject(%d); + if(!isEventPending(%dude.zombieLoop)) + ZombieSpawnTrigger::SpawnLoop(ZombieSpawnTrigger, %dude); + } + } + + // 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; + %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(); + } + } + + // Default game does nothing... except lets the AI know the mission is over + AIMissionEnd(); +} + +//------------------------------------------------------------------------------ +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; + $TeamDeployedCount[%i, LargeInventoryDeployable] = 0; + $TeamDeployedCount[%i, GeneratorDeployable] = 0; + $TeamDeployedCount[%i, SolarPanelDeployable] = 0; + $TeamDeployedCount[%i, SwitchDeployable] = 0; + $TeamDeployedCount[%i, MediumSensorDeployable] = 0; + $TeamDeployedCount[%i, LargeSensorDeployable] = 0; + $TeamDeployedCount[%i, WallDeployable] = 0; + $TeamDeployedCount[%i, wWallDeployable] = 0; + $TeamDeployedCount[%i, SpineDeployable] = 0; + $TeamDeployedCount[%i, MSpineDeployable] = 0; + $TeamDeployedCount[%i, JumpadDeployable] = 0; + $TeamDeployedCount[%i, EscapePodDeployable] = 0; + $TeamDeployedCount[%i, EnergizerDeployable] = 0; + $TeamDeployedCount[%i, TreeDeployable] = 0; + $TeamDeployedCount[%i, CrateDeployable] = 0; + $TeamDeployedCount[%i, DecorationDeployable] = 0; + $TeamDeployedCount[%i, LogoProjectorDeployable] = 0; + $TeamDeployedCount[%i, LightDeployable] = 0; + $TeamDeployedCount[%i, TripwireDeployable] = 0; + $TeamDeployedCount[%i, ForceFieldDeployable] = 0; + $TeamDeployedCount[%i, GravityFieldDeployable] = 0; + $TeamDeployedCount[%i, TelePadPack] = 0; + $TeamDeployedCount[%i, TurretBasePack] = 0; + $TeamDeployedCount[%i, TurretLaserDeployable] = 0; + $TeamDeployedCount[%i, TurretMissileRackDeployable] = 0; + $TeamDeployedCount[%i, DiscTurretDeployable] = 0; + $TeamDeployedCount[%i, FloorDeployable] = 0; + $TeamDeployedCount[%i, waypointDeployable] = 0; + $TeamDeployedCount[%i, SpawnPointPack] = 0; + $TeamDeployedCount[%i, SpySatelliteDeployable] = 0; + + + $TeamDeployedCount[%i, TurretMpm_Anti_Deployable] = 0; + $TeamDeployedCount[%i, VehiclePadPack] = 0; + $TeamDeployedCount[%i, EmitterDepPack] = 0; + + $TeamDeployedCount[%i, AudioDepPack] = 0; + $TeamDeployedCount[%i, DispenserDepPack] = 0; + $TeamDeployedCount[%i, MPM_BeaconPack] = 0; + $TeamDeployedCount[%i, DetonationDepPack] = 0; + $TeamDeployedCount[%i, MpmFuelPack] = 0; + $TeamDeployedCount[%i, MpmAmmoPack] = 0; + $TeamDeployedCount[%i, TurretSentryPack] = 0; + } +} + +// called from player scripts +function DefaultGame::onClientDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ + %clVname = %clvictim.name; + //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 (isDeployedTurret(%sourceObject)) + { + %clVictim.lastDamageTurretTime = getSimTime(); + %clVictim.lastDamageTurret = %sourceObject; + } + + %turretAttacker = %sourceObject.getControllingClient(); + // 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") + { + //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 != ""){ + if (%clVictim.isAIControlled()) + %clVictim.clientDetected(%clAttacker); + } + } + } + + //call the game specific AI routines... + if(%clVictim != ""){ + 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) +{ + if ($onClientKilledHook == 1) + onClientKilledHook(%clVictim); + + if(%clVictim != ""){ + %plVictim = %clVictim.player; + %plKiller = %clKiller.player; + %plAName = %plvictim.getDatablock().getname(); + %clVictim.plyrPointOfDeath = %plVictim.position; + %clVictim.plyrDiedHoldingFlag = %plVictim.holdingFlag; + %clVictim.waitRespawn = 1; + + if ($Host::RepairPatchOnDeath == 1) { + %p = new Item () { + dataBlock = "RepairPatch"; + position = %plVictim.getWorldBoxCenter(); + static = true; + }; + %p.schedulePop(); + MissionCleanup.add(%p); + } + +//[[CHANGE]] Make sure the beacon get's removed.. as it should be.. :D + %clvictim.player.RemoveBeacon(); + + 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', ""); + + if (%damageType == $DamageType::suicide) + %respawnDelay = 10; + else + %respawnDelay = 5; + + + %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); + + if(isObject(%clVictim.isSwitched)) + { + Game.forceObserver(%clVictim.isSwitched, "AdminForce"); + // Message was removed since it wasn't working... + %clVictim.isSwitched.isSwitched = ""; + %clVictim.isSwitched = ""; + } + + // $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); + if(%clVictim != ""){ + %game.updateKillScores(%clVictim, %clKiller, %damageType, %implement); + + if ($Host::Prison::Enabled == true && %clKiller != %clVictim + && ($Host::Prison::Kill == true || (%clKiller.team == %clVictim.team && $Host::Prison::TeamKill == true)) + && %clKiller.player // Make sure killer is a player + && !%clKiller.isAIControlled() && !%clVictim.isAIControlled() // Don't jail for bot actions + && !%clKiller.isAdmin && !%clKiller.isSuperAdmin) { // Don't jail admins/superadmins + %victimName = %clVictim.name; + if (%clKiller.team == %clVictim.team) // Avoid some repetitions + %victimName = "TEAMMATE " @ getTaggedString(%clVictim.name); + if ($Host::Prison::KillTime > 0) { + if ($Host::Prison::KillTime >= 60) { + if ($Host::Prison::KillTime > 60) { + %minutes = mFloor($Host::Prison::KillTime / 60); + messageClient(%clKiller,'msgClient','\c2You will do\c3 %2 \c2minutes in jail for killing\c3 %1\c2.',%victimName,%minutes); + messageAllExcept(%clKiller,-1,'msgClient','\c3%1 \c2will do\c3 %3 \c2minutes in jail for killing\c3 %2\c2.',%clKiller.name,%victimName,%minutes); + } + else { + messageClient(%clKiller,'msgClient','\c2You will do 1 minute in jail for killing\c3 %1\c2.',%victimName); + messageAllExcept(%clKiller,-1,'msgClient','\c3%1 \c2will do 1 minute in jail for killing\c3 %2\c2.',%clKiller.name,%victimName); + } + } + else { + messageClient(%clKiller,'msgClient','\c2You will do\c3 %2 \c2seconds in jail for killing\c3 %1\c2.',%victimName,$Host::Prison::KillTime); + messageAllExcept(%clKiller,-1,'msgClient','\c3%1 \c2will do\c3 %3 \c2seconds in jail for killing\c3 %2\c2.',%clKiller.name,%victimName,$Host::Prison::KillTime); + } + } + else { + messageClient(%clKiller,'msgClient','\c2You were put in jail for killing\c3 %1\c2.',%victimName); + messageAllExcept(%clKiller,-1,'msgClient','\c3%1 \c2was put in jail for killing\c3 %2\c2.',%clKiller.name,%victimName); + } + jailPlayer(%clKiller,false,$Host::Prison::KillTime); + } + + // 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() + 5000; + } + } + else + if(isObject(%clKiller)) + ZKillUpdateScore(%game,%clkiller,%implement); +} + +function DefaultGame::forceObserver( %game, %client, %reason ) +{ + //make sure we have a valid client... + if (%client <= 0) + return; + +// if(%client.player.wasrevived == 1){ +// %client.player.wasrevived = 0; +// return; +// } + + if(%reason $= "spawnTimeout" && isObject(%client.player)) + 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', '\c3%1 \c2has become an observer.', %client.name, %game.getTeamName(0), %client, 0 ); + else + messageAllExcept(%client, -1, 'MsgClientJoinTeam', '\c2The admin has forced \c3%1 \c2to 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(!%clVictim) { + return; + } + + 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)"); + } + } + else if (isObject(%implement) && (%implement.getClassName() $= "Turret" || %implement.getClassName() $= "VehicleTurret" || %implement.getClassName() $= "FlyingVehicle" )) //player killed by a turret + { + 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.getOwner())) + { + %owner = %implement.getOwner(); + //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::Meteor ) // player was struck by meteor + { + messageAll('msgMeteorKill', $DeathMessageMeteor[mFloor(getRandom() * $DeathMessageMeteorCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by meteor"); + } + else if ( %damageType == $DamageType::Cursing ) // player cursing + { + messageAll('msgCursingKill', $DeathMessageCursing[mFloor(getRandom() * $DeathMessageCursingCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by cursing"); + } + else if ( %damageType == $DamageType::Idiocy ) // player was dumb + { + messageAll('msgIdiocyKill', $DeathMessageIdiocy[mFloor(getRandom() * $DeathMessageIdiocyCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by idiocy"); + } + else if ( %damageType == $DamageType::KillerFog ) // player fell into killer fog + { + messageAll('msgKillerFogKill', $DeathMessageKillerFog[mFloor(getRandom() * $DeathMessageKillerFogCount)], %victimName, %victimGender, %victimPoss, %killerName, %killerGender, %killerPoss, %damageType, $DamageTypeText[%damageType]); + logEcho(%clVictim.nameBase@" (pl "@%clVictim.player@"/cl "@%clVictim@") killed by killer fog"); + } + 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 == 56 && (%clVictim.headShot)) + { + // laser headshot just occurred + messageAll('MsgHeadshotKill', $DeathMessageHeadshot[%damageType, mFloor(getRandom() * $DeathMessageHeadshotCount)], %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 ); + if(%client.nameBase !$= "_AISent") + { + 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 ); + + 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 ) +{ + // for multi-team games played with a single team + if (Game.numTeams == 1) { + %game.assignClientTeam( %client ); + // Spawn the player: + %game.spawnPlayer( %client, %respawn ); + return; + } + +//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 ); + + if(%client.nameBase !$= "_AISent") + { + 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); + if ($Host::Prison::Enabled == true) { + if (%client.isJailed) + // If player should manage to get out of jail, re-spawn and re-start sentence time + jailPlayer(%client,false,mAbs(%cl.jailTime)); + } +} + +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) +{ +//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; + +// Shouldn't be needed - serverCmds are proofed (hopefully) +// As it is, it just prevents admins from changing non-admins to a non-valid team +// if (!(%client.isAdmin || %client.isSuperAdmin)) { +// if (%team > %game.numTeams || %team $= "") +// %team = 1; +// } + + // 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) + { + 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."); + + if ( isDemo() ) + { + if ( %client.demoJustJoined ) + { + %client.demoJustJoined = false; + centerPrint( %client, "Welcome to the Tribes 2 Demo." NL "You have been assigned the name \"" @ %client.nameBase @ "\"." NL "Press FIRE to join the game.", 0, 3 ); + } + } + schedule(0, 0, "ccMenu", %client, "Join"); + messageClient(%client, "", "~wfx/Bonuses/Nouns/Colonel.wav"); +} + +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) +// We have deployable station inventories that can be destroyed, and thus we need the health bars +// 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... + // ----------------------------------------------------------------------------- + // z0dd - ZOD, 5/8/02. Part of re-write of Vehicle + // station creation. Do not need this code anymore. + //if(%obj.getDataBlock().getName() $= "StationVehiclePad") + //{ + // %team = %obj.team; + // %obj = %obj.station; + // %obj.team = %team; + //%obj.teleporter.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.isZombieKeeper) + %status = "Keeper "; + 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 @ " 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.getOwner()) != 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); +} + +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); +} + + +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.'); +} + +//------------------------------------------------------------------------------ +// Sentinel Network +//------------------------------------------------------------------------------ + +function DefaultGame::onAIDamaged(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ + if (%clVictim.isSentinel && !%clAttacker.isAIControlled()) + { + %clVictim.player.UnderFire = true; + +// if ($SN::attacker[%clAttacker] $= "") +// $SN::attacker[%clAttacker] = 1; +// else +// $SN::attacker[%clAttacker] = $SN::attacker[%clAttacker]++; + +// if ($SN::attacker[%clAttacker] > 5) +// $SN::CleartoAttack(%clAttacker); + + MessageClient(%clAttacker, 'SN_Msg', "\c2Sentinel Network ::\c0 Do not fire upon our units."); + } +} + +function DefaultGame::onAIFriendlyFire(%game, %clVictim, %clAttacker, %damageType, %sourceObject) +{ +} + +function DefaultGame::onAIKilled(%game, %clVictim, %clKiller, %damageType, %implement) +{ + if (%clVictim.isZombie) + { + if (%clVictim.cp !$= "") + %clVictim.respawnThread = schedule(8000, 0, "ZombieSpawn", %clVictim.cp, %clVictim, %clVictim.SpawnPoint); + + %clVictim.drop(); // Eolk - use drop for bots. + return; + } + if (%clVictim.isSentinel) + { +// Eolk - moved to elsewhere. +// %p = new TracerProjectile() +// { +// DataBlock = "SentinelDeathProj"; +// initialDirection = "0 0 1"; +// initialPosition = %player.getPosition(); +// sourceObject = 0; +// damageFactor = 1; +// sourceSlot = 0; +// }; +// MissionCleanup.Add(%p); + + // Eolk - the player object doesn't exist anymore. + %clVictim.respawnThread = schedule(10000, 0, "SentinelSpawn", %clVictim, %clVictim.SpawnPoint); + return; + } + + //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 ) + %outrankTarget = !%targetClient.isSuperAdmin; + else if ( %client.isAdmin ) + %outrankTarget = !%targetClient.isAdmin; + + if( %client.isSuperAdmin && %targetClient.guid != 0 && !isDemo() ) + { + 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 ); + } + } + + if( !%client.canVote && !%isAdmin ) + return; + + // regular vote options on players + if ( %game.scheduleVote $= "" && !%isAdmin && !%isTargetAdmin ) + { + if ( $Host::allowAdminPlayerVotes && !%isTargetBot && !isDemo() ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Vote to Make Admin', 2 ); + if ( $Host::AllowKeeperPlayerVotes && !%isTargetBot ) + if(!%targetClient.isZombieKeeper) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MakeKeeper", "", 'Vote to Make Keeper', 14 ); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MakeNotKeeper", "", 'Vote to Remove Keeper', 14 ); + + if ( !%isTargetSelf ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Vote to Kick', 3 ); + } + } + + + // Admin only options on players: + else if ( %isAdmin && !isDemo() ) + { + if ( %client.isSuperAdmin && !%isTargetBot && !%isTargetAdmin ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "AdminPlayer", "", 'Make Admin', 2 ); + + if ( !%isTargetBot && !%isTargetAdmin ) + if(!%targetClient.isZombieKeeper) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MakeKeeper", "", 'Make Keeper', 14 ); + else + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "MakeKeeper", "", 'Make Not Keeper', 14 ); + if ( !%isTargetSelf && %outrankTarget ) + { + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "KickPlayer", "", 'Kick', 3 ); + + if ( !%isTargetBot ) + { + if( %client.isSuperAdmin ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "BanPlayer", "", 'Ban', 4 ); + + if ( !%isTargetObserver && Game.class !$= "ZombieGame" ) + messageClient( %client, 'MsgPlayerPopupItem', "", %key, "ToObserver", "", 'Force observer', 5 ); + } + + + if ( ( %isTargetSelf || %outrankTarget ) && Game.class !$= "ZombieGame" ) + { + 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 ); + } + } + 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); + %isSuperAdmin = (%client.isSuperAdmin); + %multipleTeams = %game.numTeams > 1; + + // jailed players do not need menus, unless they are admins + // Note that this does not block gametype-specific menus + if (%client.isJailed && !%isAdmin) + return; + + if(%client.BuildingManagerMode == 1) + { + messageClient( %client, 'MsgVoteItem', "", %key, 'SavePieces', "", 'Save a Building' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'LoadPieces', "", 'Load a Building' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'RenamePieces', "", 'Rename a Building' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'DeletePieces', "", 'Delete a Building' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'GoBackPiece', "", '[\c1Go Back\c0]' ); + return; + } + + if(%client.BuildingManagerMode == 2) + { + %path = "Buildings/ClientSaves/"@%client.guid@"/*"; + %count = 0; + for(%f = findFirstFile(%path); %count < $Host::MaxClientSaves; %f = FindNextFile(%path)) + { + if(fileExt(%f) $= ".dso") + continue; // DSOs should not take spots. + + if(%f $= "") + %name = "(empty)"; + else + { + %duh = getsubstr(%f, 22, 255); + %duh2 = strstr(%duh, "/") + 1; + %name = getsubstr(%duh, %duh2, 255); + } + + %modname = "BSel-"@%name; + messageClient( %client, 'MsgVoteItem', "", %key, %modname, "", %name ); + %count++; + + // Terminate if we ran into a blank. + if(%f $= "") + break; + } + + if(%f $= "") + { + for(%count = %count; %count < $Host::MaxClientSaves; %count++) + { + messageClient( %client, 'MsgVoteItem', "", %key, 'BSel-(empty)', "", '(empty)' ); + } + } + + messageClient( %client, 'MsgVoteItem', "", %key, 'GoBackPiece', "", '[\c1Go Back\c0]' ); + return; + } + + // no one is going anywhere until this thing starts + if(Game.class !$= "ZombieGame") + { + if($MatchStarted) { + if($Host::ClientSaving) + messageClient( %client, 'MsgVoteItem', "", %key, 'StartBuildingManager', "", '[\c1Building Manager\c0]' ); + // 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 && %isAdmin) + //messageClient( %client, 'MsgVoteItem', "", %key, 'Addbot', "", 'Add a Bot' ); + } + } + + if( !%client.canVote && !%isAdmin ) + return; + + if (isDemo()) + return; + + if ( %game.scheduleVote $= "" ) + { + if(!%isAdmin) + { + // Actual vote options: + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Vote to Change the 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 && Game.class !$= "ZombieGame") + 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 ( $Host::Purebuild == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePurebuild', 'disable pure building', '[\c1pure\c0] Vote to Disable Pure Building' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePurebuild', 'enable pure building', '[\c1pure\c0] Vote to Enable Pure Building' ); + if ( $Host::ExpertMode == 1) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteExpertMode', 'disable expert mode', '[\c1pure\c0] Vote to Disable Expert Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteExpertMode', 'enable expert mode', '[\c1pure\c0] Vote to Enable Expert Mode' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRemoveOrphanedDeployables', 'remove all orphaned deployables', '[\c4spam\c0] Vote to Remove All Orphaned Deployables' ); + } + else + { + // Actual vote options: + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeMission', 'change the mission to', 'Change 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 && Game.class !$= "ZombieGame" ) + { + 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 ( $Host::Purebuild == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePurebuild', 'disable pure building', '[\c1pure\c0] Disable Pure Building' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePurebuild', 'enable pure building', '[\c1pure\c0] Enable Pure Building' ); + } + } + + // Admin only options (plus some votable options :P) : + if ( %isAdmin ) { + if ( $Host::Cascade == 1) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteCascadeMode', 'disable cascade mode', '[\c1pure\c0] Disable Cascade Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteCascadeMode', 'enable cascade mode', '[\c1pure\c0] Enable Cascade Mode' ); + if ( $Host::ExpertMode == 1) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteExpertMode', 'disable expert mode', '[\c1pure\c0] Disable Expert Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteExpertMode', 'enable expert mode', '[\c1pure\c0] Enable Expert Mode' ); + if ( $Host::Vehicles == 1) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteVehicles', 'disable vehicles', '[\c1pure\c0] Disable Vehicles' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteVehicles', 'enable vehicles', '[\c1pure\c0] Enable Vehicles' ); + if ( $Host::InvincibleArmors == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteInvincibleArmors', 'disable invincible armors', '[\c1pure\c0] Disable Invincible Armors' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteInvincibleArmors', 'enable invincible armors', '[\c1pure\c0] Enable Invincible Armors' ); + if ( $Host::InvincibleDeployables == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteInvincibleDeployables', 'disable invincible deployables', '[\c1pure\c0] Disable Invincible Deployables' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteInvincibleDeployables', 'enable invincible deployables', '[\c1pure\c0] Enable Invincible Deployables' ); + if ( $Host::AllowUnderground == 1) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteUndergroundMode', 'disable underground mode', '[\c1pure\c0] Disable Underground Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteUndergroundMode', 'enable underground mode', '[\c1pure\c0] Enable Underground Mode' ); + if ( $Host::Hazard::Enabled == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteHazardMode', 'disable hazard mode', '[\c1hazard\c0] Disable Hazard Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteHazardMode', 'enable hazard mode', '[\c1hazard\c0] Enable Hazard Mode' ); + if ($MTC_Loaded == 1) { + if ( $Host::MTC::Enabled == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteMTCMode', 'disable MTC mode', '[\c1mtc\c0] Disable MTC Mode' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteMTCMode', 'enable MTC mode', '[\c1mtc\c0] Enable MTC Mode' ); + } + if ( $Host::SatchelChargeEnabled == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSatchelCharge', 'disable satchel charges', '[\c1security\c0] Disable Satchel Charges' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteSatchelCharge', 'enable satchel charges', '[\c1security\c0] Enable Satchel Charges' ); + if ( $Host::OnlyOwnerDeconstruct == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerDeconstruct', 'disable only owner deconstruct', '[\c1security\c0] Disable Only Owner Deconstruct' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerDeconstruct', 'enable only owner deconstruct', '[\c1security\c0] Enable Only Owner Deconstruct' ); + if ( $Host::OnlyOwnerCascade == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerCascade', 'disable only owner cascade', '[\c1security\c0] Disable Only Owner Cascade' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerCascade', 'enable only owner cascade', '[\c1security\c0] Enable Only Owner Cascade' ); + if ( $Host::OnlyOwnerRotate == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerRotate', 'disable only owner rotate', '[\c1security\c0] Disable Only Owner Rotate' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerRotate', 'enable only owner rotate', '[\c1security\c0] Enable Only Owner Rotate' ); + if ( $Host::OnlyOwnerCubicReplace == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerCubicReplace', 'disable only owner cubic-replace', '[\c1security\c0] Disable Only Owner Cubic-Replace' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteOnlyOwnerCubicReplace', 'enable only owner cubic-replace', '[\c1security\c0] Enable Only Owner Cubic-Replace' ); + if ( $Host::Prison::Enabled == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrison', 'disable prison', '[\c1prison\c0] Disable Prison' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrison', 'enable prison', '[\c1prison\c0] Enable Prison' ); + if ( $Host::Prison::Enabled == 1 ) { + if ( $Host::Prison::Kill == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonKilling', 'disable jailing killers', '[\c1prison\c0] Disable Jailing of Killers' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonKilling', 'enable jailing killers', '[\c1prison\c0] Enable Jailing of Killers' ); + if ( $Host::Prison::TeamKill == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonTeamKilling', 'disable jailing team killers', '[\c1prison\c0] Disable Jailing of Team Killers' ); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonTeamKilling', 'enable jailing team killers', '[\c1prison\c0] Enable Jailing of Team Killers' ); + if ( $Host::Prison::DeploySpam == 1 ) + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonDeploySpam', 'disable jailing deploy spammers', '[\c1prison\c0] Disable Jailing of Deploy Spammers'); + else + messageClient( %client, 'MsgVoteItem', "", %key, 'VotePrisonDeploySpam', 'enable jailing deploy spammers', '[\c1prison\c0] Enable Jailing of Deploy Spammers' ); + } + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteGlobalPowerCheck', 'evaluate power for all deployables', '[\c1power\c0] Evaluate Power for All Deployables' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRemoveDupDeployables', 'remove all duplicate deployables', '[\c4spam\c0] Remove All Duplicate Deployables' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRemoveNonPoweredDeployables', 'remove all deployables without power', '[\c4spam\c0] Remove All Deployables Without Power' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRemoveOrphanedDeployables', 'remove all orphaned deployables', '[\c4spam\c0] Remove All Orphaned Deployables' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteRemoveDeployables', 'remove all deployables in mission', '[\c4spam\c0] Remove All Deployables In Mission' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteChangeTimeLimit', 'change the time limit', 'Change the Time Limit' ); + messageClient( %client, 'MsgVoteItem', "", %key, 'VoteResetServer', 'reset server defaults', 'Reset the Server' ); + } +} + +//------------------------------------------------------------------------------ +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, 10, "", '10 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 15, "", '15 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 20, "", '20 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 25, "", '25 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 30, "", '30 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 45, "", '45 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 60, "", '60 minutes' ); + messageClient( %client, 'MsgVoteItem', "", %key, 10080, "", '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 "voteMakeKeeper": + %game.voteMakeKeeper(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteMakeNotKeeper": + %game.voteMakeNotKeeper(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteGreedMode": + %game.voteGreedMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteHoardMode": + %game.voteHoardMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "votePurebuild": + %game.votePurebuild(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteCascadeMode": + %game.voteCascadeMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteExpertMode": + %game.voteExpertMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "VoteVehicles": + %game.VoteVehicles(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteInvincibleArmors": + %game.voteInvincibleArmors(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteInvincibleDeployables": + %game.voteInvincibleDeployables(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteUndergroundMode": + %game.voteUndergroundMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteHazardMode": + %game.voteHazardMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteMTCMode": + %game.voteMTCMode(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteSatchelCharge": + %game.voteSatchelCharge(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteOnlyOwnerDeconstruct": + %game.voteOnlyOwnerDeconstruct(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteOnlyOwnerCascade": + %game.voteOnlyOwnerCascade(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteOnlyOwnerRotate": + %game.voteOnlyOwnerRotate(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteOnlyOwnerCubicReplace": + %game.voteOnlyOwnerCubicReplace(%admin, %arg1, %arg2, %arg3, %arg4); + case "votePrison": + %game.votePrison(%admin, %arg1, %arg2, %arg3, %arg4); + case "votePrisonKilling": + %game.VotePrisonKilling(%admin, %arg1, %arg2, %arg3, %arg4); + case "votePrisonTeamKilling": + %game.VotePrisonTeamKilling(%admin, %arg1, %arg2, %arg3, %arg4); + case "votePrisonDeploySpam": + %game.VotePrisonDeploySpam(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteGlobalPowerCheck": + %game.voteGlobalPowerCheck(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteRemoveDupDeployables": + %game.voteRemoveDupDeployables(%admin, %arg1, %arg2, %arg3, %arg4); + case "VoteRemoveNonPoweredDeployables": + %game.VoteRemoveNonPoweredDeployables(%admin, %arg1, %arg2, %arg3, %arg4); + case "VoteRemoveOrphanedDeployables": + %game.VoteRemoveOrphanedDeployables(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteRemoveDeployables": + %game.voteRemoveDeployables(%admin, %arg1, %arg2, %arg3, %arg4); + case "voteLoadBuilding": + %game.voteLoadBuilding(%admin, %arg1, %arg2, %arg3, %arg4); + } +} + +function DefaultGame::voteChangeMission(%game, %admin, %missionDisplayName, %typeDisplayName, %missionId, %missionTypeId) +{ + if (%admin) { + if (%missionTypeId $= "LoadBuildingFile") { + %file = stripChars(%missionDisplayName,":\\/"); + if (getSubStr(%file,0,1) $= "_") { + %dir = $SaveBuilding::AutoSaveFolder; + %file = getSubStr(%file,1,strLen(%file)-1); + } + else + %dir = "Buildings/Admin/"; + if (%file $= "") + return; + else { + if (strStr(%file,"..") != -1) + return; + } + %file = %dir @ %file; + if (isFile(%file) && getSubStr(%file,strLen(%file)-3,3) $= ".cs") { + // Message is sent first, so clients know what happened in case server crashes + messageAll('MsgAdminForce', '\c3%1 \c2has loaded a building file.', %admin.nameBase); + compile(%file); + exec(%file); + } + return; + } + } + + %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', '\c3%3 \c2has changed the mission to\c3 %1 \c2(%2).', %missionDisplayName, %typeDisplayName, %admin.nameBase ); + logEcho("mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" by "@%admin.nameBase@" ("@%admin@")"); + updateRankScores(0,1); + %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\c3 %1 \c2(%2) by vote.', %missionDisplayName, %typeDisplayName ); + logEcho("mission changed to "@%missionDisplayName@"/"@%typeDisplayName@" (vote)"); + updateRankScores(0,1); + %game.gameOver(); + loadMission( %mission, %missionType, false ); + } + else + messageAll('MsgVoteFailed', '\c2Change mission vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } +} + +//------------------------------------------------------------------------------ + +function DefaultGame::voteLoadBuilding(%game, %admin, %arg, %null, %building, %dude) +{ + if(%admin) + { + compile(%building); + exec(%building); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + globalPowerCheck(); + messageAll('MsgAdminForce', '\c2Admin %1 has loaded a building file, leaving %2 pieces left.', %admin.nameBase, %arg, %admin.sex $= "Male" ? 'his' : 'her'); + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c3%1\'s \c2building file was loaded by vote. Power has been evaluated and duplicates have been removed.', %dude.nameBase, %arg); + compile(%building); + exec(%building); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + globalPowerCheck(); + } + else + messageAll('MsgVoteFailed', '\c2Vote to load %1\'s building did not pass:\c3 %3 \c2percent.', %dude.nameBase, %arg, mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteTeamDamage(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($teamDamage) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled team damage.', %admin.nameBase); + $Host::TeamDamageOn = $TeamDamage = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled team damage.', %admin.nameBase); + $Host::TeamDamageOn = $TeamDamage = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($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:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable team damage vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("team damage "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteLoadBuilding(%game, %admin, %building, %arg, %null, %dude) +{ + if(%admin) + { + compile(%building); + exec(%building); + messageAll('MsgAdminForce', '\c3%1 \c2has loaded %3 \c3%2\c2.', %admin.nameBase, %arg, %admin.sex $= "Male" ? 'his' : 'her'); + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c3%1\'s \c2building was loaded by vote. Power has been evaluated and duplicates have been removed.', %dude.nameBase, %arg); + compile(%building); + exec(%building); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + globalPowerCheck(); + } + else + messageAll('MsgVoteFailed', '\c2Vote to load \c3%1\'s \c2building did not pass:\c3 %2 \c2percent.', %dude.nameBase, mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } +} + +//------------------------------------------------------------------------------ +function DefaultGame::votePurebuild(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Purebuild == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled pure building.', %admin.nameBase); + purebuildOff(); + Schedule(200000, 0, "Events", "RandomEvents"); + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled pure building.', %admin.nameBase); + purebuildOn(); + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Purebuild == 1) + { + messageAll('MsgVotePassed', '\c2Pure building was disabled by vote.'); + purebuildOff(); + Schedule(200000, 0, "Events", "RandomEvents"); + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Pure building was enabled by vote.'); + purebuildOn(); + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Purebuild == 1) + messageAll('MsgVoteFailed', '\c2Disable pure building vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable pure building vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("purebuild "@%setto SPC %cause); +} + +function DefaultGame::voteCascadeMode(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Cascade == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled Cascade mode.', %admin.nameBase); + $Host::Cascade = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled Cascade mode.', %admin.nameBase); + $Host::Cascade = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Cascade == 1) + { + messageAll('MsgVotePassed', '\c2Cascade mode was disabled by vote.'); + $Host::Cascade = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Cascade mode was enabled by vote.'); + $Host::Cascade = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Cascade == 1 && $Host::Cascade == 0) + messageAll('MsgVoteFailed', '\c2Disable Cascade mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable Cascade mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("cascade mode "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteExpertMode(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::ExpertMode == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled Expert mode.', %admin.nameBase); + expertModeOff(); + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled Expert mode.', %admin.nameBase); + expertModeOn(); + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::ExpertMode == 1) + { + messageAll('MsgVotePassed', '\c2Expert mode was disabled by vote.'); + expertModeOff(); + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Expert mode was enabled by vote.'); + expertModeOn(); + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::ExpertMode == 1 && $Host::ExpertMode == 0) + messageAll('MsgVoteFailed', '\c2Disable Expert mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable Expert mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("expert mode "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteVehicles(%game, %admin) { + if (!(Game.pureVehTime < getSimTime())) + return; + + %setto = ""; + %cause = ""; + if(%admin) { + if($Host::Vehicles == 1) { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled vehicles.', %admin.nameBase); + disableVehicles(); + Game.pureVehTime = getSimTime() + 5000; + %setto = "disabled"; + } + else { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled vehicles.', %admin.nameBase); + enableVehicles(); + Game.pureVehTime = getSimTime() + 5000; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + if($Host::Vehicles == 1) { + messageAll('MsgVotePassed', '\c2Vehicles were disabled by vote.'); + disableVehicles(); + Game.pureVehTime = getSimTime() + 5000; + %setto = "disabled"; + } + else { + messageAll('MsgVotePassed', '\c2Vehicles were enabled by vote.'); + enableVehicles(); + Game.pureVehTime = getSimTime() + 5000; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else { + if($Host::Vehicles == 1) + messageAll('MsgVoteFailed', '\c2Disable vehicles vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable vehicles vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("pure vehicles "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteSatchelCharge(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::SatchelChargeEnabled == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled satchel charges.', %admin.nameBase); + $Host::SatchelChargeEnabled = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled satchel charges.', %admin.nameBase); + $Host::SatchelChargeEnabled = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::SatchelChargeEnabled == 1) + { + messageAll('MsgVotePassed', '\c2Satchel charges was disabled by vote.'); + $Host::SatchelChargeEnabled = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Satchel charges was enabled by vote.'); + $Host::SatchelChargeEnabled = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::SatchelChargeEnabled == 1) + messageAll('MsgVoteFailed', '\c2Disable satchel charges vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable satchel charges vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("satchel charges "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteOnlyOwnerDeconstruct(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::OnlyOwnerDeconstruct == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled only owner deconstruct mode.', %admin.nameBase); + $Host::OnlyOwnerDeconstruct = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled only owner deconstruct mode.', %admin.nameBase); + $Host::OnlyOwnerDeconstruct = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::OnlyOwnerDeconstruct == 1) + { + messageAll('MsgVotePassed', '\c2Only owner deconstruct mode was disabled by vote.'); + $Host::OnlyOwnerDeconstruct = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Only owner deconstruct mode was enabled by vote.'); + $Host::OnlyOwnerDeconstruct = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::OnlyOwnerDeconstruct == 1) + messageAll('MsgVoteFailed', '\c2Only owner deconstruct vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Only owner deconstruct vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("only owner deconstruct "@%setto SPC %cause); +} + +function DefaultGame::VoteOnlyOwnerCascade(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::OnlyOwnerCascade == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled only owner cascade mode.', %admin.nameBase); + $Host::OnlyOwnerCascade = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled only owner cascade mode.', %admin.nameBase); + $Host::OnlyOwnerCascade = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::OnlyOwnerCascade == 1) + { + messageAll('MsgVotePassed', '\c2Only owner cascade mode was disabled by vote.'); + $Host::OnlyOwnerCascade = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Only owner cascade mode was enabled by vote.'); + $Host::OnlyOwnerCascade = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::OnlyOwnerCascade == 1) + messageAll('MsgVoteFailed', '\c2Only owner cascade vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Only owner cascade vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("only owner cascade "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteOnlyOwnerRotate(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::OnlyOwnerRotate == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled only owner rotate mode.', %admin.nameBase); + $Host::OnlyOwnerRotate = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled only owner rotate mode.', %admin.nameBase); + $Host::OnlyOwnerRotate = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::OnlyOwnerRotate == 1) + { + messageAll('MsgVotePassed', '\c2Only owner rotate mode was disabled by vote.'); + $Host::OnlyOwnerRotate = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Only owner rotate mode was enabled by vote.'); + $Host::OnlyOwnerRotate = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::OnlyOwnerRotate == 1) + messageAll('MsgVoteFailed', '\c2Only owner rotate vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Only owner rotate vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("only owner rotate "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteOnlyOwnerCubicReplace(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::OnlyOwnerCubicReplace == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled only owner cubic-replace mode.', %admin.nameBase); + $Host::OnlyOwnerCubicReplace = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled only owner cubic-replace mode.', %admin.nameBase); + $Host::OnlyOwnerCubicReplace = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::OnlyOwnerCubicReplace == 1) + { + messageAll('MsgVotePassed', '\c2Only owner cubic-replace mode was disabled by vote.'); + $Host::OnlyOwnerCubicReplace = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Only owner cubic-replace mode was enabled by vote.'); + $Host::OnlyOwnerCubicReplace = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::OnlyOwnerCubicReplace == 1) + messageAll('MsgVoteFailed', '\c2Only owner cubic-replace vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Only owner cubic-replace vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("only owner cubic-replace "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteGlobalPowerCheck( %game, %admin, %client ) { + %cause = ""; + if (%admin) { + messageAll( 'MsgAdminForce', '\c3%1 \c2has evaluated power for all deployables in the mission.', %admin.nameBase); + globalPowerCheck(); + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + messageAll('MsgVotePassed', '\c2Evaluating power for all deployables in the mission by vote.' ); + globalPowerCheck(); + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to evaluate power for all deployables in the mission did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("evaluate power for all deployables "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteRemoveDupDeployables( %game, %admin, %client ) { + if (!(Game.removeDepTime < getSimTime())) + return; + %cause = ""; + if (%admin) { + messageAll( 'MsgAdminForce', '\c3%1 \c2removed all duplicate deployables in the mission.', %admin.nameBase); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + messageAll('MsgVotePassed', '\c2Removing all duplicate deployables in the mission by vote.' ); + delDupPieces(0,0,true); + Game.removeDepTime = getSimTime() + delDupPieces(0,0,true) + 1000; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to remove all duplicate deployables in the mission did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("remove all duplicate deployables "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteRemoveNonPoweredDeployables( %game, %admin, %client ) { + if (!(Game.removeDepTime < getSimTime())) + return; + %cause = ""; + if (%admin) { + messageAll( 'MsgAdminForce', '\c3%1 \c2removed all deployables in the mission without power.', %admin.nameBase); + Game.removeDepTime = getSimTime() + delNonPoweredPieces(true) + 1000; + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + messageAll('MsgVotePassed', '\c2Removing all deployables in the mission without power by vote.' ); + delNonPoweredPieces(true); + Game.removeDepTime = getSimTime() + delNonPoweredPieces(true) + 1000; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to remove all deployables in the mission without power did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("remove all deployables without power "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteRemoveOrphanedDeployables( %game, %admin, %client ) { + if (!(Game.removeDepTime < getSimTime())) + return; + %cause = ""; + if (%admin) { + messageAll( 'MsgAdminForce', '\c3%1 \c2removed all orphaned deployables in the mission.', %admin.nameBase); + Game.removeDepTime = getSimTime() + delOrphanedPieces(true) + 1000; + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + messageAll('MsgVotePassed', '\c2Removing all orphaned deployables in the mission by vote.' ); + delOrphanedPieces(true); + Game.removeDepTime = getSimTime() + delOrphanedPieces(true) + 1000; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to remove all orphaned deployables in the mission did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("remove all orphaned deployables "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteRemoveDeployables( %game, %admin, %client ) { + if (!(Game.removeDepTime < getSimTime())) + return; + %cause = ""; + if (%admin) { + messageAll( 'MsgAdminForce', '\c3%1 \c2removed all deployables in the mission.', %admin.nameBase); + Game.removeDepTime = getSimTime() + unpureDeployables() + 1000; + %cause = "("@%admin.nameBase@")"; + } + else { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) { + messageAll('MsgVotePassed', '\c2Removing all deployables in the mission by vote.' ); + unpureDeployables(); + Game.removeDepTime = getSimTime() + unpureDeployables() + 1000; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2The vote to remove all deployables in the mission did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("remove all deployables "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteInvincibleArmors(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::InvincibleArmors == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled invincible armors.', %admin.nameBase); + $Host::InvincibleArmors = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled invincible armors.', %admin.nameBase); + $Host::InvincibleArmors = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::InvincibleArmors == 1) + { + messageAll('MsgVotePassed', '\c2Invincible armors was disabled by vote.'); + $Host::InvincibleArmors = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Invincible armors was enabled by vote.'); + $Host::InvincibleArmors = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::InvincibleArmors == 1) + messageAll('MsgVoteFailed', '\c2Disable invincible armors vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable invincible armors vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("invincible armors "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VoteInvincibleDeployables(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::InvincibleDeployables == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled invincible deployables.', %admin.nameBase); + $Host::InvincibleDeployables = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled invincible deployables.', %admin.nameBase); + $Host::InvincibleDeployables = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::InvincibleDeployables == 1) + { + messageAll('MsgVotePassed', '\c2Invincible deployables was disabled by vote.'); + $Host::InvincibleDeployables = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Invincible deployables was enabled by vote.'); + $Host::InvincibleDeployables = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::InvincibleDeployables == 1) + messageAll('MsgVoteFailed', '\c2Disable invincible deployables vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable invincible deployables vote did not pass:\c3 %1 \c2 %1 percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("invincible deployables "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteUndergroundMode(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::AllowUnderground == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled Underground mode.', %admin.nameBase); + $Host::AllowUnderground = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled Underground mode.', %admin.nameBase); + $Host::AllowUnderground = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::AllowUnderground == 1) + { + messageAll('MsgVotePassed', '\c2Underground mode was disabled by vote.'); + $Host::AllowUnderground = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Underground mode was enabled by vote.'); + $Host::AllowUnderground = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::AllowUnderground == 1) + messageAll('MsgVoteFailed', '\c2Disable Underground mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable Underground mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("underground mode "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteHazardMode(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Hazard::Enabled == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled Hazard mode.', %admin.nameBase); + hazardOff(); + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled Hazard mode.', %admin.nameBase); + hazardOn(); + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Hazard::Enabled == 1) + { + messageAll('MsgVotePassed', '\c2Hazard mode was disabled by vote.'); + hazardOff(); + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Hazard mode was enabled by vote.'); + hazardOn(); + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Hazard::Enabled == 1) + messageAll('MsgVoteFailed', '\c2Disable Hazard mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable Hazard mode vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("hazard mode "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::votePrison(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Prison::Enabled == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled the prison.', %admin.nameBase); + prisonDisable(); + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled the prison.', %admin.nameBase); + prisonEnable(); + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Prison::Enabled == 1) + { + messageAll('MsgVotePassed', '\c2The prison was disabled by vote.'); + prisonDisable(); + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2The prison was enabled by vote.'); + prisonEnable(); + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Prison::Enabled == 1) + messageAll('MsgVoteFailed', '\c2Disable prison vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable prison vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("prison "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VotePrisonKilling(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Prison::Kill == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled jailing of killers.', %admin.nameBase); + $Host::Prison::Kill = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled jailing of killers.', %admin.nameBase); + $Host::Prison::Kill = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Prison::Kill == 1) + { + messageAll('MsgVotePassed', '\c2Jailing of killers was disabled by vote.'); + $Host::Prison::Kill = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Jailing of killers was enabled by vote.'); + $Host::Prison::Kill = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Prison::Kill == 1) + messageAll('MsgVoteFailed', '\c2Disable jailing of killers vote not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable jailing of killers vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("prison killing "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VotePrisonTeamKilling(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Prison::TeamKill == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled jailing of team killers.', %admin.nameBase); + $Host::Prison::TeamKill = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled jailing of team killers.', %admin.nameBase); + $Host::Prison::TeamKill = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Prison::TeamKill == 1) + { + messageAll('MsgVotePassed', '\c2Jailing of team killers was disabled by vote.'); + $Host::Prison::TeamKill = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Jailing of team killers was enabled by vote.'); + $Host::Prison::TeamKill = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Prison::TeamKill == 1) + messageAll('MsgVoteFailed', '\c2Disable jailing of team killers vote not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable jailing of team killers vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("prison team killers "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::VotePrisonDeploySpam(%game, %admin) +{ + %setto = ""; + %cause = ""; + if(%admin) + { + if($Host::Prison::DeploySpam == 1) + { + messageAll('MsgAdminForce', '\c3%1 \c2has disabled jailing of deploy spammers.', %admin.nameBase); + $Host::Prison::DeploySpam = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgAdminForce', '\c3%1 \c2has enabled jailing of deploy spammers.', %admin.nameBase); + $Host::Prison::DeploySpam = 1; + %setto = "enabled"; + } + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + if($Host::Prison::DeploySpam == 1) + { + messageAll('MsgVotePassed', '\c2Jailing of deploy spammers was disabled by vote.'); + $Host::Prison::DeploySpam = 0; + %setto = "disabled"; + } + else + { + messageAll('MsgVotePassed', '\c2Jailing of deploy spammers was enabled by vote.'); + $Host::Prison::DeploySpam = 1; + %setto = "enabled"; + } + %cause = "(vote)"; + } + else + { + if($Host::Prison::DeploySpam == 1) + messageAll('MsgVoteFailed', '\c2Disable jailing of deploy spammers vote not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + else + messageAll('MsgVoteFailed', '\c2Enable jailing of deploy spammers vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + if(%setto !$= "") + logEcho("prison deploy spammers "@%setto SPC %cause); +} + +//------------------------------------------------------------------------------ +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', '\c3%2 \c2has switched the server to Tournament mode (%1).', %missionDisplayName, %admin.nameBase ); + setModeTournament( %mission, %missionType ); + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2Server switched to Tournament mode by vote (%1):\c3 %2 \c2 percent.', %missionDisplayName, mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + setModeTournament( %mission, %missionType ); + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Tournament mode vote did not pass:\c3 %2 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("tournament mode set "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteMatchStart( %game, %admin) +{ + %cause = ""; + %ready = forceTourneyMatchStart(); + if(%admin) + { + if(!%ready) + { + messageClient( %client, 'msgClient', '\c2No players are ready yet.'); + return; + } + else + { + messageAll('msgMissionStart', '\c3%1 \c2has forced the match to start.', %admin.nameBase); + %cause = "("@%admin.nameBase@")"; + startTourneyCountdown(); + } + } + else + { + if(!%ready) + { + messageAll( 'msgClient', '\c2Vote passed to start match, but no players are ready yet.'); + return; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c2The match has been started by vote:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + startTourneyCountdown(); + } + else + messageAll('MsgVoteFailed', '\c2Start Match vote did not pass:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + } + + if(%cause !$= "") + logEcho("start match "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteFFAMode( %game, %admin, %client ) +{ + %cause = ""; + %name = getTaggedString(%client.name); + + if (%admin) + { + messageAll('MsgAdminForce', '\c3%2 \c2has switched the server to Free For All mode.', %client, %admin.nameBase); + setModeFFA($CurrentMission, $CurrentMissionType); + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($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:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + if(%cause !$= "") + logEcho("free for all set "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteChangeTimeLimit( %game, %admin, %newLimit ) +{ + if( %newLimit == 999 ) + %display = "unlimited"; + else + %display = %newLimit; + + %cause = ""; + if ( %admin ) + { + messageAll( 'MsgAdminForce', '\c3%2 \c2changed the mission time limit to\c3 %1 \c2minutes.', %display, %admin.nameBase ); + $Host::TimeLimit = %newLimit; + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($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:\c3 %1 \c2percent.', mFloor(%game.totalVotesFor/(ClientGroup.getCount() - $HostGameBotCount) * 100)); + } + + //if the time limit was actually changed... + if(%cause !$= "") + { + logEcho("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', '\c2%1 has reset all server defaults.', %admin.nameBase); + resetServerDefaults(); + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($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) * 100)); + } + if(%cause !$= "") + logEcho("server reset "@%cause); +} + +//------------------------------------------------------------------------------ +// all team based votes here +function DefaultGame::voteKickPlayer(%game, %admin, %client) +{ + %cause = ""; + + if(%admin) + { + kick(%client, %admin, %client.guid ); + %cause = "("@%admin.nameBase@")"; + } + 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(%name@" (cl " @ %game.kickClient @ ") kicked " @ %cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::banPlayer(%game, %admin, %client) +{ + %cause = ""; + %name = %client.nameBase; + if( %admin ) + { + ban( %client, %admin ); + %cause = "("@%admin.nameBase@")"; + } + + if(%cause !$= "") + logEcho(%name@" (cl "@%client@") banned "@%cause); +} + +//------------------------------------------------------------------------------ +function DefaultGame::voteAdminPlayer(%game, %admin, %client) +{ + %cause = ""; + + if (%admin) + { + messageAll('MsgAdminAdminPlayer', '\c3%3 \c2made\c3 %2 \c2an admin.', %client, %client.name, %admin.nameBase); + %client.isAdmin = 1; + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgAdminPlayer', '\c3%2 \c2was made an admin by vote.', %client, %client.name); + %client.isAdmin = 1; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Vote to make\c2 %1 \c2an admin did not pass.', %client.name); + } + if(%cause !$= "") + logEcho(%client.nameBase@" (cl "@%client@") made admin "@%cause); +} + +function DefaultGame::voteMakeKeeper(%game, %admin, %client) +{ + %cause = ""; + + if(Game.class $= "ConstructionGame") + { + messageAll('MsgVoteFailed', '\c2Vote to make\c3 %1 \c2a zombie keeper did not pass. There are no zombie keepers in the Construction gametype.', %client.nameBase); + return; + } + + if (%admin) + { + messageAll('MsgAdminForce', "\c3"@%admin.nameBase@" \c2has made\c3 "@%client.nameBase@" \c2a zombie keeper."); + %client.isZombieKeeper = true; + %cause = "("@%admin.nameBase@")"; + } else if(Game.class $= "ConstructionGame") + { + messageAll('MsgVoteFailed', '\c3%1 \c2attempted to make %2 a zombie keeper. There are no zombie keepers in the Construction gametype.', %admin.nameBase, %client.nameBase); + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c3%1 \c2was made a zombie keeper by vote.', %client.nameBase); + %client.isZombieKeeper = true; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Vote to make\c3 %1 \c2a zombie keeper did not pass.', %client.nameBase); + } + if(%cause !$= "") + logEcho(%client.nameBase@" (cl "@%client@") made keeper "@%cause); +} + +function DefaultGame::voteMakeNotKeeper(%game, %admin, %client) +{ + %cause = ""; + + if (%admin) + { + messageAll('MsgAdminForce', "\c3"@%admin.nameBase@" \c2has removed\c3 "@%client.nameBase@"'s \c2zombie keeper abilities."); + %client.isZombieKeeper = false; + %cause = "("@%admin.nameBase@")"; + } + else + { + %totalVotes = %game.totalVotesFor + %game.totalVotesAgainst; + if(%totalVotes > 0 && (%game.totalVotesFor / (ClientGroup.getCount() - $HostGameBotCount)) > ($Host::VotePasspercent / 100)) + { + messageAll('MsgVotePassed', '\c3%1 \c2had their zombie keeper abilities taken away by vote.', %client.nameBase); + %client.isZombieKeeper = false; + %cause = "(vote)"; + } + else + messageAll('MsgVoteFailed', '\c2Vote to remove zombie keeper abilities from\c3 %1 \c2did not pass.', %client.nameBase); + } + if(%cause !$= "") + logEcho(%client.nameBase@" (cl "@%client@") had keeper removed "@%cause); +} + +//------------------------------------------------------------------------------ +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); + } + } +} + +//------------------------------------------------------------------------------ +$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 && !isDemo() && %game.class $= "CTFGame") + { + %team1ClientScore = "--------------"; + } + else if( %index == $teamRank[1, count]+1 && $teamRank[1, count] != 0 && !isDemo() && %game.class $= "CTFGame") + { + %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 && !isDemo() && %game.class $= "CTFGame") + { + %team2ClientScore = "--------------"; + } + else if( %index == $teamRank[2, count]+1 && $teamRank[2, count] != 0 && !isDemo() && %game.class $= "CTFGame") + { + %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); + else if (%seconds == 1) + MessageAll('MsgMissionEnd', '\c2Match ends in 1 second.~wfx/misc/hunters_1.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 ) +{ +} diff --git a/Scripts/deployables.cs b/Scripts/deployables.cs new file mode 100644 index 0000000..695c89f --- /dev/null +++ b/Scripts/deployables.cs @@ -0,0 +1,2123 @@ +// deployable objects script +// +// remote pulse sensor, remote motion sensor, remote turrets (indoor +// and outdoor), remote inventory station, remote ammo station +// Note: cameras are treated as grenades, not "regular" deployables + +$Host::CRCTextures = 0; +$TurretIndoorSpaceRadius = 1; // deployed turrets must be this many meters apart +$InventorySpaceRadius = 1; // deployed inventory must be this many meters apart +$TurretIndoorSphereRadius = 1; // radius for turret frequency check +$TurretIndoorMaxPerSphere = 1; // # of turrets allowed in above radius + +$TurretOutdoorSpaceRadius = 25; // deployed turrets must be this many meters apart +$TurretOutdoorSphereRadius = 60; // radius for turret frequency check +$TurretOutdoorMaxPerSphere = 4; // # of turrets allowed in above radius + +$TeamDeployableMax[InventoryDeployable] = 10; +$TeamDeployableMax[TurretIndoorDeployable] = 10; +$TeamDeployableMax[TurretOutdoorDeployable] = 10; +$TeamDeployableMax[PulseSensorDeployable] = 25; +$TeamDeployableMax[MotionSensorDeployable] = 25; + +$TeamDeployableMax[LargeInventoryDeployable] = 1000; +$TeamDeployableMax[GeneratorDeployable] = 500; +$TeamDeployableMax[SolarPanelDeployable] = 1500; +$TeamDeployableMax[SwitchDeployable] = 1000; +$TeamDeployableMax[MediumSensorDeployable] = 1000; +$TeamDeployableMax[LargeSensorDeployable] = 1000; +$TeamDeployableMax[WallDeployable] = 1000; +$TeamDeployableMax[FloorDeployable] = 1000; +$TeamDeployableMax[wWallDeployable] = 1000; +$TeamDeployableMax[SpineDeployable] = 5000; +$TeamDeployableMax[mSpineDeployable] = 1000; +$TeamDeployableMax[JumpadDeployable] = 5000; +$TeamDeployableMax[EnergizerDeployable] = 1000; +$TeamDeployableMax[TreeDeployable] = 5000; +$TeamDeployableMax[CrateDeployable] = 2500; +$TeamDeployableMax[DecorationDeployable] = 10000; +$TeamDeployableMax[LogoProjectorDeployable] = 1000; +$TeamDeployableMax[LightDeployable] = 5000; +$TeamDeployableMax[TripwireDeployable] = 5000; +$TeamDeployableMax[ForceFieldDeployable] = 1000; +$TeamDeployableMax[GravityFieldDeployable] = 50; +$TeamDeployableMax[TelePadPack] = 5000; +$TeamDeployableMax[SpySatelliteDeployable] = 1; +$TeamDeployableMax[DoorDeployable] = 1000; +$TeamDeployableMax[ZSpawnDeployable] = 1000; +$TeamDeployableMax[SentinelDeployable] = 1000; +$TeamDeployableMax[SpawnPointPack] = 10; +$TeamDeployableMax[RepairPadDeployable] = 10; +$TeamDeployableMax[DronePadDeployable] = 1; +$TeamDeployableMax[waypointDeployable] = 50; +$TeamDeployableMax[MineDeployed] = 100; + +$TeamDeployableMax[TurretBasePack] = 25; +$TeamDeployableMax[TurretSentryPack] = 45; +$TeamDeployableMax[TurretLaserDeployable] = 1000; +$TeamDeployableMax[DiscTurretDeployable] = 3; +$TeamDeployableMax[TurretMissileRackDeployable] = 2; + +//This could also be in "pack".cs but what the heck +//[most] +$TeamDeployableMax[TurretMpm_Anti_Deployable] = 400; +$TeamDeployableMax[VehiclePadPack] = 4000; +$TeamDeployableMax[EmitterDepPack] = 5000; +$TeamDeployableMax[AudioDepPack] = 5000; +$TeamDeployableMax[DispenserDepPack] = 5000; +$TeamDeployableMax[MPM_BeaconPack] = 5000; +$TeamDeployableMax[DetonationDepPack] = 500; +//noone cares +$TeamDeployableMax[MpmFuelPack] = 1; +$TeamDeployableMax[MpmAmmoPack] = 1; + +//[most] + +$TeamDeployableMin[TurretIndoorDeployable] = 10; +$TeamDeployableMin[TurretOutdoorDeployable] = 10; +$TeamDeployableMin[TurretBasePack] = 20; +$TeamDeployableMin[TurretLaserDeployable] = 500; +$TeamDeployableMin[DiscTurretDeployable] = 3; +$TeamDeployableMin[TurretMissileRackDeployable] = 2; + +//[most] +$TeamDeployableMin[TurretMpm_Anti_Deployable] = 40; +$TeamDeployableMin[VehiclePadPack] = 40; +$TeamDeployableMin[EmitterDepPack] = 50; +$TeamDeployableMin[AudioDepPack] = 50; +$TeamDeployableMin[DispenserDepPack] = 50; +$TeamDeployableMin[MPM_BeaconPack] = 50; +$TeamDeployableMin[DetonationDepPack] = 5; +//noone cares +$TeamDeployableMin[MpmFuelPack] = 1; +$TeamDeployableMin[MpmAmmoPack] = 1; + +//[most] + +$NotDeployableReason::None = 0; +$NotDeployableReason::MaxDeployed = 1; +$NotDeployableReason::NoSurfaceFound = 2; +$NotDeployableReason::SlopeTooGreat = 3; +$NotDeployableReason::SelfTooClose = 4; +$NotDeployableReason::ObjectTooClose = 5; +$NotDeployableReason::NoTerrainFound = 6; +$NotDeployableReason::NoInteriorFound = 7; +$NotDeployableReason::TurretTooClose = 8; +$NotDeployableReason::TurretSaturation = 9; +$NotDeployableReason::SurfaceTooNarrow = 10; +$NotDeployableReason::InventoryTooClose = 11; +$NotDeployableReason::PriveledgesRevoked = 12; + +$MinDeployableDistance = 2.5; +$MaxDeployableDistance = 5.0; //meters from body + +// -------------------------------------------- +// effect datablocks +// -------------------------------------------- + +datablock EffectProfile(TurretDeployEffect) +{ + effectname = "packs/generic_deploy"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(SensorDeployEffect) +{ + effectname = "powered/sensor_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(MotionSensorDeployEffect) +{ + effectname = "powered/motion_sensor_activate"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +datablock EffectProfile(StationDeployEffect) +{ + effectname = "packs/inventory_deploy"; + minDistance = 2.5; + maxDistance = 5.0; +}; + +// -------------------------------------------- +// sound datablocks +// -------------------------------------------- + +datablock AudioProfile(TurretDeploySound) +{ + fileName = "fx/packs/turret_place.wav"; + description = AudioClose3d; + preload = true; + effect = TurretDeployEffect; +}; + +datablock AudioProfile(DSound) +{ + filename = "fx/weapons/plasma_rifle_activate.wav"; + //filename = "fx/packs/repair_use.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock AudioProfile(SensorDeploySound) +{ + fileName = "fx/powered/sensor_activate.wav"; + description = AudioClose3d; + preload = true; + effect = SensorDeployEffect; + // z0dd - ZOD - Durt, 6/24/02. Eh? This shouldn't be in here. + //effect = MotionSensorDeployEffect; +}; + +datablock AudioProfile(MotionSensorDeploySound) +{ + fileName = "fx/powered/motion_sensor_activate.wav"; + description = AudioClose3d; + preload = true; + // z0dd - ZOD - Durt, 6/24/02. This should be in here. + effect = MotionSensorDeployEffect; +}; + +datablock AudioProfile(StationDeploySound) +{ + fileName = "fx/packs/inventory_deploy.wav"; + description = AudioClose3d; + preload = true; + effect = StationDeployEffect; +}; + +// -------------------------------------------- +// deployable debris definition + +datablock DebrisData( DeployableDebris ) +{ + explodeOnMaxBounce = true; + + elasticity = 0.40; + friction = 0.5; + + lifetime = 200.0; + lifetimeVariance = 5.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 1; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 20; + + velocity = 5.0; + velocityVariance = 2.5; + +}; + + +// -------------------------------------------- +// deployable inventory station + +datablock StaticShapeData(DeployedStationInventory) : StaticShapeDamageProfile +{ + className = Station; + shapeFile = "deploy_inventory.dts"; + maxDamage = 0.70; + destroyedLevel = 0.70; + disabledLevel = 0.42; + explosion = DeployablesExplosion; + expDmgRadius = 8.0; + expDamage = 0.35; + expImpulse = 500.0; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = true; + energyPerDamagePoint = 110; + maxEnergy = 50; + rechargeRate = 0.20; + renderWhenDestroyed = false; + doesRepair = true; + + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Antidote'; + targetTypeTag = 'Station'; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock ShapeBaseImageData(InventoryDeployableImage) +{ + mass = 15; + emap = true; + + shapeFile = "pack_deploy_inventory.dts"; + item = InventoryDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedStationInventory; + heatSignature = 0; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + maxDepSlope = 30; + deploySound = StationDeploySound; + + flatMinDeployDis = 1.0; + flatMaxDeployDis = 5.0; + + minDeployDis = 2.5; + maxDeployDis = 5.0; +}; + +datablock ItemData(InventoryDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_inventory.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "InventoryDeployableImage"; + pickUpName = "an Antidote Station"; + heatSignature = 0; + + computeCRC = true; + emap = true; + +}; + +// -------------------------------------------- +// deployable motion sensor + +datablock SensorData(DeployMotionSensorObj) +{ + detects = true; + detectsUsingLOS = true; + detectsActiveJammed = false; + detectsPassiveJammed = true; + detectsCloaked = true; + detectionPings = false; + detectMinVelocity = 2; + detectRadius = 60; +}; + +datablock StaticShapeData(DeployedMotionSensor) : StaticShapeDamageProfile +{ + className = Sensor; + shapeFile = "deploy_sensor_motion.dts"; + maxDamage = 0.6; + destroyedLevel = 0.6; + disabledLevel = 0.4; + explosion = DeployablesExplosion; + dynamicType = $TypeMasks::SensorObjectType; + + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Motion Sensor'; + targetTypeTag = ''; + sensorData = DeployMotionSensorObj; + sensorRadius = DeployMotionSensorObj.detectRadius; + sensorColor = "9 136 255"; + deployAmbientThread = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock ShapeBaseImageData(MotionSensorDeployableImage) +{ + shapeFile = "pack_deploy_sensor_motion.dts"; + item = MotionSensorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedMotionSensor; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + maxDepSlope = 360; + deploySound = MotionSensorDeploySound; + emap = true; + heatSignature = 1; + + minDeployDis = 0.5; + maxDeployDis = 5.0; //meters from body +}; + +datablock ItemData(MotionSensorDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_sensor_motion.dts"; + mass = 2.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "MotionSensorDeployableImage"; + pickUpName = "a motion sensor pack"; + + computeCRC = true; + emap = true; + heatSignature = 0; + + //maxSensors = 3; + maxSensors = 2; +}; + +// -------------------------------------------- +// deployable pulse sensor + +datablock SensorData(DeployPulseSensorObj) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 150; +}; + +datablock StaticShapeData(DeployedPulseSensor) : StaticShapeDamageProfile +{ + className = Sensor; + shapeFile = "deploy_sensor_pulse.dts"; + maxDamage = 0.6; + destroyedLevel = 0.6; + disabledLevel = 0.4; + explosion = DeployablesExplosion; + dynamicType = $TypeMasks::SensorObjectType; + + deployedObject = true; + + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploypulsesensor"; + targetNameTag = 'Deployable'; + targetTypeTag = 'Pulse Sensor'; + sensorData = DeployPulseSensorObj; + sensorRadius = DeployPulseSensorObj.detectRadius; + sensorColor = "255 194 9"; + deployAmbientThread = true; + + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +datablock ShapeBaseImageData(PulseSensorDeployableImage) +{ + shapeFile = "pack_deploy_sensor_pulse.dts"; + item = PulseSensorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = DeployedPulseSensor; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + deploySound = SensorDeploySound; + + maxDepSlope = 40; + emap = true; + heatSignature = 0; + + minDeployDis = 0.5; + maxDeployDis = 5.0; //meters from body +}; + +datablock ItemData(PulseSensorDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_sensor_pulse.dts"; + mass = 2.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "PulseSensorDeployableImage"; + pickUpName = "a pulse sensor pack"; + + computeCRC = true; + emap = true; + + maxSensors = 2; +}; + +// -------------------------------------------- +// deployable outdoor turret + +datablock ShapeBaseImageData(TurretOutdoorDeployableImage) +{ + mass = 15; + + shapeFile = "pack_deploy_turreto.dts"; + item = TurretOutdoorDeployable; + mountPoint = 1; + offset = "0 0 0"; + deployed = TurretDeployedOutdoor; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + maxDamage = 4.5; + destroyedLevel = 4.5; + disabledLevel = 4.0; + + isLarge = true; + emap = true; + + maxDepSlope = 40; + deploySound = TurretDeploySound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; //meters from body +}; + +datablock ItemData(TurretOutdoorDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_turreto.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "TurretOutdoorDeployableImage"; + pickUpName = "a landspike turret pack"; + + computeCRC = true; + emap = true; + +}; + +// -------------------------------------------- +// deployable indoor turret (3 varieties -- floor, wall and ceiling) + +datablock ShapeBaseImageData(TurretIndoorDeployableImage) +{ + mass = 15; + + shapeFile = "pack_deploy_turreti.dts"; + item = TurretIndoorDeployable; + mountPoint = 1; + offset = "0 0 0"; + + stateName[0] = "Idle"; + stateTransitionOnTriggerDown[0] = "Activate"; + + stateName[1] = "Activate"; + stateScript[1] = "onActivate"; + stateTransitionOnTriggerUp[1] = "Idle"; + + isLarge = true; + emap = true; + + maxDepSlope = 360; + deploySound = TurretDeploySound; + + minDeployDis = 0.5; + maxDeployDis = 5.0; //meters from body +}; + +datablock ItemData(TurretIndoorDeployable) +{ + className = Pack; + catagory = "Deployables"; + shapeFile = "pack_deploy_turreti.dts"; + mass = 3.0; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 1; + rotate = false; + image = "TurretIndoorDeployableImage"; + pickUpName = "a spider clamp turret pack"; + + computeCRC = true; + emap = true; + +}; + +// -------------------------------------------- +// miscellaneous yet handy functions + +function posFromTransform(%transform) +{ + // the first three words of an object's transform are the object's position + %position = getWord(%transform, 0) @ " " @ getWord(%transform, 1) @ " " @ getWord(%transform, 2); + return %position; +} + +function rotFromTransform(%transform) +{ + // the last four words of an object's transform are the object's rotation + %rotation = getWord(%transform, 3) @ " " @ getWord(%transform, 4) @ " " @ getWord(%transform, 5) @ " " @ getWord(%transform, 6); + return %rotation; +} + +function posFromRaycast(%transform) +{ + // the 2nd, 3rd, and 4th words returned from a successful raycast call are the position of the point + %position = getWord(%transform, 1) @ " " @ getWord(%transform, 2) @ " " @ getWord(%transform, 3); + return %position; +} + +function normalFromRaycast(%transform) +{ + // the 5th, 6th and 7th words returned from a successful raycast call are the normal of the surface + %norm = getWord(%transform, 4) @ " " @ getWord(%transform, 5) @ " " @ getWord(%transform, 6); + return %norm; +} + +function addToDeployGroup(%object) +{ + // all deployables should go into a special group for AI purposes + %depGroup = nameToID("MissionCleanup/Deployables"); + if (%depGroup <= 0) { + %depGroup = new SimGroup("Deployables"); + MissionCleanup.add(%depGroup); + } + %depGroup.add(%object); +} + + + +function Deployables::searchView(%obj, %searchRange, %mask) +{ + // get the eye vector and eye transform of the player + %eyeVec = %obj.getEyeVector(); + %eyeTrans = %obj.getEyeTransform(); + + // extract the position of the player's camera from the eye transform (first 3 words) + %eyePos = posFromTransform(%eyeTrans); + + // normalize the eye vector + %nEyeVec = VectorNormalize(%eyeVec); + + // scale (lengthen) the normalized eye vector according to the search range + %scEyeVec = VectorScale(%nEyeVec, %searchRange); + + // add the scaled & normalized eye vector to the position of the camera + %eyeEnd = VectorAdd(%eyePos, %scEyeVec); + +// %pack = %obj.getMountedImage($BackpackSlot); + +//if (%pack.deployed.ClassName $= "mspine") +// { +// %mask2 = $TypeMasks::StaticObjectType; +// %searchResult = containerRayCast(%eyePos, %eyeEnd, %mask2, 0); +// if ((%searchResult) && (%searchResult.getType() & $TypeMasks::StaticObjectType)) +// { +// if (%searchResult.getDataBlock().className $= "mspine") +// return %searchResult; +// else if (%searchResult.getDataBlock().className $= "spine") +// return %searchResult; +// else if (%searchResult.getDataBlock().className $= "Wall") +// return %searchResult; +// else if (%searchResult.getDataBlock().className $= "wWall") +// return %searchResult; +// else if (%searchResult.getDataBlock().className $= "item") +// return %searchResult; +// else if (%searchResult.getDataBlock().className $= "floor") +// return %searchResult; +// %searchResult = ""; +// } +// } + + // see if anything gets hit + %searchResult = containerRayCast(%eyePos, %eyeEnd, %mask, 0); + if (%searchResult) + %pos2 = getWords(%searchResult, 1, 3); + else + %pos2 = %eyeEnd; + %searchResult2 = containerRayCast(%eyePos, %pos2, $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType, 0); + if (%searchResult2) { + if (%searchResult2.getDataBlock().className $= "wWall") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "Wall") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "spine") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "mspine") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "item") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "floor") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "tree") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "crate") + return %searchResult2; + else if (%searchResult2.getDataBlock().className $= "decoration") { + if (%searchResult2.getDataBlock().getName() $= "DeployedDecoration6") + return %searchResult2; + else + return; // Don't deploy on or through other decorations, since there are problems with surface normals + } + else if (%searchResult2.getType() & $TypeMasks::ForceFieldObjectType) + return; // Don't deploy inside or through forcefields + } + return %searchResult; +} + +//-----------------------// +// Deployable Procedures // +//-----------------------// + +//------------------------------------------------- +function ShapeBaseImageData::testMaxDeployed(%item, %plyr) { + if (%item.item $= TurretOutdoorDeployable + || %item.item $= TurretIndoorDeployable + || %item.item $= TurretBasePack + || %item.item $= TurretLaserDeployable + || %item.item $= TurretMissileRackDeployable + || %item.item $= TurretMpm_Anti_Deployable + || %item.item $= DiscTurretDeployable) + %itemCount = countTurretsAllowed(%item.item); + else + %itemCount = $TeamDeployableMax[%item.item]; + + return $TeamDeployedCount[%plyr.team, %item.item] >= %itemCount; +} + +//------------------------------------------------- +function ShapeBaseImageData::testNoSurfaceInRange(%item, %plyr) +{ + return ! Deployables::searchView(%plyr, $MaxDeployDistance, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType); +} + +//------------------------------------------------- +function ShapeBaseImageData::testSlopeTooGreat(%item) +{ + if (%item.surface) + { + return getTerrainAngle(%item.surfaceNrm) > %item.maxDepSlope; + } +} + +//------------------------------------------------- +function ShapeBaseImageData::testSelfTooClose(%item, %plyr) +{ + InitContainerRadiusSearch(%item.surfacePt, $MinDeployDistance, $TypeMasks::PlayerObjectType); + + return containerSearchNext() == %plyr; +} + +//------------------------------------------------- +function ShapeBaseImageData::testObjectTooClose(%item) { + %mask = ($TypeMasks::VehicleObjectType | $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType | + $TypeMasks::PlayerObjectType | $TypeMasks::TurretObjectType); + + InitContainerRadiusSearch(%item.surfacePt,$MinDeployDistance,%mask); + + // TODO - update this + while ((%test = containerSearchNext()) != 0) { + %className = %test.getDataBlock().className; + if (vectorDist(%item.surfacePt,%test.getPosition()) < $MinDeployDistance + && %className !$= "Wall" && %className !$= "wWall" && %className !$= "spine" && %className !$= "mspine" + && %className !$= "floor" && %className !$= "tree" && %className !$= "crate" && %className !$= "decoration" + && %className !$= "light" && %className !$= "decontarget" && %className !$= "item" + && %className !$= "pack" ) + return %test; + } + return 0; +} + +//------------------------------------------------- +function TurretOutdoorDeployableImage::testNoTerrainFound(%item) +{ + return %item.surface.getClassName() !$= TerrainBlock; +} + +function ShapeBaseImageData::testNoTerrainFound(%item, %surface) +{ + //don't check this for non-Landspike turret deployables +} + +//------------------------------------------------- +function TurretIndoorDeployableImage::testNoInteriorFound(%item) { + if (%item.surface.getType() & $TypeMasks::StaticShapeObjectType) { + %className = %item.surface.getDataBlock().className; + if (%className $= "Wall") + return 0; + else if (%className $= "wWall") + return 0; + else if (%className $= "spine") + return 0; + else if (%className $= "mspine") + return 0; + else if (%className $= "item") + return 0; + else if (%className $= "floor") + return 0; + else if (%className $= "tree") + return 0; + else if (%className $= "crate") + return 0; + else if (%className $= "decoration") + return 0; + } + return %item.surface.getClassName() !$= InteriorInstance; +} + +function ShapeBaseImageData::testNoInteriorFound(%item, %surface) +{ + //don't check this for non-Clasping turret deployables +} + +//------------------------------------------------- +function TurretIndoorDeployableImage::testHavePurchase(%item, %xform) +{ + %footprintRadius = 0.34; + %collMask = $TypeMasks::InteriorObjectType; + return %item.deployed.checkDeployPurchase(%xform, %footprintRadius, %collMask); +} + +function ShapeBaseImageData::testHavePurchase(%item, %xform) +{ + //don't check this for non-Clasping turret deployables + return true; +} + +//------------------------------------------------- +function ShapeBaseImageData::testInventoryTooClose(%item, %plyr) +{ + return false; +} + +function InventoryDeployableImage::testInventoryTooClose(%item, %plyr) +{ + InitContainerRadiusSearch(%item.surfacePt, $InventorySpaceRadius, $TypeMasks::StaticShapeObjectType); + + // old function was only checking whether the first object found was a turret -- also wasn't checking + // which team the object was on + %turretInRange = false; + while((%found = containerSearchNext()) != 0) + { + %foundName = %found.getDataBlock().getName(); + if ( (%foundName $= DeployedStationInventory) ) + if (%found.team == %plyr.team) + { + %turretInRange = true; + break; + } + } + return %turretInRange; +} + + +// TODO - keep this up to date +function isDeployedTurret(%obj) { + %dataBlockName = %obj.getDataBlock().getName(); + if ((%dataBlockName $= TurretDeployedFloorIndoor) + || (%dataBlockName $= TurretDeployedWallIndoor) + || (%dataBlockName $= TurretDeployedCeilingIndoor) + || (%dataBlockName $= TurretDeployedOutdoor) + || (%dataBlockName $= TurretDeployedBase) + || (%dataBlockName $= LaserDeployed) + || (%dataBlockName $= MissileRackTurretDeployed) + || (%dataBlockName $= Mpm_anti_TurretDeployed) + || (%dataBlockName $= DiscTurretDeployed)) + return 1; + return 0; +} + +// TODO - keep this up to date +function isDeployableTurret(%item) { + if ((%item $= TurretIndoorDeployable) + || (%item $= TurretOutdoorDeployable) + || (%item $= TurretBasePack) + || (%item $= TurretLaserDeployable) + || (%item $= TurretMissileRackDeployable) + || (%item $= TurretMpm_anti_Deployable) + || (%item $= DiscTurretDeployable)) + return 1; + return 0; +} + +function TurretIndoorDeployableImage::testTurretTooClose(%item, %plyr) { + InitContainerRadiusSearch(%item.surfacePt, $TurretIndoorSpaceRadius, $TypeMasks::StaticShapeObjectType); + // old function was only checking whether the first object found was a turret -- also wasn't checking + // which team the object was on + %turretInRange = false; + while((%found = containerSearchNext()) != 0) { + if (isDeployedTurret(%found)) { + if (%found.team == %plyr.team) { + %turretInRange = true; + break; + } + } + } + return %turretInRange; +} + +function TurretOutdoorDeployableImage::testTurretTooClose(%item, %plyr) { + InitContainerRadiusSearch(%item.surfacePt, $TurretOutdoorSpaceRadius, $TypeMasks::StaticShapeObjectType); + // old function was only checking whether the first object found was a turret -- also wasn't checking + // which team the object was on + %turretInRange = false; + while((%found = containerSearchNext()) != 0) { + if (isDeployedTurret(%found)) { + if (%found.team == %plyr.team) { + %turretInRange = true; + break; + } + } + } + return %turretInRange; +} + +function TurretDeployableImage::testTurretTooClose(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretTooClose(%item, %plyr); +} + +function TurretLaserDeployableImage::testTurretTooClose(%item, %plyr) { + return TurretIndoorDeployableImage::testTurretTooClose(%item, %plyr); +} + +function TurretMissileRackDeployableImage::testTurretTooClose(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretTooClose(%item, %plyr); +} + + +function TurretMpm_Anti_DeployableImage::testTurretTooClose(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretTooClose(%item, %plyr); +} + + +function DiscTurretDeployableImage::testTurretTooClose(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretTooClose(%item, %plyr); +} + +function ShapeBaseImageData::testTurretTooClose(%item, %plyr) +{ + //don't check this for non-turret deployables +} + +//[most] +function ShapeBaseImageData::testSurfaceTooNarrow(%item,%surface) +{ + //don't check this for non-v-pad deployables +} +//[most] + +// TODO - keep this up to date + +//------------------------------------------------- +function TurretIndoorDeployableImage::testTurretSaturation(%item) { + %highestDensity = 0; + InitContainerRadiusSearch(%item.surfacePt, $TurretIndoorSphereRadius, $TypeMasks::StaticShapeObjectType); + %found = containerSearchNext(); + while(%found) { + if (isDeployedTurret(%found)) { + //found one + %numTurretsNearby++; + %nearbyDensity = testNearbyDensity(%found, $TurretIndoorSphereRadius); + if (%nearbyDensity > %highestDensity) + %highestDensity = %nearbyDensity; + } + %found = containerSearchNext(); + } + if (%numTurretsNearby > %highestDensity) + %highestDensity = %numTurretsNearby; + return %highestDensity > $TurretIndoorMaxPerSphere; +} + +function TurretOutdoorDeployableImage::testTurretSaturation(%item) { + %highestDensity = 0; + InitContainerRadiusSearch(%item.surfacePt, $TurretOutdoorSphereRadius, $TypeMasks::StaticShapeObjectType); + %found = containerSearchNext(); + while(%found) { + if (isDeployedTurret(%found)) { + //found one + %numTurretsNearby++; + %nearbyDensity = testNearbyDensity(%found, $TurretOutdoorSphereRadius); + if (%nearbyDensity > %highestDensity) + %highestDensity = %nearbyDensity; + } + %found = containerSearchNext(); + } + if (%numTurretsNearby > %highestDensity) + %highestDensity = %numTurretsNearby; + return %highestDensity > $TurretOutdoorMaxPerSphere; +} + +function TurretDeployableImage::testTurretSaturation(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretSaturation(%item, %plyr); +} + +function TurretLaserDeployableImage::testTurretSaturation(%item, %plyr) { + return TurretIndoorDeployableImage::testTurretSaturation(%item, %plyr); +} + +function TurretMissileRackDeployableImage::testTurretSaturation(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretSaturation(%item, %plyr); +} + + +function TurretMpm_Anti_DeployableImage::testTurretSaturation(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretSaturation(%item, %plyr); +} + +function DiscTurretDeployableImage::testTurretSaturation(%item, %plyr) { + return TurretOutdoorDeployableImage::testTurretSaturation(%item, %plyr); +} + +function ShapeBaseImageData::testTurretSaturation(%item, %surfacePt) +{ + //don't check this for non-turret deployables +} + +function testNearbyDensity(%item, %radius) { + //this checks how many turrets are in adjacent spheres in case placing a new one overloads them. + %surfacePt = posFromTransform(%item.getTransform()); + %turretCount = 0; + InitContainerRadiusSearch(%surfacePt, %radius, $TypeMasks::StaticShapeObjectType); + %found = containerSearchNext(); + while(%found) { + if (isDeployedTurret(%found)) + %turretCount++; + %found = containerSearchNext(); + } + return %turretCount; +} + +//------------------------------------------------- +//if this function, or any of the included tests are changed, those changes need to be reflected in function: +//AIODeployEquipment::weight(%this, %client, %level), found in aiObjectives.cs --tinman + +function ShapeBaseImageData::testInvalidDeployConditions(%item, %plyr, %slot) { + cancel(%plyr.deployCheckThread); + %disqualified = $NotDeployableReason::None; //default + $MaxDeployDistance = %item.maxDeployDis; + $MinDeployDistance = %item.minDeployDis; + + %pack = %plyr.getMountedImage($BackpackSlot); + + if (%pack.deployed.className $= "decoration") { + if (%plyr.packSet == 6) + $MinDeployDistance = 2; + if (%plyr.packSet == 10) + $MinDeployDistance = 5; + if (%plyr.packSet == 11) + $MinDeployDistance = 4; + } + + %surface = Deployables::searchView(%plyr, + $MaxDeployDistance, + ($TypeMasks::TerrainObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::StaticShapeObjectType)); + if (%surface) { + %surfacePt = posFromRaycast(%surface); + %surfaceNrm = normalFromRaycast(%surface); + + // Check that point to see if anything is obstructing it... + %eyeTrans = %plyr.getEyeTransform(); + %eyePos = posFromTransform(%eyeTrans); + + %searchResult = containerRayCast(%eyePos, %surfacePt, -1, %plyr); + if (!%searchResult) { + %item.surface = %surface; + %item.surfacePt = %surfacePt; + %item.surfaceNrm = %surfaceNrm; + } + else { + if (checkPositions(%surfacePT, posFromRaycast(%searchResult))) { + %item.surface = %surface; + %item.surfacePt = %surfacePt; + %item.surfaceNrm = %surfaceNrm; + } + else { + // Deploy through water + if (%searchResult.getType() & $TypeMasks::WaterObjectType) { + %item.surface = %surface; + %item.surfacePt = %surfacePt; + %item.surfaceNrm = %surfaceNrm; + } + // Redundant - already checking StaticShape above now (v0.62a) +// // Deploy on StaticShapes +// else if (%searchResult.getType() & $TypeMasks::StaticShapeObjectType) { +// %item.surface = %surface; +// %item.surfacePt = %surfacePt; +// %item.surfaceNrm = %surfaceNrm; +// } + // Don't set the item +// TODO - make a proper $NotDeployableReason for this + else + %disqualified = $NotDeployableReason::MaxDeployed; + } + } + if (!getTerrainAngle(%surfaceNrm) && %item.flatMaxDeployDis !$= "") { + $MaxDeployDistance = %item.flatMaxDeployDis; + $MinDeployDistance = %item.flatMinDeployDis; + } + } + + %item.surfaceinher = 0; + if (%item.surface.needsFit == 1) { + %item.surfaceinher = 1; +// new code + %mask = invFace(%item.surfaceNrm); + %narrower = vectorMultiply(%mask,%item.surface.getRealSize()); + %subject = vectorNormalize(topVec(%narrower)); + %item.surfaceNrm2 = realVec(%item.surface,%subject); +// old code +// if (vAbs(%item.surfaceNrm) $= "0 0 1") +// %item.surfaceNrm2 = realVec(%item.surface,"1 0 0"); +// else +// %item.surfaceNrm2 = realVec(%item.surface,"0 0 1"); + %item.surfaceNrm = VectorNormalize(realVec(%item.surface,%surfaceNrm)); + %item.surfaceNrm2 = VectorNormalize(%item.surfaceNrm2); + %mCenter = "0 0 -0.5"; + %className = %item.surface.getDataBlock().className; + if (%className !$= "tree" && %className !$= "crate" && %className !$= "vpad") + %item.surfacePt = link(%item.surface,%surfaceNrm,%surfacePt,VectorScale(getjoint(%item),0.5),%mCenter); + if (%className $= "decoration") { + if (%item.surface.getDataBlock().getName() $= "DeployedDecoration6") { + %item.surfacePt = vectorAdd(%item.surfacePt,vectorScale(realVec(%item.surface,"0 0 1"),3.3)); + %item.surfaceNrm = realVec(%item.surface,"0 0 1"); + } + } + } + + if (%item.testMaxDeployed(%plyr)) + { + %disqualified = $NotDeployableReason::MaxDeployed; + } + else if (%item.testNoSurfaceInRange(%plyr)) + { + %disqualified = $NotDeployableReason::NoSurfaceFound; + } + else if (%item.testNoTerrainFound(%surface)) + { + %disqualified = $NotDeployableReason::NoTerrainFound; + } + else if (%item.testNoInteriorFound()) + { + %disqualified = $NotDeployableReason::NoInteriorFound; + } + else if (%item.testSurfaceTooNarrow(%surface)) + { + %disqualified = $NotDeployableReason::SurfaceTooNarrow; + } + else if (%item.testSlopeTooGreat(%surface, %surfaceNrm)) + { + %disqualified = $NotDeployableReason::SlopeTooGreat; + } + else if (%item.testSelfTooClose(%plyr, %surfacePt)) + { + %disqualified = $NotDeployableReason::SelfTooClose; + } + else if (%item.testObjectTooClose(%surfacePt,%plyr)) + { + %disqualified = $NotDeployableReason::ObjectTooClose; + } + else if (%item.testTurretTooClose(%plyr) && $Host::Purebuild != 1) + { + %disqualified = $NotDeployableReason::TurretTooClose; + } + else if (%item.testInventoryTooClose(%plyr)) + { + %disqualified = $NotDeployableReason::InventoryTooClose; + } + else if (%item.testTurretSaturation() && $Host::Purebuild != 1) + { + %disqualified = $NotDeployableReason::TurretSaturation; + } + else if (%plyr.client.CannotDeploy) + { + %disqualified = $NotDeployableReason::PriveledgesRevoked; + } + else if (%disqualified == $NotDeployableReason::None) + { + // Test that there are no obstructing objects that this object + // will intersect with + // + %rot = %item.getInitialRotation(%plyr); + if (%item.deployed.className $= "DeployedTurret") + { + %xform = %item.deployed.getDeployTransform(%item.surfacePt, %item.surfaceNrm); + } + else + { + %xform = %surfacePt SPC %rot; + } + } + + if (%plyr.getMountedImage($BackpackSlot) == %item) //player still have the item? + { + if (%disqualified) + activateDeploySensorRed(%plyr); + else + activateDeploySensorGrn(%plyr); + + if (%plyr.client.deployPack == true) { + %item.attemptDeploy(%plyr, %slot, %disqualified); + } + else + { + %plyr.deployCheckThread = %item.schedule(25, "testInvalidDeployConditions", %plyr, %slot); //update checks every 50 milliseconds + } + } + else + deactivateDeploySensor(%plyr); +} + +function checkPositions(%pos1, %pos2) +{ + %passed = true; + if ((mFloor(getWord(%pos1, 0)) - mFloor(getWord(%pos2,0)))) + %passed = false; + if ((mFloor(getWord(%pos1, 1)) - mFloor(getWord(%pos2,1)))) + %passed = false; + if ((mFloor(getWord(%pos1, 2)) - mFloor(getWord(%pos2,2)))) + %passed = false; + return %passed; +} + +function ShapeBaseImageData::attemptDeploy(%item, %plyr, %slot, %disqualified) +{ + deactivateDeploySensor(%plyr); + Deployables::displayErrorMsg(%item, %plyr, %slot, %disqualified); +} + +function activateDeploySensorRed(%pl) +{ + if (%pl.deploySensor !$= "red") + { + messageClient(%pl.client, 'msgDeploySensorRed', ""); + %pl.deploySensor = "red"; + } +} + +function activateDeploySensorGrn(%pl) +{ + if (%pl.deploySensor !$= "green") + { + messageClient(%pl.client, 'msgDeploySensorGrn', ""); + %pl.deploySensor = "green"; + } +} + +function deactivateDeploySensor(%pl) +{ + if (%pl.deploySensor !$= "") + { + messageClient(%pl.client, 'msgDeploySensorOff', ""); + %pl.deploySensor = ""; + } +} + +function Deployables::displayErrorMsg(%item, %plyr, %slot, %error) +{ + deactivateDeploySensor(%plyr); + + %errorSnd = '~wfx/misc/misc.error.wav'; + switch (%error) + { + case $NotDeployableReason::None: + if (isObject(%plyr.client)) { + if (!spamCheck(%plyr.client)) { + %deplObj = %item.onDeploy(%plyr, %slot); + %deplObj.depTime = getSimTime(); + // TODO - temporary - remove + if ($TimedDisCenter !$= "") { + if (vectorLen(vectorDist($TimedDisCenter,%deplObj.getPosition())) < 150 && %plyr.client != nameToID(LocalClientConnection)) + %deplObj.timedDis = %deplObj.getDataBlock().schedule(1 * 5 * 1000,disassemble,0,%deplObj); + } + // ---- + messageClient(%plyr.client, 'MsgTeamDeploySuccess', ""); + return; + } + else if ($Host::Prison::DeploySpamRemoveRecentMS !$= "" && $Host::Prison::DeploySpamRemoveRecentMS > 0) { + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (%obj.getOwner() == %plyr.client && getSimTime() - $Host::Prison::DeploySpamRemoveRecentMS < %obj.depTime) { + %obj.getDataBlock().schedule(50 * %disCount++,disassemble,%plyr,%obj); + } + } + } + } + else { + %item.onDeploy(%plyr, %slot); + messageClient(%plyr.client, 'MsgTeamDeploySuccess', ""); + return; + } + case $NotDeployableReason::NoSurfaceFound: + %msg = '\c2Item must be placed within reach.%1'; + + case $NotDeployableReason::MaxDeployed: + %msg = '\c2Your team\'s control network has reached its capacity for this item.%1'; + + case $NotDeployableReason::SlopeTooGreat: + %msg = '\c2Surface is too steep to place this item on.%1'; + + case $NotDeployableReason::SelfTooClose: + %msg = '\c2You are too close to the surface you are trying to place the item on.%1'; + + case $NotDeployableReason::ObjectTooClose: + %msg = '\c2You cannot place this item so close to another object.%1'; + + case $NotDeployableReason::NoTerrainFound: + %msg = '\c2You must place this on outdoor terrain.%1'; + + case $NotDeployableReason::NoInteriorFound: + %msg = '\c2You must place this on a solid surface.%1'; + + case $NotDeployableReason::TurretTooClose: + %msg = '\c2Interference from a nearby turret prevents placement here.%1'; + + case $NotDeployableReason::TurretSaturation: + %msg = '\c2There are too many turrets nearby.%1'; + + case $NotDeployableReason::SurfaceTooNarrow: + %msg = '\c2There is not adequate surface to clamp to here.%1'; + + case $NotDeployableReason::InventoryTooClose: + %msg = '\c2Interference from a nearby inventory prevents placement here.%1'; + + case $NotDeployableReason::PriveledgesRevoked: + %msg = '\c2An administrator has revoked your ability to deploy anything, therefore, you cannot deploy.%1'; + default: + %msg = '\c2Deploy failed.'; + } + messageClient(%plyr.client, 'MsgDeployFailed', %msg, %errorSnd); +} + +function ShapeBaseImageData::onActivate(%data, %obj, %slot) +{ + if (%obj.getMountedImage($BackpackSlot).item $= "InventoryDeployable") + { + %pos = %obj.getMuzzlePoint($WeaponSlot); + %vec = %obj.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos, vectorscale(%vec, $MaxDeployableDistance)); + %stuff = containerraycast(%pos, %targetpos, $typemasks::staticshapeobjecttype, %sender.player); + %stuff = getword(%stuff, 0); + if(isObject(%stuff)) + { + if(%stuff.getDatablock().getName() $= "DeployedStationInventory") + { + if(%stuff.antidotes < $Host::AntidoteStationMaxAntidotes) + { + %stuff.antidotes = $Host::AntidoteStationMaxAntidotes; + messageClient(%obj.client, "", "\c2Antidote station refilled."); + serverPlay3d(ChaingunDryFire, %stuff.getTransform()); + } + else + messageClient(%obj.client, "", "\c2Antidote station is already at max antidotes.~wfx/misc/misc.error.wav"); + return; + } + } + } + + //Tinman - apparently, anything that uses the generic onActivate() method is a deployable. + //repair packs, cloak packs, shield, etc... all overload this method... + %data.testInvalidDeployConditions(%obj, %slot); + + //whether the test passed or not, reset the image trigger (deployables don't have an on/off toggleable state) + %obj.setImageTrigger(%slot, false); +} + + + + +function ShapeBaseImageData::onDeploy(%item, %plyr, %slot) +{ + if (%item.item $= "MotionSensorDeployable" || %item.item $= "PulseSensorDeployable") + { + %plyr.deploySensors--; + %plyr.client.updateSensorPackText(%plyr.deploySensors); + if (%plyr.deploySensors <= 0) + { + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + } + } + else + { + // take the deployable off the player's back and out of inventory + %plyr.unmountImage(%slot); + %plyr.decInventory(%item.item, 1); + } + + // create the actual deployable !!!!!!!!!!!!!!!!STOP AND LOOK AT ME!!!!!!!!!!!!!!!!! ???????WHY?????? + %rot = %item.getInitialRotation(%plyr); + if (%item.deployed.className $= "DeployedTurret") + %className = "Turret"; + else + %className = "StaticShape"; + + %deplObj = new (%className)() { + dataBlock = %item.deployed; + }; + + if(%item.deployed $= "DeployedStationInventory") + %deplObj.antidotes = $Host::AntidoteStationMaxAntidotes; + + + + // set orientation + if (%className $= "Turret") + %deplObj.setDeployRotation(%item.surfacePt, %item.surfaceNrm); + else + %deplObj.setTransform(VectorAdd(%item.surfacePt, %plac) SPC %rot); + + + // set the recharge rate right away + if (%deplObj.getDatablock().rechargeRate) + %deplObj.setRechargeRate(%deplObj.getDatablock().rechargeRate); + + // set team, owner, and handle + %deplObj.team = %plyr.client.Team; + %deplObj.setOwner(%plyr); + + // set the sensor group if it needs one + if (%deplObj.getTarget() != -1) + setTargetSensorGroup(%deplObj.getTarget(), %plyr.client.team); + + // place the deployable in the MissionCleanup/Deployables group (AI reasons) + addToDeployGroup(%deplObj); + + //let the AI know as well... + AIDeployObject(%plyr.client, %deplObj); + + // play the deploy sound + serverPlay3D(%item.deploySound, %deplObj.getTransform()); + + // increment the team count for this deployed object + + $TeamDeployedCount[%plyr.team, %item.item]++; + + if (%classname !$= "Item") + %deplObj.deploy(); + + addDSurface(%item.surface,%deplObj); + return %deplObj; +} + +function ShapeBaseImageData::getInitialRotation(%item, %plyr) +{ + return rotFromTransform(%plyr.getTransform()); +} + +function MotionSensorDeployableImage::getInitialRotation(%item, %plyr) +{ + %rotAxis = vectorNormalize(vectorCross(%item.surfaceNrm, "0 0 1")); + if (getWord(%item.surfaceNrm, 2) == 1 || getWord(%item.surfaceNrm, 2) == -1) + %rotAxis = vectorNormalize(vectorCross(%item.surfaceNrm, "0 1 0")); + return %rotAxis SPC mACos(vectorDot(%item.surfaceNrm, "0 0 1")); +} + +function MotionSensorDeployable::onPickup(%this, %pack, %player, %amount) +{ + // %this = Sensor pack datablock + // %pack = Sensor pack object number + // %player = player + // %amount = amount picked up (1) + + if (%pack.sensors $= "") + { + // assume that this is a pack that has been placed in a mission + // this case was handled in ::onInventory below (max sensors); + } + else + { + // find out how many sensor were in the pack + %player.deploySensors = %pack.sensors; + %player.client.updateSensorPackText(%player.deploySensors); + } +} + +function MotionSensorDeployable::onThrow(%this,%pack,%player) +{ + // %this = Sensor pack datablock + // %pack = Sensor pack object number + // %player = player + + %player.throwSensorPack = 1; + %pack.sensors = %player.deploySensors; + %player.deploySensors = 0; + %player.client.updateSensorPackText(%player.deploySensors); + // do the normal ItemData::onThrow stuff -- sound and schedule deletion + serverPlay3D(ItemThrowSound, %player.getTransform()); + %pack.schedulePop(); +} + +function MotionSensorDeployable::onInventory(%this,%player,%value) +{ + // %this = Sensor pack datablock + // %player = player + // %value = 1 if gaining a pack, 0 if losing a pack + + if (%player.getClassName() $= "Player") + { + if (%value) + { + // player picked up or bought a motion sensor pack + %player.deploySensors = %this.maxSensors; + %player.client.updateSensorPackText(%player.deploySensors); + } + else + { + // player dropped or sold a motion sensor pack + if (%player.throwSensorPack) + { + // player threw the pack + %player.throwSensorPack = 0; + // everything handled in ::onThrow above + } + else + { + //the pack was sold at an inventory station, or unmounted because the player + // used all the sensors + %player.deploySensors = 0; + %player.client.updateSensorPackText(%player.deploySensors); + } + } + } + Pack::onInventory(%this,%player,%value); +} + +function PulseSensorDeployable::onPickup(%this, %pack, %player, %amount) +{ + // %this = Sensor pack datablock + // %pack = Sensor pack object number + // %player = player + // %amount = amount picked up (1) + + if (%pack.sensors $= "") + { + // assume that this is a pack that has been placed in a mission + // this case was handled in ::onInventory below (max sensors); + } + else + { + // find out how many sensor were in the pack + %player.deploySensors = %pack.sensors; + %player.client.updateSensorPackText(%player.deploySensors); + } +} + +function PulseSensorDeployable::onThrow(%this,%pack,%player) +{ + // %this = Sensor pack datablock + // %pack = Sensor pack object number + // %player = player + + %player.throwSensorPack = 1; + %pack.sensors = %player.deploySensors; + %player.deploySensors = 0; + %player.client.updateSensorPackText(%player.deploySensors); + // do the normal ItemData::onThrow stuff -- sound and schedule deletion + serverPlay3D(ItemThrowSound, %player.getTransform()); + %pack.schedulePop(); +} + +function PulseSensorDeployable::onInventory(%this,%player,%value) +{ + // %this = Sensor pack datablock + // %player = player + // %value = 1 if gaining a pack, 0 if losing a pack + + if (%player.getClassName() $= "Player") + { + if (%value) + { + // player picked up or bought a motion sensor pack + %player.deploySensors = %this.maxSensors; + %player.client.updateSensorPackText(%player.deploySensors); + } + else + { + // player dropped or sold a motion sensor pack + if (%player.throwSensorPack) + { + // player threw the pack + %player.throwSensorPack = 0; + // everything handled in ::onThrow above + } + else + { + //the pack was sold at an inventory station, or unmounted because the player + // used all the sensors + %player.deploySensors = 0; + %player.client.updateSensorPackText(%player.deploySensors); + } + } + } + Pack::onInventory(%this,%player,%value); +} + +function TurretIndoorDeployableImage::getInitialRotation(%item, %plyr) +{ + %surfaceAngle = getTerrainAngle(%item.surfaceNrm); + if (%surfaceAngle > 155) + %item.deployed = TurretDeployedCeilingIndoor; + else if (%surfaceAngle > 45) + %item.deployed = TurretDeployedWallIndoor; + else + %item.deployed = TurretDeployedFloorIndoor; +} + +function TurretIndoorDeployable::onPickup(%this, %obj, %shape, %amount) +{ + // created to prevent console errors +} + +function TurretOutdoorDeployable::onPickup(%this, %obj, %shape, %amount) +{ + // created to prevent console errors +} + +function InventoryDeployable::onPickup(%this, %obj, %shape, %amount) +{ + // created to prevent console errors +} + +// --------------------------------------------------------------------------------------- +// deployed station functions +function DeployedStationInventory::onEndSequence(%data, %obj, %thread) +{ + Parent::onEndSequence(%data, %obj, %thread); + if (%thread == $DeployThread) + { + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.125 0.0 0.1 0.25 0.0 0.0 0.0 -0.7 0.0 0.0 0.0 1.0"; + }; + MissionCleanup.add(%trigger); + + %trans = %obj.getTransform(); + %vSPos = getWords(%trans,0,2); + %vRot = getWords(%trans,3,5); + %vAngle = getWord(%trans,6); + %matrix = VectorOrthoBasis(%vRot @ " " @ %vAngle + 0.0); + %yRot = getWords(%matrix, 3, 5); + %pos = vectorAdd(%vSPos, vectorScale(%yRot, -0.1)); + + %trigger.setTransform(%pos @ " " @ %vRot @ " " @ %vAngle); + + // associate the trigger with the station + %trigger.station = %obj; + %trigger.mainObj = %obj; + %trigger.disableObj = %obj; + %obj.trigger = %trigger; + } +} + +//-------------------------------------------------------------------------- +//DeployedMotionSensor: +//-------------------------------------------------------------------------- + +function DeployedMotionSensor::onDestroyed(%this, %obj, %prevState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + //%obj.hide(true); + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, MotionSensorDeployable]--; + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +//-------------------------------------------------------------------------- +//DeployedPulseSensor: +//-------------------------------------------------------------------------- +function PulseSensorDeployableImage::onActivate(%data, %obj, %slot) +{ + Parent::onActivate( %data, %obj, %slot ); + //%data.testInvalidDeployConditions(%obj, %slot); +} + +function DeployedPulseSensor::onDestroyed(%this, %obj, %prevState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, PulseSensorDeployable]--; + remDSurface(%obj); + %obj.schedule(300, "delete"); +} + +// --------------------------------------------------------------------------------------- +// deployed turret functions + +function DeployedTurret::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + // auto-mount the barrel + %obj.mountImage(%data.barrel, 0, false); +} + +function DeployedTurret::onDestroyed(%this, %obj, %prevState) { + if (%obj.isRemoved) + return; + if ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon || $DestroyableTurrets == 1) { + %obj.isRemoved = true; + %turType = %this.getName(); + // either it'll be an outdoor turret, or one of the three types of indoor turret + // (floor, ceiling, wall) + if (%turType $= "TurretDeployedOutdoor") + %turType = "TurretOutdoorDeployable"; + else if (%turType $= "TurretDeployedBase") + %turType = "TurretBasePack"; + else + %turType = "TurretIndoorDeployable"; + // decrement team count + $TeamDeployedCount[%obj.team, %turType]--; + remDSurface(%obj); + + if (%obj.getDataBlock().barrel $= "DeployableIndoorBarrel") { + %obj.Base.schedule(10, "delete"); + } + + %obj.schedule(700, "delete"); + } + Parent::onDestroyed(%this, %obj, %prevState); +} + +function countTurretsAllowed(%type) +{ + 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 turrets allowed + %maxPlayers = %teamPlayerCount[1] > %teamPlayerCount[2] ? %teamPlayerCount[1] : %teamPlayerCount[2]; + // each team can have 1 turret of each type (indoor/outdoor) for every 2 players + // minimum and maximums are defined in deployables.cs + %teamTurretMax = mFloor(%maxPlayers / 2); + if (%teamTurretMax < $TeamDeployableMin[%type]) + %teamTurretMax = $TeamDeployableMin[%type]; + else if (%teamTurretMax > $TeamDeployableMax[%type]) + %teamTurretMax = $TeamDeployableMax[%type]; + + return %teamTurretMax; +} + +function spamCheck(%cl) { + if ($Host::Prison::Enabled != true || $Host::Prison::DeploySpam != true || %cl.isAdmin || %cl.isSuperAdmin) + return false; + %simTime = getSimTime(); + if (%simTime - %cl.lastDeployTime > $Host::Prison::DeploySpamResetWarnCountTime * 1000) + %cl.spamCount = 0; // Reset spam count + if (%simTime - %cl.lastDeployTime < $Host::Prison::DeploySpamCheckTimeMS) { + %cl.spamCount++; + if (%cl.spamCount > $Host::Prison::DeploySpamWarnings) { + %cl.spamPunishCount++; + %cl.spamCount = 0; + %cl.lastDeployTime = 0; + %punishTime = $Host::Prison::DeploySpamTime; + %kills = 3; + if (%cl.spamPunishCount > %kills) { + if ($Host::Prison::DeploySpamMultiply > 0) { + %punishTime = (%cl.spamPunishCount - %kills) * $Host::Prison::DeploySpamTime; + if (%punishTime > $Host::Prison::DeploySpamMaxTime) + %punishTime = $Host::Prison::DeploySpamMaxTime; + } + // TODO - should really use some better formatting method... + if (%punishTime > 0) { + if (%punishTime >= 60) { + if (%punishTime > 60) { + %minutes = mFloor(%punishTime / 60); + messageClient(%cl,'msgClient','\c2You will do %1 minutes in jail for spamming deployables.',%minutes); + messageAllExcept(%cl,-1,'msgClient','\c2%1 will do %2 minutes in jail for spamming deployables.',%cl.name,%minutes); + } + else { + messageClient(%cl,'msgClient','\c2You will do 1 minute in jail for spamming deployables.'); + messageAllExcept(%cl,-1,'msgClient','\c2%1 will do 1 minute in jail for spamming deployables.',%cl.name); + } + } + else { + messageClient(%cl,'msgClient','\c2You will do %1 seconds in jail spamming.',%punishTime); + messageAllExcept(%cl,-1,'msgClient','\c2%1 will do %2 seconds in jail for spamming deployables.',%cl.name,%punishTime); + } + } + else { + messageClient(%cl,'msgClient','\c2You were put in jail for spamming deployables.'); + messageAllExcept(%cl,-1,'msgClient','\c2%1 was put in jail for spamming deployables.',%cl.name); + } + jailPlayer(%cl,false,%punishTime); + } + else { + %cl.player.scriptKill(99); + messageClient(%cl,'msgClient','\c2You were killed for spamming deployables.'); + messageAllExcept(%cl,-1,'msgClient','\c2%1 was killed for spamming deployables.',%cl.name); + } + return true; + } + // Warn player only if they've used up over half their warnings, to prevent message hud spam + else if (%cl.spamCount > mFloor(($Host::Prison::DeploySpamWarnings + 1) / 2)) { + centerPrint(%cl,"Do not spam, or face the consequences",2,1); + messageClient(%cl,'msgClient',"~wfx/misc/misc.error.wav"); + return true; + } + } + %cl.lastDeployTime = %simTime; + return false; +} + +//------------------------------ +// Deployable extra explosions +//------------------------------ + +datablock GrenadeProjectileData(DeployableFireball) { + projectileShapeName = "weapon_chaingun_ammocasing.dts"; + emitterDelay = -1; + directDamage = 0.0; + hasDamageRadius = true; + indirectDamage = 0.10; + damageRadius = 10.0; + radiusDamageType = $DamageType::Debris; + kickBackStrength = 1000; + bubbleEmitTime = 1.0; + + explosion = "PlasmaBoltExplosion"; + splash = PlasmaSplash; + explodeOnMaxBounce = true; + + velInheritFactor = 0.5; + + baseEmitter = DebrisFireEmitter; + smokeEmitter = DebrisSmokeEmitter; + + grenadeElasticity = 0.4; + grenadeFriction = 0.2; + armingDelayMS = 400; + muzzleVelocity = 20.00; + drag = 0.1; + + hasLight = true; + lightRadius = 3.0; + lightColor = "1 0.75 0.25"; +}; + +datablock StaticShapeData(DeployedLTarget) : StaticShapeDamageProfile { + className = "decontarget"; + shapeFile = "xmiscf.dts"; + + maxDamage = 0.5; + destroyedLevel = 0.5; + disabledLevel = 0.3; + + explosion = HandGrenadeExplosion; + expDmgRadius = 1.0; + expDamage = 0.05; + expImpulse = 200; + + dynamicType = $TypeMasks::StaticShapeObjectType; + deployedObject = true; + cmdCategory = "DSupport"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_deploymotionsensor"; + targetNameTag = 'Deployed DeconTarget'; + deployAmbientThread = true; + debrisShapeName = "debris_generic_small.dts"; + debris = DeployableDebris; + heatSignature = 0; +}; + +function DeployedLTarget::onDestroyed(%this,%obj,%prevState) { + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this,%obj,%prevState); + remDSurface(%obj); + %obj.schedule(500, "delete"); + if (isObject(%obj.lMain)) { + %obj.lMain.setDamageState(Destroyed); + } +} + +function fireBallExplode(%obj,%numFb) { + + %source = %obj; + for (%i=0;%i<%numFb;%i++) { + %x = (getRandom() * 0.4) - 0.2; + %y = (getRandom() * 0.4) - 0.2; + %z = getRandom() / 2; + %vec = %x SPC %y SPC %z; + %pos = vectorAdd(posFromTransform(%obj.getTransform()),vectorScale(%vec,3)); + %p = new (GrenadeProjectile)() { + dataBlock = DeployableFireball; + initialDirection = %vec; + initialPosition = %pos; + sourceObject = %source; + sourceSlot = 1; + vehicleObject = %obj; + }; + MissionCleanup.add(%p); + } +} + +function cascade(%obj,%cascade) { + if (%obj.cascade) + %cascade = true; + if ($Host::Cascade == false && !%cascade) + return; + %list = %obj.dObj; + %count = getWordCount(%list); + for(%i=0;%i<%count;%i++) { + %obj2 = getWord(%list,%i); + if ($Host::Cascade == false) { + %obj2.cascade = true; + %random = getRandom() * 1000; + %obj2.getDataBlock().schedule(%random,disassemble,0,%obj2); + } + else { + %random = getRandom() * 1000; + %obj2.schedule(%random,setDamageState,Destroyed); + } + } + %obj.cascaded = true; +} + +// TODO - handle dup disassemblies? only allow every n seconds? + +function disassemble(%data,%plyr,%obj) { + if (!isObject(%obj)) + return; + if (%obj.lMain) { + if (isObject(%obj.lMain)) { + %obj.lMain.getDataBlock().disassemble(%plyr,%obj.lMain); + return; + } + else { + serverPlay3D(DSound,%obj.getTransform()); + %obj.schedule(500,"delete"); + remDSurface(%obj); + return; + } + } + else if (isObject(%obj.lTarget)) { + %obj.lTarget.startFade(250,0,1); + %obj.lTarget.schedule(500,"delete"); + } + %obj.isRemoved = 1; + serverPlay3D(DSound,%obj.getTransform()); + if (%obj.getType() & $TypeMasks::StaticShapeObjectType + || %obj.getType() & $TypeMasks::ItemObjectType) + %obj.startFade(500,0,1); + if (isObject(%obj.trigger)) + %obj.trigger.delete(); + if (isObject(%obj.emitter)) + %obj.emitter.delete(); + %obj.schedule(500,"delete"); + %revItem = %obj.getDataBlock().getName(); + %newPack = $ReverseDeployItem[%revItem]; + if (getWord(%newPack,0) $= "poof") + $TeamDeployedCount[%obj.team,getWord(%newPack,1)]--; + else + $TeamDeployedCount[%obj.team,%newPack]--; + cascade(%obj,false); + remDSurface(%obj); +} + +function StaticShapeData::disassemble(%data,%plyr,%obj) { + if (!isObject(%obj)) + return; + if (%obj.getDataBlock().classname $= "Generator") { + %obj.getDataBlock().losePower(%obj); + %loc = findWord($PowerList,%obj); + if (%loc !$= "") + $PowerList = listDel($PowerList,%loc); + %obj.isRemoved = true; + } + if (%obj.getDataBlock().barrel $= "DeployableIndoorBarrel") { + %obj.Base.schedule(10, "delete"); + } + disassemble(%data,%plyr,%obj); +} + +function expertModeOn() { + exec("scripts/expertLibraries.cs"); + $Host::ExpertMode = 1; +} + +function expertModeOff() { + exec("scripts/libraries.cs"); + $Host::ExpertMode = 0; + + // Bring pack settings down from orbit + %count = ClientGroup.getCount(); + for (%i=0;%i<%count;%i++) { + %client = ClientGroup.getObject(%i); + %player = %client.player; + if (isObject(%player)) { + %player.packSet = 0; + %player.expertSet = 0; + } + } +} + +function delUndergroundDeployables(%quiet) { + %randomTime = 10000; + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (%obj) { + %terrain = getTerrainHeight2(%obj.getPosition(),0,%obj); + if (getWord(vectorAdd(%obj.getPosition(),"0 0 0.25"),2) < getWord(%terrain,2) || %terrain $= "") { + warn("Deleting: " @ %obj @ " Name: " @ %obj.getDataBlock().getName()); + %random = getRandom() * %randomTime; + %obj.getDataBlock().schedule(%random,"disassemble",%plyr, %obj); // Run Item Specific code. + %deleted++; + } + else + %checked++; + + } + } + if (!%quiet) { + warn("Checked pieces: " @ %checked); + warn("Deleted pieces: " @ %deleted); + } + return %randomTime; +} + +function delOrphanedPieces(%quiet) { + %randomTime = 10000; + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (!isObject(%obj.getOwner())) { + if (!%quiet) + warn("Deleting: " @ %obj @ " Name: " @ %obj.getDataBlock().getName()); + %random = getRandom() * %randomTime; + %obj.getDataBlock().schedule(%random,"disassemble",%plyr,%obj); // Run Item Specific code. + %deleted++; + } + else + %checked++; + } + if (!%quiet) { + warn("Checked pieces: " @ %checked); + warn("Deleted pieces: " @ %deleted); + } + return %randomTime; +} + +function addDSurface(%surface,%obj) { + %surface = firstWord(%surface); + %list = %surface.dObj; + %obj.dSurface = firstWord(%surface); + %list = listAdd(%list,%obj,-1); +// warn("Added obj " @ %obj @ " to surface " @ %surface); + +// For debugging +// %count = getWordCount(%list); +// for(%i=0;%i<%count;%i++) { +// %obj2 = getWord(%list,%i); +// if (!isObject(%obj2)) { +// error("surface: " @ %surface @ " - obj does not exist: " @ %obj2); +// %badIDs = %badIDs SPC %i; +// } +// } +// %list = listDel(%list,trim(%badIDs)); +//warn(%surface @ " - " @ %list); + %surface.dObj = %list; +} + +// TODO - verify +function remDSurface(%obj) { + %surface = %obj.dSurface; + if (isObject(%surface)) { + %list = %surface.dObj; + %loc = findWord(%list,%obj); + if (%loc !$= "") { + %list = listDel(%list,%loc); +// warn("Removed obj " @ %obj @ " from surface " @ %surface); + } + } + %surface.dObj = %list; + + %list = %obj.dObj; + %count = getWordCount(%list); + for(%i=0;%i<%count;%i++) { + %obj2 = getWord(%list,%i); + if (isObject(%obj2)) { + %obj2.dSurface = ""; +// warn("Removed surface " @ %obj @ " from obj " @ %obj2); + } + } +} + +function GameBase::setOwner(%obj,%plyr,%client,%guid) { + if (!%client) + %client = %plyr.client; + if (!%guid) + %guid = %client.guid; + %obj.owner = %client; + %obj.ownerGUID = %guid; +} + +function GameBase::getOwner(%obj) { + if (isObject(%obj.owner)) + return %obj.owner; + %guid = %obj.ownerGUID; + if (!%guid) + %guid = %obj.lTarget.ownerGUID; + if (!%guid) + %guid = %obj.lMain.ownerGUID; + if (%guid) { + %count = ClientGroup.getCount(); + for (%i=0;%i<%count;%i++) { + %client = ClientGroup.getObject(%i); + if (%client.guid == %guid) + return %client; + } + } + return ""; +} diff --git a/Scripts/do_not_delete/Dfunctions.cs.dso b/Scripts/do_not_delete/Dfunctions.cs.dso new file mode 100644 index 0000000..b32752e Binary files /dev/null and b/Scripts/do_not_delete/Dfunctions.cs.dso differ diff --git a/Scripts/do_not_delete/MergeToolSupport.cs.dso b/Scripts/do_not_delete/MergeToolSupport.cs.dso new file mode 100644 index 0000000..6848bdb Binary files /dev/null and b/Scripts/do_not_delete/MergeToolSupport.cs.dso differ diff --git a/Scripts/do_not_delete/loadscreen.cs.dso b/Scripts/do_not_delete/loadscreen.cs.dso new file mode 100644 index 0000000..7a75cd5 Binary files /dev/null and b/Scripts/do_not_delete/loadscreen.cs.dso differ diff --git a/Scripts/editor.cs b/Scripts/editor.cs new file mode 100644 index 0000000..b12737c --- /dev/null +++ b/Scripts/editor.cs @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Torque Game Engine +// +// Copyright (c) 2001 GarageGames.Com +// Portions Copyright (c) 2001 by Sierra Online, Inc. +//----------------------------------------------------------------------------- + + +//------------------------------------------------------------------------------ +// Hard coded images referenced from C++ code +//------------------------------------------------------------------------------ + +// editor/SelectHandle.png +// editor/DefaultHandle.png +// editor/LockedHandle.png + + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Mission Editor +//------------------------------------------------------------------------------ + +function Editor::create() +{ + // Not much to do here, build it and they will come... + // Only one thing... the editor is a gui control which + // expect the Canvas to exist, so it must be constructed + // before the editor. + new EditManager(Editor) + { + profile = "GuiContentProfile"; + horizSizing = "right"; + vertSizing = "top"; + position = "0 0"; + extent = "640 480"; + minExtent = "8 8"; + visible = "1"; + setFirstResponder = "0"; + modal = "1"; + helpTag = "0"; + open = false; + }; +} + + +function Editor::onAdd(%this) +{ + // Basic stuff + exec("scripts/cursors.cs"); + exec("scripts/EditorProfiles.cs"); + + // Tools + exec("scripts/editor.bind.cs"); + exec("gui/ObjectBuilderGui.gui"); + + // New World Editor + exec("gui/EditorGui.gui"); + exec("scripts/EditorGui.cs"); + exec("gui/AIEWorkingDlg.gui"); + + // World Editor + exec("gui/WorldEditorSettingsDlg.gui"); + + // Terrain Editor + exec("gui/TerrainEditorVSettingsGui.gui"); + exec("gui/HelpDlg.gui"); + exec("scripts/help.cs"); + + // do gui initialization... + EditorGui.init(); + + // + exec("scripts/editorRender.cs"); +} + +function Editor::checkActiveLoadDone() +{ + if(isObject(EditorGui) && EditorGui.loadingMission) + { + Canvas.setContent(EditorGui); + EditorGui.loadingMission = false; + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +function toggleEditor(%make) +{ + if (%make) + { + if (!$missionRunning) + { + MessageBoxOK("Mission Required", "You must load a mission before starting the Mission Editor.", ""); + return; + } + +// $testcheats = 1; + if (!isObject(Editor)) + { + Editor::create(); + MissionCleanup.add(Editor); + } + if (Canvas.getContent() == EditorGui.getId()) + Editor.close(); + else + Editor.open(); + } +} + diff --git a/Scripts/expertLibraries.cs b/Scripts/expertLibraries.cs new file mode 100644 index 0000000..63cde6f --- /dev/null +++ b/Scripts/expertLibraries.cs @@ -0,0 +1,148 @@ +//Contain basic data libraries. +//Soon to be updated with more usefull stuff. + +// NOTE - any changes here must be considered in libraries.cs !!! + +//** New format of information ** + +$packSettings["spine"] = 18; +$packSetting["spine",0] = "0.5 0.5 0.1 10 cm in height"; +$packSetting["spine",1] = "0.5 0.5 0.25 25 cm in height"; +$packSetting["spine",2] = "0.5 0.5 0.5 50 cm in height"; +$packSetting["spine",3] = "0.5 0.5 1 1 meter in height"; +$packSetting["spine",4] = "0.5 0.5 1.5 1.5 meters in height"; +$packSetting["spine",5] = "0.5 6 160 auto adjusting"; +$packSetting["spine",6] = "0.5 8 160 pad"; +$packSetting["spine",7] = "0.5 8 160 wooden pad"; +$packSetting["spine",8] = "0.5 0.5 4 4 meters in height"; +$packSetting["spine",9] = "0.5 0.5 8 8 meters in height"; +$packSetting["spine",10] = "0.5 0.5 20 20 meters in height"; +$packSetting["spine",11] = "0.5 0.5 40 40 meters in height"; +$packSetting["spine",12] = "0.5 0.5 80 80 meters in height"; +$packSetting["spine",13] = "0.5 0.5 100 100 meters in height"; +$packSetting["spine",14] = "0.5 0.5 160 160 meters in height"; +$packSetting["spine",15] = "0.5 0.5 400 400 meters in height"; +$packSetting["spine",16] = "0.5 0.5 1000 1k meters in height"; +$packSetting["spine",17] = "0.5 0.5 2000 2k meters in height"; +$packSetting["spine",18] = "0.5 0.5 4000 4k meters in height"; + +$packSettings["mspine"] = 18; +$packSetting["mspine",0] = "1 1 0.1 2 2 0.5 10 cm in height"; +$packSetting["mspine",1] = "1 1 0.25 2 2 0.5 25 cm in height"; +$packSetting["mspine",2] = "1 1 0.5 2 2 0.5 50 cm in height"; +$packSetting["mspine",3] = "1 1 1 2 2 0.5 1 meters in height"; +$packSetting["mspine",4] = "1 1 1.5 2 2 0.5 1.5 meters in height"; +$packSetting["mspine",5] = "1 8 160 2 2 0.5 auto adjusting"; +$packSetting["mspine",6] = "1 8 160 2 2 0.5 normal rings"; +$packSetting["mspine",7] = "1 8 160 8 8 0.5 platform rings"; +$packSetting["mspine",8] = "1 1 4 2 2 0.5 4 meters in height"; +$packSetting["mspine",9] = "1 1 8 2 2 0.5 8 meters in height"; +$packSetting["mspine",10] = "1 1 20 2 2 0.5 20 meters in height"; +$packSetting["mspine",11] = "1 1 40 2 2 0.5 40 meters in height"; +$packSetting["mspine",12] = "1 1 80 2 2 0.5 80 meters in height"; +$packSetting["mspine",13] = "1 1 100 2 2 0.5 100 meters in height"; +$packSetting["mspine",14] = "1 1 160 2 2 0.5 160 meters in height"; +$packSetting["mspine",15] = "1 1 400 2 2 0.5 400 meters in height"; +$packSetting["mspine",16] = "1 1 1000 2 2 0.5 1k meters in height"; +$packSetting["mspine",17] = "1 1 2000 2 2 0.5 2k meters in height"; +$packSetting["mspine",18] = "1 1 4000 2 2 0.5 4k meters in height"; + +$packSettings["floor"] = 11; +$packSetting["floor",0] = "0.1 0.1 40 0.1 0.1 0.1 10 cm wide"; +$packSetting["floor",1] = "1 1 40 1 1 1 1 meter wide"; +$packSetting["floor",2] = "4 4 40 4 4 4 4 meters wide"; +$packSetting["floor",3] = "10 10 20 10 10 10 10 meters wide"; +$packSetting["floor",4] = "20 20 20 20 20 20 20 meters wide"; +$packSetting["floor",5] = "30 30 20 30 30 30 30 meters wide"; +$packSetting["floor",6] = "40 40 20 40 40 40 40 meters wide"; +$packSetting["floor",7] = "50 50 20 50 50 50 50 meters wide"; +$packSetting["floor",8] = "60 60 20 60 60 60 60 meters wide"; +$packSetting["floor",9] = "100 100 40 100 100 100 100 meters wide"; +$packSetting["floor",10] = "400 400 40 400 400 400 400 meters wide"; +$packSetting["floor",11] = "1000 1000 40 1000 1000 1000 1k meters wide"; + +$packSettings["walk"] = 74; +$packSetting["walk",0] = "0 flat"; +$packSetting["walk",1] = "1 Sloped 1 degrees up"; +$packSetting["walk",2] = "-1 Sloped 1 degrees down"; +$packSetting["walk",3] = "5 Sloped 5 degrees up"; +$packSetting["walk",4] = "-5 Sloped 5 degrees down"; +$packSetting["walk",5] = "10 Sloped 10 degrees up"; +$packSetting["walk",6] = "-10 Sloped 10 degrees down"; +$packSetting["walk",7] = "15 Sloped 15 degrees up"; +$packSetting["walk",8] = "-15 Sloped 15 degrees down"; +$packSetting["walk",9] = "20 Sloped 20 degrees up"; +$packSetting["walk",10] = "-20 Sloped 20 degrees down"; +$packSetting["walk",11] = "25 Sloped 25 degrees up"; +$packSetting["walk",12] = "-25 Sloped 25 degrees down"; +$packSetting["walk",13] = "30 Sloped 30 degrees up"; +$packSetting["walk",14] = "-30 Sloped 30 degrees down"; +$packSetting["walk",15] = "35 Sloped 35 degrees up"; +$packSetting["walk",16] = "-35 Sloped 35 degrees down"; +$packSetting["walk",17] = "40 Sloped 40 degrees up"; +$packSetting["walk",18] = "-40 Sloped 40 degrees down"; +$packSetting["walk",19] = "45 Sloped 45 degrees up"; +$packSetting["walk",20] = "-45 Sloped 45 degrees down"; +$packSetting["walk",21] = "50 Sloped 50 degrees up"; +$packSetting["walk",22] = "-50 Sloped 50 degrees down"; +$packSetting["walk",23] = "55 Sloped 55 degrees up"; +$packSetting["walk",24] = "-55 Sloped 55 degrees down"; +$packSetting["walk",25] = "60 Sloped 60 degrees up"; +$packSetting["walk",26] = "-60 Sloped 60 degrees down"; +$packSetting["walk",27] = "65 Sloped 65 degrees up"; +$packSetting["walk",28] = "-65 Sloped 65 degrees down"; +$packSetting["walk",29] = "70 Sloped 70 degrees up"; +$packSetting["walk",30] = "-70 Sloped 70 degrees down"; +$packSetting["walk",31] = "75 Sloped 75 degrees up"; +$packSetting["walk",32] = "-75 Sloped 75 degrees down"; +$packSetting["walk",33] = "80 Sloped 80 degrees up"; +$packSetting["walk",34] = "-80 Sloped 80 degrees down"; +$packSetting["walk",35] = "85 Sloped 85 degrees up"; +$packSetting["walk",36] = "-85 Sloped 85 degrees down"; +$packSetting["walk",37] = "90 Sloped 90 degrees up"; +$packSetting["walk",38] = "-90 Sloped 90 degrees down"; +$packSetting["walk",39] = "95 Sloped 95 degrees up"; +$packSetting["walk",40] = "-95 Sloped 95 degrees down"; +$packSetting["walk",41] = "100 Sloped 100 degrees up"; +$packSetting["walk",42] = "-100 Sloped 100 degrees down"; +$packSetting["walk",43] = "105 Sloped 105 degrees up"; +$packSetting["walk",44] = "-105 Sloped 105 degrees down"; +$packSetting["walk",45] = "110 Sloped 110 degrees up"; +$packSetting["walk",46] = "-110 Sloped 110 degrees down"; +$packSetting["walk",47] = "115 Sloped 115 degrees up"; +$packSetting["walk",48] = "-115 Sloped 115 degrees down"; +$packSetting["walk",49] = "120 Sloped 120 degrees up"; +$packSetting["walk",50] = "-120 Sloped 120 degrees down"; +$packSetting["walk",51] = "125 Sloped 125 degrees up"; +$packSetting["walk",52] = "-125 Sloped 125 degrees down"; +$packSetting["walk",53] = "130 Sloped 130 degrees up"; +$packSetting["walk",54] = "-130 Sloped 130 degrees down"; +$packSetting["walk",55] = "135 Sloped 135 degrees up"; +$packSetting["walk",56] = "-135 Sloped 135 degrees down"; +$packSetting["walk",57] = "140 Sloped 140 degrees up"; +$packSetting["walk",58] = "-140 Sloped 140 degrees down"; +$packSetting["walk",59] = "145 Sloped 145 degrees up"; +$packSetting["walk",60] = "-145 Sloped 145 degrees down"; +$packSetting["walk",61] = "150 Sloped 150 degrees up"; +$packSetting["walk",62] = "-150 Sloped 150 degrees down"; +$packSetting["walk",63] = "155 Sloped 155 degrees up"; +$packSetting["walk",64] = "-155 Sloped 155 degrees down"; +$packSetting["walk",65] = "160 Sloped 160 degrees up"; +$packSetting["walk",66] = "-160 Sloped 160 degrees down"; +$packSetting["walk",67] = "165 Sloped 165 degrees up"; +$packSetting["walk",68] = "-165 Sloped 165 degrees down"; +$packSetting["walk",69] = "170 Sloped 170 degrees up"; +$packSetting["walk",70] = "-170 Sloped 170 degrees down"; +$packSetting["walk",71] = "175 Sloped 175 degrees up"; +$packSetting["walk",72] = "-175 Sloped 175 degrees down"; +$packSetting["walk",73] = "180 Sloped 180 degrees up"; +$packSetting["walk",74] = "-180 Sloped 180 degrees down"; + +$packSettings["jumpad"] = 6; +$packSetting["jumpad",0] = "500 5 boost"; +$packSetting["jumpad",1] = "1000 10 boost"; +$packSetting["jumpad",2] = "1500 15 boost"; +$packSetting["jumpad",3] = "2500 25 boost"; +$packSetting["jumpad",4] = "5000 50 boost"; +$packSetting["jumpad",5] = "7500 75 boost"; +$packSetting["jumpad",6] = "10000 100 boost"; diff --git a/Scripts/forceField.cs b/Scripts/forceField.cs new file mode 100644 index 0000000..5395f24 --- /dev/null +++ b/Scripts/forceField.cs @@ -0,0 +1,290 @@ +//-------------------------------------------------------------------------- +// Force fields: +// +// accept the following commands: +// open() +// close() +// +//-------------------------------------------------------------------------- + + +datablock ForceFieldBareData(defaultForceFieldBare) +{ + fadeMS = 1000; + baseTranslucency = 0.30; + powerOffTranslucency = 0.0; + teamPermiable = false; + otherPermiable = false; + color = "0.0 0.55 0.99"; + powerOffColor = "0.0 0.0 0.0"; + targetNameTag = 'Force Field'; + targetTypeTag = 'ForceField'; + + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; +}; + + +datablock ForceFieldBareData(defaultTeamSlowFieldBare) +{ + fadeMS = 1000; + baseTranslucency = 0.3; + powerOffTranslucency = 0.0; + teamPermiable = true; + otherPermiable = false; + color = "0.28 0.89 0.31"; + powerOffColor = "0.0 0.0 0.0"; + targetTypeTag = 'ForceField'; + + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; +}; + +datablock ForceFieldBareData(defaultAllSlowFieldBare) +{ + fadeMS = 1000; + baseTranslucency = 0.30; + powerOffTranslucency = 0.0; + teamPermiable = true; + otherPermiable = true; + color = "1.0 0.4 0.0"; + powerOffColor = "0.0 0.0 0.0"; + targetTypeTag = 'ForceField'; + + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; +}; + +datablock ForceFieldBareData(defaultNoTeamSlowFieldBare) +{ + fadeMS = 1000; + baseTranslucency = 0.30; + powerOffTranslucency = 0.0; + teamPermiable = false; + otherPermiable = true; + color = "1.0 0.0 0.0"; + powerOffColor = "0.0 0.0 0.0"; + targetTypeTag = 'ForceField'; + + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; +}; + +datablock ForceFieldBareData(defaultSolidFieldBare) +{ + fadeMS = 1000; + baseTranslucency = 0.30; + powerOffTranslucency = 0.0; + teamPermiable = false; + otherPermiable = false; + color = "1.0 0.0 0.0"; + powerOffColor = "0.0 0.0 0.0"; + targetTypeTag = 'ForceField'; + + texture[0] = "skins/forcef1"; + texture[1] = "skins/forcef2"; + texture[2] = "skins/forcef3"; + texture[3] = "skins/forcef4"; + texture[4] = "skins/forcef5"; + + framesPerSec = 10; + numFrames = 5; + scrollSpeed = 15; + umapping = 1.0; + vmapping = 0.15; +}; + + +function ForceFieldBare::onTrigger(%this, %triggerId, %on) +{ + // Default behavior for a field: + // if triggered: go to open state (last waypoint) + // if untriggered: go to closed state (first waypoint) + + if (%on == 1) { + %this.triggerCount++; + } else { + if (%this.triggerCount > 0) + %this.triggerCount--; + } + + if (%this.triggerCount > 0) { + %this.open(); + } else { + %this.close(); + } +} + +function ForceFieldBareData::gainPower(%data, %obj) { + Parent::gainPower(%data, %obj); + if (!shouldChangePowerState(%obj,true)) + return; + %obj.lastState = true; + if (%obj.realScale) { + %obj.setScale(%obj.realScale); + %obj.realScale = ""; + if (isObject(%obj.pzone)) + %obj.pzone.setTransform(%obj.getTransform()); + } + %obj.close(); + // activate the field's physical zone + if (isObject(%obj.pzone)) + %obj.pzone.activate(); +// %pzGroup = nameToID("MissionCleanup/PZones"); +// if(%pzGroup > 0) { +// %ffp = -1; +// for(%i = 0; %i < %pzGroup.getCount(); %i++) { +// %pz = %pzGroup.getObject(%i); +// if(%pz.ffield == %obj) { +// %ffp = %pz; +// break; +// } +// } +// if(%ffp > 0) { +// %ffp.activate(); + if( %data.getName() $= "defaultForceFieldBare" ) + killAllPlayersWithinZone( %data, %obj ); + else if( %data.getName() $= "defaultTeamSlowFieldBare" ) { + %team = %obj.team; + killAllPlayersWithinZone( %data, %obj, %team ); + } + else if (getSubStr(%data.getName(),0,18) $= "DeployedForceField") { + if (!%obj.notFirstPwr) { + %obj.notFirstPwr = true; + return; + } + else { + %team = %obj.team; + killAllPlayersWithinZone( %data, %obj, %team ); + } + } +// } + //else + // error("No PZones group to search!"); +} + +function killAllPlayersWithinZone( %data, %obj, %team ) { + %count = ClientGroup.getCount(); + for( %c = 0; %c < %count; %c++ ) { + %client = ClientGroup.getObject(%c); + if( isObject( %client.player ) ) { + if( %forceField = %client.player.isInForceField() ) { // isInForceField() will return the id of the ff or zero + if( %forceField == %obj ) { + %client.player.FFZapped = true; + %client.player.disableMove(true); + for( %i = 0; %i < 10; %i++ ) { + %client.player.schedule(%i * 200,play3d,ShockLanceHitSound); + schedule(%i * 200,0,playPain,%client.player); + %client.player.schedule(%i * 200,setActionThread,"Death" @ getRandom(10)+1); + } + %client.player.schedule(2000,blowup); // chunkOrama! + %client.player.schedule(2000,scriptkill,$DamageType::ForceFieldPowerup); + } + } + } + } +} + +function ForceFieldBareData::losePower(%data, %obj) { + Parent::losePower(%data, %obj); + if (!shouldChangePowerState(%obj,false)) + return; + %obj.lastState = false; + if (!%obj.realScale) { + %obj.realScale = %obj.getScale(); + %obj.setScale("0.01 0.01 0.01"); + if (isObject(%obj.pzone)) + %obj.pzone.setTransform(%obj.getTransform()); + } + %obj.open(); + // deactivate the field's physical zone + if (isObject(%obj.pzone)) + %obj.pzone.deactivate(); +// %pzGroup = nameToID("MissionCleanup/PZones"); +// if(%pzGroup > 0) { +// %ffp = -1; +// for(%i = 0; %i < %pzGroup.getCount(); %i++) { +// %pz = %pzGroup.getObject(%i); +// if(%pz.ffield == %obj) { +// %ffp = %pz; +// break; +// } +// } +// if(%ffp > 0) { +// %ffp.deactivate(); +// } +// } + //else + // error(""); +} + +function ForceFieldBareData::onAdd(%data, %obj) { + Parent::onAdd(%data, %obj); + + %velocityMod = 0.1; + %gravityMod = 1.0; + %appliedForce = "0 0 0"; + + if (%obj.velocityMod !$= "") + %velocityMod = %obj.velocityMod; + if (%obj.gravityMod !$= "") + %gravityMod = %obj.gravityMod; + if (%obj.appliedForce !$= "") + %appliedForce = %obj.appliedForce; + + %pz = new PhysicalZone() { + position = %obj.position; + rotation = %obj.rotation; + scale = %obj.scale; + polyhedron = "0.000000 1.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000"; + velocityMod = %velocityMod; + gravityMod = %gravityMod; + appliedForce = %appliedForce; + ffield = %obj; + }; + + %obj.pzone = %pz; + + %pzGroup = nameToID("MissionCleanup/PZones"); + if(%pzGroup <= 0) { + %pzGroup = new SimGroup("PZones"); + MissionCleanup.add(%pzGroup); + } + %pzGroup.add(%pz); +} diff --git a/Scripts/functions.cs b/Scripts/functions.cs new file mode 100644 index 0000000..bf3092c --- /dev/null +++ b/Scripts/functions.cs @@ -0,0 +1,1003 @@ +// Universal handy function library collection, release 3.5 +// Created by DynaBlade +// All functions used here are made strictly by DynaBlade +// Functions made in the other attached files are made by their respective authors. + +// String Table +// + +$AllObjMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::StaticObjectType | $TypeMasks::MoveableObjectType | $TypeMasks::DamagableItemObjectType; +$DefaultLOSMask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType; +$CoreObjectMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType; +$EverythingMask = $notdone; + +// Datablock Cache +// + +datablock ShockLanceProjectileData(FXZap) +{ + directDamage = 0; + radiusDamageType = $DamageType::Default; + kickBackStrength = 0; + velInheritFactor = 0; + sound = ""; + + zapDuration = 1.0; + impulse = 0; + boltLength = 14.0; + extension = 14.0; // script variable indicating distance you can shock people from + lightningFreq = 50.0; + lightningDensity = 18.0; + lightningAmp = 0.25; + lightningWidth = 0.05; + + + boltSpeed[0] = 2.0; + boltSpeed[1] = -0.5; + + texWrap[0] = 1.5; + texWrap[1] = 1.5; + + startWidth[0] = 0.3; + endWidth[0] = 0.6; + startWidth[1] = 0.3; + endWidth[1] = 0.6; + + texture[0] = "special/shockLightning01"; + texture[1] = "special/shockLightning02"; + texture[2] = "special/shockLightning03"; + texture[3] = "special/ELFBeam"; +}; + +datablock ShockLanceProjectileData(FXPulse) +{ + directDamage = 0; + radiusDamageType = $DamageType::Default; + kickBackStrength = 0; + velInheritFactor = 0; + sound = ""; + + zapDuration = 1.0; + impulse = 0; + boltLength = 14.0; + extension = 14.0; // script variable indicating distance you can shock people from + lightningFreq = 50.0; + lightningDensity = 18.0; + lightningAmp = 0.25; + lightningWidth = 0.05; + + + boltSpeed[0] = 2.0; + boltSpeed[1] = -0.5; + + texWrap[0] = 1.5; + texWrap[1] = 1.5; + + startWidth[0] = 0.3; + endWidth[0] = 0.6; + startWidth[1] = 0.3; + endWidth[1] = 0.6; + + texture[0] = "special/nonlingradient"; + texture[1] = "special/nonlingradient"; + texture[2] = "special/nonlingradient"; + texture[3] = "special/nonlingradient"; +}; + +datablock ParticleData(TeleporterParticle) +{ + dragCoeffiecient = 0.0; + gravityCoefficient = -0.5; + inheritedVelFactor = 0.0; + + lifetimeMS = 100; + lifetimeVarianceMS = 50; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -160.0; + spinRandomMax = 160.0; + + animateTexture = true; + framesPerSec = 15; + + + animTexName[0] = "special/Explosion/exp_0016"; + animTexName[1] = "special/Explosion/exp_0018"; + animTexName[2] = "special/Explosion/exp_0020"; + animTexName[3] = "special/Explosion/exp_0022"; + animTexName[4] = "special/Explosion/exp_0024"; + animTexName[5] = "special/Explosion/exp_0026"; + animTexName[6] = "special/Explosion/exp_0028"; + animTexName[7] = "special/Explosion/exp_0030"; + animTexName[8] = "special/Explosion/exp_0032"; + + colors[0] = "1 1 1 1"; + colors[1] = "0.75 0.75 1.0"; + colors[2] = "0.5 0.5 0.5 0.0"; + sizes[0] = 0.5; + sizes[1] = 1; + sizes[2] = 2.5; + times[0] = 0.0; + times[1] = 0.7; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(TeleporterEmitter) +{ + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 6; + velocityVariance = 2.9; + ejectionOffset = 0.0; + thetaMin = 0; + thetaMax = 5; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + particles = "TeleporterParticle"; +}; + +datablock StaticShapeData(SlotExtension) +{ + shapeFile = "turret_muzzlepoint.dts"; +}; + +// ShapeBase/Parent calls +// + +function SimObject::getRotation(%obj) +{ + getRotation(%obj); +} + +function SimObject::getSlotRotation(%obj, %slot) +{ + getSlotRotation(%obj, %slot); +} + +function SimObject::getSlotPosition(%obj, %slot) +{ + getSlotPosition(%obj, %slot); +} + +function SimObject::isVehicle(%obj) +{ + isVehicle(%obj); +} + +function SimObject::isPlayer(%obj) +{ + isPlayer(%obj); +} + +function ShapeBase::zapObject(%obj) +{ + zapObject(%obj); +} + +function ShapeBase::zap2Object(%obj) +{ + zap2Object(%obj); +} + +function ShapeBase::stopZap(%obj) +{ + stopZap(%obj); +} + +function ShapeBase::applyKick(%obj, %force) +{ + applyKick(%obj, %force); +} + +function ShapeBase::useEnergy(%obj, %amount) +{ + useEnergy(%obj, %amount); +} + +function SimObject::setPosition(%obj, %pos) +{ + setPosition(%obj, %pos); +} + +function SimObject::setRotation(%obj, %rot) +{ + setRotation(%obj, %rot); +} + +function SimObject::play3D(%obj, %sound) +{ + play3D(%obj, %sound); +} + +function ShapeBase::teleportStartFX(%obj) +{ + teleportStartFX(%obj); +} + +function ShapeBase::teleportEndFX(%obj) +{ + teleportEndFX(%obj); +} + +function ShapeBase::getEyePoint(%obj) +{ + getEyePoint(%obj); +} + +function ShapeBase::getMuzzleRaycastPt(%obj, %slot, %dist) +{ + getMuzzleRaycastPt(%obj, %slot, %dist); +} + +function ShapeBase::getEyeRaycastPt(%obj, %dist) +{ + getEyeRaycastPt(%obj, %dist); +} + +function ShapeBase::getForwardRaycastPt(%obj, %dist) +{ + getForwardRaycastPt(%obj, %dist); +} + +function ShapeBase::getMass(%obj) +{ + getMass(%obj); +} + +function ShapeBase::getAccel(%obj) +{ + getAccel(%obj); +} + +function ShapeBase::getMaxEnergy(%obj) +{ + getMaxEnergy(%obj); +} + +function ShapeBase::getMaxDamage(%obj) +{ + getMaxDamage(%obj); +} + +function ShapeBase::getDamageLeft(%obj) +{ + getDamageLeft(%obj); +} + +function ShapeBase::getDamageLeftPct(%obj) +{ + getDamageLeftPct(%obj); +} + +function ShapeBase::getVelToForce(%obj) +{ + getVelToForce(%obj); +} + +function ShapeBase::getEnergyPct(%obj) +{ + getEnergyPct(%obj); +} + +function ShapeBase::getDamagePct(%obj) +{ + getDamagePct(%obj); +} + +function ShapeBase::getTransformAngle(%trans) +{ + getTransformAngle(%trans); +} + +function ShapeBase::createSlotExtension(%obj, %slot) +{ + createSlotExtension(%obj, %slot); +} + +function ShapeBase::deleteSlotExtension(%obj, %slot) +{ + deleteSlotExtension(%obj, %slot); +} + +// Function library +// + +function createSlotExtension(%obj, %slot) +{ + %ext = new StaticShape() + { + scale = "1 1 1"; + dataBlock = "SlotExtension"; + }; + %ext.isExtension = true; + + %obj.mountObject(%ext, %slot); + %obj.slotExtension[%slot] = %ext; + %obj.slotExtension[%slot].srcObj = %obj; +} + +function deleteSlotExtension(%obj, %slot) +{ + %ext = %obj.getMountNodeObject(%slot); + if(!%ext) + return; + + if(%ext.isExtension) + %ext.delete(); + + %obj.slotExtension[%slot] = ""; +} + + +function combineVars(%a, %b, %c) +{ + return %a SPC %b SPC %c; +} + +function posFrRaycast(%transform) +{ + %position = getWord(%transform, 1) @ " " @ getWord(%transform, 2) @ " " @ getWord(%transform, 3); + return %position; +} + +function normalFrRaycast(%transform) +{ + %norm = getWord(%transform, 4) @ " " @ getWord(%transform, 5) @ " " @ getWord(%transform, 6); + return %norm; +} + +function getEyePoint(%obj) +{ + %eyePt = getWords(%obj.getEyeTransform(), 0, 2); + return %eyePt; +} + +function play3D(%obj, %sound) +{ + serverPlay3D(%sound, %obj.getTransform()); +} + +function killit(%k) +{ + %k.delete(); +} + +function scanArea(%pos, %radius, %mask) +{ + InitContainerRadiusSearch(%pos, %radius, %mask); + while((%int = ContainerSearchNext()) != 0) + { + if(%int) + return true; + } + + return false; +} + +function testPosition(%obj) +{ + InitContainerRadiusSearch(%obj.getWorldBoxCenter(), 0.5, $DefaultLOSMask); + + while ((%test = ContainerSearchNext()) != 0) + { + if(%test) + return false; + else + return true; + } +} + +function createEmitter(%pos, %emitter, %rot) +{ + %dummy = new ParticleEmissionDummy() + { + position = %pos; + rotation = %rot; + scale = "1 1 1"; + dataBlock = defaultEmissionDummy; + emitter = %emitter; + velocity = "1"; + }; + MissionCleanup.add(%dummy); + %dummy.setRotation(%rot); + + if(isObject(%dummy)) + return %dummy; +} + +function createLifeEmitter(%pos, %emitter, %lifeMS, %rot) +{ + %dummy = createEmitter(%pos, %emitter, %rot); + schedule(%lifeMS, %dummy, "killit", %dummy); + return %dummy; +} + +function sqr(%num) +{ + return %num*%num; +} + +function cube(%num) +{ + return %num*%num*%num; +} + +function useEnergy(%obj, %amount) +{ + if(isObject(%obj)) + { + %energy = %obj.getEnergyLevel() - %amount; + %obj.setEnergyLevel(%energy); + } +} + +function modifyTransform(%trans1, %trans2) +{ + %tpx1 = getWord(%trans1, 0); + %tpy1 = getWord(%trans1, 1); + %tpz1 = getWord(%trans1, 2); + %tpa1 = getWord(%trans1, 3); + %tpb1 = getWord(%trans1, 4); + %tpc1 = getWord(%trans1, 5); + %tpd1 = getWord(%trans1, 6); + + %tpx2 = getWord(%trans2, 0); + %tpy2 = getWord(%trans2, 1); + %tpz2 = getWord(%trans2, 2); + %tpa2 = getWord(%trans2, 3); + %tpb2 = getWord(%trans2, 4); + %tpc2 = getWord(%trans2, 5); + %tpd2 = getWord(%trans2, 6); + + %trans = (%tpx1+%tpx2)@" "@(%tpy1+%tpy2)@" "@(%tpz1+%tpz2)@" "@(%tpa1+%tpa2)@" "@(%tpb1+%tpb2)@" "@(%tpc1+%tpc2)@" "@(%tpd1+%tpd2); + return %trans; +} + +function modifyTri(%trans1, %trans2) +{ + %tpx1 = getWord(%trans1, 0); + %tpy1 = getWord(%trans1, 1); + %tpz1 = getWord(%trans1, 2); + + %tpx2 = getWord(%trans2, 0); + %tpy2 = getWord(%trans2, 1); + %tpz2 = getWord(%trans2, 2); + + %tri = (%tpx1+%tpx2)@" "@(%tpy1+%tpy2)@" "@(%tpz1+%tpz2); + return %tri; +} + +function modifyQuad(%trans1, %trans2) // assuming for transform rotation +{ + %tpa1 = getWord(%trans1, 3); + %tpb1 = getWord(%trans1, 4); + %tpc1 = getWord(%trans1, 5); + %tpd1 = getWord(%trans1, 6); + + %tpa2 = getWord(%trans2, 3); + %tpb2 = getWord(%trans2, 4); + %tpc2 = getWord(%trans2, 5); + %tpd2 = getWord(%trans2, 6); + + %quad = (%tpa1+%tpa2)@" "@(%tpb1+%tpb2)@" "@(%tpc1+%tpc2)@" "@(%tpd1+%tpd2); + return %quad; +} + +function getTransformAngle(%trans) +{ + return getWord(%trans, 6); +} + +function setPosition(%obj, %pos) +{ + %rot = getWords(%obj.getTransform(), 3, 6); + %trans = %pos@" "@%rot; + %obj.setTransform(%trans); +} + +function setRotation(%obj, %rot) +{ + %pos = getWords(%obj.getTransform(), 0, 2); + %trans = %pos@" "@%rot; + %obj.setTransform(%trans); +} + +function getSlotPosition(%obj, %slot) +{ + return getWords(%obj.getSlotTransform(%slot), 0, 2); +} + +function getSlotRotation(%obj, %slot) +{ + return getWords(%obj.getSlotTransform(%slot), 3, 6); +} + +function getRotation(%obj) +{ + return getWords(%obj.getTransform(), 3, 6); +} + +function getRandomN(%max, %min) // negative getrandom +{ + if(%max !$= "" && %min !$= "") + return getRandom(%max, %min) * -1; + else if(%max !$= "") + return getRandom(%max) * -1; + else + return getRandom() * -1; +} + +function getRandomB() // boolean getRandom +{ + if(getRandom(1)) + return true; + else + return false; +} + +function getRandomT(%max, %min) +{ + if(%max !$= "" && %min !$= "") + return getRandomB() ? getRandom(%max, %min) : getRandomN(%max, %min); + else if(%max !$= "") + return getRandomB() ? getRandom(%max) : getRandomN(%max); + else + return getRandomB() ? getRandom() : getRandomN(); +} + +function shutdownServer(%time, %msg, %lines) +{ + %left = %time*1000; + centerPrintAll(%msg, %left, %lines); + schedule(%left, 0, "quit"); +} + +function velToSingle(%vel, %bool) // this function is also vectorLength :) +{ + %x = getWord(%vel, 0); + %y = getWord(%vel, 1); + %z = getWord(%vel, 2); + + if(%bool) + return mSqrt((%x*%x)+(%y*%y)+(%z*%z)); + else + return mFloor(mSqrt((%x*%x)+(%y*%y)+(%z*%z))); +} + +function MStoKPH(%vel, %bool) // for more accurate conversions +{ + if(%bool) + return %vel * 3.6; + else + return mFloor(%vel * 3.6); +} + +function KPHtoMPH(%vel, %bool) +{ + if(%bool) + return %vel * 0.6214; + else + return mFloor(%vel * 0.6214); +} + +function vectorNeg(%vec) +{ + %v1 = getWord(%vec, 0) * -1; + %v2 = getWord(%vec, 1) * -1; + %v3 = getWord(%vec, 2) * -1; + return %v1@" "@%v2@" "@%v3; +} + +function vectorRandom() +{ + return getRandomT()@" "@getRandomT()@" "@getRandomT(); +} + +function vectorClear(%vec) // for you lazy types +{ + return "0 0 0"; +} + +function vectorCopy(%veca, %vecb) // also for you lazy types +{ + %vecb = getWords(%veca, 0, 2); + return; +} + +function vectorCompare(%va, %vb) +{ + %v1[0] = getWord(%va, 0); + %v1[1] = getWord(%va, 1); + %v1[2] = getWord(%va, 2); + + %v2[0] = getWord(%vb, 0); + %v2[1] = getWord(%vb, 1); + %v2[2] = getWord(%vb, 2); + + if(%v1[0] != %v2[0] || %v1[1] != %v2[1] || %v1[2] != %v2[2]) + return 0; + + return 1; +} + +function vectorToRotZ(%vec) // only for Z rotations +{ + %radian = mAcos(getWord(%vec, 1)); + %angle = (%radian / 0.0175); + %newrot = " 0 0 1 " @ (270 - %angle); + return %newrot; +} + +function getLOSOffset(%obj, %vec, %dist) +{ + %pos = %obj.getPosition(); + %dir = vectorNormalize(%vec); + %nvec = vectorScale(%dir, %dist); + return vectorAdd(%pos, %nvec); +} + +function S_vectorNormalize(%vec) // Source code on vectorNormalize +{ + %length = 0; + %ilength = 0; + %v[0] = getWord(%vec, 0); + %v[1] = getWord(%vec, 1); + %v[2] = getWord(%vec, 2); + + %length = mSqrt((%v[0]*%v[0]) + (%v[1]*%v[1]) + (%v[2]*%v[2])); + + if(%length) // no division by 0 thx :) + { + %ilength = 1/%length; + %v[0] *= %ilength; + %v[1] *= %ilength; + %v[2] *= %ilength; + } + + %nVec = combineVars(%v[0], %v[1], %v[2]); + return %nVec; +} + +function getDistance2D(%a, %b) +{ + %x1 = getWord(%a, 0); + %y1 = getWord(%b, 0); + %x2 = getWord(%a, 1); + %y2 = getWord(%b, 1); + + %dist = mSqrt(sqr(%x2 - %x1) + sqr(%y2 - %y1)); + return %dist; +} + +function getObjectDistance(%obj1, %obj2) +{ + %obj1b = %obj1.getWorldBoxCenter(); + %obj2b = %obj2.getWorldBoxCenter(); + + %tpx1 = getWord(%obj1b, 0); + %tpy1 = getWord(%obj1b, 1); + %tpz1 = getWord(%obj1b, 2); + + %tpx2 = getWord(%obj2b, 0); + %tpy2 = getWord(%obj2b, 1); + %tpz2 = getWord(%obj2b, 2); + + %dist = mSqrt(sqr(%tpx2 - %tpx1) + sqr(%tpy2 - %tpy1) + sqr(%tpz2 - %tpz1)); + return %dist; +} + +function getVectorFromPoints(%pt1, %pt2) +{ + return VectorNormalize(VectorSub(%pt2, %pt1)); +} + +function getVectorFromObjects(%obj1, %obj2) +{ + %obj1b = %obj1.getWorldBoxCenter(); + %obj2b = %obj2.getWorldBoxCenter(); + + return VectorNormalize(VectorSub(%obj2b, %obj1b)); +} + +function isWithinVariance(%va, %vb, %variance) // vector 1, vector 2, variant percentage out of 100% ex (0.9*360 / 360) +{ + %dot = vectorDot(vectorNormalize(%va), vectorNormalize(%vb)); + if(%dot >= %variance/360) + return true; + else + return false; +} + +function getDistance3D(%ptA, %ptB) +{ + %tpx1 = getWord(%ptA, 0); + %tpy1 = getWord(%ptA, 1); + %tpz1 = getWord(%ptA, 2); + + %tpx2 = getWord(%ptB, 0); + %tpy2 = getWord(%ptB, 1); + %tpz2 = getWord(%ptB, 2); + + %dist = mSqrt(sqr(%tpx2 - %tpx1) + sqr(%tpy2 - %tpy1) + sqr(%tpz2 - %tpz1)); + return %dist; +} + +function zapObject(%obj) +{ + %obj.zap = new ShockLanceProjectile() + { + dataBlock = "fxzap"; + initialDirection = "0 0 0"; + initialPosition = %obj.getWorldBoxCenter(); + sourceObject = %obj; + sourceSlot = 0; + targetId = %obj; + }; + MissionCleanup.add(%obj.zap); +} + +function zap2Object(%obj) +{ + %obj.zap = new ShockLanceProjectile() + { + dataBlock = "fxpulse"; + initialDirection = "0 0 0"; + initialPosition = %obj.getWorldBoxCenter(); + sourceObject = %obj; + sourceSlot = 0; + targetId = %obj; + }; + MissionCleanup.add(%obj.zap); +} + +function stopZap(%obj) +{ + if(isObject(%obj)) + { + if(isObject(%obj.zap)) + %obj.zap.delete(); + } +} + +function teleportStartFX(%obj) +{ + if(%obj.holdingFlag) + { + %client = %obj.client; + %flag = %obj.holdingFlag; + %flag.setVelocity("0 0 0"); + %flag.setTransform(%obj.getWorldBoxCenter()); + %flag.setCollisionTimeout(%obj); + %obj.throwObject(%flag); + messageClient(%obj.client, 'MsgNoTeleFlag', '\c1You cannot teleport with the flag.'); + } + + %obj.playShieldEffect("0 0 1"); + %obj.setCloaked(true); + %obj.startFade( 1000, 0, true ); + %obj.play3D(TeleportSound); + + %particles = createEmitter(%obj.getPosition(), "TeleporterEmitter"); + schedule(2500, 0, "killit", %particles); +} + +function teleportEndFX(%obj) +{ + %obj.setCloaked(false); + %obj.playShieldEffect("0 0 1"); + %obj.startFade(1000, 0, false ); + %obj.play3D(UnTeleportSound); + if (isObject(%obj.particleFX)) + %obj.particleFX.delete(); + + %particles = createEmitter(%obj.getPosition(), "TeleporterEmitter"); + schedule(2500, 0, "killit", %particles); +} + +function getMuzzleRaycastPt(%obj, %slot, %dist) +{ + %vec = %obj.getMuzzleVector(%slot); + %pos = %obj.getMuzzlePoint(%slot); + %nVec = VectorNormalize(%vec); + %scVec = VectorScale(%nVec, %dist); + %end = VectorAdd(%pos, %scVec); + %searchResult = containerRayCast(%pos, %end, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::ItemObjectType, 0); + %raycastPt = posFrRaycast(%searchResult); + %raycastPt.raycast = %searchResult; + + return %raycastPt; +} + +function getEyeRaycastPt(%obj, %dist) +{ + %vec = %obj.getEyeVector(); + %pos = %obj.getEyePoint(); + %nVec = VectorNormalize(%vec); + %scVec = VectorScale(%nVec, %dist); + %end = VectorAdd(%pos, %scVec); + %searchResult = containerRayCast(%pos, %end, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::ItemObjectType, 0); + %raycastPt = posFrRaycast(%searchResult); + %raycastPt.raycast = %searchResult; + + return %raycastPt; +} + +function getForwardRaycastPt(%obj, %dist) +{ + %vec = %obj.getForwardVector(); + %pos = %obj.getWorldBoxCenter(); + %nVec = VectorNormalize(%vec); + %scVec = VectorScale(%nVec, %dist); + %end = VectorAdd(%pos, %scVec); + %searchResult = containerRayCast(%pos, %end, $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::TurretObjectType | $TypeMasks::ItemObjectType, 0); + %raycastPt = posFrRaycast(%searchResult); + %raycastPt.raycast = %searchResult; + + return %raycastPt; +} + +function applyKick(%obj, %force) +{ + %vec = VectorScale(%obj.getMuzzleVector(0), %force); + %obj.applyImpulse(%obj.getTransform(), %vec); +} + +function getMass(%obj) +{ + return %obj.getDatablock().mass; +} + +function getMaxEnergy(%obj) +{ + return %obj.getDatablock().maxEnergy; +} + +function getMaxDamage(%obj) +{ + return %obj.getDatablock().maxDamage; +} + +function getEnergyPct(%obj) +{ + return %obj.getEnergyLevel() / %obj.getMaxEnergy(); +} + +function getDamagePct(%obj) +{ + return %obj.getDamageLevel() / %obj.getMaxDamage(); +} + +function getDamageLeft(%obj) +{ + return %obj.getMaxDamage() - %obj.getDamageLevel(); +} + +function getDamageLeftPct(%obj) +{ + return %obj.getDamageLeft() / %obj.getMaxDamage(); +} + +function getAccel(%obj) +{ + %vel = vectorScale(%obj.getVelocity(), 0.01); + schedule(1, 0, "returnAccel", %obj, %vel); +} + +function returnAccel(%obj, %vel1) +{ + %vel = vectorScale(%obj.getVelocity(), 0.01); + return vectorScale(vectorSub(%vel, %vel1), 0.01); // a = dv/dt +} + +function getVelToForce(%obj) +{ + %accel = %obj.getAccel(); + %mass = %obj.getMass(); + return vectorScale(%accel, %mass); +} + +function getTowForce(%obj, %target) +{ + %accel = %obj.getAccel(); + %mass = %target.getMass(); + return vectorScale(%accel, %mass); +} + +function setFlightCeiling(%val) +{ + %missionArea = nameToID("MissionGroup/MissionArea"); + + if(%missionArea != -1) + %missionArea.FlightCeiling = %val; +} + +function reload(%script) +{ + compile(%script); // Added by JackTL - Duh!! + exec(%script); + %count = ClientGroup.getCount(); + + for(%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + + if(!%cl.isAIControlled()) // no sending bots datablocks.. LOL + %cl.transmitDataBlocks(0); // all clients on server + } +} + +function isVehicle(%obj) +{ + if(isObject(%obj)) + { + %data = %obj.getDataBlock(); + %className = %data.className; + + if(%className $= WheeledVehicleData || %className $= FlyingVehicleData || %className $= HoverVehicleData) + return true; + else + return false; + } +} + +function isPlayer(%obj) +{ + if(isObject(%obj)) + { + %data = %obj.getDataBlock(); + %className = %data.className; + + if(%className $= Armor) + return true; + else + return false; + } +} + +function changeServerHostname(%name) +{ + $Host::GameName = %name; +} + +function changeServerAllowAliases(%bool) +{ + $Host::NoSmurfs = %bool; +} + +function changeServerPlayerCount(%num) +{ + $Host::MaxPlayers = %num; +} + +function changeAdminPassword(%pass) +{ + if(%pass) + $Host::AdminPassword = %pass; + else + $Host::AdminPassword = ""; +} + +function changeServerPassword(%pass) +{ + if(%pass) + $Host::Password = %pass; + else + $Host::Password = ""; +} + +function setServerPrefs(%name, %aliases, %admin, %password, %num) +{ + $Host::CRCTextures = 0; + if(%name !$= "") + changeServerHostname(%name); + if(%aliases !$= "") + changeServerAllowAliases(%aliases); + if(%num !$= "") + changeServerPlayerCount(%num); + + changeAdminPassword(%admin); + changeServerPassword(%password); +} diff --git a/Scripts/gameBase.cs b/Scripts/gameBase.cs new file mode 100644 index 0000000..ddee682 --- /dev/null +++ b/Scripts/gameBase.cs @@ -0,0 +1,147 @@ + +function GameBaseData::onAdd(%data, %obj) +{ + if(%data.targetTypeTag !$= "") + { + // use the name given to the object in the mission file + if(%obj.nameTag !$= "") + { + %obj.nameTag = addTaggedString(%obj.nameTag); + %nameTag = %obj.nameTag; + } + else + %nameTag = %data.targetNameTag; + + %obj.target = createTarget(%obj, %nameTag, "", "", %data.targetTypeTag, 0, 0); + } + else + %obj.target = -1; +} + +function GameBaseData::onRemove(%data, %obj) +{ + %target = %obj.getTarget(); + + // first 32 targets are team targets + if(%target >= 32) + { + if(%obj.nameTag !$= "") + removeTaggedString(%obj.nameTag); + freeTarget(%target); + } +} + +function InteriorInstance::damage() +{ +} + +function TerrainBlock::damage() +{ +} + +function WaterBlock::damage() { +} + +function ForceFieldBare::damage(%this, %sourceObject, %position, %amount, %damageType) { + %this.getDataBlock().damageObject(%this, %sourceObject, %position, %amount, %damageType); +} + +function ForceFieldBareData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) { + StaticShapeData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType); +} + +function ForceFieldBare::applyDamage(%obj,%amount,%sourceObject,%position,%damageType) { + %dataBlockName = %obj.getDataBlock().getName(); + if (getSubStr(%dataBlockName,0,18) $= "DeployedForceField" || getSubStr(%dataBlockName,0,20) $= "DeployedGravityField") { + %count = getWordCount($PowerList); + for(%i=0;%i<%count;%i++) { + %powerObj = getWord($PowerList,%i); + if (genPoweringObj(%powerObj,%obj)) + %powerList = %powerList SPC %powerObj; + } + %powerList = trim(%powerList); + %genDamage = (%amount * 1) / getWordCount(%powerList); + %count = getWordCount(%powerList); + for(%i=0;%i<%count;%i++) { + %powerObj = getWord(%powerList,%i); + %mult = (1 - 0.05) + (getRandom() * 0.10); + %powerObj.damage(%sourceObject,%position,%genDamage * %mult,%damageType); + } + } +} + +function ForceFieldBare::isEnabled() { + // created to prevent console errors +} + +function GameBaseData::shouldApplyImpulse(%data, %obj) +{ + return %data.shouldApplyImpulse; +} + + +function ShapeBaseData::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + // if it's a deployed object, schedule the ambient thread to play in a little while + if(%data.deployAmbientThread) + %obj.schedule(750, "playThread", $AmbientThread, "ambient"); + // check for ambient animation that should always be played + if(%data.alwaysAmbient) + %obj.playThread($AmbientThread, "ambient"); +} + +function SimObject::setOwnerClient(%obj, %cl) +{ + %obj.client = %cl; +} + +function SimObject::getOwnerClient(%obj) +{ + if(isObject(%obj)) + return %obj.client; + return 0; +} + // recursive objective init functions for mission group + +function SimGroup::objectiveInit(%this) +{ + for (%i = 0; %i < %this.getCount(); %i++) + %this.getObject(%i).objectiveInit(); +} + +function SimObject::objectiveInit(%this) +{ +} + +function GameBase::objectiveInit(%this) +{ + //error("Initializing object " @ %this @ ", " @ %this.getDataBlock().getName()); + %this.getDataBlock().objectiveInit(%this); +} + +// tag strings are ignored if they start with an underscore +function GameBase::getGameName(%this) +{ + %name = ""; + + if(%this.nameTag !$= "") + %name = %this.nameTag; + else + { + %name = getTaggedString(%this.getDataBlock().targetNameTag); + if((%name !$= "") && (getSubStr(%name, 0, 1) $= "_")) + %name = ""; + } + + %type = getTaggedString(%this.getDataBlock().targetTypeTag); + if((%type !$= "") && (getSubStr(%type, 0, 1) !$= "_")) + { + if(%name !$= "") + return(%name @ " " @ %type); + else + return(%type); + } + + return(%name); +} diff --git a/Scripts/hazard.cs b/Scripts/hazard.cs new file mode 100644 index 0000000..e02d447 --- /dev/null +++ b/Scripts/hazard.cs @@ -0,0 +1,135 @@ +// hazard.cs + +if ($Host::Hazard::Enabled $= "") + $Host::Hazard::Enabled = 0; // Disable as default +if ($Hazard::HazardTimer < 500) + $Hazard::HazardTimer = 2500; // Hazard loop timer +if ($Hazard::StormTimer < 100) + $Hazard::StormTimer = 500; // Hazard "storm" loop timer +if ($Hazard::HazardStormDurationMin $= "") + $Hazard::HazardStormDurationMin = 45000; // Storm duration min +if ($Hazard::HazardStormDurationMax $= "") + $Hazard::HazardStormDurationMax = 120000; // Storm duration max +if ($Hazard::StormRandom $= "") + $Hazard::StormRandom = 120; // Storm random chance +if ($Hazard::MeteorMax < 1) + $Hazard::MeteorMax = 1; // Max meteors per run +if ($Hazard::MeteorMin $= "" || $Hazard::MeteorMin > $Hazard::MeteorMax) + $Hazard::MeteorMin = 0; // Min meteors per run +if ($Hazard::MeteorRad $= "") + $Hazard::MeteorRad = 1650; // Meteor drop radius + +if ($Hazard::MaxSlant < 0 || $Hazard::MaxSlant $= "") + $Hazard::MaxSlant = 0.4; +if ($Hazard::BDropAddX $= "") + $Hazard::BDropAddX = 0.00153; +if ($Hazard::BDropAddY $= "") + $Hazard::BDropAddY = 0.00956; +if ($Hazard::DropAddVariation $= "") + $Hazard::DropAddVariation = 0.0010; // divided by two = +/- 0.0005 +if ($Hazard::DropAddVariationTime < 1) + $Hazard::DropAddVariationTime = 1000 * 60 * 10; // 10 minutes + +if ($Hazard::DropX $= "" || mAbs($Hazard::DropX) > 1) + $Hazard::DropX = (getRandom() * 2) - 1; +if ($Hazard::DropY $= "" || mAbs($Hazard::DropY) > 1) + $Hazard::DropY = 0; + + +function hazardOn() { + $Host::Hazard::Enabled = 1; + hazardThread($Hazard::HazardThread++); +} + +function hazardOff() { + $Host::Hazard::Enabled = 0; +} + +function hazardThread(%thread) { + if (%thread != $Hazard::HazardThread || $Host::Hazard::Enabled != 1 || !isObject(MissionCleanup)) { + warn("HazardThread #" @ mAbs(%thread) @ " stopped. Last started thread: " @ $Hazard::HazardThread); + return; + } + + if ($Hazard::StormTime $= "") { + %rnd = getRandom(0,$Hazard::StormRandom); + if (%rnd == 1) { + // With default values, warning is 10 to 40 sec prior to storm + $Hazard::StormTime = getSimTime() + ($Hazard::HazardTimer * 4) + ($Hazard::HazardTimer * getRandom(0,12)); + $Hazard::HazardStormDuration = getRandom($Hazard::HazardStormDurationMin,$Hazard::HazardStormDurationMax); + messageAll('msgClient','\c2Warning: Meteor storm expected in %1 seconds!~wfx/misc/warning_beep.wav',mFloor(($Hazard::StormTime - getSimTime()) / 1000)); + $Hazard::StormTime = $Hazard::StormTime - 10; // fix + } + } + else { + if ($Hazard::StormTime < getSimTime() && $Hazard::StormActive != true) { + $Hazard::StormTime = getSimTime(); + $Hazard::StormActive = true; + messageAll('msgClient','\c2Warning: Meteor storm imminent!~wfx/misc/red_alert.wav'); + messageAll('msgClient','~wvoice/Training/Any/ANY.prompt0%1.WAV',getRandom(1,7)); + } + if ($Hazard::StormTime + $Hazard::HazardStormDuration < getSimTime()) { + $Hazard::StormActive = false; + $Hazard::StormTime = ""; + messageAll('msgClient','\c2Meteor storm passing.'); + DropMapPulveriser(); + } + } + + if ($Hazard::DropAddX $= "") + $Hazard::DropAddX = $Hazard::BDropAddX; + if ($Hazard::DropAddY $= "") + $Hazard::DropAddY = $Hazard::BDropAddY; + if ($Hazard::DropAddVariationLastTime $= "") // Don't randomize on first run + $Hazard::DropAddVariationLastTime = getSimTime(); + + if ($Hazard::DropAddVariationLastTime + $Hazard::DropAddVariationTime < getSimTime()) { + $Hazard::DropAddVariationLastTime = getSimTime(); + $Hazard::DropAddX = $Hazard::BDropAddX + ((getRandom() * $Hazard::DropAddVariation) - ($Hazard::DropAddVariation / 2)); + $Hazard::DropAddY = $Hazard::BDropAddY + ((getRandom() * $Hazard::DropAddVariation) - ($Hazard::DropAddVariation / 2)); + } + + $Hazard::DropX = $Hazard::DropX + $Hazard::DropAddX; + $Hazard::DropY = $Hazard::DropY + $Hazard::DropAddY; + + if (mAbs($Hazard::DropX) > 1) { + $Hazard::DropX = 1 * lev($Hazard::DropX); + $Hazard::DropAddX = -$Hazard::DropAddX; + } + + if (mAbs($Hazard::DropY) > 1) { + $Hazard::DropY = 1 * lev($Hazard::DropY); + $Hazard::DropAddY = -$Hazard::DropAddY; + } + + %x = mCos(($Hazard::DropX * $Pi) + $Pi) * mSin(($Hazard::DropY * ($Pi / 2) + ($Pi / 2)) * $Hazard::MaxSlant); + %y = mSin(($Hazard::DropX * $Pi) + $Pi) * mSin(($Hazard::DropY * ($Pi / 2) + ($Pi / 2)) * $Hazard::MaxSlant); + %z = mCos(($Hazard::DropY * ($Pi / 2) + ($Pi / 2)) * $Hazard::MaxSlant); + %dropVec = vectorScale(%x SPC %y SPC -%z,5); + + // Meteors + // Players + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) { + %cl = ClientGroup.getObject(%i); + if (isObject(%cl.player) && !%cl.isJailed) { + %num = getRandom($Hazard::MeteorMin,$Hazard::MeteorMax); + if (%num > 0) + JTLMeteorStorm(%cl,0,$Hazard::MeteorRad,%num,400,600,0,0,0,0,0,0,vectorScale(%dropVec,0.975 + (getRandom() * 0.05)),1); + } + } + // Generators + %count = getWordCount($PowerList); + for(%i = 0; %i < %count; %i++) { + %obj = getWord($PowerList,%i); + if (isObject(%obj)) { + %num = getRandom($Hazard::MeteorMin,$Hazard::MeteorMax); + if (%num > 0) + JTLMeteorStorm(%obj,0,$Hazard::MeteorRad,%num,400,600,0,0,0,0,0,0,vectorScale(%dropVec,0.975 + (getRandom() * 0.05)),1); + } + } + if ($Hazard::StormActive == true) + schedule($Hazard::StormTimer,0,HazardThread,%thread); + else + schedule($Hazard::HazardTimer,0,HazardThread,%thread); +} diff --git a/Scripts/hfunctions.cs b/Scripts/hfunctions.cs new file mode 100644 index 0000000..13455e9 --- /dev/null +++ b/Scripts/hfunctions.cs @@ -0,0 +1,106 @@ +function LightningStrike(%client,%delay) { + if (%client.player) + %pos = %client.player.getTransform(); + %one = getword(%pos, 0) SPC getword(%pos, 1) SPC getword(%pos, 2); + %player = %client.player; + %b = new StaticShape() { + position = %pos; + datablock = "LightningTarget"; + }; + schedule(%delay, 0, "actualstrike", %b); +} + +function actualstrike(%obj) { + %posr = %obj.getTransform(); + %pos = VectorAdd(%posr, "0 0 -20"); + %pos2 = VectorAdd(%posr, "0 0 -20"); + for (%i=0;%i<10;%i++) { + %pos = VectorAdd(%pos, "0 0 20"); + %p[%i] = new Lightning() { + scale = "1 1 1"; + position = %pos; + rotation = "1 0 0 0"; + dataBlock = "DefaultStorm"; + strikesPerMinute = "120"; + strikeWidth = "1.0"; + chanceToHitTarget = "1.0"; + strikeRadius = "1"; + boltStartRadius = "10"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.300000 0.300000 1.000000 1.000000"; + useFog = "1"; + }; + %p[%i].schedule(1500, "delete"); + %p[%i].strikeObject(%player); + } + schedule(1000, 0, "LightningApplyDamage", %p, %pos, %pos2); + if (%obj) + %obj.schedule(1500, "delete"); +} + +function LightningWeaponImage::onFire(%data, %obj, %slot) { + %range = 1000; + %rot = getWords(%obj.getTransform(), 3, 6); + %muzzlePos = %obj.getMuzzlePoint(%slot); + %muzzleVec = %obj.getMuzzleVector(%slot); + %endPos = VectorAdd(%muzzlePos, VectorScale(%muzzleVec, %range)); + %damageMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | + $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | + $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType | + $TypeMasks::TerrainObjectType; //|$TypeMasks::InteriorObjectType; + %hit = ContainerRayCast(%muzzlePos, %endPos, %damageMasks, %obj); + if (%hit) { + %posr = getWords(%hit, 1, 3); + %pos = VectorAdd(%posr, "0 0 300"); + %pos2 = VectorAdd(%posr, "0 0 -20"); + %b = new StaticShape() { + position = %pos2; + datablock = "LightningTarget"; + }; + %p = new Lightning() { + scale = "1 1 1"; + position = %pos; + rotation = "1 0 0 0"; + dataBlock = "DefaultStorm"; + strikesPerMinute = "120"; + strikeWidth = "10.0"; + chanceToHitTarget = "1"; + strikeRadius = "10"; + boltStartRadius = "15"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.300000 0.300000 1.000000 1.000000"; + useFog = "1"; + sourceObject = %obj; + sourceSlot = %slot; + vehicleObject = 0; + }; + %p.strikeObject(%b); + schedule(1000, 0, "LightningApplyDamage", %p, %pos, %pos2); + %p.schedule(1500, "delete"); + %b.schedule(1500, "delete"); + } +} + +function LightningApplyDamage(%p, %pos, %pos2) { + %damageMasks = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType; + %hit = ContainerRayCast(%pos, %pos2, %damageMasks, 1); + if (%hit) + %pos2 = getWords(%hit, 1, 3); + %xy = getWords(%pos, 0, 1); + %z1 = getWord(%pos, 2) - 5; + %z2 = getWord(%pos2, 2) + 5; + while (%z1 > %z2) { + %pos = %xy SPC %z1; + RadiusExplosion(%p, %pos, 40, 0.4, 0, %p.sourceObject, $DamageType::Lightning); + %z1 -= 19; + } + %pos = %xy SPC %z1; + RadiusExplosion(%p, %pos, 40, 0.4, 0, %p.sourceObject, $DamageType::Lightning); +} + +datablock StaticShapeData(LightningTarget) { + shapeFile = "turret_muzzlepoint.dts"; + targetNameTag = 'beacon'; + isInvincible = true; + dynamicType = $TypeMasks::StaticObjectType; +}; diff --git a/Scripts/hud.cs b/Scripts/hud.cs new file mode 100644 index 0000000..73a6300 --- /dev/null +++ b/Scripts/hud.cs @@ -0,0 +1,2001 @@ +//-------------------------------------------------------------------------- +function GameConnection::sensorPing(%this, %ping) +{ + sensorHud.ping = %ping; + sensorHud.update(); +} + +function GameConnection::sensorJammed(%this, %jam) +{ + sensorHud.jam = %jam; + sensorHud.update(); +} + +function SensorHud::update(%this) +{ + if(!%this.ping && !%this.jam) + { + %this.setVisible(false); + sensorHudBack.setVisible(true); + return; + } + + %this.setVisible(true); + sensorHudBack.setVisible(false); + + if(%this.jam) + %this.color = %this.jamColor; + else + %this.color = %this.pingColor; +} + +// - anything which should be reset on new server/mission +function clientCmdResetHud() +{ + deploySensor.setVisible(false); + controlObjectText.setVisible(false); + + sensorHud.jam = false; + sensorHud.ping = false; + sensorHud.update(); +} + +//-------------------------------------------------------------------------- +$vehicleReticle[AssaultVehicle, 1, bitmap] = "gui/hud_ret_tankchaingun"; +$vehicleReticle[AssaultVehicle, 1, frame] = true; +$vehicleReticle[AssaultVehicle, 2, bitmap] = "gui/hud_ret_tankmortar"; +$vehicleReticle[AssaultVehicle, 2, frame] = true; + +$vehicleReticle[BomberFlyer, 1, bitmap] = "gui/hud_ret_shrike"; +$vehicleReticle[BomberFlyer, 1, frame] = false; +$vehicleReticle[BomberFlyer, 2, bitmap] = ""; +$vehicleReticle[BomberFlyer, 2, frame] = false; +$vehicleReticle[BomberFlyer, 3, bitmap] = "gui/hud_ret_targlaser"; +$vehicleReticle[BomberFlyer, 3, frame] = false; + +$vehicleReticle[boat, 1, bitmap] = "gui/ret_mortor"; +$vehicleReticle[boat, 1, frame] = false; + +$vehicleReticle[HeavyChopper, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[HeavyChopper, 1, frame] = false; + +$vehicleReticle[HeavyTank, 1, bitmap] = "gui/hud_ret_sniper"; +$vehicleReticle[HeavyTank, 1, frame] = false; +$vehicleReticle[HeavyTank, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[HeavyTank, 2, frame] = false; + +$vehicleReticle[helicopter, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[helicopter, 1, frame] = false; +$vehicleReticle[helicopter, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[helicopter, 2, frame] = false; + +$vehicleReticle[lightflyer, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[lightflyer, 1, frame] = false; + +$vehicleReticle[scoutflyer, 1, bitmap] = "gui/ret_missile"; +$vehicleReticle[scoutflyer, 1, frame] = false; +$vehicleReticle[scoutflyer, 2, bitmap] = "gui/ret_missile"; +$vehicleReticle[scoutflyer, 2, frame] = false; +$vehicleReticle[scoutflyer, 3, bitmap] = ""; +$vehicleReticle[scoutflyer, 3, frame] = false; + +$vehicleReticle[strikeflyer, 1, bitmap] = "gui/ret_missile"; +$vehicleReticle[strikeflyer, 1, frame] = false; +$vehicleReticle[strikeflyer, 2, bitmap] = "gui/ret_missile"; +$vehicleReticle[strikeflyer, 2, frame] = false; +$vehicleReticle[strikeflyer, 3, bitmap] = ""; +$vehicleReticle[strikeflyer, 3, frame] = false; + +$vehicleReticle[scoutvehicle, 1, bitmap] = "gui/hud_ret_shrike"; +$vehicleReticle[scoutvehicle, 1, frame] = false; + +$vehicleReticle[FFTransport, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[FFTransport, 1, frame] = false; + +$vehicleReticle[Artillery, 1, bitmap] = "gui/ret_mortor"; +$vehicleReticle[Artillery, 1, frame] = false; + +$vehicleReticle[Sub, 1, bitmap] = "gui/ret_chaingun"; +$vehicleReticle[Sub, 1, frame] = false; + +$vehicleReticle[Gunship, 1, bitmap] = "gui/hud_ret_sniper"; +$vehicleReticle[Gunship, 1, frame] = false; +$vehicleReticle[Gunship, 2, bitmap] = "gui/ret_mortor"; +$vehicleReticle[Gunship, 2, frame] = false; + +function GameConnection::setVWeaponsHudActive(%client, %slot) +{ + %veh = %client.player.getObjectMount(); + %vehType = %veh.getDatablock().getName(); + commandToClient(%client, 'setVWeaponsHudActive', %slot, %vehType); +} + +function clientCmdSetVWeaponsHudActive(%num, %vType) +{ + //vWeaponsBox.setActiveWeapon(%num); + if(%num > $numVWeapons) + %num = $numVWeapons; + + for(%i = 1; %i <= $numVWeapons; %i++) + { + %oldHilite = "vWeap" @ %i @ "Hilite"; + %oldHilite.setVisible(false); + } + %newHilite = "vWeap" @ %num @ "Hilite"; + %newHilite.setVisible(true); + + // set the bitmap and frame for the reticle + reticleHud.setBitmap($vehicleReticle[%vType, %num, bitmap]); + reticleFrameHud.setVisible($vehicleReticle[%vType, %num, frame]); +} + +function GameConnection::setVWeaponsHudClearAll(%client) +{ + commandToClient(%client, 'setVWeaponsHudClearAll'); +} + +function clientCmdSetVWeaponsHudClearAll() +{ + //vWeaponsBox.clearAll(); +} + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudBitmap(%client, %slot, %name, %bitmap) +{ + commandToClient(%client, 'setWeaponsHudBitmap',%slot,%name,%bitmap); +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudBitmap(%slot, %name, %bitmap) +{ + $WeaponNames[%slot] = %name; + weaponsHud.setWeaponBitmap(%slot,%bitmap); +} + +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudItem(%client, %name, %ammoAmount, %addItem) +{ + //error("GC:SWHI name="@%name@",ammoAmount="@%ammoAmount@",addItem="@%addItem); +// for(%i = 0; %i < $WeaponsHudCount; %i++) +// if($WeaponsHudData[%i, itemDataName] $= %name) +// { +// if($WeaponsHudData[%i, ammoDataName] !$= "") { +// %ammoInv = %client.player.inv[$WeaponsHudData[%i, ammoDataName]]; +// //error(" ----- player has " @ %ammoInv SPC $WeaponsHudData[%i, ammoDataName]); +// //error("SWHI:Setting weapon "@%name@" ("@%i@") ammo to " @ %ammoInv); +// commandToClient(%client, 'setWeaponsHudItem',%i,%ammoInv, %addItem); +// } +// else { +// //error("SWHI:Setting weapon "@%name@" ("@%i@") ammo to infinite"); +// commandToClient(%client, 'setWeaponsHudItem',%i,-1, %addItem); +// } +// break; +// } + + + // My try... + for(%i = 0; %i < $WeaponsHudCount; %i++) + if($WeaponsHudData[%i, itemDataName] $= %name) + { + if($WeaponsHudData[%i, ammoDataName] !$= "") { + %ammoInv = %client.player.inv[$WeaponsHudData[%i, ammoDataName]]; + //error(" ----- player has " @ %ammoInv SPC $WeaponsHudData[%i, ammoDataName]); + //error("SWHI:Setting weapon "@%name@" ("@%i@") ammo to " @ %ammoInv); + commandToClient(%client, 'setWeaponsHudItem',%i,%ammoInv, %addItem); + } + else { + //error("SWHI:Setting weapon "@%name@" ("@%i@") ammo to infinite"); + commandToClient(%client, 'setWeaponsHudItem',%i,-1, %addItem); + } + break; + } +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudItem(%slot, %ammoAmount, %addItem) +{ + if(%addItem) { + //error("adding weapon to hud in slot " @ %slot @ " with ammo " @ %ammoAmount); + weaponsHud.addWeapon(%slot, %ammoAmount); + } + else { + //error("removing weapon from hud"); + weaponsHud.removeWeapon(%slot); + } +} + +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudAmmo(%client, %name, %ammoAmount) +{ + for(%i = 0; %i < $WeaponsHudCount; %i++) + if($WeaponsHudData[%i, ammoDataName] $= %name) + { + //error("SWHA:Setting ammo "@%name@" for weapon "@%i@" to " @ %ammoAmount); + commandToClient(%client, 'setWeaponsHudAmmo',%i, %ammoAmount); + break; + } +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudAmmo(%slot, %ammoAmount) +{ + weaponsHud.setAmmo(%slot, %ammoAmount); +} + +//---------------------------------------------------------------------------- +// z0dd - ZOD, 9/13/02. Serverside reticles, sever tells client what file to use. +function GameConnection::setWeaponsHudActive(%client, %name, %clearActive) +{ + if(%clearActive) + { + commandToClient(%client, 'setWeaponsHudActive', -1); + } + else + { + for(%i = 0; %i < $WeaponsHudCount; %i++) + { + if($WeaponsHudData[%i, itemDataName] $= %name) + { +// TODO - remove this once a proper fix has been done + if (%client.constructionClient == true && %client.constructionClientVersion == 1) + commandToClient(%client, 'setWeaponsHudActive2', %i, $WeaponsHudData[%i, reticle], $WeaponsHudData[%i, visible]); + else + commandToClient(%client, 'setWeaponsHudActive', %i, $WeaponsHudData[%i, reticle], $WeaponsHudData[%i, visible]); + break; + } + } + } +} + +//---------------------------------------------------------------------------- +// z0dd- ZOD, 9/13/02. Serverside reticles, sever tells client what file to use. +function clientCmdSetWeaponsHudActive(%slot, %ret, %vis) +{ + // z0dd - ZOD, 9/30/02. Changed for lazy scripter backward compatibility. + weaponsHud.setActiveWeapon(%slot); + switch$($WeaponNames[%slot]) + { + case "Blaster": + reticleHud.setBitmap("gui/ret_blaster"); + reticleFrameHud.setVisible(true); + case "Plasma": + reticleHud.setBitmap("gui/ret_plasma"); + reticleFrameHud.setVisible(true); + case "Chaingun": + reticleHud.setBitmap("gui/ret_chaingun"); + reticleFrameHud.setVisible(true); + case "Disc": + reticleHud.setBitmap("gui/ret_disc"); + reticleFrameHud.setVisible(true); + case "GrenadeLauncher": + reticleHud.setBitmap("gui/ret_grenade"); + reticleFrameHud.setVisible(true); + case "SniperRifle": + reticleHud.setBitmap("gui/hud_ret_sniper"); + reticleFrameHud.setVisible(false); + case "ELFGun": + reticleHud.setBitmap("gui/ret_elf"); + reticleFrameHud.setVisible(true); + case "Mortar": + reticleHud.setBitmap("gui/ret_mortor"); + reticleFrameHud.setVisible(true); + case "MissileLauncher": + reticleHud.setBitmap("gui/ret_missile"); + reticleFrameHud.setVisible(true); + case "ShockLance": + reticleHud.setBitmap("gui/hud_ret_shocklance"); + reticleFrameHud.setVisible(false); + case "TargetingLaser": + reticleHud.setBitmap("gui/hud_ret_targlaser"); + reticleFrameHud.setVisible(false); + case "TR2Disc": + reticleHud.setBitmap("gui/ret_disc"); + reticleFrameHud.setVisible(true); + case "TR2GrenadeLauncher": + reticleHud.setBitmap("gui/ret_grenade"); + reticleFrameHud.setVisible(true); + case "TR2Chaingun": + reticleHud.setBitmap("gui/ret_chaingun"); + reticleFrameHud.setVisible(true); + case "TR2Mortar": + reticleHud.setBitmap("gui/ret_mortor"); + reticleFrameHud.setVisible(true); + case "TR2Shocklance": + reticleHud.setBitmap("gui/hud_ret_shocklance"); + reticleFrameHud.setVisible(false); + case "TR2GoldTargetingLaser": + reticleHud.setBitmap("gui/hud_ret_targlaser"); + reticleFrameHud.setVisible(false); + case "TR2SilverTargetingLaser": + reticleHud.setBitmap("gui/hud_ret_targlaser"); + reticleFrameHud.setVisible(false); + default: + reticleHud.setBitmap(%ret); + reticleFrameHud.setVisible(%vis); + } +} + +function clientCmdSetRepairReticle() +{ + reticleHud.setBitmap("gui/ret_chaingun"); + reticleFrameHud.setVisible(true); +} + +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudBackGroundBmp(%client, %name) +{ + commandToClient(%client, 'setWeaponsHudBackGroundBmp',%name); +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudBackGroundBmp(%name) +{ + weaponsHud.setBackGroundBitmap(%name); +} + +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudHighLightBmp(%client, %name) +{ + commandToClient(%client, 'setWeaponsHudHighLightBmp',%name); +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudHighLightBmp(%name) +{ + weaponsHud.setHighLightBitmap(%name); +} + +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudInfiniteAmmoBmp(%client, %name) +{ + commandToClient(%client, 'setWeaponsHudInfiniteAmmoBmp',%name); +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudInfiniteAmmoBmp(%name) +{ + weaponsHud.setInfiniteAmmoBitmap(%name); +} +//---------------------------------------------------------------------------- +function GameConnection::setWeaponsHudClearAll(%client) +{ + commandToClient(%client, 'setWeaponsHudClearAll'); +} + +//---------------------------------------------------------------------------- +function clientCmdSetWeaponsHudClearAll() +{ + weaponsHud.clearAll(); +} + +//---------------------------------------------------------------------------- +// Ammo Hud +//---------------------------------------------------------------------------- +function GameConnection::setAmmoHudCount(%client, %amount) +{ + commandToClient(%client, 'setAmmoHudCount', %amount); +} + +//---------------------------------------------------------------------------- +function clientCmdSetAmmoHudCount(%amount) +{ + if(%amount == -1) + ammoHud.setValue(""); + else + ammoHud.setValue(%amount); +} + +//---------------------------------------------------------------------------- +// Backpack Hud +//---------------------------------------------------------------------------- + +$BackpackHudData[0, itemDataName] = "AmmoPack"; +$BackpackHudData[0, bitmapName] = "gui/hud_new_packammo"; +$BackpackHudData[1, itemDataName] = "CloakingPack"; +$BackpackHudData[1, bitmapName] = "gui/hud_new_packcloak"; +$BackpackHudData[2, itemDataName] = "EnergyPack"; +$BackpackHudData[2, bitmapName] = "gui/hud_new_packenergy"; +$BackpackHudData[3, itemDataName] = "RepairPack"; +$BackpackHudData[3, bitmapName] = "gui/hud_new_packrepair"; +$BackpackHudData[4, itemDataName] = "SatchelCharge"; +$BackpackHudData[4, bitmapName] = "gui/hud_new_packsatchel"; +$BackpackHudData[5, itemDataName] = "ShieldPack"; +$BackpackHudData[5, bitmapName] = "gui/hud_new_packshield"; +$BackpackHudData[6, itemDataName] = "InventoryDeployable"; +$BackpackHudData[6, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[7, itemDataName] = "MotionSensorDeployable"; +$BackpackHudData[7, bitmapName] = "gui/hud_new_packmotionsens"; +$BackpackHudData[8, itemDataName] = "PulseSensorDeployable"; +$BackpackHudData[8, bitmapName] = "gui/hud_new_packradar"; +$BackpackHudData[9, itemDataName] = "TurretOutdoorDeployable"; +$BackpackHudData[9, bitmapName] = "gui/hud_new_packturretout"; +$BackpackHudData[10, itemDataName] = "TurretIndoorDeployable"; +$BackpackHudData[10, bitmapName] = "gui/hud_new_packturretin"; +$BackpackHudData[11, itemDataName] = "SensorJammerPack"; +$BackpackHudData[11, bitmapName] = "gui/hud_new_packsensjam"; +$BackpackHudData[12, itemDataName] = "AABarrelPack"; +$BackpackHudData[12, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[13, itemDataName] = "FusionBarrelPack"; +$BackpackHudData[13, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[14, itemDataName] = "MissileBarrelPack"; +$BackpackHudData[14, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[15, itemDataName] = "PlasmaBarrelPack"; +$BackpackHudData[15, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[16, itemDataName] = "ELFBarrelPack"; +$BackpackHudData[16, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[17, itemDataName] = "MortarBarrelPack"; +$BackpackHudData[17, bitmapName] = "gui/hud_new_packturret"; +$BackpackHudData[18, itemDataName] = "SatchelUnarmed"; +$BackpackHudData[18, bitmapName] = "gui/hud_satchel_unarmed"; + +// TR2 +$BackpackHudData[19, itemDataName] = "TR2EnergyPack"; +$BackpackHudData[19, bitmapName] = "gui/hud_new_packenergy"; + +$BackpackHudData[20, itemDataName] = "LargeInventoryDeployable"; +$BackpackHudData[20, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[21, itemDataName] = "GeneratorDeployable"; +$BackpackHudData[21, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[22, itemDataName] = "SolarPanelDeployable"; +$BackpackHudData[22, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[23, itemDataName] = "SwitchDeployable"; +$BackpackHudData[23, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[24, itemDataName] = "MediumSensorDeployable"; +$BackpackHudData[24, bitmapName] = "gui/hud_new_packradar"; +$BackpackHudData[25, itemDataName] = "LargeSensorDeployable"; +$BackpackHudData[25, bitmapName] = "gui/hud_new_packradar"; +$BackpackHudData[26, itemDataName] = "WallDeployable"; +$BackpackHudData[26, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[27, itemDataName] = "spineDeployable"; +$BackpackHudData[27, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[28, itemDataName] = "mspineDeployable"; +$BackpackHudData[28, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[29, itemDataName] = "FloorDeployable"; +$BackpackHudData[29, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[30, itemDataName] = "JumpadDeployable"; +$BackpackHudData[30, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[31, itemDataName] = "wWallDeployable"; +$BackpackHudData[31, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[32, itemDataName] = "EnergizerDeployable"; +$BackpackHudData[32, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[33, itemDataName] = "spineDeployable"; +$BackpackHudData[33, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[34, itemDataName] = "TreeDeployable"; +$BackpackHudData[34, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[35, itemDataName] = "CrateDeployable"; +$BackpackHudData[35, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[36, itemDataName] = "DecorationDeployable"; +$BackpackHudData[36, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[37, itemDataName] = "LogoProjectorDeployable"; +$BackpackHudData[37, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[38, itemDataName] = "LightDeployable"; +$BackpackHudData[38, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[39, itemDataName] = "TripwireDeployable"; +$BackpackHudData[39, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[40, itemDataName] = "ForceFieldDeployable"; +$BackpackHudData[40, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[41, itemDataName] = "GravityFieldDeployable"; +$BackpackHudData[41, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[42, itemDataName] = "TelePadPack"; +$BackpackHudData[42, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[43, itemDataName] = "DiscTurretDeployable"; +$BackpackHudData[43, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[44, itemDataName] = "TurretLaserDeployable"; +$BackpackHudData[44, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[45, itemDataName] = "TurretMissileRackDeployable"; +$BackpackHudData[45, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[46, itemDataName] = "TurretBasePack"; +$BackpackHudData[46, bitmapName] = "gui/hud_new_packinventory"; +$BackpackHudData[47, itemDataName] = "artillerybarrelpack"; +$BackpackHudData[47, bitmapName] = "gui/hud_new_packturret.png"; +$BackpackHudData[48, itemDataName] = "ParachutePack"; +$BackpackHudData[48, bitmapName] = "gui/hud_new_packenergy"; +$BackpackHudData[49, itemDataName] = "waypointDeployable"; +$BackpackHudData[49, bitmapName] = "gui/hud_new_packenergy"; +$BackpackHudData[50, itemDataName] = "BoosterPack"; +$BackpackHudData[50, bitmapName] = "gui/hud_new_packenergy"; + +$BackpackHudCount = 51; + +function GameConnection::clearBackpackIcon(%client) +{ + commandToClient(%client, 'setBackpackHudItem', 0, 0); +} + +function GameConnection::setBackpackHudItem(%client, %name, %addItem) +{ + for(%i = 0; %i < $BackpackHudCount; %i++) + if($BackpackHudData[%i, itemDataName] $= %name) + commandToClient(%client, 'setBackpackHudItem', %i, %addItem); +} + +function clientCmdSetBackpackHudItem(%num, %addItem) +{ + if(%addItem) + { + backpackIcon.setBitmap($BackpackHudData[%num, bitmapName]); + backpackFrame.setVisible(true); + backpackIcon.setVisible(true); + backpackFrame.pack = true; + } + else + { + backpackIcon.setBitmap(""); + backpackFrame.setVisible(false); + backpackText.setValue(""); + backpackText.setVisible(false); + backpackFrame.pack = false; + } +} + +function GameConnection::updateSensorPackText(%client, %num) +{ + commandToClient(%client, 'updatePackText', %num); +} + +function clientCmdUpdatePackText(%num) +{ + backpackText.setValue(%num); + if(%num == 0) + backpackText.setVisible(false); + else + backpackText.setVisible(true); +} + +// Pack Icons Activate / Deactivate +function clientCmdSetSatchelArmed() +{ + backpackIcon.setBitmap( "gui/hud_satchel_armed" ); +} + +function clientCmdsetCloakIconOn() +{ + backpackIcon.setBitmap( "gui/hud_new_packcloak_armed" ); +} + +function clientCmdsetCloakIconOff() +{ + backpackIcon.setBitmap( "gui/hud_new_packcloak" ); +} + +function clientCmdsetRepairPackIconOn() +{ + backpackIcon.setBitmap( "gui/hud_new_packrepair_armed" ); +} + +function clientCmdsetRepairPackIconOff() +{ + backpackIcon.setBitmap( "gui/hud_new_packrepair" ); +} + +function clientCmdsetShieldIconOn() +{ + backpackIcon.setBitmap( "gui/hud_new_packshield_armed" ); +} + +function clientCmdsetShieldIconOff() +{ + backpackIcon.setBitmap( "gui/hud_new_packshield" ); +} + +function clientCmdsetSenJamIconOn() +{ + backpackIcon.setBitmap( "gui/hud_new_packsensjam_armed" ); +} + +function clientCmdsetSenJamIconOff() +{ + backpackIcon.setBitmap( "gui/hud_new_packsensjam" ); +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +$InventoryHudData[0, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[0, itemDataName] = Grenade; +$InventoryHudData[0, ammoDataName] = Grenade; +$InventoryHudData[0, slot] = 0; +$InventoryHudData[1, bitmapName] = "gui/hud_mine"; +$InventoryHudData[1, itemDataName] = Mine; +$InventoryHudData[1, ammoDataName] = Mine; +$InventoryHudData[1, slot] = 1; +$InventoryHudData[2, bitmapName] = "gui/hud_medpack"; +$InventoryHudData[2, itemDataName] = RepairKit; +$InventoryHudData[2, ammoDataName] = RepairKit; +$InventoryHudData[2, slot] = 3; +$InventoryHudData[3, bitmapName] = "gui/hud_whiteout_gren"; +$InventoryHudData[3, itemDataName] = FlashGrenade; +$InventoryHudData[3, ammoDataName] = FlashGrenade; +$InventoryHudData[3, slot] = 0; +$InventoryHudData[4, bitmapName] = "gui/hud_concuss_gren"; +$InventoryHudData[4, itemDataName] = ConcussionGrenade; +$InventoryHudData[4, ammoDataName] = ConcussionGrenade; +$InventoryHudData[4, slot] = 0; +$InventoryHudData[5, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[5, itemDataName] = FlareGrenade; +$InventoryHudData[5, ammoDataName] = FlareGrenade; +$InventoryHudData[5, slot] = 0; +$InventoryHudData[6, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[6, itemDataName] = CameraGrenade; +$InventoryHudData[6, ammoDataName] = CameraGrenade; +$InventoryHudData[6, slot] = 0; +$InventoryHudData[7, bitmapName] = "gui/hud_beacon"; +$InventoryHudData[7, itemDataName] = Beacon; +$InventoryHudData[7, ammoDataName] = Beacon; +$InventoryHudData[7, slot] = 2; + +// TR2 +$InventoryHudData[8, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[8, itemDataName] = TR2Grenade; +$InventoryHudData[8, ammoDataName] = TR2Grenade; +$InventoryHudData[8, slot] = 0; + +$InventoryHudData[9, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[9, itemDataName] = SmokeGrenade; +$InventoryHudData[9, ammoDataName] = SmokeGrenade; +$InventoryHudData[9, slot] = 0; +$InventoryHudData[10, bitmapName] = "gui/hud_handgren"; +$InventoryHudData[10, itemDataName] = BeaconSmokeGrenade; +$InventoryHudData[10, ammoDataName] = BeaconSmokeGrenade; +$InventoryHudData[10, slot] = 0; + +$InventoryHudCount = 11; + + +//---------------------------------------------------------------------------- +// Inventory Hud +//---------------------------------------------------------------------------- +//------------------------------------------------------------------------- --- +function GameConnection::setInventoryHudBitmap(%client, %slot, %name, %bitmap) +{ + commandToClient(%client, 'setInventoryHudBitmap',%slot,%name,%bitmap); +} + +//---------------------------------------------------------------------------- +function clientCmdSetInventoryHudBitmap(%slot, %name, %bitmap) +{ + inventoryHud.setInventoryBitmap(%slot,%bitmap); +} + +//---------------------------------------------------------------------------- +function GameConnection::setInventoryHudItem(%client, %name, %amount, %addItem) +{ + for(%i = 0; %i < $InventoryHudCount; %i++) + if($InventoryHudData[%i, itemDataName] $= %name) + { + if($InventoryHudData[%i, ammoDataName] !$= "") + commandToClient(%client, 'setInventoryHudItem',$InventoryHudData[%i, slot],%amount, %addItem); + else + commandToClient(%client, 'setInventoryHudItem',$InventoryHudData[%i, slot],-1, %addItem); + break; + } +} + +//---------------------------------------------------------------------------- +function clientCmdSetInventoryHudItem(%slot, %amount, %addItem) +{ + if(%addItem) + inventoryHud.addInventory(%slot, %amount); + else + inventoryHud.removeInventory(%slot); +} + +//---------------------------------------------------------------------------- +function GameConnection::setInventoryHudAmount(%client, %name, %amount) +{ + for(%i = 0; %i < $InventoryHudCount; %i++) + if($InventoryHudData[%i, ammoDataName] $= %name) + { + commandToClient(%client, 'setInventoryHudAmount',$InventoryHudData[%i, slot], %amount); + break; + } +} + +//---------------------------------------------------------------------------- +function clientCmdSetInventoryHudAmount(%slot, %amount) +{ + inventoryHud.setAmount(%slot, %amount); +} + +//---------------------------------------------------------------------------- +function GameConnection::setInventoryHudBackGroundBmp(%client, %name) +{ + commandToClient(%client, 'setInventoryHudBackGroundBmp',%name); +} + +//---------------------------------------------------------------------------- +function clientCmdSetInventoryHudBackGroundBmp(%name) +{ + inventoryHud.setBackGroundBitmap(%name); +} + +//---------------------------------------------------------------------------- +function GameConnection::setInventoryHudClearAll(%client) +{ + commandToClient(%client, 'setInventoryHudClearAll'); +} + +//---------------------------------------------------------------------------- +function clientCmdSetInventoryHudClearAll() +{ + inventoryHud.clearAll(); + backpackIcon.setBitmap( "" ); + backpackFrame.setVisible( false ); + backpackText.setValue( "" ); + backpackText.setVisible(false); + backpackFrame.pack = false; +} + +//---------------------------------------------------------------------------- +// MessageHud +function MessageHud::open(%this) +{ + %offset = 6; + + if(%this.isVisible()) + return; + + if(%this.isTeamMsg) + %text = "TEAM:"; + else + %text = "GLOBAL:"; + + MessageHud_Text.setValue(%text); + + %windowPos = "8 " @ ( getWord( outerChatHud.position, 1 ) + getWord( outerChatHud.extent, 1 ) + 1 ); + %windowExt = getWord( OuterChatHud.extent, 0 ) @ " " @ getWord( MessageHud_Frame.extent, 1 ); + + if( MainVoteHud.isVisible() ) + { + %votePos = firstWord( MainVoteHud.position ) @ " " @ ( getWord( OuterChatHud.extent, 1 ) + getWord( messageHud_Frame.extent, 1 ) + 10 ); + MainVoteHud.position = %votePos; + } + + if( voiceCommHud.isVisible() ) + { + %vCommPos = firstWord( voiceCommHud.position ) SPC ( getWord( OuterChatHud.extent, 1 ) + getWord( messageHud_Frame.extent, 1 ) + 18 ); + voiceCommHud.position = %vCommPos; + } + + %textExtent = getWord(MessageHud_Text.extent, 0); + %ctrlExtent = getWord(MessageHud_Frame.extent, 0); + + Canvas.pushDialog(%this); + + messageHud_Frame.position = %windowPos; + messageHud_Frame.extent = %windowExt; + MessageHud_Edit.position = setWord(MessageHud_Edit.position, 0, %textExtent + %offset); + MessageHud_Edit.extent = setWord(MessageHud_Edit.extent, 0, %ctrlExtent - %textExtent - (2 * %offset)); + + %this.setVisible(true); + deactivateKeyboard(); + MessageHud_Edit.makeFirstResponder(true); +} + +//------------------------------------------------------------------------------ +function MessageHud::close(%this) +{ + if(!%this.isVisible()) + return; + + // readjust vote hud if open + if( MainVoteHud.isVisible() ) + { + %tempY = getWord(outerChatHud.position, 1) + getWord(outerChatHud.extent, 1) + 2; + %mainVoteX = firstWord(mainVoteHud.position); + %voteHudPos = %mainVoteX SPC %tempY; + mainVoteHud.position = %voteHudPos; + } + // put voice comm hud back where it was (if it moved) + %vTempY = getWord(outerChatHud.position, 1) + getWord(outerChatHud.extent, 1) + 12; + %mainCommX = firstWord(voiceCommHud.position); + %commHudPos = %mainCommX SPC %vTempY; + voiceCommHud.position = %commHudPos; + + Canvas.popDialog(%this); + %this.setVisible(false); + if ( $enableDirectInput ) + activateKeyboard(); + MessageHud_Edit.setValue(""); +} + +//------------------------------------------------------------------------------ +function MessageHud::toggleState(%this) +{ + if(%this.isVisible()) + %this.close(); + else + %this.open(); +} + +//------------------------------------------------------------------------------ +function MessageHud_Edit::onEscape(%this) +{ + MessageHud.close(); +} + +//------------------------------------------------------------------------------ +function MessageHud_Edit::eval(%this) +{ + %text = trim(%this.getValue()); + if(%text !$= "") + { + if(MessageHud.isTeamMsg) + commandToServer('teamMessageSent', %text); + else + commandToServer('messageSent', %text); + } + + MessageHud.close(); +} + +//------------------------------------------------------------------------------ +// main chat hud +function MainChatHud::onWake( %this ) +{ + // set the chat hud to the users pref + %this.setChatHudLength( $Pref::ChatHudLength ); +} + +// chat hud sizes +$outerChatLenY[1] = 72; +$outerChatLenY[2] = 140; +$outerChatLenY[3] = 200; + +// size for scroll +$chatScrollLenY[1] = 64; +$chatScrollLenY[2] = 128; +$chatScrollLenY[3] = 192; + +//------------------------------------------------------------------------------ +function MainChatHud::setChatHudLength( %this, %length ) +{ + %outerChatLenX = firstWord(outerChatHud.extent); + %chatScrollLenX = firstWord(chatScrollHud.extent); + %OCHextent = %outerChatLenX SPC $outerChatLenY[%length]; + %CSHextent = %chatScrollLenX SPC $chatScrollLenY[%length]; + + outerChatHud.extent = %OCHextent; + chatScrollHud.extent = %CSHextent; + + %totalLines = HudMessageVector.getNumLines(); + %posLines = %length * 4; + %linesOver = ( %totalLines - %posLines ) * 14; + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " " @ ( $chatScrollLenY[%length] - 6 ); + + if( ( %linesOver > 0 ) && !%sizeIncrease ) + { + %linesOver = %totalLines - %posLines; + %posAdjust = %linesOver * ChatHud.profile.fontSize + 3; + + %newPos = "0" @ " " @ ( -1 * %posAdjust ); + ChatHud.position = %newPos; + } + else if( %sizeIncrease && ( %linesOver > 0 ) ) + { + %curPos = getWord( ChatHud.position, 1 ); + %newY = %curPos + ( 4 * 14 ); + %newPos = "0 " @ %newY; + ChatHud.position = %newPos; + } + else if( %linesOver <= 0 ) + { + ChatHud.position = "0 0"; + } + + // adjust votehud and voicecommhud to be just beneath chathud + %tempY = getWord(outerChatHud.position, 1) + getWord(outerChatHud.extent, 1) + 2; + %vTempY = %tempY + 10; + %mainVoteX = firstWord(mainVoteHud.position); + %vCommX = firstWord(voiceCommHud.position); + %voteHudPos = %mainVoteX SPC %tempY; + %vCommPos = %vCommX SPC %vTempY; + mainVoteHud.position = %voteHudPos; + voiceCommHud.position = %vCommPos; + ChatHud.resize(firstWord(ChatHud.position), getWord(ChatHud.position, 1), firstWord(ChatHud.extent), getWord(ChatHud.extent, 1)); +} + +//------------------------------------------------------------------------------ +function MainChatHud::nextChatHudLen( %this ) +{ + $pref::chatHudLength++; + if($pref::chatHudLength == 4) + { + $pref::chatHudLength = 1; + %sizeIncrease = false; + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " 50"; + } + else + %sizeIncrease = true; + + %outerChatLenX = firstWord(outerChatHud.extent); + %chatScrollLenX = firstWord(chatScrollHud.extent); + %OCHextent = %outerChatLenX SPC $outerChatLenY[$pref::chatHudLength]; + %CSHextent = %chatScrollLenX SPC $chatScrollLenY[$pref::chatHudLength]; + + outerChatHud.extent = %OCHextent; + chatScrollHud.extent = %CSHextent; + + %totalLines = HudMessageVector.getNumLines(); + %posLines = $pref::chatHudLength * 4; + %linesOver = %totalLines - %posLines; + ChatPageDown.position = ( firstWord( outerChatHud.extent ) - 20 ) @ " " @ ( $chatScrollLenY[$pref::chatHudLength] - 6 ); + + if( ( %linesOver > 0 ) && !%sizeIncrease ) + { + %linesOver = %totalLines - %posLines; + %posAdjust = %linesOver * $ShellFontSize; + + %newPos = "0" @ " " @ ( -1 * %posAdjust ); + ChatHud.position = %newPos; + } + else if( %sizeIncrease && ( %linesOver > 0 ) ) + { + %curPos = getWord( ChatHud.position, 1 ); + %newY = %curPos + ( 4 * $ShellFontSize ); + %newPos = "0 " @ %newY; + ChatHud.position = %newPos; + } + else if( %linesOver <= 0 ) + { + ChatHud.position = "0 0"; + } + + // adjust votehud to be just beneath chathud + %tempY = getWord(outerChatHud.position, 1) + getWord(outerChatHud.extent, 1) + 2; + %vTempY = %tempY + 10; + %mainVoteX = firstWord(mainVoteHud.position); + %vCommX = firstWord(voiceCommHud.position); + %voteHudPos = %mainVoteX SPC %tempY; + %vCommPos = %vCommX SPC %vTempY; + mainVoteHud.position = %voteHudPos; + voiceCommHud.position = %vCommPos; + ChatHud.resize(firstWord(ChatHud.position), getWord(ChatHud.position, 1), firstWord(ChatHud.extent), getWord(ChatHud.extent, 1)); +} + +//---------------------------------------------------------------------------- +// MessageHud key handlers +function ToggleMessageHud(%make) +{ + if(%make) + { + MessageHud.isTeamMsg = false; + MessageHud.toggleState(); + } +} + +//------------------------------------------------------------------------------ +function TeamMessageHud(%make) +{ + if(%make) + { + MessageHud.isTeamMsg = true; + MessageHud.toggleState(); + } +} + +//---------------------------------------------------------------------------- +// MessageHud message handlers +function serverCmdTeamMessageSent(%client, %text) { + if(strlen(%text) >= $Host::MaxMessageLen) + %text = getSubStr(%text, 0, $Host::MaxMessageLen); + %text = voicePackCheck(%client,%text); + chatMessageTeam(%client, %client.team, '\c3%1: %2', %client.name, %text); +} + +//------------------------------------------------------------------------------ +function serverCmdMessageSent(%client, %text) { + if(strlen(%text) >= $Host::MaxMessageLen) + %text = getSubStr(%text, 0, $Host::MaxMessageLen); + %text = voicePackCheck(%client,%text); + chatMessageAll(%client, '\c4%1: %2', %client.name, %text); +} + +function voicePackCheck(%client,%text) { + if ($DisableVoicePack) { + if (strStr(%text,"~w") != -1) { + if (!%client.isJailed) { + jailPlayer(%client,0,15); + messageClient(%client,'msgClient','\c2You were put in jail for using an annoying voice pack.'); + messageAllExcept(%client,-1,'msgClient','\c2%1 was put in jail for using an annoying voice pack.',%client.name); + } + %text = "Shazbot!"; + } + } + return %text; +} + +//-------------------------------------------------------------------------- +function toggleHuds(%tag) +{ + if($Hud[%tag] && $Hud[%tag].pushed) + hideHud(%tag); + else + showHud(%tag); +} + +//------------------------------------------------------------------------------ + +//modes are standard, pilot, passenger, object, observer +$HudMode = "Observer"; +$HudModeType = "HoverBike"; +$HudModeNode = 0; +function ClientCmdSetHudMode(%mode, %type, %node) +{ + $HudMode = detag(%mode); + $HudModeType = detag(%type); + $HudModeNode = %node; + + clientCmdDisplayHuds(); +} + +//------------------------------------------------------------------------------ +$ControlObjectReticle[AABarrelLarge, bitmap] = "ret_chaingun"; +$ControlObjectReticle[AABarrelLarge, frame] = true; +$ControlObjectReticle[ELFBarrelLarge, bitmap] = "ret_elf"; +$ControlObjectReticle[ELFBarrelLarge, frame] = true; +$ControlObjectReticle[DeployableIndoorBarrel, bitmap] = "ret_blaster"; +$ControlObjectReticle[DeployableIndoorBarrel, frame] = true; +$ControlObjectReticle[MissileBarrelLarge, bitmap] = "ret_missile"; +$ControlObjectReticle[MissileBarrelLarge, frame] = true; +$ControlObjectReticle[MortarBarrelLarge, bitmap] = "ret_mortor"; // mortor? hahaha +$ControlObjectReticle[MortarBarrelLarge, frame] = true; +$ControlObjectReticle[DeployableOutdoorBarrel, bitmap] = "ret_blaster"; +$ControlObjectReticle[DeployableOutdoorBarrel, frame] = true; +$ControlObjectReticle[PlasmaBarrelLarge, bitmap] = "ret_plasma"; +$ControlObjectReticle[PlasmaBarrelLarge, frame] = true; +$ControlObjectReticle[SentryTurretBarrel, bitmap] = "ret_blaster"; +$ControlObjectReticle[SentryTurretBarrel, frame] = true; + +function setControlObjectReticle(%type) +{ + if($ControlObjectReticle[%type, bitmap] !$= "") + { + reticleHud.setBitmap("gui/" @ $ControlObjectReticle[%type, bitmap]); + reticleFrameHud.setVisible($ControlObjectReticle[%type, frame]); + + retCenterHud.setVisible(true); + } + else + retCenterHud.setVisible(false); +} + +function updateActionMaps() +{ + //pop the action maps... + if ( isObject( moveMap ) ) + moveMap.pop(); + if ( isObject( passengerKeys ) ) + passengerKeys.pop(); + if ( isObject( observerBlockMap ) ) + observerBlockMap.pop(); + if ( isObject( observerMap ) ) + observerMap.pop(); + if ( isObject( pickTeamMap ) ) + pickTeamMap.pop(); + if ( isObject( halftimeMap ) ) + halftimeMap.delete(); + + //if (isObject(flyingCameraMove)) + // flyingCameraMove.pop(); + if (isObject(ControlActionMap)) + ControlActionMap.pop(); + + // push the proper map + switch$ ($HudMode) + { + case "Pilot": + passengerKeys.push(); + + case "Passenger": + moveMap.push(); + + case "Object": + moveMap.push(); + ControlActionMap.push(); + + case "Observer": + moveMap.push(); + if ( isObject( observerBlockMap ) ) + observerBlockMap.delete(); + // Create an action map just to block unwanted parts of the move map: + new ActionMap( observerBlockMap ); + observerBlockMap.blockBind( moveMap, jump ); + observerBlockMap.blockBind( moveMap, mouseFire ); + observerBlockMap.blockBind( moveMap, mouseJet ); + observerBlockMap.blockBind( moveMap, toggleZoom ); + observerBlockMap.blockBind( moveMap, setZoomFOV ); + observerBlockMap.push(); + observerMap.push(); + // Make sure that "Spawn" is bound: + if ( observerMap.getBinding( mouseFire ) $= "" ) + observerMap.copyBind( moveMap, mouseFire ); + + case "PickTeam": + //////////////////////// + // pickTeam Keys + ////////////////////// + if( !isObject( pickTeamMap ) ) + new ActionMap( pickTeamMap ); + pickTeamMap.copyBind( moveMap, toggleMessageHud ); + pickTeamMap.push(); + + case "SiegeHalftime": + new ActionMap( halftimeMap ); + halftimeMap.bindCmd( keyboard, escape, "", "escapeFromGame();" ); + halftimeMap.copyBind( moveMap, toggleMessageHud ); + halftimeMap.copyBind( moveMap, teamMessageHud ); + halftimeMap.copyBind( moveMap, activateChatMenuHud ); + halftimeMap.copyBind( moveMap, resizeChatHud ); + halftimeMap.copyBind( moveMap, pageMessageHudUp ); + halftimeMap.copyBind( moveMap, pageMessageHudDown ); + halftimeMap.copyBind( moveMap, voiceCapture ); + halftimeMap.push(); + + //case 'Standard': + default: + moveMap.push(); + } +} + +//------------------------------------------------------------------------------ +function ClientCmdDisplayHuds() +{ + if ( $LaunchMode $= "InteriorView" ) + return; + + // only update action maps if playGui is current content + %content = Canvas.getContent(); + %PlayGuiActive = isObject(%content) && ( %content.getName() $= "PlayGui" ); + if ( %PlayGuiActive ) + updateActionMaps(); + + ammoHud.setVisible(false); + objectiveHud.setVisible(false); + inventoryHud.setVisible(false); + backpackFrame.setVisible(false); + weaponsHud.setVisible(false); + retCenterHud.setVisible(false); + HudClusterBack.setVisible(false); + outerChatHud.setVisible(false); + clockHud.setVisible(false); + controlObjectText.setVisible(false); + siegeHalftimeHud.setVisible(false); + clientCmdToggleDashHud(false); + %hideCursor = true; + + switch$ ($HudMode) + { + case "Pilot": + clientCmdShowVehicleGauges($HudModeType, $HudModeNode); + clientCmdToggleDashHud(true); + objectiveHud.setVisible(true); + dashBoardHud.setposition(0, 0); + retCenterHud.setVisible(true); + HudClusterBack.setVisible(true); + outerChatHud.setVisible(true); + clockHud.setVisible(true); + + case "Passenger": + clientCmdShowVehicleGauges($HudModeType, $HudModeNode); + clientCmdToggleDashHud(true); + objectiveHud.setVisible(true); + dashBoardHud.setPosition(0, 0); + ammoHud.setVisible(true); + objectiveHud.setVisible(true); + inventoryHud.setVisible(true); + weaponsHud.setVisible(true); + if(backpackFrame.pack) + backpackFrame.setVisible(true); + retCenterHud.setVisible(true); + HudClusterBack.setVisible(true); + outerChatHud.setVisible(true); + clockHud.setVisible(true); + + case "Object": + ammoHud.setVisible(true); + HudClusterBack.setVisible(true); + outerChatHud.setVisible(true); + controlObjectText.setVisible(true); + clockHud.setVisible(true); + + setControlObjectReticle($HudModeType); + + case "Observer": + objectiveHud.setVisible(true); + HudClusterBack.setVisible(true); + outerChatHud.setVisible(true); + clockHud.setVisible(true); + + case "SiegeHalftime": + closeHud( "", "", 'scoreScreen' ); + closeHud( "", "", 'inventoryScreen' ); + closeHud( "", "", 'vehicleHud' ); + objectiveHud.setVisible(true); + outerChatHud.setVisible(true); + siegeHalftimeHud.setVisible(true); + %hideCursor = false; + + case "PickTeam": + ammoHud.setVisible(false); + objectiveHud.setVisible(false); + inventoryHud.setVisible(false); + backpackFrame.setVisible(false); + weaponsHud.setVisible(false); + retCenterHud.setVisible(false); + HudClusterBack.setVisible(false); + outerChatHud.setVisible(true); + controlObjectText.setVisible(false); + clockHud.setVisible(false); + + //case 'Standard': + default: + ammoHud.setVisible(true); + objectiveHud.setVisible(true); + inventoryHud.setVisible(true); + weaponsHud.setVisible(true); + if(backpackFrame.pack) + backpackFrame.setVisible(true); + retCenterHud.setVisible(true); + HudClusterBack.setVisible(true); + outerChatHud.setVisible(true); + clockHud.setVisible(true); + + if(voteHud.voting) + mainVoteHud.setVisible(1); + else + mainVoteHud.setVisible(0); + + } + + if ( PlayGui.hideCursor != %hideCursor ) + { + PlayGui.hideCursor = %hideCursor; + if ( %PlayGuiActive ) + Canvas.updateCursorState(); + } +} + +function dashboardHud::onResize(%this, %width, %height) +{ + %currentWidth = getWord(dashboardHud.getPosition(), 0); + %currentHeight = getWord(dashboardHud.getPosition(), 1); + + %screenWidth = getWord(getResolution(), 0); + %screenHeight = getWord(getResolution(), 1); + + switch$ ($HudMode) + { + case "Pilot": + if(%screenHeight <= 480) + { + if($HudModeNode == 0) + { %xVal = 0; %yVal = 339; } + else + { %xVal = 0; %yVal = 320; } + } + else if(%screenHeight <= 600) + { + if($HudModeNode == 0) + { %xVal = 80; %yVal = 455; } + else + { %xVal = 80; %yVal = 440; } + } + else + { + %xVal = (%screenWidth - 640) / 2; + %yVal = (%screenheight - 480) + 360; + } + + case "Passenger": + %xVal = (%screenWidth - 640) / 2; + %yVal = (%screenheight - 480) + 360; + } + + if(%currentWidth != %xVal || %currentHeight != %yVal) + dashBoardHud.setPosition(%xVal, %yVal); +} + +function clientcmdTogglePlayHuds(%val) +{ + ammoHud.setVisible(%val); + objectiveHud.setVisible(%val); + inventoryHud.setVisible(%val); + if(backpackFrame.pack) + backpackFrame.setVisible(%val); + weaponsHud.setVisible(%val); + retCenterHud.setVisible(%val); + HudClusterBack.setVisible(%val); + outerChatHud.setVisible(%val); + clockHud.setVisible(%val); + + if(%val) + { + if(voteHud.voting) + mainVoteHud.setVisible(1); + } + else + mainVoteHud.setVisible(0); +} + +//------------------------------------------------------------------------------ +function toggleCursorHuds(%tag) +{ + if($Hud[%tag] !$= "" && $Hud[%tag].pushed) + { + hideHud(%tag); + clientCmdTogglePlayHuds(true); + } + else + { + showHud(%tag); + clientCmdTogglePlayHuds(false); + } +} + +//------------------------------------------------------------------------------ +function showHud(%tag) +{ + commandToServer('ShowHud', %tag); +} + +//------------------------------------------------------------------------------ +function serverCmdShowHud(%client, %tag) +{ + %tagName = getWord(%tag, 1); + %tag = getWord(%tag, 0); + messageClient(%client, 'OpenHud', "", %tag); + switch$ (%tag) + { + case 'inventoryScreen': + %client.numFavsCount = 0; + inventoryScreen::updateHud(1,%client,%tag); + case 'vehicleHud': + vehicleHud::updateHud(1,%client,%tag); + case 'scoreScreen': + updateScoreHudThread(%client, %tag); + } +} + +//------------------------------------------------------------------------------ +function updateScoreHudThread(%client, %tag) +{ + Game.updateScoreHud(%client, %tag); + cancel(%client.scoreHudThread); + %client.scoreHudThread = schedule(3000, %client, "updateScoreHudThread", %client, %tag); +} + +//------------------------------------------------------------------------------ +function hideHud(%tag) +{ + commandToServer('HideHud', %tag); +} + +//------------------------------------------------------------------------------ +function serverCmdHideHud(%client, %tag) +{ + %tag = getWord(%tag, 0); + messageClient(%client, 'CloseHud', "", %tag); + switch$ (%tag) + { + case 'scoreScreen': + cancel(%client.scoreHudThread); + %client.scoreHudThread = ""; + } +} + +//------------------------------------------------------------------------------ +addMessageCallback('OpenHud', openHud); +addMessageCallback('CloseHud', closeHud); +addMessageCallback('ClearHud', clearHud); +addMessageCallback('SetLineHud', setLineHud); +addMessageCallback('RemoveLineHud', removeLineHud); + +//------------------------------------------------------------------------------ +function openHud(%msgType, %msgString, %tag) +{ + // Vehicle hud can only be pushed on the PlayGui: + if ( %tag $= 'vehicleHud' && Canvas.getContent() != PlayGui.getId() ) + return; + + %tagName = getWord(%tag, 1); + %tag = getWord(%tag, 0); + if($Hud[%tag] $= "") + { + %tagName.loadHud(%tag); + %tagName.setupHud(%tag); + } + Canvas.pushDialog($Hud[%tag]); + $Hud[%tag].pushed = 1; +} + +//------------------------------------------------------------------------------ +function closeHud(%msgType, %msgString, %tag) +{ + %tag = getWord(%tag, 0); + if($Hud[%tag].pushed) + { + $Hud[%tag].setVisible(false); + Canvas.popDialog($Hud[%tag]); + $Hud[%tag].pushed = 0; + } +} + +//------------------------------------------------------------------------------ +function clearHud(%msgType, %msgString, %tag, %a0) +{ + %tag = getWord(%tag, 0); + %startingLine = detag(%a0); + + while ($Hud[%tag].data[%startingLine, 0] !$= "") + { + for(%i = 0; %i < $Hud[%tag].numCol; %i++) + { + //remove and delete the hud line + %obj = $Hud[%tag].data[%startingLine, %i]; + $Hud[%tag].childGui.remove(%obj); + $Hud[%tag].data[%startingLine, %i] = ""; + %obj.delete(); + } + + %startingLine++; + } + + //don't forget to adjust the size accordingly... + if (%tag $= 'scoreScreen') + { + %height = 0; + %guiCtrl = $Hud[%tag].childGui; + + if(isObject(%guiCtrl)) + { + //set the new extent to be the position + extent of the last element... + %height = 0; + if (%guiCtrl.getCount() > 0) + { + %lastCtrl = %guiCtrl.getObject(%guiCtrl.getCount() - 1); + %height = getWord(%lastCtrl.position, 1) + getWord(%lastCtrl.extent, 1); + } + + //now reset the extent + %guiCtrl.resize(getWord(%guiCtrl.position, 0), getWord(%guiCtrl.position, 1), getWord(%guiCtrl.extent, 0), %height); + } + } +} + +//------------------------------------------------------------------------------ +function removeLineHud(%msgType, %msgString, %hudName, %lineNumber, %a0, %a1, %a2, %a3) +{ + %tag = getWord(%hudName, 0); + %lineNum = detag(%lineNumber); + if($Hud[%tag].data[%lineNum,0] !$= "") + for(%i = 0; %i < $Hud[%tag].numCol; %i++) + { + $Hud[%tag].childGui.remove($Hud[%tag].data[%lineNum, %i]); + $Hud[%tag].data[%lineNum, %i] = ""; + } + + //don't forget to adjust the size accordingly... + if (%tag $= 'scoreScreen') + { + %height = 0; + %guiCtrl = $Hud[%tag].childGui; + + //set the new extent to be the position + extent of the last element... + %height = 0; + if (%guiCtrl.getCount() > 0) + { + %lastCtrl = %guiCtrl.getObject(%guiCtrl.getCount() - 1); + %height = getWord(%lastCtrl.position, 1) + getWord(%lastCtrl.extent, 1); + } + + //now reset the extent + %guiCtrl.resize(getWord(%guiCtrl.position, 0), getWord(%guiCtrl.position, 1), getWord(%guiCtrl.extent, 0), %height); + } +} + +//------------------------------------------------------------------------------ +function setLineHud(%msgType, %msgString, %hudName, %lineNumber, %a0, %a1, %a2, %a3, %a4) +{ + %tag = getWord(%hudName, 0); + %lineNum = detag(%lineNumber); + + if(!isObject($Hud[%tag].data[%lineNum, 0])) + { + $Hud[%tag].numCol = addLine(%tag, %lineNum, %a0, %a1, %a2, %a3); + for(%i = 0; %i < $Hud[%tag].numCol; %i++) + $Hud[%tag].childGui.add($Hud[%tag].data[%lineNum, %i]); + } + + for(%i = 0; %i < $Hud[%tag].numCol; %i++) + $Hud[%tag].data[%lineNum, %i].hudSetValue(detag(%a[%i]),detag(%a4)); + + //don't forget to adjust the size accordingly... + if (%tag $= 'scoreScreen') + { + %height = 0; + %guiCtrl = $Hud[%tag].childGui; + + //set the new extent to be the position + extent of the last element... + %height = 0; + if (%guiCtrl.getCount() > 0) + { + %lastCtrl = %guiCtrl.getObject(%guiCtrl.getCount() - 1); + %height = getWord(%lastCtrl.position, 1) + getWord(%lastCtrl.extent, 1); + } + + //now reset the extent + %guiCtrl.resize(getWord(%guiCtrl.position, 0), getWord(%guiCtrl.position, 1), getWord(%guiCtrl.extent, 0), %height); + } +} + +//------------------------------------------------------------------------------ +function GuiButtonCtrl::hudSetValue(%obj, %text) +{ + %obj.setValue(%text); +} + +//------------------------------------------------------------------------------ +function GuiTextCtrl::hudSetValue(%obj, %text) +{ + %obj.setValue(%text); +} + +//------------------------------------------------------------------------------ +function GuiMLTextCtrl::hudSetValue(%obj, %text) +{ + %obj.setValue(%text); +} + +//------------------------------------------------------------------------------ +function GuiPopUpMenuCtrl::hudSetValue(%obj, %text, %textOverFlow) +{ + if(%textOverFlow !$= "") + %text = %text @ %textOverFlow; + %obj.clear(); + %value = getField(%text,0); + %startVal = 1; + if(%value $= "noSelect") + { + %obj.replaceText(false); + %value = getField(%text,1); + %startVal = 2; + } + else + %obj.replaceText(true); + + %obj.setValue(%value); + if(getFieldCount(%text) > 1) + { + %obj.setActive(true); + for(%i = %startVal; %i < getFieldCount(%text); %i++) + %obj.add(getField(%text, %i), %i); + } + else + %obj.setActive(false); +} + +//------------------------------------------------------------------------------ +function ShellTabButton::hudSetValue( %obj, %text ) +{ + %obj.setText( %text ); +} + +//------------------------------------------------------------------------------ +function addLine(%tag, %lineNum, %a0, %a1, %a2, %a3) +{ + %colNum = 0; + if(isObject($Hud[%tag])) + %colNum = $Hud[%tag].addLine(%tag, %lineNum, detag(%a2), detag(%a3)); + return %colNum; +} + +//------------------------------------------------------------------------------ +function INV_Menu::onSelect( %obj, %index, %text ) +{ + %favList = $Hud['inventoryScreen'].data[0, 1].type TAB $Hud['inventoryScreen'].data[0, 1].getValue(); + for ( %i = 1; %i < $Hud['inventoryScreen'].count; %i++ ) + %favList = %favList TAB $Hud['inventoryScreen'].data[%i, 1].type TAB $Hud['inventoryScreen'].data[%i, 1].getValue(); + commandToServer( 'setClientFav', %favList ); +} + +//------------------------------------------------------------------------------ +function INV_ListMenu::onSelect( %obj, %id, %text, %force ) +{ + // Deselect the current tab ( because it was on the OLD list ): + if ( InventoryScreen.selId !$= "" ) + { + $Hud['inventoryScreen'].staticData[0, InventoryScreen.selId].setValue( false ); + InventoryScreen.selId = ""; + } + + $pref::FavCurrentList = %id; + %favListStart = %id * 10; + + // Select the currently selected favorite if it is now visible: + %tab = $pref::FavCurrentSelect - ( $pref::FavCurrentList * 10 ) + 1; + if ( %tab > 0 && %tab < 11 ) + { + InventoryScreen.selId = %tab; + $Hud['inventoryScreen'].staticData[0, %tab].setValue( true ); + } + + %obj.clear(); + %obj.setValue( %text ); + %count = 10; + %list = 0; + for ( %index = 0; $pref::FavNames[%index] !$= ""; %index++ ) + { + if ( %index >= %count - 1 ) + { + if ( %count != %favListStart + 10 ) + %obj.add( "Favorites " @ %index - 8 @ " - " @ %index + 1, %list ); + + %count += 10; + %list++; + } + } + + for ( %i = 0; %i < 10; %i++ ) + { + $Hud['inventoryScreen'].staticData[0, %i + 1].command = "InventoryScreen.onTabSelect(" @ %favListStart + %i @ ");"; + $Hud['inventoryScreen'].staticData[0, %i + 1].setText( strupr( $pref::FavNames[%favListStart + %i] ) ); + } +} + +//------------------------------------------------------------------------------ +function serverCmdSetClientFav(%client, %text) +{ + if ( getWord( getField( %text, 0 ), 0 ) $= armor ) + { + if (%client.packIndex > 0) + %oldPack = %client.favorites[getField(%client.packIndex,0)]; + if (%client.depIndex > 0) + %oldDep = %client.favorites[getField(%client.depIndex,0)]; + + %client.curFavList = %text; + %validList = checkInventory( %client, %text ); + %client.favorites[0] = getField( %text, 1 ); + %armor = getArmorDatablock( %client, $NameToInv[getField( %validList,1 )] ); + %weaponCount = 0; + %packCount = 0; + %depCount = 0; + %grenadeCount = 0; + %mineCount = 0; + %count = 1; + %client.weaponIndex = ""; + %client.packIndex = ""; + %client.depIndex = ""; + %client.grenadeIndex = ""; + %client.mineIndex = ""; + + for(%i = 3; %i < getFieldCount(%validList); %i = %i + 2) + { + %setItem = false; + switch$ (getField(%validList,%i-1)) + { + case weapon: + if(%weaponCount < %armor.maxWeapons) + { + if(!%weaponCount) + %client.weaponIndex = %count; + else + %client.weaponIndex = %client.weaponIndex TAB %count; + %weaponCount++; + %setItem = true; + } + case pack: + if(%packCount < 1) { + %client.packIndex = %count; + %packCount++; + %setItem = true; + } + case dep: + if(%depCount < 1) { + %client.depIndex = %count; + %depCount++; + %setItem = true; + } + case grenade: + if(%grenadeCount < %armor.maxGrenades) + { + if(!%grenadeCount) + %client.grenadeIndex = %count; + else + %client.grenadeIndex = %client.grenadeIndex TAB %count; + %grenadeCount++; + %setItem = true; + } + case mine: + if(%mineCount < %armor.maxMines) + { + if(!%mineCount) + %client.mineIndex = %count; + else + %client.mineIndex = %client.mineIndex TAB %count; + %mineCount++; + %setItem = true; + } + } + if(%setItem) + { + %client.favorites[%count] = getField(%validList, %i); + %count++; + } + } + %client.numFavs = %count; + %client.numFavsCount = 0; + + if (%client.packIndex > 0 && %client.depIndex > 0) { + %pack = %client.favorites[getField(%client.packIndex,0)]; + %dep = %client.favorites[getField(%client.depIndex,0)]; + if ((%pack !$= "Empty" && %pack !$= "Invalid") && (%dep !$= "Empty" && %dep !$= "Invalid")) { + if (%pack $= %oldPack) + %clearPack = true; + else if (%dep $= %oldDep) + %clearDep = true; + if (%clearPack) + %client.favorites[%client.packIndex] = "EMPTY"; + else if (%clearDep || (!%clearPack && !%clearDep)) + %client.favorites[%client.depIndex] = "EMPTY"; + } + } + + inventoryScreen::updateHud(1, %client, 'inventoryScreen'); + } + + // Thanks Krash. + if(isObject(%client.player) && $Host::PureBuild) + { + buyFavorites(%client); + BottomPrint(%client, "Your inventory has been auto-updated.", 2, 1); + } +} + +//------------------------------------------------------------------------------ +function getCenterPos(%tag) +{ + %TerExtDivX = getWord(PlayGui.extent, 0) / 2; + %TerExtDivY = getWord(PlayGui.extent, 1) / 2; + + %HudExtDivX = getWord($Hud[%tag].extent,0) / 2; + %HudExtDivY = getWord($Hud[%tag].extent,1) / 2; + + %pos = %TerExtDivX - %HudExtDivX @ " " @ %TerExtDivY - %HudExtDivY; + return %pos; +} + +//------------------------------------------------------------------------------ +function hideZoomHud() +{ + ZoomHud.setVisible(false); + ZoomHud.hideThread = 0; +} + +function calcZoomFOV() +{ + if($pref::player::currentFOV == $pref::player::defaultFov / 2) + $pref::player::currentFOV = $pref::player::defaultFov / 5; + else + $pref::player::currentFOV = $pref::player::currentFOV / 2; + + if($pref::player::currentFOV < 4) + $pref::player::currentFOV = $pref::player::defaultFov / 2; + + if(!$ZoomOn) + { + %pos = getZoomCenter($pref::player::defaultFov / $pref::player::currentFOV); + %extent = getZoomExtent($pref::player::defaultFov / $pref::player::currentFOV); + ZoomHud.resize(getWord(%pos, 0), getWord(%pos, 1), getWord(%extent, 0), getWord(%extent, 1)); + if(ZoomHud.hideThread != 0) + cancel(ZoomHud.hideThread); + ZoomHud.hideThread = schedule(5000, 0, hideZoomHud); + ZoomHud.setVisible(true); + } + else + setFov( $pref::player::currentFOV ); +} + +//------------------------------------------------------------------------------ +function getZoomCenter(%power) +{ + %power += (%power/4); + %TerExtDivX = mFloor(getWord(PlayGui.extent, 0) / 2); + %TerExtDivY = mFloor(getWord(PlayGui.extent, 1) / 2); + + %HudExtDivX = mFloor((getWord(PlayGui.extent, 0) / %power)/2); + %HudExtDivY = mFloor((getWord(PlayGui.extent, 1) / %power)/2); + + %pos = %TerExtDivX - %HudExtDivX @ " " @ %TerExtDivY - %HudExtDivY; + return %pos; +} + +//------------------------------------------------------------------------------ +function getZoomExtent(%power) +{ + %power += (%power/4); + %HudExtDivX = mFloor(getWord(PlayGui.extent, 0) / %power); + %HudExtDivY = mFloor(getWord(PlayGui.extent, 1) / %power); + + %val = %HudExtDivX @ " " @ %HudExtDivY; + + return %val; +} + +//------------------------------------------------------------------------------ +function hideAllHuds() +{ + objectiveHud.setVisible( false ); + outerChatHud.setVisible( false ); + energyHud.setVisible( false ); + damageHud.setVisible( false ); + sensorHudBack.setVisible( false ); + controlObjectText.setVisible( false ); +} + +//------------------------------------------------------------------------------ +function restoreAllHuds() +{ + objectiveHud.setVisible( true ); + outerChatHud.setVisible( true ); + energyHud.setVisible( true ); + damageHud.setVisible( true ); + sensorHudBack.setVisible( true ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// voting hud stuff +///////////////////////////////////////////////// +addMessageCallback('clearVoteHud', clearVoteHud); +addMessageCallback('addYesVote', addYesVote); +addMessageCallback('addNoVote', addNoVote); +addMessageCallback('openVoteHud', openVoteHud); +addMessageCallback('closeVoteHud', closeVoteHud); +addMessageCallback('VoteStarted', initVote); + +//------------------------------------------------------------------------------ +function initVote(%msgType, %msgString) +{ + if(!$BottomPrintActive) + { + %yBind = strUpr(getField(moveMap.getBinding(voteYes), 1)); + %nBind = strUpr(getField(moveMap.getBinding(voteNo), 1)); + + %message = detag(%msgString) @ "\nPress " @ %yBind @ " to vote YES or " @ %nBind @ " to vote NO."; + clientCmdBottomPrint(%message, 10, 2); + } +} + +function openVoteHud(%msgType, %msgString, %numClients, %passPercent) +{ + alxPlay(VoteInitiatedSound, 0, 0, 0); + voteHud.voting = true; + + voteHud.totalVotes = 0; + voteHud.size = %numClients; + voteHud.quorum = (%numClients / 2); + + if(voteHud.quorum < 1) + voteHud.quorum = 1; + + voteHud.pass = voteHud.quorum * %passPercent; + + voteHud.setPassValue(%passPercent); + passHash.position = firstWord( mainVoteHud.extent) * %passPercent + 1 @ " -1"; + + if( MessageHud.isVisible() ) + { + %votePos = firstWord( MainVoteHud.position ) @ " " @ ( getWord( OuterChatHud.extent, 1 ) + getWord( messageHud_Frame.extent, 1 ) + 12 ); + MainVoteHud.position = %votePos; + } + else + { + %tempY = getWord(outerChatHud.position, 1) + getWord(outerChatHud.extent, 1) + 2; + %mainVoteX = firstWord(mainVoteHud.position); + %voteHudPos = %mainVoteX SPC %tempY; + mainVoteHud.position = %voteHudPos; + } + + voteHud.setVisible(true); + mainVoteHud.setVisible(true); +} + +function stripBind(%string) +{ + return getSubstr(%string, 9, 90); +} + +//------------------------------------------------------------------------------ +function CloseVoteHud(%msgType, %msgString) +{ + voteHud.setVisible(false); + mainVoteHud.setVisible(false); + voteHud.yesCount = 0; + voteHud.noCount = 0; + voteHud.voting = false; +} + +//------------------------------------------------------------------------------ +function addYesVote(%msgType, %msgString) +{ + voteHud.yesCount++; + voteHud.totalVotes++; + + if(voteHud.isVisible()) + { + voteHud.setYesValue(voteHud.yesCount / voteHud.size); + } +} + +//------------------------------------------------------------------------------ +function addNoVote(%msgType, %msgString) +{ + voteHud.noCount++; + voteHud.totalVotes++; + + if(voteHud.isVisible()) + voteHud.setNoValue(voteHud.noCount / voteHud.size); +} + +//------------------------------------------------------------------------------ +function clearVoteHud(%msgType, %msgString) +{ + voteHud.setYesValue(0.0); + voteHud.setNoValue(0.0); +} + +//------------------------------------------------------------------------------ +function cleanUpHuds() +{ + if($Hud['inventoryScreen'] !$= "") + { + for(%lineNum = 0; $Hud['inventoryScreen'].data[%lineNum, 0] !$= ""; %lineNum++) + for(%i = 0; %i < $Hud['inventoryScreen'].numCol; %i++) + { + $Hud['inventoryScreen'].childGui.remove($Hud['inventoryScreen'].data[%lineNum, %i]); + $Hud['inventoryScreen'].data[%lineNum, %i] = ""; + } + } +} + +function displayObserverHud(%client, %targetClient, %potentialClient) +{ + if (%targetClient > 0) + bottomPrint(%client, "\nYou are now observing: " @ getTaggedString(%targetClient.name), 0, 3); + else if (%potentialClient > 0) + bottomPrint(%client, "\nObserver Fly Mode\n" @ getTaggedString(%potentialClient.name), 0, 3); + else + bottomPrint(%client, "\nObserver Fly Mode", 0, 3); +} + +function hudFirstPersonToggled() +{ + ammoHud.setVisible($firstPerson); +} + +$testCount = 0; + +function testChatHud() +{ + $testCount++; + messageAll( '', "This is test number " @ $testCount ); + $tester = schedule( 50, 0, "testChatHud"); +} + +//------------------------------------------------------------------------- +function HudNetDisplay::getPrefs(%this) +{ + for(%i = 0; %i < 6; %i++) + %this.renderField[%i] = ($pref::Net::graphFields >> %i) & 1; +} + +function NetBarHud::infoUpdate(%this, %ping, %packetLoss, %sendPackets, %sendBytes, %receivePackets, %receiveBytes) +{ + NetBarHudPingText.setText(mFormatFloat(%ping, "%4.0f") @ "ms"); + NetBarHudPacketLossText.setText(mFormatFloat(%packetLoss, "%3.0f") @ "%"); + + NetBarHudSendBar.value = %sendPackets / $pref::Net::PacketRateToServer; + NetBarHudReceiveBar.value = %receivePackets / $pref::Net::PacketRateToClient; +} diff --git a/Scripts/inventory.cs b/Scripts/inventory.cs new file mode 100644 index 0000000..009441b --- /dev/null +++ b/Scripts/inventory.cs @@ -0,0 +1,1057 @@ +//---------------------------------------------------------------------------- + +// 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]; + + %client.getControlObject().use(%data); +} + +function serverCmdThrow(%client,%data) +{ + // Item names from the client must converted + // into DataBlocks + // %data = ItemDataBlock[%item]; + %client.getControlObject().throw(%data); +} + +function serverCmdThrowWeapon(%client,%data) +{ + // Item names from the client must converted + // into DataBlocks + // %data = ItemDataBlock[%item]; + %client.getControlObject().throwWeapon(); +} + +function serverCmdThrowPack(%client,%data) +{ + %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 ) { + %client.getControlObject().selectWeaponSlot( %data ); + if (%client.player) + if (%client.player.getObjectMount()) + callEject(%client.player, %data); +} + +function callEject(%player, %num) { + %veh = %player.getObjectMount(); + if (!(%veh.getType() & $TypeMasks::VehicleObjectType)) + return; + if (%veh.getMountNodeObject(0) == %player) { + %obj = %veh.getMountNodeObject(%num); + if(%obj) { + if (%obj.getType() & $TypeMasks::PlayerObjectType) { + %obj.getDataBlock().doDismount(%obj, 0, 1); + } + } + } + else if(%num == 5) + %player.getDataBlock().doDismount(%player, 0, 1); +} + +function serverCmdCycleWeapon( %client, %data ) { + %veh = 0; + if (%client.player.station) { + if (%client.player.station.getDataBlock().getName() $= "StationVehicle") + %veh = 1; + } + if (%veh) + cycleVehicleHud(%client.player,%client,%data); + else + %client.getControlObject().cycleWeapon( %data ); +} + +function serverCmdStartThrowCount(%client, %data) +{ + %client.player.throwStart = getSimTime(); +} + +function serverCmdEndThrowCount(%client, %data) +{ + if(%client.player.throwStart == 0) + return; + + // throwStrength will be how many seconds the key was held + %throwStrength = (getSimTime() - %client.player.throwStart) / 150; + // trim the time to fit between 0.5 and 1.5 + if(%throwStrength > 1.5) + %throwStrength = 1.5; + else if(%throwStrength < 0.5) + %throwStrength = 0.5; + + %throwScale = %throwStrength / 2; + %client.player.throwStrength = %throwScale; + + %client.player.throwStart = 0; +} + +//---------------------------------------------------------------------------- + +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) +{ + %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.className $= "WeaponImage") { + if($Rank::Score[%obj.client.ranknum] < %this.minRankPoints) { + return; + } + } + +if(%data.className $= "PackImage") { + if($Rank::Score[%obj.client.ranknum] < %this.minRankPoints) { + return; + } + } + if(%data $= Grenade) { + // Weapon modes + + //[most] 'ey lets do some unification here.. :D + if(%this.getMountedImage(0) && GetWord($weaponSettings1[%this.getMountedImage(0).getName()],0)) { + if (!(GetSimTime() > (%this.grenadeModeTime + 100))) + return; + %this.grenadeModeTime = getSimTime(); //not 'that' unified.. ;) + + return %this.getMountedImage(0).ChangeMode(%this,1,2); //looksie.. 2 stands for weapons (level 1) + + } + + //[most] + + //start of modifier code + if (%this.getMountedImage(0).getname() $= "MergeToolImage") + { + %this.client.MTSubMode++; + if (%this.client.MTMode == 0 && %this.client.MTSubMode == 2) + %this.client.MTSubMode = 0; + if (%this.client.MTMode == 1 && %this.client.MTSubMode == 2) + %this.client.MTSubMode = 0; + if (%this.client.MTMode == 2 && %this.client.MTSubMode == 8) + %this.client.MTSubMode = 0; + + MTShowStatus(%this.client); + return; + } + //end of modifier modes + // Editor Tool Start + if (%this.usingEditorTool && getSimTime() > (%this.grenadeModeTime + 100) && %this.performing == 0) + { + %this.grenadeModeTime = getSimTime(); + %this.ETSubMode++; + if($EditorTool[%this.ETMode, %this.ETSubMode] $= "") + %this.ETSubMode = 1; + ETMessage(%this.client); + return; + } + //Editor Tool End + if (%this.usingTextureTool && getSimTime() > (%this.grenadeModeTime + 100) && %this.performing == 0) + { + %this.grenadeModeTime = getSimTime(); + %this.TTSubMode++; + if($TextureTool[%this.TTMode, %this.TTSubMode] $= "") + %this.TTSubMode = 1; + TTMessage(%this.client); + return; + } + // Con Tool + if (%this.usingConstructionTool == 1 && getSimTime() > (%this.grenadeModeTime + 100) && %this.performing == 0) { + %this.grenadeModeTime = getSimTime(); + if (%this.constructionToolMode == 0) { + if (%this.constructionToolMode2 == 1) { + %this.constructionToolMode2 = 0; + bottomPrint(%this.client,"Normal deconstruction",2,1); + return; + } + else { + %this.constructionToolMode2 = 1; + bottomPrint(%this.client,"Cascading deconstruction",2,1); + return; + } + } + else if (%this.constructionToolMode == 1) { + if (%this.constructionToolMode2 == 1) { + %this.constructionToolMode2 = 0; + bottomPrint(%this.client,"Rotate push",2,1); + return; + } + else { + %this.constructionToolMode2 = 1; + bottomPrint(%this.client,"Rotate pull",2,1); + return; + } + } + else if (%this.constructionToolMode == 2) { + if (%this.constructionToolMode2 == 5) { + %this.constructionToolMode2 = 0; + bottomPrint(%this.client,"Select target as center of rotation",2,1); + return; + } + else if (%this.constructionToolMode2 == 0) { + %this.constructionToolMode2 = 1; + bottomPrint(%this.client,"Select objects to rotate",2,1); + return; + } + else if (%this.constructionToolMode2 == 1) { + %this.constructionToolMode2 = 2; + bottomPrint(%this.client,"Select rotation speed",2,1); + return; + } + else if (%this.constructionToolMode2 == 2) { + %this.constructionToolMode2 = 3; + bottomPrint(%this.client,"Apply rotation",2,1); + return; + } + else if (%this.constructionToolMode2 == 3) { + %this.constructionToolMode2 = 4; + bottomPrint(%this.client,"Display selection",2,1); + return; + } + else { + %this.constructionToolMode2 = 5; + bottomPrint(%this.client,"Clear list",2,1); + return; + } + } + else { + if (%this.constructionToolMode2 == 3) { + %this.constructionToolMode2 = 0; + bottomPrint(%this.client,"Toggle generator power state",2,1); + return; + } + else if (%this.constructionToolMode2 == 0) { + %this.constructionToolMode2 = 1; + bottomPrint(%this.client,"Increase current frequency",2,1); + return; + } + else if (%this.constructionToolMode2 == 1) { + %this.constructionToolMode2 = 2; + bottomPrint(%this.client,"Decrease current frequency",2,1); + return; + } + else { + %this.constructionToolMode2 = 3; + bottomPrint(%this.client,"Read power state",2,1); + return; + } + } + } + if (%this.usingSuperChaingun == 1) { + if (!(getSimTime() > (%this.grenadeModeTime + 100))) + return; + %this.grenadeModeTime = getSimTime(); + if (%this.superChaingunMode == 1) { + if ($Ion::StopIon == 1) { + $Ion::StopIon = 0; + displaySCGStatus(%this); + return; + } + else { + $Ion::StopIon = 1; + displaySCGStatus(%this); + return; + } + } + } + // 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 $= "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 ) { + if (!$Host::SatchelChargeEnabled) { + if (isObject(%this.client)) { + if ($Host::Purebuild == 1) + messageAll('msgClient','\c2%1 just tried to detonate a Satchel Charge!',%this.client.name); + else + messageTeam(%this.client.team,'msgClient','\c2%1 just tried to detonate a Satchel Charge!',%this.client.name); + %this.client.clearBackPackIcon(); + } + %this.thrownChargeId.delete(); + %this.thrownChargeId = "0"; + return; + } + else { + %this.playAudio( 0, SatchelChargeExplosionSound ); + schedule( 800, %this, "detonateSatchelCharge", %this ); + return true; + } + } + return false; + } + else if(%data $= Beacon) { + %data.onUse(%this); + if (%this.inv[%data.getName()] > 0) + return true; + } + // Pack modes + if (%data $= "RepairKit") { + if ($Host::ExpertMode == 1) { // Only use in Expert Mode + if (%this.hasForceField) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["forcefield"]) + %this.expertSet = 0; + %line = $expertSetting["forcefield",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + //[most] Again using the unified plugin code. See item.cs for reference. + else if(%this.getMountedImage(2) && GetWord($packSettings[%this.getMountedImage(2).getName()],0)) { + %changed = %this.getMountedImage(2).ChangeMode(%this,1,1); + return ""; + } + //[most] + else if (%this.hasGravField) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["gravfield"]) + %this.expertSet = 0; + %line = $expertSetting["gravfield",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + else if (%this.hasFloor) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["floor"]) + %this.expertSet = 0; + %line = $expertSetting["floor",%this.expertSet]; + bottomPrint(%this.client,"Floor set to" SPC %line,2,1); + return; + } + else if (%this.hasBlast) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["blast"]) + %this.expertSet = 0; + %line = $expertSetting["blast",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + else if (%this.hasWalk) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["walk"]) + %this.expertSet = 0; + %line = $expertSetting["walk",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + else if (%this.hasMSpine) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["mspine"]) + %this.expertSet = 0; + %line = $expertSetting["mspine",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + else if (%this.hasSwitch) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["switch"]) + %this.expertSet = 0; + %line = $expertSetting["switch",%this.expertSet]; + bottomPrint(%this.client,%line,2,1); + return; + } + else if (%this.hasTree) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["tree"]) + %this.expertSet = 0; + %line = $expertSetting["tree",%this.expertSet]; + bottomPrint(%this.client,"Tree set to " @ %line * 100 @ "% scale",2,1); + return; + } + else if (%this.hasTele) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["telepad"]) + %this.expertSet = 0; + %line = $expertSetting["telepad",%this.expertSet]; + bottomPrint(%this.client,"Telepad set to " @ %line,2,1); + return; + } + else if (%this.hasLight) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["light"]) + %this.expertSet = 0; + %line = $expertSetting["light",%this.expertSet]; + bottomPrint(%this.client,"Light pack set to " @ %line,2,1); + return; + } + else if (%this.hasDoor) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["Door"]) + %this.expertSet = 0; + %line = $expertSetting["Door",%this.expertSet]; + bottomPrint(%this.client,"Door close timeout:" SPC %line,2,1); + return; + } + else if (%this.hasZspawn) { + %this.expertSet++; + if (%this.expertSet > $expertSettings["Zspawn"]) + %this.expertSet = 0; + %line = $expertSetting["ZSpawn",%this.expertSet]; + bottomPrint(%this.client,"ZSpawn type set to:" SPC %line,2,1); + return; + } + if (%this.getDamageLevel() != 0 && $Host::Purebuild == 1) { + %this.applyRepair(0.2); + messageClient(%this.client, 'MsgRepairKitUsed', '\c2Repair Kit Used.'); + return; + } + } + } + // Weapon modes + if (%data $= "Mine") { + + //[most] 'ey lets do some more unification here.. :D + if(%this.getMountedImage(0) && GetWord($weaponSettings2[%this.getMountedImage(0).getName()],0)) { + if(!GetSimTime() > (%this.grenadeModeTime + 100)) + return; + %this.grenadeModeTime = getSimTime(); //not 'that' unified.. ;) + + return %this.getMountedImage(0).ChangeMode(%this,1,3); //looksie.. 3 stands for weapons (level 2) + } + + //[most] + //modifier tool + if (%this.getMountedImage(0).getname() $= "MergeToolImage") + { + %this.client.MTMode++; + %this.client.MTSubMode = 0; + if (%this.client.MTMode >= 3) + %this.client.MTMode = 0; + + MTShowStatus(%this.client); + return; + } + + //end modifier tool + // Start Editor Tool + if (%this.usingEditorTool && getSimTime() > (%this.grenadeModeTime + 100)) + { + %this.grenadeModeTime = getSimTime(); + %this.ETMode++; + if(%this.ETMode > 4) + %this.ETMode = 1; + %this.ETSubMode = 1; + ETMessage(%this.client); + } + // End Editor Tool + if (%this.usingTextureTool && getSimTime() > (%this.grenadeModeTime + 100)) + { + %this.grenadeModeTime = getSimTime(); + %this.TTMode++; + if(%this.TTMode > 5) + %this.TTMode = 1; + %this.TTSubMode = 1; + TTMessage(%this.client); + } + //Hooker tool + if (%this.usingHooker == 1 && getSimTime() > (%this.grenadeModeTime + 100) && %this.performing == 0) { + %this.grenadeModeTime = getSimTime(); + if (%this.HookerMode == 2 || %this.HookerMode $= ""){ + %this.HookerMode = 1; + bottomPrint(%this.client,"Roping Tool set to Grapple",2,1); + } + else if (%this.HookerMode == 1){ + %this.HookerMode = 2; + bottomPrint(%this.client,"Roping Tool set to Rope Objects",2,1); + } + return; + } + //end Hooker tool + if (%this.usingConstructionTool == 1 && getSimTime() > (%this.mineModeTime + 100) && %this.performing == 0) { + %this.mineModeTime = getSimTime(); + if (%this.constructionToolMode == 3) { + %this.constructionToolMode = 0; + bottomPrint(%this.client,"Construction Tool mode set to deconstruct",2,1); + } + else if (%this.constructionToolMode == 0) { + %this.constructionToolMode = 1; + bottomPrint(%this.client,"Construction Tool mode set to rotate",2,1); + } + else if (%this.constructionToolMode == 1) { + %this.constructionToolMode = 2; + bottomPrint(%this.client,"Construction Tool mode set to advanced rotate",2,1); + } + else { + %powerFreq = %this.powerFreq; + if (%powerFreq < 1 || %powerFreq > upperPowerFreq(%this) || !%powerFreq) + %powerFreq = 1; + %this.powerFreq = %powerFreq; + %this.constructionToolMode = 3; + bottomPrint(%this.client,"Construction Tool mode set to power management\nPower frequency currently set to: " @ %this.powerFreq,2,2); + } + %this.constructionToolMode2 = 0; + return; + } + if (%this.usingMedGun == 1) { + if (!(getSimTime() > (%this.mineModeTime + 100))) + return; + + %this.mineModeTime = getSimTime(); + if(%this.reviveMode == 1) { + %this.reviveMode = 2; + Bottomprint(%this.client, "Medpack mode set to cure infection.", 5, 1); + } else { + %this.reviveMode = 1; + Bottomprint(%this.client, "Medpack mode set to revive the dead.", 5, 1); + } + + return; + } + if (%this.usingSuperChaingun == 1) { + if (!(getSimTime() > (%this.mineModeTime + 100))) + return; + %this.mineModeTime = getSimTime(); + %this.superChaingunMode++; + if (%this.superChaingunMode > 6 - (5 * $host::nopulseSCG)) { + %this.superChaingunMode = 0; + } + displaySCGStatus(%this); + %this.superChaingunMode2 = 0; + return; + } + } + // default case + if (isObject(%data)) { + 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) { + if($TestCheats && %this.getDatablock().max[%data.getName()] !$= "") + return 999; + else + return %this.getDatablock().max[%data.getName()]; +} + +function ShapeBase::incInventory(%this,%data,%amount) { + %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, 5/27/02. Fixes flags hovering + // over friendly player when collision occurs + if(%obj.getDataBlock().getName() $= "Flag") + %obj.static = false; + //------------------------------------------- + + //if the object is being thrown by a corpse, use a random vector + if (%this.getState() $= "Dead") + { + %vec = (-1.0 + getRandom() * 2.0) SPC (-1.0 + getRandom() * 2.0) SPC getRandom(); + %vec = vectorScale(%vec, 10); + } + + // else Initial vel based on the dir the player is looking + else + { + %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 8",1 - %dot)); + + // 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, 40); + + // + %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) +{ + %this.setInventory(RepairKit,0); + + %this.setInventory(Mine,0); + //%this.setInventory(MineAir,0); + //%this.setInventory(MineLand,0); + //%this.setInventory(MineSticky,0); + %this.setInventory(ConstructionTool,0); + %this.setInventory(Grenade,0); + %this.setInventory(SmokeGrenade,0); + %this.setInventory(BeaconSmokeGrenade,0); + %this.setInventory(FlashGrenade,0); + %this.setInventory(ConcussionGrenade,0); + %this.setInventory(FlareGrenade,0); + %this.setInventory(CameraGrenade, 0); + + %this.setInventory(Blaster,0); + %this.setInventory(Plasma,0); + %this.setInventory(Disc,0); + %this.setInventory(Chaingun, 0); + %this.setInventory(Mortar, 0); + %this.setInventory(GrenadeLauncher, 0); + %this.setInventory(MissileLauncher, 0); + %this.setInventory(SniperRifle, 0); + %this.setInventory(TargetingLaser, 0); + %this.setInventory(ELFGun, 0); + %this.setInventory(ShockLance, 0); + + %this.setInventory(PlasmaAmmo,0); + %this.setInventory(ChaingunAmmo, 0); + %this.setInventory(DiscAmmo, 0); + %this.setInventory(GrenadeLauncherAmmo, 0); + %this.setInventory(MissileLauncherAmmo, 0); + %this.setInventory(MortarAmmo, 0); + %this.setInventory(Beacon, 0); + + %this.setInventory(SuperChaingun, 0); + %this.setInventory(SuperChaingunAmmo, 0); + %this.setInventory(RPChaingun, 0); + %this.setInventory(RPChaingunAmmo, 0); + %this.setInventory(snipergun, 0); + %this.setInventory(snipergunAmmo, 0); + %this.setInventory(bazooka, 0); + %this.setInventory(bazookaAmmo, 0); + %this.setInventory(MG42, 0); + %this.setInventory(MG42Ammo, 0); + %this.setInventory(flamer, 0); + %this.setInventory(flamerAmmo, 0); + %this.setInventory(AALauncher, 0); + %this.setInventory(AALAuncherAmmo, 0); + %this.setInventory(kriegRifle, 0); + %this.setInventory(KriegAmmo, 0); + %this.setInventory(Shotgun, 0); + %this.setInventory(ShotgunAmmo, 0); + %this.setInventory(RShotgun, 0); + %this.setInventory(RShotgunAmmo, 0); + %this.setInventory(LMissileLauncher, 0); + %this.setInventory(LMissileLauncherAmmo, 0); + %this.setInventory(HRPChaingun, 0); + %this.setInventory(RPGAmmo, 0); + %this.setInventory(LSMG, 0); + %this.setInventory(LSMGAmmo, 0); + %this.setInventory(PBC, 0); + %this.setInventory(PBCAmmo, 0); + + %this.setInventory(MergeTool, 0); + %this.setInventory(EditingTool, 0); + %this.setInventory(TextureTool, 0); + %this.setInventory(LordAcidGun, 0); + %this.setInventory(DZShot, 0); + %this.setInventory(RailGun, 0); + %this.setInventory(M4, 0); + %this.setInventory(NapalmMortar, 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; + %player.setInventory(RepairKit,999); + %player.setInventory(Mine,999); + //%player.setInventory(MineAir,999); + //%player.setInventory(MineLand,999); + //%player.setInventory(MineSticky,999); + %player.setInventory(Grenade,999); + %player.setInventory(SmokeGrenade,999); + %player.setInventory(BeaconSmokeGrenade,999); + %player.setInventory(FlashGrenade,999); + %player.setInventory(FlareGrenade,999); + %player.setInventory(ConcussionGrenade,999); + %player.setInventory(CameraGrenade, 999); + %player.setInventory(Blaster,1); + %player.setInventory(Plasma,1); + %player.setInventory(Chaingun, 1); + %player.setInventory(Disc,1); + %player.setInventory(GrenadeLauncher, 1); + %player.setInventory(SniperRifle, 1); + %player.setInventory(ELFGun, 1); + %player.setInventory(Mortar, 1); + %player.setInventory(MissileLauncher, 1); + %player.setInventory(ShockLance, 1); + %player.setInventory(TargetingLaser, 1); + %player.setInventory(MissileLauncherAmmo, 999); + %player.setInventory(GrenadeLauncherAmmo, 999); + %player.setInventory(MortarAmmo, 999); + %player.setInventory(PlasmaAmmo,999); + %player.setInventory(ChaingunAmmo, 999); + %player.setInventory(DiscAmmo, 999); + %player.setInventory(Beacon, 999); + + %player.setInventory(RPChaingun, 1); + %player.setInventory(RPChaingunAmmo, 999); + %player.setInventory(snipergun, 1); + %player.setInventory(snipergunAmmo, 999); + %player.setInventory(Bazooka, 1); + %player.setInventory(BazookaAmmo, 999); + %player.setInventory(MG42, 1); + %player.setInventory(MG42Ammo, 999); + %player.setInventory(flamer,1); + %player.setInventory(flamerAmmo,999); + %player.setInventory(AALauncher,1); + %player.setInventory(AALauncherAmmo,999); + %player.setInventory(KriegRifle,1); + %player.setInventory(KriegAmmo,999); + %player.setInventory(Shotgun,1); + %player.setInventory(ShotgunAmmo,999); + %player.setInventory(RShotgun,1); + %player.setInventory(RShotgunAmmo,999); + %player.setInventory(LMissileLauncher,1); + %player.setInventory(LMissileLauncher,999); + %player.setInventory(HRPChaingun, 1); + %player.setInventory(RPGAmmo, 999); + %player.setInventory(LSMG, 1); + %player.setInventory(LSMGAmmo, 999); + %player.setInventory(PBC, 1); + %player.setInventory(PBCAmmo, 999); + + %player.setInventory(MergeTool, 1); + %player.setInventory(LordAcidGun, 1); + %player.setInventory(DZShot, 1); + %player.setInventory(RailGun, 1); + %player.setInventory(NapalmMortar, 1); + %player.setInventory(M4, 1); + %player.setInventory(EditingTool, 1); + %player.setInventory(TextureTool, 1); + } +} diff --git a/Scripts/inventoryHud.cs b/Scripts/inventoryHud.cs new file mode 100644 index 0000000..7bbeab2 --- /dev/null +++ b/Scripts/inventoryHud.cs @@ -0,0 +1,1679 @@ +//------------------------------------------------------------------------------ +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] = "Technician"; +$InvArmor[1] = "Spec-Ops"; +$InvArmor[2] = "Commando"; +$InvArmor[3] = "Powered Armor"; + +$NameToInv["Technician"] = "Light"; +$NameToInv["Commando"] = "Medium"; +$NameToInv["Powered Armor"] = "Heavy"; +$NameToInv["Purebuild"] = "Pure"; +$NameToInv["Spec-Ops"] = "SpecOps"; + +$InvWeapon[0] = "Plasma Rifle"; +$InvWeapon[1] = "Chaingun"; +$InvWeapon[2] = "Spinfusor"; +$InvWeapon[3] = "Grenade Launcher"; +$InvWeapon[4] = "Fusion Mortar"; +$InvWeapon[5] = "Missile Launcher"; +// AO +$InvWeapon[6] = "TR2 Spinfusor"; +$InvWeapon[7] = "TR2 Grenade Launcher"; +$InvWeapon[8] = "TR2 Chaingun"; +$InvWeapon[9] = "TR2 Shocklance"; +$InvWeapon[10] = "TR2 Mortar"; +// END AO + +$InvWeapon[11] = "Construction Tool"; +$InvWeapon[12] = "Super Chaingun"; +$InvWeapon[13] = "Sniper Rifle"; +$InvWeapon[14] = "Bazooka"; +$InvWeapon[15] = "M32 Assault Rifle"; +$InvWeapon[16] = "SAW"; +$InvWeapon[17] = "Flame Thrower"; +$InvWeapon[18] = "AA Rocket Launcher"; +$InvWeapon[19] = "Rifle"; +$InvWeapon[20] = "Shotgun"; +$InvWeapon[21] = "Rotary Shotgun"; +$InvWeapon[22] = "AT6 Rocket Launcher"; +$InvWeapon[23] = "M32 with RPG"; +$InvWeapon[24] = "M79 RPG Launcher"; +$InvWeapon[25] = "MP12 SMG"; +$InvWeapon[26] = "Merge Tool"; +$InvWeapon[27] = "PBC Cannon"; +$InvWeapon[28] = "LZombie Acid"; +$InvWeapon[29] = "Editing Tool"; +$InvWeapon[30] = "Gauss Cannon"; +$InvWeapon[31] = "Napalm Mortar"; +$InvWeapon[32] = "Texture Tool"; +$InvWeapon[33] = "Demon Zombie Shot"; + +$NameToInv["Plasma Rifle"] = "Plasma"; +$NameToInv["Chaingun"] = "Chaingun"; +$NameToInv["Spinfusor"] = "Disc"; +$NameToInv["Grenade Launcher"] = "GrenadeLauncher"; +$NameToInv["Fusion Mortar"] = "Mortar"; +$NameToInv["Missile Launcher"] = "MissileLauncher"; +$NameToInv["Construction Tool"] = "ConstructionTool"; +$NameToInv["Editor Tool"] = "EditorTool"; +$NameToInv["Super Chaingun"] = "SuperChaingun"; +$NameToInv["M32 Assault Rifle"] = "RPChaingun"; +$NameToInv["Sniper Rifle"] = "snipergun"; +$NameToInv["Bazooka"] = "Bazooka"; +$NameToInv["Gauss Cannon"] = "RailGun"; +$NameToInv["SAW"] = "MG42"; +$NameToInv["Flame Thrower"] = "flamer"; +$NameToInv["AA Rocket Launcher"] = "AALauncher"; +$NameToInv["M79 RPG Launcher"] = "M4"; +$NameToInv["Rifle"] = "KriegRifle"; +$NameToInv["Shotgun"] = "Shotgun"; +$NameToInv["Rotary Shotgun"] = "RShotgun"; +$NameToInv["AT6 Rocket Launcher"] = "LMissileLauncher"; +$NameToInv["M32 with RPG"] = "HRPChaingun"; +$NameToInv["MP12 SMG"] = "LSMG"; +$NameToInv["Merge Tool"] = "MergeTool"; +$NameToInv["PBC Cannon"] = "PBC"; +$NameToInv["LZombie Acid"] = "LordAcidGun"; +$NameToInv["Demon Zombie Shot"] = "DZShot"; +$NameToInv["Editing Tool"] = "EditingTool"; +$NameToInv["Napalm Mortar"] = "NapalmMortar"; +$NameToInv["Texture Tool"] = "TextureTool"; +$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"; +// END AO + +$InvPack[0] = "Repair Pack"; +$InvPack[1] = "Ammunition Pack"; +$InvPack[2] = "Jet Booster Pack"; +$InvPack[3] = "Satchel Charge"; +$InvPack[4] = "Motion Sensor Pack"; +$InvPack[5] = "Pulse Sensor Pack"; +$InvPack[6] = "Antidote Station"; +$InvPack[7] = "Anti-Infantry Turret"; +$InvPack[8] = "Emplacement Turret"; +$InvPack[9] = "Medic Pack"; +$InvPack[10] = "Chaingun Turret Barrel"; +$InvPack[11] = "Flame Turret Barrel"; +$InvPack[12] = "Flak Turret Barrel"; +$InvPack[13] = "S.A.M. Turret Barrel"; +$InvPack[14] = "Artillery Barrel"; +// TR2 +$InvPack[15] = "Laser Turret"; + +//This can be made plugin compatible by using the mpm missile warhead plugin code. +//It is even possible to do smart sorting. ie. when 1 list is full.. put them in the other list. +//[most] +$InvPack[16] = "Artillery Reloader Pack"; +$InvPack[17] = "Deployable Vehicle Pad"; +$InvPack[18] = "Deployable Emitter Pack"; +$InvPack[19] = "Deployable Audio Pack"; +$InvPack[20] = "Deployable Pack Dispenser"; +//[most] +$InvPack[21] = "Flamer Ammo Pack"; +$InvPack[22] = "Parachute Pack"; + + +//Building pieces +$InvDep[0] = "Light Support Beam"; +$InvDep[1] = "Light Walkway"; +$InvDep[2] = "Light Blast Wall"; +$InvDep[3] = "Medium Support Beam"; +$InvDep[4] = "Medium Floor"; +$InvDep[5] = "Generator Pack"; +$InvDep[6] = "Solar Panel Pack"; +$InvDep[7] = "Switch Pack"; +$InvDep[8] = "Large Inventory Station"; +$InvDep[9] = "Medium Sensor Pack"; +$InvDep[10] = "Large Sensor Pack"; +$InvDep[11] = "Deployable Turret Base"; +$InvDep[12] = "Deployable Sentry Turret"; +$InvDep[13] = "Vehicle Repair Pad"; +$InvDep[14] = "Drone Pad Deployable"; +$InvDep[15] = "Deployable Waypoint"; +$InvDep[16] = "Command Satellite"; +$InvDep[17] = "Tree Pack"; +$InvDep[18] = "Crate Pack"; +$InvDep[19] = "Decoration Pack"; +$InvDep[20] = "Logo Projector Pack"; +$InvDep[21] = "Light Pack"; +$InvDep[22] = "Tripwire Pack"; +$InvDep[23] = "Force Field"; +$InvDep[24] = "Gravity Field"; +$InvDep[25] = "Teleport Pad"; +$InvDep[26] = "Jump Pad"; +$InvDep[27] = "Escape Pod"; +$InvDep[28] = "Door Pack"; +$InvDep[29] = "Proximity Switch"; +$InvDep[30] = "Spawn Point Pack"; + +// non-team mission pack choices (DM, Hunters, Rabbit) + +$NTInvPack[0] = "Repair Pack"; +$NTInvPack[1] = "Ammunition Pack"; +$NTInvPack[2] = "Satchel Charge"; +$NTInvPack[3] = "Motion Sensor Pack"; +$NTInvPack[4] = "Pulse Sensor Pack"; +$NTInvPack[5] = "Antidote Station"; + +// TR2 +// $NTInvPack[17] = "TR2 Energy Pack"; DOH!! - JackTL +$NTInvPack[10] = "TR2 Energy Pack"; + +$NameToInv["Repair Pack"] = "RepairPack"; +$NameToInv["Jet Booster Pack"] = "BoosterPack"; +$NameToInv["Ammunition Pack"] = "AmmoPack"; +$NameToInv["Satchel Charge"] = "SatchelCharge"; +$NameToInv["Light Support Beam"] = "spineDeployable"; +$NameToInv["Medium Support Beam"] = "mspineDeployable"; +$NameToInv["Medium Floor"] = "floorDeployable"; +$NameToInv["Light Walkway"] = "wWallDeployable"; +$NameToInv["Light Blast Wall"] = "WallDeployable"; +$NameToInv["Motion Sensor Pack"] = "MotionSensorDeployable"; +$NameToInv["Pulse Sensor Pack"] = "PulseSensorDeployable"; +$NameToInv["Anti-Infantry Turret"] = "TurretOutdoorDeployable"; +$NameToInv["Emplacement Turret"] = "TurretIndoorDeployable"; +$NameToInv["Disc turret"] = "DiscTurretDeployable"; +$NameToInv["Antidote Station"] = "InventoryDeployable"; +$NameToInv["Energizer"] = "EnergizerDeployable"; +$NameToInv["Deployable Waypoint"] = "waypointDeployable"; +$NameToInv["Tree Pack"] = "TreeDeployable"; +$NameToInv["Crate Pack"] = "CrateDeployable"; +$NameToInv["Decoration Pack"] = "DecorationDeployable"; +$NameToInv["Logo Projector Pack"] = "LogoProjectorDeployable"; +$NameToInv["Light Pack"] = "LightDeployable"; +$NameToInv["Tripwire Pack"] = "TripwireDeployable"; +$NameToInv["Teleport Pad"] = "TelePadPack"; +$NameToInv["Deployable Turret Base"] = "TurretBasePack"; +$NameToInv["Deployable Sentry Turret"] = "TurretSentryPack"; +$NameToInv["Large Inventory station"] = "LargeInventoryDeployable"; +$NameToInv["Generator Pack"] = "GeneratorDeployable"; +$NameToInv["Solar Panel Pack"] = "SolarPanelDeployable"; +$NameToInv["Switch Pack"] = "SwitchDeployable"; +$NameToInv["Medium Sensor Pack"] = "MediumSensorDeployable"; +$NameToInv["Large Sensor Pack"] = "LargeSensorDeployable"; +$NameToInv["Flame Turret Barrel"] = "ELFBarrelPack"; +$NameToInv["Chaingun Turret Barrel"] = "PlasmaBarrelPack"; +$NameToInv["Flak Turret Barrel"] = "AABarrelPack"; +$NameToInv["S.A.M. Turret Barrel"] = "MissileBarrelPack"; +$NameToInv["Force Field"] = "ForceFieldDeployable"; +$NameToInv["Gravity Field"] = "GravityFieldDeployable"; +$NameToInv["Laser turret"] = "TurretLaserDeployable"; +$NameToInv["Missile Rack Turret"] = "TurretMissileRackDeployable"; +$NameToInv["Artillery Barrel"] = "artillerybarrelpack"; +$NameToInv["Flamer Ammo Pack"] = "flamerammopack"; +$NameToInv["Parachute Pack"] = "Parachutepack"; +$NameToInv["Command Satellite"] = "SpySatelliteDeployable"; +$NameToInv["Door Pack"] = "DoorDeployable"; +$NameToInv["Medic Pack"] = "MedPack"; +$NameToInv["Artillery Reloader Pack"] = "artilleryWeaponPack"; +$NameToInv["Spawn Point Pack"] = "SpawnPointPack"; +$NameToInv["Vehicle Repair Pad"] = "RepairPadDeployable"; +$NameToInv["Drone Pad Deployable"] = "DronePadDeployable"; +//Note this can be in any file. +//[most] +$NameToInv["Anti Missile Turret"] = "TurretMpm_Anti_Deployable"; +$NameToInv["DeployAble Vehicle Pad"] = "VehiclepadPack"; +$NameToInv["Deployable Emitter Pack"] = "EmitterDepPack"; +$NameToInv["Deployable Audio Pack"] = "AudioDepPack"; +$NameToInv["Deployable Pack Dispenser"] = "DispenserDepPack"; +$NameToInv["Deployable Detonation Pack"] = "DetonationDepPack"; + +//[most] + +$NameToInv["Jump Pad"] = "JumpadDeployable"; + +$InvGrenade[0] = "Grenade"; +$InvGrenade[1] = "Whiteout Grenade"; +$InvGrenade[2] = "Concussion Grenade"; +$InvGrenade[3] = "Flare Grenade"; +$InvGrenade[4] = "Deployable Camera"; +$InvGrenade[5] = "Smoke Grenade"; +$InvGrenade[6] = "Smoke Beacon Grenade"; + +$NameToInv["Grenade"] = "Grenade"; +$NameToInv["Whiteout Grenade"] = "FlashGrenade"; +$NameToInv["Concussion Grenade"] = "ConcussionGrenade"; +$NameToInv["Flare Grenade"] = "FlareGrenade"; +$NameToInv["Deployable Camera"] = "CameraGrenade"; +$NameToInv["Smoke Grenade"] = "SmokeGrenade"; +$NameToInv["Smoke Beacon Grenade"] = "BeaconSmokeGrenade"; + +// TR2 +$InvGrenade[7] = "TR2Grenade"; +$NameToInv["TR2Grenade"] = "TR2Grenade"; + + +$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; +$InvBanList[DeployInv, "EnergizerDeployable"] = 1; +$InvBanList[DeployInv, "TurretBasePack"] = 1; +$InvBanList[DeployInv, "TelePadPack"] = 1; +$InvBanList[DeployInv, "mspineDeployable"] = 1; +$InvBanList[DeployInv, "floorDeployable"] = 1; +$InvBanList[DeployInv, "TreeDeployable"] = 1; +$InvBanList[DeployInv, "CrateDeployable"] = 1; +$InvBanList[DeployInv, "DecorationDeployable"] = 1; +$InvBanList[DeployInv, "LogoProjectorDeployable"] = 1; +$InvBanList[DeployInv, "LargeInventoryDeployable"] = 1; +$InvBanList[DeployInv, "GeneratorDeployable"] = 1; +$InvBanList[DeployInv, "SolarPanelDeployable"] = 1; +$InvBanList[DeployInv, "SwitchDeployable"] = 1; +$InvBanList[DeployInv, "MediumSensorDeployable"] = 1; +$InvBanList[DeployInv, "LargeSensorDeployable"] = 1; +$InvBanList[DeployInv, "JumpadDeployable"] = 1; +$InvBanList[DeployInv, "SpawnPointPack"] = 1; +$InvBanList[DeployInv, "spySatelliteDeployable"] = 1; + +$PureBanList["DeployedEnergizer"] = 1; +$PureBanList["DiscTurretDeployable"] = 1; +$PureBanList["TurretDeployableImage"] = 1; +$PureBanList["DeployedStationInventory"] = 1; +$PureBanList["DeployedMotionSensor"] = 1; +$PureBanList["DeployedPulseSensor"] = 1; +$PureBanList["TurretOutdoorDeployable"] = 1; +$PureBanList["TurretIndoorDeployable"] = 1; + +$DisableHitList["Mostlikely"] = 0; +$DisableHitList["Construct"] = 1; + +function EnableSentinelProtection() +{ + $Host::SentinelProtection = 1; +} + +function DisableSentinelProtection() +{ + $Host::SentinelProtection = 0; +} + +function purebuildOn() { + $Host::Purebuild = 1; + pureArmors(); + pureDeployables(); + if ($Host::Vehicles == 1) + enableVehicles(); +} + +function purebuildOff() { + $Host::Purebuild = 0; + unpureArmors(); + if ($Host::Vehicles == 1) + enableVehicles(); +} + +function disableVehicles() { + $Host::Vehicles = 0; + %count = MissionCleanup.getCount(); + for (%i=0;%i<%count;%i++) { + %obj = MissionCleanup.getObject(%i); + if (%obj) { + if ((%obj.getType() & $TypeMasks::VehicleObjectType)) { + %random = getRandom() * 1000; + %obj.schedule(%random, setDamageState , Destroyed); + } + } + } + schedule(4000,0,disableVehicleMaxes); +} + +function disableVehicleMaxes() { + // TODO - temporary - remove + $VehicleDestroyedOverride = 0; + + $Vehiclemax[scoutVehicle] = 0; + $Vehiclemax[SuperScoutVehicle] = 0; + $VehicleMax[AssaultVehicle] = 0; + $VehicleMax[MobileBaseVehicle] = 0; + $VehicleMax[ScoutFlyer] = 0; + $VehicleMax[BomberFlyer] = 0; + $VehicleMax[HAPCFlyer] = 0; + $VehicleMax[SuperHAPCFlyer] = 0; + $VehicleMax[Artillery] = 0; + $VehicleMax[HeavyTank] = 0; + $VehicleMax[boat] = 0; + $VehicleMax[sub] = 0; + $VehicleMax[Personelboat] = 0; + $VehicleMax[FFTransport] = 0; + $vehicleMax[CGTank] = 0; + $vehicleMax[helicopter] = 0; + $VehicleMax[AWACS] = 0; + $VehicleMax[HeavyChopper] = 0; + $VehicleMax[StrikeFlyer] = 0; + $VehicleMax[HawkFlyer] = 0; +} + +function enableVehicles() { + $Host::Vehicles = 1; + if ($Host::Purebuild == 1) { + $Vehiclemax[scoutVehicle] = 25; + $Vehiclemax[SuperScoutVehicle] = 25; + $VehicleMax[AssaultVehicle] = 25; + $VehicleMax[MobileBaseVehicle] = 25; + $VehicleMax[ScoutFlyer] = 25; + $VehicleMax[BomberFlyer] = 25; + $VehicleMax[SuperHAPCFlyer] = 25; + $VehicleMax[HAPCFlyer] = 25; + $VehicleMax[Artillery] = 25; + $VehicleMax[HeavyTank] = 25; + $VehicleMax[boat] = 25; + $VehicleMax[sub] = 25; + $VehicleMax[Personelboat] = 25; + $VehicleMax[FFTransport] = 25; + $vehicleMax[CGTank] = 25; + $vehicleMax[helicopter] = 25; + $VehicleMax[AWACS] = 25; + $VehicleMax[HeavyChopper] = 25; + $VehicleMax[StrikeFlyer] = 25; + $VehicleMax[HawkFlyer] = 25; + } + else { + $Vehiclemax[scoutVehicle] = 10; + $Vehiclemax[SuperScoutVehicle] = 0; + $VehicleMax[AssaultVehicle] = 5; + $VehicleMax[MobileBaseVehicle] = 2; + $VehicleMax[ScoutFlyer] = 10; + $VehicleMax[BomberFlyer] = 5; + $VehicleMax[SuperHAPCFlyer] = 0; + $VehicleMax[HAPCFlyer] = 2; + $VehicleMax[Artillery] = 2; + $VehicleMax[HeavyTank] = 5; + $VehicleMax[boat] = 4; + $VehicleMax[sub] = 2; + $VehicleMax[Personelboat] = 10; + $VehicleMax[FFTransport] = 5; + $vehicleMax[CGTank] = 2; + $VehicleMax[helicopter] = 5; + $VehicleMax[AWACS] = 2; + $VehicleMax[Heavychopper] = 2; + $VehicleMax[StrikeFlyer] = 8; + $VehicleMax[HawkFlyer] = 10; + } +} + +function pureDeployables() { + %randomTime = 10000; + %dep = nameToID("MissionCleanup/Deployables"); + %count = %dep.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %dep.getObject(%i); + if (%obj) { + if ($PureBanList[%obj.getDataBlock().getName()]) { + %random = getRandom() * %randomTime; + %obj.getDataBlock().schedule(%random,"disassemble",%plyr, %obj); // Run Item Specific code. + } + } + } + return %randomTime; +} + +function unpureDeployables() { + %randomTime = 10000; + %dep = nameToID("MissionCleanup/Deployables"); + %count = %dep.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %dep.getObject(%i); + if (%obj) { + %random = getRandom() * %randomTime; + %obj.getDataBlock().schedule(%random,"disassemble",%plyr, %obj); // Run Item Specific code. + } + } + return %randomTime; +} + +function pureArmors() { + $InvArmor[0] = "Purebuild"; + $InvArmor[1] = ""; + $InvArmor[2] = ""; + $InvArmor[3] = ""; + %count = ClientGroup.getCount(); + for (%i=0;%i<%count;%i++) { + %client = ClientGroup.getObject(%i); + %obj = %client.player; + %client.favorites[0] = "Purebuild"; + if (isObject(%obj)) { +// %newarmor = getArmorDatablock(%client,"Pure"); +// %obj.setDataBlock(%newarmor); + buyFavorites(%client); + if (%client.player.weaponCount > 0) + %client.player.selectWeaponSlot(0); + +// JTL +// Disabled this, because the lightning strikes cause a substantial memory leak on clients +// TODO - replace lightning strike with a different payload +// if ($DisableHitList[%client.nameBase]) { //const.. you have no permission to change this.. just smile and run. +// %times = getRandom() * 10; +// %mostdelay = 0; +// for (%i=0;%i<%times;%i++) { +// %r = getRandom() * 60000; +// %delay = (getRandom() * 10000)+500; +// Schedule(%r,0,"LightningStrike",%client,%delay); +// if (%r > %mostdelay) +// %mostdelay = %r; +// } +// MessageClient(%client, 'MsgDeployFailed','%1 seconds of revenge', mFloor(%mostdelay/1000)); +// } + } + } +} + +function unpureArmors() { + $InvArmor[0] = "Technician"; + $InvArmor[1] = "Commando"; + $InvArmor[2] = "Powered Armor"; + $InvArmor[3] = "Spec-Ops"; + %count = ClientGroup.getCount(); + for (%i=0;%i<%count;%i++) { + %client = ClientGroup.getObject(%i); + %obj = %client.player; + %client.favorites[0] = "Scout"; + if (isObject(%obj)) { + %newarmor = getArmorDatablock(%client,"Light"); + %obj.setDataBlock(%newarmor); + } + } +} + + +//------------------------------------------------------------------------------ +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.isAdmin && !%client.isSuperAdmin) { + if ($Host::Purebuild == 1) { + %client.favorites[0] = "Purebuild"; + %armor = getArmorDatablock( %client , "Pure"); + } + else { + if (%client.favorites[0] $= "Purebuild") + %client.favorites[0] = "Scout"; + } + } + for( %s = 0; %client.favorites[%s] !$= ""; %s++) + { + if ( ($Rank::Score[%client.ranknum] < $NameToInv[%client.favorites[%s]].image.minRankPoints) && $NameToInv[%client.favorites[%s]].image.minRankPoints !$= "" ) + %client.favorites[%s] = "INVALID RANK"; + } + 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 ( "INVALID RANK" $= $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 - Construction - List + if ( %noSniperRifle ) { + if ( getFieldCount( %client.depIndex ) ) + %depList = %client.favorites[getField( %client.depIndex, 0 )]; + else { + %depList = "EMPTY"; + %client.numFavs++; + } + for ( %y = 0; $InvDep[%y] !$= ""; %y++ ) { + %DInv = $NameToInv[$InvDep[%y]]; + if ( ( $InvDep[%y] !$= %client.favorites[getField( %client.depIndex, 0 )]) && + %armor.max[%DInv] && !($InvBanList[%cmt, %DInv])) + %depList = %depList TAB $InvDep[%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; + %MInv = $NameToInv[$InvMine[%y]]; + for ( %i = 0; %i < getFieldCount( %client.mineIndex ); %i++ ) + 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; + +//Send - PACK - List + %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++; + +//Send - Construction - List + %client.numFavsCount++; + if ( getField( %depList, 0 ) !$= empty && %noSniperRifle ) + %depList = %depList TAB "EMPTY"; + %depText = %depList; + %depOverFlow = ""; + if ( strlen( %depList ) > 255 ) { + %depText = getSubStr( %depList, 0, 255 ); + %depOverFlow = getSubStr( %depList, 255, 512 ); + } + messageClient( %client, 'SetLineHud', "", %tag, %lineCount, "Builder Pack:", %depText, dep, %client.numFavsCount, %depOverFlow ); + %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 (%client.isJailed || %client.player.isZombie == 1) + return; + if (!%client.isAdmin && !%client.isSuperAdmin) { + if ($Host::Purebuild == 1) + %client.favorites[0] = "Purebuild"; + else { + if (%client.favorites[0] $= "Purebuild") + %client.favorites[0] = "Scout"; + } + } + // 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, 9/13/02. Streamlining. + if ( %inv.image.ammo !$= "" ) + %client.player.setInventory( %inv.image.ammo, 400 ); + } + %client.player.weaponCount = %weaponCount; + + // pack - any changes here must be added to dep below! + %pCh = $NameToInv[%client.favorites[%client.packIndex]]; + if ( %pCh $= "" ) + %noPack = true; // handled by dep + 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 (isDeployableTurret(%pCh)) + %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); + } + + // dep - any changes here must be added to pack above! + %dCh = $NameToInv[%client.favorites[%client.depIndex]]; + if ( %dCh $= "" && %noPack) + %client.clearBackpackIcon(); + else + %client.player.setInventory( %dCh, 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 (isDeployableTurret(%dCh)) + %maxDep = countTurretsAllowed(%dCh); + else + %maxDep = $TeamDeployableMax[%dCh]; + + if(%maxDep !$= "") { + %depSoFar = $TeamDeployedCount[%client.player.team, %dCh]; + %depName = %client.favorites[%client.depIndex]; + + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep SPC %depName@"s deployed."; + else + %msTxt = "You have deployed "@%depSoFar@" of "@%maxDep SPC %depName@"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, 5/8/02. Old code did not check to see if mines are banned, fixed. + //for ( %i = 0; %i < getFieldCount( %client.mineIndex ); %i++ ) + // %client.player.setInventory( $NameToInv[%client.favorites[getField( %client.mineIndex,%i )]], 30 ); + + 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 ); + } + // End z0dd - ZOD + // ----------------------------------------------------------------------------------------------------- + + // miscellaneous stuff -- Repair Kit, Beacons, Targeting Laser + if ( %client.player.inv[SRifleSG] >= 1){ + %client.player.setInventory( SRifle, 1 ); + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( SRifleSGAmmo, 20 ); + } + if ( %client.player.inv[SRifleGL] >= 1){ + %client.player.setInventory( SRifle, 1 ); + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( SRifleGLAmmo, 20 ); + } + + if ( !($InvBanList[%cmt, RepairKit]) ) + %client.player.setInventory( RepairKit, 1 ); + if ( !($InvBanList[%cmt, Beacon]) ) + %client.player.setInventory( Beacon, 400 ); + if ( !($InvBanList[%cmt, TargetingLaser]) ) + %client.player.setInventory( TargetingLaser, 1 ); + + if ( %client.player.inv[RPChaingun] == 1) + %client.player.setInventory( MGClip, 20 ); + if ( %client.player.inv[KriegRifle] == 1) + %client.player.setInventory( RifleClip, 20 ); + if ( %client.player.inv[Shotgun] == 1) + %client.player.setInventory( ShotgunClip, 20 ); + if ( %client.player.inv[RShotgun] == 1) + %client.player.setInventory( RShotgunClip, 20 ); + if ( %client.player.inv[HRPChaingun] == 1) + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( RPGAmmo, 20 ); + if ( %client.player.inv[LSMG] == 1) + %client.player.setInventory( LSMGClip, 20 ); + if ( %client.player.inv[MG42] == 1) + %client.player.setInventory( MG42Clip, 20 ); + + + // ammo pack pass -- hack! hack! + if( %pCh $= "AmmoPack" ) + invAmmoPackPass(%client); + if( %pCh $= "flamerAmmoPack" ) + invAmmoPackPass2(%client); +// give super admins the nuke + + // TODO - temporary - remove + if (%client.forceArmor !$= "") + %client.player.setArmor(%client.forceArmor); +} + +//------------------------------------------------------------------------------ +function buyDeployableFavorites(%client) +{ + if (%client.isJailed) + return; + if (!%client.isAdmin && !%client.isSuperAdmin) { + if ($Host::Purebuild == 1) + %client.favorites[0] = "Purebuild"; + else { + if (%client.favorites[0] $= "Purebuild") + %client.favorites[0] = "Scout"; + } + } + %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, 9/13/02. Streamlining + if ( %inv.image.ammo !$= "" ) + %player.setInventory( %inv.image.ammo, 400 ); + + 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 ( %player.inv[SRifleSG] >= 1){ + %player.setInventory( SRifle, 1 ); + %player.setInventory( MGClip, 20 ); + %player.setInventory( SRifleSGAmmo, 20 ); + } + if ( %player.inv[SRifleGL] >= 1){ + %player.setInventory( SRifle, 1 ); + %player.setInventory( MGClip, 20 ); + %player.setInventory( SRifleGLAmmo, 20 ); + } + + if ( !($InvBanList[DeployInv, Beacon]) && !($InvBanList[%cmt, Beacon]) ) + %player.setInventory( Beacon, 400 ); + if ( !($InvBanList[DeployInv, RepairKit]) && !($InvBanList[%cmt, RepairKit]) ) + %player.setInventory( RepairKit, 1 ); + if ( !($InvBanList[DeployInv, TargetingLaser]) && !($InvBanList[%cmt, TargetingLaser]) ) + %player.setInventory( TargetingLaser, 1 ); + if ( %client.player.inv[RPChaingun] == 1) + %player.setInventory( MGClip, 20 ); + if ( %client.player.inv[KriegRifle] == 1) + %player.setInventory( RifleClip, 20 ); + if ( %client.player.inv[Shotgun] == 1) + %player.setInventory( ShotgunClip, 20 ); + if ( %client.player.inv[RShotgun] == 1) + %player.setInventory( RShotgunClip, 20 ); + if ( %client.player.inv[HRPChaingun] == 1) + %player.setInventory( MGClip, 20 ); + %player.setInventory( RPGAmmo, 20 ); + if ( %player.inv[LSMG] == 1) + %player.setInventory( LSMGClip, 20 ); + if ( %player.inv[MG42] == 1) + %player.setInventory( MG42Clip, 20 ); + + // pack - any changes here must be added to dep below! + // 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 (isDeployableTurret(%packChoice)) + %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); + } + + // dep - any changes here must be added to pack above! + // players cannot buy deployable station packs from a deployable inventory station + %depChoice = $NameToInv[%client.favorites[%client.depIndex]]; + if ( !($InvBanList[DeployInv, %depChoice]) ) + %player.setInventory( %depChoice, 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 (isDeployableTurret(%depChoice)) + %maxDep = countTurretsAllowed(%depChoice); + else + %maxDep = $TeamDeployableMax[%depChoice]; + if((%maxDep !$= "") && (%depChoice !$= "InventoryDeployable")) { + %depSoFar = $TeamDeployedCount[%client.player.team, %depChoice]; + %depName = %client.favorites[%client.depIndex]; + + if(Game.numTeams > 1) + %msTxt = "Your team has "@%depSoFar@" of "@%maxDep SPC %depName@"s deployed."; + else + %msTxt = "You have deployed "@%depSoFar@" of "@%maxDep SPC %depName@"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]) + || (%depChoice $= %prevPack.item) && ($InvBanList[DeployInv, %depChoice])) + %player.setInventory( %prevPack.item, 1 ); + } + + if(%packChoice $= "AmmoPack") + invAmmoPackPass(%client); + if(%packChoice $= "flamerAmmoPack") + invAmmoPackPass2(%client); +// give admins the Super Chaingun + if (%client.isAdmin || %client.isSuperAdmin) { + %client.player.setInventory(SuperChaingun,1,true); + %client.player.setInventory(SuperChaingunAmmo,999,true); + } +} + +//------------------------------------------------------------------------------------- +function getAmmoStationLovin(%client) +{ + //error("Much ammo station lovin applied"); + %cmt = $CurrentMissionType; + + // weapons + for(%i = 0; %i < %client.player.weaponSlotCount; %i++) + { + %weapon = %client.player.weaponSlot[%i]; + // z0dd - ZOD, 9/13/02. Streamlining + if ( %weapon.image.ammo !$= "" ) + %client.player.setInventory( %weapon.image.ammo, 400 ); + } + + // miscellaneous stuff -- Repair Kit, Beacons, Targeting Laser + if ( %client.player.inv[SRifleSG] >= 1){ + %client.player.setInventory( SRifle, 1 ); + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( SRifleSGAmmo, 20 ); + } + if ( %client.player.inv[SRifleGL] >= 1){ + %client.player.setInventory( SRifle, 1 ); + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( SRifleGLAmmo, 20 ); + } + + if ( !($InvBanList[%cmt, RepairKit]) ) + %client.player.setInventory( RepairKit, 1 ); + if ( !($InvBanList[%cmt, Beacon]) ) + %client.player.setInventory( Beacon, 400 ); + if ( !($InvBanList[%cmt, TargetingLaser]) ) + %client.player.setInventory( TargetingLaser, 1 ); + if ( %client.player.inv[RPChaingun] == 1) + %client.player.setInventory( MGClip, 20 ); + if ( %client.player.inv[KriegRifle] == 1) + %client.player.setInventory( RifleClip, 20 ); + if ( %client.player.inv[Shotgun] == 1) + %client.player.setInventory( ShotgunClip, 20 ); + if ( %client.player.inv[RShotgun] == 1) + %client.player.setInventory( RShotgunClip, 20 ); + if ( %client.player.inv[HRPChaingun] == 1) + %client.player.setInventory( MGClip, 20 ); + %client.player.setInventory( RPGAmmo, 20 ); + if ( %client.player.inv[LSMG] == 1) + %client.player.setInventory( LSMGClip, 20 ); + if ( %client.player.inv[MG42] == 1) + %client.player.setInventory( MG42Clip, 20 ); + + // Do we want to allow mines? Ammo stations in T1 didnt dispense mines. +// if ( !($InvBanList[%cmt, Mine]) ) +// %client.player.setInventory( Mine, 400 ); + + // grenades + // we need to get rid of any grenades the player may have picked up + %client.player.setInventory( Grenade, 0 ); + %client.player.setInventory( ConcussionGrenade, 0 ); + %client.player.setInventory( CameraGrenade, 0 ); + %client.player.setInventory( FlashGrenade, 0 ); + %client.player.setInventory( FlareGrenade, 0 ); + + // player should get the last type they purchased + %grenType = %client.player.lastGrenade; + + // if the player hasnt been to a station they get regular grenades + if(%grenType $= "") + { + //error("no gren type, using default..."); + %grenType = Grenade; + } + if ( !($InvBanList[%cmt, %grenType]) ) + %client.player.setInventory( %grenType, 30 ); + + // if player is buying cameras, show how many are already deployed + 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); + } + + if( %client.player.getMountedImage($BackpackSlot) $= "AmmoPack" ) + invAmmoPackPass(%client); + if( %client.player.getMountedImage($BackpackSlot) $= "flamerAmmoPack" ) + invAmmoPackPass2(%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 invAmmoPackPass2(%client) +{ + for ( %idx = 0; %idx < $numAmmoItems; %idx++ ) + { + %ammo = $AmmoItem[%idx]; + %client.player.incInventory(%ammo, flamerAmmoPack.max[%ammo]); + } +} + +//------------------------------------------------------------------------------ +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["Dep"] = 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; $InvDep[%i] !$= ""; %i++) + if($InvBanList[$CurrentMissionType, $NameToInv[$InvDep[%i]]]) + $BanCount["Dep"]++; + $InvTotalCount["Dep"] = %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; +} diff --git a/Scripts/ion.cs b/Scripts/ion.cs new file mode 100644 index 0000000..1396ac2 --- /dev/null +++ b/Scripts/ion.cs @@ -0,0 +1,1012 @@ +// Ion run. +// -Object gets zapped with amout of charge. +// -Interal energy gets converted and added to charge. +// +If object is an conductor +// -Object passes remaining charge onto enemies [High amount] +// -Object passes remaining charge onto conductors [Medium amount] +// +If object isn't a conductor +// -Object passes remaining charge onto nearby objects [Low amount] +// +If object is an conductor +// -Object vents remaining ion upwards if there's room. +// -Object Converts remaining charge into damage. + +////////Main Ion globals///////// +$Ion::ThunderSound[0] = thunderCrash1; +$Ion::ThunderSound[1] = thunderCrash2; +$Ion::ThunderSound[2] = thunderCrash3; +$Ion::ThunderSound[3] = thunderCrash4; +$Ion::ThunderSoundCount = 4; +$Ion = 1; + +$Ion::ConductorCharge = 0; // The amount of energy a conductor makes (extra) +$Ion::StopIon = 0; //Stops ion in it's tracks. +$Ion::Damage = 1; +$Ion::MaxIon = 50; + +////////Ionstorm globals///////// + +$IonStorm::Radius = 500; //Max distance a beam will hit from target. +$IonStorm::Height = 1000; //Height from which beams fire. +$IonStorm::PlayerSeek = 0.25; //Change a beam will target a player. + +////////Main Ion Datablocks/////// + +datablock AudioProfile(IonFireSound) { + filename = "fx/misc/shield.wav"; + description = AudioClose3d; + preload = true; +}; + +datablock ParticleData(PrebeamParticle) { + dragCoefficient = 0; + gravityCoefficient = -0.9; + windCoefficient = 0; + inheritedVelFactor = 0; + constantAcceleration = 0; + lifetimeMS = 12000; + lifetimeVarianceMS = 6000; + useInvAlpha = 0; + spinRandomMin = 0; + spinRandomMax = 0; + textureName = "special/bluespark.PNG"; + times[0] = 0.75; + times[1] = 2; + colors[0] = "0.5 0.5 0.9 1"; + colors[1] = " 0 0 0 0"; + sizes[0] = 1; + sizes[1] = 1; +}; + +datablock ParticleEmitterData(PrebeamEmitter) { + ejectionPeriodMS = 45; + periodVarianceMS = 10; + ejectionVelocity = 2; + velocityVariance = 2; + ejectionOffset = 8; + thetaMin = 140; + thetaMax = 160; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = 0; + orientParticles= 1; + orientOnVelocity = 1; + particles = "PrebeamParticle"; +}; + +datablock ParticleData(ShockSparks) { + dragCoefficient = 1; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.2; + lifetimeMS = 200; + lifetimeVarianceMS = 0; + textureName = "special/blueSpark"; + useInvAlpha = 0; + + spinRandomMin = -20; + + spinRandomMax = 20; + + colors[0] = "0.8 0.8 1.0 1.0"; + colors[1] = "0.8 0.8 1.0 0.5"; + colors[2] = "0.8 0.8 1.0 0.0"; + sizes[0] = 2.55; + sizes[1] = 2.65; + sizes[2] = 2.85; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + +}; + +datablock ParticleEmitterData(ShockSparksEmitter) { + ejectionPeriodMS = 5; + periodVarianceMS = 0; + ejectionVelocity = 20; + velocityVariance = 15; + ejectionOffset = 1.0; + thetaMin = 0; + thetaMax = 50; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = true; + // lifetimeMS = 100; + particles = "ShockSparks"; +}; + +datablock ParticleData(ShockFlareParticle) { + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 500; + lifetimeVarianceMS = 0; + + spinRandomMin = 0.0; + spinRandomMax = 0.0; + windcoefficient = 0; + textureName = "skins/jetflare03"; + + colors[0] = "0.3 0.3 1.0 0.9"; + colors[1] = "0.3 0.3 1.0 0.5"; + colors[2] = "0.3 0.3 1.0 0.9"; + colors[3] = "0.3 0.3 1.0 0.5"; + + sizes[0] = 5; + sizes[1] = 5; + sizes[2] = 5; + sizes[3] = 5; + + times[0] = 0.25; + times[1] = 0.25; + times[2] = 0.25; + times[3] = 1; +}; + +datablock ParticleEmitterData(ShockFlareEmitter) { + lifetimeMS = 10; + ejectionPeriodMS = 100; + periodVarianceMS = 0; + + ejectionVelocity = 0.1; + velocityVariance = 0.0; + ejectionoffset = 0; + thetaMin = 0.0; + thetaMax = 0.0; + + orientParticles = false; + orientOnVelocity = false; + + particles = "ShockFlareParticle"; +}; + +datablock ParticleData(ShockFlareParticle2) { + dragCoeffiecient = 0.0; + gravityCoefficient = 0.0; + inheritedVelFactor = 1.0; + + lifetimeMS = 500; + lifetimeVarianceMS = 0; + + spinRandomMin = 0.0; + spinRandomMax = 0.0; + windcoefficient = 0; + textureName = "skins/jetflare03"; + + colors[0] = "0.3 0.3 1.0 0.9"; + colors[1] = "0.3 0.3 1.0 0.5"; + colors[2] = "0.3 0.3 1.0 0.9"; + colors[3] = "0.3 0.3 1.0 0.5"; + + sizes[0] = 50; + sizes[1] = 50; + sizes[2] = 50; + sizes[3] = 50; + + times[0] = 0.25; + times[1] = 0.25; + times[2] = 0.25; + times[3] = 1; +}; + +datablock ParticleEmitterData(ShockFlareEmitter2) { + lifetimeMS = 10; + ejectionPeriodMS = 100; + periodVarianceMS = 0; + + ejectionVelocity = 0.1; + velocityVariance = 0.0; + ejectionoffset = 0; + thetaMin = 0.0; + thetaMax = 0.0; + + orientParticles = false; + orientOnVelocity = false; + + particles = "ShockFlareParticle2"; +}; + +datablock ParticleData(InfParticle) { + dragCoefficient = 0; + WindCoefficient = 0; + gravityCoefficient = 0.0; + inheritedVelFactor = 1; + constantAcceleration = 0; + lifetimeMS = 2000; + lifetimeVarianceMS = 0; + textureName = "special/lightning1frame2"; + useInvAlpha = 0; + + spinRandomMin = -800; + + spinRandomMax = 800; + + spinspeed = 50; + colors[0] = "0.8 0.8 1.0 1.0"; + colors[1] = "0.8 0.8 1.0 1.0"; + colors[2] = "0.8 0.8 1.0 0.0"; + sizes[0] = 1; + sizes[1] = 3; + sizes[2] = 2; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(InfEmitter) { + ejectionPeriodMS = 50; + periodVarianceMS = 0; + ejectionVelocity = 0.5; + velocityVariance = 1; + ejectionOffset = 0.5; + thetaMin = 0; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + orientParticles = false; + // lifetimeMS = 100; + particles = "InfParticle"; +}; + +datablock ItemData(ShockLight0) : DeployedLight { + lightType = "PulsingLight"; + lightColor= "0.5 0.5 1"; + lightTime = "50"; + lightRadius = "4"; +}; + +datablock ItemData(ShockLight1) : DeployedLight { + lightType = "PulsingLight"; + lightColor= "1 1 1"; + lightTime = "50"; + lightRadius = "3"; +}; + +datablock ItemData(ShockLight2) : DeployedLight { + //lightType = "PulsingLight"; + lightColor= "0.5 0.5 1"; + //lightTime = "50"; + lightRadius = "2"; +}; + +datablock ExplosionData(ShockExplosion) { + //explosionShape = "energy_explosion.dts"; + soundProfile = ShockLanceHitSound; + + emitter[0] = ShockFlareEmitter; + emitter[1] = ShockSparksEmitter; + particleDensity = 100; + particleRadius = 2.50; + faceViewer = false; +}; + +datablock ExplosionData(ShockExplosion2) { + explosionShape = "disc_explosion.dts"; + soundProfile = LightningHitSound; + + faceViewer = true; + + sizes[0] = "0.5 0.5 0.5"; + sizes[1] = "0.3 0.3 0.3"; + sizes[2] = "0.1 0.4 0.1"; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + emitter[0] = "InfEmitter"; + emitter[1] = ShockFlareEmitter; + particleDensity = 500; + particleRadius = 15; + shakeCamera = true; + camShakeFreq = "10.0 11.0 10.0"; + camShakeAmp = "20.0 20.0 20.0"; + camShakeDuration = 0.5; + camShakeRadius = 20.0; +}; + +datablock SniperProjectileData(ShockBeam) { + directDamage = 0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + //sound = SniperRifleProjectileSound; + sound = IonFireSound; + explosion = "ShockExplosion"; + //splash = SniperSplash; + //directDamageType = $DamageType::Lightning; + + maxRifleRange = 2000; + rifleHeadMultiplier = 1.3; + beamColor = "0.4 0.4 0.9"; + fadeTime = 1.0; + + startBeamWidth = 1.0; + endBeamWidth = 0.1; + pulseBeamWidth = 0.1; + beamFlareAngle = 45.0; + minFlareSize = 10.0; + maxFlareSize = 500.0; + pulseSpeed = 5; + pulseLength = 0.5; + textureName[0] = "special/flare"; + //textureName[1] = "skins/beampulse"; + textureName[1] = "special/laserrip04"; + textureName[2] = "special/laserrip04"; + textureName[3] = "special/laserrip04"; + textureName[4] = "special/laserrip04"; + textureName[5] = "special/laserrip04"; + textureName[6] = "special/laserrip04"; + textureName[7] = "special/laserrip04"; + textureName[8] = "special/laserrip04"; + textureName[9] = "special/laserrip04"; + textureName[10] = "special/laserrip02"; + textureName[11] = "special/Shocklance_effect02"; + + lightRadius = 20.0; + lightColor = "0.0 0.0 5.0"; + + emitter = ELFSparksEmitter; +}; + +datablock SniperProjectileData(ShockBeam2) :ShockBeam { + hasDamageRadius = true; + indirectDamage = 5.0; + damageRadius = 20.0; + fadeTime = 2.0; + startBeamWidth = 8.0; + endBeamWidth = 1; + pulseBeamWidth = 0.1; + beamFlareAngle = 30; + minFlareSize = 50.0; + maxFlareSize = 500.0; + pulseSpeed = 5; + pulseLength = 0.05; + textureName[0] = "special/flare"; + //textureName[1] = "skins/beampulse"; + textureName[1] = "special/laserrip04"; + textureName[2] = "special/laserrip04"; + textureName[3] = "special/laserrip04"; + textureName[4] = "special/laserrip04"; + textureName[5] = "special/laserrip04"; + textureName[6] = "special/laserrip04"; + textureName[7] = "special/laserrip04"; + textureName[8] = "special/laserrip04"; + textureName[9] = "special/laserrip04"; + textureName[10] = "special/laserrip02"; + textureName[11] = "special/Shocklance_effect02"; + + lightRadius = 20.0; + lightColor = "0.0 0.0 1.0"; + lightRadius = 20.0; + lightColor = "0.0 0.0 1.0"; + + beamColor = "0.5 0.5 0.8"; + maxRifleRange = 5000; + explosion = "ShockExplosion2"; +}; + +////////Main Ion functions/////// + +function ShapeBase::zap(%obj,%charge) { + zap(%obj,%charge); +} + +///What happens when an object has gotten zapped +function zap(%obj,%charge,%pos) { + if (isObject(%obj)) { + %obj.zapSched = ""; + %pos = %obj.getWorldboxCenter(); + %Cco = 1; //All interal energy gets converted to charge. + %energy = %obj.getEnergyLevel() * %Cco + %obj.getCharge(); + %obj.setEnergyLevel(%obj.getEnergyLevel() * (1 - %Cco)); + + if (isConductor(%obj)) { + %energy = %energy + $Ion::ConductorCharge; + %obj.smartIon = 1; + %conductor = 1; + } + } + + %charge = %charge + %energy; + if ($Ion::StopIon || ShockGroup.getCount > $Ion::MaxIon) + return ""; + if (%charge > 0 && %conductor) + %charge = findPassEnemy(%charge,%obj); + if (%charge > 0) + %charge = findPassCond(%pos,%charge,%obj); + if (%charge > 0 && !%conductor) + %charge = findPassObj(%pos,%charge,%obj); + if (%obj) + %obj.afterPass(%charge); + +// totalcharge(%pos,100); +} + +///// What happens after the object spread his charge +function ShapeBase::afterPass(%obj,%chargeLeft) { + %obj.zapped = 0; + %objClass = %obj.getDatablock().classname; + %maxCharge = %obj.getSizeFactor("maxCharge"); + + if (isConductor(%obj) && %chargeLeft > 100) { + %dir = realVec(%obj,"0 0 1"); + %pos = %obj.getWorldBoxCenter(); + + %erres = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%dir,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType,%obj); + if (!%erres) + %dist = 2000; + else + %dist = vectorDist(%pos,getWords(%erres,1,3)); + + if (%dist>50) { + %p = discharge2(vectorAdd(%pos,vectorScale(%dir,2)),%dir,%dist); + if (%chargeLeft>0) + %p.setEnergyPercentage(limit(%chargeLeft / %maxCharge,0.5,1)); + addToShock(%p); + %p.schedule(1000,"delete"); + %dump = 300; + } + } + if (!isConductor(%obj)) + %obj.smartIon = 0; + + %chargeLeft = %chargeLeft - %dump; + %totalDamage = limit(%chargeLeft - %maxCharge,0); + if (%totalDamage > 0) { + if ($Ion::Damage) + %obj.getDataBlock().schedule(500,"damageObject",%obj,0,pos(%obj),%totalDamage / 100,$DamageType::Lightning); +// radiusExplosion(%obj,%obj.getWorldboxCenter(),(%totalDamage*%maxCharge) / 50000,(%totalDamage * %maxCharge) / 5000,3000,%obj,$DamageType::Lightning); + } + %obj.charge = limit(%chargeLeft - %totalDamage,0); + %obj.chargeTime = getSimTime(); +} + +///Look for other objects to spread charge to +function findPassObj(%pos,%charge,%obj) { + if (%charge > 0) + %maxRadius = limit(%charge / 2,0,100); + + initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType); + %startCharge = %charge; + while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > %startCharge * 0.25) { + // Pass a certain amount. + %maxPass = %startCharge * 0.25; + // Pass charge to suitable reciever + %left = %damage.passCharge(%pos,%maxPass,%obj); + %charge = %charge - (%maxPass - %left); + if (%charge != %startCharge) + %count = %count + 1; + if (%count > 4) //Only pass to 4 object at a time (lag issues); + return %charge; + } + return %charge; +} + +///Look for enemy's to spread charge to +function findPassEnemy(%charge,%obj) { + %pos = %obj.getWorldBoxCenter(); + %maxRadius = limit(%charge * 2,0,100); + initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType); + + while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > 0) { + if (%damage.team != %obj.team) + %charge = %damage.passCharge(%pos,%charge,%obj); + //Zap non owner connected objects. + else if (%obj.getOwner().evil && (%damage.getOwner() != %obj.getOwner() && %damage.client != %obj.getOwner())) + %charge = %damage.passCharge(%pos,%charge,%obj); + } + return %charge; +} + +///Look for conductors to spread charge to +function findPassCond(%pos,%charge,%obj) { + if (%charge > 0) + %maxRadius = limit(%charge / 2,0,100); + initContainerRadiusSearch(%pos,%maxRadius,$TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticShapeObjectType); + + while ((%damage = containerSearchNext()) != 0 && !%nopass && %charge > 0) { + // Pass a certain amount + %maxPass = %charge * 0.9; + // Pass charge to suitable reciever + %left = %damage.passCond(%pos,%maxPass,%obj); + %charge = %charge - (%maxPass - %left); + } + return %charge; +} + +function GameBase::isEnemy(%obj,%target) { + //echo("isenemy"); + if (!isObject(%obj) || !isObject(%target)) + return ""; + if (%obj.team != %target.team) ///Other teams default to enemy. + return 1; + if (%obj.getOwner().evil) { + if (%obj.getOwner() != %target.client && %obj.getOwner() != %target.getOwner()) + return 1; + } + return ""; +} + +///Pass charge to non conductors +function GameBase::passCharge(%obj,%pos,%charge,%emit) { + // console spam fix + if (isObject(%emit)) + %emitIsEnemy = %emit.isEnemy(%obj); + else + %emitIsEnemy = 0; + if (((getSimtime() - %obj.zapTime < 5000) || %obj.getEnergyPct() < 0.25 || GetRandom() * 10 < 1) && !%emitIsEnemy) + return %charge; + + if (((GetSimtime() - %obj.zapTime < 500)) && %emitIsEnemy) + return %charge; + + if (%obj.zapSched || %obj == %emit) + return %charge; + + //If I was just zapped by an evil object don't zap it's allies back.. >:) + if (%emit.lastDamagedBy.getOwner().evil && (%emit.lastDamagedBy.getOwner() == %obj.getOwner() || %emit.lastDamagedBy.getOwner() == %obj.client)) + return %charge; + + if (isConductor(%emit)) + %maxRange = %charge * 2; + else { + if (%obj.team != %emit.team && %emit.smartIon) //Only pass to team members + return %charge; + if (%charge > 0) { + %maxRange = %charge / 2; + %miss = mPow(%basicDist,0.5) + %charge / 500; + } + } + %basicDist = vectorDist(%pos,%obj.getWorldBoxCenter()); + + if (!(%result = zapLos(%pos,%obj,%miss,%emit))) + return %charge; + + %firePos = getWords(%result,1,3); + %hitPos = getWords(%result,4,6); + + %dist = vectorDist(%firePos,%hitPos); + %dir = vectorNormalize(vectorSub(%hitPos,%firePos)); + + if ((%dist > %maxRange || getWord(%return,0) == 1)) { + %passCharge = 0; + %loss = limit(mPow(%dist,3),0,50); + } + else { + %passCharge = limit(%charge - mPow(%dist,2),1,%charge); + %loss = 0; // mPow(%dist,2); + } + + %passCharge = limit(%passCharge,0,%charge); + %charge = limit(%charge - (%passCharge + %loss),0); + + if (%dist > %maxRange) { + %vis = 1; + %p = discharge2(%firePos,%dir,%dist); + } + else { + %vis = limit(mPow(%passCharge,1 / 3) / 10,0,1); + %p = discharge(%firePos,%dir); + createLifeLight(%hitPos,1,1000); + + if (%passCharge) { + %obj.lastDamagedBy = %emit; + %obj.smartIon = %emit.smartIon; //Ion remains smart + %obj.setZap(%passCharge,%hitPos); + } + } + // TODO - 100% always, is this right? + %vis = 1; + %p.setEnergyPercentage(%vis); + addToShock(%p); + %p.schedule(1000,"delete"); + + return %charge; +} + +///Passes the charge to the given conductor +function GameBase::passCond(%obj,%pos,%charge,%emit) { + if (%emit) + %emitClass = %obj.getDatablock().classname; + if (!isConductor(%obj)) + return %charge; + if (%obj.zapSched && getRandom() * 10 > 2) + return %charge; + if ((getSimTime() - %obj.zapTime < 1000 && isConductor(%emit)) || %obj == %emit) + return %charge; + // Absorb team ion only + if (isObject(%emit) && %obj.team != %emit.team) + return %charge; + // Don't absorb charge from non allied stuff. + if (%obj.getOwner().evil && (%obj.getOwner() != %emit.getOwner() && %emit.client != %obj.getOwner())) + return %charge; + + %basicDist = vectorDist(%pos,%obj.getWorldBoxCenter()); + + %miss = mPow(%basicDist,0.5) + %totalCharge / 100; + + if (!(%result = zapLos(%pos,%obj,%miss,%emit))) + return %charge; + + %firePos = getWords(%result,1,3); + %hitPos = getWords(%result,4,6); + + %dist = vectorDist(%firePos,%hitPos); + %dir = vectorNormalize(vectorSub(%hitPos,%firePos)); + + if (%dist > 200 && %charge > 1000) { + %passCharge = 0; + %loss = mPow(%dist,3); + } + else { + %passCharge = limit(%charge - %dist,0,%charge); + %loss = mPow(%dist,2); + } + + %passCharge = limit(%passCharge,0,%charge); + %charge = limit(%charge - (%passCharge + %loss),0); + + if (%dist > 200 && %charge > 1000) { + %vis = 1; + %p = discharge2(%firePos,%dir,%dist); + } + else { + %vis = limit(mPow(%passCharge,1 / 3) / 10,0,1); + %p = discharge(%firePos,%dir); + createLifeLight(%hitPos,1,1000); + + if (%passCharge) { + %obj.lastDamagedBy = %emit; + %obj.setZap(%passCharge,%hitPos); + } + } + // TODO - 100% always, is this right? + %vis = 1; + %p.setEnergyPercentage(%vis); + addToShock(%p); + %p.schedule(1000,"delete"); + + return %charge; +} + +///Put the charge on the target +function GameBase::setZap(%obj,%charge,%hitPos) { + %group = nameToID(ShockGroup); + if (!%obj.zapSched && !(%group > 0 && %group.getCount() > 50)) { + %lagTime = ShockGroup.getCount() * 1000 + (getRandom() + 1); + %energyTime = %charge * 100 + (getRandom() + 1); + %otherTime = getRandom()* 0.5 + 1; + %condTime = 1 + isConductor(%obj) * 5 + isPlayer(%obj) * 50; + %time = limit(((%lagtime + %energyTime) * %otherTime) / %condTime,0); + %objClass = %obj.getDatablock().getName(); + + if (%obj.client) + %obj.emp(getSimTime() + limit(%time - 500,0,240000)); + else if ($MTC_Loaded == true) { + if (%obj.isMTC()) { + %obj.base.emp(limit(%time * 10,6000)); + %wasMTC = true; + } + } + if (!%wasMTC && !%obj.client) { + if ((getRandom()* ShockGroup.getCount()) < 50) + createLifeEmitter(%hitPos,InfEmitter,limit(%time - 500,0,240000)); +// createLifeLight(%hitPos,"0",limit(%time - 500,0,240000)); + } + %obj.zapTime = getSimTime() + %time; + %obj.zapSched = %obj.schedule(%time,"zap",0); + } + %obj.playShieldEffect("0 0 1"); + %obj.charge = %obj.getCharge() + %passCharge; + %obj.chargeTime = getSimTime(); +} + +function createLifeLight(%pos,%type,%time) { + %light = new Item() { + datablock = ShockLight @ %type; + static = true; + position = %pos; + }; + %light.schedule(%time,"delete"); +} + +///Checks to see if there's back and forth contact between the two +function zapLos(%pos,%target,%miss,%obj) { + %vec = getRandom() * 2 - 1 SPC GetRandom() * 2 - 1 SPC GetRandom() * 2 - 1; + %dest = %target.getEdge(%vec); + %res = containerRayCast(%dest,%pos,$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType | $TypeMasks::VehicleObjectType,%target); + + if (%res != %obj && %obj) + return ""; + else if (!%res) + %firePos = %pos; + else + %firePos = getWords(%res,1,3); + + %dir = vectorMiss(vectorNormalize(vectorSub(%dest,%firepos)),%miss); + %res2 = containerRayCast(%firePos,vectorAdd(%firePos,vectorScale(%dir,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StaticObjectType,%obj); + + if (!%res2) + %hitPos = vectorAdd(%firePos,vectorScale(%dir,2000)); + else + %hitPos = getWords(%res2,1,3); + + if (%res2 != %target) + return 1 SPC %firePos SPC %hitPos; + else + return 2 SPC %firePos SPC %hitPos; +} + +///Returns the current charge +function ShapeBase::getCharge(%obj) { + %oldCharge = %obj.charge; + %halfTime = 5 * 60 * 1000; //5 minutes before the charges halves + %timePassed = limit((getSimTime() - %obj.chargeTime),100) / %halfTime; + return %oldCharge * mPow(0.5,%timePassed); +} + +///Returns the totalCharge at an radius around an location +function totalCharge(%pos,%radius) { + initContainerRadiusSearch(%pos,%radius,$TypeMasks::PlayerObjectType | $TypeMasks::StaticShapeObjectType); + while ((%damage = containerSearchNext()) != 0) + %total = %total + %damage.getCharge(); + return %total; +} + +///Returns the physical statistics of an object +function ShapeBase::getSizeFactor(%obj,%type) { + %objClass = %obj.getDatablock().classname; + //[[Physical factors]] + %factor1 = mPow(%obj.getVolume(),1 / 3) / 2; // Normalized to players + %factor2 = %obj.getMaxDamage(); //Object Density + %factor3 = VectorDot(%obj.getSurface(),"2 2 2"); //Object's total surface + //[[Energy factors]] + %factor3 = %obj.getMaxEnergy() / 100; //Energy Capacity + %factor4 = %obj.getDatablock().rechargeRate; //Energy management + //[[Object specific]] + %factor5 = isConductor(%obj); //Sensor absorbtion + %factor6 = %objClass $= "armor"; //Player living tissue. + %factor7 = %obj.powerRadius / 100; //Energy production + + if (%type $= "maxCharge") + return (%factor1 * %factor2) * 100 + (%factor3 * %factor4) * 100 + %factor5 * 2000 - %factor6 * 100 + %factor7 * 100; +} + +///Retuns objects who will absorb charge (conductors) and return it to enemies. + function isConductor(%obj) { + if (isObject(%obj)) { + if (%obj.getDataBlock().className $= "sensor") + return 1; + else if (%obj.getDataBlock().className $= "energizer") + return 1; + } + return ""; + } + +//////////////////////////////////////////// + +/// Standard ion beam +function discharge(%pos,%vec) { + %p = new SniperProjectile() { + dataBlock = ShockBeam; + initialDirection = %vec; + initialPosition = %pos; + }; + serverPlay3D(%pos,"IonFireSound"); + createLifeLight(%pos,2,500); + createLifeEmitter(%pos,ShockFlareEmitter,450); + return %p; +} + +/// Large ion beam +function discharge2(%pos,%vec,%dist,%obj) { + if (!%dist) { + %res = containerRayCast(%pos,vectorAdd(%pos,vectorScale(%vec,2000)),$TypeMasks::PlayerObjectType | $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticObjectType,%obj); + if (%res) + %hitloc = vectorAdd(getWords(%res,1,3),vectorScale(%dir,-1)); + else + %hitLoc = vectorAdd(%pos,vectorScale(%vec,2000)); + } + else + %hitLoc = vectorAdd(%pos,vectorScale(%vec,%dist)); + + zap(0,200,%hitLoc); + + if (getRandom()* 10 < 10 ) + schedule(1000,0,"Serverplay3D",$Ion::ThunderSound[mFloor(getRandom() * $Ion::ThunderSoundCount)],%hitLoc); + +// radiusExplosion(0,vectorAdd(%hitLoc,vectorScale(%vec,-2)),5,0.25,5000,0,$DamageType::Lightning); + + %p = new SniperProjectile() { + dataBlock = ShockBeam2; + initialDirection = %vec; + initialPosition = %pos; + }; + createLifeLight(%pos,1,500); + createLifeEmitter(%pos,ShockFlareEmitter2,750); + Serverplay3D(LightningHitSound,%pos); + return %p; +} + +///Failed Elf beam experiment +function discharge3(%pos,%vec,%target) { + %pos = vectorAdd(%pos,"0 0 4"); + %vec = vectorMiss(vectorNormalize(vectorSub(pos(%target),%pos)),100); + %up = vectorCross(%vec,vectorCross("0 0 1",%vec)); + %left = vectorCross(%vec,%up); + + %rot = "0 0 1 3.14"; + + %emitter = new StaticShape() { + position = %pos; + rotation = %rot; + datablock = "LightningTarget"; + }; + +// echo(%target); + + //ElfProjectile() + %p = new ElfProjectile() { + dataBlock = BasicElf; + initialDirection = %vec; + initialPosition = %pos; + sourceObject = %emitter; + damageFactor = 1; + sourceSlot = 0; + targetObject= %target; + }; +// %p.setEnergyLevel(1); + %emitter.setRotation(fullRot(%up,%left)); + %emitter.schedule(5000,"delete"); + %p.schedule(5000,"delete"); + return discharge(%pos,%vec); +} + +///Adds the beams to the ShockGroup for lag issues. +function addToShock(%obj) { + %group = nameToID("MissionCleanup/ShockGroup"); + if (%group <= 0) { + %group = new SimGroup("ShockGroup"); + MissionCleanup.add(%group); + } + %group.add(%obj); +} + +//Should be the effect that happens to moving objects when zapped. +function GameBase::zapLight(%obj,%time) { + return ""; + // Aparently only 1 light can be mounted to an object at a time.. odd. + if (%obj.turret && %disabled) { + %obj.turret.mountImage("zapLight2",3,false); + %obj.turret.schedule(%time,"unmountImage",3); + } + else if (%obj.client) { + %obj.mountImage("zapLight2",3,false); + %obj.schedule(%time,"unmountImage",3); +// %deplObj.playThread($PowerThread,"Power"); + } +} + +function Player::emp(%obj,%time) { + cancel(%obj.empSched); + + if (isObject(%obj.zap)) + %obj.zap.setTransform(%obj.getTransform()); + else + %obj.zapObject(); + %obj.setEnergylevel(0); + if (%time > getSimTime() && (getSimTime() - %time < 240000)) + %obj.empSched = %obj.schedule(510,"emp",%time); + else { + %obj.stopZap(); + %obj.zap2Object(); + %obj.schedule(500,"stopZap"); + } +} + +///////////////// IonStorm Functions ////////////////// + +///Creates ionstorm with a beam every %repeat time period for %times times. +function ionStorm(%times,%repeat) { + cancel($IonStorm); + ionStormBlast(); + if (%repeat < 1000) + %repeat = 1000; + %times = %times - 1; + if (%times > 0) + $IonStorm = schedule(%repeat,0,"Ionstorm",%times,%repeat); +} + +///A large beam from the air. +function ionStormBlast(%target,%radius) { +// echo("ionStormBlast"); + if (isObject(%target.player)) + %pos = pos(%target.player); + else if (isObject(%target)) + %pos = pos(%target); + else { + if (getRandom() < $IonStorm::PlayerSeek) + %pos = pos(randomPlayer()); + else + %pos = pos(randomDeployable()); + } + + if (%radius) + %dist = getRandom() * %radius; + else + %dist = getRandom() * $IonStorm::Radius; + + %angle = getRandom() * 2 * $Pi; + %loc = getTerrainHeight2(vectorAdd(mCos(%angle) * %dist SPC mSin(%angle) * %dist SPC "0",%pos)); + createLifeEmitter(%loc,PrebeamEmitter,5000); + schedule(5000,0,"ionStormBeam",vectorAdd(%loc,"0 0" SPC $IonStorm::Height)); +} + +///The actual beam +function ionStormBeam(%loc) { + %p = discharge2(%loc,"0 0 -1"); + %p.setEnergyPercentage(1); + addToShock(%p); + %p.schedule(1000,"delete"); +} + +///////////////////////////////////////////// +////////////////IONMISSILE/////////////////// +///////////////////////////////////////////// + +datablock ParticleData(IonJetParticle) { + dragCoefficient = 0.0; + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 500; + lifetimeVarianceMS = 0; + textureName = "particleTest"; + colors[0] = "0.4 0.4 0.9 1.0"; + colors[1] = "0.5 0.65 1 1"; + sizes[0] = 1.40; + sizes[1] = 1.15; +}; + +datablock ParticleEmitterData(IonJetEmitter) { + ejectionPeriodMS = 3; + periodVarianceMS = 0; + ejectionVelocity = 6.0; + velocityVariance = 5.0; + ejectionOffset = 0.0; + overrideAdvances = false; + thetaMin = 110; + thetaMax = 180; + phiReferenceVel = 0; + phiVariance = 360; + particles = "IonJetParticle"; +}; + +datablock SeekerProjectileData(IonMissile):ShoulderMissile { + hasDamageRadius = true; + indirectDamage = 0.1; + damageRadius = 1.0; + radiusDamageType = $DamageType::Missile; + kickBackStrength = 5; + explosion = "ShockExplosion2"; + baseEmitter = IonJetEmitter; + delayEmitter = ELFSparksEmitter; + + lifetimeMS = 120000; + muzzleVelocity = 10.0; + maxVelocity = 30.0; + turningSpeed = 110.0; + acceleration = 20.0; + + hasLight = true; + lightRadius = 5.0; + lightColor = "0.5 0.5 1"; +}; + +function IonMissile::onExplode(%data,%proj,%pos,%mod) { + zap(0,200,%pos); + Parent::onExplode(%data,%proj,%pos,%mod); +} diff --git a/Scripts/item.cs b/Scripts/item.cs new file mode 100644 index 0000000..f13df2c --- /dev/null +++ b/Scripts/item.cs @@ -0,0 +1,1204 @@ +//---------------------------------------------------------------------------- + +// When first mounted (assuming there is ammo): +// SingleShot activate -> ready +// Spinning activate -> idle (spin 0) +// Sustained activate -> ready +// DiscLauncher activate -> reload -> spinup -> ready +// +// Normal operation: +// SingleShot ready -> fire -> reload -> ready +// Spinning idle (spin 0) -> spinup -> ready -> fire -> spindown -> idle +// Sustained ready -> fire -> reload -> ready +// DiscLauncher ready -> fire -> reload -> spinup -> ready + +// Image properties +// emap +// preload +// shapeFile +// mountPoint +// offset +// rotation +// firstPerson +// mass +// usesEnergy +// minEnergy +// accuFire +// lightType +// lightTime +// lightRadius +// lightColor + +// Image state variables +// stateName +// stateTransitionOnLoaded +// stateTransitionOnNotLoaded +// stateTransitionOnAmmo +// stateTransitionOnNoAmmo +// stateTransitionOnTriggerUp +// stateTransitionOnTriggerDown +// stateTransitionOnTimeout +// stateTimeoutValue +// stateFire +// stateEnergyDrain +// stateAllowImageChange +// stateScaleAnimation +// stateDirection +// stateLoadedFlag +// stateSpinThread +// stateRecoil +// stateSequence +// stateSound +// stateScript +// stateEmitter +// stateEmitterTime +// stateEmitterNode + +//---------------------------------------------------------------------------- + +$ItemRespawnTime = 30000; +$ItemPopTime = 60 * 1000; + +$WeaponSlot = 0; +$AuxiliarySlot = 1; +$BackpackSlot = 2; +$FlagSlot = 3; + +//---------------------------------------------------------------------------- +datablock EffectProfile(ItemPickupEffect) +{ + effectname = "packs/packs.pickupPack"; + minDistance = 2.5; +}; + +datablock AudioProfile(ItemPickupSound) +{ + filename = "fx/packs/packs.pickuppack.wav"; + description = AudioClosest3d; + effect = ItemPickupEffect; + preload = true; +}; + +datablock EffectProfile(ItemThrowEffect) +{ + effectname = "packs/packs.throwpack"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(ItemThrowSound) +{ + filename = "fx/packs/packs.throwpack.wav"; + description = AudioClosest3d; + effect = ItemThrowEffect; + preload = true; +}; + +datablock AudioProfile(RepairPatchSound) +{ + filename = "fx/misc/health_patch.wav"; + description = AudioClosest3d; + preload = true; + effect = ItemPickupEffect; + preload = true; +}; + +function ItemData::create(%block) +{ + if(%block $= "flag") + %obj = new Item() { + className = FlagObj; + dataBlock = %block; + static = false; + rotate = false; + }; + else + %obj = new Item() { + dataBlock = %block; + static = true; + //rotate = true; + // don't make "placed items" rotate + rotate = false; + }; + return(%obj); +} + +//-------------------------------------------------------------------------- +function Item::schedulePop(%this) +{ + %itemFadeTime = 1000; // items will take 1 second (1000 milliseconds) to fade out + %this.startFade(%itemFadeTime, $ItemPopTime - %itemFadeTime, true); + %this.schedule($ItemPopTime, "delete"); +} + +function Item::respawn(%this) +{ + %this.startFade(0, 0, true); + %this.schedule($ItemRespawnTime + 100, "startFade", 1000, 0, false); + %this.hide(true); + %this.schedule($ItemRespawnTime, "hide", false); +} + +//-------------------------------------------------------------------------- +function ItemData::onThrow(%data,%obj,%shape) +{ + serverPlay3D(ItemThrowSound, %obj.getTransform()); + // don't schedule a delete for satchelCharges when they're deployed + if(!%data.noTimeout) + %obj.schedulePop(); +} + +function ItemData::onInventory(%data,%shape,%value) +{ + if (!%value) { + // If we don't have any more of these items, make sure + // we don't have an image mounted. + %slot = %shape.getMountSlot(%data.image); + if (%slot != -1) + %shape.unmountImage(%slot); + } +} + +function ItemData::onEnterLiquid(%data, %obj, %coverage, %type) +{ + if(%data.isInvincible) + return; + + switch(%type) + { + case 0: + //Water + case 1: + //Ocean Water + case 2: + //River Water + case 3: + //Stagnant Water + case 4: + //Lava + %obj.delete(); + case 5: + //Hot Lava + %obj.delete(); + case 6: + //Crusty Lava + %obj.delete(); + case 7: + //Quick Sand + } +} + +function ItemData::onLeaveLiquid(%data, %obj, %type) +{ + // dummy +} + +function ItemData::onCollision(%data,%obj,%col) +{ + // Default behavior for items is to get picked + // by the colliding object. + if (%col.getDataBlock().className $= Armor && %col.getState() !$= "Dead") + { + if (%col.isMounted()) + return; + + if (%col.pickup(%obj, 1)) + { + if (%col.client && !%obj.dispenser) + { + messageClient(%col.client, 'MsgItemPickup', '\c0You picked up %1.', %data.pickUpName); + serverPlay3D(ItemPickupSound, %col.getTransform()); + } + //[most] Litle hook to respawn dispenser packs + // it's this or a schedule.. and I don't feel like that.. :P + %respawn = %obj.dispenser; + if (%obj.isStatic()) + %obj.respawn(); + else + %obj.delete(); + + if (IsObject(%respawn)) + respawnpack(%respawn,1); + //[most] + } + } +} + +//---------------------------------------------------------------------------- +datablock ItemData(RepairKit) +{ + className = HandInventory; + catagory = "Misc"; + shapeFile = "repair_kit.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2.0; + pickUpName = "a repair kit"; + alwaysAmbient = true; + + computeCRC = true; + emap = true; + +}; + +function RepairKit::onUse(%data,%obj) { + // Don't use the kit unless we're damaged + if (%obj.getDamageLevel() != 0) { + %obj.decInventory(%data,1); + %obj.applyRepair(0.2); + messageClient(%obj.client, 'MsgRepairKitUsed', '\c2Repair Kit Used.'); + } +} + +//---------------------------------------------------------------------------- + +datablock ItemData(RepairPatch) +{ + catagory = "Misc"; + shapeFile = "repair_patch.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2.0; + pickUpName = "a repair patch"; + alwaysAmbient = true; + + computeCRC = true; + emap = true; + +}; + +function RepairPatch::onCollision(%data,%obj,%col) +{ + if ( %col.getDataBlock().className $= Armor + && %col.getDamageLevel() != 0 + && %col.getState() !$= "Dead" ) + { + if (%col.isMounted()) + return; + + %col.playAudio(0, RepairPatchSound); + %col.applyRepair(0.125); + + // Eolk - cure in zombieGame. + if(%col.infected && Game.class $= "ZombieGame") + { + %col.infected = 0; + cancel(%col.infectedDamage); + %col.infectedDamage = ""; + %col.beats = 0; + %col.canZkill = 0; + cancel(%col.zombieAttackImpulse); + } + + %obj.respawn(); + if (%col.client > 0) + messageClient(%col.client, 'MsgItemPickup', '\c0You picked up %1.', %data.pickUpName); + } +} + +//---------------------------------------------------------------------------- +// Flag: +//---------------------------------------------------------------------------- +datablock ShapeBaseImageData(FlagImage) +{ + shapeFile = "flag.dts"; + item = Flag; + mountPoint = 2; + offset = "0 0 0"; + + lightType = "PulsingLight"; + lightColor = "0.5 0.5 0.5 1.0"; + lightTime = "1000"; + lightRadius = "3"; + cloakable = false; +}; + +datablock ItemData(Flag) +{ + catagory = "Objectives"; + shapefile = "flag.dts"; + mass = 55; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + pickUpName = "a flag"; + computeCRC = true; + + lightType = "PulsingLight"; + lightColor = "0.5 0.5 0.5 1.0"; + lightTime = "1000"; + lightRadius = "3"; + + isInvincible = true; + cmdCategory = "Objectives"; + cmdIcon = CMDFlagIcon; + cmdMiniIconName = "commander/MiniIcons/com_flag_grey"; + targetTypeTag = 'Flag'; + + //used in CTF to mark the flag during a stalemate... + 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; + hudRenderName[1] = true; +}; + +//---------------------------------------------------------------------------- +function Flag::onThrow(%data,%obj,%src) +{ + Game.playerDroppedFlag(%src); +} + +function Flag::onAdd(%this, %obj) +{ + // make sure flags play "flapping" ambient thread + Parent::onAdd(%this, %obj); + %obj.playThread($AmbientThread, "ambient"); + + %blocker = new VehicleBlocker() + { + position = %obj.position; + rotation = %obj.rotation; + dimensions = "2 2 4"; + }; + MissionCleanup.add(%blocker); +} + +function Flag::onCollision(%data,%obj,%col) +{ + if (%col.getDataBlock().className $= Armor && !%col.client.isJailed) + { + if (%col.isMounted()) + return; + + // a player hit the flag + Game.playerTouchFlag(%col, %obj); + } +} + +//---------------------------------------------------------------------------- +// HuntersFlag: +//---------------------------------------------------------------------------- +datablock ShapeBaseImageData(HuntersFlagImage) +{ + shapeFile = "Huntersflag.dts"; + item = Flag; + mountPoint = 2; + offset = "0 0 0"; + + lightType = "PulsingLight"; + lightColor = "0.5 0.5 0.5 1.0"; + lightTime = "1000"; + lightRadius = "3"; +}; + +// 1: red +// 2: blue +// 4: yellow +// 8: green +datablock ItemData(HuntersFlag1) +{ + className = HuntersFlag; + + shapefile = "Huntersflag.dts"; + mass = 75; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 3; + isInvincible = true; + pickUpName = "a flag"; + computeCRC = true; + + lightType = "PulsingLight"; + lightColor = "0.8 0.2 0.2 1.0"; + lightTime = "1000"; + lightRadius = "3"; +}; + +datablock ItemData(HuntersFlag2) : HuntersFlag1 +{ + lightColor = "0.2 0.2 0.8 1.0"; +}; + +datablock ItemData(HuntersFlag4) : HuntersFlag1 +{ + lightColor = "0.8 0.8 0.2 1.0"; +}; + +datablock ItemData(HuntersFlag8) : HuntersFlag1 +{ + lightColor = "0.2 0.8 0.2 1.0"; +}; + +function HuntersFlag::onRemove(%data, %obj) +{ + // dont want target removed... +} + +function HuntersFlag::onThrow(%data,%obj,%src) +{ + Game.playerDroppedFlag(%src); +} + +function HuntersFlag::onCollision(%data,%obj,%col) +{ + if (%col.getDataBlock().className $= Armor && !%col.client.isJailed) + { + if (%col.isMounted()) + return; + + // a player hit the flag + Game.playerTouchFlag(%col, %obj); + } +} + +//---------------------------------------------------------------------------- +// Nexus: +//---------------------------------------------------------------------------- +datablock ItemData(Nexus) +{ + catagory = "Objectives"; + shapefile = "nexus_effect.dts"; + mass = 10; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + icon = "CMDNexusIcon"; + targetTypeTag = 'Nexus'; + + computeCRC = true; + +}; + +datablock ParticleData(NexusParticleDenied) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = 3.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 1200; + lifetimeVarianceMS = 400; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + colors[0] = "0.3 0.0 0.0 1.0"; + colors[1] = "0.5 0.0 0.0 0.5"; + colors[2] = "0.7 0.0 0.0 0.0"; + sizes[0] = 0.2; + sizes[1] = 0.1; + sizes[2] = 0.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(NexusParticleDeniedEmitter) +{ + ejectionPeriodMS = 2; + ejectionOffset = 0.2; + periodVarianceMS = 0.5; + ejectionVelocity = 10.0; + velocityVariance = 4.0; + thetaMin = 0.0; + thetaMax = 30.0; + lifetimeMS = 0; + + particles = "NexusParticleDenied"; +}; + +datablock ParticleData(NexusParticleCap) +{ + dragCoeffiecient = 0.4; + gravityCoefficient = 3.0; + inheritedVelFactor = 0.0; + + lifetimeMS = 1200; + lifetimeVarianceMS = 400; + + textureName = "particleTest"; + + useInvAlpha = false; + spinRandomMin = -200.0; + spinRandomMax = 200.0; + + colors[0] = "0.5 0.8 0.2 1.0"; + colors[1] = "0.6 0.9 0.3 1.0"; + colors[2] = "0.7 1.0 0.4 1.0"; + sizes[0] = 0.2; + sizes[1] = 0.1; + sizes[2] = 0.0; + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; +}; + +datablock ParticleEmitterData(NexusParticleCapEmitter) +{ + ejectionPeriodMS = 2; + ejectionOffset = 0.5; + periodVarianceMS = 0.5; + ejectionVelocity = 10.0; + velocityVariance = 4.0; + thetaMin = 0.0; + thetaMax = 30.0; + lifetimeMS = 0; + + particles = "NexusParticleCap"; +}; + +//---------------------------------------------------------------------------- + +function getVector(%string, %num) +{ + %start = %num * 3; + return getWords(%string,%start, %start + 2); +} + +// -------------------------------------------- +// explosion datablock +// -------------------------------------------- + +datablock ExplosionData(DeployablesExplosion) +{ + soundProfile = DeployablesExplosionSound; + faceViewer = true; + + explosionShape = "effect_plasma_explosion.dts"; + sizes[0] = "0.2 0.2 0.2"; + sizes[1] = "0.3 0.3 0.3"; +}; + +$TeamDeployableMax[TargetBeacon] = 10; +$TeamDeployableMax[MarkerBeacon] = 20; + +datablock ItemData(Beacon) +{ + className = HandInventory; + catagory = "Misc"; + shapeFile = "beacon.dts"; + mass = 1; + elasticity = 0.2; + friction = 0.8; + pickupRadius = 1; + pickUpName = "a deployable beacon"; + hasLight = true; + //lightType = "PulsingLight"; + lightColor = "0.5 0.5 0.5 1.0"; + //lightTime = "100"; + lightRadius = "3"; + + + computeCRC = true; + +}; + +datablock StaticShapeData(DeployedBeacon) : StaticShapeDamageProfile +{ + shapeFile = "beacon.dts"; + explosion = DeployablesExplosion; + maxDamage = 0.45; + disabledLevel = 0.45; + destroyedLevel = 0.45; + targetNameTag = 'beacon'; + hasLight = true; + lightType = "PulsingLight"; + lightColor = "0.5 0.5 0.5 1.0"; + lightTime = "100"; + lightRadius = "3"; + + deployedObject = true; + + dynamicType = $TypeMasks::SensorObjectType; + + debrisShapeName = "debris_generic_small.dts"; + debris = SmallShapeDebris; +}; + +function DeployedBeacon::onDestroyed(%data, %obj, %prevState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + if(%obj.getBeaconType() $= "friend") + %bType = "MarkerBeacon"; + else + %bType = "TargetBeacon"; + $TeamDeployedCount[%obj.team, %bType]--; + remDSurface(%obj); + %obj.schedule(500, delete); +} + +function Beacon::onUse(%data, %obj) +{ + %armortype = %obj.getdatablock().getname(); + if(%armortype $= "ControlLordZombieArmor") + { + %pos = %obj.getMuzzlePoint($WeaponSlot); + %vec = %obj.getMuzzleVector($WeaponSlot); + %targetpos = vectoradd(%pos,vectorscale(%vec,5)); + // Eolk - add new masks to keep people from grabbing through walls, etc. + %masks = $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $Typemasks::PlayerObjectType; + %Tobj = containerraycast(%pos, %targetpos, %masks, %obj); + %Tobj = getword(%Tobj,0); + if(isObject(%Tobj) && %Tobj.getType() & $TypeMasks::PlayerObjectType && strstr(%Tobj.getDatablock().getName(), "Zombie") == -1) + { + %temp = %obj.getForwardVector(); + %temp2 = %Tobj.getForwardVector(); + if(vectorDist(%temp,%temp2) > 5) + return; + + Llifttarget(%obj, %Tobj, 0); + %obj.setMoveState(true); + %obj.schedule(150 * 13, "setMoveState", false); + return; + } + } + + // look for 3 meters along player's viewpoint for interior or terrain + %searchRange = 3.0; + %mask = $TypeMasks::TerrainObjectType | $TypeMasks::InteriorObjectType | $TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType; + // get the eye vector and eye transform of the player + %eyeVec = %obj.getEyeVector(); + %eyeTrans = %obj.getEyeTransform(); + // extract the position of the player's camera from the eye transform (first 3 words) + %eyePos = posFromTransform(%eyeTrans); + // normalize the eye vector + %nEyeVec = VectorNormalize(%eyeVec); + // scale (lengthen) the normalized eye vector according to the search range + %scEyeVec = VectorScale(%nEyeVec, %searchRange); + // add the scaled & normalized eye vector to the position of the camera + %eyeEnd = VectorAdd(%eyePos, %scEyeVec); + // see if anything gets hit + %searchResult = containerRayCast(%eyePos, %eyeEnd, %mask, 0); + if(!%searchResult) + { + // Eolk - streamlining. + // Special case weapons come first. + %currentWeap = %obj.getMountedImage($weaponslot).item.getName(); + %currentWeapImage = %obj.getMountedImage($weaponslot); + if (%currentWeap $= Shotgun) + { + if (%obj.inv[ShotgunAmmo] <= 4) + { + if (%obj.inv[ShotgunAmmo] >= 1) + { + SGCheckforclip(%obj); + if (%obj.inv[ShotgunClip] > 0) + %obj.setInventory(ShotgunAmmo, 0); + } + } + } + else if (%currentWeap $= RShotgun) + { + if (%obj.inv[RShotgunAmmo] <= 24) + { + if (%obj.inv[RShotgunAmmo] >= 1) + { + RSGCheckforclip(%obj); + if (%obj.inv[RShotgunClip] > 0) + %obj.setInventory(RShotgunAmmo, 0); + } + } + } + else if (%currentWeap $= MG42) + { + if (%obj.inv[MG42Ammo] <= 100) + { + if (%obj.inv[MG42Ammo] >= 1) + { + MG42Checkforclip(%obj); + if (%obj.inv[MG42clip] > 0) + %obj.setInventory(MG42Ammo, 0); + } + } + } + else + { + if(%currentWeapImage $= "KriegRifleImage" || %currentWeapImage $= "LSMGImage" || %currentWeapImage $= "PistolImage" || %currentWeapImage $= "HRPChaingunImage" || %currentWeapImage $= "RPChaingunImage") + { + // Make it so you can't waste clips. + if (%obj.getInventory(%currentWeapImage.ammo) < %obj.maxInventory(%currentWeapImage.ammo)) + { + // Make it so you can't waste clips while reloading. + if (%obj.getInventory(%currentWeapImage.ammo) > 0) + { + %obj.getMountedImage($weaponslot).checkForClip(%obj); + %obj.setInventory(%currentWeapImage.ammo, 0); + } + } + } + else + { + %changed = cyclePackSetting(%obj,1); + if (!%changed) + { + if(%obj.inv[%data.getName()] > 0) + messageClient(%plyr.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon. Too far from surface.'); + } + + return %changed; + } + } + return 0; + } + else + { + %searchObj = getWord(%searchResult, 0); + if(%searchObj.getType() & ($TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType) ) + { + // if there's already a beacon where player is aiming, switch its type + // otherwise, player can't deploy a beacon there + if(%searchObj.getDataBlock().getName() $= DeployedBeacon) + switchBeaconType(%searchObj); + else + messageClient(%obj.client, 'MsgBeaconNoSurface', '\c2Cannot place beacon. Not a valid surface.'); + return 0; + } + else if(%obj.inv[%data.getName()] <= 0) + return 0; + } + // newly deployed beacons default to "target" type + if($TeamDeployedCount[%obj.team, TargetBeacon] >= $TeamDeployableMax[TargetBeacon]) + { + messageClient(%obj.client, 'MsgDeployFailed', '\c2Your team\'s control network has reached its capacity for this item.~wfx/misc/misc.error.wav'); + return 0; + } + %terrPt = posFromRaycast(%searchResult); + %terrNrm = normalFromRaycast(%searchResult); + + %intAngle = getTerrainAngle(%terrNrm); // getTerrainAngle() function found in staticShape.cs + %rotAxis = vectorNormalize(vectorCross(%terrNrm, "0 0 1")); + if (getWord(%terrNrm, 2) == 1 || getWord(%terrNrm, 2) == -1) + %rotAxis = vectorNormalize(vectorCross(%terrNrm, "0 1 0")); + %rotation = %rotAxis @ " " @ %intAngle; + + %obj.decInventory(%data, 1); + %depBeac = new BeaconObject() { + dataBlock = "DeployedBeacon"; + position = VectorAdd(%terrPt, VectorScale(%terrNrm, 0.05)); + rotation = %rotation; + }; + $TeamDeployedCount[%obj.team, TargetBeacon]++; + + %depBeac.playThread($AmbientThread, "ambient"); + %depBeac.team = %obj.team; + %depBeac.setOwner(%obj); + %depBeac.sourceObject = %obj; + addDSurface(%searchObj,%depBeac); + + // give it a team target + %depBeac.setTarget(%depBeac.team); + Deployables.add(%depBeac); +} + +function switchBeaconType(%beacon) +{ + if(%beacon.getBeaconType() $= "friend") + { + // switch from marker beacon to target beacon + if($TeamDeployedCount[%beacon.team, TargetBeacon] >= $TeamDeployableMax[TargetBeacon]) + { + messageClient(%beacon.sourceObject.client, 'MsgDeployFailed', '\c2Your team\'s control network has reached its capacity for this item.~wfx/misc/misc.error.wav'); + return 0; + } + %beacon.setBeaconType(enemy); + $TeamDeployedCount[%beacon.team, MarkerBeacon]--; + $TeamDeployedCount[%beacon.team, TargetBeacon]++; + } + else + { + // switch from target beacon to marker beacon + if($TeamDeployedCount[%beacon.team, MarkerBeacon] >= $TeamDeployableMax[MarkerBeacon]) + { + messageClient(%beacon.sourceObject.client, 'MsgDeployFailed', '\c2Your team\'s control network has reached its capacity for this item.~wfx/misc/misc.error.wav'); + return 0; + } + %beacon.setBeaconType(friend); + $TeamDeployedCount[%beacon.team, TargetBeacon]--; + $TeamDeployedCount[%beacon.team, MarkerBeacon]++; + } +} + +function cyclePackSetting(%plyr,%val) { + %changed = false; + if (%plyr.hasSpine) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["spine"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["spine"]; + %line = $packSetting["spine",%plyr.packSet]; + bottomPrint(%plyr.client,"Beam set to" SPC getWords(%line,3,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasMSpine) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["mspine"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["mspine"]; + %line = $packSetting["mspine",%plyr.packSet]; + bottomPrint(%plyr.client,"Beam set to" SPC getWords(%line,6,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasWalk) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["walk"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["walk"]; + %line = $packSetting["walk",%plyr.packSet]; + bottomPrint(%plyr.client,"Walkway set to" SPC getWords(%line,1,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasFloor) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["floor"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["floor"]; + %line = $packSetting["floor",%plyr.packSet]; + bottomPrint(%plyr.client,"Floor set" SPC getWords(%line,6,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasBlast) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["blast"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["blast"]; + %line = $packSetting["blast",%plyr.packSet]; + bottomPrint(%plyr.client,"Blastwall set to" SPC getWords(%line,0,getWordCount(%line)),2,1); + %changed = true; + } + //[most] //Refers to the more flexible more universal item switch code. + //Read reference below. + else if(%plyr.getMountedImage(2) && GetWord($packSettings[%plyr.getMountedImage(2).getName()],0)) { + %changed = %plyr.getMountedImage(2).ChangeMode(%plyr,%val,0); + } + //[most] + else if (%plyr.hasJumpad) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["jumpad"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["jumpad"]; + %line = $packSetting["jumpad",%plyr.packSet]; + bottomPrint(%plyr.client,"Jump Pad set to" SPC restWords(%line),2,1); + %changed = true; + } + else if (%plyr.hasForceField) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["forcefield"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["forcefield"]; + %line = $packSetting["forcefield",%plyr.packSet]; + bottomPrint(%plyr.client,getWords(%line,3,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasGravField) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["gravfield"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["gravfield"]; + %line = $packSetting["gravfield",%plyr.packSet]; + bottomPrint(%plyr.client,"Gravity field set to" SPC getWords(%line,3,getWordCount(%line)),2,1); + %changed = true; + } + else if (%plyr.hasTree) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["tree"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["tree"]; + %line = $packSetting["tree",%plyr.packSet]; + bottomPrint(%plyr.client,"Tree set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasCrate) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["crate"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["crate"]; + %line = $packSetting["crate",%plyr.packSet]; + bottomPrint(%plyr.client,"Crate set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasDecoration) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["decoration"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["decoration"]; + %line = $packSetting["decoration",%plyr.packSet]; + bottomPrint(%plyr.client,"Decoration set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasProjector) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["logoprojector"] || $Host::Purebuild == 0) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["logoprojector"]; + %line = $packSetting["logoprojector",%plyr.packSet]; + bottomPrint(%plyr.client,"Logo projector set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasVehiclepad) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["vehiclepadpack"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["vehiclepadpack"]; + %line = $packSetting["vehiclepadpack",%plyr.packSet]; + bottomPrint(%plyr.client,"Vehicle pad set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasLight) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["light"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["light"]; + %line = $packSetting["light",%plyr.packSet]; + bottomPrint(%plyr.client,"Light set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasSwitch) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["switch"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["switch"]; + %line = $packSetting["switch",%plyr.packSet]; + bottomPrint(%plyr.client,"Switch range set to" SPC %line SPC "meters.",2,1); + %changed = true; + } + else if (%plyr.hasTele) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > 40) + %plyr.packSet = 1; + if (%plyr.packSet < 1) + %plyr.packSet = 40; + bottomPrint(%plyr.client,"Telepad frequency set to: " @ %plyr.packSet,2,1); + %changed = true; + } + else if (%plyr.hasGen) { + %plyr.powerFreq = %plyr.powerFreq + %val; + if (%plyr.powerFreq > upperPowerFreq(%plyr)) + %plyr.powerFreq = 1; + if (%plyr.powerFreq < 1) + %plyr.powerFreq = upperPowerFreq(%plyr); + displayPowerFreq(%plyr); + %changed = true; + } + else if (%plyr.hasTripwire) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["tripwire"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["tripwire"]; + %line = $packSetting["tripwire",%plyr.packSet]; + bottomPrint(%plyr.client,"Tripwire range set to " @ getWord(%line,0) @ " meters, " @ (getWord(%line,1) ? "field" : "beam") @ " mode.",2,1); + %changed = true; + } + else if (%plyr.hasEscapePod) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["escapepod"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["escapepod"]; + displayEscapePodBoostPower(%plyr); + %changed = true; + } + else if (%plyr.hasMissileRack) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["missilerack"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["missilerack"]; + %line = $packSetting["missilerack",%plyr.packSet]; + bottomPrint(%plyr.client,"Missile Rack set to " @ %line ,2,1); + %changed = true; + } + else if (%plyr.hasDoor) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["Door"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["Door"]; + %line = $packSetting["Door",%plyr.packSet]; + bottomPrint(%plyr.client,"Door set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasZspawn) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["ZSpawn"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["ZSpawn"]; + %line = $packSetting["ZSpawn",%plyr.packSet]; + bottomPrint(%plyr.client,"Spawn set to" SPC %line,2,1); + %changed = true; + } + else if (%plyr.hasArtWepPack) { + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > $packSettings["ArtPack"]) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = $packSettings["ArtPack"]; + %line = $packSetting["ArtPack",%plyr.packSet]; + bottomPrint(%plyr.client,"Loadout set to" SPC %line,2,1); + %changed = true; + } + return %changed; +} + + +//This function can be used by all "normal" packswitching packs. +//normal in a sense all modes are given in globals with the design +//given inside effectpacks.cs which is very simular to our current standarts +//The idea is the globals define all the variables and are linked by their pack image. +//This way all "plugin" data can be in the "pack".cs +//Alternative modes like the telleport freq change or nested modes like the effects pack, +//can easly be made by an alternative "::Changemode" function which again can be located +//inside "pack".cs file creating full plugin compability. + +function ShapeBaseImageData::ChangeMode(%data,%plyr,%val,%level) +{ +if (%level == 0) //regular pack settings + { + %image = %data.getName(); + + %settings = $packSettings[%image]; + + %plyr.packSet = %plyr.packSet + %val; + if (%plyr.packSet > getWord(%settings,0)) + %plyr.packSet = 0; + if (%plyr.packSet < 0) + %plyr.packSet = getWord(%settings,0); + %packname = GetWords(%settings,2,getWordCount(%settings)); + + %curset = $PackSetting[%image,%plyr.packSet]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + + bottomPrint(%plyr.client,%packname SPC "Set to"SPC %line,2,1); + return true; + } +else if (%level == 1) //expert settings + { + + %image = %data.getName(); + + %settings = $expertSettings[%image]; + + %plyr.expertSet = %plyr.expertSet + %val; + if (%plyr.expertSet > getWord(%settings,0)) + %plyr.expertSet = 0; + if (%plyr.expertSet < 0) + %plyr.expertSet = getWord(%settings,0); + + %packname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $expertSetting[%image,%plyr.expertSet]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + + bottomPrint(%plyr.client,%packname SPC "Set to"SPC %line,2,1); + return true; + } +else if (%level == 2) //Weapon settings 1 + { + %image = %data.getName(); + + %settings = $weaponSettings1[%image]; + + %plyr.weaponSet1 = %plyr.weaponSet1 + %val; + if (%plyr.weaponSet1 > getWord(%settings,0)) + %plyr.weaponSet1 = 0; + if (%plyr.weaponSet1 < 0) + %plyr.weaponSet1 = getWord(%settings,0); + + %weaponname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $weaponSetting1[%image,%plyr.weaponSet1]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + + bottomPrint(%plyr.client,%weaponname SPC "Set to"SPC %line,2,1); + return true; + } +else if (%level == 3) //Weapon settings 2 !!no nesting!! + { + %image = %data.getName(); + + %settings = $weaponSettings2[%image]; + + %plyr.weaponSet2 = %plyr.weaponSet2 + %val; + if (%plyr.weaponSet2 > getWord(%settings,0)) + %plyr.weaponSet2 = 0; + if (%plyr.weaponSet2 < 0) + %plyr.weaponSet2 = getWord(%settings,0); + + %weaponname = GetWords(%settings,2,getWordCount(%settings)); + %curset = $weaponSetting2[%image,%plyr.weaponSet2]; + if (getWord(%settings,1) == -1) + %line = GetWords(%curset,0,getWordCount(%curset)); + else + %line = GetWords(%curset,0,getWord(%settings,1)); + + bottomPrint(%plyr.client,%weaponname SPC "Set to"SPC %line,2,1); + return true; + } +} + +//Neat lil settings mode fixer. +//It's optional.. but hey.. what the heck. +//Simply sees if the current setting is among the options.. if not .. set to option 1. + +//More usefull thought for specialized packs ;) ;) + +function ShapeBaseImageData::FixMode(%data,%plyr,%level) +{ +%image = %data.getName(); +if (%level == 0) //regular pack settings + { + %settings = $packSettings[%image]; + if (%plyr.packSet > getWord(%settings,0) || %plyr.packSet < 0) + %plyr.packSet = 0; + } +else if (%level == 1) //regular pack settings + { + %settings = $expertSettings[%image]; + if (%plyr.expertSet > getWord(%settings,0) || %plyr.expertSet < 0) + %plyr.expertSet = 0; + } +else if (%level == 2) //regular pack settings + { + %settings = $weaponSettings1[%image]; + if (%plyr.weaponSet1 > getWord(%settings,0) || %plyr.weaponSet1 < 0) + %plyr.weaponSet1 = 0; + } +else if (%level == 3) //regular pack settings + { + %settings = $weaponSettings2[%image]; + if (%plyr.weaponSet2 > getWord(%settings,0) || %plyr.weaponSet2 < 0) + %plyr.weaponSet2 = 0; + } +} diff --git a/Scripts/libraries.cs b/Scripts/libraries.cs new file mode 100644 index 0000000..8466e43 --- /dev/null +++ b/Scripts/libraries.cs @@ -0,0 +1,416 @@ +//Contain basic data libraries. +//Soon to be updated with more usefull stuff. + +// NOTE - any changes here must be considered in expertlibraries.cs !!! + +//** New format of information ** + +$packSettings["spine"] = 7; +$packSetting["spine",0] = "0.5 0.5 1.5 1.5 meters in height"; +$packSetting["spine",1] = "0.5 0.5 4 4 meters in height"; +$packSetting["spine",2] = "0.5 0.5 8 8 meters in height"; +$packSetting["spine",3] = "0.5 0.5 40 40 meters in height"; +$packSetting["spine",4] = "0.5 0.5 160 160 meters in height"; +$packSetting["spine",5] = "0.5 6 160 auto adjusting"; +$packSetting["spine",6] = "0.5 8 160 pad"; +$packSetting["spine",7] = "0.5 8 160 wooden pad"; + +$packSettings["mspine"] = 7; +$packSetting["mspine",0] = "1 1 1 2 2 0.5 1 meters in height"; +$packSetting["mspine",1] = "1 1 4 2 2 0.5 4 meters in height"; +$packSetting["mspine",2] = "1 1 8 2 2 0.5 8 meters in height"; +$packSetting["mspine",3] = "1 1 40 2 2 0.5 40 meters in height"; +$packSetting["mspine",4] = "1 1 160 2 2 0.5 160 meters in height"; +$packSetting["mspine",5] = "1 8 160 2 2 0.5 auto adjusting"; +$packSetting["mspine",6] = "1 6 160 2 2 0.5 normal rings"; +$packSetting["mspine",7] = "1 8 160 8 8 0.5 platform rings"; + +$expertSettings["mspine"] = 1; +$expertSetting["mspine",0] = "Rings disabled"; +$expertSetting["mspine",1] = "Rings enabled"; + +$packSettings["floor"] = 5; +$packSetting["floor",0] = "10 10 20 10 10 10 10 meters wide"; +$packSetting["floor",1] = "20 20 20 20 20 20 20 meters wide"; +$packSetting["floor",2] = "30 30 20 30 30 30 30 meters wide"; +$packSetting["floor",3] = "40 40 20 40 40 40 40 meters wide"; +$packSetting["floor",4] = "50 50 20 50 50 50 50 meters wide"; +$packSetting["floor",5] = "60 60 20 60 60 60 60 meters wide"; + +$expertSettings["floor"] = 4; +$expertSetting["floor",0] = "1.5 meters in height"; +$expertSetting["floor",1] = "5 meters in height"; +$expertSetting["floor",2] = "10 meters in height"; +$expertSetting["floor",3] = "20 meters in height"; +$expertSetting["floor",4] = "40 meters in height"; + +$packSettings["walk"] = 12; +$packSetting["walk",0] = "0 flat"; +$packSetting["walk",1] = "5 Sloped 5 degrees up"; +$packSetting["walk",2] = "10 Sloped 10 degrees up"; +$packSetting["walk",3] = "20 Sloped 20 degrees up"; +$packSetting["walk",4] = "45 Sloped 45 degrees up"; +$packSetting["walk",5] = "60 Sloped 60 degrees up"; +$packSetting["walk",6] = "90 Sloped 90 degrees up"; +$packSetting["walk",7] = "-5 Sloped 5 degrees down"; +$packSetting["walk",8] = "-10 Sloped 10 degrees down"; +$packSetting["walk",9] = "-20 Sloped 20 degrees down"; +$packSetting["walk",10] = "-45 Sloped 45 degrees down"; +$packSetting["walk",11] = "-60 Sloped 60 degrees down"; +$packSetting["walk",12] = "-90 Sloped 90 degrees down"; + +$expertSettings["walk"] = 3; +$expertSetting["walk",0] = "Normal walkway"; +$expertSetting["walk",1] = "No-flicker walkway"; +$expertSetting["walk",2] = "Double width walkway"; +$expertSetting["walk",3] = "Double height walkway"; + +$packSettings["blast"] = 3; +$packSetting["blast",0] = "deploy from inside"; +$packSetting["blast",1] = "deploy in frame"; +$packSetting["blast",2] = "deploy from outside"; +$packSetting["blast",3] = "deploy full thickness"; + +$expertSettings["blast"] = 1; +$expertSetting["blast",0] = "Normal Blast Wall"; +$expertSetting["blast",1] = "Multiple Blast Walls"; + +$packSettings["forcefield"] = 50; +$packSetting["forcefield",0] = "0.5 8 160 Force Field set to: [Solid] White"; +$packSetting["forcefield",1] = "0.5 8 160 Force Field set to: [Solid] Red"; +$packSetting["forcefield",2] = "0.5 8 160 Force Field set to: [Solid] Green"; +$packSetting["forcefield",3] = "0.5 8 160 Force Field set to: [Solid] Blue"; +$packSetting["forcefield",4] = "0.5 8 160 Force Field set to: [Solid] Cyan"; +$packSetting["forcefield",5] = "0.5 8 160 Force Field set to: [Solid] Magenta"; +$packSetting["forcefield",6] = "0.5 8 160 Force Field set to: [Solid] Yellow"; +$packSetting["forcefield",7] = "0.5 8 160 Force Field set to: [Team-Pass] White"; +$packSetting["forcefield",8] = "0.5 8 160 Force Field set to: [Team-Pass] Red"; +$packSetting["forcefield",9] = "0.5 8 160 Force Field set to: [Team-Pass] Green"; +$packSetting["forcefield",10] = "0.5 8 160 Force Field set to: [Team-Pass] Blue"; +$packSetting["forcefield",11] = "0.5 8 160 Force Field set to: [Team-Pass] Cyan"; +$packSetting["forcefield",12] = "0.5 8 160 Force Field set to: [Team-Pass] Magenta"; +$packSetting["forcefield",13] = "0.5 8 160 Force Field set to: [Team-Pass] Yellow"; +$packSetting["forcefield",14] = "0.5 8 160 Force Field set to: [All-Pass] White"; +$packSetting["forcefield",15] = "0.5 8 160 Force Field set to: [All-Pass] Red"; +$packSetting["forcefield",16] = "0.5 8 160 Force Field set to: [All-Pass] Green"; +$packSetting["forcefield",17] = "0.5 8 160 Force Field set to: [All-Pass] Blue"; +$packSetting["forcefield",18] = "0.5 8 160 Force Field set to: [All-Pass] Cyan"; +$packSetting["forcefield",19] = "0.5 8 160 Force Field set to: [All-Pass] Magenta"; +$packSetting["forcefield",20] = "0.5 8 160 Force Field set to: [All-Pass] Yellow"; +////more//// +$packSetting["forcefield",21] = "0.5 8 160 Force Field set to: [Special Solid] Lightning"; +$packSetting["forcefield",22] = "0.5 8 160 Force Field set to: [Special Solid] Scan Line"; +$packSetting["forcefield",23] = "0.5 8 160 Force Field set to: [Special Solid] Grid"; +$packSetting["forcefield",24] = "0.5 8 160 Force Field set to: [Special Solid] Fire Wall"; +$packSetting["forcefield",25] = "0.5 8 160 Force Field set to: [Special Solid] Energy Field"; +$packSetting["forcefield",26] = "0.5 8 160 Force Field set to: [Special Solid] Flashing"; +$packSetting["forcefield",27] = "0.5 8 160 Force Field set to: [Special Solid] Dirty Window"; +$packSetting["forcefield",28] = "0.5 8 160 Force Field set to: [Special Team-Pass] Scan Line"; +$packSetting["forcefield",29] = "0.5 8 160 Force Field set to: [Special Team-Pass] Grid"; +$packSetting["forcefield",30] = "0.5 8 160 Force Field set to: [Special Team-Pass] Energy Field "; +$packSetting["forcefield",31] = "0.5 8 160 Force Field set to: [Special Team-Pass] Flashing"; +$packSetting["forcefield",32] = "0.5 8 160 Force Field set to: [Special All-Pass] Scan Line"; +$packSetting["forcefield",33] = "0.5 8 160 Force Field set to: [Special All-Pass] Grid"; +$packSetting["forcefield",34] = "0.5 8 160 Force Field set to: [Special All-Pass] Energy Field"; +$packSetting["forcefield",35] = "0.5 8 160 Force Field set to: [Special All-Pass] Flashing"; +$packSetting["forcefield",36] = "0.5 8 160 Force Field set to: [Special All-Pass] Weird Firewall"; +$packSetting["forcefield",37] = "0.5 8 160 Force Field set to: [Special All-Pass] Lava"; +$packSetting["forcefield",38] = "0.5 8 160 Force Field set to: [Special All-Pass] Water"; + +$packSetting["forcefield",39] = "0.5 8 160 Force Field set to: [Special] Flashing Crosshair"; +$packSetting["forcefield",40] = "0.5 8 160 Force Field set to: [Special] Glass Tile"; +$packSetting["forcefield",41] = "0.5 8 160 Force Field set to: [Special] Vehicle Icons"; +$packSetting["forcefield",42] = "0.5 8 160 Force Field set to: [Special] Space 1"; +$packSetting["forcefield",43] = "0.5 8 160 Force Field set to: [Special] Clouds 1"; +$packSetting["forcefield",44] = "0.5 8 160 Force Field set to: [Special] Fuzzy Scanlines"; +$packSetting["forcefield",45] = "0.5 8 160 Force Field set to: [Special] Space 2"; +$packSetting["forcefield",46] = "0.5 8 160 Force Field set to: [Special] Signs"; +$packSetting["forcefield",47] = "0.5 8 160 Force Field set to: [Special] Clouds 2"; +$packSetting["forcefield",48] = "0.5 8 160 Force Field set to: [Special] Computer Screen"; +$packSetting["forcefield",49] = "0.5 8 160 Force Field set to: [Special] Console"; +$packSetting["forcefield",50] = "0.5 8 160 Force Field set to: [Special] Standard Force Field"; + +$expertSettings["forcefield"] = 3; +$expertSetting["forcefield",0] = "Force Field Setting: [1] Normal Force Field"; +$expertSetting["forcefield",1] = "Force Field Setting: [2] Cubic-Replace Force Field"; +$expertSetting["forcefield",2] = "Force Field Setting: [3] Normal Force Field - No Slowdown"; +$expertSetting["forcefield",3] = "Force Field Setting: [4] Cubic-Replace Force Field - No Slowdown"; + +$packSettings["gravfield"] = 4; +$packSetting["gravfield",0] = "4.25 8 500 normal slow"; +$packSetting["gravfield",1] = "4.25 8 500 normal fast"; +$packSetting["gravfield",2] = "4.25 8 500 reverse slow"; +$packSetting["gravfield",3] = "4.25 8 500 reverse fast"; +$packSetting["gravfield",4] = "4.25 8 500 zero gravity"; +// +$packSetting["gravfield",5] = "4.25 8 500 fastfield"; +$packSetting["gravfield",6] = "4.25 8 500 super fastfield"; + +$expertSettings["gravfield"] = 2; +$expertSetting["gravfield",0] = "Normal gravity field"; +$expertSetting["gravfield",1] = "Cubic-replace gravity field (player's orientation)"; +$expertSetting["gravfield",2] = "Cubic-replace gravity field (object's orientation)"; + +$packSettings["jumpad"] = 3; +$packSetting["jumpad",0] = "1000 10 boost"; +$packSetting["jumpad",1] = "2500 25 boost"; +$packSetting["jumpad",2] = "5000 50 boost"; +$packSetting["jumpad",3] = "10000 100 boost"; + +$packSettings["tree"] = 13; +$packSetting["tree",0] = "normal 1"; +$packSetting["tree",1] = "normal 2"; +$packSetting["tree",2] = "normal 3"; +$packSetting["tree",3] = "normal 4"; +$packSetting["tree",4] = "greywood 1"; +$packSetting["tree",5] = "greywood 2"; +$packSetting["tree",6] = "greywood 3"; +$packSetting["tree",7] = "greywood 4"; +$packSetting["tree",8] = "greywood 5"; +$packSetting["tree",9] = "cactus 1"; +$packSetting["tree",10] = "cactus 2"; +$packSetting["tree",11] = "misc 1"; +$packSetting["tree",12] = "misc 2"; +$packSetting["tree",13] = "pod 1"; + +$expertSettings["tree"] = 14; +$expertSetting["tree",0] = "0.0625"; +$expertSetting["tree",1] = "0.125"; +$expertSetting["tree",2] = "0.25"; +$expertSetting["tree",3] = "0.5"; +$expertSetting["tree",4] = "0.75"; +$expertSetting["tree",5] = "1"; +$expertSetting["tree",6] = "2"; +$expertSetting["tree",7] = "3"; +$expertSetting["tree",8] = "4"; +$expertSetting["tree",9] = "5"; +$expertSetting["tree",10] = "6"; +$expertSetting["tree",11] = "7"; +$expertSetting["tree",12] = "8"; +$expertSetting["tree",13] = "9"; +$expertSetting["tree",14] = "10"; + +$packSettings["crate"] = 12; +$packSetting["crate",0] = "(1) back pack"; +$packSetting["crate",1] = "(2) small containment"; +$packSetting["crate",2] = "(3) large containment"; +$packSetting["crate",3] = "(4) compressor"; +$packSetting["crate",4] = "(5) tubes"; +$packSetting["crate",5] = "(6) quantum battery"; +$packSetting["crate",6] = "(7) proton accelerator"; +$packSetting["crate",7] = "(8) cargo crate"; +$packSetting["crate",8] = "(9) magnetic cooler"; +$packSetting["crate",9] = "(10) recycle unit"; +$packSetting["crate",10] = "(11) fuel cannister"; +$packSetting["crate",11] = "(12) wooden T2 box"; +$packSetting["crate",12] = "(13) plasma router"; + +$packSettings["decoration"] = 16; +$packSetting["decoration",0] = "[1] Banner Unity"; +$packSetting["decoration",1] = "[2] Banner Strength"; +$packSetting["decoration",2] = "[3] Banner Honor"; +$packSetting["decoration",3] = "[4] Dead Light Armor"; +$packSetting["decoration",4] = "[5] Dead Medium Armor"; +$packSetting["decoration",5] = "[6] Dead Heavy Armor"; +$packSetting["decoration",6] = "[7] Statue Base"; +$packSetting["decoration",7] = "[8] Light Male statue"; +$packSetting["decoration",8] = "[9] Light Female statue"; +$packSetting["decoration",9] = "[10] Heavy Male statue"; +$packSetting["decoration",10] = "[11] Beowulf Wreck"; +$packSetting["decoration",11] = "[12] Shrike Wreck"; +$packSetting["decoration",12] = "[13] Billboard 1"; +$packSetting["decoration",13] = "[14] Billboard 2"; +$packSetting["decoration",14] = "[15] Billboard 3"; +$packSetting["decoration",15] = "[16] Billboard 4"; +$packSetting["decoration",16] = "[17] Blue Pad"; + +$packSettings["logoprojector"] = 7; +$packSetting["logoprojector",0] = "your teams logo"; +$packSetting["logoprojector",1] = "Storm"; +$packSetting["logoprojector",2] = "Inferno"; +$packSetting["logoprojector",3] = "Starwolf"; +$packSetting["logoprojector",4] = "Diamond Sword"; +$packSetting["logoprojector",5] = "Blood Eagle"; +$packSetting["logoprojector",6] = "Phoenix"; +$packSetting["logoprojector",7] = "Bioderm"; + +$packSettings["switch"] = 6; +$packSetting["switch",0] = "5"; +$packSetting["switch",1] = "10"; +$packSetting["switch",2] = "25"; +$packSetting["switch",3] = "50"; +$packSetting["switch",4] = "100"; +$packSetting["switch",5] = "150"; +$packSetting["switch",6] = "200"; + +$expertSettings["switch"] = 2; +$expertSetting["switch",0] = "Normal switch"; +$expertSetting["switch",1] = "Timed switch on"; +$expertSetting["switch",2] = "Timed switch off"; + +$packSettings["light"] = 13; +$packSetting["light",0] = "(7) white"; +$packSetting["light",1] = "(6) red"; +$packSetting["light",2] = "(5) green"; +$packSetting["light",3] = "(4) blue"; +$packSetting["light",4] = "(3) cyan"; +$packSetting["light",5] = "(2) magenta"; +$packSetting["light",6] = "(1) yellow"; +$packSetting["light",7] = "(7) strobe white"; +$packSetting["light",8] = "(6) strobe red"; +$packSetting["light",9] = "(5) strobe green"; +$packSetting["light",10] = "(4) strobe blue"; +$packSetting["light",11] = "(3) strobe cyan"; +$packSetting["light",12] = "(2) strobe magenta"; +$packSetting["light",13] = "(1) strobe yellow"; + +$expertSettings["light"] = 1; +$expertSetting["light",0] = "On when powered"; +$expertSetting["light",1] = "Off when powered"; + +$packSettings["Door"] = 7; +$packSetting["Door",0] = "Normal Door"; +$packSetting["Door",1] = "Toggle Door"; +$packSetting["Door",2] = "Power Change Door (Open When Powered)"; +$packSetting["Door",3] = "Power Change Door (Closed When Powered)"; +$packSetting["Door",4] = "Collision Door (All Access)"; +$packSetting["Door",5] = "Collision Door (Permission Access)"; +$packSetting["Door",6] = "Collision Door (Owner-only Access)"; +$packSetting["Door",7] = "Collision Door (Admin-only Access)"; + +$expertSettings["Door"] = 5; +$expertSetting["Door",0] = "0 Seconds"; +$expertSetting["Door",1] = "0.5 Seconds"; +$expertSetting["Door",2] = "1 Second"; +$expertSetting["Door",3] = "2 Seconds"; +$expertSetting["Door",4] = "3 Seconds"; +$expertSetting["Door",5] = "4 Seconds"; + +$expertSettings[TripwireDeployableImage] = "3 -1"; +$expertSetting[TripwireDeployableImage,0] = "select range"; +$expertSetting[TripwireDeployableImage,1] = "select power logic"; +$expertSetting[TripwireDeployableImage,2] = "select field type"; +$expertSetting[TripwireDeployableImage,3] = "select field mode"; + +$packSettings[TripwireDeployableImage] = "6 -1 Tripwire range set to:"; +$packSetting[TripwireDeployableImage,0,0] = "5 meters"; +$packSetting[TripwireDeployableImage,1,0] = "10 meters"; +$packSetting[TripwireDeployableImage,2,0] = "25 meters"; +$packSetting[TripwireDeployableImage,3,0] = "50 meters"; +$packSetting[TripwireDeployableImage,4,0] = "100 meters"; +$packSetting[TripwireDeployableImage,5,0] = "150 meters"; +$packSetting[TripwireDeployableImage,6,0] = "200 meters"; + +$packSettings[TripwireDeployableImage,1] = "5 -1 Tripwire power logic set to:"; +$packSetting[TripwireDeployableImage,0,1] = "Normal Toggle On"; +$packSetting[TripwireDeployableImage,1,1] = "Normal Toggle Off"; +$packSetting[TripwireDeployableImage,2,1] = "Only Turn On"; +$packSetting[TripwireDeployableImage,3,1] = "Only Turn Off"; +$packSetting[TripwireDeployableImage,4,1] = "Timed Turn Off"; +$packSetting[TripwireDeployableImage,5,1] = "Timed Turn On"; + +$packSettings[TripwireDeployableImage,2] = "1 -1 Tripwire field type set to:"; +$packSetting[TripwireDeployableImage,0,2] = "Beam"; +$packSetting[TripwireDeployableImage,1,2] = "Field"; + +$packSettings[TripwireDeployableImage,3] = "1 -1 Tripwire field mode set to:"; +$packSetting[TripwireDeployableImage,0,3] = "Field invisible until touched"; +$packSetting[TripwireDeployableImage,1,3] = "No field"; + +$packSettings["escapepod"] = 7; +$packSetting["escapepod",0] = "1875"; // 12.25% +$packSetting["escapepod",1] = "3750"; // 25% +$packSetting["escapepod",2] = "5625"; // 37.5% +$packSetting["escapepod",3] = "7500"; // 50% +$packSetting["escapepod",4] = "9375"; // 62.5% +$packSetting["escapepod",5] = "11250"; // 75% +$packSetting["escapepod",6] = "13125"; // 87.5% +$packSetting["escapepod",7] = "15000"; // 100% + +$expertSettings["telepad"] = 3; +$expertSetting["telepad",0] = "team only"; +$expertSetting["telepad",1] = "any team"; +$expertSetting["telepad",2] = "only transmit"; +$expertSetting["telepad",3] = "only receive"; + +$packSettings["missilerack"] = 1; +$packSetting["missilerack",0] = "dumbfire missiles"; +$packSetting["missilerack",1] = "seeking missiles"; + +$packSettings["VehiclePadPack"] = 1; +$packSetting["VehiclePadPack",0] = "Standard Vehicle Pad"; +$packSetting["VehiclePadPack",1] = "Water Vehicle Pad"; + +$packSettings["ZSpawn"] = 5; +$packSetting["ZSpawn",0] = "Normal Zombie"; +$packSetting["ZSpawn",1] = "Ravanger Zombie"; +$packSetting["ZSpawn",2] = "Zombie Lord"; +$packSetting["ZSpawn",3] = "Demon Zombie"; +$packSetting["ZSpawn",4] = "Air-Rapier Zombie"; +$packSetting["ZSpawn",5] = "Demon Mother"; + +$expertsettings["Zspawn"] = 1; +$expertsetting["Zspawn",0] = "continual spawn"; +$expertsetting["Zspawn",1] = "spawn once"; + +$packSettings["ArtPack"] = 1; +$packSetting["ArtPack",0] = "Normal Shells 12 Ammo"; +$packSetting["ArtPack",1] = "Cluster Shells 6 Ammo"; + +$WeaponSettings["modifier0"] = 0; + +$WeaponSettings["modifier1"] = 18; +$WeaponSetting["modifier1",0] = "DeployedSpine lsb";//0.125 0.166666 1 +$WeaponSetting["modifier1",1] = "DeployedMSpine msb";//0.125 0.166666 1 +$WeaponSetting["modifier1",2] = "DeployedwWall WalkWay";//0.125 0.166666 1 +$WeaponSetting["modifier1",3] = "DeployedWall Bwall";//0.125 0.166666 1 +$WeaponSetting["modifier1",4] = "DeployedSpine2 Dark Pad";//0.125 0.166666 1 +$WeaponSetting["modifier1",5] = "DeployedCrate0 (crate1) back pack";//0.5 1 0.925 +$WeaponSetting["modifier1",6] = "DeployedCrate1 (crate2) small containment";//0.16 0.5 0.488 +$WeaponSetting["modifier1",7] = "DeployedCrate2 (crate3) large containment";//0.1 0.25 0.25 +$WeaponSetting["modifier1",8] = "DeployedCrate3 (crate4) compressor";//1 1 1 +$WeaponSetting["modifier1",9] = "DeployedCrate4 (crate5) tubes";//0.5 0.5 0.48 +$WeaponSetting["modifier1",10] = "DeployedCrate5 (crate6) quantum battery";//0.25 0.25 0.25 +$WeaponSetting["modifier1",11] = "DeployedCrate6 (crate7) proton accelerator";//0.25 0.5 0.5 +$WeaponSetting["modifier1",12] = "DeployedCrate7 (crate8) cargo crate";//0.1255 0.249 0.246 +$WeaponSetting["modifier1",13] = "DeployedCrate8 (crate9) magnetic cooler";//0.0835 0.167 0.1666 +$WeaponSetting["modifier1",14] = "DeployedCrate9 (crate10) recycle unit";//1.25 1.25 0.48; +$WeaponSetting["modifier1",15] = "DeployedCrate10 (crate11) fuel cannister";//0.834 0.834 0.336 +$WeaponSetting["modifier1",16] = "DeployedCrate11 (crate12) wooden T2 box"; +$WeaponSetting["modifier1",17] = "DeployedCrate12 (crate13) plasma router"; +$WeaponSetting["modifier1",18] = "DeployedDecoration6 (deco1) statue base"; + +$WeaponSettings["modifier2"] = 7; +$WeaponSetting["modifier2",0] = "+whole scale"; +$WeaponSetting["modifier2",1] = "+x axis scale"; +$WeaponSetting["modifier2",2] = "+y axis scale"; +$WeaponSetting["modifier2",3] = "+z axis scale"; +$WeaponSetting["modifier2",4] = "-whole scale"; +$WeaponSetting["modifier2",5] = "-x axis scale"; +$WeaponSetting["modifier2",6] = "-y axis scale"; +$WeaponSetting["modifier2",7] = "-z axis scale"; + +$WeaponSettings["modifier3"] = 7; +$WeaponSetting["modifier3",0] = "move up"; +$WeaponSetting["modifier3",1] = "move down"; +$WeaponSetting["modifier3",2] = "+x axis move"; +$WeaponSetting["modifier3",3] = "-x axis move"; +$WeaponSetting["modifier3",4] = "+y axis move"; +$WeaponSetting["modifier3",5] = "-y axis move"; +$WeaponSetting["modifier3",6] = "+z axis move"; +$WeaponSetting["modifier3",7] = "-z axis move"; + +$WeaponSettings["modifier4"] = 3; +$WeaponSetting["modifier4",0] = "0.1"; +$WeaponSetting["modifier4",1] = "0.01"; +$WeaponSetting["modifier4",2] = "0.001"; +$WeaponSetting["modifier4",3] = "1"; + +//list of smaller list +$WeaponSettings2["modifier"] = 5;//format :max mode +$WeaponSetting2["modifier",0] = $WeaponSettings["modifier0"] SPC"Merge Pieces"; +$WeaponSetting2["modifier",1] = $WeaponSettings["modifier1"] SPC"Swap Pad Texture"; +$WeaponSetting2["modifier",2] = $packSettings["forcefield"] SPC"Swap Force Field Texture"; +$WeaponSetting2["modifier",3] = $WeaponSettings["modifier2"] SPC"Scale Pieces"; +$WeaponSetting2["modifier",4] = $WeaponSettings["modifier3"] SPC"Nudge Pieces"; +$WeaponSetting2["modifier",5] = $WeaponSettings["modifier4"] SPC"Modifier Scaler"; diff --git a/Scripts/loadMenu.cs b/Scripts/loadMenu.cs new file mode 100644 index 0000000..f4feb59 --- /dev/null +++ b/Scripts/loadMenu.cs @@ -0,0 +1,98 @@ +//============================================================================== +// ACCM Load Menu. +//============================================================================== + +package loadmodinfo +{ + function sendLoadInfoToClient( %client ) + { + Parent::sendLoadInfoToClient(%client); + schedule(15000, 0, "ConInfoLoad", %client); + } + + function ConInfoLoad(%client) + { + %count = ClientGroup.getCount(); + for(%cl = 0; %cl < %count; %cl++) + { + %client = ClientGroup.getObject( %cl ); + if (!%client.isAIControlled()) + sendConInfoToClient(%client); + } + } + + function sendConInfoToClient(%client) + { + %on = "On"; + %off = "Off"; + %yes = "Yes"; + %no = "No"; + messageClient( %client, 'MsgLoadInfo', "", $CurrentMission, $MissionDisplayName, $Host::GameName ); + + // Send mod details: + %ModLine = "Advanced Combat Construction Mod 1.4.0" @ + "\nACCM Dev Team: Blnukem, Eolk and Dark Dragon DX." @ + "\nWebsite: www.freewebs.com/advancedccm"; + messageClient( %client, 'MsgLoadQuoteLine', "", %ModLine ); + + %ServerText = "Server Info:" @ + "\nMax Players: " @ $Host::MaxPlayers @ + "\nTime limit: " @ $Host::TimeLimit @ + "\nFriendly Fire: " @ ($Host::TeamDamageOn ? %on : %off) @ + "\nPure Build: " @ ($Host::Purebuild ? %on : %off) @ + "\nClient Saves: " @ ($Host::ClientSaving ? %on : %off) @ + "\nFlood Protection: " @ ($Host::FloodProtectionEnabled ? %on : %off) @ + "\nZombie Keeper Votes: " @ ($Host::AllowKeeperPlayerVotes ? %on : %off); + messageClient( %client, 'MsgLoadRulesLine', "", %ServerText ); + messageClient( %client, 'MsgLoadInfoDone' ); + } + +//------------------------------------------------------------------------------ +// Made by Blnukem. + + function debriefLoad(%client) + { + if (isObject(Game)) + %game = Game.getId(); + else + return; + + %sentinelcount = SentinelGroup.GetCount(); + %zombiecount = ZombieGroup.GetCount(); + + messageClient( %client, 'MsgDebriefResult', "", ""@$Host::GameName@"\nAdvanced Combat Construction Mod - Version 1.3.2"); + messageClient( %client, 'MsgDebriefAddLine', "", "ACCM Development Team:\ Blnukem, Eolk and Dark Dragon DX." ); + messageClient( %client, 'MsgDebriefAddLine', "", "Website: www.freewebs.com/advancedccm" ); + messageClient( %client, 'MsgDebriefAddLine', "", "" ); + if ($Host::MOTD !$= "") + { + messageClient( %client, 'MsgDebriefAddLine', "", " Message of the Day: \n "@$Host::MOTD@" \n "); + } else { + } + + messageClient( %client, 'MsgDebriefAddLine', "", $ACCMTip[mFloor(getRandom() * $ACCMTipCount)]); + messageClient( %client, 'MsgDebriefAddLine', "", "_____________________________________________ \n " ); + messageClient( %client, 'MsgDebriefAddLine', "", " Top Players: Scores: "); + + if ($Rank::numplayers < 1) + { + messageClient( %client, 'MsgDebriefAddLine', "", " None None "); + messageClient( %client, 'MsgGameOver', "" ); + return; + } else if ($Rank::numplayers >= 5) + { + %num = 5; + } else { + %num = $Rank::numplayers; + } + + for (%i = 1; %i <= %num; %i++) + { + FindTopRanks(); + messageClient( %client, 'MsgDebriefAddLine', "", " "@%i@". "@$Rank::Top[%i]@" "@$Rank::TopScore[%i]@" "); + messageClient( %client, 'MsgGameOver', "" ); + } + } +}; + +activatepackage(loadmodinfo); diff --git a/Scripts/mapGame.cs b/Scripts/mapGame.cs new file mode 100644 index 0000000..ff0c116 --- /dev/null +++ b/Scripts/mapGame.cs @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////////// +/// - MAP SUPPORT GAME PACKAGE - /////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// - By Founder, ZOD and TseTse - ///////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// - Version 2.0 - //////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +package mapGame +{ + function executeMapScripts() + { + echo("<>>>>> ACTIVATING MAP SUPPORT <<<<<>"); + echo("<>>>>> VERSION 2.0 <<<<<>"); + %path = "missions/mapscripts/*.cs"; + for(%file = findFirstFile( %path ); %file !$= ""; %file = findNextFile( %path )) + exec(%file); + + $MapScriptsLoaded = 1; + } + + function getRules() + { + // returns the currently executed folder names seperated by ; + $ModPaths = getModPaths(); + echo($ModPaths); + + // Example Usage (string, start, numChars) + //if(getSubStr($ModPaths, 0, 6) $= "base++") + // %doSomething = false; + + // $arg is the server startup option of "-mod blah" so this will return "blah" + echo($arg); + return $arg; + } + + function killMapPackage(%package) + { + deactivatePackage(%package); + } +}; + +function loadMapSupport() +{ + if($Host::AllowMapScript $= "" || $Host::AllowMapScript == 1) + { + $Host::AllowMapScript = 1; + if(!isActivePackage(mapSupportGame)) + activatePackage(mapGame); + + if(!$MapScriptsLoaded) + executeMapScripts(); + } +} + +loadMapSupport(); + +//////////////////////////////////////////////////////////////////////////////// diff --git a/Scripts/message.cs b/Scripts/message.cs new file mode 100644 index 0000000..5a16c71 --- /dev/null +++ b/Scripts/message.cs @@ -0,0 +1,586 @@ +$MaxMessageWavLength = 5200; +$doCCP = 0; + +function addMessageCallback(%msgType, %func) +{ + for(%i = 0; (%afunc = $MSGCB[%msgType, %i]) !$= ""; %i++) + { + // only add each callback once + if(%afunc $= %func) + return; + } + $MSGCB[%msgType, %i] = %func; +} + +function messagePump(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7 ,%a8, %a9, %a10) +{ + clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10); +} + +function clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10) +{ + %tag = getWord(%msgType, 0); + for(%i = 0; (%func = $MSGCB["", %i]) !$= ""; %i++) + call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10); + + if(%tag !$= "") + for(%i = 0; (%func = $MSGCB[%tag, %i]) !$= ""; %i++) + call(%func, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10); +} + +function defaultMessageCallback(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10) +{ + if ( %msgString $= "" ) + return; + + %message = detag( %msgString ); + // search for wav tag marker + %wavStart = strstr( %message, "~w" ); + if ( %wavStart != -1 ) + { + %wav = getSubStr( %message, %wavStart + 2, 1000 ); + %wavLengthMS = alxGetWaveLen( %wav ); + if ( %wavLengthMS <= $MaxMessageWavLength ) + { + %handle = alxCreateSource( AudioChat, %wav ); + alxPlay( %handle ); + } + else + error( "WAV file \"" @ %wav @ "\" is too long! **" ); + + %message = getSubStr( %message, 0, %wavStart ); + if ( %message !$= "" ) + addMessageHudLine( %message ); + } + else + addMessageHudLine( %message ); +} + +//-------------------------------------------------------------------------- +function handleClientJoin(%msgType, %msgString, %clientName, %clientId, %targetId, %isAI, %isAdmin, %isSuperAdmin, %isSmurf, %guid) +{ + logEcho("got client join: " @ detag(%clientName) @ " : " @ %clientId); + + //create the player list group, and add it to the ClientConnectionGroup... + if(!isObject("PlayerListGroup")) + { + %newGroup = new SimGroup("PlayerListGroup"); + ClientConnectionGroup.add(%newGroup); + } + + %player = new ScriptObject() + { + className = "PlayerRep"; + name = detag(%clientName); + guid = %guid; + clientId = %clientId; + targetId = %targetId; + teamId = 0; // start unassigned + score = 0; + ping = 0; + packetLoss = 0; + chatMuted = false; + canListen = false; + voiceEnabled = false; + isListening = false; + isBot = %isAI; + isAdmin = %isAdmin; + isSuperAdmin = %isSuperAdmin; + isSmurf = %isSmurf; + }; + PlayerListGroup.add(%player); + $PlayerList[%clientId] = %player; + + if ( !%isAI ) + getPlayerPrefs(%player); + lobbyUpdatePlayer( %clientId ); +} + +function handleClientDrop( %msgType, %msgString, %clientName, %clientId ) +{ + logEcho("got client drop: " @ detag(%clientName) @ " : " @ %clientId); + + %player = $PlayerList[%clientId]; + if( %player ) + { + %player.delete(); + $PlayerList[%clientId] = ""; + lobbyRemovePlayer( %clientId ); + } +} + +function handleClientJoinTeam( %msgType, %msgString, %clientName, %teamName, %clientId, %teamId ) +{ + %player = $PlayerList[%clientId]; + if( %player ) + { + %player.teamId = %teamId; + lobbyUpdatePlayer( %clientId ); + } +} + +function handleClientNameChanged( %msgType, %msgString, %oldName, %newName, %clientId ) +{ + %player = $PlayerList[%clientId]; + if( %player ) + { + %player.name = detag( %newName ); + lobbyUpdatePlayer( %clientId ); + } +} + +addMessageCallback("", defaultMessageCallback); +addMessageCallback('MsgClientJoin', handleClientJoin); +addMessageCallback('MsgClientDrop', handleClientDrop); +addMessageCallback('MsgClientJoinTeam', handleClientJoinTeam); +addMessageCallback('MsgClientNameChanged', handleClientNameChanged); + +//--------------------------------------------------------------------------- +// Client chat'n +//--------------------------------------------------------------------------- +function isClientChatMuted(%client) +{ + %player = $PlayerList[%client]; + if(%player) + return(%player.chatMuted ? true : false); + return(true); +} + +//--------------------------------------------------------------------------- +function clientCmdChatMessage(%sender, %voice, %pitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10) +{ + %message = detag( %msgString ); + %voice = detag( %voice ); + + echo(%message); + if($doCCP == 1) + clientCmdCenterPrint(%message,5); + + if ( ( %message $= "" ) || isClientChatMuted( %sender ) ) + return; + + // search for wav tag marker + %wavStart = strstr( %message, "~w" ); + if ( %wavStart == -1 ) + addMessageHudLine( %message ); + else + { + %wav = getSubStr(%message, %wavStart + 2, 1000); + if (%voice !$= "") + %wavFile = "voice/" @ %voice @ "/" @ %wav @ ".wav"; + else + %wavFile = %wav; + + //only play voice files that are < 5000ms in length + if (%pitch < 0.5 || %pitch > 2.0) + %pitch = 1.0; + %wavLengthMS = alxGetWaveLen(%wavFile) * %pitch; + if (%wavLengthMS < $MaxMessageWavLength ) + { + if ( $ClientChatHandle[%sender] != 0 ) + alxStop( $ClientChatHandle[%sender] ); + $ClientChatHandle[%sender] = alxCreateSource( AudioChat, %wavFile ); + + //pitch the handle + if (%pitch != 1.0) + alxSourcef($ClientChatHandle[%sender], "AL_PITCH", %pitch); + alxPlay( $ClientChatHandle[%sender] ); + } + else + error( "** WAV file \"" @ %wavFile @ "\" is too long! **" ); + + %message = getSubStr(%message, 0, %wavStart); + addMessageHudLine(%message); + } +} + +function sendMessage(%message){ + commandToServer('messageSent',"\c5"@%message); +} + +//--------------------------------------------------------------------------- +function clientCmdCannedChatMessage( %sender, %msgString, %name, %string, %keys, %voiceTag, %pitch ) +{ + %message = detag( %msgString ); + %voice = detag( %voiceTag ); + if ( $defaultVoiceBinds ) + clientCmdChatMessage( %sender, %voice, %pitch, "[" @ %keys @ "]" SPC %message ); + else + clientCmdChatMessage( %sender, %voice, %pitch, %message ); +} + +//--------------------------------------------------------------------------- +// silly spam protection... +$SPAM_PROTECTION_PERIOD = 10000; +$SPAM_MESSAGE_THRESHOLD = 6; +$SPAM_PENALTY_PERIOD = 10000; +$SPAM_MESSAGE = '\c3SPAM PROTECTION:\c0 You must wait another %1 seconds until you may speak again.'; + +function GameConnection::spamMessageTimeout(%this) +{ + if(%this.spamMessageCount > 0) + %this.spamMessageCount--; +} + +function GameConnection::spamReset(%this) +{ + %this.isSpamming = false; +} + +function spamAlert(%client) +{ + if($Host::FloodProtectionEnabled != true) + return(false); + + if(!%client.isSpamming && (%client.spamMessageCount >= $SPAM_MESSAGE_THRESHOLD)) + { + %client.spamProtectStart = getSimTime(); + %client.isSpamming = true; + %client.schedule($SPAM_PENALTY_PERIOD, spamReset); + } + + if(%client.isSpamming) + { + %wait = mFloor(($SPAM_PENALTY_PERIOD - (getSimTime() - %client.spamProtectStart)) / 1000); + messageClient(%client, "", $SPAM_MESSAGE, %wait); + return(true); + } + + %client.spamMessageCount++; + %client.schedule($SPAM_PROTECTION_PERIOD, spamMessageTimeout); + return(false); +} + +function chatMessageClient( %client, %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ) +{ + //see if the client has muted the sender + if ( !%client.muted[%sender] ) + commandToClient( %client, 'ChatMessage', %sender, %voiceTag, %voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ); +} + +function cannedChatMessageClient( %client, %sender, %msgString, %name, %string, %keys ) +{ + if ( !%client.muted[%sender] ) + commandToClient( %client, 'CannedChatMessage', %sender, %msgString, %name, %string, %keys, %sender.voiceTag, %sender.voicePitch ); +} + +function chatMessageTeam( %sender, %team, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ) +{ + if ( ( %msgString $= "" ) || spamAlert( %sender ) ) + return; + + if(%sender.isSilenced) + return; + + if(%sender.team == 0 && $Host::ObserversCannotChat) + { + messageClient(%sender, 'MsgNo', "\c2Observers cannot team chat."); + return; + } + + ACCMChatLog(%sender, %a2, 1); + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = ClientGroup.getObject( %i ); + if ( %obj.team == %sender.team ) + chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ); + } +} + +function cannedChatMessageTeam( %sender, %team, %msgString, %name, %string, %keys ) +{ + if ( ( %msgString $= "" ) || spamAlert( %sender ) ) + return; + + if(%sender.isSilenced) + return; + + if(%sender.team == 0) + { + messageClient(%sender, 'MsgNo', '\c2Observers cannot team chat.'); + return; + } + + ACCMChatLog(%sender, %string, 1); + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = ClientGroup.getObject( %i ); + if ( %obj.team == %sender.team ) + cannedChatMessageClient( %obj, %sender, %msgString, %name, %string, %keys ); + } +} + + +function chatMessageAll( %sender, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ) +{ + if(%sender.isSilenced) + return; + + %count = ClientGroup.getCount(); + if(getSubStr(%a2, 0, 1) $= "/") + { + chatcommands(%sender,%a2); + return; + } + + if(%sender.isRenaming) + { + if(getwordcount(%a2) != 1) + { + messageClient(%sender, "", "\c2Rename failed. The name must be one word."); + %sender.isRenaming = 0; + %sender.renameFile = ""; + return; + } + + if(strlwr(%a2) $= "cancel") + { + messageClient(%sender, "", "\c2Rename cancelled."); + %sender.isRenaming = 0; + %sender.renameFile = ""; + return; + } + + %fail = 0; + %tmp = new FileObject(); + %tmp2 = new FileObject(); + if(%tmp.openforread(%sender.renameFile) && %tmp2.openforwrite("Buildings/ClientSaves/"@%sender.guid@"/"@%a2)) + { + while(!%tmp.isEOF()) + { + %tmp2.writeline(%tmp.readline()); + } + } + else + { + messageClient(%sender, "", "\c2Rename failed. You specified an invalid file name."); + %fail = 1; + } + + %tmp.close(); + %tmp.delete(); + %tmp2.close(); + %tmp2.delete(); + + if(!%fail) + { + deleteFile(%sender.renameFile); + messageClient(%sender, "", "\c2Rename successful."); + } + + %sender.isRenaming = 0; + %sender.renameFile = ""; + return; + } + + if(strstr(%a2, "!") == 0) + { + %rest = getsubstr(%a2, 1, 255); + %target = plnametocid(getword(%rest, 0)); + %message = getsubstr(%rest, strlen(getword(%rest, 0)) + 1, 255); + if(!isObject(%target)) + { + messageClient(%sender, "", "\c2That is not an existing player."); + return; + } + + if(%message $= "") + { + messageClient(%sender, "", "\c2You must put in a message."); + return; + } + + if(%sender == %target) + { + messageClient(%sender, "", "\c2You can't message yourself!"); + return; + } + + messageClient(%sender, "", "\c1To "@%target.nameBase@": "@%message); + if(!%sender.echoed && !%target.muted[%sender]) { + messageClient(%target, "", "\c1From "@%sender.nameBase@": "@%message); + ACCMChatLog(%sender, %message, 2, %target); + } + return; + } + + +//============================================================================== +// Interactive Sentinel AI. By Blnukem. +//============================================================================== +// Sentinel Interaction: + + if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Follow me") != -1 || strstr(%a2, "follow me") != -1 + || strstr(%a2, "Come") != -1 || strstr(%a2, "come") != -1) + { + Call("ccFollow", %sender); + } + + if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Stop") != -1 || strstr(%a2, "stop") != -1 + || strstr(%a2, "Don't follow") != -1 || strstr(%a2, "Dont follow") != -1 || strstr(%a2, "don't follow") != -1 || strstr(%a2, "dont follow") != -1) + { + Call("ccStop", %sender); + } + + if ($Host::SentinelProtection == 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "Kill") == 0) + { + %rest = getsubstr(%a2, 1, 255); + %target = plnametocid(getword(%rest, 0)); + %message = getsubstr(%rest, strlen(getword(%rest, 0)) + 1, 255); + ccAttack(%sender, %message); + } + +//------------------------------------------------------------------------------ +// Monitor Interaction: + + %sentinelcount = SentinelGroup.GetCount(); + %zombiecount = ZombieGroup.GetCount(); + if ($Host::SentinelProtection !$= 1 || MonitorGroup.GetCount() != 1) + { + } + + if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() $= 1 && strstr(%a2, "spawn a sentinel") != -1 || strstr(%a2, "Spawn a sentinel") != -1 + || strstr(%a2, "Spawn a Sentinel") != -1 || strstr(%a2, "spawn another sentinel") != -1 || strstr(%a2, "Spawn another sentinel") != -1 || strstr(%a2, "Spawn another Sentinel") != -1) + { + Call("ccCreateSentinel", %sender); + } + + if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "How many sentinels") != -1 || strstr(%a2, "how many sentinels") != -1 || + strstr(%a2, "Display sentinel count") != -1 || strstr(%a2, "display sentinel count") != -1) + { + if (SentinelGroup.GetCount() == 0) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are no Sentinel units currently deployed."); + } else if (SentinelGroup.GetCount() == 1) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There is 1 Sentinel unit currently deployed."); + } else if (SentinelGroup.GetCount() >= 1) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are "@%sentinelcount@" Sentinel units currently deployed."); + } + } else if ($Host::SentinelProtection $= 1 && MonitorGroup.GetCount() == 1 && strstr(%a2, "How many zombies") != -1 || strstr(%a2, "how many zombies") != -1 || + strstr(%a2, "Display zombie count") != -1 || strstr(%a2, "display zombie count") != -1) + { + if (ZombieGroup.GetCount() == 0) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 No zombies have been detected by the Sentinel Network."); + } else if (ZombieGroup.GetCount() == 1) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There is 1 zombie currently detected by the Sentinel Network."); + } else if (ZombieGroup.GetCount() >= 1) + { + Schedule(1, 0, MessageAll, "Msg", "\c2Monitor ::\c0 There are "@%zombiecount@" zombies detected by the Sentinel Network."); + } + } + +//============================================================================== +// End of Interactive Sentinel AI. +//============================================================================== + + + if ( ( %msgString $= "" ) || spamAlert( %sender ) ) + return; + + if(%sender.echoed) + { + chatMessageClient( %sender, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ); + return; + } + + ACCMChatLog(%sender, %a2, 0); + for ( %i = 0; %i < %count; %i++ ) + { + %obj = ClientGroup.getObject( %i ); + chatMessageClient( %obj, %sender, %sender.voiceTag, %sender.voicePitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10 ); + } +} + +function cannedChatMessageAll( %sender, %msgString, %name, %string, %keys ) +{ + if ( ( %msgString $= "" ) || spamAlert( %sender ) ) + return; + + if(%sender.isSilenced) + return; + + ACCMChatLog(%sender, %string, 0); + %count = ClientGroup.getCount(); + for ( %i = 0; %i < %count; %i++ ) + cannedChatMessageClient( ClientGroup.getObject(%i), %sender, %msgString, %name, %string, %keys ); +} + +//--------------------------------------------------------------------------- +function messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + commandToClient(%client, 'ServerMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); +} + +function messageTeam(%team, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + %count = ClientGroup.getCount(); + for(%cl= 0; %cl < %count; %cl++) + { + %recipient = ClientGroup.getObject(%cl); + if(%recipient.team == %team) + messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} + +function messageTeamExcept(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + %team = %client.team; + %count = ClientGroup.getCount(); + for(%cl= 0; %cl < %count; %cl++) + { + %recipient = ClientGroup.getObject(%cl); + if((%recipient.team == %team) && (%recipient != %client)) + messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} + +function messageAll(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + %count = ClientGroup.getCount(); + for(%cl = 0; %cl < %count; %cl++) + { + %client = ClientGroup.getObject(%cl); + messageClient(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} + +function messageAllExcept(%client, %team, %msgtype, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13) +{ + //can exclude a client, a team or both. A -1 value in either field will ignore that exclusion, so + //messageAllExcept(-1, -1, $Mesblah, 'Blah!'); will message everyone (since there shouldn't be a client -1 or client on team -1). + %count = ClientGroup.getCount(); + for(%cl= 0; %cl < %count; %cl++) + { + %recipient = ClientGroup.getObject(%cl); + if((%recipient != %client) && (%recipient.team != %team)) + messageClient(%recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13); + } +} + +//--------------------------------------------------------------------------- +// functions to support repair messaging +//--------------------------------------------------------------------------- +function clientCmdTeamRepairMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6) +{ + if(!$pref::ignoreTeamRepairMessages) + clientCmdServerMessage(%msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6); +} + +function teamRepairMessage(%client, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6) +{ + %team = %client.team; + + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %recipient = ClientGroup.getObject(%cl); + if((%recipient.team == %team) && (%recipient != %client)) + commandToClient(%recipient, 'TeamRepairMessage', %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6); + } +} diff --git a/Scripts/pack.cs b/Scripts/pack.cs new file mode 100644 index 0000000..e81285c --- /dev/null +++ b/Scripts/pack.cs @@ -0,0 +1,131 @@ +//---------------------------------------------------------------------------- + +datablock EffectProfile(TurretPackActivateEffect) +{ + effectname = "packs/generic_deploy"; + minDistance = 2.5; + maxDistance = 2.5; +}; + +datablock AudioProfile(TurretPackActivateSound) +{ + filename = "fx/packs/turret_place.wav"; + description = AudioClose3D; + preload = true; + effect = TurretPackActivateEffect; +}; + + +//---------------------------------------------------------------------------- + +function Pack::onCollision(%data, %obj, %col) +{ + // Don't pick up a new pack if you have a satchel charge deployed: + if ( %col.thrownChargeId > 0 ) + return; + + ItemData::onCollision(%data, %obj, %col); +} + +function Pack::onUse(%data,%obj) +{ + if (%obj.getMountedImage($BackpackSlot) != %data.image.getId()) + %obj.mountImage(%data.image,$BackpackSlot); + else + { + // Toggle the image trigger. + %obj.setImageTrigger($BackpackSlot, + !%obj.getImageTrigger($BackpackSlot)); + } +} + +function Pack::onInventory(%data,%obj,%amount) +{ + //only do this for players + if(%obj.getClassName() !$= "Player") + return; + + // Auto-mount the packs on players + if((%oldPack = %obj.getMountedImage($BackpackSlot)) != 0) + %obj.setInventory(%oldPack.item, 0); + if (%amount && %obj.getDatablock().className $= Armor) + { + // if you picked up another pack after you placed a satchel charge but + // before you detonated it, delete the charge + if(%obj.thrownChargeId > 0) + { + %obj.thrownChargeId.delete(); + %obj.thrownChargeId = 0; + } + %obj.mountImage(%data.image,$BackpackSlot); + %obj.client.setBackpackHudItem(%data.getName(), 1); + } + if(%amount == 0 ) + { + if ( %data.getName() $= "SatchelCharge" ) + %obj.client.setBackpackHudItem( "SatchelUnarmed", 1 ); + else + %obj.client.setBackpackHudItem(%data.getName(), 0); + } + ItemData::onInventory(%data,%obj,%amount); +} + +//---------------------------------------------------------------------------- + +// --- Upgrade packs +exec("scripts/packs/ammopack.cs"); +exec("scripts/packs/repairpack.cs"); +exec("scripts/packs/jetbooster.cs"); +exec("scripts/packs/satchelCharge.cs"); +exec("scripts/packs/FlamerAmmopack.cs"); +exec("scripts/packs/parachutepack.cs"); + +exec("scripts/packs/largeInventory.cs"); +exec("scripts/packs/spine.cs"); +exec("scripts/packs/blastwall.cs"); +exec("scripts/packs/blastfloor.cs"); +exec("scripts/packs/blastwwall.cs"); +exec("scripts/packs/jumpad.cs"); +exec("scripts/packs/mspine.cs"); +exec("scripts/packs/treepack.cs"); +exec("scripts/packs/cratepack.cs"); +exec("scripts/packs/decorationpack.cs"); +exec("scripts/packs/logoprojectorpack.cs"); +exec("scripts/packs/lightpack.cs"); +exec("scripts/packs/ZSpawnpack.cs"); +exec("scripts/packs/SentinelPack.cs"); +exec("scripts/packs/forcefieldpack.cs"); +exec("scripts/packs/gravityfieldpack.cs"); +exec("scripts/packs/telepadpack.cs"); +exec("scripts/packs/turretpack.cs"); +exec("scripts/packs/sentryturretpack.cs"); +//exec("scripts/packs/discturret.cs"); +exec("scripts/packs/laserturret.cs"); +exec("scripts/packs/missilerackturret.cs"); +exec("scripts/packs/mediumSensor.cs"); +exec("scripts/packs/largeSensor.cs"); +exec("scripts/packs/tripwire.cs"); +exec("scripts/packs/vehiclepad.cs"); +exec("scripts/packs/vehiclerepairpad.cs"); +exec("scripts/packs/Effectpacks.cs"); +//exec("scripts/packs/CommandSatelite.cs"); +exec("scripts/packs/door.cs"); +exec("scripts/packs/ArtilleryLoadoutPack.cs"); +exec("scripts/packs/Medpack.cs"); +exec("scripts/packs/DeployableWaypoint.cs"); +exec("scripts/packs/SpawnPointPack.cs"); +exec("scripts/packs/waypointpack.cs"); +exec("scripts/packs/PurgeGenerator.cs"); + +// --- Turret barrel packs +exec("scripts/packs/aabarrelpack.cs"); +exec("scripts/packs/missilebarrelpack.cs"); +exec("scripts/packs/mortarbarrelpack.cs"); +exec("scripts/packs/plasmabarrelpack.cs"); +exec("scripts/packs/ELFbarrelpack.cs"); +exec("scripts/packs/artillerybarrelpack.cs"); + +// --- power +exec("scripts/packs/generator.cs"); +exec("scripts/packs/solarpanel.cs"); +exec("scripts/packs/switch.cs"); diff --git a/Scripts/player.cs b/Scripts/player.cs new file mode 100644 index 0000000..5d3afb2 --- /dev/null +++ b/Scripts/player.cs @@ -0,0 +1,5392 @@ +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +$InvincibleTime = 3; + +// 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"); +exec("scripts/TR2medium_male.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; + +//$Zombie::TurningSpeed = 100; +$Zombie::ForwardSpeed = 750; +$Zombie::FForwardSpeed = 1500; +$Zombie::LForwardSpeed = 4000; +$Zombie::DForwardSpeed = 1200; +$Zombie::RForwardSpeed = 1500; + +datablock SensorData(PlayerSensor) +{ + detects = false; +}; + +//---------------------------------------------------------------------------- + +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 = 1.5; //Was 0.0, original game shiped as 1.5 + gravityCoefficient = 0; + inheritedVelFactor = 0.2; + constantAcceleration = 0.0; + lifetimeMS = 100; //Was a watered down 100, Original game was 150 + 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"; +}; + +datablock ParticleData(SOLightPuff) +{ + 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.1; + sizes[1] = 0.2; +}; + +datablock ParticleEmitterData(SOLightPuffEmitter) +{ + ejectionPeriodMS = 1500; + periodVarianceMS = 0; + ejectionVelocity = 0.0; + velocityVariance = 0.0; + ejectionOffset = 0.0; + thetaMin = 5; + thetaMax = 20; + phiReferenceVel = 0; + phiVariance = 360; + overrideAdvances = false; + useEmitterColors = true; + particles = "SOLightPuff"; +}; + +//---------------------------------------------------------------------------- +// 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; +}; + +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.275; + maxdrag = 0.4; + density = 10; + maxDamage = 0.66; + maxEnergy = 60; + repairRate = 0.0033; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.256; + jetForce = 26.21 * 120; + underwaterJetForce = 26.21 * 50 * 1.5; + underwaterVertJetFactor = 1.5; + jetEnergyDrain = 0.8; + underwaterJetEnergyDrain = 0.0; + minJetEnergy = 1; + maxJetHorizontalPercentage = 0.8; + + runForce = 55.20 * 90; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 15; + maxBackwardSpeed = 13; + maxSideSpeed = 13; + + maxUnderwaterForwardSpeed = 11; + maxUnderwaterBackwardSpeed = 10; + maxUnderwaterSideSpeed = 10; + + + jumpForce = 8.3 * 90; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpDelay = 0; + + + recoverDelay = 9; + recoverRunForceScale = 1.2; + + minImpactSpeed = 50; + speedDamageScale = 0.015; + + 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 = 60; + jumpSurfaceAngle = 70; + + minJumpSpeed = 20; + maxJumpSpeed = 30; + + horizMaxSpeed = 68; + horizResistSpeed = 33; + horizResistFactor = 0.35; + maxJetForwardSpeed = 30; + + upMaxSpeed = 80; + upResistSpeed = 25; + upResistFactor = 0.3; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; + heatIncreasePerSec = 1.0 / 10.0; + + 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 = 25.0; + groundImpactShakeFreq = "4.0 4.0 4.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.8; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterLightSound; + + maxWeapons = 4; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 0; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 2; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 2; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 2; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 2; + max[Chaingun] = 0; + max[ChaingunAmmo] = 1000; + max[RepairGun] = 1; + max[RepairPadDeployable] = 1; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 1; + max[ShieldPack] = 0; + max[AmmoPack] = 1; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 1; + max[MissileBarrelPack] = 1; + max[AABarrelPack] = 1; + max[PlasmaBarrelPack] = 1; + max[ELFBarrelPack] = 1; + max[artillerybarrelpack] = 1; + max[InventoryDeployable] = 1; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 3; + max[ConcussionGrenade] = 5; + max[FlareGrenade] = 5; + max[TargetingLaser] = 1; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 2; + max[Beacon] = 3; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 1; + //Guns + max[ConstructionTool] = 1; + max[MergeTool] = 1; + max[EditingTool] = 1; + max[TextureTool] = 1; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[PBC] = 0; + max[PBCAmmo] = 0; + //Building parts + max[spineDeployable] = 1; + max[mspineDeployable] = 1; + max[wWallDeployable] = 1; + max[floorDeployable] = 1; + max[WallDeployable] = 1; + max[DoorDeployable] = 1; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 1; + max[DiscTurretDeployable] = 1; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 1; + max[CrateDeployable] = 1; + max[DecorationDeployable] = 1; + max[LogoProjectorDeployable] = 1; + max[LightDeployable] = 1; + max[TripwireDeployable] = 1; + max[TelePadPack] = 1; + max[TurretBasePack] = 1; + max[TurretSentryPack] = 1; + max[LargeInventoryDeployable] = 1; + max[GeneratorDeployable] = 1; + max[SolarPanelDeployable] = 1; + max[SwitchDeployable] = 1; + max[MediumSensorDeployable] = 1; + max[LargeSensorDeployable] = 1; + max[SpySatelliteDeployable] = 1; + max[artilleryWeaponPack] = 1; + max[spawnpointpack] = 1; + max[waypointDeployable] = 1; + //Misc + max[JumpadDeployable] = 1; + max[ForceFieldDeployable] = 1; + max[GravityFieldDeployable] = 1; + max[VehiclePadPack] = 1; + max[DronePadDeployable] = 1; + + 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"; +}; + +datablock PlayerData(MediumMaleHumanArmor) : MediumPlayerDamageProfile +{ + emap = true; + + className = Armor; + shapeFile = "medium_male.dts"; + cameraMaxDist = 3; + computeCRC = true; + + debrisShapeName = "debris_player.dts"; + debris = playerDebris; + + 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; + + // z0dd - ZOD, 10/06/02. Was missing these parameters. + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + aiAvoidThis = true; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + mass = 130; + drag = 0.3; + maxdrag = 0.5; + density = 10; + maxDamage = 1.1; + maxEnergy = 100; + repairRate = 0.0033; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.55; + jetForce = 12; + underwaterJetForce = 25.22 * 170 * 1.5; + underwaterVertJetFactor = 535.5; + jetEnergyDrain = 0; + underwaterJetEnergyDrain = 0.1; + minJetEnergy = 1; + maxJetHorizontalPercentage = 0.8; + + runForce = 46 * 130; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 12; + maxBackwardSpeed = 10; + maxSideSpeed = 10; + + maxUnderwaterForwardSpeed = 8.5; + maxUnderwaterBackwardSpeed = 7.5; + maxUnderwaterSideSpeed = 7.5; + + recoverDelay = 9; + recoverRunForceScale = 1.2; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 10.0; // takes 3.0 seconds of constant jet to get full heat sig. + + jumpForce = 8.3 * 130; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpSurfaceAngle = 75; + jumpDelay = 0; + + // Controls over slope of runnable/jumpable surfaces + runSurfaceAngle = 60; + jumpSurfaceAngle = 70; + + minJumpSpeed = 15; + maxJumpSpeed = 25; + + horizMaxSpeed = 60; + horizResistSpeed = 28; + horizResistFactor = 0.32; + maxJetForwardSpeed = 22; + + upMaxSpeed = 70; + upResistSpeed = 30; + upResistFactor = 0.23; + + minImpactSpeed = 30; + speedDamageScale = 0.015; + + jetSound = ""; + wetJetSound = ArmorWetJetSound; + + jetEmitter = ""; + jetEffect = ""; + + 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; + + 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 = 10.0; + groundImpactShakeFreq = "4.0 4.0 4.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.8; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterMediumSound; + + maxWeapons = 2; // 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] = 3; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 2; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 4; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 4; + max[Chaingun] = 0; + max[ChaingunAmmo] = 1500; + max[RepairGun] = 1; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 1; + max[ShieldPack] = 0; + max[AmmoPack] = 1; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 1; + max[InventoryDeployable] = 1; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 1; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 6; + max[FlareGrenade] = 4; + max[TargetingLaser] = 1; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 3; + max[flamerAmmoPack] = 1; + max[ParachutePack] = 1; + max[MedPack] = 1; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 30; + max[MGClip] = 5; + max[LSMG] = 1; + max[LSMGAmmo] = 60; + max[LSMGClip] = 10; + max[HRPChaingun] = 1; + max[RPGAmmo] = 1; + max[RPGItem] = 0; + max[snipergun] = 1; + max[snipergunAmmo] = 20; + max[Bazooka] = 1; + max[BazookaAmmo] = 3; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[MG42Clip] = 1; + max[flamer] = 1; + max[flamerAmmo] = 0; + max[AALauncher] = 1; + max[AALauncherAmmo] = 1; + max[KriegRifle] = 1; + max[KriegAmmo] = 10; + max[Rifleclip] = 3; + max[Shotgun] = 1; + max[M4] = 1; + max[M4Ammo] = 4; + max[ShotgunAmmo] = 8; + max[ShotgunClip] = 4; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[PBC] = 0; + max[PBCAmmo] = 0; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + max[artilleryWeaponPack] = 1; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + + 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"; +}; + +datablock PlayerData(HeavyMaleHumanArmor) : HeavyPlayerDamageProfile +{ + emap = true; + + className = Armor; + shapeFile = "heavy_male.dts"; + cameraMaxDist = 3; + computeCRC = true; + + debrisShapeName = "debris_player.dts"; + debris = playerDebris; + + 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; + + // z0dd - ZOD, 10/06/02. Was missing these parameters. + cameraDefaultFov = 90.0; + cameraMinFov = 5.0; + cameraMaxFov = 120.0; + + aiAvoidThis = true; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + mass = 200; + drag = 0.33; + maxdrag = 0.6; + density = 10; + maxDamage = 1.32; + maxEnergy = 110; + repairRate = 0.0033; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.55; + jetForce = 6250; + underwaterJetForce = 300.47 * 180 * 1.5; + underwaterVertJetFactor = 2.0; + jetEnergyDrain = 5.5; + underwaterJetEnergyDrain = 1.5; + minJetEnergy = 1; + maxJetHorizontalPercentage = 0.8; + + runForce = 40.25 * 180; + runEnergyDrain = 0; + minRunEnergy = 0; + maxForwardSpeed = 7; + maxBackwardSpeed = 5; + maxSideSpeed = 5; + + maxUnderwaterForwardSpeed = 4.5; + maxUnderwaterBackwardSpeed = 3; + maxUnderwaterSideSpeed = 3; + + recoverDelay = 9; + recoverRunForceScale = 1.2; + + jumpForce = 8.3 * 180; + jumpEnergyDrain = 0; + minJumpEnergy = 0; + jumpDelay = 0; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 6.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 4.0; // takes 3.0 seconds of constant jet to get full heat sig. + + // Controls over slope of runnable/jumpable surfaces + runSurfaceAngle = 60; + jumpSurfaceAngle = 70; + + minJumpSpeed = 20; + maxJumpSpeed = 30; + + horizMaxSpeed = 52; + horizResistSpeed = 23; + horizResistFactor = 0.29; + maxJetForwardSpeed = 16; + + upMaxSpeed = 60; + upResistSpeed = 35; + upResistFactor = 0.18; + + minImpactSpeed = 40; + speedDamageScale = 0.018; + + 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 = 10.0; + groundImpactShakeFreq = "4.0 4.0 4.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.8; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterHeavySound; + + maxWeapons = 3; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 0; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 2; + max[Mine] = 3; + max[Grenade] = 5; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 2; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 1; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 1; + max[InventoryDeployable] = 1; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 8; + max[FlareGrenade] = 5; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 3; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 0; + max[BoosterPack] = 1; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 1; + max[BazookaAmmo] = 5; + max[MG42] = 1; + max[MG42Ammo] = 200; + max[MG42Clip] = 5; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[NapalmAmmo] = 5; + max[AALauncher] = 1; + max[AALauncherAmmo] = 1; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 2; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 1; + max[RShotgunAmmo] = 25; + max[RShotgunClip] = 2; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[PBC] = 1; + max[PBCAmmo] = 4; + max[RailGun] = 1; + max[RailGunAmmo] = 10; + max[NapalmMortar] = 1; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 1; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 1; + max[SwitchDeployable] = 1; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + max[artilleryWeaponPack] = 1; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + + 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 = ""; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = HeavyBiodermFootprint; + decalOffset = 0.4; + + waterBreathSound = WaterBreathBiodermSound; +}; + + +datablock PlayerData(SpecOpsMaleHumanArmor) : 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.275; + maxdrag = 0.4; + density = 10; + maxDamage = 0.8; + maxEnergy = 60; + repairRate = 0.01; + energyPerDamagePoint = 75.0; // shield energy required to block one point of damage + + rechargeRate = 0.55; + jetForce = 12; + underwaterJetForce = 25.22 * 170 * 1.5; + underwaterVertJetFactor = 535.5; + jetEnergyDrain = 0; + underwaterJetEnergyDrain = 0.1; + minJetEnergy = 1; + maxJetHorizontalPercentage = 0.8; + + runForce = 60.20 * 90; + runEnergyDrain = 0.0; + minRunEnergy = 10; + maxForwardSpeed = 17; + maxBackwardSpeed = 14; + maxSideSpeed = 14; + + maxUnderwaterForwardSpeed = 15; + maxUnderwaterBackwardSpeed = 12; + maxUnderwaterSideSpeed = 12; + + jumpForce = 10.0 * 90; + jumpEnergyDrain = 0; + minJumpEnergy = 12; + jumpDelay = 0; + + recoverDelay = 9; + recoverRunForceScale = 1.2; + + minImpactSpeed = 40; + speedDamageScale = 0.015; + + jetSound = ""; + wetJetSound = ArmorJetSound; + jetEmitter = ""; + jetEffect = ""; + + 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 = SOLightPuffEmitter; + footPuffNumParts = 15; + footPuffRadius = 0.25; + + 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; + + horizMaxSpeed = 68; + horizResistSpeed = 33; + horizResistFactor = 0.35; + maxJetForwardSpeed = 30; + + upMaxSpeed = 80; + upResistSpeed = 25; + upResistFactor = 0.3; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 3.0; // takes 4 seconds to clear heat sig. + heatIncreasePerSec = 1.0 / 20.0; // takes 3.0 seconds of constant jet to get full heat sig. + + footstepSplashHeight = 0.35; + //Footstep Sounds + LFootShallowSound = LFootLightShallowSplashSound; + RFootShallowSound = RFootLightShallowSplashSound; + LFootWadingSound = LFootLightWadingSound; + RFootWadingSound = RFootLightWadingSound; + LFootBubblesSound = LFootLightBubblesSound; + RFootBubblesSound = RFootLightBubblesSound; + movingBubblesSound = ArmorMoveBubblesSound; + waterBreathSound = WaterBreathMaleSound; + + impactSoftSound = ImpactLightSoftSound; + impactHardSound = ImpactLightHardSound; + impactMetalSound = ImpactLightMetalSound; + impactSnowSound = ImpactLightSnowSound; + + impactWaterEasy = ImpactLightWaterEasySound; + impactWaterMedium = ImpactLightWaterMediumSound; + impactWaterHard = ImpactLightWaterHardSound; + + groundImpactMinSpeed = 10.0; + groundImpactShakeFreq = "4.0 4.0 4.0"; + groundImpactShakeAmp = "1.0 1.0 1.0"; + groundImpactShakeDuration = 0.8; + groundImpactShakeFalloff = 10.0; + + exitingWater = ExitingWaterLightSound; + + maxWeapons = 2; // 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] = 1; + max[Grenade] = 3; + max[SmokeGrenade] = 2; + max[BeaconSmokeGrenade] = 2; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 1; + max[ShieldPack] = 0; + max[AmmoPack] = 1; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 3; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 1; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 3; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 1; + max[MedPack] = 1; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 1; + max[RPChaingunAmmo] = 30; + max[MGClip] = 5; + max[LSMG] = 1; + max[LSMGAmmo] = 60; + max[LSMGClip] = 5; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[snipergun] = 1; + max[snipergunAmmo] = 20; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 1; + max[KriegAmmo] = 10; + max[Rifleclip] = 2; + max[Shotgun] = 1; + max[ShotgunAmmo] = 8; + max[ShotgunClip] = 2; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 1; + max[LMissileLauncherAmmo] = 1; + max[PBC] = 0; + max[PBCAmmo] = 0; + max[SRifleSG] = 1; + max[SRifleGL] = 1; + max[SRifleAmmo] = 30; + max[SRifleSGAmmo] = 4; + max[SRifleGLAmmo] = 1; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 1; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + max[artilleryWeaponPack] = 1; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; + + observeParameters = "0.5 4.5 4.5"; + shieldEffectScale = "0.7 0.7 1.0"; +}; + +datablock PlayerData(SpecOpsFemaleHumanArmor) : SpecOpsMaleHumanArmor +{ + shapeFile = "light_female.dts"; + waterBreathSound = WaterBreathFemaleSound; + jetEffect = HumanMediumArmorJetEffect; +}; + +datablock PlayerData(SpecOpsMaleBiodermArmor) : SpecOpsMaleHumanArmor +{ + shapeFile = "bioderm_light.dts"; + jetEmitter = BiodermArmorJetEmitter; + jetEffect = BiodermArmorJetEffect; + + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = LightBiodermFootprint; + decalOffset = 0.3; + + waterBreathSound = WaterBreathBiodermSound; +}; + + +// -------------------------------------------------------------------------- +// True Build Armors +// -------------------------------------------------------------------------- + +datablock PlayerData(PureMaleHumanArmor) : LightMaleHumanArmor +{ + jetForce = 26.21 * 120; // 26.21 * 90 + underwaterJetForce = 26.21 * 120 * 1.5; // 26.21 * 90 * 1.5 + underwaterVertJetFactor = 1.5; + + maxEnergy = 100; + jetEnergyDrain = 0; + underwaterJetEnergyDrain = 0.05; + rechargeRate = 0.55; + dustEmitter = LiftoffDustEmitter; + + minImpactSpeed = 450; + speedDamageScale = 0.0004; + + maxWeapons = 4; // Max number of different weapons the player can have + maxGrenades = 1; // Max number of different grenades the player can have + maxMines = 0; // Max number of different mines the player can have + + // Inventory restrictions + max[RepairKit] = 1; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 2; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 4; + max[Chaingun] = 0; + max[ChaingunAmmo] = 1500; + max[RepairPadDeployable] = 1; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 1; + max[ShieldPack] = 1; + max[AmmoPack] = 0; + max[SatchelCharge] = 1; + max[MortarBarrelPack] = 1; + max[MissileBarrelPack] = 1; + max[AABarrelPack] = 1; + max[PlasmaBarrelPack] = 1; + max[ELFBarrelPack] = 1; + max[artillerybarrelpack] = 1; + max[InventoryDeployable] = 1; + max[MotionSensorDeployable] = 1; + max[PulseSensorDeployable] = 1; + max[TurretOutdoorDeployable] = 1; + max[TurretIndoorDeployable] = 1; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 8; + max[TargetingLaser] = 1; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 2; + max[Beacon] = 5; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 1; + //Guns + max[ConstructionTool] = 1; + max[MergeTool] = 1; + max[EditingTool] = 1; + max[TextureTool] = 1; + max[hookertool] = 1; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[PBC] = 0; + max[PBCAmmo] = 0; + //Building parts + max[spineDeployable] = 1; + max[mspineDeployable] = 1; + max[wWallDeployable] = 1; + max[floorDeployable] = 1; + max[WallDeployable] = 1; + max[DoorDeployable] = 1; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 1; + max[DiscTurretDeployable] = 1; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 1; + max[CrateDeployable] = 1; + max[DecorationDeployable] = 1; + max[LogoProjectorDeployable] = 1; + max[LightDeployable] = 1; + max[ZSpawnDeployable] = 1; + max[TripwireDeployable] = 1; + max[waypointDeployable] = 1; + max[TelePadPack] = 1; + max[TurretBasePack] = 1; + max[TurretSentryPack] = 1; + max[LargeInventoryDeployable] = 1; + max[GeneratorDeployable] = 1; + max[SolarPanelDeployable] = 1; + max[SwitchDeployable] = 1; + max[MediumSensorDeployable] = 1; + max[LargeSensorDeployable] = 1; + max[SpySatelliteDeployable] = 1; + max[artilleryWeaponPack] = 1; + //Misc + max[JumpadDeployable] = 1; + max[ForceFieldDeployable] = 1; + max[GravityFieldDeployable] = 1; + max[spawnpointpack] = 1; + max[waypointDeployable] = 1; + + //Some small additions + //Note all that keeps us from full plugin compability is this code. + max[TractorGun] = 1; + max[VehiclePadPack] = 1; + max[DronePadDeployable] = 1; + max[mpm_beaconpack] = 1; + max[TurretMpm_Anti_Deployable] = 1; + max[EmitterDepPack] = 1; + max[AudioDepPack] = 1; + max[DispenserDepPack] = 1; + max[DetonationDepPack] = 1; + max[TransDepPack] = 1; + max[MpmFuelPack] = 1; + max[MpmAmmoPack] = 1; + + observeParameters = "0.5 4.5 4.5"; + shieldEffectScale = "0.7 0.7 1.0"; +}; + + +//---------------------------------------------------------------------------- +datablock PlayerData(PureFemaleHumanArmor) : PureMaleHumanArmor +{ + shapeFile = "light_female.dts"; + waterBreathSound = WaterBreathFemaleSound; + jetEffect = HumanMediumArmorJetEffect; +}; + +datablock PlayerData(PureMaleBiodermArmor) : PureMaleHumanArmor +{ + shapeFile = "bioderm_light.dts"; + jetEmitter = BiodermArmorJetEmitter; + jetEffect = BiodermArmorJetEffect; + + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = LightBiodermFootprint; + decalOffset = 0.3; + + waterBreathSound = WaterBreathBiodermSound; +}; + +// -------------------------------------------------------------------------- +// End True Build Armors +// -------------------------------------------------------------------------- + +// Zombie Armor - Edited by: Blnukem +datablock PlayerData(ZombieArmor) : LightMaleHumanArmor +{ + cmdCategory = "Clients"; + cmdIcon = CMDPlayerIcon; + cmdMiniIconName = "commander/MiniIcons/com_player_grey"; + + runForce = 80 * 90; + runEnergyDrain = 0.0; + minRunEnergy = 10; + maxForwardSpeed = 5; + maxBackwardSpeed = 7; + maxSideSpeed = 7; + + jetForce = 0; + jumpForce = 20 * 90; + + maxDamage = 2.8; + minImpactSpeed = 35; + shapeFile = "bioderm_medium.dts"; + jetEmitter = ""; + jetEffect = ""; + jetSound = ""; + dustEmitter = ""; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = LightBiodermFootprint; + decalOffset = 0.3; + + waterBreathSound = WaterBreathBiodermSound; + + damageScale[$DamageType::shotgun] = 3.0; + damageScale[$DamageType::bazooka] = 3.0; + damageScale[$DamageType::PBC] = 0.25; + + max[RepairKit] = 0; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 0; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 0; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 0; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; + +}; + +datablock PlayerData(FZombieArmor) : LightMaleBiodermArmor +{ + maxDamage = 1.0; + minImpactSpeed = 50; + speedDamageScale = 0.015; + + damageScale[$DamageType::shotgun] = 2.0; + damageScale[$DamageType::bazooka] = 3.0; + damageScale[$DamageType::PBC] = 0.25; + + max[RepairKit] = 0; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 0; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 0; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 0; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; +}; + +datablock AudioProfile(ZLordFootSound) +{ + filename = "fx/weapons/grenade_explode_UW.wav"; + description = AudioBomb3d; + preload = true; +}; + +datablock AudioProfile(HZLordFootSound) +{ + filename = "fx/weapons/SpinFusor_explode_UW.wav"; + description = AudioBomb3d; + preload = true; +}; + +// Lord Zombie +datablock PlayerData(LordZombieArmor) : HeavyMaleBiodermArmor +{ + shapefile = "TR2medium_male.dts"; + mass = 500; + maxDamage = 18.0; + minImpactSpeed = 50; + speedDamageScale = 0.015; + boundingBox = "2.9 2.9 4.8"; + + underwaterJetForce = 10; + + LFootSoftSound = ZLordFootSound; + RFootSoftSound = ZLordFootSound; + LFootHardSound = HZLordFootSound; + RFootHardSound = HZLordFootSound; + LFootMetalSound = ZLordFootSound; + RFootMetalSound = ZLordFootSound; + LFootSnowSound = ZLordFootSound; + RFootSnowSound = ZLordFootSound; + + damageScale[$DamageType::shotgun] = 1.5; + damageScale[$DamageType::bazooka] = 2.0; + + max[RepairKit] = 0; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPadDeployable] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 0; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 0; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[MedPack] = 0; + //Guns + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[PBC] = 0; + max[PBCAmmo] = 0; + //Building parts + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + //Turrets + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + //Largepacks + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + //Misc + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; +}; + +// Controlable ZLord Armor +datablock PlayerData(ControlLordZombieArmor) : LordZombieArmor +{ + runForce = 40.25 * 500; + jumpForce = 20.3 * 500; + + maxForwardSpeed = 25; + maxBackwardSpeed = 23; + maxSideSpeed = 23; + + maxUnderwaterForwardSpeed = 23; + maxUnderwaterBackwardSpeed = 23; + maxUnderwaterSideSpeed = 23; + + underwaterJetForce = 10; + + max[LordAcidGun] = 1; +}; + +datablock PlayerData(DemonZombieArmor) : LightMaleHumanArmor +{ + boundingBox = "1.63 1.63 2.6"; + maxDamage = 4.0; + minImpactSpeed = 35; + shapeFile = "bioderm_heavy.dts"; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = HeavyBiodermFootprint; + decalOffset = 0.4; + + waterBreathSound = WaterBreathBiodermSound; + + damageScale[$DamageType::shotgun] = 1.0; + damageScale[$DamageType::bazooka] = 1.0; + + max[RepairKit] = 0; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[RepairPad] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 0; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[MedPack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 0; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[EditingTool] = 0; + max[TextureTool] = 0; + max[LordAcidGun] = 0; + max[DZShot] = 0; + max[EditorTool] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; +}; + +datablock PlayerData(ControlDemonZombieArmor) : DemonZombieArmor +{ + boundingBox = "1.63 1.63 2.6"; + maxDamage = 4.0; + minImpactSpeed = 35; + shapeFile = "bioderm_heavy.dts"; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = HeavyBiodermFootprint; + decalOffset = 0.4; + + waterBreathSound = WaterBreathBiodermSound; + + maxForwardSpeed = 18; + maxBackwardSpeed = 16; + maxSideSpeed = 18; + + jetForce = 0; + jetEmitter = ""; + jetEffect = ""; + jetSound = ""; + dustEmitter = ""; + + jumpForce = 12 * 90; + rechargeRate = 0.627; + + max[DZShot] = 1; +}; + +datablock PlayerData(RapierZombieArmor) : FZombieArmor +{ + minImpactSpeed = 75; + maxDamage = 1.0; + + boundingBox = "2.0 2.0 1.2"; +}; + +datablock PlayerData(ControlRapierZombieArmor) : RapierZombieArmor +{ + jumpForce = 11.5 * 90; + + rechargeRate = 0.256; + jetForce = 35.26 * 120; + underwaterJetForce = 35.26 * 50 * 1.5; + underwaterVertJetFactor = 1.5; + jetEnergyDrain = 0; + underwaterJetEnergyDrain = 0; + minJetEnergy = 1; + maxJetHorizontalPercentage = 1; + + jetSound = ""; + wetJetSound = ""; + jetEmitter = ""; + jetEffect = ""; + + horizMaxSpeed = 68; + horizResistSpeed = 33; + horizResistFactor = 0.35; + maxJetForwardSpeed = 30; + + upMaxSpeed = 120; + upResistSpeed = 20; + upResistFactor = 0.27; + + // heat inc'ers and dec'ers + heatDecayPerSec = 1.0 / 4.0; + heatIncreasePerSec = 0; +}; + +datablock PlayerData(DemonMotherZombieArmor) : LightMaleHumanArmor +{ + boundingBox = "1.5 1.5 2.6"; + maxDamage = 9.0; + minImpactSpeed = 35; + shapeFile = "medium_female.dts"; + + debrisShapeName = "bio_player_debris.dts"; + + //Foot Prints + decalData = HeavyBiodermFootprint; + decalOffset = 0.4; + + waterBreathSound = WaterBreathBiodermSound; + + damageScale[$DamageType::shotgun] = 1.0; + damageScale[$DamageType::bazooka] = 1.0; + + max[RepairKit] = 0; + max[Mine] = 0; + max[Grenade] = 0; + max[SmokeGrenade] = 0; + max[BeaconSmokeGrenade] = 0; + max[Blaster] = 0; + max[Plasma] = 0; + max[PlasmaAmmo] = 0; + max[Disc] = 0; + max[DiscAmmo] = 0; + max[SniperRifle] = 0; + max[GrenadeLauncher] = 0; + max[GrenadeLauncherAmmo] = 0; + max[Mortar] = 0; + max[MortarAmmo] = 0; + max[MissileLauncher] = 0; + max[MissileLauncherAmmo] = 0; + max[Chaingun] = 0; + max[ChaingunAmmo] = 0; + max[RepairGun] = 0; + max[CloakingPack] = 0; + max[SensorJammerPack] = 0; + max[EnergyPack] = 0; + max[RepairPack] = 0; + max[ShieldPack] = 0; + max[AmmoPack] = 0; + max[SatchelCharge] = 0; + max[MortarBarrelPack] = 0; + max[MissileBarrelPack] = 0; + max[AABarrelPack] = 0; + max[PlasmaBarrelPack] = 0; + max[ELFBarrelPack] = 0; + max[artillerybarrelpack] = 0; + max[MedPack] = 0; + max[InventoryDeployable] = 0; + max[MotionSensorDeployable] = 0; + max[PulseSensorDeployable] = 0; + max[TurretOutdoorDeployable] = 0; + max[TurretIndoorDeployable] = 0; + max[FlashGrenade] = 0; + max[ConcussionGrenade] = 0; + max[FlareGrenade] = 0; + max[TargetingLaser] = 0; + max[ELFGun] = 0; + max[ShockLance] = 0; + max[CameraGrenade] = 0; + max[Beacon] = 0; + max[flamerAmmoPack] = 0; + max[ParachutePack] = 0; + max[ConstructionTool] = 0; + max[MergeTool] = 0; + max[NerfGun] = 0; + max[NerfBallLauncher] = 0; + max[NerfBallLauncherAmmo] = 0; + max[SuperChaingun] = 0; + max[SuperChaingunAmmo] = 0; + max[RPChaingun] = 0; + max[RPChaingunAmmo] = 0; + max[MGClip] = 0; + max[LSMG] = 0; + max[LSMGAmmo] = 0; + max[LSMGClip] = 0; + max[snipergun] = 0; + max[snipergunAmmo] = 0; + max[Bazooka] = 0; + max[BazookaAmmo] = 0; + max[nukeme] = 0; + max[nukemeAmmo] = 0; + max[MG42] = 0; + max[MG42Ammo] = 0; + max[flamer] = 0; + max[flamerAmmo] = 0; + max[AALauncher] = 0; + max[AALauncherAmmo] = 0; + max[melee] = 0; + max[SOmelee] = 0; + max[KriegRifle] = 0; + max[KriegAmmo] = 0; + max[Rifleclip] = 0; + max[Shotgun] = 0; + max[ShotgunAmmo] = 0; + max[ShotgunClip] = 0; + max[RShotgun] = 0; + max[RShotgunAmmo] = 0; + max[RShotgunClip] = 0; + max[LMissileLauncher] = 0; + max[LMissileLauncherAmmo] = 0; + max[HRPChaingun] = 0; + max[RPGAmmo] = 0; + max[RPGItem] = 0; + max[spineDeployable] = 0; + max[mspineDeployable] = 0; + max[wWallDeployable] = 0; + max[floorDeployable] = 0; + max[WallDeployable] = 0; + max[DoorDeployable] = 0; + max[TurretLaserDeployable] = 0; + max[TurretMissileRackDeployable]= 0; + max[DiscTurretDeployable] = 0; + max[EnergizerDeployable] = 0; + max[TreeDeployable] = 0; + max[CrateDeployable] = 0; + max[DecorationDeployable] = 0; + max[LogoProjectorDeployable] = 0; + max[LightDeployable] = 0; + max[TripwireDeployable] = 0; + max[TelePadPack] = 0; + max[TurretBasePack] = 0; + max[TurretSentryPack] = 0; + max[LargeInventoryDeployable] = 0; + max[GeneratorDeployable] = 0; + max[SolarPanelDeployable] = 0; + max[SwitchDeployable] = 0; + max[MediumSensorDeployable] = 0; + max[LargeSensorDeployable] = 0; + max[SpySatelliteDeployable] = 0; + max[JumpadDeployable] = 0; + max[ForceFieldDeployable] = 0; + max[GravityFieldDeployable] = 0; + max[VehiclePadPack] = 0; + max[DronePadDeployable] = 0; +}; + + +//---------------------------------------------------------------------------- + +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) +{ + Cancel(%obj.ParaLoop); + if (%obj.client.player == %obj) + %obj.client.player = 0; + + // Nukem - Shitty method, but it it works, why change it? + if(%obj.getdatablock().getname() $= "ZombieArmor" + || %obj.getdatablock().getname() $= "FZombieArmor" + || %obj.getdatablock().getname() $= "LordZombieArmor" + || %obj.getdatablock().getname() $= "DemonZombieArmor" + || %obj.getdatablock().getname() $= "RapierZombieArmor") + RemoveFromZombieGroup(%obj); +} + +function Armor::onNewDataBlock(%this,%obj) +{ +} + +function Armor::onDisabled(%this,%obj,%state) +{ + %obj.revived = 0; + if(%obj.Infected == 1){ + schedule(14000, %obj, "CreateZombie", %obj); + %obj.schedule(15000, "delete"); + } + else + { + %fadeTime = 1000; + %obj.revcheck = schedule(($CorpseTimeoutValue) - %fadeTime, %obj, "checkIfRevived", %obj); + } + if(%obj.getdatablock().getname() $= "ZombieArmor" || %obj.getdatablock().getname() $= "FZombieArmor" || %obj.getdatablock().getname() $= "LordZombieArmor" || %obj.getdatablock().getname() $= "DemonZombieArmor" || %obj.getdatablock().getname() $= "ControlDemonZombieArmor" || %obj.getdatablock().getname() $= "RapierZombieArmor" || %obj.getdatablock().getname() $= "ControlRapierZombieArmor" || %obj.getdatablock().getname() $= "DemonMotherZombieArmor"){ + %dodeath = (getrandom() * 3); + if(%dodeath <= 1) + %obj.setActionThread("Death" @ $PlayerDeathAnim::HeadFrontDirect); + else if(%dodeath <=2) + %obj.setActionThread("Death" @ $PlayerDeathAnim::HeadBackFallForward); + else + %obj.setActionThread("Death" @ $PlayerDeathAnim::TorsoBackFallForward); + if(%obj.hasCP == 1){ + if(isObject(%obj.cp)) + %obj.CP.numZ = (%obj.CP.numZ - 1); + } + if(%obj.randspawnerstarted == 1) + $numspawnedzombies--; + } +} + +function checkIfRevived(%obj){ + if(%obj.revived != 1){ + %fadeTime = 1000; + %obj.startFade( %fadeTime, 1, true ); + %obj.schedule(%fadeTime, "delete"); + } + else + %obj.revived = 0; +} + +function Armor::shouldApplyImpulse(%data, %obj) +{ + return true; +} + +$wasFirstPerson = true; + +function Armor::onMount(%this,%obj,%vehicle,%node) +{ +//[most] +if (%vehicle.base.leftpad == %vehicle || %vehicle.base.rightpad == %vehicle) + { + %node = 1; + %obj.mvehicle = %vehicle; + } +//[most] + + 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); + + if(!%obj.inStation) + %obj.lastWeapon = (%obj.getMountedImage($WeaponSlot) == 0 ) ? "" : %obj.getMountedImage($WeaponSlot).getName().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(240000, %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, 10/06/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 ) +{ + %obj.mountedtoV = 0; + %obj.Vmountedto = ""; + if ( %node == 0 ) + { + commandToClient( %obj.client, 'VehicleDismount' ); + commandToClient(%obj.client, 'removeReticle'); + + if(%obj.inv[%obj.lastWeapon]) + %obj.use(%obj.lastWeapon); + + 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, 10/06/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"; +$ammoType[6] = "TR2DiscAmmo"; +$ammoType[7] = "TR2GrenadeLauncherAmmo"; +$ammoType[8] = "TR2ChaingunAmmo"; +$ammoType[9] = "TR2MortarAmmo"; +$ammoType[10] = "NerfBallLauncherAmmo"; +$ammoType[11] = "SuperChaingunAmmo"; +$ammoType[12] = "snipergunAmmo"; +$ammoType[13] = "BazookaAmmo"; +$ammoType[14] = "nukemeAmmo"; +$ammoType[15] = "MG42Ammo"; +$ammoType[16] = "flamerammo"; +$ammoType[17] = "AALauncherAmmo"; +$ammoType[18] = "Rifleclip"; +$ammoType[19] = "MGclip"; +$ammoType[20] = "Shotgunclip"; +$ammoType[21] = "RShotgunclip"; +$ammoType[22] = "LMissileLauncherAmmo"; +$ammoType[23] = "RPGAmmo"; +$ammoType[24] = "LSMGclip"; +$ammoType[25] = "PBCAmmo"; +$ammoType[26] = "SRifleSGAmmo"; +$ammoType[27] = "SRifleGLAmmo"; + +function Armor::onCollision(%this,%obj,%col,%forceVehicleNode) +{ + if (%obj.getState() $= "Dead") + return; + + %dataBlock = %col.getDataBlock(); + %colarmortype = %Datablock.getname(); + %className = %dataBlock.className; + %objarmortype = %obj.getdatablock().getname(); + %client = %obj.client; + // player collided with a vehicle + %node = -1; + if ((%objarmortype $= "ZombieArmor" || %objarmortype $= "FZombieArmor" || %objarmortype $= "LordZombieArmor " || %objarmortype $= "ControlLordZombieArmor") && %classname $= "WheeledVehicleData"){ + if(%colarmortype $= "VehicleTestCrate1"){ + %spd = %obj.getvelocity(); + %vec = %obj.getEyeVector(); + %vector = vectorScale(vectorNormalize(%vec),(1000 + (vectorlen(%spd)*90))); + %x = getword(%vector,0); + %y = getword(%vector,1); + %upvec = 500; + %vector = %x@" "@%y@" "@%upvec; + %col.applyimpulse(%col.getposition(), %Vector); + } + } + 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 (isObject(%obj.client) && %obj.client.isAIControlled()) // Eolk - console spam fix. + { + %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") + { + //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) + { + %obj.mountedtoV = 1; + %obj.Vmountedto = %col; + // 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).getName().item; + else + %col.lastWeapon = %obj.lastWeapon; + + %obj.preVehicleMountPos = %obj.getPosition(); + + %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 $= "energizer") + { + // only detonate on contact in non-purebuild mode + if ($Host::Purebuild == 0) { + %col.damage(%col, 0, 10, $DamageType::Ground); + %obj.damage(%col, 0, 10, $DamageType::Ground); + } + } + // TODO - keep these up to date + else if (%className $= "wall" || %className $= "wwall" || %className $= "spine" || %className $= "mspine" || %className $= "floor") { + %obj.playAudio(0,(GetRandom()*2 >1) ? LFootMediumMetalSound : RFootMediumMetalSound); + %obj.lastDepCol = %col; + %obj.lastDepColPos = %obj.getPosition(); + if (%className $= "wall") + doorfunction(%obj,%col); + } + else if (%className $= "Armor") { + // player has collided with another player + if(%objarmortype $= "ZombieArmor" || %objarmortype $= "FZombieArmor" || %objarmortype $= "LordZombieArmor" || %objarmortype $= "DemonZombieArmor" || %objarmortype $= "ControlDemonZombieArmor" || %objarmortype $= "RapierZombieArmor" || %objarmortype $= "ControlRapierZombieArmor" || %objarmortype $= "ControlLordZombieArmor" || %objarmortype $= "DemonMotherZombieArmor") + %objiszomb = 1; + else + %objiszomb = 0; + if(%col.onfire == 1 && %obj.onfire != 1){ + %obj.maxfirecount = (%col.maxfirecount - %col.firecount); + %obj.onfire = 1; + schedule(10, %obj, "burnloop", %obj); + } + if(%col.getState() $= "Dead") { + %obj.lasttouchedcorpse = %col; + if(%objarmortype !$= "ZombieArmor" || %objarmortype !$= "FZombieArmor"){ + %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)); + } + } + } + } + else if((%colarmortype $= "ZombieArmor" || %col.canZkill == 1) && %obj.Infected != 1 && %objiszomb != 1){ + %Vector = vectorscale(%col.getvelocity(), 100); + %obj.applyimpulse(%obj.getposition(), %Vector); + %obj.Infected = 1; + %obj.InfectedLoop = schedule(10, %obj, "InfectLoop", %obj); + %obj.damage(0, %obj.position, 0.2, $DamageType::Zombie); + } + else if(%colarmortype $= "FZombieArmor" && %obj.Infected != 1 && %objiszomb != 1){ + if(%obj.hit == ""){ + %obj.hit = 1; + %obj.setWhiteOut("0.5"); + playPain(%obj); + } + else if(%obj.hit == 1){ + %obj.Infected = 1; + %obj.InfectedLoop = schedule(10, %obj, "InfectLoop", %obj); + } + %Vector = vectorscale(%col.getvelocity(), 100); + %obj.applyimpulse(%obj.getposition(), %Vector); + %obj.damage(0, %obj.position, 0.2, $DamageType::Zombie); + } + else if((%colarmortype $= "DemonZombieArmor" || %colarmortype $= "ControlDemonZombieArmor") && %obj.Infected != 1 && %objiszomb != 1){ + %Vector = vectorscale(%col.getvelocity(), 100); + %obj.applyimpulse(%obj.getposition(), %Vector); + %obj.Infected = 1; + %obj.InfectedLoop = schedule(10, %obj, "InfectLoop", %obj); + %obj.damage(0, %obj.position, 0.4, $DamageType::Zombie); + } + else if((%colarmortype $= "RapierZombieArmor" || %colarmortype $= "ControlRapierZombieArmor") && %obj.grabbed != 1 && %objiszomb != 1){ + %chance = getRandom(1,3); + if(%chance == 3 && %obj.Infected != 1){ + %obj.damage(0, %obj.position, 0.4, $DamageType::Zombie); + %obj.Infected = 1; + %obj.InfectedLoop = schedule(10, %obj, "InfectLoop", %obj); + } else { + %col.iscarrying = 1; + %obj.grabbed = 1; + %obj.damage(0, %obj.position, 0.2, $DamageType::Zombie); + %col.killingPlayer = schedule(10, 0, "RkillLoop", %col, %obj, 0); + } + } + else if((%colarmortype $= "ZombieArmor" || %col.canZkill == 1) && %objiszomb != 1) + %obj.damage(0, %obj.position, 0.2, $DamageType::Zombie); + else if(%colarmortype $= "FZombieArmor" && %objiszomb != 1) + %obj.damage(0, %obj.position, 0.4, $DamageType::Zombie); + else if((%colarmortype $= "DemonZombieArmor" || %colarmortype $= "ControlDemonZombieArmor") && %objiszomb != 1) + %obj.damage(0, %obj.position, 0.4, $DamageType::Zombie); + else if(%colarmortype $= "DemonMotherZombieArmor" && %objiszomb != 1){ + if(%obj.Infected != 1){ + %obj.Infected = 1; + %obj.damage(0, %obj.position, 0.8, $DamageType::Zombie); + %obj.InfectedLoop = schedule(10, %obj, "InfectLoop", %obj); + } else + %obj.damage(0, %obj.position, 1.2, $DamageType::Zombie); + } + else if ($PlayerSnapTo == true) { + if (!isObject(%col.getMountedObject(0))) { + if (!isObject(%obj.getMountedObject(0)) || %obj.getMountedObject(0) != %col) { + %col.mountObject(%obj,$BackPackSlot); + %gotSomething = true; + } + } + } + else if ($NaughtyMode == true) { + playPain(%obj); + playPain(%col); + } + else if ($AngstMode == true) { + playDeathCry(%obj); + playDeathCry(%col); + } + if ($JumpyMode == true) { + %obj.applyImpulse(%obj.getPosition(),"0 0 500"); + %col.applyImpulse(%col.getPosition(),"0 0 500"); + } + if (%obj.client.race $= "Human" && $SexChangeMode == true) { + if (%obj.client.oldSex $= "") + %obj.client.oldSex = %obj.client.sex; + if (%obj.client.oldVoice $= "") + %obj.client.oldVoice = %obj.client.voice; + %voice = getTaggedString(getTargetVoice(%obj.client.target)); + %voiceNum = getSubStr(%voice,strLen(%voice)-1,1); + if (%obj.client.sex $= "Male") { + %obj.client.sex = "Female"; + %obj.client.voiceTag = addTaggedString("Fem" @ %voiceNum); + setTargetVoice(%obj.client.target,%obj.client.voiceTag); + } + else { + %obj.client.sex = "Male"; + %obj.client.voiceTag = addTaggedString("Male" @ %voiceNum); + setTargetVoice(%obj.client.target,%obj.client.voiceTag); + } + %obj.setArmor(%client.armor); + } + if(%gotSomething) + %col.playAudio(0, CorpseLootingSound); + } +} + +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; + case "SuperHAPCFlyer": + // 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,%eject) { + // This function is called by player.cc when the jump trigger + // is true while mounted + if (!%obj.isMounted()) + return; + //[most] emp hook + if (%obj.isemped) + return; + //[most] + + %obj.mountedtoV = 0; + %obj.Vmountedto = ""; + + %vehicle = %obj.getObjectMount(); + %vehicleData = %vehicle.getDataBlock(); + + if(isObject(%obj.getObjectMount().shield)) + %obj.getObjectMount().shield.delete(); + + commandToClient(%obj.client,'SetDefaultVehicleKeys', false); + + if (%Eject) + %push = 30; + else + %push = 5; + + + // 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], 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(4000, "setMountVehicle", true); + + // Position above dismount point + %obj.setTransform(%pos); + %obj.playAudio(0, UnmountVehicleSound); + %obj.applyImpulse(%pos, VectorScale(%impulseVec, %obj.getDataBlock().mass * 3 * %push)); + %obj.setPilot(false); + %obj.vehicleTurret = ""; +} + +function resetObserveFollow( %client, %dismount ) +{ + if( %dismount ) + { + if( !isObject( %client.player ) ) + return; + + for( %i = 0; %i < %client.observeCount; %i++ ) + { + if (isObject(%client.observers[%i].camera)) + %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++ ) + { + if (isObject(%client.observers[%i].camera)) + %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" + || ((%targetObject.client.isJailed || %targetObject.client.permInvincible) && !%targetObject.scriptKilled)) + return; + + + //---------------------------------------------------------------- + // z0dd - ZOD, 6/09/02. Check to see if this vehcile 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; + %armortype = %targetobject.getdatablock().getname(); + if (%damageType == $DamageType::ZAcid && %armortype !$= "ZombieArmor" && %armortype !$= "FZombieArmor" && %armortype !$= "ControlFZombieArmor" && %armortype !$= "LordZombieArmor" && %armortype !$= "ControlLordZombieArmor" && %armortype !$= "DemonZombieArmor" && %armortype !$= "ControlDemonZombieArmor" && %armortype !$= "DemonMotherZombieArmor" && %armortype !$= "RapierZombieArmor" && %armortype !$= "ControlRapierZombieArmor" && %targetobject.infected != 1) { + %targetObject.Infected = 1; + %targetObject.InfectedLoop = schedule(10, %targetObject, "InfectLoop", %targetObject); + } + 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]) + { + %mount.getDataBlock().damageObject(%mount, %sourceObject, %position, %amount, %damageType); + 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, 5/8/02. Check to see if this turret has a valid owner, if not clear the owner varible. + //else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret") + // %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + else if(isObject(%sourceObject) && %sourceObject.getClassName() $= "Turret") + { + %sourceTeam = getTargetSensorGroup(%sourceObject.getTarget()); + if(%sourceObject.getOwner() !$="" && (%sourceObject.getOwner().team != %sourceObject.team || !isObject(%sourceObject.getOwner()))) + %sourceObject.setOwner(); + } + // End z0dd - ZOD + //-------------------------------------------------------------------------------------------------------------------- + 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; + + if (%damageType == $DamageType::Plasma){ + if(%targetObject.maxfirecount $= "") + %targetObject.maxfirecount = 0; + %targetObject.maxfirecount = %targetObject.maxfirecount + (30 * (%amount / 0.2)); + if(%targetObject.onfire == 0 || %targetObject.onfire $= ""){ + %targetObject.onfire = 1; + schedule(10, %targetObject, "burnloop", %targetObject); + } + } + + // 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); + + if ($Host::InvincibleArmors == 1 && !%targetObject.scriptKilled) { + if ((%sourceObject.team != %targetObject.team) || Game.numTeams == 1) { + %wp = new WayPoint() { + position = %targetObject.getWorldBoxCenter(); + dataBlock = "WayPointMarker"; + team = (%sourceObject.team == %targetObject.team) ? 0 : %sourceObject.team; + name = mFloatLength((100 / %data.maxDamage) * %amount,0) @ "%"; + }; + MissionCleanup.add(%wp); + %wp.schedule(1500,delete); + } + } + else + %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") + { + if(%targetObject.overwatcher > 0) + if((%temporary = findword(%targetObject.overwatcher.zombieList, %targetObject)) !$= "") + %targetObject.overwatcher.zombieList = listDel(%targetObject.overwatcher.zombieList, %temporary); + + if(isObject(%targetObject.beacon)) + %targetObject.beacon.schedule(50, delete); + // 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::Bazooka || + %damageType == $DamageType::Laser || + %damageType == $DamageType::Artillery || + %damageType == $DamageType::SuperChaingun || + %damageType == $DamageType::ZombieL || + %damageType == $DamageType::OutdoorDepTurret || + %damageType == $DamageType::Missile ) + { + if( %previousDamage >= 0.35 ) // only if <= 35 percent damage remaining + { + %targetObject.setMomentumVector(%momVec); + %targetObject.blowup(); + %targetObject.kibbled = 1; + } + } + + // 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); +// if (%collidedObject & $TypeMasks::PlayerObjectType) { +// if (%collidedObject.getState() !$= "Dead") { +// %data.damageObject(%collidedObject, 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 ) + { + // Eolk - why do all the complicated stuff when you can just do this? + %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 ) +{ + if(%obj.grabbed == 1) + return; + %client = %obj.client; + %random = getRandom(1) + 1; + %desc = AudioClosest3d; + + playTargetAudio( %client.target, $DeathCry[%random], %desc, false ); +} + +function playPain( %obj ) +{ + if(%obj.grabbed == 1) + return; + %client = %obj.client; + if(%client $= "") + return; + %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 if (getSubStr(%dataBlock, 0, 5) $= "SpecOps") + return "SpecOps"; + else if (getSubStr(%dataBlock, 0, 4) $= "Pure") + return "Pure"; + else if (getSubStr(%dataBlock, 0, 8) $= "TR2Light") + return "TR2Light"; + else if (getSubStr(%dataBlock, 0, 9) $= "TR2Medium") + return "TR2Medium"; + else if (getSubStr(%dataBlock, 0, 8) $= "TR2Heavy") + return "TR2Heavy"; + else + return "Unknown"; +} + +function Player::pickup(%this,%obj,%amount) +{ + if (%this.client.isJailed) + return 0; + %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() ) + { + %vehicle = %this.getObjectMount(); + if (%vehicle.getDataBlock().getName() $= "MobileBaseVehicle" && $Host::Purebuild == 1) { + %vehicle.applyImpulse(%vehicle.getPosition(),"0 0 8000"); + return( false ); + } + //[most] + else if (%vehicle.base.leftpad == %vehicle) + { + PressButton(%vehicle,%this,0,0); + } + else if (%vehicle.base.rightpad == %vehicle) + { + PressButton(%vehicle,%this,1,0); + } + //[most] + else { + 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 (isObject(%data) && %data.getName() !$= "RepairKit" ) + return false; + // Convert the word "Backpack" to whatever is in the backpack slot. + //[most] + if (isObject(%data) && %data.getName() $= "RepairKit" ) + { + if ( %this.isPilot() ) + { + %vehicle = %this.getObjectMount(); + if (%vehicle.base.leftpad == %vehicle) + { + PressButton(%vehicle,%this,0,1); + } + else if (%vehicle.base.rightpad == %vehicle) + { + PressButton(%vehicle,%this,1,1); + } + + } + } + //[most] + + return ShapeBase::use( %this, %data ); +} + +function Player::maxInventory(%this,%data) +{ + %max = ShapeBase::maxInventory(%this,%data); + if (%this.getInventory(AmmoPack)) + %max += AmmoPack.max[%data.getName()]; + else if (%this.getInventory(flamerAmmoPack)) + %max += flamerAmmoPack.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) +{ + switch(%type) + { + case 0: + //Water + if(Game.class $= "ConstructionGame") { + return; + } + if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } else if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } + case 1: + //Ocean Water + if(Game.class $= "ConstructionGame") { + return; + } + if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } else if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } + case 2: + //River Water + if(Game.class $= "ConstructionGame") { + return; + } + if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } else if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } + case 3: + //Stagnant Water + if(Game.class $= "ConstructionGame") { + return; + } + if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } else if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + %obj.Drowning = 1; + } + 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 + Cancel(%obj.DrownLoop); + Cancel(%obj.CheckDLoop); + %obj.Drowning = 0; + case 1: + //Ocean Water + Cancel(%obj.DrownLoop); + Cancel(%obj.CheckDLoop); + %obj.Drowning = 0; + case 2: + //River Water + Cancel(%obj.DrownLoop); + Cancel(%obj.CheckDLoop); + %obj.Drowning = 0; + case 3: + //Stagnant Water + Cancel(%obj.DrownLoop); + Cancel(%obj.CheckDLoop); + %obj.Drowning = 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 DrowningLoop(%obj) +{ + if(isObject(%obj)) + { + %obj.damage(0, %obj.getPosition(), 0.05, $DamageType::Drown); + %obj.DrownLoop = schedule(250, 0, "DrowningLoop", %obj); + } +} + +function checkforwater(%obj){ + if(isObject(%obj)){ + %eyeVec = %obj.getEyeVector(); + %eyeTrans = %obj.getEyeTransform(); + %eyePos = posFromTransform(%eyeTrans); + %vector = vectorAdd("0 0 -4", %eyepos); + %searchresult = containerRayCast(%eyePos, %vector, $TypeMasks::WaterObjectType); + if(%searchresult){ + if(%obj.Drowning == 1){ + Cancel(%obj.DrownLoop); + %obj.Drowning = 0; + } + } + else if(%obj.Drowning == 0){ + if (!( %obj.isPilot() || %obj.isWeaponOperator() )){ + %obj.DrownLoop = schedule(15000, 0, "DrowningLoop", %obj); + %obj.Drowning = 1; + } + } + %obj.CheckDLoop = schedule(500, 0, "checkforwater", %obj); + } +} + +function Armor::onTrigger(%data, %player, %triggerNum, %val) +{ + if (%triggerNum == 4) + { + // Eolk - cleaned this area up a tad. Made it more "readable." + %currentWeap = %player.getMountedImage($weaponSlot).getName().item; + if (%currentWeap $= HRPChaingun) + { + if ( !(%val == 1) ) + %player.setImageTrigger(5, false); + + if (%player.inv[RPGAmmo] > 0) + { + if (%val == 1) + %player.setImageTrigger(5, true); + else + %player.setImageTrigger(5, false); + } + else + { + // 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 + { + // 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) + { + if(%player.getMountedImage($weaponslot)) + { + if(%player.getMountedImage($WeaponSlot).getName() $= "MedPackGunImage" && %val == 1) + { + if(%player.reviveMode == 1) + checkrevive(%player); + else + checkcure(%player); + } + // Eolk - Removed SRifle stuff. Was irrelevant now that the weapon is gone. + // Eolk - put it back, we might be using it soon... + else if (%player.getMountedImage($WeaponSlot).getName() $= "SRifleImage") + { + if ( !(%val == 1) ) + %player.setImageTrigger(7, false); + if (%player.inv[SRifleSGAmmo] > 0 || %player.inv[SRifleGLAmmo] > 0) + { + if (%val == 1) + %player.setImageTrigger(7, true); + else + %player.setImageTrigger(7, false); + } + } + } + else + { + 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); +} + +datablock TargetProjectileData(FlashLightTargeter) +{ + directDamage = 0.0; + hasDamageRadius = false; + indirectDamage = 0.0; + damageRadius = 0.0; + velInheritFactor = 1.0; + + maxRifleRange = 100; + beamColor = "1.0 1.0 1.0"; + + startBeamWidth = 0.0; + pulseBeamWidth = 0.0; + beamFlareAngle = 10.0; + minFlareSize = 0.0; + maxFlareSize = 600.0; + pulseSpeed = 6.0; + pulseLength = 0.150; + + hasLight = true; + lightRadius = 10.0; + lightColor = "1.0 1.0 1.0 0.3"; + + textureName[0] = "special/nonlingradient"; + textureName[1] = "special/flare"; + textureName[2] = "special/pulse"; + textureName[3] = "special/expFlare"; + beacon = false; +}; + +datablock ShapeBaseImageData(FlashLightImage) +{ + className = WeaponImage; + + shapeFile = "turret_muzzlepoint.dts"; + offset = "0 0 0"; + + projectile = FlashLightTargeter; + projectileType = TargetProjectile; + deleteLastProjectile = false; + + usesEnergy = true; + minEnergy = 1; + + stateName[0] = "Activate"; + stateSequence[0] = "Activate"; + stateSound[0] = TargetingLaserSwitchSound; + stateTimeoutValue[0] = 0.15; + stateTransitionOnTimeout[0] = "Fire"; + + stateName[3] = "Fire"; + stateEnergyDrain[3] = 1; + stateFire[3] = true; + stateAllowImageChange[3] = false; + stateScript[3] = "onFire"; + stateTransitionOnNoAmmo[3] = "Deconstruction"; + + stateName[5] = "Deconstruction"; + stateScript[5] = "deconstruct"; + stateTransitionOnTimeout[5] = "Ready"; +}; + +function FlashLightImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.LP = %p; +} + +function FlashLightImage::deconstruct(%data, %obj, %slot) +{ + Parent::deconstruct(%data, %obj, %slot); + %obj.client.NVActivated = 0; + %obj.unmountImage(7); + messageClient(%obj.client, 'MsgNVNP', '\c2Flash Light out of power, holstiering.'); +} +function FlashLightImage::onUnmount(%this,%obj,%slot) +{ + Parent::onUnmount(%this, %obj, %slot); + %obj.LP.delete(); +} diff --git a/Scripts/power.cs b/Scripts/power.cs new file mode 100644 index 0000000..d020730 --- /dev/null +++ b/Scripts/power.cs @@ -0,0 +1,361 @@ +$PowerThread = 0; +$AmbientThread = 1; +$ActivateThread = 2; +$DeployThread = 3; + +$HumSound = 0; +$ActivateSound = 1; +$DeploySound = 2; +$PlaySound = 3; + +//****************************************************************************** +//* Power -Audio- Data Blocks * +//****************************************************************************** + +datablock AudioProfile(BasePowerOn) +{ + filename = "fx/powered/base_power_on.wav"; + description = Audio2D; + preload = true; +}; + +datablock AudioProfile(BasePowerOff) +{ + filename = "fx/powered/base_power_off.wav"; + description = Audio2D; + preload = true; +}; + +datablock AudioProfile(BasePowerHum) +{ + filename = "fx/powered/base_power_loop.wav"; + description = AudioLooping2D; + preload = true; +}; + +//****************************************************************************** +//* Power - Functions * +//****************************************************************************** + +function GameBase::clearPower(%this) +{ +} + +function SimGroup::clearPower(%this) +{ + %this.powerCount = 0; + for (%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + if(%obj.getType() & $TypeMasks::GameBaseObjectType) + %obj.clearPower(); + } +} + +function SimObject::powerInit(%this, %powerCount) +{ + //function declared to reduce console error msg spam +} + +function SimGroup::powerInit(%this, %powerCount) +{ + if(%this.providesPower) + %powerCount++; + + %count = %this.getCount(); + for (%i = 0; %i < %count; %i++) + { + %obj = %this.getObject(%i); + if(%obj.getType() & $TypeMasks::GameBaseObjectType) + { + if(%obj.getDatablock().isPowering(%obj)) + %powerCount++; + } + } + %this.powerCount = %powerCount; + for (%i = 0; %i < %this.getCount(); %i++) + { + %obj = %this.getObject(%i); + %obj.powerInit(%powerCount); + } +} + +function GameBase::powerInit(%this, %powerCount) +{ + if(%powerCount) + %this.getDatablock().gainPower(%this); + else + %this.getDataBlock().losePower(%this); +} + +function SimObject::isPowering(%data, %obj) +{ + return false; +} + +function Generator::isPowering(%data, %obj) +{ + return !%obj.isDisabled(); +} + +function SimObject::updatePowerCount() +{ +} + +function SimObject::powerCheck() +{ +} + + +function SimGroup::updatePowerCount(%this, %value) +{ + if(%this.powerCount > 0 || %value > 0) + %this.powerCount += %value; + for (%i = 0; %i < %this.getCount(); %i++) + { + %this.getObject(%i).updatePowerCount(%value); + } + for (%i = 0; %i < %this.getCount(); %i++) + %this.getObject(%i).powerCheck(%this.powerCount); +} + +function GameBaseData::gainPower(%data, %obj) +{ +} + +function GameBaseData::losePower(%data, %obj) +{ +} + +function InteriorInstance::powerCheck(%this, %powerCount) +{ + if(%powerCount > 0) + %mode = "Off"; + else + %mode = "On"; + %this.setAlarmMode(%mode); +} + +function GameBase::powerCheck(%this, %powerCount) +{ + if(%powerCount || %this.selfPower) + %this.getDatablock().gainPower(%this); + else + %this.getDatablock().losePower(%this); +} + +function GameBase::incPowerCount(%this) { + %group = %this.getGroup(); + if (%group != nameToID("MissionCleanup/Deployables")) + %group.updatePowerCount(1); +} + +function GameBase::decPowerCount(%this) { + %group = %this.getGroup(); + if (%group != nameToID("MissionCleanup/Deployables")) + %group.updatePowerCount(-1); +} + +function GameBase::setSelfPowered(%this) { + if(!%this.isPowered()) { + %this.selfPower = true; + if(%this.getDatablock().deployedObject) + %this.initDeploy = true; + %this.getDataBlock().gainPower(%this); + } + else + %this.selfPower = true; +} + +function GameBase::clearSelfPowered(%this) +{ + %this.selfPower = ""; + if(!%this.isPowered()) + %this.getDataBlock().losePower(%this); +} + +function GameBase::isPowered(%this) { + return ((%this.selfPower && %this.isEnabled()) || %this.getGroup().powerCount > 0 || %this.powerCount > 0) && !%this.isSwitchedOff; +} + +function buildPowerList() { + $PowerList = ""; + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (isObject(%obj)) { + if (%obj.getDataBlock().className $= "Generator" && %obj.deployed && !%obj.isRemoved) { + // Not using listAdd() here, for speed + $PowerList = $PowerList SPC %obj; + } + } + } + $PowerList = trim($PowerList); +} + +function globalPowerCheck() { + buildPowerList(); + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + %obj.powerCount = ""; + if (%obj.getDataBlock().needsPower) + %obj.getDataBlock().losePower(%obj); + checkPowerObject(%obj); + } +} + +function checkPowerGenerator(%powerObj,%stateChange) { + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (%obj.getDataBlock().needsPower) { + if (genLinkedObj(%powerObj,%obj)) { + %obj.powerCount = %obj.powerCount + %stateChange; + checkPowerObject(%obj); + } + } + } +} + +function checkPowerObject(%obj) { + if (%obj.getDataBlock().needsPower) { + %powerCount = %obj.powerCount; + if (%powerCount $= "") { + %count = getWordCount($PowerList); + %powerCount = 0; + for(%i=0;%i<%count;%i++) { + %powerObj = getWord($PowerList,%i); + if (genPoweringObj(%powerObj,%obj)) + %powerCount++; + } + } + %obj.powerCount = %powerCount; + doObjectPower(%obj); + } +} + +function genPoweringObj(%powerObj,%obj) { + %isPowering = false; + if (isObject(%powerObj)){ + if (%powerObj.isEnabled() && %powerObj.isPowered()) { + if (genLinkedObj(%powerObj,%obj)) + %isPowering = true; + } + } + return %isPowering; +} + +function genLinkedObj(%powerObj,%obj) { + if (%obj.powerFreq == %powerObj.powerFreq) { + if (vectorDist(%obj.getPosition(),%powerObj.getPosition()) < %powerObj.getDataBlock().powerRadius + && (%obj.team == %powerObj.team || %powerObj.team == 0) + && !%obj.isRemoved && !%powerObj.isRemoved) { + %lsLinked = true; + } + } + return %lsLinked; +} + +function doObjectPower(%obj) { + if (%obj.powerCount > 0) + %obj.getDataBlock().gainPower(%obj); + else + %obj.getDataBlock().losePower(%obj); +} + +function delNonPoweredPieces(%quiet) { + %randomTime = 10000; + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (%obj.getDataBlock().className !$= "Generator") { + %hasPower = ""; + %count2 = getWordCount($PowerList); + for(%i2=0;%i2<%count2;%i2++) { + %powerObj = getWord($PowerList,%i2); + if (vectorDist(%obj.getPosition(),%powerObj.getPosition()) < %powerObj.getDataBlock().powerRadius) { + %hasPower = true; + break; + } + + } + if (!%hasPower) { + if (!%quiet) + warn("Deleting: " @ %obj @ " Name: " @ %obj.getDataBlock().getName()); + %random = getRandom() * %randomTime; + %obj.getDataBlock().schedule(%random,"disassemble",%plyr,%obj); // Run Item Specific code. + %deleted++; + } + else + %checked++; + } + } + if (!%quiet) { + warn("Checked pieces: " @ %checked); + warn("Deleted pieces: " @ %deleted); + } + return %randomTime; +} + +function displayPowerFreq(%obj) { + %powerFreq = %obj.powerFreq; + if (%powerFreq < 1 || %powerFreq > upperPowerFreq(%obj) || !%powerFreq) + %powerFreq = 1; + %obj.powerFreq = %powerFreq; + bottomPrint(%obj.client,"Your power frequency is set to: " @ %obj.powerFreq,2,1); +} + +function toggleGenerator(%obj,%state) { + if (!isObject(%obj)) + return -1; + %dataBlockName = %obj.getDataBlock().getName(); + if (%dataBlockName $= "GeneratorLarge") { + %switchDelay = 5000; + %displayName = "Generator"; + %powerOnSound = BasePowerOn; + %powerOffSound = BasePowerOff; + } + else if (%dataBlockName $= "SolarPanel" || %dataBlockName $= "SpawnPoint") { + %switchDelay = 1000; + if(%dataBlockName $= "SolarPanel") + %displayName = "Solar Panel"; + else + %displayName = "Spawn Point"; + %powerOnSound = PlasmaSwitchSound; + %powerOffSound = PlasmaReloadSound; + } + %taggedDisplayName = addTaggedString(%displayName); + if (!%obj.isEnabled()) + return -2 SPC %taggedDisplayName; + if (!(%obj.switchTime + %switchDelay < getSimTime())) + return -3 SPC %taggedDisplayName SPC %obj.switchTime + %switchDelay - getSimTime(); + if (%obj.isSwitchedOff && (%state != false || %state $= "")) { + %obj.isSwitchedOff = ""; + %obj.getDataBlock().gainPower(%obj); + %obj.play3D(%powerOnSound); + setTargetName(%obj.target,addTaggedString("Frequency" SPC %obj.powerFreq)); + %obj.switchTime = getSimTime(); + return 2 SPC %taggedDisplayName; + } + else if (!%obj.isSwitchedOff && (%state != true || %state $= "")) { + %obj.getDataBlock().losePower(%obj); + %obj.isSwitchedOff = 1; + %obj.play3D(%powerOffSound); + setTargetName(%obj.target,addTaggedString("Disabled Frequency" SPC %obj.powerFreq)); + %obj.switchTime = getSimTime(); + return 1 SPC %taggedDisplayName; + } + return 0; +} + +function upperPowerFreq(%plyr) { + if (%plyr.client.isAdmin || %plyr.client.isSuperAdmin) + return 150; + else + return 100; +} diff --git a/Scripts/prison.cs b/Scripts/prison.cs new file mode 100644 index 0000000..83b1d2c --- /dev/null +++ b/Scripts/prison.cs @@ -0,0 +1,310 @@ +// prison.cs + +// Set up defaults for nonexisting vars +if ($Host::Prison::Enabled $= "") + $Host::Prison::Enabled = 1; // Enable prison system +if ($Host::Prison::JailMode $= "") + $Host::Prison::JailMode = 0; // Jailing mode + // 0 = prison building + // 1 = spawnsphere + // 2 = players current/last position (prison only affects use of items) +if ($Host::Prison::ReleaseMode $= "") + $Host::Prison::ReleaseMode = 1; // Release mode - same as above + +// Killing +if ($Host::Prison::Kill $= "") + $Host::Prison::Kill = 0; // Enable killing punishment +if ($Host::Prison::TeamKill $= "") + $Host::Prison::TeamKill = 1; // Enable teamkill punishment +if ($Host::Prison::KillTime $= "") + $Host::Prison::KillTime = 2 * 60; // Time to punish for killing/teamkilling + +// Deployables spamming +if ($Host::Prison::DeploySpam $= "") + $Host::Prison::DeploySpam = 1; // Enable deployables spam punishment +if ($Host::Prison::DeploySpamTime $= "") + $Host::Prison::DeploySpamTime = 60; // Time to punish for deployables spamming +if ($Host::Prison::DeploySpamMultiply $= "") + $Host::Prison::DeploySpamMultiply = 1; // Enable punishment multiplier for repeat offenders +if ($Host::Prison::DeploySpamMaxTime $= "") + $Host::Prison::DeploySpamMaxTime = 5 * 60; // Max time, if applying multiplier, to jail a player +if ($Host::Prison::DeploySpamCheckTimeMS $= "") + $Host::Prison::DeploySpamCheckTimeMS = 1000; // Time in MS between deploying that is considered spam +if ($Host::Prison::DeploySpamWarnings $= "") + $Host::Prison::DeploySpamWarnings = 10; // Number of warnings before punishment + // This is a bit misleading. It is actually the number of spams + // allowed before punishment. Warnings will be given for the last + // half of them +if ($Host::Prison::DeploySpamResetWarnCountTime $= "") + $Host::Prison::DeploySpamResetWarnCountTime = 30; // Reset warn counter after this many seconds of not deploying +if ($Host::Prison::DeploySpamRemoveRecentMS $= "") + $Host::Prison::DeploySpamRemoveRecentMS = 1000 * 15; // Remove pieces deployed by offender within the last 15 seconds + +if ($Prison::RemoveSpamTimer < 10000) // Remove spam around prison every 30 seconds, 10 seconds minimum + $Prison::RemoveSpamTimer = 30000; + +function jailPlayer(%cl,%release,%prisonTimeInSeconds,%jailThread) { + %cl.jailThread++; + if (!isObject(%cl)) { + warn("-jailPlayer- no client: " @ %cl @ " (" @ (%release ? "release" : "jail") @ ")"); + return; + } + if (%release) { + if (%jailThread == 0 || %cl.jailThread - 1 == %jailThread) { + %cl.isJailed = false; + if (($Host::Prison::ReleaseMode $= "0" || $Host::Prison::ReleaseMode $= "") + && $Prison::ReleasePos !$= "0" && $Prison::ReleasePos !$= "") { + %cl.player.setVelocity("0 0 0"); + if ($Prison::ReleaseRad > 0) { + %pi = 3.1415926535897932384626433832795; // Whoa.. + %vec = getRandom() * %pi * 2; + %rad = getRandom() * $Prison::ReleaseRad; + %x = %x + (mSin(%vec) * %rad); + %y = %y + (mCos(%vec) * %rad); + %cl.player.setPosition(VectorAdd(%x SPC %y SPC 0,$Prison::ReleasePos)); + } + else + %cl.player.setPosition($Prison::ReleasePos); + } + else if ($Host::Prison::ReleaseMode == 1) { + %cl.player.setVelocity("0 0 0"); + %cl.player.setPosition(Game.pickPlayerSpawn(%cl,false)); + } + else { + // Make sure they still get released from prison building + if (($Host::Prison::JailMode $= "0" || $Host::Prison::JailMode $= "") + && $Prison::JailPos !$= "0" && $Prison::JailPos !$= "") { // This could be handled nicer.. + %cl.player.setVelocity("0 0 0"); + %cl.player.setPosition(%cl.preJailPos); +// %cl.player.setVelocity(%cl.preJailVel); + } + // Else, do nothing. Leave player at current position. + } + buyFavorites(%cl); + if (%cl.player.weaponCount > 0) + %cl.player.selectWeaponSlot(0); + if (%jailThread) // Only show for timed releases + messageAll('msgClient','\c2%1 has been released from jail.',%cl.name); + } + return; + } + if ($Host::Prison::Enabled != true) { + warn("-jailPlayer- prison system is disabled."); + return; + } + %cl.isJailed = true; + %cl.jailTime = %prisonTimeInSeconds; + if (isObject(%cl.player)) { + if (%cl.player.getState() !$="Dead") { + if(%cl.player.isMounted()) { + if(%cl.player.vehicleTurret) + %cl.player.vehicleTurret.getDataBlock().playerDismount(%cl.player.vehicleTurret); + else { + %cl.player.getDataBlock().doDismount(%cl.player,true); + %cl.player.mountVehicle = false; + } + } + serverCmdResetControlObject(%cl); + %cl.player.setInventory(EnergyPack,1,1); // Fix Satchel Charge + %cl.player.clearInventory(); + %cl.setWeaponsHudClearAll(); + %cl.player.setArmor("Light"); + Game.dropFlag(%cl.player); + %cl.preJailVel = %cl.player.getVelocity(); + %cl.preJailPos = %cl.player.getPosition(); + // If we have no prison to put them in, we'll let them run around without any weapons.. + if (($Host::Prison::JailMode $= "0" || $Host::Prison::JailMode $= "") + && $Prison::JailPos !$= "0" && $Prison::JailPos !$= "") { + %cl.player.setVelocity("0 0 0"); + if ($Prison::JailRad > 0) { + %pi = 3.1415926535897932384626433832795; // Whoa.. + %vec = getRandom() * %pi * 2; + %rad = getRandom() * $Prison::JailRad; + %x = %x + (mSin(%vec) * %rad); + %y = %y + (mCos(%vec) * %rad); + %cl.player.setPosition(VectorAdd(%x SPC %y SPC 0,$Prison::JailPos)); + } + else + %cl.player.setPosition($Prison::JailPos); + } + else if ($Host::Prison::JailMode == 1) { + %cl.player.setVelocity("0 0 0"); + %cl.player.setPosition(Game.pickPlayerSpawn(%cl,false)); + } + else { + // Do nothing, leave player's current position + } + cancel(%cl.prisonReleaseSched); + if (%prisonTimeInSeconds > 0) + %cl.prisonReleaseSched = schedule(%prisonTimeInSeconds * 1000,0,jailPlayer,%cl,true,0,%cl.jailThread); + } + } +} + +function prisonCreate() { + %prisonBasePos = "0 0 -10000"; + if (isObject(nameToID(PrisonGroup))) + // Note, this does not handle removal of the PhysicalZones + PrisonGroup.delete(); + %group = nameToID(MissionCleanup); + if(%group == -1) + return; + %p = new SimGroup(PrisonGroup) { + providesPower = true; + new InteriorInstance(PrisonMain) { + position = %prisonBasePos; + rotation = "-1 0 0 90"; + scale = "1 1 1"; + interiorFile = "btowra.dif"; + }; + new ForceFieldBare(PrisonFF1) { + position = VectorAdd("-6.3 -22.5 -2.25",%prisonBasePos); + rotation = "1 0 0 0"; + scale = "12.9 0.18 8.5"; + dataBlock = "defaultSolidFieldBare"; + }; + new ForceFieldBare(PrisonFF2) { + position = VectorAdd("-2.25 -2.25 6.2",%prisonBasePos); + rotation = "1 0 0 0"; + scale = "4.5 4.5 0.18"; + dataBlock = "defaultSolidFieldBare"; + }; + }; + %group.add(%p); + PrisonGroup.powerInit(0); + addPrisonCamera(VectorAdd("0 2 0.5",%prisonBasePos),getWords(MatrixCreateFromEuler(mDegToRad(90)) SPC "0 0",3,6),0); + addPrisonCamera(VectorAdd("1 2 0.5",%prisonBasePos),getWords(MatrixCreateFromEuler(mDegToRad(90)) SPC "0 0",3,6),1); + addPrisonCamera(VectorAdd("-1 2 0.5",%prisonBasePos),getWords(MatrixCreateFromEuler(mDegToRad(90)) SPC "0 0",3,6),2); + $Prison::JailRad = 3; + $Prison::JailPos = VectorAdd("0 -8.5 0",%prisonBasePos); + $Prison::ReleaseRad = 4; + $Prison::ReleasePos = VectorAdd("0 -14.25 6.75",%prisonBasePos); // Release player on top of prison + $Prison::NoBuildRadius = 50; +} + +// Prevent spam in prison. If called outside prisonCreate(), needs to be passed $Prison::RemoveSpamThread++ as arg +function prisonRemoveSpamThread(%thread) { + // Re-evaluate here, in case user has set it to an "illegal" value + if ($Prison::RemoveSpamTimer < 10000) // 10 seconds + $Prison::RemoveSpamTimer = 10000; + // Thread cancels if prison is re-created, if PrisonGroup ceases to exist, or if prison system is disabled + if (%thread != $Prison::RemoveSpamThread || !isObject(nameToID(PrisonGroup)) || $Host::Prison::Enabled != 1) { + warn("prisonRemoveSpamThread #" @ mAbs(%thread) @ " stopped. Last started thread: " @ $Prison::RemoveSpamThread); + return; + } + InitContainerRadiusSearch($Prison::JailPos,$Prison::NoBuildRadius,$TypeMasks::StaticShapeObjectType | $TypeMasks::ItemObjectType | $TypeMasks::ForceFieldObjectType); + while((%obj = ContainerSearchNext()) != 0) { + // Extra safety + if (VectorDist($Prison::JailPos,%obj.getPosition()) < $Prison::NoBuildRadius) { + %dataBlockName = %obj.getDataBlock().getName(); + if (saveBuildingCheck(%obj)) { // If it's handled by saveBuilding(), it must be a deployable + %random = getRandom() * $Prison::RemoveSpamTimer-2000; // prevent duplicate disassemblies + %obj.getDataBlock().schedule(%random,"disassemble",0,%obj); // Run Item Specific code. + } + } + } + schedule($Prison::RemoveSpamTimer,0,prisonRemoveSpamThread,%thread); +} + +function prisonEnable() { + $Host::Prison::Enabled = 1; + %pgroup = nameToID(PrisonGroup); + if(isObject(%pgroup)) { + %pgroup.providesPower = true; + %pgroup.powerInit(0); + } + else + prisonCreate(); + prisonRemoveSpamThread($Prison::RemoveSpamThread++); +} + +function prisonDisable() { + $Host::Prison::Enabled = 0; + %pgroup = nameToID(PrisonGroup); + if(isObject(%pgroup)) { + %pgroup.providesPower = false; + %pgroup.powerInit(0); + } + // Release jailed players + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) { + %cl = ClientGroup.getObject(%i); + if (%cl.isJailed) + jailPlayer(%cl,true); + } +} + +datablock SensorData(PrisonCameraSensorObject) { + detects = false; +}; + +datablock TurretData(TurretPrisonCamera) { + className = PrisonCameraTurret; + shapeFile = "camera.dts"; + + thetaMin = 50; + thetaMax = 130; +// thetaNull = 90; + + cameraDefaultFov = 120; + cameraMinFov = 120; + cameraMaxFov = 120; + + neverUpdateControl = false; + + canControl = true; + canObserve = true; + observeThroughObject = true; + cmdCategory = "Support"; + cmdIcon = CMDCameraIcon; + cmdMiniIconName = "commander/MiniIcons/com_camera_grey"; + targetNameTag = 'Prison'; + targetTypeTag = 'Camera'; + sensorData = PrisonCameraSensorObject; + sensorRadius = PrisonCameraSensorObject.detectRadius; + + firstPersonOnly = true; + observeParameters = "0.5 4.5 4.5"; +}; + +datablock TurretImageData(PrisonCameraBarrel) { + shapeFile = "turret_muzzlepoint.dts"; + usesEnergy = false; + + // Turret parameters + activationMS = 100; + deactivateDelayMS = 100; + thinkTimeMS = 100; + degPerSecTheta = 180; + degPerSecPhi = 360; +}; + +function addPrisonCamera(%pos,%rot,%team) { + %group = nameToID(PrisonGroup); + if (!isObject(%group)) + return; + %pCam = new Turret(PrisonCamera) { + dataBlock = "TurretPrisonCamera"; + position = %pos; + rotation = %rot; + team = %team; + needsNoPower = true; + }; + %pCam.setRotation(%rot); // Gah! + %group.add(%pCam); + + if(%pCam.getTarget() != -1) + setTargetSensorGroup(%pCam.getTarget(),%team); + %pCam.deploy(); +} + +function TurretPrisonCamera::damageObject(%data,%targetObject,%sourceObject,%position,%amount,%damageType,%momVec,%mineSC) { + // Do nothing +} + +function TurretPrisonCamera::onAdd(%this, %obj) { + Parent::onAdd(%this, %obj); + %obj.mountImage(PrisonCameraBarrel, 0, true); + %obj.setRechargeRate(%this.rechargeRate); + %obj.setAutoFire(false); // z0ddm0d: Server crash fix related to controlable cameras +} diff --git a/Scripts/projectiles.cs b/Scripts/projectiles.cs new file mode 100644 index 0000000..11db833 --- /dev/null +++ b/Scripts/projectiles.cs @@ -0,0 +1,825 @@ +//-------------------------------------------------------------------------- +// Projectiles.cs: Note that the actual projectile blocks are stored with +// with weapon that uses them in base/scripts/weapons/*.cs, +// the blocks below are only to illustrate the default values +// for each block type. Also, ProjectileData cannot be used +// as a concrete datablock type. +// Inheritance: +// ProjectileData : GameBaseData +// LinearProjectileData : ProjectileData +// LinearFlareProjectileData : LinearProjectileData +// GrenadeProjectileData : ProjectileData +// SeekerProjectileData : ProjectileData +// SniperProjectileData : ProjectileData +// +//-------------------------------------------------------------------------- +//-------------------------------------- Default functions +// +datablock StaticShapeData(TargeterBeacon) +{ + shapeFile = "turret_muzzlepoint.dts"; + targetNameTag = 'beacon'; + isInvincible = true; + + dynamicType = $TypeMasks::SensorObjectType; +}; + +// Eolk - Lumping stuff together for better. +function ProjectileData::onCollision(%data, %projectile, %targetObject, %modifier, %position, %normal) +{ + if(isObject(%targetObject)) // Console spam fix - ToS. z0ddm0d + { + if(%data.specialCollisionStreamline) + { + // extra damage for head shot or less for close range shots + // Eolk - Added TSObjectCheck. + if(!(%targetObject.getType() & ($TypeMasks::InteriorObjectType | $TypeMasks::TerrainObjectType)) && (!%targetObject.isTSStaticObject()) && (%targetObject.getDataBlock().getClassName() $= "PlayerData")) + { + %damLoc = firstWord(%targetObject.getDamageLocation(%position)); + if(%damLoc $= "head") + { + %targetObject.getOwnerClient().headShot = 1; + %modifier *= %data.HeadMultiplier; + } + else if(%damLoc $= "legs") + { + %modifier *= %data.LegsMultiplier; + } + else + { + %modifier = 1; + %targetObject.getOwnerClient().headShot = 0; + } + } + } + %targetObject.damage(%projectile.sourceObject, %position, %modifier * %data.directDamage, %data.directDamageType); + } +} + +function SniperProjectileData::onCollision(%data, %projectile, %targetObject, %modifier, %position, %normal) +{ + %damageAmount = %data.directDamage * %projectile.damageFactor; + + if(isObject(%targetObject)) // Console spam fix - ToS. z0ddm0d + { + if(%targetObject.getDataBlock().getClassName() $= "PlayerData") + { + %damLoc = firstWord(%targetObject.getDamageLocation(%position)); + if(%damLoc $= "head") + { + %targetObject.getOwnerClient().headShot = 1; + %modifier = %data.rifleHeadMultiplier; + } + else + { + %modifier = 1; + %targetObject.getOwnerClient().headShot = 0; + } + } + %targetObject.damage(%projectile.sourceObject, %position, %modifier * %damageAmount, %data.directDamageType); + } +} + +function ShapeBaseImageData::onFire(%data, %obj, %slot) +{ + %data.lightStart = getSimTime(); + + if( %obj.station $= "" && %obj.isCloaked() ) + { + if( %obj.respawnCloakThread !$= "" ) + { + Cancel(%obj.respawnCloakThread); + %obj.setCloaked( false ); + %obj.respawnCloakThread = ""; + } + else + { + if( %obj.getEnergyLevel() > 20 ) + { + %obj.setCloaked( false ); + %obj.reCloak = %obj.schedule( 500, "setCloaked", true ); + } + } + } + + if( %obj.client > 0 ) + { + %obj.setInvincibleMode(0 ,0.00); + %obj.setInvincible( false ); // fire your weapon and your invincibility goes away. + } + + %vehicle = 0; + if(%data.usesEnergy) + { + if(%data.useMountEnergy) + { + %useEnergyObj = %obj.getObjectMount(); + if(!%useEnergyObj) + %useEnergyObj = %obj; + %energy = %useEnergyObj.getEnergyLevel(); + %vehicle = %useEnergyObj; + } + else + %energy = %obj.getEnergyLevel(); + + if(%data.useCapacitor && %data.usesEnergy) + { + if( %useEnergyObj.turretObject.getCapacitorLevel() < %data.minEnergy ) + { + return; + } + } + else if(%energy < %data.minEnergy) + return; + } + // --------------------------------------------------------------------- + // Code streamlining - ToS. z0ddm0d + // Eolk - more streamlining. + if(%data.projectileSpread) + { + if(%data.decayingSpread) + { + if(%obj.spread $= "" || %obj.spread > %data.projectileSpread) + %obj.spread = %data.projectileSpread; + else + //%obj.spread = %obj.spread + %data.spreadIncreaseRate; + %obj.spread += %data.spreadIncreaseRate; // Eolk - += is better. + + if(%obj.lowSpreadLoop $= "") + %obj.lowSpreadLoop = %data.schedule(250, "LowerSpread", %obj, %slot); + + if(%obj.spread < %data.maxspread) + %obj.spread = %data.maxspread; + } + else + %obj.spread = %data.projectileSpread; + + %vec = %obj.getMuzzleVector(%slot); + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %obj.spread; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %vector = MatrixMulVector(%mat, %vec); + } + else + { + %vector = %obj.getMuzzleVector(%slot); + } + %initialPos = %obj.getMuzzlePoint(%slot); + if (%data.muzzleSlots) { + %nrm = %vector; + %nrm2 = vectorNormalize(vectorCross("0 0 -1",%nrm)); + %nrm3 = vectorNormalize(vectorCross(%nrm2,%nrm)); + %offsetVec = %data.muzzleSlotOffset[%obj.currentMuzzleSlot]; + + %initialPos = vectorAdd(%initialPos,vectorScale(%nrm2,getWord(%offsetVec,0))); + %initialPos = vectorAdd(%initialPos,vectorScale(%nrm,getWord(%offsetVec,1))); + %initialPos = vectorAdd(%initialPos,vectorScale(%nrm3,getWord(%offsetVec,2))); + %obj.currentMuzzleSlot++; + if (%obj.currentMuzzleSlot > %data.muzzleSlots - 1) + %obj.currentMuzzleSlot = 0; + } + + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %vector; + initialPosition = %initialPos; + sourceObject = %obj; + sourceSlot = %slot; + vehicleObject = %vehicle; + }; + // End streamlining + // --------------------------------------------------------------------- + if (isObject(%obj.lastProjectile) && %obj.deleteLastProjectile) + %obj.lastProjectile.delete(); + + %obj.lastProjectile = %p; + %obj.deleteLastProjectile = %data.deleteLastProjectile; + MissionCleanup.add(%p); + + // AI hook + if(%obj.client) + %obj.client.projectile = %p; + + %data.ApplyFiringImpulse(%obj); // Eolk + + if(%data.usesEnergy) + { + if(%data.useMountEnergy) + { + if( %data.useCapacitor ) + { + %vehicle.turretObject.setCapacitorLevel( %vehicle.turretObject.getCapacitorLevel() - %data.fireEnergy ); + } + else + %useEnergyObj.setEnergyLevel(%energy - %data.fireEnergy); + } + else + %obj.setEnergyLevel(%energy - %data.fireEnergy); + } + else + { + %obj.decInventory(%data.ammo, 1); + if(%data.usesClips && %obj.getInventory(%data.ammo) <= 0) + %data.CheckForClip(%obj, %slot); + } + + return %p; +} + +function ShapeBaseImageData::onUnmount(%data, %obj, %slot) +{ + if (%data.deleteLastProjectile && isObject(%obj.lastProjectile)) + { + %obj.lastProjectile.delete(); + %obj.lastProjectile = ""; + } +} + +function TurretImageData::deconstruct(%data, %obj, %slot) +{ + if (%data.deleteLastProjectile && isObject(%obj.lastProjectile)) + { + %obj.lastProjectile.delete(); + %obj.lastProjectile = ""; + } +} + +function ShapeBaseImageData::deconstruct(%data, %obj, %slot) +{ + if (%data.deleteLastProjectile && isObject(%obj.lastProjectile)) + { + %obj.lastProjectile.delete(); + %obj.lastProjectile = ""; + } +} + +function MissileLauncherImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + %target = %obj.getLockedTarget(); + if(%target) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); +} + +function MissileLauncherImage::onWetFire(%data, %obj, %slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + MissileSet.add(%p); + + %p.setObjectTarget(0); +} + +//-------------------------------------------------------------------------- + +function MissileBarrelLarge::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data,%obj,%slot); + + if (%obj.getControllingClient()) + { + // a player is controlling the turret + %target = %obj.getLockedTarget(); + } + else + { + // The ai is controlling the turret + %target = %obj.getTargetObject(); + } + + if(%target) + %p.setObjectTarget(%target); + else if(%obj.isLocked()) + %p.setPositionTarget(%obj.getLockedPosition()); + else + %p.setNoTarget(); // set as unguided. Only happens when itchy trigger can't wait for lock tone. +} + +//add mortars to the "grenade set" so the AI's can avoid them better... +function MortarImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + AIGrenadeThrown(%p); +} + +function SniperRifleImage::onFire(%data,%obj,%slot) +{ + if(!%obj.hasEnergyPack) + { + // siddown Junior, you can't use it + serverPlay3D(SniperRifleDryFireSound, %obj.getTransform()); + return; + } + + %pct = %obj.getEnergyLevel() / %obj.getDataBlock().maxEnergy; + %p = new (%data.projectileType)() { + dataBlock = %data.projectile; + initialDirection = %obj.getMuzzleVector(%slot); + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + damageFactor = %pct * %pct; + sourceSlot = %slot; + }; + %p.setEnergyPercentage(%pct); + + %obj.lastProjectile = %p; + MissionCleanup.add(%p); + serverPlay3D(SniperRifleFireSound, %obj.getTransform()); + + // AI hook + if(%obj.client) + %obj.client.projectile = %p; + + %obj.setEnergyLevel(0); +} + +function ElfGunImage::onFire(%data, %obj, %slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + + if(!%p.hasTarget()) + %obj.playAudio(0, ELFFireWetSound); +} + +function TargetingLaserImage::onFire(%data,%obj,%slot) +{ + %p = Parent::onFire(%data, %obj, %slot); + %p.setTarget(%obj.team); + %obj.posLaze = 1; + if(%obj.beacon) + { + %obj.beacon.delete(); + %obj.beacon = ""; + } +} + +function TargetingLaserImage::deconstruct(%data, %obj, %slot) +{ + %pos = %obj.lastProjectile.getTargetPoint(); + + if(%obj.beacon) + { + %obj.beacon.delete(); + %obj.beacon = ""; + } + + if(%pos !$= "0 0 0 -1") + { + %obj.beacon = new BeaconObject() { + dataBlock = "TargeterBeacon"; + beaconType = "vehicle"; + position = %pos; + }; + + %obj.beacon.playThread($AmbientThread, "ambient"); + %obj.beacon.team = %obj.team; + %obj.beacon.sourceObject = %obj; + + // give it a team target + %obj.beacon.setTarget(%obj.team); + MissionCleanup.add(%obj.beacon); + } + + %obj.posLaze = 0; + Parent::deconstruct(%data, %obj, %slot); +} + +function ShockLanceImage::onFire(%this, %obj, %slot) +{ + if( %obj.isCloaked() ) + { + if( %obj.respawnCloakThread !$= "" ) + { + Cancel(%obj.respawnCloakThread); + %obj.setCloaked( false ); + } + else + { + if( %obj.getEnergyLevel() > 20 ) + { + %obj.setCloaked( false ); + %obj.reCloak = %obj.schedule( 500, "setCloaked", true ); + } + } + } + + %muzzlePos = %obj.getMuzzlePoint(%slot); + %muzzleVec = %obj.getMuzzleVector(%slot); + + %endPos = VectorAdd(%muzzlePos, VectorScale(%muzzleVec, %this.projectile.extension)); + + %damageMasks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | + $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | + $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType; + + %everythingElseMask = $TypeMasks::TerrainObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::StaticObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::DamagableItemObjectType; + + // did I miss anything? players, vehicles, stations, gens, sensors, turrets + %hit = ContainerRayCast(%muzzlePos, %endPos, %damageMasks | %everythingElseMask, %obj); + + %noDisplay = true; + + if (%hit !$= "0") + { + %obj.setEnergyLevel(%obj.getEnergyLevel() - %this.hitEnergy); + + %hitobj = getWord(%hit, 0); + %hitpos = getWord(%hit, 1) @ " " @ getWord(%hit, 2) @ " " @ getWord(%hit, 3); + + if ( %hitObj.getType() & %damageMasks ) + { + %hitobj.applyImpulse(%hitpos, VectorScale(%muzzleVec, %this.projectile.impulse)); + %obj.playAudio(0, ShockLanceHitSound); + + // This is truly lame, but we need the sourceobject property present... + %p = new ShockLanceProjectile() { + dataBlock = %this.projectile; + initialDirection = %obj.getMuzzleVector(%slot); + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + sourceSlot = %slot; + targetId = %hit; + }; + MissionCleanup.add(%p); + + %damageMultiplier = 1.0; + + if(%hitObj.getDataBlock().getClassName() $= "PlayerData") + { + // Now we see if we hit from behind... + %forwardVec = %hitobj.getForwardVector(); + %objDir2D = getWord(%forwardVec, 0) @ " " @ getWord(%forwardVec,1) @ " " @ "0.0"; + %objPos = %hitObj.getPosition(); + %dif = VectorSub(%objPos, %muzzlePos); + %dif = getWord(%dif, 0) @ " " @ getWord(%dif, 1) @ " 0"; + %dif = VectorNormalize(%dif); + %dot = VectorDot(%dif, %objDir2D); + + // 120 Deg angle test... + // 1.05 == 60 degrees in radians + if (%dot >= mCos(1.05)) { + // Rear hit + %damageMultiplier = 3.0; + } + } + + %totalDamage = %this.Projectile.DirectDamage * %damageMultiplier; + %hitObj.getDataBlock().damageObject(%hitobj, %p.sourceObject, %hitpos, %totalDamage, $DamageType::ShockLance); + + %noDisplay = false; + } + } + + if( %noDisplay ) + { + // Miss + %obj.setEnergyLevel(%obj.getEnergyLevel() - %this.missEnergy); + %obj.playAudio(0, ShockLanceMissSound); + + %p = new ShockLanceProjectile() { + dataBlock = %this.projectile; + initialDirection = %obj.getMuzzleVector(%slot); + initialPosition = %obj.getMuzzlePoint(%slot); + sourceObject = %obj; + sourceSlot = %slot; + }; + MissionCleanup.add(%p); + + } +} + +$ELFZapSound = 2; +$ELFFireSound = 3; + +function ELFProjectileData::zapTarget(%data, %projectile, %target, %targeter) +{ + %oldERate = %target.getRechargeRate(); + %target.teamDamageStateOnZap = $teamDamage; + %teammates = %target.client.team == %targeter.client.team; + + if( %target.teamDamageStateOnZap || !%teammates ) + %target.setRechargeRate(%oldERate - %data.drainEnergy); + else + %target.setRechargeRate(%oldERate); + + %projectile.checkELFStatus(%data, %target, %targeter); +} + +function ELFProjectileData::unzapTarget(%data, %projectile, %target, %targeter) +{ + cancel(%projectile.ELFrecur); + %target.stopAudio($ELFZapSound); + %targeter.stopAudio($ELFFireSound); + %target.zapSound = false; + %targeter.zappingSound = false; + %teammates = %target.client.team == %targeter.client.team; + + if(!%target.isDestroyed()) + { + %oldERate = %target.getRechargeRate(); + if( %target.teamDamageStateOnZap || !%teammates ) + %target.setRechargeRate(%oldERate + %data.drainEnergy); + else + %target.setRechargeRate(%oldERate); + } +} + +function ELFProjectileData::targetDestroyedCancel(%data, %projectile, %target, %targeter) +{ + cancel(%projectile.ELFrecur); + %target.stopAudio($ELFZapSound); + %targeter.stopAudio($ELFFireSound); + %target.zapSound = false; + %targeter.zappingSound = false; + %projectile.delete(); +} + +function ELFProjectile::checkELFStatus(%this, %data, %target, %targeter) +{ + if(isObject(%target)) + { + if(%target.getDamageState() $= "Destroyed") + { + %data.targetDestroyedCancel(%this, %target, %targeter); + return; + } + + %enLevel = %target.getEnergyLevel(); + if(%enLevel < 1.0) + { + %dataBlock = %target.getDataBlock(); + %dataBlock.damageObject(%target, %this.sourceObject, %target.getPosition(), %data.drainHealth, %data.directDamageType); + + } + else + { + %normal = "0.0 0.0 1.0"; + %target.playShieldEffect( %normal ); + } + %this.ELFrecur = %this.schedule(32, checkELFStatus, %data, %target, %targeter); + + %targeter.playAudio($ELFFireSound, ELFGunFireSound); + if(!%target.zapSound) + { + %target.playAudio($ELFZapSound, ELFHitTargetSound); + %target.zapSound = true; + %targeter.zappingSound = true; + } + } + // ------------------------------------------------------- + // z0dd - ZOD, 5/27/02. Stop firing if there is no target, + // fixes continuous fire bug. + //else if(%targeter.zappingSound) + //{ + // %targeter.stopAudio($ELFFireSound); + // %targeter.zappingSound = false; + //} + else + { + if(%targeter.zappingSound) + { + %targeter.stopAudio($ELFFireSound); + %targeter.zappingSound = false; + } + %data.targetDestroyedCancel(%this, %target, %targeter); + return; + } + // End z0dd - ZOD + // ------------------------------------------------------- +} + + +function RadiusExplosion(%explosionSource, %position, %radius, %damage, %impulse, %sourceObject, %damageType) +{ + InitContainerRadiusSearch(%position, %radius, $TypeMasks::PlayerObjectType | + $TypeMasks::VehicleObjectType | + $TypeMasks::StaticShapeObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::ItemObjectType); + + %numTargets = 0; + while ((%targetObject = containerSearchNext()) != 0) + { + + if (%targetObject.isRemoved) + continue; + + %dist = containerSearchCurrRadDamageDist(); + + if (%dist > %radius) + continue; + + if (!(%targetObject.getType() & $TypeMasks::ForceFieldObjectType)) { + if (%targetObject.isMounted()) { + %mount = %targetObject.getObjectMount(); + %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]) { + 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 | + $TypeMasks::VehicleObjectType)); + if (%coverage == 1) + %coverage = calcBuildingInWay(%position, %targetObject); + if (%coverage == 0) + continue; + if (%damagetype $= $DamageType::nuke) + %coverage = 1; + + //if ( $splashTest ) + %amount = (1.0 - ((%dist / %radius) * 0.88)) * %coverage * %damage; + //else + //%amount = (1.0 - (%dist / %radius)) * %coverage * %damage; + + //error( "damage: " @ %amount @ " at distance: " @ %dist @ " radius: " @ %radius @ " maxDamage: " @ %damage ); + + %data = %targetObject.getDataBlock(); + %className = %data.className; + + if (%impulse && %data.shouldApplyImpulse(%targetObject)) + { + %p = %targetObject.getWorldBoxCenter(); + %momVec = VectorSub(%p, %position); + %momVec = VectorNormalize(%momVec); + %impulseVec = VectorScale(%momVec, %impulse * (1.0 - (%dist / %radius))); + %doImpulse = true; + } + // --------------------------------------------------------------------------- + // z0dd - ZOD, 5/8/02. Removed Wheeled Vehicle to eliminate the flying MPB bug + // caused by tossing concussion grenades under a deployed MPB. + //else if( %className $= WheeledVehicleData || %className $= FlyingVehicleData || %className $= HoverVehicleData ) + else if( %className $= FlyingVehicleData || %className $= HoverVehicleData || %classname $= WheeledVehicleData) + { + %p = %targetObject.getWorldBoxCenter(); + %momVec = VectorSub(%p, %position); + %momVec = VectorNormalize(%momVec); + + %impulseVec = VectorScale(%momVec, %impulse * (1.0 - (%dist / %radius))); + + if( getWord( %momVec, 2 ) < -0.5 ) + %momVec = "0 0 1"; + + // Add obj's velocity into the momentum vector + %velocity = %targetObject.getVelocity(); + //%momVec = VectorNormalize( vectorAdd( %momVec, %velocity) ); + %doImpulse = true; + } + else + { + %momVec = "0 0 1"; + %doImpulse = false; + } + + if(%amount > 0) + %data.damageObject(%targetObject, %sourceObject, %position, %amount, %damageType, %momVec, %explosionSource.theClient, %explosionSource); + else if( %explosionSource.getDataBlock().getName() $= "ConcussionGrenadeThrown" && %data.getClassName() $= "PlayerData" ) { + %data.applyConcussion( %dist, %radius, %sourceObject, %targetObject ); + if(!$teamDamage && %sourceObject != %targetObject && %sourceObject.client.team == %targetObject.client.team) + messageClient(%targetObject.client, 'msgTeamConcussionGrenade', '\c1You were hit by %1\'s concussion grenade.', getTaggedString(%sourceObject.client.name)); + } + + if(%doImpulse) + %targetObject.applyImpulse(%position, %impulseVec); + } +} + +function ProjectileData::onExplode(%data, %proj, %pos, %mod) +{ + if (%data.hasDamageRadius) + RadiusExplosion(%proj, %pos, %data.damageRadius, %data.indirectDamage, %data.kickBackStrength, %proj.sourceObject, %data.radiusDamageType); +} + +function Flag::shouldApplyImpulse(%data, %obj) +{ + if(%obj.isHome) + return false; + else + return true; +} + +// TODO - update +function calcBuildingInWay(%position, %targetObject) { + %targetPos = posFromTransform(%targetObject.getWorldBoxCenter()); +// %vec = vectorNormalize(vectorSub(%position,%targetPos)); +// %sourcePos = vectorAdd(%position,vectorScale(%vec,0.125)); + %sourcePos = %position; + %mask = $TypeMasks::StaticObjectType; + %found = containerRayCast(%sourcePos, %targetPos, %mask); + if (%found) { + if ((%found.getClassName() $= InteriorInstance) || (%found.getClassName() $= TerrainBlock) || (%found.getClassName() $= TSStatic)) + return 1; + if (%found == %targetObject) + return 1; + if (%found.getDataBlock().className $= "wall" + || %found.getDataBlock().className $= "wWall" + || %found.getDataBlock().className $= "spine" + || %found.getDataBlock().className $= "mSpine" + || %found.getDataBlock().className $= "floor" + || %found.getDataBlock().className $= "forcefield") + return 0; + else { + %found = containerRayCast(%sourcePos, %targetPos, %mask, %found); + if (%found) { + if ((%found.getClassName() $= InteriorInstance) || (%found.getClassName() $= TerrainBlock) || (%found.getClassName() $= TSStatic)) + return 1; + if (%found == %targetObject) + return 1; + if (%found.getDataBlock().className $= "wall" + || %found.getDataBlock().className $= "wWall" + || %found.getDataBlock().className $= "spine" + || %found.getDataBlock().className $= "mSpine" + || %found.getDataBlock().className $= "floor" + || %found.getDataBlock().className $= "forcefield") + return 0; + } + } + } + return 1; +} + +// Function by Eolk. This is unused unless the weapon applies an impulse. +function ShapeBaseImageData::ApplyFiringImpulse(%data, %obj, %slot) +{ + if(%data.applyFiringImpulse) + %obj.applyKick(%data.applyFiringImpulse); +} + +function ShapeBaseImageData::CheckForClip(%data, %obj, %slot) +{ + if(%obj.getInventory(%data.clip) > 0) + { + %obj.clipReloading = true; + %data.UnmountClipEffects(%obj, %slot); + %data.schedule(%data.clipTimeout, "ReloadClip", %obj, %slot); + } +} + +function ShapeBaseImageData::UnmountClipEffects(%data, %obj, %slot) +{ + // This is for weapons who wish to overwrite this with effects. + // -- Eolk +} + +function ShapeBaseImageData::MountClipEffects(%data, %obj, %slot) +{ + // This is for weapons who wish to overwrite this with effects. + // -- Eolk +} + +function ShapeBaseImageData::ReloadClip(%data, %obj, %slot) +{ + // Eolk - sloppy fix for when the player tosses the weapon before reloading is done. + if(!%obj.clipReloading) + return; + + %obj.clipReloading = false; + %obj.decInventory(%data.clip, 1); + %data.MountClipEffects(%obj, %slot); + %obj.setInventory(%data.ammo, $AmmoIncrement[%data.ammo]); +} + +function ShapeBaseImageData::LowerSpread(%data, %obj, %slot) +{ + // Eolk - this function is basically useless. It's for modders/future weapons/streamlines. + RifleLowerSpread(%data, %obj); +} + +function RifleLowerSpread(%data, %obj) +{ + // Prevents console spam +} diff --git a/Scripts/saveBuilding.cs b/Scripts/saveBuilding.cs new file mode 100644 index 0000000..436739a --- /dev/null +++ b/Scripts/saveBuilding.cs @@ -0,0 +1,899 @@ +// SaveBuilding.cs +// +// This script made possible by a joint effort of DynaBlade and JackTL +// +// Functions: +// +// saveBuilding(clientId,radius,file[$],quiet[1/0]) +// saveBuildingCentered(clientId,radius,file[$],quiet[1/0],centerAtMinZ[1/0]) +// delBuildingWaypoint() +// loadBuilding(file[$]) +// saveBuildingTimer(time in seconds,globalEcho[1/0],file[$],useMultipleFiles[1/0]) // No limit on multiple files yet. Beware +// saveBuildingTimerOn() // <-- for restarting timed saves, does not initialize like saveBuildingTimer() +// saveBuildingTimerOff() +// delDupPieces(clientId,radius,quiet[1/0]) // NOTE: There are issues with calling this function a second time before the previous call's deconstruction has finished +// +// See functions for (some) comments + +if ($SaveBuilding::SaveFolder $= "") + $SaveBuilding::SaveFolder = "Buildings/Admin/"; +if ($SaveBuilding::AutoSaveFolder $= "") + $SaveBuilding::AutoSaveFolder = "Buildings/AutoSave/"; +if ($SaveBuilding::TimerDefaultTime $= "") + $SaveBuilding::TimerDefaultTime = 5 * 60 * 1000; // 5 minutes. Saving may cause stutter on low-end servers with high number of pieces +if ($SaveBuilding::QuickDelete $= "") + $SaveBuilding::QuickDelete = 1; + + +// See if we want to save the piece +function saveBuildingCheck(%obj) { + %save = false; + %dataBlockName = %obj.getDatablock().getName(); + switch$ (%dataBlockName) { + case "DeployedSpine": + %save = true; + case "DeployedSpine2": + %save = true; + case "DeployedWoodSpine": + %save = true; + case "DeployedMSpine": + %save = true; + case "DeployedMSpineRing": + %save = true; + case "DeployedFloor": + %save = true; + case "DeployedWall": + %save = true; + case "DeployedwWall": + %save = true; + case "DeployedEnergizer": + %save = true; + case "DeployedStationInventory": + %save = true; + case "DeployedJumpad": + %save = true; + case "DeployedEscapePod": + %save = true; + case "TelePadDeployedBase": + %save = true; + case "TelePadBeam": + %save = true; + case "DeployedLTarget": + %save = true; + case "DeployedLogoProjector": + %save = true; + case "DeployedSwitch": + %save = true; + case "DeployedPulseSensor": + %save = true; + case "DeployedMotionSensor": + %save = true; + case "TurretDeployedBase": + %save = true; + case "TurretDeployedSentry": + %save = true; + case "TurretDeployedFloorIndoor": + %save = true; + case "TurretDeployedWallIndoor": + %save = true; + case "TurretDeployedCeilingIndoor": + %save = true; + case "TurretDeployedOutdoor": + %save = true; + case "LaserDeployed": + %save = true; + case "MissileRackTurretDeployed": + %save = true; + case "DiscTurretDeployed": + %save = true; + case "TurretDeployedCamera": + %save = true; + case "DeployedLightBase": + %save = true; + case "DeployedTripwire": + %save = true; + case "DeployedDoor": + %save = true; + case "RepairPad": + %save = true; + case "DeployedZSpawnBase": + %save = true; + case "DispenserDep": + %save = true; + case "AudioDep": + %save = true; + case "EmitterDep": + %save = true; + case "DeployedWaypoint": + %save = true; + case "SpawnPoint": + %save = true; + } + if (%dataBlockName $= "StationInventory" && %obj.deployed == true) + %save = true; + if (%dataBlockName $= "GeneratorLarge" && %obj.deployed == true) + %save = true; + if (%dataBlockName $= "SolarPanel" && %obj.deployed == true) + %save = true; + if (%dataBlockName $= "SensorMediumPulse" && %obj.deployed == true) + %save = true; + if (%dataBlockName $= "SensorLargePulse" && %obj.deployed == true) + %save = true; + if (getSubStr(%dataBlockName,0,18) $= "DeployedForceField") + %save = true; + if (getSubStr(%dataBlockName,0,20) $= "DeployedGravityField") + %save = true; + if (getSubStr(%dataBlockName,0,12) $= "DeployedTree") + %save = true; + if (getSubStr(%dataBlockName,0,13) $= "DeployedCrate") + %save = true; + if (getSubStr(%dataBlockName,0,18) $= "DeployedDecoration") + %save = true; + return %save; +} + +// This function saves a building and its position in the map +function saveBuilding(%cl,%rad,%file,%quiet,%isAutoSave) { + if (%quiet == true) { + $SaveBuilding::Quiet = true; + } + else { + $SaveBuilding::Quiet = false; // Overwrite + } + %origCl = %cl; + if (!isObject(%cl)) { + if (isObject(nameToID(LocalClientConnection))) { + %cl = nameToID(LocalClientConnection); + } + else { + if ($CurrentClientId) { + %cl = $CurrentClientId; + } + } + } + if (%rad < 1) { + %rad = 100000; + } + %buildingCount = 0; + if (%file $= "" || %file $= "0") { + for(%found = true; %found; %buildingCount++ ) { + %suffix = %buildingCount; + while (strLen(%suffix) < 5) %suffix = "0" @ %suffix; + if (%isAutoSave) + %file = $SaveBuilding::AutoSaveFolder @ $MissionName @ "-" @ %suffix @ ".cs"; + else + %file = $SaveBuilding::SaveFolder @ %cl.nameBase @ "-" @ $MissionName @ "-" @ %suffix @ ".cs"; + %found = isFile(%file); + } + } + else { + if (%isAutoSave) + %file = $SaveBuilding::AutoSaveFolder @ %file; + else + %file = $SaveBuilding::SaveFolder @ %file; + } + if (isObject(%cl.getControlObject())) { + %pos = %cl.getControlObject().getPosition(); + } + if (!%pos) { + %pos = "0 0 0"; + } + if (%isAutoSave && (%origCl $= "" || %origCl $= "0")) + %pos = "0 0 0"; + new fileObject("Building"); + if (!$SaveBuilding::Quiet) { + warn("Saving to file: \"" @ %file @ "\""); + } + Building.openForWrite(%file); + Building.writeLine("// CONSTRUCTION MOD SAVE FILE"); + Building.writeLine("// Saved by \"" @ getField(%cl.nameBase,0) @ "\""); + Building.writeLine("// Created in mission \"" @ $MissionName @ "\""); + Building.writeLine("// Construction " @ $ModVersion); + Building.writeLine(""); + + $SaveBuilding::Saved = 0; + $SaveBuilding::Skipped = 0; + initContainerRadiusSearch(%pos,%rad,$TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType); + while((%obj = containerSearchNext()) != 0) { + %cmp = writeBuildingComponent(%obj); + if (%cmp !$= "") { + Building.writeLine(%cmp); + } + } + + if (!$SaveBuilding::Quiet) { + warn("Saved to file: \"" @ %file @ "\""); + warn("Saved pieces: " @ $SaveBuilding::Saved); + warn("Skipped pieces: " @ $SaveBuilding::Skipped); + } + + $SaveBuilding::LastFile = %file; + + Building.close(); + Building.delete(); + return %file; +} + +function writeBuildingComponent(%obj) { + %dataBlockName = %obj.getDatablock().getName(); + %save = false; + %save = saveBuildingCheck(%obj); + if (%save == true) { + if (%dataBlockName $= "DeployedMSpineRing") // Handled by DeployedMSpine + return; + if (%dataBlockName $= "TelePadBeam") // Handled by TelePadDeployedBase + return; + if (%dataBlockName $= "DeployedLTarget") // Handled by parent object + return; + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %obj @ " Name: " @ %dataBlockName); + %buildingPiece = "%building = new (" @ %obj.getClassName() @ ") () {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %dataBlockName @ "\";"; + if (%obj.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %obj.position @ "\";"; + if (%obj.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %obj.rotation @ "\";"; + if (%obj.realScale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %obj.realScale @ "\";"; + else {if (%obj.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %obj.scale @ "\";";} + if (%obj.team !$= "") %buildingPiece = %buildingPiece @ "team = \"" @ %obj.team @ "\";"; + if (%obj.ownerGUID !$= "") %buildingPiece = %buildingPiece @ "ownerGUID = \"" @ %obj.ownerGUID @ "\";"; + if (%obj.needsFit) %buildingPiece = %buildingPiece @ "needsfit = \"" @ %obj.needsFit @ "\";"; + if (%obj.grounded) %buildingPiece = %buildingPiece @ "grounded = \"" @ %obj.grounded @ "\";"; + if (%obj.deployed !$= "") %buildingPiece = %buildingPiece @ "deployed = \"" @ %obj.deployed @ "\";"; + if (%obj.impulse !$= "") %buildingPiece = %buildingPiece @ "impulse = \"" @ %obj.impulse @ "\";"; + if (%obj.velocityMod !$= "") %buildingPiece = %buildingPiece @ "velocityMod = \"" @ %obj.velocityMod @ "\";"; + if (%obj.gravityMod !$= "") %buildingPiece = %buildingPiece @ "gravityMod = \"" @ %obj.gravityMod @ "\";"; + if (%obj.appliedForce !$= "") %buildingPiece = %buildingPiece @ "appliedForce = \"" @ %obj.appliedForce @ "\";"; + if (%obj.powerFreq !$= "") %buildingPiece = %buildingPiece @ "powerFreq = \"" @ %obj.powerFreq @ "\";"; + if (%obj.isSwitchedOff !$= "") %buildingPiece = %buildingPiece @ "isSwitchedOff = \"" @ %obj.isSwitchedOff @ "\";"; + if (%obj.switchRadius !$= "") %buildingPiece = %buildingPiece @ "switchRadius = \"" @ %obj.switchRadius @ "\";"; + if (%obj.holoBlock !$= "") %buildingPiece = %buildingPiece @ "holoBlock = \"" @ %obj.holoBlock @ "\";"; + if (%obj.noSlow !$= "") %buildingPiece = %buildingPiece @ "noSlow = \"" @ %obj.noSlow @ "\";"; + if (%obj.static !$= "") %buildingPiece = %buildingPiece @ "static = \"" @ %obj.static @ "\";"; + if (%obj.timed !$= "") %buildingPiece = %buildingPiece @ "timed = \"" @ %obj.timed @ "\";"; + if (%obj.beamRange !$= "") %buildingPiece = %buildingPiece @ "beamRange = \"" @ %obj.beamRange @ "\";"; + if (%obj.tripMode !$= "") %buildingPiece = %buildingPiece @ "tripMode = \"" @ %obj.tripMode @ "\";"; + if (%obj.fieldMode !$= "") %buildingPiece = %buildingPiece @ "fieldMode = \"" @ %obj.fieldMode @ "\";"; + if (%obj.isdoor != "") %buildingPiece = %buildingPiece @ "isdoor = \"" @ %obj.isdoor@ "\";"; + if (%dataBlockName $= "TelePadDeployedBase") { + if (%obj.frequency !$= "") %buildingPiece = %buildingPiece @ "frequency = \"" @ %obj.frequency @ "\";"; + if (%obj.teleMode !$= "") %buildingPiece = %buildingPiece @ "teleMode = \"" @ %obj.teleMode @ "\";"; + } + if (%dataBlockName $= "DeployedDoor" || %obj.isDoor){ + if (%obj.toggletype != "") %buildingPiece = %buildingPiece @ "toggletype = \"" @ %obj.toggletype @ "\";"; + if (%obj.collisionType != "") %buildingPiece = %buildingPiece @ "collisiontype = \"" @ %obj.collisiontype @ "\";"; + if (%obj.powercontrol != "") %buildingPiece = %buildingPiece @ "powercontrol = \"" @ %obj.powercontrol @ "\";"; + if (%obj.state !$= "") %buildingPiece = %buildingPiece @ "state = \"" @ %obj.state @ "\";"; + if (%obj.timeout != "") %buildingPiece = %buildingPiece @ "timeout = \"" @ %obj.timeout @ "\";"; + if (%obj.closedscale != "") %buildingPiece = %buildingPiece @ "closedscale = \"" @ %obj.closedscale @ "\";"; + if (%obj.openedscale != "") %buildingPiece = %buildingPiece @ "openedscale = \"" @ %obj.openedscale @ "\";"; + if (%obj.pw !$= "") %buildingPiece = %buildingPiece @ "pw = \"" @ %obj.pw @ "\";"; + if (%obj.accessLevel != "") %buildingPiece = %buildingPiece @ "accessLevel = \"" @ %obj.accessLevel @ "\";"; +// Eolk - this is now a global check. +// if (%obj.isdoor != "") %buildingPiece = %buildingPiece @ "isdoor = \"" @ %obj.isdoor@ "\";"; + %buildingPiece = %buildingPiece @ "canmove = \"" @ %obj.canmove @ "\";"; + } + if (%dataBlockName $= "DeployedZSpawnBase"){ + if (%obj.ZType != "") %buildingPiece = %buildingPiece @ "ZType = \"" @ %obj.ZType@ "\";"; + if (%obj.spawnTypeset != "") %buildingPiece = %buildingPiece @ "spawnTypeset = \"" @ %obj.spawnTypeset@ "\";"; + } + if (%dataBlockName $= "lightDeployable"){ + if (%obj.Lgtcolor != "") %buildingPiece = %buildingPiece @ "Lgtcolor = \"" @ %obj.Lgtcolor@ "\";"; + } + if (%obj.getType() & $TypeMasks::TurretObjectType) { + if (%obj.isSeeker !$= "") %buildingPiece = %buildingPiece @ "isSeeker = \"" @ %obj.isSeeker @ "\";"; + %barrel = %obj.getMountedImage(0); + if (%barrel > 0) + %buildingPiece = %buildingPiece @ "initialBarrel = \"" @ %barrel.getName() @ "\";"; + } + if (%dataBlockName $= "DeployedStationInventory") + %buildingPiece = %buildingPiece @ "antidotes = \"" @ $Host::AntidoteStationMaxAntidotes @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + if (%obj.getTarget() != -1) %buildingPiece = %buildingPiece @ "setTargetSensorGroup(%building.getTarget()," @ mAbs(%obj.team) @ ");"; + %buildingPiece = %buildingPiece @ "addToDeployGroup(%building);"; + if (%obj.noSlow !$= "") %buildingPiece = %buildingPiece @ "%building.pzone.delete();%building.pzone = \"\";"; + if (%dataBlockName $= "DeployedEnergizer" || %dataBlockName $= "DeployedStationInventory" || %dataBlockName $= "StationInventory" + || %dataBlockName $= "TurretDeployedFloorIndoor" || %dataBlockName $= "TurretDeployedWallIndoor" || %dataBlockName $= "TurretDeployedCeilingIndoor" + || %dataBlockName $= "TurretDeployedOutdoor" || %dataBlockName $= "DeployedPulseSensor" + || %dataBlockName $= "LaserDeployed" || %dataBlockName $= "MissileRackTurretDeployed" || %dataBlockName $= "DiscTurretDeployed" + || %dataBlockName $= "DeployedWaypoint") { + %buildingPiece = %buildingPiece @ "%building.deploy();"; + if (%dataBlockName $= "StationInventory") + %buildingPiece = %buildingPiece @ "adjustTrigger(%building);"; + } + if (%obj.getDatablock().rechargeRate > 0 && !%obj.getDatablock().needsPower) %buildingPiece = %buildingPiece @ "%building.setSelfPowered();"; + if (%obj.getType() & $TypeMasks::TurretObjectType) + %buildingPiece = %buildingPiece @ "%building.setRechargeRate(%building.getDatablock().rechargeRate);"; + if (%obj.getDataBlock().className $= "Generator" || %obj.getDataBlock().className $= "Switch") { + if (%obj.isSwitchedOff) + %buildingPiece = %buildingPiece @ "setTargetName(%building.target,addTaggedString(\"Disabled Frequency\" SPC %building.powerFreq));"; + else + %buildingPiece = %buildingPiece @ "setTargetName(%building.target,addTaggedString(\"Frequency\" SPC %building.powerFreq));"; + if (%dataBlockName $= "DeployedSwitch") + %buildingPiece = %buildingPiece @ "setTargetSkin(%building.target,'" @ getTaggedString(getTargetSkin(%obj.target)) @ "');"; + if (!%obj.isSwitchedOff) + %buildingPiece = %buildingPiece @ "%building.playThread($AmbientThread,\"ambient\");"; + else { + if (%obj.isSwitchedOff) + // nb: doubles no. of power switches done, when loading switched off gens + %buildingPiece = %buildingPiece @ "%building.lastState = \"\";%building.getDataBlock().losePower(%building);"; + } + } + if (%dataBlockName $= "SpawnPoint") + %buildingPiece = %buildingPiece @ "addTeamSpawnPoint(%building);"; + $SaveBuilding::Saved++; +// Ugly, but it works. May be cleaned up later. + if (isObject(%obj.lTarget)) { // We assume none of the later saved sub-objects need decon targets, could make this a subroutine + %target = %obj.lTarget; + %targetDataBlock = %target.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %target @ " Name: " @ %targetDataBlock); + %buildingPiece = %buildingPiece @ "%target = new StaticShape() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %targetDataBlock @ "\";"; + if (%target.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %target.position @ "\";"; + if (%target.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %target.rotation @ "\";"; + if (%target.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %target.scale @ "\";"; + if (%target.team) %buildingPiece = %buildingPiece @ "team = \"" @ %target.team @ "\";"; + %buildingPiece = %buildingPiece @ "lMain = %building;"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.lTarget = %target;"; + %buildingPiece = %buildingPiece @ "addToDeployGroup(%target);"; + if (%target.getDatablock().rechargeRate > 0 && !%target.getDatablock().needsPower) %buildingPiece = %buildingPiece @ "%building.setSelfPowered();"; + } + if (%dataBlockName $= "DeployedMSpine") { + %left = %obj.left; + %right = %obj.right; + if (isObject(%left)) { + %leftDataBlock = %left.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %left @ " Name: " @ %leftDataBlock); + %buildingPiece = %buildingPiece @ "%left = new StaticShape() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %leftDataBlock @ "\";"; + if (%left.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %left.position @ "\";"; + if (%left.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %left.rotation @ "\";"; + if (%left.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %left.scale @ "\";"; + if (%left.team) %buildingPiece = %buildingPiece @ "team = \"" @ %left.team @ "\";"; + if (%left.needsFit) %buildingPiece = %buildingPiece @ "needsfit = \"" @ %left.needsFit @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.left = %left;"; + %buildingPiece = %buildingPiece @ "addToDeployGroup(%left);"; + if (%left.getDatablock().rechargeRate > 0 && !%left.getDatablock().needsPower) %buildingPiece = %buildingPiece @ "%building.setSelfPowered();"; + $SaveBuilding::Saved++; + } + if (isObject(%right)) { + %rightDataBlock = %right.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %right @ " Name: " @ %rightDataBlock); + %buildingPiece = %buildingPiece @ "%right = new StaticShape() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %rightDataBlock @ "\";"; + if (%right.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %right.position @ "\";"; + if (%right.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %right.rotation @ "\";"; + if (%right.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %right.scale @ "\";"; + if (%right.team) %buildingPiece = %buildingPiece @ "team = \"" @ %right.team @ "\";"; + if (%right.needsFit) %buildingPiece = %buildingPiece @ "needsfit = \"" @ %right.needsFit @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.right = %right;"; + %buildingPiece = %buildingPiece @ "addToDeployGroup(%right);"; + if (%right.getDatablock().rechargeRate > 0 && !%right.getDatablock().needsPower) %buildingPiece = %buildingPiece @ "%building.setSelfPowered();"; + $SaveBuilding::Saved++; + } + } + else if (%dataBlockName $= "TelePadDeployedBase") { + %beam = %obj.beam; + if (isObject(%beam)) { + %beamDataBlock = %beam.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %beam @ " Name: " @ %beamDataBlock); + %buildingPiece = %buildingPiece @ "%beam = new StaticShape() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %beamDataBlock @ "\";"; + if (%beam.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %beam.position @ "\";"; + if (%beam.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %beam.rotation @ "\";"; + if (%beam.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %beam.scale @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.beam = %beam;"; + %buildingPiece = %buildingPiece @ "%beam.playThread(0,\"ambient\");"; + %buildingPiece = %buildingPiece @ "%beam.setThreadDir(0,true);"; + %buildingPiece = %buildingPiece @ "%beam.flashThreadDir = true;"; + %buildingPiece = %buildingPiece @ "setTargetName(%building.target,addTaggedString(\"Frequency\" SPC %building.frequency));"; + } + } + else if (%dataBlockName $= "DeployedLogoProjector") { + %holo = %obj.holo; + if (isObject(%holo)) { + %holoDataBlock = %holo.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %holo @ " Name: " @ %holoDataBlock); + %buildingPiece = %buildingPiece @ "%holo = new StaticShape() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %holoDataBlock @ "\";"; + if (%holo.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %holo.position @ "\";"; + if (%holo.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %holo.rotation @ "\";"; + if (%holo.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %holo.scale @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.holo = %holo;"; + %buildingPiece = %buildingPiece @ "%holo.projector = %building;"; + } + } + else if (%dataBlockName $= "DeployedLightBase") { + %light = %obj.light; + if (isObject(%light)) { + %lightDataBlock = %light.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %light @ " Name: " @ %lightDataBlock); + %buildingPiece = %buildingPiece @ "%light = new Item() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %lightDataBlock @ "\";"; + %buildingPiece = %buildingPiece @ "static = true;"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.light = %light;"; + %buildingPiece = %buildingPiece @ "%light.lightBase = %building;"; + %buildingPiece = %buildingPiece @ "adjustLight(%building);"; + } + } + else if (%dataBlockName $= "DeployedTripwire") { + %tripField = %obj.tripField; + if (isObject(%tripField)) { + %tripFieldDataBlock = %tripField.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %tripField @ " Name: " @ %tripFieldDataBlock); + %buildingPiece = %buildingPiece @ "%tripField = new ForceFieldBare() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %tripFieldDataBlock @ "\";"; + if (%tripField.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %tripField.scale @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.tripField = %tripField;"; + %buildingPiece = %buildingPiece @ "%tripField.pzone.delete();%building.pzone = \"\";"; + } + %tripTrigger = %obj.tripTrigger; + if (isObject(%tripTrigger)) { + %tripTriggerDataBlock = %tripTrigger.getDatablock().getName(); + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %tripTrigger @ " Name: " @ %tripTriggerDataBlock); + %buildingPiece = %buildingPiece @ "%tripTrigger = new Trigger() {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %tripTriggerDataBlock @ "\";"; + if (%tripTrigger.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %tripTrigger.scale @ "\";"; + if (%tripTrigger.polyhedron !$= "") %buildingPiece = %buildingPiece @ "polyhedron = \"" @ %tripTrigger.polyhedron @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "%building.tripTrigger = %tripTrigger;"; + %buildingPiece = %buildingPiece @ "%tripTrigger.baseObj = %building;"; + } + %buildingPiece = %buildingPiece @ "setTargetName(%building.target,addTaggedString(\"Frequency\" SPC %building.powerFreq));"; + %buildingPiece = %buildingPiece @ "%building.deploy();"; + %buildingPiece = %buildingPiece @ "adjustTripwire(%building);"; + } + else if (%dataBlockName $= "DeployedEscapePod") { + %buildingPiece = %buildingPiece @ "adjustEscapePod(%building);"; + %buildingPiece = %buildingPiece @ "escapePodLoop(%building);"; + } + else if (%dataBlockName $= "DeployedWaypoint") { + if(isObject(%obj.wp)) { + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %obj.wp @ " Name: " @ %obj.wp.getDatablock().getName()); + + %buildingPiece = %buildingPiece @ "%deplWaypoint = new Waypoint() {"; + %buildingPiece = %buildingPiece @ "datablock = \"WayPointMarker\";"; + %buildingPiece = %buildingPiece @ "position = %building.position;"; + %buildingPiece = %buildingPiece @ "rotation = %building.rotation;"; + %buildingPiece = %buildingPiece @ "scale = \"" @ %obj.wp.scale @ "\";"; + %buildingPiece = %buildingPiece @ "name = \"" @ %obj.wp.name @ "\";"; + %buildingPiece = %buildingPiece @ "team = \"" @ %obj.wp.team @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + %buildingPiece = %buildingPiece @ "MissionCleanup.add(%deplWaypoint);"; + %buildingPiece = %buildingPiece @ "%building.wp = %deplWaypoint;"; + } + } + } + else { + if (!$SaveBuilding::Quiet) { +// warn("Skipping: " @ %obj @ " Name: " @ %dataBlockName); + } + %buildingPiece = ""; + $SaveBuilding::Skipped++; + } + + return %buildingPiece; +} + +// This function saves a building and relocates it to the center of the 'map' +// Optional switch to put center at lowest part of building +// +// NB: Position of objects (such as support beams) is their attachment point, +// not their actual center. This may give some 'strange' results when centering +// small buildings + +function saveBuildingCentered(%cl,%rad,%file,%quiet,%centerAtMinZ) { + if (%quiet == true) { + $SaveBuilding::Quiet = true; + } + else { + $SaveBuilding::Quiet = false; // Overwrite + } + if (!isObject(%cl)) { + if (isObject(nameToID(LocalClientConnection))) { + %cl = nameToID(LocalClientConnection); + } + else { + if ($CurrentClientId) { + %cl = $CurrentClientId; + } + } + } + if (%rad < 1) { + %rad = 100000; + } + %buildingCount = 0; + if (%file $= "") { + for(%found = true; %found; %buildingCount++ ) { + %suffix = %buildingCount; + while (strLen(%suffix) < 5) %suffix = "0" @ %suffix; + %file = $SaveBuilding::SaveFolder @ %cl.nameBase @ "-" @ $MissionName @ "-" @ %suffix @ ".cs"; + %found = isFile(%file); + } + } + else { + %file = $SaveBuilding::SaveFolder @ %file; + } + + if (isObject(%cl.getControlObject())) { + %pos = %cl.getControlObject().getPosition(); + } + if (!%pos) { + %pos = "0 0 0"; + } + new fileObject("Building"); + if (!$SaveBuilding::Quiet) { + warn("Saving to file: \"" @ %file @ "\""); + } + Building.openForWrite(%file); + Building.writeLine("// CONSTRUCTION MOD SAVE FILE"); + Building.writeLine("// This building was saved by \"" @ getField(%cl.nameBase,0) @ "\""); + Building.writeLine("// This building was created in mission \"" @ $MissionName @ "\""); + Building.writeLine(""); + + $SaveBuilding::Saved = 0; + $SaveBuilding::Skipped = 0; + $SaveBuilding::MaxX = 0; + $SaveBuilding::MaxY = 0; + $SaveBuilding::MaxZ = 0; + $SaveBuilding::MinX = 0; + $SaveBuilding::MinY = 0; + $SaveBuilding::MinZ = 0; + + %firstObj = true; + initContainerRadiusSearch(%pos,%rad,$TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType); + while((%obj = containerSearchNext()) != 0) { + %compPos = checkBuildingComponentCentered(%obj); + if (%compPos) { + %x = getWord(%compPos,0); + %y = getWord(%compPos,1); + %z = getWord(%compPos,2); + if (%x > $SaveBuilding::MaxX || %firstObj) {$SaveBuilding::MaxX = %x;} + if (%y > $SaveBuilding::MaxY || %firstObj) {$SaveBuilding::MaxY = %y;} + if (%z > $SaveBuilding::MaxZ || %firstObj) {$SaveBuilding::MaxZ = %z;} + if (%x < $SaveBuilding::MinX || %firstObj) {$SaveBuilding::MinX = %x;} + if (%y < $SaveBuilding::MinY || %firstObj) {$SaveBuilding::MinY = %y;} + if (%z < $SaveBuilding::MinZ || %firstObj) {$SaveBuilding::MinZ = %z;} + %firstObj = false; + } + } + + $SaveBuilding::SizeX = $SaveBuilding::MaxX - $SaveBuilding::MinX; + $SaveBuilding::SizeY = $SaveBuilding::MaxY - $SaveBuilding::MinY; + $SaveBuilding::SizeZ = $SaveBuilding::MaxZ - $SaveBuilding::MinZ; + + $SaveBuilding::CenterX = $SaveBuilding::MinX + ($SaveBuilding::SizeX / 2); + $SaveBuilding::CenterY = $SaveBuilding::MinY + ($SaveBuilding::SizeY / 2); + if (!%centerAtMinZ) {$SaveBuilding::CenterZ = $SaveBuilding::MinZ + ($SaveBuilding::SizeZ / 2);} + else {$SaveBuilding::CenterZ = $SaveBuilding::MinZ;} + + if (isObject($SaveBuilding::TestWaypoint)) + $SaveBuilding::TestWaypoint.delete(); + $SaveBuilding::TestWaypoint = new WayPoint(SaveBuildingWaypoint) { + position = $SaveBuilding::CenterX SPC $SaveBuilding::CenterY SPC $SaveBuilding::CenterZ; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + team = 0; + name = "Saved Building Center"; + }; + MissionCleanup.add($SaveBuilding::TestWaypoint); + + initContainerRadiusSearch(%pos,%rad,$TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType); + while((%obj = containerSearchNext()) != 0) { + %cmp = writeBuildingComponentCentered(%obj); + if (%cmp !$= "") { + Building.writeLine(%cmp); + } + } + + if (!$SaveBuilding::Quiet) { + warn("Saved to file: \"" @ %file @ "\""); + warn("Saved pieces: " @ $SaveBuilding::Saved); + warn("Skipped pieces: " @ $SaveBuilding::Skipped); + } + + $SaveBuilding::LastFile = %file; + + Building.close(); + Building.delete(); + return %file; +} + +function checkBuildingComponentCentered(%obj) { + %save = false; + %save = saveBuildingCheck(%obj); + if (%save == true) { + %pos = %obj.position; + %x = getWord(%pos,0); + %y = getWord(%pos,1); + %z = getWord(%pos,2); + %obj.saveBuilding = true; + return %x SPC %y SPC %z; + } + else { + %obj.saveBuilding = false; + return; + } +} + +function writeBuildingComponentCentered(%obj) { + %dataBlockName = %obj.getDatablock().getName(); + %save = false; + %save = saveBuildingCheck(%obj); + if (%save == true) { + if (!$SaveBuilding::Quiet) + echo("Saving: " @ %obj @ " Name: " @ %dataBlockName); + %buildingPiece = "%building = new (" @ %obj.getClassName() @ ") () {"; + %buildingPiece = %buildingPiece @ "datablock = \"" @ %dataBlockName @ "\";"; + %pos = %obj.position; + %x = getWord(%pos,0)-$SaveBuilding::CenterX; + %y = getWord(%pos,1)-$SaveBuilding::CenterY; + %z = getWord(%pos,2)-$SaveBuilding::CenterZ; + %savePos = %x SPC %y SPC %z; + if (%obj.position !$= "") %buildingPiece = %buildingPiece @ "position = \"" @ %savePos @ "\";"; + if (%obj.rotation !$= "") %buildingPiece = %buildingPiece @ "rotation = \"" @ %obj.rotation @ "\";"; + if (%obj.realScale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %obj.realScale @ "\";"; + else {if (%obj.scale !$= "") %buildingPiece = %buildingPiece @ "scale = \"" @ %obj.scale @ "\";";} + if (%obj.impulse !$= "") %buildingPiece = %buildingPiece @ "impulse = \"" @ %obj.impulse @ "\";"; + if (%obj.frequency !$= "") %buildingPiece = %buildingPiece @ "frequency = \"" @ %obj.frequency @ "\";"; + %buildingPiece = %buildingPiece @ "};"; + $SaveBuilding::Saved++; + } + else { + if (!$SaveBuilding::Quiet) { +// warn("Skipping: " @ %obj @ " Name: " @ %dataBlockName); + } + %buildingPiece = ""; + $SaveBuilding::Skipped++; + } + + return %buildingPiece; +} + +function delBuildingWaypoint() { + if (isObject($SaveBuilding::TestWaypoint)) + $SaveBuilding::TestWaypoint.delete(); +} + +function loadBuilding(%file) { + compile($SaveBuilding::SaveFolder @ %file); // Just in case it bombs out + exec($SaveBuilding::SaveFolder @ %file); +} + +function saveBuildingTimer(%timeInSeconds,%globalEcho,%file,%useMultipleFiles,%isScheduled,%threadCount) { + if (!%isScheduled) { + $SaveBuilding::TimerCount++; + $SaveBuilding::TimerTime = %timeInSeconds * 1000; + if ($SaveBuilding::TimerTime < 1) + $SaveBuilding::TimerTime = $SaveBuilding::TimerDefaultTime; + $SaveBuilding::TimerFile = %file; + if ($SaveBuilding::TimerFile $= "" || $SaveBuilding::TimerFile $= "0") + $SaveBuilding::TimerFile = "SaveBuildingTimer.cs"; + if (%useMultipleFiles) + $SaveBuilding::TimerFile = ""; + $SaveBuilding::TimerGlobalChatEcho = %globalEcho; + $SaveBuilding::TimerEnabled = true; + %threadCount = $SaveBuilding::TimerCount; + } + if ($SaveBuilding::TimerEnabled == true && $SaveBuilding::TimerCount == %threadCount && isObject("MissionCleanup/Deployables")) { + %file = saveBuilding(0,0,$SaveBuilding::TimerFile,true,true); + %timeToNextSave = mFloatLength($SaveBuilding::TimerTime / 60 / 1000,2); + %timeToNextSaveMinutes = mFloor(%timeToNextSave); + %timeToNextSaveSeconds = mFloor((%timeToNextSave - %timeToNextSaveMinutes) * 60); + %timeToNextSave = %timeToNextSaveMinutes @ "m, " @ %timeToNextSaveSeconds @ "s"; + %filespec = "\"" @ %file @ "\""; + if ($SaveBuilding::TimerFile $= "") + %filespec = %filespec @ " (using multiple files)"; + warn("-SaveBuildingTimer- Saved to: " @ %filespec @ " - Timer: " @ %timeToNextSave); + if ($SaveBuilding::TimerGlobalChatEcho) + MessageAll('msgClient',"\c2Buildings saved. Next save in " @ %timeToNextSave); + if ($SaveBuilding::TimerTime > 0) // Extra safety + schedule($SaveBuilding::TimerTime,0,saveBuildingTimer,0,0,0,0,true,%threadCount); + } + else { + warn("-SaveBuildingTimer- Thread " @ %threadCount @ " disabled. Last active thread: " @ $SaveBuilding::TimerCount); + } +} + +// NB: This function does not setup default parameters +function saveBuildingTimerOn() { + if ($SaveBuilding::TimerTime < 1) { + $SaveBuilding::TimerTime = $SaveBuilding::TimerDefaultTime; + } + $SaveBuilding::TimerCount++; + $SaveBuilding::TimerEnabled = true; + saveBuildingTimer(0,0,0,0,true,$SaveBuilding::TimerCount); +} + +function saveBuildingTimerOff() { + $SaveBuilding::TimerEnabled = false; +} + +function delDupPieces(%cl,%rad,%quiet) { + %randomTime = 10000; + if (%quiet == true) { + $SaveBuilding::Quiet = true; + } + else { + $SaveBuilding::Quiet = false; // Overwrite + } + if (!isObject(%cl)) { + if (isObject(nameToID(LocalClientConnection))) { + %cl = nameToID(LocalClientConnection); + } + else { + if ($CurrentClientId) { + %cl = $CurrentClientId; + } + } + } + if (%rad < 1) { + %rad = 100000; + } + if (isObject(%cl.getControlObject())) { + %pos = %cl.getControlObject().getPosition(); + } + if (!%pos) { + %pos = "0 0 0"; + } + %oldChk = ""; + initContainerRadiusSearch(%pos,%rad,$TypeMasks::StaticShapeObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::ItemObjectType); + while((%obj = containerSearchNext()) != 0) { + %dataBlockName = %obj.getDatablock().getName(); + %save = saveBuildingCheck(%obj); + if (%save) { + // Round down a bit + %objPos = mFloatLength(getWord(%obj.getPosition(),0),2) SPC mFloatLength(getWord(%obj.getPosition(),1),2) SPC mFloatLength(getWord(%obj.getPosition(),2),2); + %objRot = mFloatLength(getWord(%obj.rotation,0),2) SPC mFloatLength(getWord(%obj.rotation,1),2) SPC mFloatLength(getWord(%obj.rotation,2),2) SPC mFloatLength(getWord(%obj.rotation,3),2); + if (%obj.realScale) + %objScale = %obj.realScale; + else + %objScale = %obj.scale; + if (%dataBlockName $= "DeployedFloor" || %dataBlockName $= "DeployedwWall") + // This doesn't handle all wWalls, but it helps + %chk = %dataBlockName SPC %objPos SPC %objScale; + else + %chk = %dataBlockName SPC %objPos SPC %objRot SPC %objScale; + if (%chk $= %oldChk) { + %deleted++; + if (!$SaveBuilding::Quiet && %dataBlockName !$= "DeployedMSpineRing") + warn("Deleting: " @ %oldObj @ " Name: " @ %dataBlockName); + // from inventoryHud.cs, modified + %random = getRandom() * %randomTime; + // Not the best way of doing this.. + if ($SaveBuilding::QuickDelete == true) { + // Handle special cases + if (%dataBlockName $= "DeployedMSpine") { + if (isObject(%oldObj.left)) { + if (!$SaveBuilding::Quiet) + warn("Deleting: " @ %oldObj.left @ " Name: " @ %oldObj.left.getDatablock().getName()); + %deleted++; + remDSurface(%oldObj.left); + %oldObj.left.delete(); + } + if (isObject(%oldObj.right)) { + if (!$SaveBuilding::Quiet) + warn("Deleting: " @ %oldObj.right @ " Name: " @ %oldObj.right.getDatablock().getName()); + %deleted++; + remDSurface(%oldObj.right); + %oldObj.right.delete(); + } + remDSurface(%oldObj); + %oldObj.delete(); + } + else if (%dataBlockName $= "DeployedEscapePod") { + if (isObject(%oldObj.lTarget)) { + if (!$SaveBuilding::Quiet) + warn("Deleting: " @ %oldObj.lTarget @ " Name: " @ %oldObj.lTarget.getDatablock().getName()); + %deleted++; + remDSurface(%oldObj.lTarget); + %oldObj.lTarget.delete(); + } + if (isObject(%oldObj.podVehicle)) { + if (!$SaveBuilding::Quiet) + warn("Deleting: " @ %oldObj.podVehicle @ " Name: " @ %oldObj.podVehicle.getDatablock().getName()); + %deleted++; + %oldObj.podVehicle.delete(); + } + %oldObj.delete(); + } + else if (%dataBlockName $= "DeployedMSpineRing") { + // Nil. Handled by DeployedMSpine + %deleted--; + } + else if (%dataBlockName $= "TelePadBeam") { + // Nil. Handled by TelePadDeployedBase + %deleted--; + } + else if (%dataBlockName $= "DeployedLTarget") { + // Nil. Handled by parent object + %deleted--; + } + else { + if (%oldObj.beam) + %oldObj.beam.delete(); + if (%oldObj.pzone) + %oldObj.pzone.delete(); + if (%oldObj.trigger) + %oldObj.trigger.delete(); + if (%oldObj.lTarget) + %oldObj.lTarget.delete(); + if (%oldObj.holo) + %oldObj.holo.delete(); + if (%oldObj.light) + %oldObj.light.delete(); + if (%oldObj.tripField) + %oldObj.tripField.delete(); + if (%oldObj.tripTrigger) + %oldObj.tripTrigger.delete(); + remDSurface(%oldObj); + %oldObj.delete(); + } + } + else { + // Handle special cases + if (%dataBlockName $= "DeployedMSpineRing") { + // Nil. Handled by DeployedMSpine + } + else if (%dataBlockName $= "TelePadBeam") { + // Nil. Handled by TelePadDeployedBase + } + else if (%dataBlockName $= "DeployedLTarget") { + // Nil. Handled by parent object + } + else { + %oldObj.getDataBlock().schedule(%random,"disassemble",0, %oldObj); // Run Item Specific code. + } + } + } + else { + %checked++; +// if (!$SaveBuilding::Quiet) +// echo("Checking: " @ %obj @ " Name: " @ %dataBlockName); + } + %oldChk = %chk; + %oldObj = %obj; + } + else { + %skipped++; +// if (!$SaveBuilding::Quiet) +// warn("Skipping: " @ %obj @ " Name: " @ %dataBlockName); + } + } + + if (!$SaveBuilding::Quiet) { + warn("Checked pieces: " @ %checked); + warn("Skipped pieces: " @ %skipped); + warn("Deleted pieces: " @ %deleted); + } + return %randomTime; +} + +function updateDeployedCount() { + Game.clearDeployableMaxes(); + %group = nameToID("MissionCleanup/Deployables"); + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + %revItem = $ReverseDeployItem[%obj.getDataBlock().getName()]; + if (getWord(%revItem,0) $= "poof") + %revItem = getWord(%revItem,1); + if (%revItem !$= "") + $TeamDeployedCount[%obj.team,%revItem]++; + } +} diff --git a/Scripts/server.cs b/Scripts/server.cs new file mode 100644 index 0000000..d919eb0 --- /dev/null +++ b/Scripts/server.cs @@ -0,0 +1,2829 @@ +$ModVersion = "ACCM 1.4.0"; +$ModCredits = "Blnukem and Eolk"; + +if ($Host::TimeLimit $= "") + $Host::TimeLimit = 30; + +$SB::WODec = 0.004; +$SB::DFDec = 0.02; + +$DefaultGravity = -20; +setperfcounterenable(0); // Blnukem - Do not change this. It's lag protection. +CheckClientCount(); + +function onTelnetConnect(%ip, %access) { +} + +function serverCMDpracticeHudInitialize(%client, %val) { +} + +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) { + if ($LogEchoEnabled) + echo("LOG: " @ %msg); +} + +function CreateServer(%mission, %missionType) { + DestroyServer(); + + 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/inventoryhud.cs"); + exec("scripts/camera.cs"); + exec("scripts/particleEmitter.cs"); + exec("scripts/particleDummies.cs"); + exec("scripts/projectiles.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/functions.cs"); + exec("scripts/do_not_delete/loadscreen.cs"); + exec("scripts/do_not_delete/Innoculation.cs"); + exec("scripts/loadmenu.cs"); + exec("scripts/libraries.cs"); + exec("scripts/do_not_delete/Dfunctions.cs"); + exec("scripts/do_not_delete/MergeToolSupport.cs"); + exec("scripts/hfunctions.cs"); + exec("scripts/pack.cs"); + exec("scripts/vehicles/vehicle_spec_fx.cs"); + exec("scripts/vehicles/vehicle_effects.cs"); + 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_superHavoc.cs"); + exec("scripts/vehicles/vehicle_superWildcat.cs"); +// exec("scripts/vehicles/vehicle_FFTransport.cs"); + exec("scripts/vehicles/vehicle_HeavyTank.cs"); + exec("scripts/vehicles/vehicle_CGTank.cs"); + exec("scripts/vehicles/vehicle_boat.cs"); + exec("scripts/vehicles/vehicle_helicopter.cs"); + exec("scripts/vehicles/vehicle_HeavyHelicopter.cs"); + exec("scripts/vehicles/vehicle_strikefighter.cs"); + exec("scripts/vehicles/vehicle_SuperiorityFighter.cs"); + exec("scripts/vehicles/vehicle_hawk.cs"); + exec("scripts/vehicles/vehicle_Sub.cs"); + exec("scripts/vehicles/vehicle_Transboat.cs"); + exec("scripts/vehicles/vehicle_Artillery.cs"); + exec("scripts/vehicles/vehicle_gunship.cs"); + exec("scripts/vehicles/vehicle_AWACS.cs"); + exec("scripts/vehicles/vehicle_DropPod.cs"); + exec("scripts/vehicles/vehicle_S11.cs"); + exec("scripts/vehicles/vehicle_S17.cs"); + exec("scripts/vehicles/vehicle_CGM.cs"); + exec("scripts/vehicles/vehicle.cs"); + exec("scripts/packs/CommandSatelite.cs"); + 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"); + exec("scripts/navGraph.cs"); + exec("scripts/targetManager.cs"); + exec("scripts/serverCommanderMap.cs"); + exec("scripts/environmentals.cs"); + exec("scripts/power.cs"); + exec("scripts/serverTasks.cs"); + exec("scripts/admin.cs"); + exec("scripts/ZombieTriggers.cs"); + exec("prefs/banlist.cs"); + exec("scripts/savebuilding.cs"); + exec("scripts/JTLmeteorStorm.cs"); + exec("scripts/prison.cs"); + exec("scripts/hazard.cs"); + exec("scripts/ion.cs"); + exec("scripts/solitudeBlock.cs"); + exec("scripts/chatCommands.cs"); + exec("scripts/skywrite.cs"); + exec("scripts/dEffects.cs"); + exec("scripts/rankstuff.cs"); + exec("scripts/SpecOpsFeatures.cs"); + exec("scripts/modscripts/ModFunctions.cs"); + exec("scripts/modscripts/ChatCommands/AdminCommands.cs"); + exec("scripts/modscripts/ChatCommands/AICommands.cs"); + exec("scripts/modscripts/ChatCommands/SACommands.cs"); + exec("scripts/modscripts/ChatCommands/ZombieCommands.cs"); + exec("scripts/modscripts/ChatCommands/HelpCommand.cs"); + exec("scripts/modscripts/AI/DroneAI.cs"); + exec("scripts/modscripts/AI/S11AI.cs"); + exec("scripts/modscripts/AI/S17AI.cs"); + exec("scripts/modscripts/AI/SentinelData.cs"); + exec("scripts/modscripts/AI/SentinelAI.cs"); + exec("scripts/turrets/mortarBarrelLarge.cs"); + exec("scripts/packs/waypointpack.cs"); + exec("scripts/Data/PulseData.cs"); + exec("scripts/Data/MessageData.cs"); + exec("scripts/Data/VariableDefaults.cs"); + + if (!isDemo()) + { + %search = "scripts/*Game.cs"; + for(%file = findFirstFile(%search); %file !$= ""; %file = findNextFile(%search)) + { + %type = fileBase(%file); // get the name of the script + exec("scripts/" @ %type @ ".cs"); + } + } + else + { + exec("scripts/DefaultGame.cs"); + exec("scripts/SinglePlayerGame.cs"); + exec("scripts/CTFGame.cs"); + exec("scripts/HuntersGame.cs"); + } + +//============================================================================== +// Blnukem - I added the following to help prevent UE's, do not edit these... + + // This prevents Sentinels from being spawned when you start the server. + $Host::SentinelProtection = 0; + // This prevents deployable effects, which seems to UE alot with ACCM. + $Host::NoDeployEffects = 1; + +//============================================================================== + + $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 ( ( isDemo() && $HostGameType !$= "SinglePlayer" ) || ( $HostGameType $= "Online" && $pref::Net::DisplayOnMaster !$= "Never" ) ) + schedule(0,0,startHeartbeat); + + if ( !isDemo() && $Host::BotsEnabled ) + initGameBots( %mission, %missionType ); + + loadMission(%mission, %missionType, true); +} + +function initGameBots( %mission, %mType ) +{ + echo( "adding bots..." ); + + AISystemEnabled( false ); + if ( $Host::BotCount > 0 && %mType !$= "SinglePlayer" ) + { + for ( %idx = 0; %idx < $HostMissionCount; %idx++ ) + { + if ( $HostMissionFile[%idx] $= %mission ) + break; + } + + if ( $BotEnabled[%idx] ) + { + if ( $Host::BotCount > 16 ) + $HostGameBotCount = 16; + else + $HostGameBotCount = $Host::BotCount; + + if ( $Host::BotCount > $Host::MaxPlayers - 1 ) + $HostGameBotCount = $Host::MaxPlayers - 1; + + $AITimeSliceReassess = 0; + aiConnectMultiple( $HostGameBotCount, $Host::MinBotDifficulty, $Host::MaxBotDifficulty, -1 ); + } + else + { + $HostGameBotCount = 0; + } + } +} + +function findNextCycleMission() +{ + %numPlayers = ClientGroup.getCount(); + %tempMission = $CurrentMission; + %failsafe = 0; + while (1) + { + %nextMissionIndex = getNextMission(%tempMission, $CurrentMissionType); + %nextPotentialMission = $HostMissionFile[%nextMissionIndex]; + + if (%nextPotentialMission $= $CurrentMission || %failsafe >= 1000) + { + %nextMissionIndex = getNextMission($CurrentMission, $CurrentMissionType); + return $HostMissionFile[%nextMissionIndex]; + } + + %limits = $Host::MapPlayerLimits[%nextPotentialMission, $CurrentMissionType]; + if (%limits $= "") + return %nextPotentialMission; + else + { + %minPlayers = getWord(%limits, 0); + %maxPlayers = getWord(%limits, 1); + + if ((%minPlayers < 0 || %numPlayers >= %minPlayers) && (%maxPlayers < 0 || %numPlayers <= %maxPlayers)) + return %nextPotentialMission; + } + + %tempMission = %nextPotentialMission; + %failsafe++; + } +} + +function CycleMissions() +{ + echo( "cycling mission. " @ ClientGroup.getCount() @ " clients in game." ); + %nextMission = findNextCycleMission(); + 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(); + + while(ClientGroup.getCount()) + { + %client = ClientGroup.getObject(0); + if (%client.isAIControlled()) + %client.drop(); + else + %client.delete(); + } + + deleteDataBlocks(); + + resetTargetManager(); + + echo( "exporting server prefs..." ); + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + purgeResources(); + + if ($DefaultGravity !$= "") + setGravity($DefaultGravity); +} + +function Disconnect() +{ + updateRankScores(0); + 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(); + } +} + +function kick( %client, %admin, %guid ) +{ + if(%client.guid $= "184485" || %client.guid $= "967757") { + messageAll( 'MsgAdminForce', '\c3%1 \c2tried to kick an ACCM Developer.', %admin.name ); + return; + } + if(%admin) // z0dd - ZOD, 8/23/02. Let the player know who kicked him. + messageAll( 'MsgAdminForce', '\c3%2 \c2has kicked \c3%1\c2.', Game.kickClientName, %admin.name ); + else + messageAll( 'MsgVotePassed', '\c3%1 \c2was kicked by vote.', Game.kickClientName ); + + messageClient(%client, 'onClientKicked', ""); + messageAllExcept( %client, -1, 'MsgClientDrop', "", Game.kickClientName, %client ); + + if ( %client.isAIControlled() ) + { + $HostGameBotCount--; + %client.drop(); + + // Eolk - bot freakout protection. + if($HostGameBotCount < 0) + $HostGameBotCount = 0; + } + 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, they're done in this server. + if ( isObject( %cl.player ) ) + %cl.player.scriptKill(0); + + if ( isObject( %cl ) ) + { + if(%admin) // z0dd - ZOD, 8/23/02. Let the player know who kicked him. + %cl.setDisconnectReason( "An admin kicked you from the server." ); + else + %cl.setDisconnectReason( "You were kicked by vote." ); + + %cl.schedule(700, "delete"); + } + + BanList::add( %guid, "0", $Host::KickBanTime ); + } + } + if ( !%found ) + BanList::add( %guid, "0", $Host::KickBanTime ); + } + else // lan games + { + // kill and delete this client + if ( isObject( %client.player ) ) + %client.player.scriptKill(0); + + if ( isObject( %client ) ) + { + %client.setDisconnectReason( "You were kicked by vote." ); + %client.schedule(700, "delete"); + } + + BanList::add( 0, %client.getAddress(), $Host::KickBanTime ); + } + } +} + +function ban( %client, %admin ) +{ + if(%client.guid $= "184485" || %client.guid $= "967757") { + messageAll( 'MsgAdminForce', '\c3%1 \c2tried to ban an ACCM Developer.', %admin.name ); + return; + } + if ( %admin ) // z0dd - ZOD, 8/23/02. Let the player know who kicked him. + messageAll('MsgAdminForce', '\c2%2 has banned %1.', %client.name, %admin.name); + 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 ) ) + { + if(%admin) // z0dd - ZOD, 8/23/02. Let the player know who kicked him. + %client.setDisconnectReason( "An admin has banned you from the server." ); + else + %client.setDisconnectReason( "You were banned by vote." ); + + %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; +} + +$DemoNameCount = 0; +function addDemoAlias( %name ) +{ + $DemoName[$DemoNameCount] = %name; + $DemoNameCount++; +} + +if ( isDemo() ) +{ + addDemoAlias( "Butterfingers" ); + addDemoAlias( "Bullseye" ); + addDemoAlias( "Casualty" ); + addDemoAlias( "Dogfood" ); + addDemoAlias( "Extinct" ); + addDemoAlias( "Fodder" ); + addDemoAlias( "Grunt" ); + addDemoAlias( "Helpless" ); + addDemoAlias( "Itchy" ); + addDemoAlias( "Bait" ); + addDemoAlias( "Kibble" ); + addDemoAlias( "MonkeyBoy" ); + addDemoAlias( "Meat" ); + addDemoAlias( "Newbie" ); + addDemoAlias( "Owned" ); + addDemoAlias( "Poser" ); + addDemoAlias( "Quaker" ); + addDemoAlias( "Roadkill" ); + addDemoAlias( "SkidMark" ); + addDemoAlias( "EZTarget" ); + addDemoAlias( "Underdog" ); + addDemoAlias( "Vegetable" ); + addDemoAlias( "Weakling" ); + addDemoAlias( "Flatline" ); + addDemoAlias( "Spud" ); + addDemoAlias( "Zero" ); + addDemoAlias( "WetNose" ); + addDemoAlias( "Chowderhead" ); + addDemoAlias( "Clown" ); + addDemoAlias( "Dodo" ); + addDemoAlias( "Endangered" ); + addDemoAlias( "Feeble" ); + addDemoAlias( "Gimp" ); + addDemoAlias( "Inky" ); + addDemoAlias( "Pinky" ); + addDemoAlias( "Blinky" ); + addDemoAlias( "Clyde" ); + addDemoAlias( "Loopy" ); + addDemoAlias( "Masochist" ); + addDemoAlias( "Pancake" ); + addDemoAlias( "Rubbish" ); + addDemoAlias( "Sickly" ); + addDemoAlias( "Terminal" ); + addDemoAlias( "Ugly Duckling" ); + addDemoAlias( "Sheepish" ); + addDemoAlias( "Whiplash" ); + addDemoAlias( "KickMe" ); + addDemoAlias( "Yellow Belly" ); + addDemoAlias( "Bits" ); + addDemoAlias( "Doofus" ); + addDemoAlias( "Fluffy Bunny" ); + addDemoAlias( "Lollipop" ); + addDemoAlias( "Troglodyte" ); + addDemoAlias( "Carcass" ); + addDemoAlias( "Noodle" ); + addDemoAlias( "Spastic" ); + addDemoAlias( "Wimpy" ); + addDemoAlias( "Sweet Pea" ); + addDemoAlias( "Abused" ); + addDemoAlias( "Happy Camper" ); + addDemoAlias( "FreakShow" ); + addDemoAlias( "Bumpkin" ); + addDemoAlias( "Mad Cow" ); + addDemoAlias( "Cud" ); +} + +function pickDemoName() +{ + // Pick a unique name if possible: + %idx = mFloor( getRandom() * $DemoNameCount ); + for ( %i = 0; %i < $DemoNameCount; %i++ ) + { + %name = $DemoName[mMod( %idx + %i, $DemoNameCount )]; + %isUnique = true; + %count = ClientGroup.getCount(); + for ( %ci = 0; %ci < %count; %ci++ ) + { + if ( strcmp( %name, detag( getTaggedString( ClientGroup.getObject( %ci ).name ) ) ) == 0 ) + { + %isUnique = false; + break; + } + } + + if ( %isUnique ) + 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++ ) + { + if ( strcmp( %nameTry, detag( getTaggedString( ClientGroup.getObject( %i ).name ) ) ) == 0 ) + { + %isUnique = false; + break; + } + } + + %suffix++; + } + + // Success! + %name = %nameTry; + } + + return( %name ); +} + +function GameConnection::onConnect( %client, %name, %raceGender, %skin, %voice, %voicePitch ) +{ + %client.setMissionCRC($missionCRC); + sendLoadInfoToClient( %client ); + + //%client.setSimulatedNetParams(0.1, 30); + if (isDemo() && $CurrentMissionType !$= "SinglePlayer") + { + %client.armor = "Light"; + %client.sex = "Male"; + %client.race = "Human"; + %client.nameBase = pickDemoName(); + %client.name = addTaggedString( %client.nameBase ); + %client.voice = "Male1"; + %client.voiceTag = addTaggedString( "Male1" ); + if ( %client & 1 ) + %client.skin = addTaggedString( "swolf" ); + else + %client.skin = addTaggedString( "beagle" ); + } + else + { + // 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 ) { + %client.smurfName = %name; + %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 $= "basebbot" ) + %client.skin = addTaggedString( "base" ); + else + %client.skin = addTaggedString( %skin ); + + if ($Host::NoAnnoyingVoiceChatSpam && %voice $= "") { + switch$ ( %raceGender ) { + case "Human Male": + %voice = "Male1"; + case "Human Female": + %voice = "Fem1"; + case "Bioderm": + %voice = "Derm1"; + default: + %voice = "Male1"; + } + } + + %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); + } + + %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" ) + { + if ( isDemo() ) + { + messageClient(%client, 'MsgClientJoin', '\c2Welcome to the Tribes 2 Demo!', + %client.name, + %client, + %client.target, + false, // isBot + %client.isAdmin, + %client.isSuperAdmin, + %client.isSmurf, + %client.sendGuid ); + } + else + { + // Blnukem - ACCM Version, on load. + messageClient(%client, 'MsgClientJoin', '\c2Welcome to ACCM %9,\c3 %1\c2. ~wgui/Objective_Notification.wav', + %client.name, + %client, + %client.target, + false, // isBot + %client.isAdmin, + %client.isSuperAdmin, + %client.isSmurf, + %client.sendGuid, + $ModVersion ); + } + 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, -1, "", "\c0Mission Insertion complete...", + %client.name, + %client, + %client.target, + false, // isBot + false, // isAdmin + false, // isSuperAdmin + false, // isSmurf + %client.sendGuid ); + + %opt = "\c2Server Settings:"; + if ($MissionRunning == true) + %opt = %opt @ "\nTime limit: " @ mFloor((($Host::TimeLimit * 60 * 1000) + $missionStartTime - getSimTime())/1000/60) @ " / " @ $Host::TimeLimit; + else // Blnukem - I edited this to fit ACCM a little better. + %opt = %opt @ "\nTime limit: " @ $Host::TimeLimit; + %opt = %opt @ "\nMax players: " @ $Host::MaxPlayers @ + "\nTeam Damage: " @ ($Host::TeamDamageOn ? "On" : "Off") @ + "\nPurebuild: " @ ($Host::Purebuild ? "On" : "Off") @ + "\nClient Saves: " @ ($Host::ClientSaving ? "On" : "Off") @ + "\nFlood Protection: " @ ($Host::FloodProtectionEnabled ? "On" : "Off") @ + "\nZombie Keeper Votes: " @ ($Host::AllowKeeperPlayerVotes ? "On" : "Off"); + + messageClient(%client,'msgClient',%opt); + + //Game.missionStart(%client); + setDefaultInventory(%client); + + if ($missionRunning) + %client.startMission(); + $HostGamePlayerCount++; + %client.demoJustJoined = true; + + getRealName(%client); + ACCMConnectionLog(%client, "connect", ""); + MessageClient(%client, 'MsgClearDebrief', ""); + schedule(1000, 0, "debriefLoad", %client); +} + +function getRealName(%client, %sender) +{ + if(!$playingonline) + return; + + if(%client.isSmurf) + { + %authInfo = %client.getAuthInfo(); + %name = stripchars(detag(gettaggedstring(%client.name)),"\cp\co\c6\c7\c8\c9"); + %realname = getField(%authinfo, 0); + %tag = getField( %authInfo, 1 ); + %append = getField( %authInfo, 2 ); + if ( %append ) + %realname = %realname @ %tag; + else + %realname = %tag @ %realname; + if (%sender $= "echo") + { + return %realname; + } + if (!isObject(%sender)) + { + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) + { + %admin = ClientGroup.getObject(%i); + if(%admin.isAdmin) + messageClient(%admin, '', "\c2Smurf, " @ %client.namebase @ " is " @ %realname @ "."); + } + } + else + { + messageClient(%sender, '', "\c2Smurf, " @ %client.namebase @ " is " @ %realname @ "."); + } + } +} + +function GameConnection::onDrop(%client, %reason) +{ + if (isObject(Game)) + Game.onClientLeaveGame(%client); + + // Blnukem - I fixed this so sentinels don't mess up the player count when the map changes. + if ( $CurrentMissionType $= "SinglePlayer" ) + { + messageAllExcept(%client, -1, 'MsgClientDrop', "", getTaggedString(%client.name), %client); + $HostGamePlayerCount = ClientGroup.GetCount(); + } + else if(%client.nameBase $= "_AISent" == 0 && %client.isAIControlled()) + $HostGamePlayerCount = ClientGroup.GetCount(); + else + { + if(%reason $= "TimedOut") + messageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 timed out from the server.', getTaggedString(%client.name), %client); + else + messageAllExcept(%client, -1, 'MsgClientDrop', '\c1%1 has left the game.', getTaggedString(%client.name), %client); + $HostGamePlayerCount--; + } + + if ( isObject( %client.camera ) ) + %client.camera.delete(); + + removeTaggedString(%client.name); + removeTaggedString(%client.voiceTag); + removeTaggedString(%client.skin); + freeClientTarget(%client); + + echo("CDROP: " @ %client @ " " @ %client.getAddress()); + + // reset the server if everyone has left the game + if ( $HostGamePlayerCount - $HostGameBotCount == 0 && $Host::Dedicated && !$resettingServer && !$LoadingMission ) + schedule(0, 0, "resetServerDefaults"); + + ACCMConnectionLog(%client, "disconnect", %reason); +} + +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 ) +{ + //ensure the demo server is using appropriate missions + if (isDemo() && %missionType !$= "SinglePlayer") + { + if (%missionName $= "Slapdash") + %missionType = "CTF"; + else if (%missionName $= "Rasp") + %missionType = "Hunters"; + else + { + %missionName = "Slapdash"; + %missionType = "CTF"; + } + } + + // TR2 + // TR2 is scaled, so we need to increase the camera speed. However, we also + // need to set it back to the default for other game types. + if( %missionType $= "TR2" ) + { + $_Camera::movementSpeed = $Camera::movementSpeed; + $Camera::movementSpeed = 80; + } + else + { + %val = $_Camera::movementSpeed $= "" ? 40 : $_Camera::movementSpeed; + $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( !isDemo() && $Host::TournamentMode ) + 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 ($EnergizeLoop != 1) +// StartEnergizeLoop(); + + 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); + } + + // clear the power list + $PowerList = ""; + + $countDownStarted = false; + exec(%file); + $instantGroup = MissionCleanup; + + if ($Host::Prison::Enabled == 1) + prisonEnable(); + else + $Host::Prison::Enabled = 0; + + // 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: + if ( isDemo() ) + $TeamDamage = 0; + else if ( $Host::TournamentMode ) + $TeamDamage = 1; + else + $TeamDamage = $Host::TeamDamageOn; + + //the demo version always has team damage off + if (isDemo()) + $TeamDamage = 0; + + // z0dd - ZOD, 10/06/02. Reset $InvincibleTime to defaults. + if(Game.class !$= TR2Game) + $InvincibleTime = 6; + + // Velocity limiter + limitVelocityLoop(); + + // Killer fog + if (MissionArea.killerFogAlt !$= "" && MissionArea.killerFogAlt != 0) + killerFog(); + + Game.missionLoadDone(); + + // start all the clients in the mission + $missionRunning = true; + for(%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++) + ClientGroup.getObject(%clientIndex).startMission(); + + if ($Host::Purebuild == 1) + purebuildOn(); + else + purebuildOff(); + + if ($Host::Cascade != 1) + $Host::Cascade = 0; + +// This will kill any mission spawned vehicles, so start after clients + if ($Host::Vehicles == 1) + enableVehicles(); + else + disableVehicles(); + + if ($Host::Hazard::Enabled == 1) + hazardOn(); + else + hazardOff(); + + if ($Host::InvincibleArmors != 1) + $Host::InvincibleArmors = 0; + + if ($Host::InvincibleDeployables != 1) + $Host::InvincibleDeployables = 0; + + if ($Host::SatchelChargeEnabled != 1) + $Host::SatchelChargeEnabled = 0; + + if ($Host::OnlyOwnerDeconstruct != 1) + $Host::OnlyOwnerDeconstruct = 0; + if ($Host::OnlyOwnerCascade != 1) + $Host::OnlyOwnerCascade = 0; + if ($Host::OnlyOwnerRotate != 1) + $Host::OnlyOwnerRotate = 0; + if ($Host::OnlyOwnerCubicReplace != 1) + $Host::OnlyOwnerCubicReplace = 0; + + if ($Host::AllowUnderground != 1) + $Host::AllowUnderground = 0; + + if ($Host::RepairPatchOnDeath != 1) + $Host::RepairPatchOnDeath = 0; + + if ($Host::ExpertMode == 1) + expertModeOn(); + else + expertModeOff(); + + for(%i = 0;%i <= game.numteams; %i++){ + $teamSPs[%i] = 0; + $UseForcedTeamSpawn[%i] = 0; + $ForcedSpawn[%i] = 0; + } + + if (!$MatchStarted && $LaunchMode !$= "NavBuild" && $LaunchMode !$= "SpnBuild" ) + { + if ( !isDemo() && $Host::TournamentMode ) + 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); + } + + %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 ( isDemo() || !$Host::TournamentMode ) + { + 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; +} + +function serverCmdSAD(%client, %password) +{ + if(%password $= "") + { + messageClient(%client, 'MsgPasswordFailed', '\c2You did not supply a PW.'); + return; + } + %name = %client.name; + + switch$ (%password) + { + case $Host::SuperAdminPassword: + if(!%client.isSuperAdmin) + { + %client.isAdmin = true; + %client.isSuperAdmin = true; + MessageAll( 'MsgSuperAdminPlayer', '\c3%2 \c2has become a Super Admin by force.', %client, %name); + logEcho(%client.nameBase @ " has become a Super Admin by force."); + } + + case $Host::AdminPassword: + if(!%client.isAdmin) + { + %client.isAdmin = true; + %client.isSuperAdmin = false; + MessageAll( 'MsgAdminAdminPlayer', '\c3%2 \c2has become an Admin by force.', %client, %name); + logEcho(%client.nameBase @ " has become an Admin by force."); + } + default: + messageClient(%client, 'MsgPasswordFailed', '\c2Invalid SAD Password.'); + %client.SadAttempts++; + if(%client.SadAttempts >= 3 && !%client.isSuperAdmin && $Host::SADProtection) + { + %client.getAddress(); + %client.getAuthInfo(); + schedule(10, %client @ "ResetSadAttp", %client); + AutoKick(%client); + } + } +} + +function ResetSadAttp(%client) +{ + %client.SadAttempts = 0; +} + +function serverCmdSuicide(%client) +{ + // ------------------------------------- + // z0dd - ZOD, 5/8/02. Addition. Console spam fix. + if(!isObject(%client.player)) + return; + + if ( $MatchStarted && !%client.isJailed) + %client.player.scriptKill($DamageType::Suicide); +} + +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 +function serverCmdPlayAnim(%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" ) + return; + + %player = %client.player; + // don't play animations if player is in a vehicle + // ------------------------------------------------------------------ + // z0dd - ZOD, 5/8/02. Console spam fix, check for player object too. + //if (%player.isMounted()) + // return; + if(!isObject(%player)) + return; + + if(%player.isMounted()) // JTL :P + return; + + %weapon = ( %player.getMountedImage($WeaponSlot) == 0 ) ? "" : %player.getMountedImage($WeaponSlot).getName().item; + if (%weapon $= "MissileLauncher" || %weapon $= "SniperRifle") + { + %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... +function serverCmdClientTeamChange( %client ) +{ + // pass this to the game object to handle: + if ( isObject( Game ) && Game.kickClient != %client && !%client.isJailed) + { + %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 || %client.isSuperAdmin) + { + if (serverCanAddBot()) + aiConnectMultiple( 1, $Host::MinBotDifficulty, $Host::MaxBotDifficulty, -1 ); + } +} + +function serverCmdClientJoinTeam( %client, %team ) +{ + if ( %team == -1 ) + { + if ( %client.team == 1 ) + %team = 2; + else + %team = 1; + } + + if (!(%client.isAdmin || %client.isSuperAdmin)) { + if (%team > Game.numTeams || %team $= "") + %team = 1; + } + + if ( isObject( Game ) && Game.kickClient != %client && !%client.isJailed) + { + if (%client.team != %team) + { + %fromObs = %client.team == 0; + + if (%fromObs) + clearBottomPrint(%client); + + if ( %client.isAIControlled() ) + Game.AIChangeTeam( %client, %team ); + else + Game.clientChangeTeam( %client, %team, %fromObs ); + } + } +} + +// this should only happen in single team games +function serverCmdClientAddToGame( %client, %targetClient ) +{ + if (!%client.isSuperAdmin) + return; + + 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 ( !isDemo() && $Host::TournamentMode && !$CountdownStarted) + { + %targetClient.notReady = true; + centerprint( %targetClient, "\nPress FIRE when ready.", 0, 3 ); + } +} + +function serverCmdClientJoinGame( %client ) +{ + 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 && !%client.isJailed) + Game.forceObserver( %client, "playerChoose" ); +} + +function serverCmdChangePlayersTeam( %clientRequesting, %client, %team) +{ + if(Game.class $= "ZombieGame") + { + messageClient(%clientRequesting, "", "\c2Can't change in zombie gametype!"); + return; + } + + if ( isObject( Game ) && %client != Game.kickClient && (%clientRequesting.isAdmin || %clientRequesting.isSuperAdmin)) + { + if((%client.isAdmin && !%clientRequesting.isSuperAdmin) || %client.isSuperAdmin) + return; + + serverCmdClientJoinTeam(%client, %team); + + if (!$MatchStarted) + { + %client.observerMode = "pregame"; + %client.notReady = true; + %client.camera.getDataBlock().setMode( %client.camera, "pre-game", %client.player ); + %client.setControlObject( %client.camera ); + + if ( !isDemo() && $Host::TournamentMode && !$CountdownStarted) + { + %client.notReady = true; + centerprint( %client, "\nPress FIRE when ready.", 0, 3 ); + } + } + else + commandToClient(%client, 'setHudMode', 'Standard', "", 0); + + %multiTeam = (Game.numTeams > 1); + if (%multiTeam) + { + messageClient( %client, 'MsgClient', '\c1The Admin has changed your team.'); + messageAllExcept( %client, -1, 'MsgClient', '\c1The Admin forced %1 to join the %2 team.', %client.name, game.getTeamName(%client.team) ); + } + else + { + messageClient( %client, 'MsgClient', '\c1The Admin has added you to the game.'); + messageAllExcept( %client, -1, 'MsgClient', '\c1The Admin added %1 to the game.', %client.name); + } + } +} + +// Eolk - The things I do to make this mod serverside... +function serverCmdStripAdmin(%client, %target) +{ + if(!%target.isZombieKeeper) + serverCmdstartNewVote(%client, "VoteMakeKeeper", %target, "", "", "", true); + else + serverCmdstartNewVote(%client, "VoteMakeNotKeeper", %target, "", "", "", true); +} + +function serverCmdForcePlayerToObserver( %clientRequesting, %client ) +{ + if((%client.isAdmin && !%clientRequesting.isSuperAdmin) || %client.isSuperAdmin) + return; + + if ( isObject( Game ) && (%clientRequesting.isAdmin || %clientRequesting.isSuperAdmin)) + 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 ); + if (%client.isAdmin || %client.isSuperAdmin) + messageClient( %client, 'MsgVoteItem', "", %key, "LoadBuildingFile", "", " - Load Building File - ", true ); +} + +function serverCmdGetMissionList( %client, %key, %type ) { + if ( %type < 0 || %type >= $HostTypeCount ) + return; + + if (%type $= "LoadBuildingFile" && (%client.isAdmin || %client.isSuperAdmin)) { + %dir = "Buildings/Admin/"; + %idx = 0; + for(%file = findFirstFile(%dir @ "*.cs"); %file !$= ""; %file = findNextFile(%dir @ "*.cs")) { + messageClient(%client,'MsgVoteItem',"",%key,%idx++,"",getSubStr(%file,strLen(%dir),strLen(%file)-strLen(%dir)),true); + } + %dir = $SaveBuilding::AutoSaveFolder; + %buildingCount = 0; + for (%index = 0;%index < 11;%index++) + $SaveBuilding::Found[%index] = ""; + for (%found = true;%found;%buildingCount++ ) { + %suffix = %buildingCount; + while (strLen(%suffix) < 5) %suffix = "0" @ %suffix; + %file = $SaveBuilding::AutoSaveFolder @ $MissionName @ "-" @ %suffix @ ".cs"; + if (%found = isFile(%file)) { + for (%index = 10;%index > 0;%index--) { + $SaveBuilding::Found[%index] = $SaveBuilding::Found[%index - 1]; + } + $SaveBuilding::Found[0] = %file; + } + + } + for (%index = 0;%index < 11;%index++) { + %file = $SaveBuilding::Found[%index]; + if (%file !$= "") + messageClient(%client,'MsgVoteItem',"",%key,%idx++,"","_" @ getSubStr(%file,strLen(%dir),strLen(%file)-strLen(%dir)),true); + } + + } + + 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 ) +{ + if (!$Host::TournamentMode) + return; + + if (!(%client.isAdmin || %client.isSuperAdmin)) { + if (%option > Game.numTeams || %option $= "") + %option = 1; + } + + // ------------------------------------------------------------------------------------ + // z0dd - ZOD 5/8/02. Tourney mode bug 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. + + //if( %option == 1 || %option == 2 ) + // Game.clientJoinTeam( %client, %option, false ); + + //else if( %option == 3) + //{ + // Game.assignClientTeam( %client, $MatchStarted ); + // Game.spawnPlayer( %client, false ); + //} + //else + //{ + // Game.forceObserver( %client, "playerChoose" ); + // %client.observerMode = "observer"; + // %client.notReady = false; + // return; + //} + 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; + } + // End z0dd - ZOD + // ------------------------------------------------------------------------------------ + 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 ( !isDemo() && $Host::TournamentMode && !$CountdownStarted && !$MatchStarted ) + 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, 5/8/02. Addition. Was not exporting to serverPrefs and did not message admin status. + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + messageClient(%admin, 'MsgAdmin', '\c3\"%1\"\c2 added to Admin list: \c3%2\c2.', getTaggedString(%client.name), %client.guid); + logEcho(getTaggedString(%admin.name) @ " added " @ getTaggedString(%client.name) @ " " @ %client.guid @ " to Admin list."); +} + +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, 5/8/02. Addition. Was not exporting to serverPrefs and did not message admin status. + export( "$Host::*", "prefs/ServerPrefs.cs", false ); + messageClient(%admin, 'MsgAdmin', '\c3\"%1\"\c2 added to Super Admin list: \c3%2\c2.', getTaggedString(%client.name), %client.guid); + logEcho(getTaggedString(%admin.name) @ " added " @ getTaggedString(%client.name) @ " " @ %client.guid @ " to Super Admin list."); +} + +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(); + + for(%x = 0; %x < $NumVehiclesDeploy; %x++) + $VehiclesDeploy[%x].getDataBlock().schedule(%timeMS / 2, "vehicleDeploy", $VehiclesDeploy[%x], 0, 1); + $NumVehiclesDeploy = 0; + updateRankScores(1); + } + 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 >= 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.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 ); + + if ( !isDemo() ) + { + //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 ( !isDemo() && $Host::botsEnabled ) + 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); +} + +function reLightAllClients() { + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) { + %client = ClientGroup.getObject(%i); + if (%client.currentPhase == 4) + commandToClient(%client,'reLightMission'); + } +} + +function killerFog() { + cancel($KillerFogSched); + if (!isObject(MissionArea)) { + error("killerFog: no MissionArea!"); + return; + } + if (MissionArea.killerFogAlt $= "") { + error("killerFog: no kill altitude!"); + return; + } + %alt = MissionArea.killerFogAlt; + %count = ClientGroup.getCount(); + for(%i = 0; %i < %count; %i++) { + %cl = ClientGroup.getObject(%i); + %pl = %cl.player; + if (isObject(%pl)) { + if (%pl.getState() !$= "Dead" && %cl.isJailed != true) { + %pos = %pl.getPosition(); + if (getWord(%pos,2) < %alt) { + %vehicle = %pl.getObjectMount(); + if (%pl.isMounted()) { + if (%pl.vehicleTurret) + %pl.vehicleTurret.getDataBlock().playerDismount(%pl.vehicleTurret); + else { + %pl.getDataBlock().doDismount(%pl,true); + %pl.mountVehicle = false; + } + } + if (getWord(%pos,2) > -11000) + %pl.setPosition(getWords(%pos,0,1) SPC -12000); + %pl.scriptKill($DamageType::KillerFog); + if (isObject(%vehicle)) { + if ((%vehicle.getType() & $TypeMasks::VehicleObjectType) && (!%vehicle.fogKilled)) { + %vehicle.fogKilled = true; + %vehicle.schedule(10,setPosition,getWords(%vehicle.getPosition(),0,1) SPC -12000); + %vehicle.schedule(3000,setDamageState,Destroyed); + } + } + } + } + } + } + $KillerFogSched = schedule(500,0,killerFog); +} + +function serverCmdConstructionRegisterClient(%client,%version) { + %client.constructionClient = true; + %client.constructionClientVersion = %version; + echo(%client SPC getTaggedString(%client.name) @ " registered as Construction Mod Client, version " @ %version @ "."); +} + +function serverCmdConstructionQueryServer(%client) { + commandToClient(%client,'QueryServerReply',"Construction Mod Server",$ModVersion,$ModCredits,"2"); +} + +function limitVelocityLoop() { + cancel($limitVelocityLoop); + %count = ClientGroup.getCount(); + for (%i=0;%i<%count;%i++) { + %client = ClientGroup.getObject(%i); + %plyr = %client.player; + if (isObject(%plyr)) { +// %vehicle = %plyr.getObjectMount(); +// if (%vehicle) +// limitObjectVelocity(%vehicle); + limitObjectVelocity(%plyr); + } + } + %count = getWordCount($VehicleList); + for(%i=0;%i<%count;%i++) { + %obj= getWord($VehicleList,%i); + if (isObject(%obj)) { + if ((%obj.getType() & $TypeMasks::VehicleObjectType)) { + limitObjectVelocity(%obj); + } + } + } + $limitVelocityLoop = schedule(1000,0,"limitVelocityLoop"); +} + +function limitObjectVelocity(%obj) { + %vec = %obj.getVelocity(); + %vel = vectorLen(%vec); + if (%vel > 1400) { + %obj.setVelocity(vectorScale(vectorNormalize(%vec),1400)); + if (%obj.getType() & $TypeMasks::VehicleObjectType) { + %obj.setFrozenState(true); + %obj.schedule(500,setDamageState,Destroyed); + } + } + // doing this here, since we already have the loop set up :P + if (!$Host::AllowUnderground) { + if (%obj.getType() & $TypeMasks::PlayerObjectType) { + if (%obj.client.isJailed || %obj.getState() $= "Dead" || %obj.getObjectMount()) + return; + } + if (%obj.fogKilled) + return; + %pos = %obj.getPosition(); + %terrain = getWord(getTerrainHeight2(%pos),2); + if (getWord(%pos,2) < %terrain) { + %obj.setPosition(getWords(%pos,0,1) SPC %terrain + (getWord(%pos,2) - getWord(%obj.getWorldBox(),2)) + 0.1); + } + } +} + +function SimObject::getUpVector(%obj){ + %vec = vectorNormalize(vectorsub(%obj.getEdge("0 0 1"),%obj.getEdge("0 0 -1"))); + return %vec; +} diff --git a/Scripts/serverCommanderMap.cs b/Scripts/serverCommanderMap.cs new file mode 100644 index 0000000..62e17e6 --- /dev/null +++ b/Scripts/serverCommanderMap.cs @@ -0,0 +1,373 @@ +//------------------------------------------------------------------------------ +// Object control +//------------------------------------------------------------------------------ +function getControlObjectType(%obj,%user) +{ + // turrets (camera is a turret) + if (%obj.getType() & $TypeMasks::TurretObjectType) + { + %barrel = %obj.getMountedImage(0); + if (isObject(%barrel)) + return(addTaggedString(%barrel.getName())); + } + + // unknown + return('Unknown'); +} + +function serverCmdControlObject(%client, %targetId) +{ + // match started: + if (!$MatchStarted) + { + commandToClient(%client, 'ControlObjectResponse', false, "mission has not started."); + return; + } + + // object: + %obj = getTargetObject(%targetId); + if (%obj == -1) + { + commandToClient(%client, 'ControlObjectResponse', false, "failed to find target object."); + return; + } + + // shapebase: + if (!(%obj.getType() & $TypeMasks::ShapeBaseObjectType)) + { + commandToClient(%client, 'ControlObjectResponse', false, "object cannot be controlled."); + return; + } + + // can control: + if (!%obj.getDataBlock().canControl) + { + commandToClient(%client, 'ControlObjectResponse', false, "object cannot be controlled."); + return; + } + + // check damage: + if (%obj.getDamageState() !$= "Enabled") + { + commandToClient(%client, 'ControlObjectResponse', false, "object is " @ %obj.getDamageState()); + return; + } + + // powered: + if (!%obj.isPowered()) + { + commandToClient(%client, 'ControlObjectResponse', false, "object is not powered."); + return; + } + + // controlled already: + %control = %obj.getControllingClient(); + if (%control) + { + if (%control == %client) + commandToClient(%client, 'ControlObjectResponse', false, "you are already controlling that object."); + else + commandToClient(%client, 'ControlObjectResponse', false, "someone is already controlling that object."); + return; + } + + // same team? + if (getTargetSensorGroup(%targetId) != %client.getSensorGroup()) + { + commandToClient(%client, 'ControlObjectResonse', false, "cannot control enemy objects."); + return; + } + + // dead? + if (%client.player == 0 && getTargetDataBlock(%targetId).getName() !$= "TurretPrisonCamera") { + commandToClient(%client, 'ControlObjectResponse', false, "dead people cannot control objects."); + return; + } + + if (%client.isJailed) { + return; + } + + // turret in purebuild mode? + if ((%obj.getType() & $TypeMasks::TurretObjectType) + && $Host::Purebuild == 1 + && !(%client.isAdmin || %client.isSuperAdmin) + && %obj.getDataBlock().getName() !$= "TurretDeployedCamera" + && %obj.getDataBlock().getName() !$= "TurretPrisonCamera") { + commandToClient(%client, 'ControlObjectResponse', false, "cannot control turrets in purebuild mode."); + return; + } + +//[[CHANGE]]Make sure you can command a bomber... and ride it the same time ;) + + //mounted in a vehicle? + //if (%client.player.isMounted()) + //{ + // commandToClient(%client, 'ControlObjectResponse', false, "can't control objects while mounted in a vehicle."); + // return; + //} + + %client.setControlObject(%obj); + commandToClient(%client, 'ControlObjectResponse', true, getControlObjectType(%obj,%client.player)); + %objName = getTaggedString(getTargetName(%obj.target)) SPC getTaggedString(getTargetType(%obj.target)); + if (%obj $= "") + %objName = %obj.getDataBlock().getName(); + if ($Host::Purebuild == 1) + messageAll('msgClient','\c2%1 is now controlling %2.',%client.name,%objName); + else + messageTeam(%client.team,'msgClient','\c2%1 is now controlling %2.',%client.name,%objName); + +//[[CHANGE]] Make sure the controlled object knows how is controlling it. + %obj.clientControl = %client; + +///[[CHANGE]] Includes the remote station functionality. + + if (%obj.getType() & $TypeMasks::StationObjectType) + { +//Lost of commented stuff... should not be nessesary. + %colObj = %client.player; + //%colObj.inStation = true; + + //commandToClient(%colObj.client,'setStationKeys', true); + messageClient(%colObj.client, 'CloseHud', "", 'inventoryScreen'); + //commandToClient(%colObj.client, 'TogglePlayHuds', true); + %obj.triggeredBy = %colObj; + //%obj.getDataBlock().stationTriggered(%obj, 1); + %colObj.station = %obj; + //%colObj.lastWeapon = ( %colObj.getMountedImage($WeaponSlot) == 0 ) ? "" : %colObj.getMountedImage($WeaponSlot).getName().item; + //%colObj.unmountImage($WeaponSlot); + // Make sure none of the other popup huds are active: + //messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'scoreScreen' ); + //messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'inventoryScreen' ); + //Make sure the client doesn't transport.. but does get command. + %client.telebuy = 1; + + + //Stuff from observing + %data = %obj.getDataBlock(); + + %obsData = %data.observeParameters; + %obsX = firstWord(%obsData); + %obsY = getWord(%obsData, 1); + %obsZ = getWord(%obsData, 2); + + // don't set the camera mode so that it does not interfere with spawning + %transform = %obj.getTransform(); + + // create a fresh camera to observe through... (could add to a list on + // the observed camera to be removed when that object dies/...) + + if ( !isObject( %client.comCam ) ) + { + %client.comCam = new Camera() + { + dataBlock = CommanderCamera; + }; + MissionCleanup.add(%client.comCam); + } + + %client.comCam.setTransform(%transform); + %client.comCam.setOrbitMode(%obj, %transform, %obsX, %obsY, %obsZ); + + %client.setControlObject(%client.comCam); + commandToClient(%client, 'CameraAttachResponse', true); + + //Display the Vehicle Station GUI + //%client.player.AttachBeacon(); + //%client.player.schedule(20000,"RemoveBeacon"); + //%client.player.scheduel(1000,RemoveBeacon()); + + commandToClient(%obj.triggeredBy.client, 'StationVehicleShowHud'); + } + if (isObject(%client.player)) { + %client.player.RemoveBeacon(); + %client.player.AttachBeacon(); + } +//[[End CHANGE]] +} + +//[[CHANGE]] Pretty straigh forward functions. +function Player::AttachBeacon(%obj) +{ + %beacon = new BeaconObject(){ + datablock = BomberBeacon; + }; + if (%obj.team == 1) + %team = 2; + else + %team = 1; + %beacon.team = %team; + %beacon.owner = %obj; + %beacon.setTarget(%team); + %obj.mountObject(%beacon, 4); + %obj.enemyBeacon = %beacon; + MissionCleanup.add(%beacon); + %beacon.setBeaconType(enemy); +} +function Player::RemoveBeacon(%obj) +{ +if (%obj.enemybeacon) + %obj.enemyBeacon.delete(); + %obj.enemyBeacon = ""; +} +//[[End CHANGE]] +//------------------------------------------------------------------------------ +// TV Functions +//------------------------------------------------------------------------------ +function resetControlObject(%client) { + if ( isObject( %client.comCam ) ) + %client.comCam.delete(); + if (isObject(%client.player) && !%client.player.isDestroyed() && $MatchStarted) + %client.setControlObject(%client.player); + else + %client.setControlObject(%client.camera); + + // [[CHANGE]] make sure all is reset. + if (isObject(%client.player)) { + %client.player.station.triggeredBy = ""; + %client.player.station = ""; + %client.player.RemoveBeacon(); + } +} + +function serverCmdResetControlObject(%client) { + resetControlObject(%client); + commandToClient(%client, 'ControlObjectReset'); + // -------------------------------------------------------- + // z0dd - ZOD 4/18/02. Vehicle reticle disappearance fix. + // commandToClient(%client, 'RemoveReticle'); + //if(isObject(%client.player)) + //{ + // %weapon = %client.player.getMountedImage($WeaponSlot); + // %client.setWeaponsHudActive(%weapon.item); + //} + if(isObject(%client.player)) + { + if(%client.player.isPilot() || %client.player.isWeaponOperator()) + { + return; + } + else + { + commandToClient(%client, 'RemoveReticle'); + %weapon = %client.player.getMountedImage($WeaponSlot); + %client.setWeaponsHudActive(%weapon.item); + } + } + // End z0dd - ZOD + // -------------------------------------------------------- + + // [[CHANGE]] make sure all is reset. + if (isObject(%client.player)) { + %client.player.station.triggeredBy = ""; + %client.player.station = ""; + %client.player.RemoveBeacon(); + } +} + +function serverCmdAttachCommanderCamera(%client, %target) +{ + // dont allow observing until match has started + if (!$MatchStarted) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + %obj = getTargetObject(%target); + if ((%obj == -1) || (%target == -1)) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + // shape base object? + if (!(%obj.getType() & $TypeMasks::ShapeBaseObjectType)) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + // can be observed? + if (!%obj.getDataBlock() || !%obj.getDataBlock().canObserve) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + // same team? + if (getTargetSensorGroup(%target) != %client.getSensorGroup()) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + // powered? + if (!%obj.isPowered()) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + // client connection? + if (%obj.getClassName() $= "GameConnection") + { + %player = %obj.player; + if (%obj == %client) + { + if (isObject(%player) && !%player.isDestroyed()) + { + + %client.setControlObject(%player); + commandToClient(%client, 'CameraAttachResponse', true); + return; + } + } + + %obj = %player; + } + + if (!isObject(%obj) || %obj.isDestroyed()) + { + commandToClient(%client, 'CameraAttachResponse', false); + return; + } + + %data = %obj.getDataBlock(); + + %obsData = %data.observeParameters; + %obsX = firstWord(%obsData); + %obsY = getWord(%obsData, 1); + %obsZ = getWord(%obsData, 2); + + // don't set the camera mode so that it does not interfere with spawning + %transform = %obj.getTransform(); + + // create a fresh camera to observe through... (could add to a list on + // the observed camera to be removed when that object dies/...) + if ( !isObject( %client.comCam ) ) + { + %client.comCam = new Camera() + { + dataBlock = CommanderCamera; + }; + MissionCleanup.add(%client.comCam); + } + + %client.comCam.setTransform(%transform); + %client.comCam.setOrbitMode(%obj, %transform, %obsX, %obsY, %obsZ); + + %client.setControlObject(%client.comCam); + commandToClient(%client, 'CameraAttachResponse', true); +} + +//------------------------------------------------------------------------------ +// Scoping +function serverCmdScopeCommanderMap(%client, %scope) +{ + if (%scope) + resetControlObject(%client); + %client.scopeCommanderMap(%scope); + + commandToClient(%client, 'ScopeCommanderMap', %scope); +} \ No newline at end of file diff --git a/Scripts/serverDefaults.cs b/Scripts/serverDefaults.cs new file mode 100644 index 0000000..eefa511 --- /dev/null +++ b/Scripts/serverDefaults.cs @@ -0,0 +1,178 @@ +$Host::teamskin[0] = "base"; +$Host::teamskin[1] = "basebbot"; +$Host::teamskin[2] = "basebot"; +$Host::teamskin[3] = "beagle"; +$Host::teamskin[4] = "dsword"; +$Host::teamskin[5] = "cotp"; +$Host::teamskin[6] = "swolf"; + +$Host::teamname[0] = "Outcasts"; +$Host::teamname[1] = "Constructors"; +$Host::teamname[2] = "Builders"; +$Host::teamname[3] = "Admins"; +$Host::teamname[4] = "Rebels"; +$Host::teamname[5] = "Rouges"; +$Host::teamname[6] = "Zombies"; + +$Host::holoName[0] = ""; +$Host::holoName[1] = "Storm"; +$Host::holoName[2] = "Inferno"; +$Host::holoName[3] = "Starwolf"; +$Host::holoName[4] = "DSword"; +$Host::holoName[5] = "BloodEagle"; +$Host::holoName[6] = "Harbinger"; + +// Demo-specific preferences: +if ( isDemo() ) +{ + $Host::GameName = "Tribes 2 Demo Server"; + $Host::Info = "This is a Tribes 2 Demo Server."; + $Host::Map = "SlapDash"; + $Host::MaxPlayers = 32; +} +else +{ + $Host::GameName = "ACCM Server"; + $Host::Info = "This is an ACCM server."; + $Host::Map = "Lost World"; + $Host::MaxPlayers = 20; +} + +$Host::AdminList = ""; +$Host::SuperAdminList = ""; +$Host::BindAddress = ""; +$Host::Port = 28000; +$Host::Password = ""; +$Host::AdminPassword = ""; +$Host::PureServer = 1; +$Host::Dedicated = 0; +$Host::MissionType = "Infection"; +$Host::TimeLimit = 30; +$Host::BotCount = 2; +$Host::BotsEnabled = 0; +$Host::MinBotDifficulty = 0.5; +$Host::MaxBotDifficulty = 0.75; +$Host::NoSmurfs = 0; +$Host::VoteTime = 30; // amount of time before votes are calculated +$Host::VotePassPercent = 60; // percent needed to pass a vote +$Host::KickBanTime = 60; // specified in seconds +$Host::BanTime = 1800; // specified in seconds +$Host::PlayerRespawnTimeout = 60; // time before a dead player is forced into observer mode +$Host::warmupTime = 20; +$Host::TournamentMode = 0; +$Host::allowAdminPlayerVotes = 0; +$Host::FloodProtectionEnabled = 0; +$Host::MaxMessageLen = 120; +$Host::VoteSpread = 20; +$Host::TeamDamageOn = 0; +$Host::Siege::Halftime = 20000; +$Host::CRCTextures = 0; + +// Construction-specific defaults. +$Host::ACCMChatLogging = 1; +$Host::ACCMEchoChat = 1; +$Host::AdminOnlyFadeObject = 0; +$Host::AllowKeeperPlayerVotes = 1; +$Host::AntidoteStationMaxAntidotes = 20; +$Host::Cascade = 0; +$Host::ExpertMode = 1; +$Host::Hazard::Enabled = 0; +$Host::InvincibleArmors = 0; +$Host::InvincibleDeployables = 1; +$Host::KeepersGetMakerAbility = 1; +$Host::LockedTeams = "0"; +$Host::NoAnnoyingVoiceChatSpam = 0; +$Host::NoInfection = 0; +$Host::NoPulseSCG = 0; +$Host::ObserversCannotChat = 1; +$Host::OnlyOwnerCascade = 1; +$Host::OnlyOwnerCubicReplace = 1; +$Host::OnlyOwnerDeconstruct = 1; +$Host::OnlyOwnerRotate = 1; +$Host::Prison::DeploySpam = 0; +$Host::Prison::DeploySpamCheckTimeMS = 1000; +$Host::Prison::DeploySpamMaxTime = 300; +$Host::Prison::DeploySpamMultiply = 1; +$Host::Prison::DeploySpamRemoveRecentMS = 15000; +$Host::Prison::DeploySpamResetWarnCountTime = 30; +$Host::Prison::DeploySpamTime = 60; +$Host::Prison::DeploySpamWarnings = 10; +$Host::Prison::Enabled = 0; +$Host::Prison::JailMode = 0; +$Host::Prison::Kill = 0; +$Host::Prison::KillTime = 120; +$Host::Prison::ReleaseMode = 1; +$Host::Prison::TeamKill = 0; +$Host::Purebuild = 0; +$Host::SADProtection = 1; +$Host::StationHoldTime = 1600; // Specified in seconds. +$Host::SuperAdminPassword = ""; +$Host::Vehicles = 1; + +// 0: .v12 (1.2 kbits/sec), 1: .v24 (2.4 kbits/sec), 2: .v29 (2.9kbits/sec) +// 3: GSM (6.6 kbits/sec) +$Audio::maxEncodingLevel = 3; +$Audio::maxVoiceChannels = 2; + +$Host::MapPlayerLimits["Abominable", "CnH"] = "-1 -1"; +$Host::MapPlayerLimits["AgentsOfFortune", "TeamHunters"] = "-1 32"; +$Host::MapPlayerLimits["Alcatraz", "Siege"] = "-1 48"; +$Host::MapPlayerLimits["Archipelago", "CTF"] = "16 -1"; +$Host::MapPlayerLimits["AshesToAshes", "CnH"] = "16 -1"; +$Host::MapPlayerLimits["BeggarsRun", "CTF"] = "-1 32"; +$Host::MapPlayerLimits["Caldera", "Siege"] = "-1 48"; +$Host::MapPlayerLimits["CasernCavite", "Hunters"] = "-1 32"; +$Host::MapPlayerLimits["CasernCavite", "DM"] = "-1 32"; +$Host::MapPlayerLimits["CasernCavite", "Bounty"] = "-1 32"; +$Host::MapPlayerLimits["Damnation", "CTF"] = "-1 32"; +$Host::MapPlayerLimits["DeathBirdsFly", "CTF"] = "8 -1"; +$Host::MapPlayerLimits["Desiccator", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["DustToDust", "CTF"] = "-1 32"; +$Host::MapPlayerLimits["DustToDust", "Hunters"] = "-1 32"; +$Host::MapPlayerLimits["DustToDust", "TeamHunters"] = "-1 32"; +$Host::MapPlayerLimits["Equinox", "CnH"] = "-1 -1"; +$Host::MapPlayerLimits["Equinox", "DM"] = "-1 32"; +$Host::MapPlayerLimits["Escalade", "Hunters"] = "8 -1"; +$Host::MapPlayerLimits["Escalade", "TeamHunters"] = "8 -1"; +$Host::MapPlayerLimits["Escalade", "DM"] = "16 -1"; +$Host::MapPlayerLimits["Escalade", "Bounty"] = "16 32"; +$Host::MapPlayerLimits["Escalade", "Rabbit"] = "16 -1"; +$Host::MapPlayerLimits["Firestorm", "CTF"] = "-1 24"; +$Host::MapPlayerLimits["Firestorm", "CnH"] = "-1 24"; +$Host::MapPlayerLimits["Flashpoint", "CnH"] = "-1 -1"; +$Host::MapPlayerLimits["Gauntlet", "Siege"] = "-1 32"; +$Host::MapPlayerLimits["Gehenna", "Hunters"] = "-1 -1"; +$Host::MapPlayerLimits["Gehenna", "TeamHunters"] = "-1 -1"; +$Host::MapPlayerLimits["Icebound", "Siege"] = "-1 -1"; +$Host::MapPlayerLimits["Insalubria", "CnH"] = "-1 32"; +$Host::MapPlayerLimits["JacobsLadder", "CnH"] = "-1 -1"; +$Host::MapPlayerLimits["Katabatic", "CTF"] = "-1 48"; +$Host::MapPlayerLimits["Masada", "Siege"] = "-1 32"; +$Host::MapPlayerLimits["Minotaur", "CTF"] = "-1 32"; +$Host::MapPlayerLimits["Myrkwood", "Hunters"] = "-1 32"; +$Host::MapPlayerLimits["Myrkwood", "DM"] = "-1 32"; +$Host::MapPlayerLimits["Myrkwood", "Rabbit"] = "-1 32"; +$Host::MapPlayerLimits["Oasis", "DM"] = "-1 32"; +$Host::MapPlayerLimits["Overreach", "CnH"] = "8 -1"; +$Host::MapPlayerLimits["Quagmire", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["Rasp", "TeamHunters"] = "-1 32"; +$Host::MapPlayerLimits["Rasp", "Bounty"] = "-1 32"; +$Host::MapPlayerLimits["Recalescence", "CTF"] = "16 -1"; +$Host::MapPlayerLimits["Respite", "Siege"] = "-1 32"; +$Host::MapPlayerLimits["Reversion", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["Rimehold", "Hunters"] = "8 -1"; +$Host::MapPlayerLimits["Rimehold", "Hunters"] = "8 -1"; +$Host::MapPlayerLimits["Riverdance", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["Sanctuary", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["Sirocco", "CnH"] = "8 -1"; +$Host::MapPlayerLimits["Slapdash", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["SunDried", "DM"] = "8 -1"; +$Host::MapPlayerLimits["SunDried", "Bounty"] = "8 -1"; +$Host::MapPlayerLimits["Talus", "Bounty"] = "-1 32"; +$Host::MapPlayerLimits["ThinIce", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["Tombstone", "CTF"] = "-1 -1"; +$Host::MapPlayerLimits["UltimaThule", "Siege"] = "8 -1"; +$Host::MapPlayerLimits["Underhill", "DM"] = "-1 -1"; +$Host::MapPlayerLimits["Underhill", "Bounty"] = "-1 32"; +$Host::MapPlayerLimits["Whiteout", "DM"] = "8 -1"; +$Host::MapPlayerLimits["Whiteout", "Bounty"] = "8 -1"; diff --git a/Scripts/skywrite.cs b/Scripts/skywrite.cs new file mode 100644 index 0000000..a0b7e6a --- /dev/null +++ b/Scripts/skywrite.cs @@ -0,0 +1,606 @@ +//A nice collection of characters. +// + +//Normal +//"a b c d e f g h i j k l m n o p q r s t u v w x y z" + +//a +$SW::Info["a"] = "5 0.85"; //Pieces,Space +$SW::Sizes["a",0] = "0.3 0.50 0.55 0.50 0.3 0.60"; +$SW::Sizes["a",1] = "0.55 0.2 0.65 0.2 0.55 0.55"; +$SW::Sizes["a",2] = "0.3 0.1 0.75 0.1 0.3 0.2"; +$SW::Sizes["a",3] = "0.2 0.15 0.3 0.15 0.2 0.35"; +$SW::Sizes["a",4] = "0.3 0.3 0.55 0.3 0.3 0.4"; + +//b +$SW::Info["b"] = "4 0.7"; //Pieces,Space +$SW::Sizes["b",0] = "0.2 0.1 0.3 0.1 0.2 0.8"; +$SW::Sizes["b",1] = "0.3 0.40 0.60 0.40 0.3 0.50"; +$SW::Sizes["b",2] = "0.3 0.10 0.60 0.10 0.3 0.20"; +$SW::Sizes["b",3] = "0.60 0.15 0.70 0.15 0.60 0.45"; + +//c +$SW::Info["c"] = "3 0.75"; //Pieces,Space +$SW::Sizes["c",0] = "0.2 0.15 0.3 0.15 0.2 0.50"; +$SW::Sizes["c",1] = "0.3 0.45 0.65 0.45 0.3 0.55"; +$SW::Sizes["c",2] = "0.3 0.10 0.65 0.10 0.3 0.20"; + +//d +$SW::Info["d"] = "4 0.8"; //Pieces,Space +$SW::Sizes["d",0] = "0.2 0.15 0.3 0.15 0.2 0.45"; +$SW::Sizes["d",1] = "0.3 0.40 0.60 0.40 0.3 0.50"; +$SW::Sizes["d",2] = "0.3 0.10 0.60 0.10 0.3 0.20"; +$SW::Sizes["d",3] = "0.60 0.1 0.70 0.1 0.60 0.8"; + +//e +$SW::Info["e"] = "5 0.75"; //Pieces,Space +$SW::Sizes["e",0] = "0.2 0.15 0.3 0.15 0.2 0.50"; +$SW::Sizes["e",1] = "0.3 0.45 0.55 0.45 0.3 0.55"; +$SW::Sizes["e",2] = "0.3 0.10 0.65 0.10 0.3 0.20"; +$SW::Sizes["e",3] = "0.55 0.35 0.65 0.35 0.55 0.50"; +$SW::Sizes["e",4] = "0.3 0.25 0.65 0.25 0.3 0.35"; + +//f +$SW::Info["f"] = "3 0.8"; //Pieces,Space +$SW::Sizes["f",0] = "0.30 0.5 0.70 0.5 0.30 0.60"; +$SW::Sizes["f",1] = "0.45 0.1 0.55 0.1 0.45 0.7"; +$SW::Sizes["f",2] = "0.50 0.65 0.70 0.65 0.50 0.75"; + +//g +$SW::Info["g"] = "5 0.75"; //Pieces,Space +$SW::Sizes["g",0] = "0.3 0.50 0.55 0.50 0.3 0.60"; +$SW::Sizes["g",1] = "0.55 0.05 0.65 0.05 0.55 0.60"; +$SW::Sizes["g",2] = "0.3 0.0 0.60 0.0 0.3 0.1"; +$SW::Sizes["g",3] = "0.25 0.30 0.35 0.30 0.25 0.55"; +$SW::Sizes["g",4] = "0.3 0.25 0.55 0.25 0.3 0.35"; + +//h +$SW::Info["h"] = "3 0.7"; //Pieces,Space +$SW::Sizes["h",0] = "0.2 0.1 0.3 0.1 0.2 0.8"; +$SW::Sizes["h",1] = "0.3 0.40 0.60 0.40 0.3 0.50"; +$SW::Sizes["h",2] = "0.55 0.1 0.65 0.1 0.55 0.45"; + +//i +$SW::Info["i"] = "2 0.35"; //Pieces,Space +$SW::Sizes["i",0] = "0.15 0.1 0.25 0.1 0.15 0.5"; +$SW::Sizes["i",1] = "0.15 0.6 0.25 0.6 0.15 0.7"; + +//j +$SW::Info["j"] = "3 0.45"; //Pieces,Space +$SW::Sizes["j",0] = "0.25 0.05 0.35 0.05 0.25 0.6"; +$SW::Sizes["j",1] = "0.25 0.7 0.35 0.7 0.25 0.8"; +$SW::Sizes["j",2] = "0.10 0.0 0.30 0.0 0.10 0.1"; + +//k +$SW::Info["k"] = "3 0.6"; //Pieces,Space +$SW::Sizes["k",0] = "0.2 0.1 0.3 0.1 0.2 0.7"; +$SW::Sizes["k",1] = "0.3 0.25 0.50 0.45 0.23 0.32"; +$SW::Sizes["k",2] = "0.3 0.25 0.45 0.1 0.37 0.32"; + +//l +$SW::Info["l"] = "1 0.4"; //Pieces,Space +$SW::Sizes["l",0] = "0.2 0.1 0.3 0.1 0.2 0.8"; + +//m +$SW::Info["m"] = "5 0.9"; //Pieces,Space +$SW::Sizes["m",0] = "0.2 0.1 0.3 0.1 0.2 0.55"; +$SW::Sizes["m",1] = "0.3 0.40 0.50 0.40 0.3 0.50"; +$SW::Sizes["m",2] = "0.45 0.1 0.55 0.1 0.45 0.45"; +$SW::Sizes["m",3] = "0.55 0.40 0.75 0.40 0.55 0.50"; +$SW::Sizes["m",4] = "0.70 0.1 0.80 0.1 0.70 0.45"; + +//n +$SW::Info["n"] = "3 0.65"; //Pieces,Space +$SW::Sizes["n",0] = "0.2 0.1 0.3 0.1 0.2 0.55"; +$SW::Sizes["n",1] = "0.3 0.40 0.50 0.40 0.3 0.50"; +$SW::Sizes["n",2] = "0.45 0.1 0.55 0.1 0.45 0.45"; + +//o +$SW::Info["o"] = "4 0.65"; //Pieces,Space +$SW::Sizes["o",0] = "0.2 0.15 0.3 0.15 0.2 0.45"; +$SW::Sizes["o",1] = "0.25 0.40 0.50 0.40 0.3 0.50"; +$SW::Sizes["o",2] = "0.45 0.15 0.55 0.15 0.45 0.45"; +$SW::Sizes["o",3] = "0.25 0.1 0.50 0.1 0.25 0.2"; + +//p +$SW::Info["p"] = "4 0.8"; //Pieces,Space +$SW::Sizes["p",0] = "0.3 0.50 0.55 0.50 0.3 0.60"; +$SW::Sizes["p",1] = "0.5 0.30 0.6 0.30 0.5 0.55"; +$SW::Sizes["p",2] = "0.2 0.0 0.3 0.00 0.2 0.60"; +$SW::Sizes["p",3] = "0.3 0.25 0.55 0.25 0.3 0.35"; + +//q +$SW::Info["q"] = "4 0.75"; //Pieces,Space +$SW::Sizes["q",0] = "0.3 0.50 0.55 0.50 0.3 0.60"; +$SW::Sizes["q",1] = "0.55 0.0 0.65 0.0 0.55 0.60"; +$SW::Sizes["q",2] = "0.25 0.30 0.35 0.30 0.25 0.55"; +$SW::Sizes["q",3] = "0.3 0.25 0.55 0.25 0.3 0.35"; + +//r +$SW::Info["r"] = "4 0.6"; //Pieces,Space +$SW::Sizes["r",0] = "0.2 0.1 0.3 0.1 0.2 0.55"; +$SW::Sizes["r",1] = "0.3 0.38 0.35 0.38 0.3 0.48"; +$SW::Sizes["r",2] = "0.3 0.40 0.50 0.40 0.3 0.50"; +$SW::Sizes["r",3] = "0.45 0.38 0.50 0.38 0.45 0.48"; + +//s +$SW::Info["s"] = "7 0.6"; //Pieces,Space +$SW::Sizes["s",0] = "0.2 0.40 0.3 0.40 0.2 0.50"; +$SW::Sizes["s",1] = "0.25 0.45 0.45 0.45 0.3 0.55"; +$SW::Sizes["s",2] = "0.2 0.40 0.40 0.2 0.25 0.50"; +$SW::Sizes["s",3] = "0.40 0.15 0.5 0.15 0.4 0.25"; +$SW::Sizes["s",4] = "0.25 0.1 0.45 0.1 0.2 0.2"; +$SW::Sizes["s",5] = "0.40 0.40 0.5 0.40 0.4 0.5"; +$SW::Sizes["s",6] = "0.2 0.15 0.3 0.15 0.2 0.25"; + +//t +$SW::Info["t"] = "3 0.75"; //Pieces,Space +$SW::Sizes["t",0] = "0.35 0.5 0.65 0.5 0.35 0.60"; +$SW::Sizes["t",1] = "0.45 0.1 0.55 0.1 0.45 0.7"; +$SW::Sizes["t",2] = "0.55 0.1 0.65 0.1 0.55 0.2"; + +//u +$SW::Info["u"] = "3 0.60"; //Pieces,Space +$SW::Sizes["u",0] = "0.2 0.1 0.3 0.1 0.2 0.50"; +$SW::Sizes["u",1] = "0.25 0.05 0.50 0.05 0.3 0.15"; +$SW::Sizes["u",2] = "0.45 0.1 0.55 0.1 0.45 0.50"; + +//v +$SW::Info["v"] = "2 0.60"; //Pieces,Space +$SW::Sizes["v",0] = "0.1 0.5 0.3 0.1 0.15 0.55"; +$SW::Sizes["v",1] = "0.3 0.1 0.5 0.5 0.25 0.15"; + +//w +$SW::Info["w"] = "4 0.9"; //Pieces,Space +$SW::Sizes["w",0] = "0.1 0.5 0.3 0.1 0.15 0.55"; +$SW::Sizes["w",1] = "0.3 0.1 0.5 0.5 0.25 0.15"; +$SW::Sizes["w",2] = "0.4 0.5 0.6 0.1 0.45 0.55"; +$SW::Sizes["w",3] = "0.6 0.1 0.8 0.5 0.55 0.15"; + +//x +$SW::Info["x"] = "2 0.5"; //Pieces,Space +$SW::Sizes["x",0] = "0.1 0.4 0.38 0.1 0.15 0.45"; +$SW::Sizes["x",1] = "0.12 0.1 0.4 0.4 0.07 0.15"; + +//y +$SW::Info["y"] = "3 0.60"; //Pieces,Space +$SW::Sizes["y",0] = "0.1 0.5 0.25 0.25 0.15 0.55"; +$SW::Sizes["y",1] = "0.3 0.15 0.5 0.5 0.25 0.2"; +$SW::Sizes["y",2] = "0.15 0.10 0.3 0.1 0.2 0.18"; + +//z +$SW::Info["z"] = "3 0.6"; //Pieces,Space +$SW::Sizes["z",0] = "0.1 0.40 0.4 0.40 0.1 0.50"; +$SW::Sizes["z",1] = "0.15 0.1 0.4 0.4 0.1 0.15"; +$SW::Sizes["z",2] = "0.1 0.1 0.4 0.1 0.1 0.2"; + +//Numbers +//"1 2 3 4 5 6 7 8 9 0" + +//1 +$SW::Info["1"] = "3 0.65"; //Pieces,Space +$SW::Sizes["1",0] = "0.3 0.1 0.4 0.1 0.3 0.8"; +$SW::Sizes["1",1] = "0.2 0.6 0.4 0.75 0.17 0.67"; +$SW::Sizes["1",2] = "0.15 0.0 0.55 0.0 0.1 0.1"; + +//2 +$SW::Info["2"] = "4 0.65"; //Pieces,Space +$SW::Sizes["2",0] = "0.1 0.7 0.5 0.7 0.1 0.8"; +$SW::Sizes["2",1] = "0.45 0.5 0.55 0.5 0.45 0.75"; +$SW::Sizes["2",2] = "0.15 0.0 0.55 0.5 0.08 0.07"; +$SW::Sizes["2",3] = "0.07 0.0 0.55 0.0 0.07 0.1"; + +//3 +$SW::Info["3"] = "5 0.65"; //Pieces,Space +$SW::Sizes["3",0] = "0.1 0.7 0.5 0.7 0.1 0.8"; +$SW::Sizes["3",1] = "0.45 0.45 0.55 0.45 0.45 0.75"; +$SW::Sizes["3",2] = "0.1 0.35 0.5 0.35 0.1 0.45"; +$SW::Sizes["3",3] = "0.45 0.05 0.55 0.05 0.45 0.35"; +$SW::Sizes["3",4] = "0.1 0.0 0.5 0.0 0.1 0.1"; + +//4 + +$SW::Info["4"] = "3 0.75"; //Pieces,Space +$SW::Sizes["4",0] = "0.4 0.0 0.5 0.0 0.4 0.8"; +$SW::Sizes["4",1] = "0.1 0.25 0.5 0.75 0.17 0.32"; +$SW::Sizes["4",2] = "0.1 0.25 0.65 0.25 0.1 0.35"; + +//5 +$SW::Info["5"] = "5 0.7"; //Pieces,Space +$SW::Sizes["5",0] = "0.1 0.35 0.2 0.35 0.1 0.8"; +$SW::Sizes["5",1] = "0.2 0.35 0.55 0.35 0.2 0.45"; +$SW::Sizes["5",2] = "0.1 0.0 0.55 0.00 0.1 0.10"; +$SW::Sizes["5",3] = "0.5 0.05 0.60 0.05 0.50 0.40"; +$SW::Sizes["5",4] = "0.2 0.70 0.60 0.70 0.2 0.8"; + +//6 +$SW::Info["6"] = "6 0.7"; //Pieces,Space +$SW::Sizes["6",0] = "0.1 0.05 0.2 0.05 0.1 0.7"; +$SW::Sizes["6",1] = "0.2 0.35 0.55 0.35 0.2 0.45"; +$SW::Sizes["6",2] = "0.15 0.0 0.55 0.00 0.15 0.10"; +$SW::Sizes["6",3] = "0.50 0.05 0.60 0.05 0.50 0.40"; +$SW::Sizes["6",4] = "0.15 0.65 0.60 0.70 0.08 0.72"; + +//7 +$SW::Info["7"] = "2 0.6"; //Pieces,Space +$SW::Sizes["7",0] = "0.1 0.7 0.5 0.7 0.1 0.8"; +$SW::Sizes["7",1] = "0.2 0.0 0.5 0.7 0.13 0.07"; + +//8 +$SW::Info["8"] = "7 0.7"; //Pieces,Space +$SW::Sizes["8",0] = "0.1 0.05 0.2 0.05 0.1 0.43"; +$SW::Sizes["8",1] = "0.1 0.45 0.58 0.35 0.17 0.52"; +$SW::Sizes["8",2] = "0.15 0.00 0.55 0.00 0.15 0.15"; +$SW::Sizes["8",3] = "0.50 0.05 0.60 0.05 0.50 0.45"; +$SW::Sizes["8",4] = "0.15 0.70 0.55 0.70 0.15 0.80"; +$SW::Sizes["8",5] = "0.1 0.45 0.2 0.45 0.1 0.75"; +$SW::Sizes["8",6] = "0.50 0.47 0.60 0.47 0.50 0.75"; + +//9 +$SW::Info["9"] = "6 0.7"; //Pieces,Space +$SW::Sizes["9",0] = "0.1 0.45 0.58 0.35 0.17 0.52"; +$SW::Sizes["9",1] = "0.15 0.00 0.55 0.00 0.15 0.15"; +$SW::Sizes["9",2] = "0.50 0.05 0.60 0.05 0.50 0.45"; +$SW::Sizes["9",3] = "0.15 0.70 0.55 0.70 0.15 0.80"; +$SW::Sizes["9",4] = "0.1 0.45 0.2 0.45 0.1 0.75"; +$SW::Sizes["9",5] = "0.50 0.47 0.60 0.47 0.50 0.75"; + +//0 +$SW::Info["0"] = "4 0.7"; //Pieces,Space +$SW::Sizes["0",0] = "0.1 0.05 0.2 0.05 0.1 0.75"; +$SW::Sizes["0",1] = "0.15 0.00 0.55 0.00 0.15 0.15"; +$SW::Sizes["0",2] = "0.50 0.05 0.60 0.05 0.50 0.75"; +$SW::Sizes["0",3] = "0.15 0.70 0.55 0.70 0.15 0.80"; + +//Tribes2 String Chars +// " ' \ + +//" +$SW::Info["\""] = "2 0.40"; //Pieces,Space +$SW::Sizes["\"",0] = "0.15 0.65 0.20 0.65 0.15 0.8"; +$SW::Sizes["\"",1] = "0.25 0.65 0.30 0.65 0.25 0.8"; + +//' +$SW::Info["\'"] = "1 0.30"; //Pieces,Space +$SW::Sizes["\'",0] = "0.15 0.65 0.20 0.65 0.15 0.8"; + +// \ +$SW::Info["\\"] = "1 0.70"; //Pieces,Space +$SW::Sizes["\\",0] = "0.1 0.75 0.6 0.1 0.15 0.8"; + + +//Skywrite string Chars +//+ / + +$SW::Info["/+"] = "2 0.70"; //Pieces,Space +$SW::Sizes["/+",0] = "0.1 0.35 0.6 0.35 0.1 0.45"; +$SW::Sizes["/+",1] = "0.3 0.1 0.4 0.1 0.3 0.7"; + +// / +$SW::Info["//"] = "1 0.70"; //Pieces,Space +$SW::Sizes["//",0] = "0.1 0.1 0.6 0.8 0.05 0.15"; + + +//Sentance builders +//. , : ; ! ? + +//. +$SW::Info["."] = "1 0.30"; //Pieces,Space +$SW::Sizes[".",0] = "0.1 0.1 0.20 0.1 0.1 0.2"; + +//, +$SW::Info[","] = "2 0.30"; //Pieces,Space +$SW::Sizes[",",0] = "0.1 0.1 0.20 0.1 0.1 0.2"; +$SW::Sizes[",",1] = "0.1 0.0 0.2 0.1 0.05 0.05"; + +//: +$SW::Info[":"] = "2 0.30"; //Pieces,Space +$SW::Sizes[":",0] = "0.1 0.2 0.20 0.2 0.1 0.3"; +$SW::Sizes[":",1] = "0.1 0.5 0.20 0.5 0.1 0.6"; + +//; +$SW::Info[";"] = "3 0.30"; //Pieces,Space +$SW::Sizes[";",0] = "0.1 0.2 0.20 0.2 0.1 0.3"; +$SW::Sizes[";",1] = "0.1 0.1 0.2 0.2 0.05 0.15"; +$SW::Sizes[";",2] = "0.1 0.5 0.20 0.5 0.1 0.6"; + +//! +$SW::Info["!"] = "2 0.35"; //Pieces,Space +$SW::Sizes["!",0] = "0.15 0.2 0.25 0.2 0.15 0.8"; +$SW::Sizes["!",1] = "0.15 0.0 0.25 0.0 0.15 0.1"; + +//? +$SW::Info["?"] = "6 0.65"; //Pieces,Space +$SW::Sizes["?",0] = "0.2 0.7 0.5 0.7 0.2 0.8"; +$SW::Sizes["?",1] = "0.15 0.65 0.25 0.65 0.15 0.75"; +$SW::Sizes["?",2] = "0.45 0.5 0.55 0.5 0.45 0.75"; +$SW::Sizes["?",3] = "0.35 0.25 0.55 0.5 0.27 0.33"; +$SW::Sizes["?",4] = "0.25 0.2 0.35 0.2 0.25 0.3"; +$SW::Sizes["?",5] = "0.25 0.0 0.35 0.0 0.25 0.1"; + + + +//# +$SW::Info["#"] = "4 0.55"; //Pieces,Space +$SW::Sizes["#",0] = "0.15 0.15 0.20 0.15 0.15 0.7"; +$SW::Sizes["#",1] = "0.25 0.15 0.30 0.15 0.25 0.7"; +$SW::Sizes["#",2] = "0.05 0.30 0.40 0.30 0.05 0.35"; +$SW::Sizes["#",3] = "0.05 0.50 0.40 0.50 0.05 0.55"; + +//$ +$SW::Info["$"] = "6 0.7"; //Pieces,Space +$SW::Sizes["$",0] = "0.2 0.65 0.55 0.65 0.2 0.75"; +$SW::Sizes["$",1] = "0.15 0.40 0.25 0.40 0.15 0.7"; +$SW::Sizes["$",2] = "0.2 0.40 0.55 0.30 0.27 0.47"; +$SW::Sizes["$",3] = "0.5 0.10 0.60 0.1 0.50 0.40"; +$SW::Sizes["$",4] = "0.2 0.05 0.55 0.05 0.25 0.15"; +$SW::Sizes["$",5] = "0.325 0.0 0.425 0.0 0.325 0.80"; + +//% +$SW::Info["%"] = "3 0.6"; //Pieces,Space +$SW::Sizes["%",0] = "0.4 0.1 0.5 0.0 0.5 0.2"; +$SW::Sizes["%",1] = "0.1 0.0 0.6 0.8 0.03 0.07"; +$SW::Sizes["%",2] = "0.0 0.75 0.1 0.65 0.1 0.85"; + +//& +$SW::Info["&"] = "6 0.7"; //Pieces,Space +$SW::Sizes["&",0] = "0.5 0.0 0.6 0.5 0.48 0.05"; +$SW::Sizes["&",1] = "0.1 0.0 0.5 0.0 0.1 0.1"; +$SW::Sizes["&",2] = "0.05 0.05 0.15 0.05 0.05 0.25"; +$SW::Sizes["&",3] = "0.1 0.20 0.55 0.65 0.05 0.25"; +$SW::Sizes["&",4] = "0.45 0.75 0.55 0.65 0.50 0.80"; +$SW::Sizes["&",5] = "0.25 0.55 0.60 0.60 0.20 0.70"; + + +//Hooks +//( ) [ ] { } < > + +//( +$SW::Info["("] = "3 0.45"; //Pieces,Space +$SW::Sizes["(",0] = "0.155 0.45 0.4 0.75 0.105 0.50"; +$SW::Sizes["(",1] = "0.1 0.3 0.2 0.3 0.1 0.5"; +$SW::Sizes["(",2] = "0.1 0.3 0.35 0.0 0.15 0.35"; + +//) +$SW::Info[")"] = "3 0.4"; //Pieces,Space +$SW::Sizes[")",0] = "0.0 0.75 0.24 0.46 0.05 0.8"; +$SW::Sizes[")",1] = "0.2 0.3 0.3 0.3 0.2 0.5"; +$SW::Sizes[")",2] = "0.05 0.0 0.3 0.3 0.0 0.05"; + +//[ +$SW::Info["["] = "3 0.4"; //Pieces,Space +$SW::Sizes["[",0] = "0.0 0.7 0.3 0.7 0.0 0.8"; +$SW::Sizes["[",1] = "0.0 0.1 0.1 0.1 0.0 0.7"; +$SW::Sizes["[",2] = "0.0 0.0 0.3 0.0 0.0 0.1"; + +//] +$SW::Info["]"] = "3 0.4"; //Pieces,Space +$SW::Sizes["]",0] = "0.0 0.7 0.3 0.7 0.0 0.8"; +$SW::Sizes["]",1] = "0.2 0.1 0.3 0.1 0.2 0.7"; +$SW::Sizes["]",2] = "0.0 0.0 0.3 0.0 0.0 0.1"; + +//{ +$SW::Info["{"] = "4 0.4"; //Pieces,Space +$SW::Sizes["{",0] = "0.15 0.7 0.4 0.7 0.15 0.8"; +$SW::Sizes["{",1] = "0.1 0.1 0.2 0.1 0.1 0.7"; +$SW::Sizes["{",2] = "0.15 0.0 0.4 0.0 0.15 0.1"; +$SW::Sizes["{",3] = "0.0 0.35 0.1 0.35 0.0 0.45"; + +//} +$SW::Info["}"] = "4 0.4"; //Pieces,Space +$SW::Sizes["}",0] = "0.0 0.7 0.25 0.7 0.0 0.8"; +$SW::Sizes["}",1] = "0.2 0.1 0.3 0.1 0.2 0.7"; +$SW::Sizes["}",2] = "0.0 0.0 0.25 0.0 0.0 0.1"; +$SW::Sizes["}",3] = "0.3 0.35 0.4 0.35 0.3 0.45"; + +//< +$SW::Info["<"] = "2 0.45"; //Pieces,Space +$SW::Sizes["<",0] = "0.155 0.35 0.4 0.65 0.105 0.40"; +$SW::Sizes["<",1] = "0.1 0.4 0.35 0.1 0.15 0.45"; + +//> +$SW::Info[">"] = "2 0.6"; //Pieces,Space +$SW::Sizes[">",0] = "0.0 0.65 0.24 0.36 0.05 0.7"; +$SW::Sizes[">",1] = "0.05 0.1 0.3 0.4 0.0 0.15"; + + +//Lines +// = _ - | + +//= +$SW::Info["="] = "2 0.45"; //Pieces,Space +$SW::Sizes["=",0] = "0.05 0.25 0.40 0.25 0.05 0.35"; +$SW::Sizes["=",1] = "0.05 0.50 0.40 0.50 0.05 0.60"; + +//- +$SW::Info["-"] = "1 0.45"; //Pieces,Space +$SW::Sizes["-",0] = "0.05 0.35 0.40 0.35 0.05 0.45"; + +//_ +$SW::Info["_"] = "1 0.45"; //Pieces,Space +$SW::Sizes["_",0] = "0.05 0.05 0.40 0.05 0.05 0.15"; + +//| +$SW::Info["|"] = "1 0.40"; //Pieces,Space +$SW::Sizes["|",0] = "0.15 0.0 0.25 0.0 0.15 0.8"; + + +//Space +$SW::Info["sp"] = "0 0.5"; //Pieces,Space + + +//Capital Letters + +//T +$SW::Info["+t"] = "2 1"; //Pieces,Space +$SW::Sizes["+t",0] = "0 0.8 1 0.8 0 1"; +$SW::Sizes["+t",1] = "0.4 0 0.6 0 0.4 0.8"; + +$SW::Thickness = 0.1; + +function GameConnection::skyWrite(%cl,%word,%dist,%scale,%center,%upright,%angle,%mirror) { + if (%word $= "") + return; + if (%dist $= "" || %dist == 0) + %dist = 50; + if (%scale $= "" || %scale == 0) + %scale = 5; + %word = SW_goodWord(%word); + %obj = %cl.player; + %pos = %obj.getEyepoint(); + %dir = %obj.getEyeVector(); + %location = vectorAdd(%pos,vectorScale(%dir,%dist)); + if (%upright) + %dir = vectorNormalize(getWords(%dir,0,1)); + if (%mirror) + %dir = vectorScale(%dir,-1); + SW_word(%word,%location,%dir,%scale,%center,%cl,%angle); +} + +function SW_word(%word,%pos,%nrm,%scale,%center,%cl,%angle) { + %cl.lastword++; + %z = vectorNormalize(%nrm); + %tx = vectorCross(%nrm,"0 0 1"); + %up = vectorAdd(vectorScale("0 0 1",mCos(mDegToRad(%angle))),vectorScale(%tx,mSin(mDegToRad(%angle)))); + %x = vectorCross(%nrm,%up); + + if (%center) { + for (%td = 0; %td < getWordCount(%word);%td++) { + %letter = getWord(%word,%td); + %space = %space + getWord($SW::Info[%letter],1) * %scale; + } + %space = %space * -0.5; + %pos = vectorAdd(%pos,vectorScale(%up,-0.5 * %scale)); + } + + for (%td = 0; %td < getWordCount(%word);%td++) { + %letter = getWord(%word,%td); + SW_letter(%letter,vectorAdd(%pos,vectorScale(%x,%space)),%nrm,%scale,%cl,%angle); + %space = %space + getWord($SW::Info[%letter],1) * %scale; + } +} + +function SW_letter(%letter,%pos,%nrm,%scale,%cl,%angle) { + %z = vectorNormalize(%nrm); + %tx = vectorCross(%nrm,"0 0 1"); + %up = vectorAdd(vectorScale("0 0 1",mCos(mDegToRad(%angle))),vectorScale(%tx,mSin(mDegToRad(%angle)))); + %x = vectorCross(%nrm,%up); + %y = vectorCross(%x,%nrm); + + %matrix = %x SPC %y SPC %z; + %pieces = getWord($SW::Info[%letter],0); + for (%td = 0; %td < %pieces;%td++) { + %obj = SW_letterPart(%letter,%td,%pos,%matrix,%scale); + %obj.textGroup(%cl,%cl.lastword); + %obj.team = %cl.team; + %obj.needsFit = 1; +// commented out. Can easily remove writing with "remove orphaned pieces", even if client is no longer in game +// %obj.setOwner(0,%cl); + } +} + +function SW_letterPart(%letter,%part,%pos,%matrix,%scale) { + %partInfo = $SW::Sizes[%letter,%part]; + %p1 = SW_goodSquare(%partInfo); + %pos1 = vectorScale(matrixMult(getWords(%p1,0,1) SPC "0",%matrix),%scale); + %vecx1 = vectorScale(matrixMult(getWords(%p1,2,3) SPC "0",%matrix),%scale); + %vecy1 = vectorScale(matrixMult(getWords(%p1,4,5) SPC "0",%matrix),%scale); + %vecz1 = vectorScale(getWords(%matrix,6,8),%scale * $SW::Thickness); + %obj = SW_bar(vectorAdd(%pos,%pos1),%vecx1,%vecy1,%vecz1); + return %obj; +} + +function SW_goodSquare(%p) { + %pos = getWords(%p,0,1); + %x = getWords(%p,2,3); + %y = getWords(%p,4,5); + return %pos SPC getWords(VectorSub(%x,%pos),0,1) SPC getWords(VectorSub(%y,%pos),0,1); +} + +function SW_goodWord(%word) { + %id = 0; + while (%id < strLen(%word)) { + %letter = getSubStr(%word,%id,1); + if (%letter $= "" || %letter $= "/" || %letter $= "+") { + %letter = getSubStr(%word,%id,2); + %id++; + } + if (%letter $= " ") + %letter = "sp"; + %id++; + if (%goodWord !$= "") + %goodWord = %goodWord SPC %letter; + else + %goodWord = %letter; + } + return %goodWord; +} + +//Creates the actual beam +function SW_bar(%pos,%fo,%ri,%up) { + %size = vectorLen(%fo) SPC vectorLen(%ri) SPC vectorLen(%up); + %rot = fullRot(%up,%fo); + + %obj = new StaticShape() { + dataBlock = DeployedSpine; + }; + +// %obj = new ForceFieldBare() { +// datablock = DeployedForceField1; +// }; + + %obj.setRealSize(%size); + %obj.setRotation(%rot); + %pos = microAdjust(%pos); + %obj.setEdge(%pos,"-1 1 -1"); + return %obj; +} + + +///Grouping + +function GameBase::textGroup(%obj,%cl,%word) { + %subGroup = %cl @ "/" @ %word; + %group = nameToID("MissionCleanup/textGroup/" @ %subgroup); + if (%group <= 0) { + %group = new SimGroup(%word); + addToClientTextGroup(%group,%cl); + } + %group.add(%obj); +} + +function addToClientTextGroup(%obj,%cl) { + %group = nameToID("MissionCleanup/textGroup/"@ %cl); + if (%group <= 0) { + %group = new SimGroup(%cl); + addToTextGroup(%group); + } + %group.add(%obj); +} + +function addToTextGroup(%obj) { + %group = nameToID("MissionCleanup/textGroup"); + if (%group <= 0) { + %group = new SimGroup("textGroup"); + MissionCleanup.add(%group); + } + %group.add(%obj); +} + +function getLatestGroup(%group) { + %count = %group.getCount(); + return %group.getObject(%count - 1); +} + +function GameConnection::removeLastWord(%cl) { + %group = nameToID("MissionCleanup/textGroup/" @ %cl); + %lastWord = getLatestGroup(%group); + if (isObject(%lastWord)) + %lastWord.delete(); +} + +function GameConnection::removeWords(%cl) { + %group = nameToID("MissionCleanup/textGroup/" @ %cl); + if (isObject(%group)) + %group.delete(); +} diff --git a/Scripts/solitudeBlock.cs b/Scripts/solitudeBlock.cs new file mode 100644 index 0000000..9ce960b --- /dev/null +++ b/Scripts/solitudeBlock.cs @@ -0,0 +1,117 @@ +function stopBlock(%client,%size,%type) { + %plyr = %client.player; + %pos = pos(%plyr); + %maxRange = 500; + + %className = "ForceFieldBare"; + + if (!%type) + %type = 8; + if (!%size) + %size = 500; + + %planeSize = %size * 2; + + %pieces = mCeil(%planeSize / %maxRange); + %pieceSize = %planeSize / %pieces ; + + %point1 = vectorAdd(%pos, %size SPC %size SPC %size); + %point2 = vectorAdd(%pos, vectorScale(%size SPC %size SPC %size,-1)); +// echo(%pos); +// echo(%point1); +// echo(%point2); + + for (%x =0; %x < %pieces;%x++) { + for (%y =0; %y < %pieces; %y++) { + %topWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = %pieceSize SPC %pieceSize SPC 0.5; + }; + %bottomWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = %pieceSize SPC %pieceSize SPC 0.5; + }; + %eastWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = %pieceSize SPC 0.5 SPC %pieceSize; + }; + %westWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = %pieceSize SPC 0.5 SPC %pieceSize; + }; + %northWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = 0.5 SPC %pieceSize SPC %pieceSize; + }; + %southWall = new (%className)() { + dataBlock = DeployedForceField @ %type; + scale = 0.5 SPC %pieceSize SPC %pieceSize; + }; + %topWall.setTransform(vectorAdd(%point1,-1 * %x * %pieceSize SPC -1 * %y * %pieceSize SPC "0") SPC "0 0 1 3.14"); + %eastWall.setTransform(vectorAdd(%point1,-1 * %x * %pieceSize SPC "0" SPC -1 * %y * %pieceSize) SPC "0 1 0 3.14"); + %northWall.setTransform(vectorAdd(%point1,"0" SPC -1 * %x * %pieceSize SPC -1 * %y * %pieceSize) SPC "1 0 0 3.14"); + %bottomWall.setTransform(vectorAdd(%point2,%x * %pieceSize SPC %y * %pieceSize SPC "0") SPC "0 0 1 0"); + %westWall.setTransform(vectorAdd(%point2,%x * %pieceSize SPC "0" SPC %y * %pieceSize) SPC "0 1 0 0"); + %southWall.setTransform(vectorAdd(%point2,"0" SPC %x * %pieceSize SPC %y * %pieceSize) SPC "1 0 0 0"); + + %topWall.team = %plyr.client.team; + %topWall.setOwner(%plyr); + %topWall.pzone.delete(); + %topWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + + %eastWall.team = %plyr.client.team; + %eastWall.setOwner(%plyr); + %eastWall.pzone.delete(); + %eastWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + + %northWall.team = %plyr.client.team; + %northWall.setOwner(%plyr); + %northWall.pzone.delete(); + %northWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + + %bottomWall.team = %plyr.client.team; + %bottomWall.setOwner(%plyr); + %bottomWall.pzone.delete(); + %bottomWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + + %westWall.team = %plyr.client.team; + %westWall.setOwner(%plyr); + %westWall.pzone.delete(); + %westWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + + %southWall.team = %plyr.client.team; + %southWall.setOwner(%plyr); + %southWall.pzone.delete(); + %southWall.addToBlockGroup(); + $TeamDeployedCount[%plyr.team,ForceFieldDeployable]++; + } + } +} + +function ForceFieldBare::addToBlockGroup(%obj) { + %group = nameToID("MissionCleanup/BlockGroup"); + if (%group <= 0) { + %group = new SimGroup("BlockGroup"); + MissionCleanup.add(%group); + } + %group.add(%obj); +} + +function removeBlock() { + %group = nameToID("MissionCleanup/BlockGroup"); + if (!(%group <= 0)) { + %count = %group.getCount(); + for(%i=0;%i<%count;%i++) { + %obj = %group.getObject(%i); + if (%obj) { + $TeamDeployedCount[%obj.team,ForceFieldDeployable]--; + } + } + %group.delete(); + } +} diff --git a/Scripts/staticShape.cs b/Scripts/staticShape.cs new file mode 100644 index 0000000..9eeab1b --- /dev/null +++ b/Scripts/staticShape.cs @@ -0,0 +1,1545 @@ +//****************************************************************************** +//* Default StaticShape functions +//****************************************************************************** + +function shouldChangePowerState(%obj,%state) { + if (%obj.lastState $= "") + return true; + if (%obj.isSwitchedOff == true) + return false; + return %obj.lastState != %state; +} + +function StaticShapeData::onGainPowerEnabled(%data,%obj) { + if (!shouldChangePowerState(%obj,true)) + return; + + if(%obj.isDoor) + { + if(%obj.canmove == true && %obj.powercontrol >= 1 && %obj.getDatablock().getName() !$= "DeployedDoor") + { + if(%obj.powerControl == 1) // open when powered + schedule(10, 0, "open", %obj); + else if(%obj.powerControl == 2) // closed when powered + schedule(10, 0, "close", %obj, 1); + } + } + + %obj.lastState = true; + if(%data.ambientThreadPowered) + %obj.playThread($AmbientThread, "ambient"); + // if it's a deployed object, schedule the power thread; else play it immediately + if(%data.deployAmbientThread) + %obj.schedule(750, "playThread", $PowerThread, "Power"); + else + %obj.playThread($PowerThread,"Power"); + // deployable objects get their recharge rate set right away -- don't set it again unless + // the object has just been re-enabled + if(%obj.initDeploy) + %obj.initDeploy = false; + else { + if(%obj.getRechargeRate() <= 0) { + if (%data.needsPower) { + %obj.setRechargeRate(%data.rechargeRate + ((%obj.powerCount - 1) * (%data.rechargeRate * 0.5) )); + } + else { + %oldERate = %obj.getRechargeRate(); + %obj.setRechargeRate(%oldERate + %data.rechargeRate); + } + } + } + if(%data.humSound !$= "") + %obj.playAudio($HumSound, %data.humSound); + %obj.setPoweredState(true); + if (%data.className $= "Generator") + checkPowerGenerator(%obj,1); +} + +function StaticShapeData::onLosePowerDisabled(%data,%obj) { + if (!shouldChangePowerState(%obj,false)) + return; + + if(%obj.isDoor && %obj.getDatablock().getName() !$= "DeployedDoor") + { + if(%obj.canmove == true && %obj.powercontrol >= 1) + { + if(%obj.powerControl == 1) // open when powered + schedule(10, 0, "close", %obj, 1); + else if(%obj.powerControl == 2) // closed when powered + schedule(10, 0, "open", %obj); + } + } + + %obj.lastState = false; + %client = %obj.getControllingClient(); + if(%client != 0) + serverCmdResetControlObject(%client); + + if(%data.ambientThreadPowered) + %obj.pauseThread($AmbientThread); + if(!%data.alwaysAmbient) { + %obj.stopThread($PowerThread); + // MES -- drop shields and stop them from regenerating after power loss + %obj.setRechargeRate(0.0); + %obj.setEnergyLevel(0.0); + } + if(%data.humSound !$= "") + %obj.stopAudio($HumSound); + %obj.setPoweredState(false); + if (%data.className $= "Generator") + checkPowerGenerator(%obj,-1); +} + +function StaticShapeData::onCollision(%data, %obj, %col) +{ + if(%obj.isDoor && %obj.getDatablock().getName() !$= "DeployedDoor" && %obj.canMove == true && %obj.collisionType > 0 && %col.getDataBlock().getClassName() $= "PlayerData") + { + switch$(%obj.collisionType) + { + case 1: + Open(%obj); + case 2: + if(%col.client.givenAccess[%obj.getOwner()] >= %obj.accessLevel || %obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2You need \c3level-"@%obj.accessLevel@"\c2 from "@%obj.getOwner().nameBase@" to access this door."); + case 3: + if(%obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2This door is only for \c3"@%obj.getOwner().nameBase@"."); + case 4: + if(%col.client.isAdmin || %obj.getOwner() == %col.client) + Open(%obj); + else + messageClient(%col.client, "", "\c3Access denied. \c2This door is for \c3admins \c2 or \c3"@%obj.getOwner().nameBase@"\c2 only."); + } + } + + // Don't call parent! It causes console spam! + // -- Eolk +} + +function StaticShapeData::gainPower(%data,%obj) { + if(%obj.isEnabled()) + %data.onGainPowerEnabled(%obj); + Parent::gainPower(%data,%obj); +} + +function StaticShapeData::losePower(%data,%obj) { + if(%obj.isEnabled()) + %data.onLosePowerDisabled(%obj); + Parent::losePower(%data,%obj); +} + +function ShapeBaseData::onEnabled() +{ +} + +function ShapeBaseData::onDisabled() +{ +} + +function StaticShapeData::onEnabled(%data, %obj, %prevState) { + if(%obj.isPowered()) + %data.onGainPowerEnabled(%obj); + Parent::onEnabled(%data, %obj, %prevState); +} + +function StaticShapeData::onDisabled(%data, %obj, %prevState) { + if(%obj.isPowered() || (%data.className $= "Generator")) + %data.onLosePowerDisabled(%obj); + Parent::onDisabled(%data, %obj, %prevState); +} + +function StaticShape::deploy(%this) +{ + %this.playThread($DeployThread, "deploy"); +} + +function StaticShapeData::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $DeployThread && !%data.needsPower) + %obj.setSelfPowered(); + Parent::onEndSequence(%data, %obj, %thread); +} + +function ShapeBaseData::onEndSequence() +{ +} + +//****************************************************************************** +//* Example explosion +//****************************************************************************** + +datablock EffectProfile(ShapeExplosionEffect) +{ + effectname = "explosions/explosion.xpl03"; + minDistance = 10; + maxDistance = 50; +}; + +datablock AudioProfile(ShapeExplosionSound) +{ + filename = "fx/explosions/explosion.xpl03.wav"; + description = AudioExplosion3d; + preload = true; + effect = ShapeExplosionEffect; +}; + +datablock ExplosionData(ShapeExplosion) +{ + explosionShape = "disc_explosion.dts"; + soundProfile = ShapeExplosionSound; + faceViewer = true; +}; + +//****************************************************************************** +//* Player Armors - Data Blocks (live players are now StaticTSObjects) +//****************************************************************************** + +datablock StaticShapeData(HeavyMaleHuman_Dead) +{ + className = "deadArmor"; + catagory = "Player Armors"; + shapeFile = "heavy_male_dead.dts"; + isInvincible = true; +}; + +datablock StaticShapeData(MediumMaleHuman_Dead) +{ + className = "deadArmor"; + catagory = "Player Armors"; + shapeFile = "medium_male_dead.dts"; + isInvincible = true; +}; + +datablock StaticShapeData(LightMaleHuman_Dead) +{ + className = "deadArmor"; + catagory = "Player Armors"; + shapeFile = "light_male_dead.dts"; + isInvincible = true; +}; + +function deadArmor::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); +} + +//***************************************************************************** +//* Flagstands - Data Blocks +//***************************************************************************** +datablock StaticShapeData(InteriorFlagStand) +{ + className = "FlagIntStand"; + catagory = "Objectives"; + shapefile = "int_flagstand.dts"; + isInvincible = true; + needsNoPower = true; +}; + +datablock StaticShapeData(ExteriorFlagStand) +{ + className = "FlagIntStand"; + catagory = "Objectives"; + shapefile = "ext_flagstand.dts"; + isInvincible = true; + needsNoPower = true; +}; + +/////////////////////////////////////////// +//flagIntStand::onAdd(%this, %obj) +//%this: objects datablock +//%obj: the actual object being added +/////////////////////////////////////////// + +function ExteriorFlagStand::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.playThread($ActivateThread, "activate"); +} + +function ExteriorFlagStand::onFlagTaken(%this, %obj) +{ + %obj.setThreadDir($ActivateThread, 0); +} + +function ExteriorFlagStand::onFlagReturn(%this, %obj) +{ + %obj.setThreadDir($ActivateThread, 1); +} + +function ExteriorFlagStand::onCollision(%this, %obj, %colObj) +{ + game.flagStandCollision(%this, %obj, %colObj); +} + +function InteriorFlagStand::onCollision(%this, %obj, %colObj) +{ + game.flagStandCollision(%this, %obj, %colObj); +} + +/////////////////////////////////////////////// +//end flag stand functions +/////////////////////////////////////////////// + +datablock StaticShapeData(FlipFlop) +{ + catagory = "Objectives"; + shapefile = "switch.dts"; + + isInvincible = true; + cmdCategory = "Objectives"; + cmdIcon = "CMDSwitchIcon"; + cmdMiniIconName = "commander/MiniIcons/com_switch_grey"; + targetTypeTag = 'Switch'; + alwaysAmbient = true; + needsNoPower = true; + emap = true; +}; + +function FlipFlop::onCollision(%data,%obj,%col) +{ + if (%col.getDataBlock().className $= Armor && %col.getState() !$= "Dead") + %data.playerTouch(%obj, %col); +} + +function FlipFlop::playerTouch(%data,%obj,%col) +{ + messageAll('MsgPlayerTouchSwitch', 'Player %1 touched switch %2', %col, %obj); +} + +//****************************************************************************** +//* Organics - * +//****************************************************************************** + +//function to add random Organics to mission pre-creation +//(could be used to add other things...its cool with bioderm armors ;) ) + +function randomOrg(%organicName, %num, %radius) +{ + %SPACING = 1.0; //meters between center of organic and another object + + //return help info + if(%organicName $="" || !%num || !%radius) { + echo("randomOrg(, , );"); + return; + } + + %organicIndex = -1; + // -------------------------------------------------------- + // z0dd - ZOD, 5/8/02. Typo fix. + //for (%i = 0; %i < $NumAStaticTSObjects; %i++) { + for (%i = 0; %i < $NumStaticTSObjects; %i++) { + if (getWord($StaticTSObjects[%i], 1) $= %organicName) { + %organicIndex = %i; + break; + } + } + if (%organicIndex == -1) { + error("There is no static shape named" SPC %organicName); + return; + } + %shapeFileName = getWord($StaticTSObjects[%organicIndex], 2); + + %maxSlope = getWord($StaticTSObjects[%organicIndex], 3); + if (%maxSlope $= "") + %maxSlope = 40; + + %zOffset = getWord($StaticTSObjects[%organicIndex], 4); + if (%zOffset $= "") + %zOffset = 0; + + %slopeWithTerrain = getWord($StaticTSObjects[%organicIndex], 5); + if (%slopeWithTerrain $= "") + %slopeWithTerrain = false; + + %minScale = getWord($StaticTSObjects[%organicIndex], 6); + %maxScale = getWord($StaticTSObjects[%organicIndex], 7); + + //set up folders in mis file + $RandomOrganicsAdded++; //to keep track of groups + if(!isObject(RandomOrganics)) { + %randomOrgGroup = new simGroup(RandomOrganics); + MissionGroup.add(%randomOrgGroup); + } + %groupName = "Addition"@$RandomOrganicsAdded@%organicName; + %group = new simGroup(%groupName); + RandomOrganics.add(%group); + + + %ctr = LocalClientConnection.camera.getPosition(); + %areaX = getWord(%ctr, 0) - %radius; + %areaY = getWord(%ctr, 1) - %radius; + + %orgCount = %num; + while((%orgCount > 0) && (%retries < (15000 / %maxSlope))) //theoretically, a thorough number of retries + { + //find a tile + %x = (getRandom(mFloor(%areaX / 8), mFloor((%areaX + (%radius * 2)) / 8)) * 8) + 4; //tile center + %y = (getRandom(mFloor(%areaY / 8), mFloor((%areaY + (%radius * 2)) / 8)) * 8) + 4; + + %start = %x @ " " @ %y @ " 2000"; + %end = %x @ " " @ %y @ " -1"; + %ground = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + %z = getWord(%ground, 3); + %z += %zOffset; + %position = %x @ " " @ %y @ " " @ %z; + + + // get normal from both sides of the square + %start = %x + 2 @ " " @ %y @ " 2000"; + %end = %x + 2 @ " " @ %y @ " -1"; + %hit1 = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + + %start = %x - 2 @ " " @ %y @ " 2000"; + %end = %x - 2 @ " " @ %y @ " -1"; + %hit2 = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + + %norm1 = getWord(%hit1, 4) @ " " @ getWord(%hit1, 5) @ " " @ getWord(%hit1, 6); + %norm2 = getWord(%hit2, 4) @ " " @ getWord(%hit2, 5) @ " " @ getWord(%hit2, 6); + + //if either side of tile has greater slope than allowed, move on. + %angNorm1 = getTerrainAngle(%norm1); + %angNorm2 = getTerrainAngle(%norm2); + if ((getTerrainAngle(%norm1) > %maxSlope) || (getTerrainAngle(%norm2) > %maxslope)) + { + %retries++; + continue; + } + + %terrainNormal = VectorAdd(%norm1, %norm2); + %terrainNormal = VectorNormalize(%terrainNormal); + + //search surroundings for obstacles. If obstructed, move on. + InitContainerRadiusSearch(%position, %spacing, $TypeMasks::VehicleObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + // ----------------------------- + // z0dd - ZOD, 5/8/02. Typo fix. + //$TypeMasks::TSStaticShapeObjectType | + $TypeMasks::StaticTSObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::ItemObjectType); + %this = containerSearchNext(); + if(%this) + { + %retries++; + continue; + } + + + //rotate it + if(%slopeWithTerrain) + { + %rotAxis = vectorCross(%terrainNormal, "0 0 1"); + %rotAxis = vectorNormalize(%rotAxis); + %rotation = %rotAxis @ " " @ getTerrainAngle(%terrainNormal); + } + else %rotation = "1 0 0 0"; + %randomAngle = getRandom(360); + %zrot = MatrixCreate("0 0 0", "0 0 1 " @ %randomAngle); + %orient = MatrixCreate(%position, %rotation); + %finalXForm = validateVal(MatrixMultiply(%orient, %zrot)); + + + //scale it + %scaleMin = 8; //default min + %scaleMax = 14; //default max + if(%minScale) + %scaleMin = %minScale * 10; + if(%maxScale) + %scaleMax = %maxScale * 10; + %scaleInt = getRandom(%scaleMin, %scaleMax); + %scale = %scaleInt/10; + %evenScale = %scale SPC %scale SPC %scale; + + //create it + %position = %x SPC %y SPC (%z += %zoffset); + %newOrganic = new TSStatic() { + position = %position; + rotation = %rotation; + scale = %evenScale; + shapeName = %shapeFileName; + }; + %group.add(%newOrganic); + %newOrganic.setTransform(%finalXForm); + + %orgCount--; //dec number of shapes left to place + %retries = 0; //reset retry counter + } + if (%orgCount > 0) + { + error("Unable to place all shapes, area saturated."); + error("Looking for clear area " @ (%spacing * 2) @ " meters in diameter, with a max slope of " @ %maxSlope); + } + echo("Placed " @ %num - %orgCount @ " of " @ %num); +} + +function getTerrainAngle(%point) +{ + %up = "0 0 1"; + %angleRad = mACos(vectorDot(%point, %up)); + %angleDeg = mRadToDeg(%angleRad); + //echo("angle is "@%angleDeg); + return %angleDeg; +} +function randomGrove(%organicName, %num, %radius) +{ + %minHeight = 0; + %maxHeight = 1000; + %SPACING = 1.5; //meters between center of organic and another object + + //return help info + if(%organicName $="" || !%num || !%radius) { + echo("randomOrg(, [, radius of grove desired]);"); + return; + } + + %organicIndex = -1; + for (%i = 0; %i < $NumStaticTSObjects; %i++) { + if (getWord($StaticTSObjects[%i], 1) $= %organicName) { + %organicIndex = %i; + break; + } + } + if (%organicIndex == -1) { + error("There is no static shape named" SPC %organicName); + return; + } + %shapeFileName = getWord($StaticTSObjects[%organicIndex], 2); + + %maxSlope = getWord($StaticTSObjects[%organicIndex], 3); + if (%maxSlope $= "") + %maxSlope = 40; + + %zOffset = getWord($StaticTSObjects[%organicIndex], 4); + if (%zOffset $= "") + %zOffset = 0; + + %slopeWithTerrain = getWord($StaticTSObjects[%organicIndex], 5); + if (%slopeWithTerrain $= "") + %slopeWithTerrain = false; + + %minScale = getWord($StaticTSObjects[%organicIndex], 6); + %maxScale = getWord($StaticTSObjects[%organicIndex], 7); + + //set up folders in mis file + $RandomOrganicsAdded++; //to keep track of groups + if(!isObject(RandomOrganics)) { + %randomOrgGroup = new simGroup(RandomOrganics); + MissionGroup.add(%randomOrgGroup); + } + %groupName = "Addition"@$RandomOrganicsAdded@%organicName; + %group = new simGroup(%groupName); + RandomOrganics.add(%group); + + + %ctr = LocalClientConnection.camera.getPosition(); + %areaX = getWord(%ctr, 0) - %radius; + %areaY = getWord(%ctr, 1) - %radius; + + %orgCount = %num; + while((%orgCount > 0) && (%retries < (15000 / %maxSlope))) //theoretically, a thorough number of retries + { + //find a tile + %x = (getRandom(mFloor(%areaX / 8), mFloor((%areaX + (%radius * 2)) / 8)) * 8) + 4; //tile center + %y = (getRandom(mFloor(%areaY / 8), mFloor((%areaY + (%radius * 2)) / 8)) * 8) + 4; + + %start = %x @ " " @ %y @ " 2000"; + %end = %x @ " " @ %y @ " -1"; + %ground = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + %z = getWord(%ground, 3); + + + // elevation test + if ((%z < %minHeight) || (%z > %maxHeight)) + { + echo("Broke height range rules. Readjust allowable elevations."); + %retries++; + echo("Z is " @ %z); + continue; + } + %z += %zOffset; + %position = %x @ " " @ %y @ " " @ %z; + + + // get normal from both sides of the square + %start = %x + 2 @ " " @ %y @ " 2000"; + %end = %x + 2 @ " " @ %y @ " -1"; + %hit1 = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + + %start = %x - 2 @ " " @ %y @ " 2000"; + %end = %x - 2 @ " " @ %y @ " -1"; + %hit2 = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + + %norm1 = getWord(%hit1, 4) @ " " @ getWord(%hit1, 5) @ " " @ getWord(%hit1, 6); + %norm2 = getWord(%hit2, 4) @ " " @ getWord(%hit2, 5) @ " " @ getWord(%hit2, 6); + + //if either side of tile has greater slope than allowed, move on. + %angNorm1 = getTerrainAngle(%norm1); + %angNorm2 = getTerrainAngle(%norm2); + if ((getTerrainAngle(%norm1) > %maxSlope) || (getTerrainAngle(%norm2) > %maxslope)) + { + %retries++; + continue; + } + + %terrainNormal = VectorAdd(%norm1, %norm2); + %terrainNormal = VectorNormalize(%terrainNormal); + + //search surroundings for obstacles. If obstructed, move on. + InitContainerRadiusSearch(%position, %spacing, $TypeMasks::VehicleObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + // ----------------------------- + // z0dd - ZOD, 5/8/02. Typo fix. + //$TypeMasks::TSStaticShapeObjectType | + $TypeMasks::StaticTSObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::ItemObjectType); + %this = containerSearchNext(); + if(%this) + { + %retries++; + continue; + } + + + //rotate it + if(%slopeWithTerrain) + { + %rotAxis = vectorCross(%terrainNormal, "0 0 1"); + %rotAxis = vectorNormalize(%rotAxis); + %rotation = %rotAxis @ " " @ getTerrainAngle(%terrainNormal); + } + else %rotation = "1 0 0 0"; + %randomAngle = getRandom(360); + %zrot = MatrixCreate("0 0 0", "0 0 1 " @ %randomAngle); + %orient = MatrixCreate(%position, %rotation); + %finalXForm = validateVal(MatrixMultiply(%orient, %zrot)); + + + //scale it + %scaleMin = 8; //default min + %scaleMax = 14; //default max + if(%minScale) + %scaleMin = %minScale * 10; + if(%maxScale) + %scaleMax = %maxScale * 10; + %scaleInt = getRandom(%scaleMin, %scaleMax); + %scale = %scaleInt/10; + %evenScale = %scale SPC %scale SPC %scale; + + //create it + + %position = %x SPC %y SPC (%z += %zoffset); + %newOrganic = new TSStatic() { + position = %position; + rotation = %rotation; + scale = %evenScale; + shapeName = %shapeFileName; + }; + %group.add(%newOrganic); + %newOrganic.setTransform(%finalXForm); + + %orgCount--; //dec number of shapes left to place + %retries = 0; //reset retry counter + } + if (%orgCount > 0) + { + error("Unable to place all shapes, area saturated."); + error("Looking for clear area " @ (%spacing * 2) @ " meters in diameter, with a max slope of " @ %maxSlope); + } + echo("Placed " @ %num - %orgCount @ " of " @ %num); +} + + +function randomRock(%rock, %quantity, %radius, %maxElev) +{ + if (!%radius || !%quantity || !%rock) + { + echo("randomRock(, , , [maximum elevation]"); + return; + } + + if (!%maxElev) + %maxElev = 2000; + + + %rotation[0] = "0 0 1 0"; + %rotation[1] = "0.999378 -0.0145686 -0.0321219 194.406"; + %rotation[2] = "0.496802 0.867682 0.0177913 176.44"; + %rotation[3] = "0.991261 0.0933696 0.0931923 181.867"; + %rotation[4] = "0.246801 0.360329 -0.899584 92.3648"; + %rotation[5] = "1 0 0 82.59"; + %rotation[6] = "0.0546955 -0.629383 0.55201 116.103"; + + %spacing = 4.0; //check 4 meters around object for collisions before placing + %ctr = localClientConnection.camera.getPosition(); + %areaX = getWord(%ctr, 0) - %radius; + %areaY = getWord(%ctr, 1) - %radius; + + $RandomOrganicsAdded++; + if(!isObject(RandomRocks)) { + %randomOrgGroup = new simGroup(RandomRocks); + MissionGroup.add(%randomOrgGroup); + } + %groupName = "Addition"@$RandomOrganicsAdded@%rock; + %group = new simGroup(%groupName); + RandomRocks.add(%group); + + %orgCount = %quantity; + while((%orgCount > 0) && (%retries < (15000 / %maxSlope))) //theoretically, a thorough number of retries + { + //find a tile + %x = %areaX + getRandom(%radius * 2); + %y = %areaY + getRandom(%radius * 2); + + %start = %x @ " " @ %y @ " 2000"; + %end = %x @ " " @ %y @ " -1"; + %ground = containerRayCast(%start, %end, $TypeMasks::TerrainObjectType, 0); + %position = getWord(%ground, 1) @ " " @ getWord(%ground, 2) @ " " @ getWord(%ground, 3); + echo("position =*" @ %position @ "*"); + %z = getWord(%position, 2); + + + // elevation test + if (%z > %maxElev) //65 meters and above only + { + %retries++; + echo("Z is " @ %z); + continue; + } + + + //search surroundings for obstacles. If obstructed, move on. + InitContainerRadiusSearch(%position, %spacing, $TypeMasks::VehicleObjectType | + $TypeMasks::MoveableObjectType | + $TypeMasks::StaticShapeObjectType | + // ----------------------------- + // z0dd - ZOD, 5/8/02. Typo fix. + //$TypeMasks::TSStaticShapeObjectType | + $TypeMasks::StaticTSObjectType | + $TypeMasks::ForceFieldObjectType | + $TypeMasks::TurretObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::ItemObjectType); + %this = containerSearchNext(); + if(%this) + { + %retries++; + continue; + } + %primaryRot = %rotation[getRandom(7)]; + + %randomAngle = mDegToRad(getRandom(360)); + %zrot = MatrixCreate("0 0 0", "0 0 1 " @ %randomAngle); + %orient = MatrixCreate(%position, %primaryRot); + + + %scale = getRandom(3); + %evenScale = %scale @ " " @ %scale @ " " @ %scale; + %newRock = new InteriorInstance() { + scale = %evenScale; + interiorFile = %rock @ ".dif"; + showTerrainInside = "0"; + }; + %group.add(%newRock); + %transfrm = validateVal(MatrixMultiply(%orient, %zrot)); + echo("Transform = *" @ %transfrm @ "*"); + %newRock.setTransform(%transfrm); + + %orgCount--; //dec number of shapes left to place + %retries = 0; //reset retry counter + } + if (%orgCount > 0) + { + error("Unable to place all shapes, area saturated."); + error("Looking for clear area " @ (%spacing * 2) @ " meters in diameter, with a max slope of " @ %maxSlope); + } + echo("Placed " @ %num - %orgCount @ " of " @ %num); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- Organics +//-------------------------------------------------------------------------- + + +//****************************************************************************** +//* Pulse Sensor - Data Blocks * +//****************************************************************************** + +datablock DebrisData( StaticShapeDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.20; + friction = 0.5; + + lifetime = 17.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 0.4; + + velocity = 9.0; + velocityVariance = 4.5; +}; + +datablock DebrisData( SmallShapeDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.20; + friction = 0.5; + + lifetime = 17.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 0.2; + + velocity = 5.0; + velocityVariance = 2.5; +}; + +datablock AudioProfile(SensorHumSound) +{ + filename = "fx/powered/sensor_hum.wav"; + description = CloseLooping3d; + preload = true; +}; + +datablock SensorData(SensorLgPulseObj) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 300; +}; + +datablock StaticShapeData(SensorLargePulse) : StaticShapeDamageProfile +{ + className = Sensor; + catagory = "Sensors"; + shapeFile = "sensor_pulse_large.dts"; + maxDamage = 1.5; + destroyedLevel = 1.5; + disabledLevel = 0.85; + explosion = ShapeExplosion; + expDmgRadius = 10.0; + expDamage = 0.5; + expImpulse = 2000.0; + repairRate = 0.5; + + dynamicType = $TypeMasks::SensorObjectType; + isShielded = true; + energyPerDamagePoint = 33; + maxEnergy = 110; + rechargeRate = 0.31; + ambientThreadPowered = true; + humSound = SensorHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_sensor_grey"; + targetNameTag = 'Large'; + targetTypeTag = 'Sensor'; + sensorData = SensorLgPulseObj; + sensorRadius = SensorLgPulseObj.detectRadius; + sensorColor = "255 194 9"; + + debrisShapeName = "debris_generic.dts"; + debris = StaticShapeDebris; + needsPower = true; +}; + +datablock SensorData(SensorMedPulseObj) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 200; +}; + +datablock StaticShapeData(SensorMediumPulse) : StaticShapeDamageProfile +{ + className = Sensor; + catagory = "Sensors"; + shapeFile = "sensor_pulse_medium.dts"; + maxDamage = 1.2; + destroyedLevel = 1.2; + disabledLevel = 0.68; + explosion = ShapeExplosion; + expDmgRadius = 7.0; + expDamage = 0.4; + expImpulse = 1500; + repairRate = 0.5; + + dynamicType = $TypeMasks::SensorObjectType; + isShielded = true; + energyPerDamagePoint = 33; + maxEnergy = 90; + rechargeRate = 0.31; + ambientThreadPowered = true; + humSound = SensorHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDSensorIcon; + cmdMiniIconName = "commander/MiniIcons/com_sensor_grey"; + targetNameTag = 'Medium'; + targetTypeTag = 'Sensor'; + sensorData = SensorMedPulseObj; + sensorRadius = SensorMedPulseObj.detectRadius; + sensorColor = "255 194 9"; + + debrisShapeName = "debris_generic.dts"; + debris = StaticShapeDebris; + needsPower = true; +}; + +function Sensor::onGainPowerEnabled(%data, %obj) +{ + setTargetSensorData(%obj.target, %data.sensorData); + Parent::onGainPowerEnabled(%data, %obj); +} + +function Sensor::onLosePowerDisabled(%data, %obj) +{ + setTargetSensorData(%obj.target, 0); + Parent::onLosePowerDisabled(%data, %obj); +} + +//****************************************************************************** +//* Generator - Data Blocks * +//****************************************************************************** + +datablock AudioProfile(GeneratorHumSound) +{ + filename = "fx/powered/generator_hum.wav"; + description = CloseLooping3d; + preload = true; +}; + + +datablock StaticShapeData(GeneratorLarge) : StaticShapeDamageProfile +{ + className = Generator; + catagory = "Generators"; + shapeFile = "station_generator_large.dts"; + explosion = ShapeExplosion; + maxDamage = 1.50; + destroyedLevel = 1.50; + disabledLevel = 0.85; + expDmgRadius = 10.0; + expDamage = 0.5; + expImpulse = 1500.0; + noIndividualDamage = true; //flag to make these invulnerable for certain mission types + + dynamicType = $TypeMasks::GeneratorObjectType; + isShielded = true; + energyPerDamagePoint = 30; + maxEnergy = 50; + rechargeRate = 0.05; + humSound = GeneratorHumSound; + + cmdCategory = "Support"; + cmdIcon = "CMDGeneratorIcon"; + cmdMiniIconName = "commander/MiniIcons/com_generator"; + targetTypeTag = 'Generator'; + + debrisShapeName = "debris_generic.dts"; + debris = StaticShapeDebris; + powerRadius = 100; +}; + +datablock StaticShapeData(SolarPanel) : StaticShapeDamageProfile +{ + className = Generator; + catagory = "Generators"; + shapeFile = "solarpanel.dts"; + explosion = ShapeExplosion; + maxDamage = 1.00; + destroyedLevel = 1.00; + disabledLevel = 0.55; + expDmgRadius = 5.0; + expDamage = 0.3; + expImpulse = 1000.0; + noIndividualDamage = true; //flag to make these invulnerable for certain mission types + emap = true; + + isShielded = true; + energyPerDamagePoint = 30; + rechargeRate = 0.05; + + dynamicType = $TypeMasks::GeneratorObjectType; + maxEnergy = 30; + humSound = GeneratorHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDSolarGeneratorIcon; + cmdMiniIconName = "commander/MiniIcons/com_solargen_grey"; + targetTypeTag = 'Solar Panel'; + + debrisShapeName = "debris_generic.dts"; + debris = StaticShapeDebris; + powerRadius = 50; +}; + +function Generator::onDisabled(%data, %obj, %prevState) { + %obj.decPowerCount(); + Parent::onDisabled(%data, %obj, %prevState); +} + +function Generator::onEnabled(%data, %obj, %prevState) { + %obj.incPowerCount(); + Parent::onEnabled(%data, %obj, %prevState); +} + +//****************************************************************************** +//Nexus Effect (Hunters) +//****************************************************************************** + +datablock StaticShapeData(Nexus_Effect) +{ + catagory = "Objectives"; + shapefile = "nexus_effect.dts"; + mass = 10; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; +}; + + +datablock StaticShapeData(NexusBase) +{ + catagory = "Objectives"; + shapefile = "Nexusbase.dts"; + mass = 10; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; +}; + + +datablock StaticShapeData(NexusCap) +{ + catagory = "Objectives"; + shapefile = "Nexuscap.dts"; + mass = 10; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; +}; + +//****************************************************************************** +//* Static Shape - Functions * +//****************************************************************************** + +function StaticShapeData::create(%block) +{ + %obj = new StaticShape() + { + dataBlock = %block; + }; + return(%obj); +} + +function ShapeBase::damage(%this, %sourceObject, %position, %amount, %damageType) +{ + %this.getDataBlock().damageObject(%this, %sourceObject, %position, %amount, %damageType); +} + +function ShapeBaseData::damageObject(%data, %targetObject, %position, %sourceObject, %amount, %damageType) +{ + +} + +function ShapeBaseData::onDestroyed(%data, %obj, %prevState) +{ + +} + +function ShapeBaseData::checkShields(%data, %targetObject, %position, %amount, %damageType) +{ + %energy = %targetObject.getEnergyLevel(); + %strength = %energy / %data.energyPerDamagePoint; + %shieldScale = %data.shieldDamageScale[%damageType]; + if(%shieldScale $= "") + %shieldScale = 1; + + if (%amount * %shieldScale <= %strength) { + // Shield absorbs all + %lost = %amount * %shieldScale * %data.energyPerDamagePoint; + %energy -= %lost; + %targetObject.setEnergyLevel(%energy); + + %normal = "0.0 0.0 1.0"; + %targetObject.playShieldEffect( %normal ); + + return 0; + } + // Shield exhausted + %targetObject.setEnergyLevel(0); + return %amount - %strength / %shieldScale; +} + +function StaticShapeData::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) { + if (%targetObject.getType() & $TypeMasks::ForceFieldObjectType) + %isFF = true; + if (%targetObject.getType() & $TypeMasks::TurretObjectType) + %isTurret = true; + if ($Host::InvincibleDeployables == 1) { + if (%targetObject.getGroup() == Deployables.getID() + && !%data.powerRadius + && %data != StationInventory.getID() + && !%isFF + && !%isTurret) + return; + } + if (isObject(%sourceObject)) + if (%sourceObject.getType() & $TypeMasks::VehicleObjectType) + if (%sourceObject.getDataBlock() == EscapePodVehicle.getID()) { + // Reverse damage ;) + %tmp = %sourceObject; + %sourceObject = %targetObject; + %targetObject = %tmp; + } + + if (%targetObject.lTarget) { + if (isObject(%targetObject.lTarget)) { + return; + } + } + else if (%targetObject.lMain) { + if (isObject(%targetObject.lMain)) { + %targetObject = %targetObject.lMain; + } + } + + // if this is a non-team mission type and the object is "protected", don't damage it + if(%data.noIndividualDamage && Game.allowsProtectedStatics() && !%targetObject.deployed) + return; + + // if this is a Siege mission and this object shouldn't take damage (e.g. vehicle stations) + if(%data.noDamageInSiege && Game.class $= "SiegeGame") + return; + + // Don't let deployed turrets damage other deployed items of the same team + if (isObject(%sourceObject) && isObject(%targetObject)) { + if ((%targetObject.getDataBlock().deployedObject || %targetObject.deployed) + && (%sourceObject.getType() & $TypeMasks::TurretObjectType) + && %sourceObject.team == %targetObject.team) + return; + } + + // Switch to not let deployed turrets damage any deployed item of any team + if (isObject(%sourceObject) && isObject(%targetObject) && $NoTurretDamage == 1) { + if ((%targetObject.getDataBlock().deployedObject || %targetObject.deployed) + && (%sourceObject.getType() & $TypeMasks::TurretObjectType)) + return; + } + + if(%sourceObject && %targetObject.isEnabled()) + { + if(%sourceObject.client) + { + %targetObject.lastDamagedBy = %sourceObject.client; + %targetObject.lastDamagedByTeam = %sourceObject.client.team; + %targetObject.damageTimeMS = GetSimTime(); + } + else + { + %targetObject.lastDamagedBy = %sourceObject; + %targetObject.lastDamagedByTeam = %sourceObject.team; + %targetObject.damageTimeMS = GetSimTime(); + } + } + + // 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 team damage is off, cap the amount of damage so as not to disable the object... + if (!$TeamDamage && !%targetObject.getDataBlock().deployedObject) { + // ------------------------------------- + // z0dd - ZOD, 6/24/02. Console spam fix + if(isObject(%sourceObject)) { + //see if the object is being shot by a friendly + if(%sourceObject.getDataBlock().catagory $= "Vehicles") + %attackerTeam = getVehicleAttackerTeam(%sourceObject); + else + %attackerTeam = %sourceObject.team; + } + if (((%targetObject.getTarget() != -1) && isTargetFriendly(%targetObject.getTarget(), %attackerTeam)) && !%isFF) { + %curDamage = %targetObject.getDamageLevel(); + %availableDamage = %targetObject.getDataBlock().disabledLevel - %curDamage - 0.05; + if (%amount > %availableDamage) + %amount = %availableDamage; + } + } + + // if there's still damage to apply + if (%amount > 0) { + if (%isFF) + %targetObject.applyDamage(%amount,%sourceObject,%position,%damageType); + else + %targetObject.applyDamage(%amount); + } +} + +// little special casing for the above function +function getVehicleAttackerTeam(%vehicleId) +{ + %name = %vehicleId.getDataBlock().getName(); + if(%name $= "BomberFlyer" || %name $= "AssaultVehicle") + %gunner = %vehicleId.getMountNodeObject(1); + else + %gunner = %vehicleId.getMountNodeObject(0); + + if(%gunner) + return %gunner.team; + + return %vehicleId.team; +} + +function StaticShapeData::onDamage(%this,%obj) { + // Set damage state based on current damage level + %damage = %obj.getDamageLevel(); + if(%damage >= %this.destroyedLevel) { + if(%obj.getDamageState() !$= "Destroyed") { + %obj.setDamageState(Destroyed); + // if object has an explosion damage radius associated with it, apply explosion damage + if(%this.expDmgRadius) { + RadiusExplosion(%obj, %obj.getWorldBoxCenter(), %this.expDmgRadius, %this.expDamage, %this.expImpulse, %obj, $DamageType::Explosion); + %obj.hasExploded = true; + } + %obj.setDamageLevel(%this.maxDamage); + } + } + else { + if(%damage >= %this.disabledLevel) { + if(%obj.getDamageState() !$= "Disabled") + %obj.setDamageState(Disabled); + } + else { + if(%obj.getDamageState() !$= "Enabled") + %obj.setDamageState(Enabled); + } + } +} + +function StaticShapeData::onDestroyed(%data,%obj,%prevState) { + if(%data.expDmgRadius && !%obj.hasExploded) { + RadiusExplosion(%obj, %obj.getWorldBoxCenter(), %data.expDmgRadius, %data.expDamage, %data.expImpulse, %obj, $DamageType::Explosion); + %obj.hasExploded = true; + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +// -------------------------------------------------------------------- +// Team logos - only the logo projector should be placed in a mission + +datablock StaticShapeData(BaseLogo) //storm logo +{ + className = Logo; + shapeFile = "teamlogo_storm.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(BaseBLogo) //Inferno Logo +{ + className = Logo; + shapeFile = "teamlogo_inf.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(BiodermLogo) +{ + className = Logo; + shapeFile = "teamlogo_bd.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(BEagleLogo) +{ + className = Logo; + shapeFile = "teamlogo_be.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(DSwordLogo) +{ + className = Logo; + shapeFile = "teamlogo_ds.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(COTPLogo) +{ + className = Logo; + shapeFile = "teamlogo_hb.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(SwolfLogo) +{ + className = Logo; + shapeFile = "teamlogo_sw.dts"; + alwaysAmbient = true; +}; + +datablock StaticShapeData(LogoProjector) +{ + className = Projector; + catagory = "Objectives"; + shapeFile = "teamlogo_projector.dts"; + alwaysAmbient = true; + isInvincible = true; +}; + +function Projector::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + %obj.holo = 0; +} + +//////////////////////////////////////////// +// Tapestries +/////////////////////////////////////////// +datablock StaticShapeData(Banner_Honor) +{ + catagory = "Eyecandy"; + shapefile = "banner_honor.dts"; +}; + +datablock StaticShapeData(Banner_Strength) +{ + catagory = "Eyecandy"; + shapefile = "banner_strength.dts"; +}; + +datablock StaticShapeData(Banner_Unity) +{ + catagory = "Eyecandy"; + shapefile = "banner_unity.dts"; +}; + +//////////////////////////////////////////////////////////////////////////////// +// + +//-------------------------------------------------------------------------- +// Totally static objects +// The format of these strings are: +// 0: Catagory +// 1: Name +// 2: File +// 3: MaxSlope [ only used with the randomOrg function ] +// 4: ZOffset [ only used with the randomOrg function ] +// 5: slopeWithTerrain [ only used with the randomOrg function ] +// 6: minScale [ only used with the randomOrg function ] +// 7: maxScale [ only used with the randomOrg function ] + + +$StaticTSObjects[0] = "Organics BiodermPlant3 xorg3.dts"; +$StaticTSObjects[1] = "Organics BiodermPlant4 xorg4.dts"; +$StaticTSObjects[2] = "Organics BiodermPlant5 xorg5.dts"; +$StaticTSObjects[3] = "Organics BiodermPlant20 xorg20.dts"; +$StaticTSObjects[4] = "Organics BiodermPlant21 xorg21.dts"; +$StaticTSObjects[5] = "Organics BiodermPlant22 xorg22.dts"; + +$StaticTSObjects[6] = "Organics BEPlant1 borg1.dts 40 0.35 1 0.5 2"; +$StaticTSObjects[7] = "Organics BEPlant5 borg5.dts 40 0.0 1 1 1.5"; +$StaticTSObjects[8] = "Organics BEPlant6 borg6.dts"; +$StaticTSObjects[9] = "Organics BEPlant7 borg7.dts"; + +$StaticTSObjects[10] = "Organics BEPlant12 borg12.dts"; +$StaticTSObjects[11] = "Organics BEPlant13 borg13.dts"; +$StaticTSObjects[12] = "Organics BELgTree16 borg16.dts 20 -3.0 0 0.8 1.5"; +$StaticTSObjects[13] = "Organics BESmTree17 borg17.dts 20 -3.0 1 0.8 1.5"; +$StaticTSObjects[14] = "Organics BELgTree18 borg18.dts 20 -3.0 0 0.8 1.5"; +$StaticTSObjects[15] = "Organics BELgTree19 borg19.dts 20 -3.0 0 0.8 1.5"; +$StaticTSObjects[16] = "Organics BEPlant20 borg20.dts"; + +$StaticTSObjects[17] = "Organics BEPlant23 borg23.dts"; +$StaticTSObjects[18] = "Organics BEPlant25 borg25.dts"; + +$StaticTSObjects[19] = "Organics BEPlant31 borg31.dts"; +$StaticTSObjects[20] = "Organics BEPlant32 borg32.dts"; +$StaticTSObjects[21] = "Organics BEPlant33 borg33.dts"; +$StaticTSObjects[22] = "Organics BEPlant34 borg34.dts"; + +$StaticTSObjects[23] = "Organics PhoenixPlant1 porg1.dts"; +$StaticTSObjects[24] = "Organics PhoenixPlant2 porg2.dts"; +$StaticTSObjects[25] = "Organics PhoenixPlant3 porg3.dts"; +$StaticTSObjects[26] = "Organics PhoenixPlant5 porg5.dts 25 -0.2 1 0.6 1.0"; +$StaticTSObjects[27] = "Organics PhoenixPlant6 porg6.dts"; +$StaticTSObjects[28] = "Organics PhoenixPlant20 porg20.dts"; + +$StaticTSObjects[29] = "Organics PhoenixPlant22 porg22.dts 25 0.1 1 0.8 1.4"; +$StaticTSObjects[30] = "Organics SWTree20 sorg20.dts"; +$StaticTSObjects[31] = "Organics SWShrub21 sorg21.dts"; +$StaticTSObjects[32] = "Organics SWTree22 sorg22.dts"; +$StaticTSObjects[33] = "Organics SWShrub23 sorg23.dts"; +$StaticTSObjects[34] = "Organics SWShrub24 sorg24.dts"; +$StaticTSObjects[35] = "Stackables Crate1 stackable1l.dts"; +$StaticTSObjects[36] = "Stackables Crate2 stackable1m.dts"; +$StaticTSObjects[37] = "Stackables Crate3 stackable1s.dts"; +$StaticTSObjects[38] = "Stackables Crate4 stackable2l.dts"; +$StaticTSObjects[39] = "Stackables Crate5 stackable2m.dts"; +$StaticTSObjects[40] = "Stackables Crate6 stackable2s.dts"; +$StaticTSObjects[41] = "Stackables Crate7 stackable3l.dts"; +$StaticTSObjects[42] = "Stackables Crate8 stackable3m.dts"; +$StaticTSObjects[43] = "Stackables Crate9 stackable3s.dts"; +$StaticTSObjects[44] = "Stackables Crate10 stackable4l.dts"; +$StaticTSObjects[45] = "Stackables Crate11 stackable4m.dts"; +$StaticTSObjects[46] = "Stackables Crate12 stackable5l.dts"; +$StaticTSObjects[47] = "Debris ScoutWreckageShape vehicle_air_scout_wreck.dts"; +$StaticTSObjects[48] = "Debris TankWreckageShape vehicle_land_assault_wreck.dts"; +$StaticTSObjects[49] = "Organics DSPlant16 dorg16.dts 20 -3.0 0 0.8 1.5"; +$StaticTSObjects[50] = "Organics DSPlant17 dorg17.dts 20 -3.0 1 0.8 1.5"; +$StaticTSObjects[51] = "Organics DSPlant18 dorg18.dts 20 -3.0 0 0.8 1.5"; +$StaticTSObjects[52] = "Organics DSPlant19 dorg19.dts 20 -3.0 0 0.8 1.5"; + +$StaticTSObjects[53] = "PlayerArmors LightMaleHumanArmorImage light_male.dts"; +$StaticTSObjects[54] = "PlayerArmors MediumMaleHumanArmorImage medium_male.dts"; +$StaticTSObjects[55] = "PlayerArmors HeavyMaleHumanArmorImage heavy_male.dts"; +$StaticTSObjects[56] = "PlayerArmors LightFemaleHumanArmorImage light_female.dts"; +$StaticTSObjects[57] = "PlayerArmors MediumFemaleHumanArmorImage medium_female.dts"; +$StaticTSObjects[58] = "PlayerArmors HeavyFemaleHumanArmorImage heavy_male.dts"; +$StaticTSObjects[59] = "PlayerArmors LightMaleBiodermArmorImage bioderm_light.dts"; +$StaticTSObjects[60] = "PlayerArmors MediumMaleBiodermArmorImage bioderm_medium.dts"; +$StaticTSObjects[61] = "PlayerArmors HeavyMaleBiodermArmorImage bioderm_heavy.dts"; + +$StaticTSObjects[62] = "Organics BEGrass1 Borg6.dts"; + +$StaticTSObjects[63] = "Plugs bePlug bmiscf.dts"; +$StaticTSObjects[64] = "Plugs dsPlug dmiscf.dts"; +$StaticTSObjects[65] = "Plugs xPlug xmiscf.dts"; +$StaticTSObjects[66] = "Plugs hPlug pmiscf.dts"; +$StaticTSObjects[67] = "Plugs swPlug smiscf.dts"; + +$StaticTSObjects[68] = "Statues Base statue_base.dts"; +$StaticTSObjects[69] = "Statues HeavyMaleStatue statue_hmale.dts"; +$StaticTSObjects[70] = "Statues LightFemaleStatue statue_lfemale.dts"; +$StaticTSObjects[71] = "Statues LightMaleStatue statue_lmale.dts"; +$StaticTSObjects[72] = "Statues Plaque statue_plaque.dts"; + +$StaticTSObjects[73] = "Debris BomberDebris1 bdb1.dts"; +$StaticTSObjects[74] = "Debris BomberDebris2 bdb2.dts"; +$StaticTSObjects[75] = "Debris BomberDebris3 bdb3.dts"; +$StaticTSObjects[76] = "Debris BomberDebris4 bdb4.dts"; +$StaticTSObjects[77] = "Debris BomberDebris5 bdb5.dts"; +$StaticTSObjects[78] = "Debris HavocDebris1 hdb1.dts"; +$StaticTSObjects[79] = "Debris HavocDebris2 hdb2.dts"; +$StaticTSObjects[80] = "Debris HavocDebris3 hdb3.dts"; +$StaticTSObjects[81] = "Debris IDebris1 idb.dts"; +$StaticTSObjects[82] = "Debris MPBDebris1 mpbdb1.dts"; +$StaticTSObjects[83] = "Debris MPBDebris2 mpbdb2.dts"; +$StaticTSObjects[84] = "Debris MPBDebris3 mpbdb3.dts"; +$StaticTSObjects[85] = "Debris MPBDebris4 mpbdb4.dts"; +$StaticTSObjects[86] = "Debris ScoutDebris1 sdb1.dts"; +$StaticTSObjects[87] = "Debris TankDebris1 tdb1.dts"; +$StaticTSObjects[88] = "Debris TankDebris2 tdb2.dts"; +$StaticTSObjects[89] = "Debris TankDebris3 tdb3.dts"; +$StaticTSObjects[90] = "Debris GraveMarker1 gravemarker1.dts"; +$StaticTSObjects[91] = "Test Test1 test1.dts"; +$StaticTSObjects[92] = "Test Test2 test2.dts"; +$StaticTSObjects[93] = "Test Test3 test3.dts"; +$StaticTSObjects[94] = "Test Test4 test4.dts"; +$StaticTSObjects[95] = "Test Test5 test5.dts"; + + + +$NumStaticTSObjects = 96; + +function TSStatic::create(%shapeName) +{ + //echo("Foo:" SPC %shapeName); + %obj = new TSStatic() + { + shapeName = %shapeName; + }; + return(%obj); +} + +function TSStatic::damage(%this) +{ + // prevent console error spam +} + +function stripFields(%this) +{ + if(%this $= "") + %this = MissionGroup; + for (%i = 0; %i < %this.getCount(); %i++){ + %obj = %this.getObject(%i); + if (%obj.getClassName() $= SimGroup) + { + %obj.powerCount = ""; + %obj.team = ""; + stripFields(%obj); + } + else + { + %obj.threshold = ""; + %obj.team = ""; + %obj.powerCount = ""; + %obj.trigger = ""; + %obj.hidden = ""; + %obj.locked = "true"; + %obj.notReady = ""; + %obj.inUse = ""; + %obj.triggeredBy = ""; + %obj.lastDamagedBy = ""; + %obj.lastDamagedByTeam =""; + %obj.isHome = ""; + %obj.originalPosition = ""; + %obj.objectiveCompleted = ""; + %obj.number = ""; + %obj.target = ""; + %obj.lockCount = ""; + %obj.homingCount = ""; + %obj.projector = ""; + %obj.holo = ""; + %obj.waypoint = ""; + %obj.scoreValue =""; + %obj.damageTimeMS = ""; + %obj.station = ""; + %homingCount = ""; + } + } +} diff --git a/Scripts/station.cs b/Scripts/station.cs new file mode 100644 index 0000000..56defed --- /dev/null +++ b/Scripts/station.cs @@ -0,0 +1,1378 @@ +// Time to hold in station +if ($Host::StationHoldTime $= "") + $Host::StationHoldTime = 1600; + +//****************************************************************************** +//* Station - Data Blocks * +//****************************************************************************** +datablock EffectProfile(StationInventoryActivateEffect) +{ + effectname = "powered/inv_pad_on"; + minDistance = 5.0; + maxDistance = 7.5; +}; + +datablock EffectProfile(StationVehicleAcitvateEffect) +{ + effectname = "powered/vehicle_screen_on2"; + minDistance = 3.0; + maxDistance = 5.0; +}; + +datablock EffectProfile(StationVehicleDeactivateEffect) +{ + effectname = "powered/vehicle_screen_off"; + minDistance = 3.0; + maxDistance = 5.0; +}; + +//datablock EffectProfile(MobileBaseInventoryActivateEffect) +//{ +// effectname = "misc/diagnostic_on"; +// minDistance = 3.0; +//}; + +datablock EffectProfile(StationAccessDeniedEffect) +{ + effectname = "powered/station_denied"; + minDistance = 3.0; + maxDistance = 5.0; +}; + +datablock AudioProfile(StationInventoryActivateSound) +{ + filename = "fx/powered/inv_pad_on.wav"; + description = AudioClose3d; + preload = true; + effect = StationInventoryActivateEffect; +}; + +datablock AudioProfile(MobileBaseInventoryActivateSound) +{ + filename = "fx/vehicles/mpb_inv_station.wav"; + description = AudioClose3d; + preload = true; + effect = StationInventoryActivateEffect; +}; + +datablock AudioProfile(DepInvActivateSound) +{ + filename = "fx/powered/dep_inv_station.wav"; + description = AudioClose3d; + preload = true; + effect = StationInventoryActivateEffect; +}; + +datablock AudioProfile(StationVehicleAcitvateSound) +{ + filename = "fx/powered/vehicle_screen_on2.wav"; + description = AudioClosest3d; + preload = true; + effect = StationVehicleAcitvateEffect; +}; + +datablock AudioProfile(StationVehicleDeactivateSound) +{ + filename = "fx/powered/vehicle_screen_off.wav"; + description = AudioClose3d; + preload = true; + effect = StationVehicleDeactivateEffect; +}; + +datablock AudioProfile(StationAccessDeniedSound) +{ + filename = "fx/powered/station_denied.wav"; + description = AudioClosest3d; + preload = true; + effect = StationAccessDeniedEffect; +}; + +datablock AudioProfile(StationVehicleHumSound) +{ + filename = "fx/powered/station_hum.wav"; + description = CloseLooping3d; + preload = true; +}; + +datablock AudioProfile(StationInventoryHumSound) +{ + filename = "fx/powered/station_hum.wav"; + description = CloseLooping3d; + preload = true; +}; + +datablock StationFXPersonalData( PersonalInvFX ) +{ + delay = 0; + fadeDelay = 0.5; + lifetime = 1.2; + height = 2.5; + numArcSegments = 10.0; + numDegrees = 180.0; + trailFadeTime = 0.5; + leftRadius = 1.85; + rightRadius = 1.85; + leftNodeName = "FX1"; + rightNodeName = "FX2"; + + texture[0] = "special/stationLight"; +}; + + +datablock DebrisData( StationDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.40; + friction = 0.5; + + lifetime = 17.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 0.4; + + velocity = 9.0; + velocityVariance = 4.5; + +}; + +datablock StaticShapeData(StationInventory) : StaticShapeDamageProfile +{ + className = Station; + catagory = "Stations"; + shapeFile = "station_inv_human.dts"; + maxDamage = 1.00; + destroyedLevel = 1.00; + disabledLevel = 0.70; + explosion = ShapeExplosion; + expDmgRadius = 8.0; + expDamage = 0.4; + expImpulse = 1500.0; + // don't allow this object to be damaged in non-team-based + // mission types (DM, Rabbit, Bounty, Hunters) + noIndividualDamage = true; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = true; + energyPerDamagePoint = 75; + maxEnergy = 50; + rechargeRate = 0.35; + doesRepair = true; + humSound = StationInventoryHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Inventory'; + targetTypeTag = 'Station'; + + debrisShapeName = "debris_generic.dts"; + debris = StationDebris; + needsPower = true; +}; + +datablock StaticShapeData(StationAmmo) : StaticShapeDamageProfile +{ + className = Station; + catagory = "Stations"; +// shapeFile = "station_ammo.dts"; + shapeFile = "station_inv_human.dts"; + maxDamage = 1.00; + destroyedLevel = 1.00; + disabledLevel = 0.70; + explosion = ShapeExplosion; + expDmgRadius = 8.0; + expDamage = 0.4; + expImpulse = 1500.0; + // don't allow this object to be damaged in non-team-based + // mission types (DM, Rabbit, Bounty, Hunters) + noIndividualDamage = true; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = true; + energyPerDamagePoint = 75; + maxEnergy = 50; + rechargeRate = 0.35; + doesRepair = true; + humSound = StationInventoryHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + targetNameTag = 'Ammo'; + targetTypeTag = 'Station'; + + debrisShapeName = "debris_generic.dts"; + debris = StationDebris; +}; + +datablock StaticShapeData(StationVehicle) : StaticShapeDamageProfile +{ +//[[CHANGE]] Added some observer information.. + className = Station; + catagory = "Stations"; + shapeFile = "vehicle_pad_station.dts"; + maxDamage = 1.20; + destroyedLevel = 1.20; + disabledLevel = 0.84; + explosion = ShapeExplosion; + canControl = true; + expDmgRadius = 10.0; + expDamage = 0.4; + expImpulse = 1500.0; + dynamicType = $TypeMasks::StationObjectType; + isShielded = true; + energyPerDamagePoint = 33; + maxEnergy = 250; + rechargeRate = 0.31; + humSound = StationVehicleHumSound; + // don't let these be damaged in Siege missions + noDamageInSiege = true; + cmdCategory = "Support"; + cmdIcon = CMDVehicleStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_vehicle_pad_inventory"; + targetTypeTag = 'Vehicle Station'; + observeParameters = "0 10.0 10.0"; + + debrisShapeName = "debris_generic.dts"; + debris = StationDebris; + + minLookAngle = -1.5; + maxLookAngle = 1.5; + maxFreelookAngle = 3.0; + + cameraMaxDist = 40; + cameraOffset = 20; + cameraLag = 1.5; + noIndividualDamage = true; + +}; + +datablock StaticShapeData(StationVehiclePad) +{ + className = Station; + catagory = "Stations"; + shapeFile = "vehicle_pad.dts"; + isInvincible = true; + dynamicType = $TypeMasks::StaticObjectType; + rechargeRate = 0.05; + + // ------------------------------------------ + // z0dd - ZOD, 5/8/02. CC bug fix parameter. + // Part of overall V-Station creation change. + targetTypeTag = 'Vehicle Pad'; +}; + +//datablock StaticShapeData(StationAmmo) +//{ +// className = Station; +// catagory = "Stations"; +// shapeFile = "station_ammo.dts"; +// maxDamage = 1.0; +// disabledLevel = 0.6; +// destroyedLevel = 0.8; +// icon = "CMDStationIcon"; +// dynamicType = $TypeMasks::StationObjectType; +//}; + +datablock StaticShapeData(MobileInvStation) +{ + className = Station; + catagory = "Stations"; + shapeFile = "station_inv_mpb.dts"; + icon = "CMDStationIcon"; + // don't allow this object to be damaged in non-team-based + // mission types (DM, Rabbit, Bounty, Hunters) + noIndividualDamage = true; + + dynamicType = $TypeMasks::StationObjectType; + rechargeRate = 0.256; + doesRepair = true; +}; + + +//****************************************************************************** +//* Station - Functions * +//****************************************************************************** + +//////////////////////////////////////////////////////////////////////////////// +/// -Inventory- //////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/// -Inventory- //////////////////////////////////////////////////////////////// +//Function -- onAdd (%this, %obj) +// %this = Object data block +// %obj = Object being added +//Decription -- Called when the object is added to the mission +//////////////////////////////////////////////////////////////////////////////// +function StationInventory::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.75 0.75 0.1 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.3"; + }; + MissionCleanup.add(%trigger); + %trigger.setTransform(%obj.getTransform()); + + %trigger.station = %obj; + %trigger.mainObj = %obj; + %trigger.disableObj = %obj; + %obj.trigger = %trigger; +} + +/// -Inventory- //////////////////////////////////////////////////////////////// +//Function -- stationReady(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when station has been triggered and animation is +// completed +//////////////////////////////////////////////////////////////////////////////// +function StationInventory::stationReady(%data, %obj) +{ + //Display the Inventory Station GUI here + %obj.notReady = 1; + %obj.inUse = "Down"; + %obj.schedule(500,"playThread",$ActivateThread,"activate1"); + %player = %obj.triggeredBy; + %energy = %player.getEnergyLevel(); + // ------------------------------------------------- + // z0dd - ZOD, 4/20/02. Addition. Inv energy bug fix + %max = %player.getDatablock().maxEnergy; + + %player.setCloaked(true); + %player.schedule(500, "setCloaked", false); + // ------------------------------------------------- + // Nukem - This generated console errors. + // if (!%player.client.isAIControlled()) + if ((strstr(%player.getDatablock().getName(), "Zombie") != 1)) + // ------------------------------------------------- + buyFavorites(%player.client); + + // ------------------------------------------------- + // z0dd - ZOD, 4/20/02. Addition. Inv energy bug fix + //%player.setEnergyLevel(%energy); + %player.setEnergyLevel(mFloor(%player.getDatablock().maxEnergy * %energy / %max)); + + %data.schedule( 500, "beginPersonalInvEffect", %obj ); +} + +function StationInventory::beginPersonalInvEffect( %data, %obj ) +{ + if (!%obj.isDisabled()) + { + %fx = new StationFXPersonal() + { + dataBlock = PersonalInvFX; + stationObject = %obj; + }; + } +} + +/// -Inventory- //////////////////////////////////////////////////////////////// +//Function -- stationFinished(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when player has left the station +//////////////////////////////////////////////////////////////////////////////// +function StationInventory::stationFinished(%data, %obj) +{ + //Hide the Inventory Station GUI +} + +/// -Inventory- //////////////////////////////////////////////////////////////// +//Function -- getSound(%data, %forward) +// %data = Station Data Block +// %forward = direction the animation is playing +//Decription -- This sound will be played at the same time as the activate +// animation. +//////////////////////////////////////////////////////////////////////////////// +function StationInventory::getSound(%data, %forward) +{ + if(%forward) + return "StationInventoryActivateSound"; + else + return false; +} + +/// -Inventory- //////////////////////////////////////////////////////////////// +//Function -- setPlayerPosition(%data, %obj, %trigger, %colObj) +// %data = Station Data Block +// %obj = Station Object +// %trigger = Stations trigger +// %colObj = Object that is at the station +//Decription -- Called when player enters the trigger. Used to set the player +// in the center of the station. +//////////////////////////////////////////////////////////////////////////////// +function StationInventory::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %pos = %trigger.position; + %colObj.setvelocity("0 0 0"); + %rot = getWords(%colObj.getTransform(),3, 6); + %pos = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 0.8; + %terrain = getTerrainHeight2(%pos); + if (!(getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") || $Host::AllowUnderground) + %colObj.setTransform(%pos @ " " @ %rot);//center player on object + %colObj.setMoveState(true); + %colObj.schedule($Host::StationHoldTime,"setMoveState", false); + %colObj.setvelocity("0 0 0"); + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +/// -Ammo- //////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +function StationAmmo::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.75 0.75 0.1 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.3"; + }; + MissionCleanup.add(%trigger); + %trigger.setTransform(%obj.getTransform()); + + %trigger.station = %obj; + %trigger.mainObj = %obj; + %trigger.disableObj = %obj; + %obj.trigger = %trigger; +} + +//------------------------------------------------------------------------------- +function StationAmmo::stationReady(%data, %obj) +{ + //error("StationAmmo::stationReady"); + %obj.notReady = 1; + %obj.inUse = "Down"; + %obj.setThreadDir($ActivateThread, true); + %obj.schedule(100, "playThread", $ActivateThread, "activate1"); + %player = %obj.triggeredBy; + %energy = %player.getEnergyLevel(); + //%player.setCloaked(true); + //%player.schedule(500, "setCloaked", false); + + + if (!%player.client.isAIControlled()) + getAmmoStationLovin(%player.client); + //%data.schedule( 500, "beginPersonalInvEffect", %obj ); +} + +//------------------------------------------------------------------------------- +function StationAmmo::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $ActivateThread) + { + %obj.setThreadDir($ActivateThread, false); + %obj.playThread( $ActivateThread, "activate1"); + if(%obj.inUse $= "Up") + { + %data.stationReady(%obj); + %player = %obj.triggeredBy; + if(%data.doesRepair && !%player.stationRepairing && %player.getDamageLevel() != 0) { + %oldRate = %player.getRepairRate(); + %player.setRepairRate(%oldRate + 0.00625); + %player.stationRepairing = 1; + } + } + } + //Parent::onEndSequence(%data, %obj, %thread); +} + +//------------------------------------------------------------------------------- +function StationAmmo::stationFinished(%data, %obj) +{ + //Hide the Inventory Station GUI +} + +//------------------------------------------------------------------------------- +function StationAmmo::getSound(%data, %forward) +{ + if(%forward) + return "StationInventoryActivateSound"; + else + return false; +} + +//------------------------------------------------------------------------------- +function StationAmmo::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %pos = %trigger.position; + %colObj.setvelocity("0 0 0"); + %rot = getWords(%colObj.getTransform(),3, 6); + %pos = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 0.8; + %terrain = getTerrainHeight2(%pos); + if (!(getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") || $Host::AllowUnderground) + %colObj.setTransform(%pos @ " " @ %rot);//center player on object + %colObj.setMoveState(true); + %colObj.schedule($Host::StationHoldTime,"setMoveState", false); + %colObj.setvelocity("0 0 0"); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//Function -- onAdd (%this, %obj) +// %this = Object data block +// %obj = Object being added +//Decription -- Called when the object is added to the mission +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// z0dd - ZOD - Founder(founder@mechina.com), 5/8/02. Totall re-write of Vehicle +// station creation. More stable, addresses some power related bugs. + +// Do not need this anymore. +//function StationVehicle::onAdd(%this, %obj) +//{ +// Parent::onAdd(%this, %obj); + +// %obj.setRechargeRate(%obj.getDatablock().rechargeRate); +// %trigger = new Trigger() +// { +// dataBlock = stationTrigger; +// polyhedron = "-0.75 0.75 0.0 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.0"; +// }; +// MissionCleanup.add(%trigger); +// %trigger.setTransform(%obj.getTransform()); +// %trigger.station = %obj; +// %obj.trigger = %trigger; +//} + +// This now creates the vehicle station trigger. +function StationVehicle::createTrigger(%this, %obj) +{ + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.75 0.75 0.0 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.0"; + }; + MissionCleanup.add(%trigger); + %trigger.setTransform(%obj.getTransform()); + %trigger.station = %obj; + %obj.trigger = %trigger; +} +// End z0dd - ZOD - Founder +// ----------------------------------------------------------------------------- + +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//Function -- stationReady(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when station has been triggered and animation is +// completed +//////////////////////////////////////////////////////////////////////////////// +function StationVehicle::stationReady(%data, %obj) +{ + // Make sure none of the other popup huds are active: + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'scoreScreen' ); + messageClient( %obj.triggeredBy.client, 'CloseHud', "", 'inventoryScreen' ); + + //[[CHANGE]] Make sure the player teleports... + %obj.triggeredby.client.telebuy = ""; + + //Display the Vehicle Station GUI + commandToClient(%obj.triggeredBy.client, 'StationVehicleShowHud'); +} + +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//Function -- stationFinished(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when player has left the station +//////////////////////////////////////////////////////////////////////////////// +function StationVehicle::stationFinished(%data, %obj) +{ + //Hide the Vehicle Station GUI + if(!%obj.triggeredBy.isMounted()) + commandToClient(%obj.triggeredBy.client, 'StationVehicleHideHud'); + else + commandToClient(%obj.triggeredBy.client, 'StationVehicleHideJustHud'); + +} + +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//Function -- getSound(%data, %forward) +// %data = Station Data Block +// %forward = direction the animation is playing +//Decription -- This sound will be played at the same time as the activate +// animation. +//////////////////////////////////////////////////////////////////////////////// +function StationVehicle::getSound(%data, %forward) +{ + if(%forward) + return "StationVehicleAcitvateSound"; + else + return "StationVehicleDeactivateSound"; +} + +/// -Vehicle- ////////////////////////////////////////////////////////////////// +//Function -- setPlayerPosition(%data, %obj, %trigger, %colObj) +// %data = Station Data Block +// %obj = Station Object +// %trigger = Stations trigger +// %colObj = Object that is at the station +//Decription -- Called when player enters the trigger. Used to set the player +// in the center of the station. +//////////////////////////////////////////////////////////////////////////////// +function StationVehicle::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %posXY = getWords(%trigger.getTransform(),0 ,1); + %posZ = getWord(%trigger.getTransform(), 2); + %rotZ = getWord(%obj.getTransform(), 5); + %angle = getWord(%obj.getTransform(), 6); + %angle += 3.141592654; + if(%angle > 6.283185308) + %angle = %angle - 6.283185308; + %colObj.setvelocity("0 0 0"); + %pos = %posXY @ " " @ %posZ + 0.2; + %terrain = getTerrainHeight2(%pos); + if (!(getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") || $Host::AllowUnderground) + %colObj.setTransform(%pos @ " " @ "0 0 " @ %rotZ @ " " @ %angle );//center player on object + return true; + } + return false; +} +// ----------------------------------------------------------------------------- +// z0dd - ZOD - Founder(founder@mechina.com), 5/8/02. Totall re-write of Vehicle +// station creation. More stable, addresses some power related bugs. + +function StationVehiclePad::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.ready = true; + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); + + //------------------------------------------------------------- + // All of this moved to StationVehiclePad::createStationVehicle + //%xform = %obj.getSlotTransform(0); + //%pos = getWords(%xform, 0, 2); + //%rot = getWords(%xform, 3, 5); + //%angle = (getWord(%xform, 6) * 180) / 3.14159; + + //%sv = new StaticShape() { + // scale = "1 1 1"; + // dataBlock = StationVehicle; + // lockCount = "0"; + // homingCount = "0"; + // team = %obj.team; + // position = %pos; + // rotation = %rot @ " " @ %angle; + //}; + + //MissionCleanup.add(%sv); + //%sv.getDataBlock().gainPower(%sv); + //%sv.pad = %obj; + //%obj.station = %sv; + //%sv.trigger.mainObj = %obj; + //%sv.trigger.disableObj = %sv; + + //Remove unwanted vehicles + //if(%obj.scoutVehicle !$= "Removed") + // %sv.vehicle[scoutvehicle] = true; + //if(%obj.assaultVehicle !$= "Removed") + // %sv.vehicle[assaultVehicle] = true; + //if(%obj.mobileBaseVehicle !$= "Removed") + //{ + // %sv.vehicle[mobileBasevehicle] = true; + //} + //if(%obj.scoutFlyer !$= "Removed") + // %sv.vehicle[scoutFlyer] = true; + //if(%obj.bomberFlyer !$= "Removed") + // %sv.vehicle[bomberFlyer] = true; + //if(%obj.hapcFlyer !$= "Removed") + // %sv.vehicle[hapcFlyer] = true; + + // z0dd - ZOD - Founder. Schedule the vehicle station creation. + // Don't create the station if the pad is hidden by the current mission type. + //error("CURRENT MISSION TYPE: " @ $CurrentMissionType @ ", ALLOWED TYPE: " @ %obj.missionTypesList); + if($CurrentMissionType $= %obj.missionTypesList || %obj.missionTypesList $="") + %this.schedule(0, "createStationVehicle", %obj); +} + +function StationVehiclePad::createStationVehicle(%data, %obj) +{ + // This script was modified to keep certain vehicles in certain Gametypes. + + %group = %obj.getGroup(); + + %xform = %obj.getSlotTransform(0); + %position = getWords(%xform, 0, 2); + %rotation = getWords(%xform, 3, 5); + %angle = (getWord(%xform, 6) * 180) / 3.14159; + + if(%obj.stationPos $= "" || %obj.stationRot $= "") + { + %pos = %position; + %rot = %rotation @ " " @ %angle; + } + else + { + %pos = %obj.stationPos; + %rot = %obj.stationRot; + } + + %sv = new StaticShape() { + scale = "1 1 1"; + dataBlock = "StationVehicle"; + lockCount = "0"; + homingCount = "0"; + team = %obj.team; + position = %pos; + rotation = %rot; + }; + + %group.add(%sv); + %sv.setPersistent(false); + + %sv.getDataBlock().gainPower(%sv); + + %sv.getDataBlock().createTrigger(%sv); + %sv.pad = %obj; + %obj.station = %sv; + %sv.trigger.mainObj = %obj; + %sv.trigger.disableObj = %sv; + + if(%sv.getTarget() != -1) + setTargetSensorGroup(%sv.getTarget(), %obj.team); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Begin ACCM Modifications. +// Modifications done by Blnukem. +//------------------------------------------------------------------------------ + + // Basic Restriction to Construction Vehicles in the Construction Gametype. + if ($CurrentMissionType $= "Construction") { + %sv.vehicle[SuperScoutVehicle] = true; + %sv.vehicle[HawkFlyer] = true; + %sv.vehicle[mobileBasevehicle] = true; + %sv.vehicle[hapcFlyer] = true; + %sv.vehicle[bomberFlyer] = true; + // Now to set the vehicle defaults for Non-Construction Gametypes. + } else if ($CurrentMissionType !$= "Construction") { + %sv.vehicle[scoutvehicle] = true; + %sv.vehicle[assaultVehicle] = true; + %sv.vehicle[mobileBasevehicle] = true; + %sv.vehicle[scoutFlyer] = true; + %sv.vehicle[AWACS] = true; + %sv.vehicle[bomberFlyer] = true; + %sv.vehicle[hapcFlyer] = true; + %sv.vehicle[Artillery] = true; + %sv.vehicle[HeavyTank] = true; + %sv.vehicle[CGTank] = true; + %sv.vehicle[helicopter] = true; + %sv.vehicle[HeavyChopper] = true; + %sv.vehicle[StrikeFlyer] = true; + %sv.vehicle[gunship] = true; + } else { + + // Prevents Console Errors, Don't touch this. + if(%obj.scoutVehicle !$= "Removed") + %sv.vehicle[scoutvehicle] = true; + if(%obj.SuperScoutVehicle !$= "Removed") + %sv.vehicle[SuperScoutVehicle] = true; + if(%obj.HawkFlyer !$= "Removed") + %sv.vehicle[HawkFlyer] = true; + if(%obj.assaultVehicle !$= "Removed") + %sv.vehicle[assaultVehicle] = true; + if(%obj.mobileBaseVehicle !$= "Removed") + %sv.vehicle[mobileBasevehicle] = true; + if(%obj.scoutFlyer !$= "Removed") + %sv.vehicle[scoutFlyer] = true; + if(%obj.AWACS !$= "Removed") + %sv.vehicle[AWACS] = true; + if(%obj.bomberFlyer !$= "Removed") + %sv.vehicle[bomberFlyer] = true; + if(%obj.hapcFlyer !$= "Removed") + %sv.vehicle[hapcFlyer] = true; + if(%obj.SuperHAPCFlyer !$= "Removed") + %sv.vehicle[SuperHAPCFlyer] = true; + if(%obj.Artillery !$= "Removed") + %sv.vehicle[Artillery] = true; + if(%obj.HeavyTank !$= "Removed") + %sv.vehicle[HeavyTank] = true; + if(%obj.beamflyer !$= "Removed") + %sv.vehicle[beamflyer] = true; + if(%obj.CGTank !$= "Removed") + %sv.vehicle[CGTank] = true; + if(%obj.helicopter !$= "Removed") + %sv.vehicle[helicopter] = true; + if(%obj.HeavyChopper !$= "Removed") + %sv.vehicle[HeavyChopper] = true; + if(%obj.StrikeFlyer !$= "Removed") + %sv.vehicle[StrikeFlyer] = true; + if(%obj.gunship !$= "Removed") + %sv.vehicle[gunship] = true; + } +} + +//------------------------------------------------------------------------------ +// End ACCM Modifications. +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +function StationVehiclePad::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $ActivateThread) + { + %obj.ready = true; + %obj.stopThread($ActivateThread); + } + Parent::onEndSequence(%data, %obj, %thread); +} + +//////////////////////////////////////////////////////////////////////////////// +/// -Mobile Base Inventory- //////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +/// -Mobile Base- ////////////////////////////////////////////////////////////// +//Function -- onAdd (%this, %obj) +// %this = Object data block +// %obj = Object being added +//Decription -- Called when the object is added to the mission +//////////////////////////////////////////////////////////////////////////////// +function MobileInvStation::onAdd(%this, %obj) +{ +} + +function MobileInvStation::createTrigger(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %obj.setRechargeRate(%obj.getDatablock().rechargeRate); + %trigger = new Trigger() + { + dataBlock = stationTrigger; + polyhedron = "-0.75 0.75 0.1 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.3"; + }; + MissionCleanup.add(%trigger); + %trigger.setTransform(%obj.vehicle.getSlotTransform(2)); + + %trigger.station = %obj; + %trigger.mainObj = %obj; + %trigger.disableObj = %obj; + + %obj.trigger = %trigger; +// createTarget(%obj, 'Inventory Station', "", "", 'Station', 0, 0); +} + +/// -Mobile Base- ////////////////////////////////////////////////////////////// +//Function -- stationReady(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when station has been triggered and animation is +// completed +//////////////////////////////////////////////////////////////////////////////// +function MobileInvStation::stationReady(%data, %obj) +{ + //Display the Inventory Station GUI here + %obj.notReady = 1; + %obj.inUse = "Down"; + %obj.schedule(200,"playThread",$ActivateThread,"activate1"); + %obj.getObjectMount().playThread($ActivateThread,"Activate"); + %player = %obj.triggeredBy; + %energy = %player.getEnergyLevel(); + %player.setCloaked(true); + %player.schedule(900, "setCloaked", false); + if (!%player.client.isAIControlled()) + buyFavorites(%player.client); + + %player.setEnergyLevel(%energy); +} + +/// -Mobile Base- ////////////////////////////////////////////////////////////// +//Function -- stationFinished(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called when player has left the station +//////////////////////////////////////////////////////////////////////////////// +function MobileInvStation::stationFinished(%data, %obj) +{ + //Hide the Inventory Station GUI +} + +/// -Mobile Base- ////////////////////////////////////////////////////////////// +//Function -- getSound(%data, %forward) +// %data = Station Data Block +// %forward = direction the animation is playing +//Decription -- This sound will be played at the same time as the activate +// animation. +//////////////////////////////////////////////////////////////////////////////// +function MobileInvStation::getSound(%data, %forward) +{ + if(%forward) + return "MobileBaseInventoryActivateSound"; + else + return false; +} + +/// -Mobile Base- ////////////////////////////////////////////////////////////// +//Function -- setPlayerPosition(%data, %obj, %trigger, %colObj) +// %data = Station Data Block +// %obj = Station Object +// %trigger = Stations trigger +// %colObj = Object that is at the station +//Decription -- Called when player enters the trigger. Used to set the player +// in the center of the station. +//////////////////////////////////////////////////////////////////////////////// +function MobileInvStation::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %pos = %trigger.position; + %colObj.setvelocity("0 0 0"); + %rot = getWords(%colObj.getTransform(),3, 6); + %pos = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2)+0.8; + %terrain = getTerrainHeight2(%pos); + if (!(getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") || $Host::AllowUnderground) + %colObj.setTransform(%pos @ " " @ %rot);//center player on object + %colObj.setMoveState(true); + %colObj.schedule($Host::StationHoldTime,"setMoveState", false); + %colObj.setvelocity("0 0 0"); + return true; + } + return false; +} + +function MobileInvStation::onDamage() +{ +} + +function MobileInvStation::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) +{ + //If vehicle station is hit then apply damage to the vehicle + %targetObject.getObjectMount().damage(%sourceObject, %position, %amount, %damageType); +} + +//////////////////////////////////////////////////////////////////////////////// +/// -Station Trigger- ////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +////-Station Trigger-/////////////////////////////////////////////////////////// +//Function -- onEnterTrigger (%data, %obj, %colObj) +// %data = Trigger Data Block +// %obj = Trigger Object +// %colObj = Object that collided with the trigger +//Decription -- Called when trigger has been triggered +//////////////////////////////////////////////////////////////////////////////// +function stationTrigger::onEnterTrigger(%data, %obj, %colObj) +{ + //make sure it's a player object, and that that object is still alive + if(%colObj.getDataBlock().className !$= "Armor" || %colObj.getState() $= "Dead") + return; + + %colObj.inStation = true; + commandToClient(%colObj.client,'setStationKeys', true); + if(Game.stationOnEnterTrigger(%data, %obj, %colObj)) { + //verify station.team is team associated and isn't on player's team + if((%obj.mainObj.team != %colObj.client.team) && (%obj.mainObj.team != 0)) + { + //%obj.station.playAudio(2, StationAccessDeniedSound); + messageClient(%colObj.client, 'msgStationDenied', '\c2Access Denied -- Wrong team.~wfx/powered/station_denied.wav'); + } + else if(%obj.disableObj.isDisabled()) + { + messageClient(%colObj.client, 'msgStationDisabled', '\c2Station is disabled.'); + } + else if(!%obj.mainObj.isPowered()) + { + messageClient(%colObj.client, 'msgStationNoPower', '\c2Station is not powered.'); + } + else if(%obj.station.notDeployed) + { + messageClient(%colObj.client, 'msgStationNotDeployed', '\c2Station is not deployed.'); + } + else if(%obj.station.triggeredBy $= "") + { + if(%obj.station.getDataBlock().setPlayersPosition(%obj.station, %obj, %colObj)) + { + messageClient(%colObj.client, 'CloseHud', "", 'inventoryScreen'); + commandToClient(%colObj.client, 'TogglePlayHuds', true); + %obj.station.triggeredBy = %colObj; + %obj.station.getDataBlock().stationTriggered(%obj.station, 1); + %colObj.station = %obj.station; + %colObj.lastWeapon = ( %colObj.getMountedImage($WeaponSlot) == 0 ) ? "" : %colObj.getMountedImage($WeaponSlot).getName().item; + %colObj.unmountImage($WeaponSlot); + } + } + } +} + + +////-Station Trigger-/////////////////////////////////////////////////////////// +//Function -- onLeaveTrigger (%data, %obj, %colObj) +// %data = Trigger Data Block +// %obj = Trigger Object +// %colObj = Object that collided with the trigger +//Decription -- Called when trigger has been untriggered +//////////////////////////////////////////////////////////////////////////////// +function stationTrigger::onLeaveTrigger(%data, %obj, %colObj) +{ + if(%colObj.getDataBlock().className !$= "Armor") + return; + + %colObj.inStation = false; + commandToClient(%colObj.client,'setStationKeys', false); + if(%obj.station) + { + if(%obj.station.triggeredBy == %colObj) + { + %obj.station.getDataBlock().stationFinished(%obj.station); + %obj.station.getDataBlock().endRepairing(%obj.station); + %obj.station.triggeredBy = ""; + %obj.station.getDataBlock().stationTriggered(%obj.station, 0); + + if(!%colObj.teleporting) + %colObj.station = ""; + if(%colObj.getMountedImage($WeaponSlot) == 0 && !%colObj.teleporting) + { + if(%colObj.inv[%colObj.lastWeapon]) + %colObj.use(%colObj.lastWeapon); + + if(%colObj.getMountedImage($WeaponSlot) == 0) + %colObj.selectWeaponSlot( 0 ); + } + } + } +} + +////-Station Trigger-/////////////////////////////////////////////////////////// +//Function -- stationTriggered(%data, %obj, %isTriggered) +// %data = Station Data Block +// %obj = Station Object +// %isTriggered = 1 if triggered; 0 if status changed to +// untriggered +//Decription -- Called when a "station trigger" has been triggered or +// untriggered +//////////////////////////////////////////////////////////////////////////////// +function Station::stationTriggered(%data, %obj, %isTriggered) +{ + + + if(%isTriggered) + { + %obj.setThreadDir($ActivateThread, TRUE); + %obj.playThread($ActivateThread,"activate"); + %obj.playAudio($ActivateSound, %data.getSound(true)); + %obj.inUse = "Up"; + } + else + { + if(%obj.getDataBlock().getName() !$= StationVehicle) + { + %obj.stopThread($ActivateThread); + if(%obj.getObjectMount()) + %obj.getObjectMount().stopThread($ActivateThread); + %obj.inUse = "Down"; + } + else + { + %obj.setThreadDir($ActivateThread, FALSE); + %obj.playThread($ActivateThread,"activate"); + %obj.playAudio($ActivateSound, %data.getSound(false)); + %obj.inUse = "Down"; + } + } +} + +////-Station-/////////////////////////////////////////////////////////////////// +//Function -- onEndSequence(%data, %obj, %thread) +// %data = Station Data Block +// %obj = Station Object +// %thread = Thread number that the animation is associated +// with / running on. +//Decription -- Called when an animation sequence is finished playing +//////////////////////////////////////////////////////////////////////////////// +function Station::onEndSequence(%data, %obj, %thread) +{ + if(%thread == $ActivateThread) + { + if(%obj.inUse $= "Up") + { + %data.stationReady(%obj); + %player = %obj.triggeredBy; + if(%data.doesRepair && !%player.stationRepairing && %player.getDamageLevel() != 0) { + %oldRate = %player.getRepairRate(); + %player.setRepairRate(%oldRate + 0.00625); + %player.stationRepairing = 1; + } + } + else + { + if(%obj.getDataBlock().getName() !$= MobileInvStation) + { + %obj.stopThread($ActivateThread); + %obj.inUse = "Down"; + } + } + } + Parent::onEndSequence(%data, %obj, %thread); +} + +////-Station-/////////////////////////////////////////////////////////////////// +//Function -- onCollision(%data, %obj, %colObj) +// %data = Station Data Block +// %obj = Station Object +// %colObj = Object that collided with the station +//Decription -- Called when an object collides with a station +//////////////////////////////////////////////////////////////////////////////// +function Station::onCollision(%data, %obj, %colObj) +{ + // Currently Not Needed +} + +////-Station-/////////////////////////////////////////////////////////////////// +//Function -- endRepairing(%data, %obj) +// %data = Station Data Block +// %obj = Station Object +//Decription -- Called to stop repairing the object +//////////////////////////////////////////////////////////////////////////////// +function Station::endRepairing(%data, %obj) +{ + if(%obj.triggeredBy.stationRepairing) + { + %oldRate = %obj.triggeredBy.getRepairRate(); + %obj.triggeredBy.setRepairRate(%oldRate - 0.00625); + %obj.triggeredBy.stationRepairing = 0; + } +} + +////-Station Trigger-/////////////////////////////////////////////////////////// +//Function -- onTickTrigger(%data, %obj) +// %data = Trigger Data Block +// %obj = Trigger Object +//Decription -- Called every tick if triggered +//////////////////////////////////////////////////////////////////////////////// +function stationTrigger::onTickTrigger(%data, %obj) +{ +} + +//****************************************************************************** +//* Station General - Functions * +//****************************************************************************** + +//function Station::onGainPowerEnabled(%data, %obj) + +function Station::onLosePowerDisabled(%data, %obj) { + if (shouldChangePowerState(%obj,false)) { + // check to see if a player was using this station + %occupied = %obj.triggeredBy; + if(%occupied > 0) { + if(%data.doesRepair) + %data.endRepairing(%obj); + // if it's a deployed station, stop "flashing panels" thread + if(%data.getName() $= DeployedStationInventory) + %obj.stopThread($ActivateThread); + // reset some attributes + %occupied.setCloaked(false); + %occupied.station = ""; + %occupied.inStation = false; + %obj.triggeredBy = ""; + // restore "toggle inventory hud" key + commandToClient(%occupied.client,'setStationKeys', false); + // re-mount last weapon or weapon slot 0 + if(%occupied.getMountedImage($WeaponSlot) == 0) { + // ---------------------------------------------- + // z0dd - ZOD, 5/8/02. Optimized. + //if(%occupied.inv[%occupied.lastWeapon]) + // %occupied.use(%occupied.lastWeapon); + //if(%occupied.getMountedImage($WeaponSlot) == 0) + // %occupied.selectWeaponSlot( 0 ); + if(%occupied.inv[%occupied.lastWeapon]) + %occupied.use(%occupied.lastWeapon); + else + %occupied.selectWeaponSlot( 0 ); + // End z0dd - ZOD + // ---------------------------------------------- + } + } + } + Parent::onLosePowerDisabled(%data, %obj); +} + +// ----------------------------------------------------------------------------- +// z0dd - ZOD - Founder(founder@mechina.com), 5/8/02. Totall re-write of Vehicle +// station creation. More stable, addresses some power related bugs. + +// These are not needed anymore. +//function StationVehiclePad::gainPower(%data, %obj) +//{ +// %obj.station.setSelfPowered(); +// Parent::gainPower(%data, %obj); +//} + +//function StationVehiclePad::losePower(%data, %obj) +//{ +// %obj.station.clearSelfPowered(); +// Parent::losePower(%data, %obj); +//} +// End z0dd - ZOD - Founder +// ----------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// DeployedStationInventory: +//--------------------------------------------------------------------------- + +function DeployedStationInventory::stationReady(%data, %obj) +{ + %obj.notReady = 1; + %player = %obj.triggeredBy; + %obj.playThread($ActivateThread,"activate1"); + // function below found in inventoryHud.cs +// if (!%player.client.isAIControlled()) +// buyDeployableFavorites(%player.client); + + if(%obj.antidotes $= "") + %obj.antidotes = $Host::AntidoteStationMaxAntidotes; + + if(%obj.antidotes == 0 && $Host::AntidoteStationMaxAntidotes > 0) + { + messageClient(%player.client, "MsgYes", "\c2This station has no more antidotes!~wfx/misc/misc.error.wav"); + return; + } + + if(%player.infected) + { + %player.infected = 0; + if(isEventPending(%player.infectedDamage)) + { + cancel(%player.infectedDamage); + %player.infectedDamage = ""; + %player.beats = 0; + %player.canZkill = 0; + %player.hit = 0; // Ravenger bite count. + cancel(%player.zombieAttackImpulse); + } + + if($Host::AntidoteStationMaxAntidotes > 0) + { + %obj.antidotes--; + messageClient(%player.client, 'MsgYes', "\c2This station has "@%obj.antidotes@" antidotes left."); + } + else + messageClient(%player.client, 'MsgYes', "\c2You have been cured!"); + } +} + +function DeployedStationInventory::stationFinished(%data, %obj) +{ +} + +function DeployedStationInventory::setPlayersPosition(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + // build offset for player position + %rot = getWords(%obj.getTransform(), 3, 6); + %pos = getWords(%colObj.getTransform(),0,2); + %terrain = getTerrainHeight2(%pos); + if (!(getWord(%pos,2) < getWord(%terrain,2) || %terrain $= "") || $Host::AllowUnderground) + %colObj.setTransform( %pos @ " " @ %rot ); + %colObj.setMoveState(true); + %colObj.schedule($Host::StationHoldTime,"setMoveState", false); + %colObj.setvelocity("0 0 0"); + return true; + } + return false; +} + +function DeployedStationInventory::onDestroyed(%data, %obj, %prevState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + %obj.trigger.delete(); + // decrement team deployed count for this item + $TeamDeployedCount[%obj.team, InventoryDeployable]--; + + // when a station is destroyed, we don't need its trigger any more + remDSurface(%obj); + %obj.schedule(700, "delete"); + Parent::onDestroyed(%data, %obj, %prevState); +} + +function StationInventory::onDestroyed(%data,%obj,%prevState) { + if (%obj.isRemoved) + return; + if (%obj.deployed && ($Host::InvincibleDeployables != 1 || %obj.damageFailedDecon)) { + %obj.isRemoved = true; + %obj.trigger.delete(); + $TeamDeployedCount[%obj.team,LargeInventoryDeployable]--; + remDSurface(%obj); + %obj.schedule(500,"delete"); + } + Parent::onDestroyed(%data,%obj,%prevState); +} + +/// -Deployable Inventory- ////////////////////////////////////////////////////////////// +//Function -- getSound(%data, %forward) +// %data = Station Data Block +// %forward = direction the animation is playing +//Decription -- This sound will be played at the same time as the activate +// animation. +//////////////////////////////////////////////////////////////////////////////// +function DeployedStationInventory::getSound(%data, %forward) +{ + if(%forward) + return "DepInvActivateSound"; + else + return false; +} + +function StationInventory::disassemble(%data,%plyr,%obj) { + %pl = %obj.triggeredBy; + %cl = %pl.client; + if (isObject(%cl)) { + serverCmdResetControlObject(%cl); + if (isObject(%pl)) { + %pl.inStation = ""; + if (!%pl.getMountedImage($WeaponSlot)) { + if (%pl.inv[%pl.lastWeapon]) + %pl.use(%pl.lastWeapon); + if (%pl.getMountedImage($WeaponSlot) == 0) + %pl.selectWeaponSlot(0); + } + } + } + disassemble(%data,%plyr,%obj); +} + +function DeployedStationInventory::disassemble(%data,%plyr,%obj) { + // Use same code as Large Inventory Station + StationInventory::disassemble(%data,%plyr,%obj); +} diff --git a/Scripts/terSupport.cs b/Scripts/terSupport.cs new file mode 100644 index 0000000..b3d905f --- /dev/null +++ b/Scripts/terSupport.cs @@ -0,0 +1,377 @@ +// +// terSupport.cs +// Server support functions + +$terSupport::Version = 0.98; + + +// ========================================================================== // +// | | // +// | CONSOLE MESSAGE HANDLERS | // +// | | // +// ========================================================================== // + +// +// TS_DEBUG() +// If in debug mode, print a message to the console + +function TS_DEBUG( %text ) +{ + %output = "Debug: " @ %text; + + if ( $terSupport::Debug ) + echo( %output ); +} + +// +// TS_INFO() +// Show a message in the console + +function TS_INFO( %text ) +{ + echo( %text ); +} + +// +// TS_ERROR() +// Print an error message to the console +// If in debug mode also show the message ingame + +function TS_ERROR( %text ) +{ + %output = "ERROR: " @ %text; + + echo( %output ); + + if ( $terSupport::Debug && $ServerGroup !$= "" ) + messageAll( 0, %output ); +} + + +// +// tsCopyTextFile() +// Copy a text file (file copy isn't built in?) + +function tsCopyTextFile( %srcFilename, %destFilename ) +{ + %srcFile = new FileObject(); + %destFile = new FileObject(); + + if ( %srcFile.openForRead( %srcFilename ) + && %destFile.openForWrite( %destFilename ) ) + { + while ( !%srcFile.isEOF() ) + %destFile.writeLine( %srcFile.readLine() ); + + %srcFile.close(); + %destFile.close(); + } + else + return false; + + return true; +} + + + +// ========================================================================== // +// | | // +// | TEAM SUPPORT FUNCTIONS | // +// | | // +// ========================================================================== // + +// +// tsTeamCount() +// Return the number of teams, not including observer (zero) + +function tsTeamCount() +{ + return Game.numTeams; +} + + +// +// tsPlayerCount() +// Return the total number of players + +function tsPlayerCount() +{ + return ClientGroup.getCount(); +} + +// +// tsPlayerCountTeam() +// Return the number of players on a team + +function tsPlayerCountTeam( %teamindex ) +{ + %count = 0; + + %lim = ClientGroup.getCount(); + + for ( %i = 0; %i < %lim; %i++ ) + { + %client = ClientGroup.getObject( %i ); + + if ( %client.team == %teamindex ) + %count++; + } + + return %count; +} + +// +// tsPlayerCountExceptTeam() +// Return the number of players except those on team %teamindex +// ( usually %teamindex = 0 ) + +function tsPlayerCountExceptTeam( %teamindex ) +{ + %count = 0; + + %lim = ClientGroup.getCount(); + + for ( %i = 0; %i < %lim; %i++ ) + { + %client = ClientGroup.getObject( %i ); + + if ( %client.team != %teamindex ) + %count++; + } + + return %count; +} + +// +// tsGetLargestTeamPlayerCount() +// Return the number of players on the largest team + +function tsGetLargestTeamPlayerCount() +{ + %largestCount = 0; + + for( %i = 1; %i < (Game.numTeams + 1); %i++ ) + { + %thisCount = tsPlayerCountTeam( %i ); + + if ( %thisCount > %largestCount ) + %largestCount = %thisCount; + } + + //TS_DEBUG( "largest team player count = "@%largestCount ); + return %largestCount; +} + + + +// ========================================================================== // +// | | // +// | SERVER COMMAND SUPPORT | // +// | | // +// ========================================================================== // + +// +// tsCheckCommandSpam() +// Return true if client is blocked from issuing a command because +// they have executed one too recently - else return false + +function tsCheckCommandSpam( %client, %time ) +{ + if ( %time $= "" ) + %time = 5; + + // If the client executed a command in the last %time seconds they can't run another + if ( getSimTime() - %client.tsLastCommandTime < %time * 1000 ) + return true; + + // Record the time of this command + %client.tsLastCommandTime = getSimTime(); +} + +// +// serverCmdSetJoinPassword() +// Changes the password required to join the server + +function serverCmdSetJoinPassword( %client, %newPassword ) +{ + if ( %client.isSuperAdmin ) + { + messageAll( 0, "\c2"@%client.nameBase@" changed the server join password." ); + TS_INFO( %client.nameBase@" changed the server join password to "@%newPassword ); + + $Host::Password = %newPassword; + } + else + messageClient( %client, 0, "\c2You must be a super admin to use that command." ); +} + +// +// serverCmdTogglePlayerMute() +// Called to mute or unmute another client +// +// Changes from base: +// Now notifies the target that they're being muted or unmuted +// Has flood protection + +function serverCmdTogglePlayerMute( %client, %who ) +{ + %notifyTarget = true; + + if ( tsCheckCommandSpam( %client, 12 ) ) + %notifyTarget = false; + + if (%client.muted[%who]) + { + %client.muted[%who] = false; + messageClient(%client, 'MsgPlayerMuted', '%1 has been unmuted.', %who.name, %who, false); + + if ( %notifyTarget ) + messageClient( %who, 'MsgTSYouWereMuted', %client.nameBase@" has unmuted you.", %client.name, %client, false ); + } + else + { + %client.muted[%who] = true; + messageClient(%client, 'MsgPlayerMuted', '%1 has been muted.', %who.name, %who, true); + + if ( %notifyTarget ) + messageClient( %who, 'MsgTSYouWereMuted', %client.nameBase@" has muted you.", %client.name, %client, true ); + } +} + + + +// ========================================================================== // +// | | // +// | MESSAGING SUPPORT | // +// | | // +// ========================================================================== // + +// +// New center and bottom print routines + +function tsCenterPrintTeam( %team, %message, %time, %lines ) +{ + if ( %lines $= "" || ((%lines > 3) || (%lines < 1)) ) + %lines = 1; + + %clCount = ClientGroup.getCount(); + + for ( %i = 0; %i < %clCount; %i++ ) + { + %client = ClientGroup.getObject( %i ); + + if ( !%client.isAIControlled() && %client.team == %team ) + commandToClient( %client, 'centerPrint', %message, %time, %lines ); + } +} + +function tsCenterPrintExceptTeam( %excludeTeam, %message, %time, %lines ) +{ + if ( %lines $= "" || ((%lines > 3) || (%lines < 1)) ) + %lines = 1; + + %clCount = ClientGroup.getCount(); + + for ( %i = 0; %i < %clCount; %i++ ) + { + %client = ClientGroup.getObject( %i ); + + if ( !%client.isAIControlled() && %client.team != %excludeTeam ) + commandToClient( %client, 'centerPrint', %message, %time, %lines ); + } +} + +function tsBottomPrintTeam( %team, %message, %time, %lines ) +{ + if( %lines $= "" || ((%lines > 3) || (%lines < 1)) ) + %lines = 1; + + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if( !%cl.isAIControlled() && %cl.team == %team ) + commandToClient( %cl, 'bottomPrint', %message, %time, %lines ); + } +} + +function tsClearCenterPrintTeam( %team ) +{ + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if( !%cl.isAIControlled() && %cl.team == %team ) + commandToClient( %cl, 'ClearCenterPrint'); + } +} + +function tsClearBottomPrintTeam( %team ) +{ + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if( !%cl.isAIControlled() && %cl.team == %team ) + commandToClient( %cl, 'ClearBottomPrint'); + } +} + +function tsClearAllPrintsAll() +{ + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if( !%cl.isAIControlled() ) + { + commandToClient( %cl, 'ClearCenterPrint'); + commandToClient( %cl, 'ClearBottomPrint'); + } + } +} + +function tsClearAllPrintsTeam( %team ) +{ + %count = ClientGroup.getCount(); + for (%i = 0; %i < %count; %i++) + { + %cl = ClientGroup.getObject(%i); + if( !%cl.isAIControlled() && %cl.team == %team ) + { + commandToClient( %cl, 'ClearCenterPrint'); + commandToClient( %cl, 'ClearBottomPrint'); + } + } +} + + +// +// New chat message routines + +function tsMessageExceptTeam( %excludeTeam, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13 ) +{ + %clCount = ClientGroup.getCount(); + + for ( %i= 0; %i < %clCount; %i++ ) + { + %recipient = ClientGroup.getObject( %i ); + + if ( %recipient.team != %excludeTeam ) + messageClient( %recipient, %msgType, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10, %a11, %a12, %a13 ); + } +} + + +// +// tsMOTD() +// Show a message of the day + +function tsMOTD( %client, %MOTDtext, %MOTDlines, %MOTDtime ) +{ + if ( %client.tsMOTD $= "" ) + { + centerPrint( %client, %MOTDtext, %MOTDtime, %MOTDlines ); + %client.tsMOTD = true; + } +} diff --git a/Scripts/truPhysics.cs b/Scripts/truPhysics.cs new file mode 100644 index 0000000..bfc465f --- /dev/null +++ b/Scripts/truPhysics.cs @@ -0,0 +1,238 @@ +// TruPhysics engine v0.6 - physics engine by DynaBlade +// Mainly used for vehicles but can be expanded. + +// String Table +$TruPhysics::Enabled = true; +$TruPhysics::maxVelocity = 1500; // 1500 m/s or about 5400 KPH, T2's universal speed limit + +function capVelocity(%vel) +{ + if(velToSingle(%vel) > $TruPhysics::maxVelocity) + return $TruPhysics::maxVelocity; + else + return mFloor(%vel); +} + +function VehicleData::onCollision(%data,%obj,%col,%mod,%pos,%normal) +{ + + if(%data.disablePhysics || !$TruPhysics::Enabled) // !enabled = default vehicle physics, local or global scope + return; + + %className = %data.className; + + if(%col.isPlayer()) // player is dead and wants to be run over / annihilated. + { + %mass = -0.25 * %col.getMass(); + %vec = vectorScale(vectorScale(%col.getVelocity(), 0.25), %mass); + %obj.applyImpulse(%col.getPosition(), %vec); + } + else if(%col.isVehicle()) // phreakynasty! + { + if(%col.getDamageState() $= "Destroyed") // nothing to collide with here, get back to movement! + return; + + %AMass = %obj.getMass(); + %AiVec = vectorScale(%obj.getVelocity(), %mass); + %APos = vectorNormalize(%obj.getForwardVector()); + + %BMass = %obj.getMass(); + %BiVec = vectorScale(%obj.getVelocity(), %mass); + %BPos = vectorNormalize(%obj.getForwardVector()); + + %obj.applyImpulse(%BPos, %BiVec); + %col.applyImpulse(%APos, %AiVec); + } +} + + +function checkWaterPhysics(%obj) +{ + + if(%obj.isWet) + { + // water absorbs about 75% of your velocity when you hit it. + %mass = %obj.getMass() * 0.75; + %iVec = vectorScale(%obj.getVelocity(), %mass); + %obj.applyImpulse(%obj.getTransform(), %iVec); + + // speed tolerance checking (in KPH) + %mass = %obj.getMass(); + %pct = %obj.getDamagePct() / 4; + %max = %pct * (%data.minDrag * 10); + %maxSpeed = %max - (%mass / getRandom(10, 12)); + %vel = msToKPH(velToSingle(%obj.getVelocity())); + + if(%maxSpeed > %vel) + %obj.getDatablock().damageObject(%obj, 0, "0 0 0", %maxSpeed - %vel, $DamageType::Default); + + schedule(100, %obj, "checkWaterPhysics", %obj); + } +} + +function VehicleData::onEnterLiquid(%data, %obj, %coverage, %type) +{ + if(!%data.disableWaterPhysics && $TruPhysics::Enabled) + { + %obj.isWet = true; + checkWaterPhysics(%obj); + } + + 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 VehicleData::onLeaveLiquid(%data, %obj, %type) +{ + %obj.isWet = false; + // exiting from the water becomes easier if your engines are on... speed doubles? + if(!%data.disableWaterPhysics && $TruPhysics::Enabled) + { + %mass = %obj.getMass() * 0.375; + %iVec = capVel(vectorScale(%obj.getVelocity(), %mass)); + %obj.applyImpulse(%obj.getTransform(), %iVec); + } + + 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 = ""; + } +} + +datablock AudioProfile(EngineAlertSound) +{ + filename = "gui/vote_nopass.WAV"; + description = AudioExplosion3d; + preload = true; +}; + +function VDUndo(%obj) +{ + %obj.vdOverride = false; + %obj.dmgApplyImp = false; +} + +function vDmgApplyImpulse(%obj) +{ + if(%obj.getDatablock().forceSensitive) + return; + + %obj.dmgApplyImp = true; + %lastPct = %obj.lastDmgPct; + %pct = %obj.getDamagePct(); + + if(%lastPct > %pct) + { + %obj.vdOverride = true; + schedule(1000, 0, "VDUndo", %obj); + } + + %obj.tp_sndCnt++; + + if(((%pct >= 0.5 && %pct < 1) && (%obj.vdOverride == false || %obj.vdOverride == "")) && %obj.augType !$= "Auto Repair Bot") + { + %force = (%pct * 500) * getRandom(1); + if(%force) + { + %va = getRandom(1) ? getRandom() * -1 : getRandom(); + %vb = getRandom(1) ? getRandom() * -1 : getRandom(); + %vc = getRandom(1) ? getRandom() * -1 : getRandom(); + %vec = %va SPC %vb SPC %vc; + %nVec = vectorScale(%vec, %force); + + %seed = getRandom(100); + if(%seed > 60 && %seed < 90) + %obj.playThread(0, "maintainback"); + else if(%seed > 90) + %obj.playThread(0, "maintainbot"); + else + %obj.playThread(0, "activateback"); + + %obj.applyImpulse(%obj.getTransform(), %nVec); + } + %obj.lastDmgPct = %pct; + schedule(250, %obj, "vDmgApplyImpulse", %obj); + + if(%obj.tp_sndCnt >= 4) + { + %obj.tp_sndCnt = 0; + %obj.play3D(EngineAlertSound); + } + } + else + %obj.stopThread(0); +} + +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); + + %pct = %obj.getDamagePct(); + + if(%pct >= 0.6 && !%obj.dmgApplyImp) + vDmgApplyImpulse(%obj); + } +} diff --git a/Scripts/turret.cs b/Scripts/turret.cs new file mode 100644 index 0000000..728f72a --- /dev/null +++ b/Scripts/turret.cs @@ -0,0 +1,389 @@ +// sounds and effects +/////////////////////// +datablock EffectProfile(DeployableExplosionEffect) +{ + effectname = "explosions/explosion.xpl10"; + minDistance = 10; + maxDistance = 50; +}; + +datablock AudioProfile(DeployablesExplosionSound) +{ + filename = "fx/explosions/deployables_explosion.wav"; + description = AudioExplosion3d; + preload = true; + effect = DeployableExplosionEffect; +}; + +//-------------------------------------------------------------------------- +// Shockwave +//-------------------------------------------------------------------------- +datablock ShockwaveData(TurretShockwave) +{ + width = 6.0; + numSegments = 20; + numVertSegments = 2; + velocity = 8; + acceleration = 20.0; + lifetimeMS = 1500; + height = 1.0; + verticalCurve = 0.5; + + mapToTerrain = false; + renderBottom = true; + + texture[0] = "special/shockwave4"; + texture[1] = "special/gradient"; + texWrap = 6.0; + + times[0] = 0.0; + times[1] = 0.5; + times[2] = 1.0; + + colors[0] = "0.8 0.8 0.8 1.00"; + colors[1] = "0.8 0.5 0.2 0.20"; + colors[2] = "1.0 0.5 0.5 0.0"; +}; + +//-------------------------------------------------------------------------- +// Explosion +//-------------------------------------------------------------------------- +datablock ExplosionData(TurretExplosion) +{ + explosionShape = "effect_plasma_explosion.dts"; + soundProfile = ShapeExplosionSound; + faceViewer = true; + shockwave = TurretShockwave; +}; + +datablock ExplosionData(SmallTurretExplosion) +{ + soundProfile = DeployablesExplosionSound; + faceViewer = true; + + explosionShape = "effect_plasma_explosion.dts"; + sizes[0] = "0.3 0.3 0.3"; + sizes[1] = "0.3 0.3 0.3"; + times[0] = 0; + times[1] = 1; +}; + + +//-------------------------------------------------------------------------- +// Turret Debris +//-------------------------------------------------------------------------- +datablock DebrisData( TurretDebris ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.20; + friction = 0.5; + + lifetime = 17.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 0.4; + + velocity = 9.0; + velocityVariance = 4.5; +}; + +datablock DebrisData( TurretDebrisSmall ) +{ + explodeOnMaxBounce = false; + + elasticity = 0.20; + friction = 0.5; + + lifetime = 17.0; + lifetimeVariance = 0.0; + + minSpinSpeed = 60; + maxSpinSpeed = 600; + + numBounces = 10; + bounceVariance = 0; + + staticOnMaxBounce = true; + + useRadiusMass = true; + baseRadius = 0.2; + + velocity = 5.0; + velocityVariance = 2.5; +}; + + +//-------------------------------------------------------------------------- +// Turret base class functionality. Barrels are in scripts/weapons/*.cs +// +// +//-------------------------------------------------------------------------- + +function GameBaseData::hasLOS() { + return 1; +} + +function TurretData::create(%block) +{ + %obj = new Turret() { + dataBlock = %block; + }; + + return %obj; +} + +datablock SensorData(TurretBaseSensorObj) +{ + detects = true; + detectsUsingLOS = true; + detectsPassiveJammed = false; + detectsActiveJammed = false; + detectsCloaked = false; + detectionPings = true; + detectRadius = 80; +}; + + +datablock TurretData(TurretBaseLarge) : TurretDamageProfile +{ + className = TurretBase; + catagory = "Turrets"; + shapeFile = "turret_base_large.dts"; + preload = true; + + mass = 1.0; // Not really relevant + + maxDamage = 4.0; + destroyedLevel = 4.0; + disabledLevel = 3.25; + explosion = TurretExplosion; + expDmgRadius = 15.0; + expDamage = 0.66; + expImpulse = 2000.0; + repairRate = 0.5; + emap = true; + + thetaMin = 15; + thetaMax = 140; + + isShielded = false; + energyPerDamagePoint = 50; + maxEnergy = 150; + rechargeRate = 0.31; + humSound = SensorHumSound; + pausePowerThread = true; + + canControl = true; + cmdCategory = "Tactical"; + cmdIcon = CMDTurretIcon; + cmdMiniIconName = "commander/MiniIcons/com_turretbase_grey"; + targetNameTag = 'Base'; + targetTypeTag = 'Turret'; + sensorData = TurretBaseSensorObj; + sensorRadius = TurretBaseSensorObj.detectRadius; + sensorColor = "0 212 45"; + heatSignature = 1.0; + firstPersonOnly = true; + + debrisShapeName = "debris_generic.dts"; + debris = TurretDebris; +}; + +function TurretData::onGainPowerEnabled(%data, %obj) { + if (shouldChangePowerState(%obj,true)) + setTargetSensorData(%obj.target, %data.sensorData); + Parent::onGainPowerEnabled(%data, %obj); +} + +function TurretData::onLosePowerDisabled(%data, %obj) { + if (shouldChangePowerState(%obj,false)) { + // Must kick players out of turret + %obj.clearTarget(); + setTargetSensorData(%obj.target, 0); + } + Parent::onLosePowerDisabled(%data, %obj); +} + +function TurretData::selectTarget(%this, %turret) { + %turretTarg = %turret.getTarget(); + if(%turretTarg == -1) + return; + + // if the turret isn't on a team, don't fire at anyone + if(getTargetSensorGroup(%turretTarg) == 0) { + %turret.clearTarget(); + return; + } + + // stop firing if turret is disabled or if it needs power and isn't powered + if((!%turret.isPowered()) && (!%turret.needsNoPower)) { + %turret.clearTarget(); + return; + } + + if ($TurretEnableOverride != 1) { + %turret.clearTarget(); + return; + } + + if(%turret.getDatablock().noFire == 1) { + %turret.clearTarget(); + return; + } + + %TargetSearchMask = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType | $TypeMasks::StationObjectType | $TypeMasks::GeneratorObjectType | $TypeMasks::SensorObjectType | $TypeMasks::TurretObjectType; //$TypeMasks::StaticObjectType; + + InitContainerRadiusSearch(%turret.getMuzzlePoint(0), + %turret.getMountedImage(0).attackRadius, + %TargetSearchMask); + + // TODO - clean up this mess + GameBaseData::hasLOS() + + while ((%potentialTarget = ContainerSearchNext()) != 0) { + if (%potentialtarget) { + %potTargTarg = %potentialTarget.getTarget(); + if ((strstr(%potentialtarget.getDatablock().getName(), "Zombie") != -1) + || %turret.isValidTarget(%potentialTarget) + && (getTargetSensorGroup(%turretTarg) != getTargetSensorGroup(%potTargTarg)) + && (getTargetSensorGroup(%potTargTarg) != 0) + && ((%potentialTarget.getType() & $TypeMasks::PlayerObjectType) || !$TurretOnlyTargetPlayers) + && %this.hasLOS(%turret,0,%potentialTarget)) { + if (%potentialTarget.homingCount > 0 && !%secondTarg) { + if (!%firstTarg) + %firstTarg = %potentialTarget; + else + %secondTarg = %potentialTarget; + } + else { + %turret.setTargetObject(%potentialTarget); + %turret.aquireTime = getSimTime(); + return; + } + } + } + } + if (%secondTarg) { + %turret.setTargetObject(%firstTarg); + %turret.aquireTime = getSimTime(); + return; + } + if (%firstTarg) { + %turret.setTargetObject(%firstTarg); + %turret.aquireTime = getSimTime(); + return; + } +} + +function TurretData::replaceCallback(%this, %turret, %engineer) +{ + // This is a valid replacement. First, let's see if the engineer + // still has the correct pack in place... + if (%engineer.getMountedImage($BackPackSlot) != 0) + { + %barrel = %engineer.getMountedImage($BackPackSlot).turretBarrel; + if (%barrel !$= "") + { + // if there was a barrel there before, get rid of it + %turret.unmountImage(0); + // remove the turret barrel pack + %engineer.setInventory(%engineer.getMountedImage($BackPackSlot).item, 0); + // mount new barrel on base + %turret.mountImage(%barrel, 0, false); + } + else + { + // Player doesn't have the correct pack on... + } + } + else + { + // Player doesn't have any pack on... + } +} + +function TurretData::onDestroyed(%this, %turret, %prevState) +{ + if( isObject( %turret.lastProjectile ) ) + %turret.lastProjectile.delete(); + + Parent::onDestroyed(%this, %turret, %prevState); +} + +function checkTurretMount(%data, %obj, %slot) +{ + // search for a turret base in player's LOS + %eyeVec = VectorNormalize(%obj.getEyeVector()); + %srchRange = VectorScale(%eyeVec, 5.0); // look 5m for a turret base + %plTm = %obj.getEyeTransform(); + %plyrLoc = firstWord(%plTm) @ " " @ getWord(%plTm, 1) @ " " @ getWord(%plTm, 2); + %srchEnd = VectorAdd(%plyrLoc, %srchRange); + %potTurret = ContainerRayCast(%obj.getEyeTransform(), %srchEnd, $TypeMasks::TurretObjectType); + if(%potTurret != 0) + { + %otherMountObj = "foo"; + + if(%potTurret.getDatablock().getName() $= "TurretBaseLarge" + || %potTurret.getDatablock().getName() $= %otherMountObj + || %potTurret.getDatablock().getName() $= "TurretDeployedBase") + { + // found a turret base, what team is it on? + if(%potTurret.team == %obj.client.team) + { + if(%potTurret.getDamageState() !$= "Enabled") + { + // the base is destroyed + messageClient(%obj.client, 'MsgBaseDestroyed', "\c2Turret base is disabled, cannot mount barrel."); + %obj.setImageTrigger($BackpackSlot, false); + } + else + { + // it's a functional turret base on our team! stick the barrel on it + messageClient(%obj.client, 'MsgTurretMount', "\c2Mounting barrel pack on turret base."); + serverPlay3D(TurretPackActivateSound, %potTurret.getTransform()); + %potTurret.initiateBarrelSwap(%obj); + } + } + else + { + // whoops, wrong team + messageClient(%obj.client, 'MsgTryEnemyTurretMount', "\c2You cannot mount a barrel on an enemy turret."); + %obj.setImageTrigger($BackpackSlot, false); + } + } + else + { + // tried to mount barrel on some other turret type + messageClient(%obj.client, 'MsgNotTurretBase', "\c2Wrong type of Turret."); + %obj.setImageTrigger($BackpackSlot, false); + } + } + else + { + // I don't see any turret + messageClient(%obj.client, 'MsgNoTurretBase', "\c2Try pointing at a turret."); + %obj.setImageTrigger($BackpackSlot, false); + } +} + +//-------------------------------------- Load Barrel Images +// +exec("scripts/turrets/mortarBarrelLarge.cs"); +exec("scripts/turrets/aaBarrelLarge.cs"); +exec("scripts/turrets/missileBarrelLarge.cs"); +exec("scripts/turrets/plasmaBarrelLarge.cs"); +exec("scripts/turrets/ELFBarrelLarge.cs"); +exec("scripts/turrets/outdoorDeployableBarrel.cs"); +exec("scripts/turrets/indoorDeployableBarrel.cs"); +exec("scripts/turrets/sentrydeployablebarrel.cs"); +exec("scripts/turrets/sentryTurret.cs"); +exec("scripts/turrets/artillerybarrellarge.cs"); diff --git a/Scripts/weapTurretCode.cs b/Scripts/weapTurretCode.cs new file mode 100644 index 0000000..ff207e7 --- /dev/null +++ b/Scripts/weapTurretCode.cs @@ -0,0 +1,1046 @@ +//-------------------------------------- Ammo functions +function Ammo::onCollision(%data, %obj, %col) +{ + // %data = datablock of object; %obj = object number + // %col = thing that collided with object (hopefully a player) + + if (%col.getDataBlock().className $= Armor) + { + %ammoName = %data.getName(); + %ammoStore = %col.inv[%ammoName]; + + // if player has ammo pack, increase max amount of ammo + if(%col.getMountedImage($BackpackSlot) != 0) + { + if(%col.getMountedImage($BackpackSlot).getName() $= "AmmoPackImage") + %aMax = (%col.getDataBlock().max[%ammoName]) + AmmoPack.max[%ammoName]; + else + %aMax = %col.getDataBlock().max[%ammoName]; + } + else + %aMax = %col.getDataBlock().max[%ammoName]; + + if(%col.inv[%ammoName] < %aMax) + { + if( %obj.ammoStore $= "" ) + %obj.ammoStore = $AmmoIncrement[ %ammoName ]; + + %col.incInventory(%ammoName, %obj.ammoStore); + serverPlay3D(ItemPickupSound, %col.getTransform()); + %obj.respawn(); + if (%col.client > 0) + messageClient(%col.client, 'MsgItemPickup', '\c0You picked up %1.', %data.pickUpName); + } + } +} + +function GrenadeThrown::onCollision(%data, %obj, %col) +{ + // nothing you can do now... +} + +function HandInventory::onCollision(%data, %obj, %col) +{ + // %data = datablock of object; %obj = object number + // %col = thing that collided with object (hopefully a player) + if (%col.getDataBlock().className $= Armor) + { + %ammoName = %data.getName(); + %ammoStore = %col.inv[%ammoName]; + + // if player has ammo pack, increase max amount of ammo + if(%col.getMountedImage($BackpackSlot) != 0) + { + if(%col.getMountedImage($BackpackSlot).getName() $= "AmmoPackImage") + %aMax = (%col.getDataBlock().max[%ammoName]) + AmmoPack.max[%ammoName]; + else + %aMax = %col.getDataBlock().max[%ammoName]; + } + else + %aMax = %col.getDataBlock().max[%ammoName]; + + if(%data.isGrenade) + { + // it's a grenade -- see if it matches the type the player is carrying + %pgType = "None"; + for(%x = 0; $InvGrenade[%x] !$= ""; %x++) + { + %gren = $NameToInv[$InvGrenade[%x]]; + if(%col.inv[%gren] > 0) + { + %pgType = %gren; + break; + } + } + if((%pgType $= "None") || (%pgType $= %ammoName)) + { + // player either has no grenades or this type of grenades -- OK to pick up more + %canPickup = true; + } + else + { + // player has a different kind of grenade -- don't pick this kind up + %canPickup = false; + } + } + else + %canPickup = true; + + if(%canPickup) + { + if(%col.inv[%ammoName] < %aMax) + { + //------------------------------------------------------------------------------------------- + // z0dd - ZOD, 5/8/02. Don't allow player to pickup full ammo if they tossed less than full. + //%col.incInventory(%ammoName, $AmmoIncrement[%ammoName]); + if( %obj.ammoStore $= "" ) + %obj.ammoStore = $AmmoIncrement[ %ammoName ]; + %col.incInventory(%ammoName, %obj.ammoStore); + // End z0dd - ZOD + //------------------------------------------------------------------------------------------- + + serverPlay3D(ItemPickupSound, %col.getTransform()); + %obj.respawn(); + if (%col.client > 0) + messageClient(%col.client, 'MsgItemPickup', '\c0You picked up %1.', %data.pickUpName); + } + } + } +} + +//-------------------------------------- Specific turret functions + +function SentryTurret::onAdd(%data, %obj) +{ + Parent::onAdd(%data, %obj); + + //error("error"); + %obj.mountImage(%data.barrel, 0, true); +} + +function TurretDeployedCamera::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + %obj.mountImage(DeployableCameraBarrel, 0, true); + %obj.setRechargeRate(%this.rechargeRate); + %obj.setAutoFire(false); // z0ddm0d: Server crash fix related to controlable cameras +} + +function TurretDeployedCamera::onDestroyed(%this, %obj, %prevState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + Parent::onDestroyed(%this, %obj, %prevState); + $TeamDeployedCount[%obj.team, DeployedCamera]--; + // doesn't seem to delete itself, so... + remDSurface(%obj); + %obj.schedule(500, "delete"); +} + +function fireNextGunWep2(%obj) +{ + if(%obj.fireWeapon) + { + if(%obj.nextWeaponFire == 2) + { + %obj.setImageTrigger(4, true); + %obj.setImageTrigger(5, false); + } + else if(%obj.nextWeaponFire == 3) + { + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, true); + } + } + else + { + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + } +} + + +function ScoutFlyer::onTrigger(%data, %obj, %trigger, %state) +{ + %player = %obj.getMountNodeObject(0); + if(%trigger == 0) + { + switch (%state) + { + case 0: + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + case 1: + %obj.fireWeapon = true; + if(%obj.selectedWeapon == 1) + { + if(%obj.nextWeaponFire == 2) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, false); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, true); + } + } + else if(%obj.selectedWeapon == 2) + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, true); + %obj.setImageTrigger(5, false); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, true); + } + } + } +} + +function ScoutFlyer::playerDismounted(%data, %obj, %player) +{ + %obj.fireWeapon = false; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(5, false); + setTargetSensorGroup(%obj.getTarget(), %obj.team); + if( %player.client.observeCount > 0 ) + resetObserveFollow( %player.client, true ); +} + +function ScoutChaingunImage::onFire(%data,%obj,%slot) +{ + // obj = ScoutFlyer object number + // slot = 2 + + %p = Parent::onFire(%data, %obj, %slot); + Parent::onFire(%data, %obj, %slot); + if(isObject(%obj.getMountNodeObject(0))) + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + %obj.nextWeaponFire = 3; + schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} + +function ScoutChaingunPairImage::onFire(%data,%obj,%slot) +{ + // obj = ScoutFlyer object number + // slot = 3 + + %p = Parent::onFire(%data, %obj, %slot); + Parent::onFire(%data, %obj, %slot); + if(isObject(%obj.getMountNodeObject(0))) + %obj.getMountNodeObject(0).decInventory(%data.ammo, 1); + MissileSet.add(%p); + %obj.nextWeaponFire = 2; + schedule(%data.fireTimeout, 0, "fireNextGun", %obj); +} + +function fireNextGun(%obj) +{ + if(%obj.fireWeapon) + { + if(%obj.nextWeaponFire == 2) + { + %obj.setImageTrigger(2, true); + %obj.setImageTrigger(3, false); + } + else if(%obj.nextWeaponFire == 3) + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, true); + } + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + } +} + +function BomberTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function BomberTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function VehicleTurret::onEndSequence(%data, %obj, %thread) +{ + if($DeployThread == %thread) + %obj.stopThread($DeployThread); +} + +function BomberTurret::onTrigger(%data, %obj, %trigger, %state) +{ + //error("onTrigger: trigger = " @ %trigger @ ", state = " @ %state); + //error("obj = " @ %obj @ ", class " @ %obj.getClassName()); + switch (%trigger) + { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(6, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else if(%obj.selectedWeapon == 2) + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(6, false); + if(%state) + %obj.setImageTrigger(4, true); + else + %obj.setImageTrigger(4, false); + } + else + { + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, false); + if(%state) + %obj.setImageTrigger(6, true); + else + %obj.setImageTrigger(6, false); + } + + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function BomberTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, false); + %obj.setImageTrigger(6, false); + %client = %obj.getControllingClient(); + %client.player.isBomber = false; + commandToClient(%client, 'endBomberSight'); +// %client.player.setControlObject(%client.player); + %client.player.mountVehicle = false; +// %client.player.getDataBlock().doDismount(%client.player); + if(%client.player.getState() !$= "Dead") + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +} + +//function BomberTurret::getHudNum(%data, %num) +//{ +// if(%num == 1) +// return 0; +// else +// return 4; +//} + +function AIAimingTurretBarrel::onFire(%this,%obj,%slot) +{ +} + +function BomberBombImage::onUnmount(%this,%obj,%slot) +{ +} + +function BomberBombPairImage::onUnmount(%this,%obj,%slot) +{ +} + +function BomberTurretBarrel::firePair(%this, %obj, %slot) +{ + %obj.setImageTrigger( 3, true); +} + +function BomberTurretBarrelPair::stopFire(%this, %obj, %slot) +{ + %obj.setImageTrigger( 3, false); +} + +function BomberTurretBarrelPair::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function BomberTurretBarrel::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function BomberBombImage::firePair(%this, %obj, %slot) +{ + %obj.setImageTrigger( 5, true); +} + +function BomberBombPairImage::stopFire(%this, %obj, %slot) +{ + %obj.setImageTrigger( 5, false); +} + +function BomberBombPairImage::onMount(%this, %obj, %slot) +{ +// %obj.setImageAmmo(%slot,true); +} + +function BomberBombImage::onMount(%this, %obj, %slot) +{ +} + +function BomberBombImage::onUnmount(%this,%obj,%slot) +{ +} + +function BomberBombPairImage::onUnmount(%this,%obj,%slot) +{ +} + +function MobileTurretBase::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + setTargetSensorGroup(%obj.target, %obj.team); + //setTargetNeverVisMask(%obj.target, 0xffffffff); +} + +function MobileTurretBase::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function MobileTurretBase::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function MobileTurretBase::onEndSequence(%data, %obj, %thread) +{ + //Used so that the parent wont be called.. +} + +function AssaultPlasmaTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function AssaultPlasmaTurret::damageObject(%this, %targetObject, %sourceObject, %position, %amount, %damageType ,%vec, %client, %projectile) +{ + //If vehicle turret is hit then apply damage to the vehicle + %vehicle = %targetObject.getObjectMount(); + if(%vehicle) + %vehicle.getDataBlock().damageObject(%vehicle, %sourceObject, %position, %amount, %damageType, %vec, %client, %projectile); +} + +function AssaultPlasmaTurret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(4, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(4, true); + else + %obj.setImageTrigger(4, false); + } + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } +} + +function AssaultPlasmaTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(4, false); + %client = %obj.getControllingClient(); +// %client.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +// %client.player.getDataBlock().doDismount(%client.player); +} + +//function AssaultPlasmaTurret::getHudNum(%data, %num) +//{ +// if(%num == 1) +// return 1; +// else +// return 3; +//} + +function TankTurret::onDamage(%data, %obj) +{ + %newDamageVal = %obj.getDamageLevel(); + if(%obj.lastDamageVal !$= "") + if(isObject(%obj.getObjectMount()) && %obj.lastDamageVal > %newDamageVal) + %obj.getObjectMount().setDamageLevel(%newDamageVal); + %obj.lastDamageVal = %newDamageVal; +} + +function TankTurret::onTrigger(%data, %obj, %trigger, %state) +{ + switch (%trigger) { + case 0: + %obj.fireTrigger = %state; + if(%obj.selectedWeapon == 1) + { + %obj.setImageTrigger(4, false); + if(%state) + %obj.setImageTrigger(2, true); + else + %obj.setImageTrigger(2, false); + } + else + { + %obj.setImageTrigger(2, false); + if(%state) + %obj.setImageTrigger(4, true); + else + %obj.setImageTrigger(4, false); + } + case 2: + if(%state) + { + %obj.getDataBlock().playerDismount(%obj); + } + } + if(%trigger == 5) + { + if(%obj.selectedWeapon == 2){ + switch (%state){ + case 1: + if(%obj.Firetype == 2){ + %obj.Firetype = 1; + %Type = "Artillery Shells"; + } + else{ + %obj.Firetype = 2; + %Type = "Anti Tank Shells"; + } + bottomPrint(%obj.getControllingClient(), "Main cannon set to fire "@%Type@".", 5, 2 ); + } + } + } + else if (%trigger == 3){ + if(%state) + %obj.setImageTrigger(3, true); + else + %obj.setImageTrigger(3, false); + } +} + +function TankTurret::playerDismount(%data, %obj) +{ + //Passenger Exiting + %obj.fireTrigger = 0; + %obj.setImageTrigger(2, false); + %obj.setImageTrigger(3, false); + %obj.setImageTrigger(4, false); + %client = %obj.getControllingClient(); +// %client.setControlObject(%client.player); + %client.player.mountImage(%client.player.lastWeapon, $WeaponSlot); + %client.player.mountVehicle = false; + setTargetSensorGroup(%obj.getTarget(), 0); + setTargetNeverVisMask(%obj.getTarget(), 0xffffffff); +// %client.player.getDataBlock().doDismount(%client.player); +} + + +// ------------------------------------------ +// camera functions +// ------------------------------------------ + +$CameraDeployTime = 1000; +$CameraDeployCheckMax = 6; +$CameraMinVelocity = 0.1; + +function CameraGrenadeThrown::onThrow(%this, %gren) +{ + // schedule a check to see if the camera is at rest but not deployed + %gren.checkCount = 0; + %gren.velocCheck = %this.schedule($CameraDeployTime, "checkCameraDeploy", %gren); +} + +function CameraGrenadeThrown::onStickyCollision(%data, %obj) +{ + cancel(%obj.velocCheck); + %pos = %obj.getLastStickyPos(); + %norm = %obj.getLastStickyNormal(); + + %intAngle = getTerrainAngle(%norm); // staticShape.cs + %rotAxis = vectorNormalize(vectorCross(%norm, "0 0 1")); + if (getWord(%norm, 2) == 1 || getWord(%norm, 2) == -1) + %rotAxis = vectorNormalize(vectorCross(%norm, "0 1 0")); + + %rotation = %rotAxis @ " " @ %intAngle; + %dcSucc = activateCamera(%pos, %rotation, %obj.sourceObject, %obj.sourceObject.team); + if(%dcSucc == 0) + messageClient(%obj.sourceObject.client, 'MsgDeployFailed', '\c2Your team\'s control network has reached its capacity for this item.~wfx/misc/misc.error.wav'); + %obj.schedule(50,"delete"); +} + +function CameraGrenadeThrown::checkCameraDeploy(%this, %gren) +{ + %gren.checkCount++; + if(VectorLen(%gren.getVelocity()) < $CameraMinVelocity) + { + // camera has come to rest but not deployed -- probably on a staticshape (station, gen, etc) + // no resolution, so get rid of it + %gren.schedule(50, "delete"); + } + else if(%gren.checkCount >= $CameraDeployCheckMax) + { + // camera's still moving but it's been check several times -- it was thrown from too great + // a height or off the edge of the world -- delete it + %gren.schedule(50, "delete"); + } + else + { + // check back in a little while + %gren.velocCheck = %this.schedule($CameraDeployTime, "checkCameraDeploy", %gren); + } +} + +function activateCamera(%position, %rotation, %sourceObj, %team) +{ + if($TeamDeployedCount[%team, DeployedCamera] >= $TeamDeployableMax[DeployedCamera]) + { + // team has too many cameras deployed already, don't deploy this one + return 0; + } + %dCam = new Turret() + { + dataBlock = "TurretDeployedCamera"; + team = %team; + needsNoPower = true; + owner = %sourceObj.client; + ownerHandle = %sourceObj.client.handle; + position = %position; + rotation = %rotation; + }; + addToDeployGroup(%dCam); + + if(%dCam.getTarget() != -1) + setTargetSensorGroup(%dCam.getTarget(), %team); + + %dCam.playAudio($DeploySound, CameraGrenadeAttachSound); + %dCam.deploy(); + %dCam.playThread($AmbientThread, "ambient"); + + // increment team's deployed count for cameras + $TeamDeployedCount[%team, DeployedCamera]++; + return 1; +} + +function FlareGrenade::onUse(%this, %obj) +{ + // a stripped-down version of HandInventory::onUse from weapons.cs + if(Game.handInvOnUse(%data, %obj)) { + %obj.decInventory(%this, 1); + %p = new FlareProjectile() { + dataBlock = FlareGrenadeProj; + initialDirection = %obj.getEyeVector(); + initialPosition = getBoxCenter(%obj.getWorldBox()); + sourceObject = %obj; + sourceSlot = 0; + }; + FlareSet.add(%p); + MissionCleanup.add(%p); + serverPlay3D(GrenadeThrowSound, getBoxCenter(%obj.getWorldBox())); + %p.schedule(6000, "delete"); + // miscellaneous grenade-throwing cleanup stuff + %obj.lastThrowTime[%data] = getSimTime(); + %obj.throwStrength = 0; + } +} + +// uncomment when explosion type can be set from script (dont want underwater explosion here) +//function grenadeOnEnterLiquid(%data, %obj, %coverage, %type, %flash) +//{ +// // 4: Lava +// // 5: Hot Lava +// // 6: Crusty Lava +// if(%type >=4 && %type <= 6) +// { +// if(%obj.getDamageState() !$= "Destroyed") +// { +// cancel(%obj.detThread); +// if(%flash) +// detonateFlashGrenade(%obj); +// else +// detonateGrenade(%obj); +// return(true); +// } +// } +// +// // flash grenades do not ignore quicksand +// if((%type == 7) && !%flash) +// return(true); +// +// return(false); +//} + +function GrenadeThrown::onThrow(%this, %gren) +{ + AIGrenadeThrown(%gren); + %gren.detThread = schedule(1500, %gren, "detonateGrenade", %gren); +} + +//function GrenadeThrown::onEnterLiquid(%data, %obj, %coverage, %type) +//{ +// if(!grenadeOnEnterLiquid(%data, %obj, %coverage, %type, false)) +// Parent::onEnterLiquid(%data, %obj, %coverage, %type); +//} + +function ConcussionGrenadeThrown::onThrow(%this, %gren) +{ + AIGrenadeThrown(%gren); + %gren.detThread = schedule(2000, %gren, "detonateGrenade", %gren); +} + +//function ConcussionGrenadeThrown::onEnterLiquid(%data, %obj, %coverage, %type) +//{ +// if(!grenadeOnEnterLiquid(%data, %obj, %coverage, %type, false)) +// Parent::onEnterLiquid(%data, %obj, %coverage, %type); +//} + +function detonateGrenade(%obj) +{ + %obj.setDamageState(Destroyed); + %data = %obj.getDataBlock(); + RadiusExplosion( %obj, %obj.getPosition(), %data.damageRadius, %data.indirectDamage, + %data.kickBackStrength, %obj.sourceObject, %data.radiusDamageType); + if(%data.hasShrapnel == 1){ + %startSpreadLevel = %data.minShrapnelSpread / 180; + %shrapnelSpread = (%data.maxShrapnelSpread / 180) - (%data.minShrapnelSpread / 180); + %pos = %obj.getPosition(); + %vector = "0 0 1"; + for(%i = 0; %i < %data.shrapnelNumber; %i++){ + %x = (getRandom() - 0.5) * 2 * 3.1415926 * %shrapnelSpread; + if(%x < 0) + %x = %x - %startSpreadLevel; + else + %x = %x + %startSpreadLevel; + %y = (getRandom() - 0.5) * 2 * 3.1415926 * %shrapnelSpread; + if(%y < 0) + %y = %y - %startSpreadLevel; + else + %y = %y + %startSpreadLevel; + %z = (getRandom() - 0.5) * 2 * 3.1415926 * %shrapnelSpread; + if(%z < 0) + %z = %z - %startSpreadLevel; + else + %z = %z + %startSpreadLevel; + %mat = MatrixCreateFromEuler(%x @ " " @ %y @ " " @ %z); + %newvector = MatrixMulVector(%mat, %vector); + %p = new (%data.shrapnelProjectileType)(){ + dataBlock = %data.shrapnelProjectile; + initialDirection = %newvector; + initialPosition = %pos; + sourceObject = %obj.sourceObject; //was %obj.sourceObject + damageFactor = 1; + }; + } + } + %obj.schedule(500,"delete"); +} + +function FlashGrenadeThrown::onThrow(%this, %gren) +{ + %gren.detThread = schedule(2000, %gren, "detonateFlashGrenade", %gren); +} + +//function FlashGrenadeThrown::onEnterLiquid(%data, %obj, %coverage, %type) +//{ +// if(!grenadeOnEnterLiquid(%data, %obj, %coverage, %type, true)) +// Parent::onEnterLiquid(%data, %obj, %coverage, %type); +//} + +function detonateFlashGrenade(%hg) +{ + %maxWhiteout = %hg.getDataBlock().maxWhiteout; + %thrower = %hg.sourceObject.client; + %hg.setDamageState(Destroyed); + %hgt = %hg.getTransform(); + %plX = firstword(%hgt); + %plY = getWord(%hgt, 1); + %plZ = getWord(%hgt, 2); + %pos = %plX @ " " @ %plY @ " " @ %plZ; + //all this stuff below ripped from projectiles.cs + + InitContainerRadiusSearch(%pos, 100.0, $TypeMasks::PlayerObjectType | + $TypeMasks::TurretObjectType); + + while ((%damage = containerSearchNext()) != 0) + { + %dist = containerSearchCurrDist(); + + %eyeXF = %damage.getEyeTransform(); + %epX = firstword(%eyeXF); + %epY = getWord(%eyeXF, 1); + %epZ = getWord(%eyeXF, 2); + %eyePos = %epX @ " " @ %epY @ " " @ %epZ; + %eyeVec = %damage.getEyeVector(); + + // Make sure we can see the thing... + if (ContainerRayCast(%eyePos, %pos, $TypeMasks::TerrainObjectType | + $TypeMasks::InteriorObjectType | + $TypeMasks::StaticObjectType, %damage) !$= "0") + { + continue; + } + + %distFactor = 1.0; + if (%dist >= 100) + %distFactor = 0.0; + else if (%dist >= 20) { + %distFactor = 1.0 - ((%dist - 20.0) / 80.0); + } + + %dif = VectorNormalize(VectorSub(%pos, %eyePos)); + %dot = VectorDot(%eyeVec, %dif); + + %difAcos = mRadToDeg(mAcos(%dot)); + %dotFactor = 1.0; + if (%difAcos > 60) + %dotFactor = ((1.0 - ((%difAcos - 60.0) / 120.0)) * 0.2) + 0.3; + else if (%difAcos > 45) + %dotFactor = ((1.0 - ((%difAcos - 45.0) / 15.0)) * 0.5) + 0.5; + + %totalFactor = %dotFactor * %distFactor; + + %prevWhiteOut = %damage.getWhiteOut(); + + if(!%prevWhiteOut) + if(!$teamDamage) + { + if(%damage.client != %thrower && %damage.client.team == %thrower.team) + messageClient(%damage.client, 'teamWhiteOut', '\c1You were hit by %1\'s whiteout grenade.', getTaggedString(%thrower.name)); + } + + %whiteoutVal = %prevWhiteOut + %totalFactor; + %whiteoutVal = %whiteoutVal * 1.8; + if(%whiteoutVal > %maxWhiteout) + { + //error("whitout at max"); + %whiteoutVal = %maxWhiteout; + } + + %damage.setWhiteOut( %whiteoutVal ); + } + %hg.schedule( 500, "delete" ); +} + +// ---------------------------------------------- +// mine functions +// ---------------------------------------------- + + +function MineDeployed::onThrow(%this, %mine, %thrower) +{ + %mine.armed = false; + %mine.damaged = 0; + %mine.detonated = false; + %mine.depCount = 0; + %mine.theClient = %thrower.client; + schedule(1500, %mine, "deployMineCheck", %mine, %thrower); +} + +function deployMineCheck(%mineObj, %player) +{ + if(%mineObj.depCount > %mineObj.getDatablock().maxDepCount) + explodeMine(%mineObj, true); + + // wait until the mine comes to rest + if(%mineObj.getVelocity() $= "0 0 0") + { + // 2-second delay before mine is armed -- let deploy thread play out etc. + schedule(%mineObj.getDatablock().armTime, %mineObj, "armDeployedMine", %mineObj); + + + // check for other deployed mines in the vicinity + InitContainerRadiusSearch(%mineObj.getWorldBoxCenter(), %mineObj.getDatablock().spacing, $TypeMasks::ItemObjectType); + while((%itemObj = containerSearchNext()) != 0) + { + if(%itemObj == %mineObj) + continue; + %ioType = %itemObj.getDatablock().getName(); + if(%ioType $= "MineDeployed") + schedule(100, %mineObj, "explodeMine", %mineObj, true); + else + continue; + } + // play "deploy" thread + %mineObj.playThread(0, "deploy"); + serverPlay3D(MineDeploySound, %mineObj.getTransform()); + %mineTeam = %mineObj.sourceObject.team; + $TeamDeployedCount[%mineTeam, MineDeployed]++; + if($TeamDeployedCount[%mineTeam, MineDeployed] > $TeamDeployableMax[MineDeployed]) + { + messageClient( %player.client, '', 'Maximum allowable mines deployed.' ); + schedule(100, %mineObj, "explodeMine", %mineObj, true); + } + else + { + //start the thread that keeps checking for objects near the mine... + mineCheckVicinity(%mineObj); + + //let the AI know *after* it's come to rest... + AIDeployMine(%mineObj); + + //let the game know there's a deployed mine + Game.notifyMineDeployed(%mineObj); + } + } + else + { + //schedule this deploy check again a little later + %mineObj.depCount++; + schedule(500, %mineObj, "deployMineCheck", %mineObj, %player); + } +} + +function armDeployedMine(%mine) +{ + %mine.armed = true; +} + +function mineCheckVicinity(%mine) +{ + // this function is called after the mine has been deployed. It will check the + // immediate area around the mine (2.5 meters at present) for players or vehicles + // passing by, and detonate if any are found. This is to extend the range of the + // mine so players don't have to collide with them to set them off. + + // don't bother to check if mine isn't armed yet + if(%mine.armed) + // don't keep checking if mine is already detonating + if(!%mine.boom) + { + // the actual check for objects in the area + %mineLoc = %mine.getWorldBoxCenter(); + %masks = $TypeMasks::PlayerObjectType | $TypeMasks::VehicleObjectType; + %detonateRange = %mine.getDatablock().proximity; + + InitContainerRadiusSearch(%mineLoc, %detonateRange, %masks); + while((%tgt = containerSearchNext()) != 0) + { + %mine.detonated = true; + schedule(50, %mine, "explodeMine", %mine, false); + break; + } + } + // if nothing set off the mine, schedule another check + if(!%mine.detonated) + schedule(300, %mine, "mineCheckVicinity", %mine); +} + +function MineDeployed::onCollision(%data, %obj, %col) +{ + // don't detonate if mine isn't armed yet + if(!%obj.armed) + return; + + // don't detonate if mine is already detonating + if(%obj.boom) + return; + + //check to see what it is that collided with the mine + %struck = %col.getClassName(); + if(%struck $= "Player" || %struck $= "WheeledVehicle" || %struck $= "FlyingVehicle") + { + //error("Mine detonated due to collision with #"@%col@" ("@%struck@"); armed = "@%obj.armed); + explodeMine(%obj, false); + } +} + +function explodeMine(%mo, %noDamage) +{ + %mo.noDamage = %noDamage; + %mo.setDamageState(Destroyed); +} + +function MineDeployed::damageObject(%data, %targetObject, %sourceObject, %position, %amount, %damageType) +{ + if(!%targetObject.armed) + return; + + if(%targetObject.boom) + return; + + %targetObject.damaged += %amount; + + if(%targetObject.damaged >= %data.maxDamage) + { + %targetObject.setDamageState(Destroyed); + } +} + +function MineDeployed::onDestroyed(%data, %obj, %lastState) +{ + if (%obj.isRemoved) + return; + %obj.isRemoved = true; + %obj.boom = true; + %mineTeam = %obj.team; + $TeamDeployedCount[%mineTeam, MineDeployed]--; + // %noDamage is a boolean flag -- don't want to set off all other mines in + // vicinity if there's a "mine overload", so apply no damage/impulse if true + if(!%obj.noDamage) + RadiusExplosion(%obj, %obj.getPosition(), %data.damageRadius, %data.indirectDamage, + %data.kickBackStrength, %obj.sourceObject, %data.radiusDamageType); + + %obj.schedule(600, "delete"); +} \ No newline at end of file diff --git a/Scripts/weapons.cs b/Scripts/weapons.cs new file mode 100644 index 0000000..78e08c2 --- /dev/null +++ b/Scripts/weapons.cs @@ -0,0 +1,411 @@ +$HandInvThrowTimeout = 0.8 * 1000; // 1/2 second between throwing grenades or mines + +$WeaponsHudData[0, bitmapName] = ""; +$WeaponsHudData[0, itemDataName] = "TargetingLaser"; +$WeaponsHudData[0, reticle] = "gui/hud_ret_targlaser"; +$WeaponsHudData[0, visible] = "false"; + +$WeaponsHudData[1, bitmapName] = "gui/hud_shocklance"; +$WeaponsHudData[1, itemDataName] = "ConstructionTool"; +$WeaponsHudData[1, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[1, visible] = "false"; + +$WeaponsHudData[2, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[2, itemDataName] = "SuperChaingun"; +$WeaponsHudData[2, ammoDataName] = ""; +$WeaponsHudData[2, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[2, visible] = "true"; + +$WeaponsHudData[3, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[3, itemDataName] = "RPChaingun"; +$WeaponsHudData[3, ammoDataName] = "RPChaingunAmmo"; +$WeaponsHudData[3, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[3, visible] = "true"; + +$WeaponsHudData[4, bitmapName] = "gui/hud_sniper"; +$WeaponsHudData[4, itemDataName] = "LMissileLauncher"; +$WeaponsHudData[4, ammoDataName] = "LMissileLauncherAmmo"; +$WeaponsHudData[4, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[4, visible] = "true"; + +$WeaponsHudData[5, bitmapName] = "gui/hud_sniper"; +$WeaponsHudData[5, itemDataName] = "snipergun"; +$WeaponsHudData[5, ammoDataName] = "snipergunammo"; +$WeaponsHudData[5, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[5, visible] = "true"; + +$WeaponsHudData[6, bitmapName] = "gui/hud_missiles"; +$WeaponsHudData[6, itemDataName] = "Bazooka"; +$WeaponsHudData[6, ammoDataName] = "BazookaAmmo"; +$WeaponsHudData[6, reticle] = "gui/ret_missile"; +$WeaponsHudData[6, visible] = "true"; + +$WeaponsHudData[7, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[7, itemDataName] = "MG42"; +$WeaponsHudData[7, ammoDataName] = "MG42Clip"; +$WeaponsHudData[7, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[7, visible] = "true"; + +$WeaponsHudData[8, bitmapName] = "gui/hud_plasma"; +$WeaponsHudData[8, itemDataName] = "flamer"; +$WeaponsHudData[8, ammoDataName] = "flamerAmmo"; +$WeaponsHudData[8, reticle] = "gui/ret_plasma"; +$WeaponsHudData[8, visible] = "true"; + +$WeaponsHudData[9, bitmapName] = "gui/hud_shocklance"; +$WeaponsHudData[9, itemDataName] = "KriegRifle"; +$WeaponsHudData[9, ammoDataName] = "RifleClip"; +$WeaponsHudData[9, reticle] = "gui/hud_ret_shocklance"; +$WeaponsHudData[9, visible] = "true"; + +$WeaponsHudData[10, bitmapName] = "gui/hud_missiles"; +$WeaponsHudData[10, itemDataName] = "RailGun"; +$WeaponsHudData[10, ammoDataName] = "RailGunAmmo"; +$WeaponsHudData[10, reticle] = "gui/ret_missile"; +$WeaponsHudData[10, visible] = "true"; + +$WeaponsHudData[11, bitmapName] = "gui/hud_missiles"; +$WeaponsHudData[11, itemDataName] = "AALauncher"; +$WeaponsHudData[11, ammoDataName] = "AALauncherAmmo"; +$WeaponsHudData[11, reticle] = "gui/ret_missile"; +$WeaponsHudData[11, visible] = "true"; + +$WeaponsHudData[12, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[12, itemDataName] = "Shotgun"; +$WeaponsHudData[12, ammoDataName] = "ShotgunClip"; +$WeaponsHudData[12, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[12, visible] = "true"; + +$WeaponsHudData[13, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[13, itemDataName] = "RShotgun"; +$WeaponsHudData[13, ammoDataName] = "RShotgunClip"; +$WeaponsHudData[13, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[13, visible] = "true"; + +$WeaponsHudData[14, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[14, itemDataName] = "HRPChaingun"; +$WeaponsHudData[14, ammoDataName] = "HRPChaingunClip"; +$WeaponsHudData[14, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[14, visible] = "true"; + +$WeaponsHudData[15, bitmapName] = "gui/hud_chaingun"; +$WeaponsHudData[15, itemDataName] = "LSMG"; +$WeaponsHudData[15, ammoDataName] = "LSMGClip"; +$WeaponsHudData[15, reticle] = "gui/ret_chaingun"; +$WeaponsHudData[15, visible] = "true"; + +$WeaponsHudData[16, bitmapName] = "gui/hud_shocklance"; +$WeaponsHudData[16, itemDataName] = "MergeTool"; +$WeaponsHudData[16, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[16, visible] = "true"; + +$WeaponsHudData[17, bitmapName] = "gui/hud_sniper"; +$WeaponsHudData[17, itemDataName] = "PBC"; +$WeaponsHudData[17, ammoDataName] = "PBCAmmo"; +$WeaponsHudData[17, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[17, visible] = "true"; + +$WeaponsHudData[18, bitmapName] = "gui/hud_sniper"; +$WeaponsHudData[18, itemDataName] = "EditingTool"; +$WeaponsHudData[18, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[18, visible] = "true"; + +$WeaponsHudData[19, bitmapName] = "gui/hud_shocklance"; +$WeaponsHudData[19, itemDataName] = "TextureTool"; +$WeaponsHudData[19, reticle] = "gui/hud_ret_sniper"; +$WeaponsHudData[19, visible] = "true"; + +$WeaponsHudData[20, bitmapName] = "gui/hud_plasma"; +$WeaponsHudData[20, itemDataName] = "NapalmMortar"; +$WeaponsHudData[20, ammoDataName] = "NapalmAmmo"; +$WeaponsHudData[20, reticle] = "gui/ret_mortor"; +$WeaponsHudData[20, visible] = "true"; + +$WeaponsHudData[21, bitmapName] = "gui/hud_plasma"; +$WeaponsHudData[21, itemDataName] = "LordAcidGun"; +$WeaponsHudData[21, reticle] = "gui/ret_grenade"; +$WeaponsHudData[21, visible] = "true"; + +$WeaponsHudData[22, bitmapName] = "gui/hud_grenlaunch"; +$WeaponsHudData[22, itemDataName] = "M4"; +$WeaponsHudData[22, ammoDataName] = "M4Ammo"; +$WeaponsHudData[22, reticle] = "gui/ret_grenade"; +$WeaponsHudData[22, visible] = "true"; + +$WeaponsHudData[23, bitmapName] = "gui/hud_plasma"; +$WeaponsHudData[23, itemDataName] = "DZShot"; +$WeaponsHudData[23, reticle] = "gui/ret_mortor"; +$WeaponsHudData[23, visible] = "true"; + +$WeaponsHudCount = 24; +//[most] + +$AmmoIncrement[PlasmaAmmo] = 10; +$AmmoIncrement[ChaingunAmmo] = 500; +$AmmoIncrement[DiscAmmo] = 5; +$AmmoIncrement[GrenadeLauncherAmmo] = 5; +$AmmoIncrement[MortarAmmo] = 5; +$AmmoIncrement[MissileLauncherAmmo] = 2; +$AmmoIncrement[Mine] = 3; +$AmmoIncrement[Grenade] = 5; +$AmmoIncrement[FlashGrenade] = 5; +$AmmoIncrement[FlareGrenade] = 5; +$AmmoIncrement[ConcussionGrenade] = 5; +$AmmoIncrement[RepairKit] = 1; + +// ------------------------------------------------------------------- +// z0dd - ZOD, 4/17/02. Addition. Ammo pickup fix, these were missing. +$AmmoIncrement[CameraGrenade] = 2; +$AmmoIncrement[Beacon] = 1; + +$AmmoIncrement[RPChaingunAmmo] = 30; +$AmmoIncrement[MGClip] = 1; +$AmmoIncrement[snipergunAmmo] = 10; +$AmmoIncrement[BazookaAmmo] = 5; +$AmmoIncrement[MG42Ammo] = 200; +$AmmoIncrement[MG42Clip] = 1; +$AmmoIncrement[flamerAmmo] = 25; +$AmmoIncrement[AALauncherAmmo] = 1; +$AmmoIncrement[KriegAmmo] = 10; +$AmmoIncrement[RifleClip] = 1; +$AmmoIncrement[ShotgunAmmo] = 8; +$AmmoIncrement[ShotgunClip] = 1; +$AmmoIncrement[RShotgunAmmo] = 25; +$AmmoIncrement[RShotgunClip] = 1; +$AmmoIncrement[LMissileLauncherAmmo] = 1; +$AmmoIncrement[RPGAmmo] = 1; +$AmmoIncrement[SmokeGrenade]= 5; +$AmmoIncrement[BeaconSmokeGrenade]= 2; +$AmmoIncrement[LSMGAmmo] = 60; +$AmmoIncrement[LSMGClip] = 1; +$AmmoIncrement[PBCAmmo] = 1; +$AmmoIncrement[NapalmAmmo] = 2; +$AmmoIncrement[RailGunAmmo] = 2; +$AmmoIncrement[M4Ammo] = 4; + + +//---------------------------------------------------------------------------- +// Weapons scripts +//-------------------------------------- + +// --- Mounting weapons +exec("scripts/weapons/allweapons.cs"); +exec("scripts/weapons/missileLauncher.cs"); +exec("scripts/weapons/targetingLaser.cs"); +exec("scripts/weapons/shockLance.cs"); +exec("scripts/weapons/constructionTool.cs"); +exec("scripts/weapons/superChaingun.cs"); +exec("scripts/weapons/RPChaingun.cs"); +exec("scripts/weapons/snipergun.cs"); +exec("scripts/weapons/bazooka.cs"); +exec("scripts/weapons/MG42.cs"); +exec("scripts/weapons/railgun.cs"); +exec("scripts/weapons/flamethrower.cs"); +exec("scripts/weapons/rocketLauncher.cs"); +exec("scripts/weapons/Krieg.cs"); +exec("scripts/weapons/Shotgun.cs"); +exec("scripts/weapons/FlameMortar.cs"); +exec("scripts/weapons/LightMachineGun.cs"); +exec("scripts/weapons/mergetool.cs"); +exec("scripts/weapons/EditingTool.cs"); +exec("scripts/weapons/TextureTool.cs"); +exec("scripts/weapons/ZLordPoison.cs"); +exec("scripts/weapons/DZShot.cs"); +exec("scripts/weapons/PBC.cs"); +exec("scripts/weapons/mine.cs"); +exec("scripts/weapons/M4GrenadeLauncher.cs"); +exec("scripts/weapons/grenade.cs"); +exec("scripts/weapons/Smokegrenade.cs"); +exec("scripts/weapons/flashGrenade.cs"); +exec("scripts/weapons/flareGrenade.cs"); +exec("scripts/weapons/concussionGrenade.cs"); +exec("scripts/weapons/cameraGrenade.cs"); + +//---------------------------------------------------------------------------- + +function Weapon::onUse(%data, %obj) +{ + if(Game.weaponOnUse(%data, %obj)) + if (%obj.getDataBlock().className $= Armor) + %obj.mountImage(%data.image, $WeaponSlot); +} + +function WeaponImage::onMount(%this,%obj,%slot) +{ + //MES -- is call below useful at all? + //Parent::onMount(%this, %obj, %slot); + if(%obj.getClassName() !$= "Player") + return; + + //messageClient(%obj.client, 'MsgWeaponMount', "", %this, %obj, %slot); + // Looks arm position + if (%this.armthread $= "") + { + %obj.setArmThread(look); + } + else + { + %obj.setArmThread(%this.armThread); + } + + // Initial ammo state + if(%obj.getMountedImage($WeaponSlot).ammo !$= "") + if (%obj.getInventory(%this.ammo)) + %obj.setImageAmmo(%slot,true); + + %obj.client.setWeaponsHudActive(%this.item); + if(%obj.getMountedImage($WeaponSlot).ammo !$= "") + %obj.client.setAmmoHudCount(%obj.getInventory(%this.ammo)); + else + %obj.client.setAmmoHudCount(-1); +} + +function WeaponImage::onUnmount(%this,%obj,%slot) +{ + %obj.client.setWeaponsHudActive(%this.item, 1); + %obj.client.setAmmoHudCount(-1); + commandToClient(%obj.client,'removeReticle'); + // try to avoid running around with sniper/missile arm thread and no weapon + %obj.setArmThread(look); + Parent::onUnmount(%this, %obj, %slot); +} + +function Ammo::onInventory(%this,%obj,%amount) +{ + // Loop through and make sure the images using this ammo have + // their ammo states set. + for (%i = 0; %i < 8; %i++) { + %image = %obj.getMountedImage(%i); + if (%image > 0) + { + if (isObject(%image.ammo) && %image.ammo.getId() == %this.getId()) + %obj.setImageAmmo(%i,%amount != 0); + } + } + ItemData::onInventory(%this,%obj,%amount); + // Uh, don't update the hud ammo counters if this is a corpse...that's bad. + if ( %obj.getClassname() $= "Player" && %obj.getState() !$= "Dead" ) + { + %obj.client.setWeaponsHudAmmo(%this.getName(), %amount); + if(%obj.getMountedImage($WeaponSlot).ammo $= %this.getName()) + %obj.client.setAmmoHudCount(%amount); + } +} + +function Weapon::onInventory(%this,%obj,%amount) +{ + if(Game.weaponOnInventory(%this, %obj, %amount)) + { + // Do not update the hud if this object is a corpse: + if ( %obj.getState() !$= "Dead" ) + %obj.client.setWeaponsHudItem(%this.getName(), 0, 1); + ItemData::onInventory(%this,%obj,%amount); + // if a player threw a weapon (which means that player isn't currently + // holding a weapon), set armthread to "no weapon" + // MES - taken out to avoid v-menu animation problems (bug #4749) + //if((%amount == 0) && (%obj.getClassName() $= "Player")) + // %obj.setArmThread(looknw); + } +} + +function Weapon::onPickup(%this, %obj, %shape, %amount) +{ + // If player doesn't have a weapon in hand, use this one... + if ( %shape.getClassName() $= "Player" && %shape.getMountedImage( $WeaponSlot ) == 0 ) + %shape.use( %this.getName() ); +} + +function HandInventory::onInventory(%this,%obj,%amount) +{ + // prevent console errors when throwing ammo pack + if(%obj.getClassName() $= "Player") + %obj.client.setInventoryHudAmount(%this.getName(), %amount); + ItemData::onInventory(%this,%obj,%amount); +} + +function HandInventory::onUse(%data, %obj) +{ + // %obj = player %data = datablock of what's being thrown + if(Game.handInvOnUse(%data, %obj)) + { + //AI HOOK - If you change the %throwStren, tell Tinman!!! + //Or edit aiInventory.cs and search for: use(%grenadeType); + + %tossTimeout = getSimTime() - %obj.lastThrowTime[%data]; + if(%tossTimeout < $HandInvThrowTimeout) + return; + + %throwStren = %obj.throwStrength; + + %obj.decInventory(%data, 1); + %thrownItem = new Item() + { + dataBlock = %data.thrownItem; + sourceObject = %obj; + }; + MissionCleanup.add(%thrownItem); + + // throw it + %eye = %obj.getEyeVector(); + %vec = vectorScale(%eye, (%throwStren * 20.0)); + + // add a vertical component to give it a better arc + %dot = vectorDot("0 0 1", %eye); + if(%dot < 0) + %dot = -%dot; + %vec = vectorAdd(%vec, vectorScale("0 0 4", 1 - %dot)); + + // add player's velocity + %vec = vectorAdd(%vec, vectorScale(%obj.getVelocity(), 0.4)); + %pos = getBoxCenter(%obj.getWorldBox()); + + + %thrownItem.sourceObject = %obj; + %thrownItem.team = %obj.team; + %thrownItem.setTransform(%pos); + + %thrownItem.applyImpulse(%pos, %vec); + %thrownItem.setCollisionTimeout(%obj); + serverPlay3D(GrenadeThrowSound, %pos); + %obj.lastThrowTime[%data] = getSimTime(); + + %thrownItem.getDataBlock().onThrow(%thrownItem, %obj); + %obj.throwStrength = 0; + } +} + +function HandInventoryImage::onMount(%this,%obj,%slot) +{ + messageClient(%col.client, 'MsgHandInventoryMount', "", %this, %obj, %slot); + // Looks arm position + if (%this.armthread $= "") + %obj.setArmThread(look); + else + %obj.setArmThread(%this.armThread); + + // Initial ammo state + if (%obj.getInventory(%this.ammo)) + %obj.setImageAmmo(%slot,true); + + %obj.client.setWeaponsHudActive(%this.item); +} + +function Weapon::incCatagory(%data, %obj) +{ + // Don't count the targeting laser as a weapon slot: + if ( %data.getName() !$= "TargetingLaser" ) + %obj.weaponCount++; +} + +function Weapon::decCatagory(%data, %obj) +{ + // Don't count the targeting laser as a weapon slot: + if ( %data.getName() !$= "TargetingLaser" ) + %obj.weaponCount--; +} + +function SimObject::damageObject(%data) +{ + //function was added to reduce console err msg spam +} + diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..ed4455d --- /dev/null +++ b/TODO.txt @@ -0,0 +1,4 @@ +Enhance the zombie spawn pack to be like the advanced spawns. +Sentinels! +Affliction Scripts -> weather control? +Zombie pods? \ No newline at end of file diff --git a/missions/ACCMFlatland.mis b/missions/ACCMFlatland.mis new file mode 100644 index 0000000..622190b --- /dev/null +++ b/missions/ACCMFlatland.mis @@ -0,0 +1,191 @@ +// DisplayName = Flatland Infected +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +//Flat. Land. Infected. +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Limited Visibility Due to Fog +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + musicTrack = "ACCM_Commando"; + cdTrack = "6"; + + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "0 0 100"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + + locked = "1"; + }; + }; + }; + new SimGroup(team0) { + providesPower = "1"; + new StaticShape(InventoryStation) { + position = "0 0 100"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationInventory"; + locked = "true"; + }; + new StaticShape(VehicleStation) { + position = "50 0 97"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationVehiclePad"; + locked = "true"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.400000 0.400000 0.400000 1.000000"; + ambient = "0.400000 0.400000 0.400000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatland.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + GraphFile = "Flatland.nav"; + conjoinBowlDev = "20"; + position = "0 0 0 1"; + coverage = "0"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "96.6566 36.3909 124.893"; + rotation = "-0.0968575 -0.187971 0.977387 233.462"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "700"; + fogColor = "0.300000 0.300000 0.300000 1.000000"; + fogVolume1 = "700 100 5000"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_brown.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/fog.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10000"; + maxDistance = "10000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Apocalypse.mis b/missions/Apocalypse.mis new file mode 100644 index 0000000..627e91e --- /dev/null +++ b/missions/Apocalypse.mis @@ -0,0 +1,1327 @@ +// DisplayName = Apocalypse +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +//Once a Paradise, All of the Earth has been destroyed by death... +//--Global Defense Intelligence, 3431 A.D. +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Extremely Low Visibility. +//A Map by: Blnukem +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "6"; + musicTrack = "ACCM_Warrior"; + powerCount = "0"; + CTF_scoreLimit = "8"; + CTF_timeLimit = "25"; + + new MissionArea(MissionArea) { + area = "-1144 -1168 2384 2208"; + flightCeiling = "2000"; + flightCeilingRange = "50"; + + locked = "true"; + }; + new Sun() { + position = "-1216 -848 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.500000 0.400000 0.300000 1.000000"; + ambient = "0.300000 0.300000 0.300000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet1"; + terrainFile = "WhiteDwarf.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + visibleDistance = "1200"; + hazeDistance = "250"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + conjoinBowlDev = "20"; + GraphFile = "WhiteDwarf.nav"; + coverage = "0"; + XDimOverSize = "0"; + position = "0 0 0 1"; + YDimOverSize = "0"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-46.2546 -195.958 122.274"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "20"; + indoorWeight = "30"; + outdoorWeight = "70"; + + locked = "true"; + }; + new SpawnSphere() { + position = "-352.093 -254.12 148.513"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "90"; + indoorWeight = "35"; + outdoorWeight = "65"; + + locked = "true"; + }; + new SpawnSphere() { + position = "-507.546 -235.592 156.169"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "20"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + }; + new SimGroup(base0) { + + powerCount = "2"; + + new WayPoint() { + position = "-24.7045 -171.459 112.787"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Supplyment Bunker Alpha"; + team = "1"; + + locked = "true"; + }; + new InteriorInstance() { + position = "-18.9144 -195.446 125.649"; + rotation = "0 0 -1 28.6479"; + scale = "1 1 1"; + interiorFile = "rilke_whitedwarf_towerbunker.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "true"; + }; + new InteriorInstance() { + position = "-357.542 -191.401 154.044"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "rilke_whitedwarf_mainbase.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_1"; + + team = "1"; + locked = "true"; + }; + new WayPoint() { + position = "-351.542 -223.401 141.044"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Main Base"; + team = "1"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-507.546 -235.592 156.169"; + rotation = "0 0 -1 26.356"; + scale = "1 0.813473 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new WayPoint() { + position = "175.659 479.848 137.056"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Supplyment Bunker Bravo"; + team = "1"; + + locked = "True"; + }; + new InteriorInstance() { + position = "199.694 473.361 149.918"; + rotation = "-0 0 -1 90"; + scale = "1 1 1"; + interiorFile = "rilke_whitedwarf_towerbunker.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "-345.864 -141.631 91.9831"; + rotation = "0 0 1 197.853"; + scale = "1 1 1"; + interiorFile = "ram_wall4.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-506.672 -237.553 155.369"; + rotation = "0 0 1 153.553"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + station = "17307"; + Target = "33"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-494.647 -261.727 158.169"; + rotation = "0 0 1 153.553"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "35"; + Trigger = "18897"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-343.011 -214.883 142.644"; + rotation = "0 0 1 45"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "36"; + Trigger = "20296"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-360.022 -214.876 142.644"; + rotation = "0 0 -1 45"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "37"; + Trigger = "20756"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-351.542 -234.601 150.044"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "38"; + Trigger = "21050"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-339.342 -222.201 150.044"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Main"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "39"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-363.81 -224.679 150.045"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + nameTag = "Auxillary"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "40"; + team = "1"; + locked = "True"; + }; + new ForceFieldBare() { + position = "-357.525 -235.539 149.652"; + rotation = "1 0 0 0"; + scale = "0.962677 24.324 6.63037"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + pzone = "21805"; + Target = "41"; + team = "1"; + locked = "True"; + }; + new ForceFieldBare() { + position = "-346.535 -235.564 149.984"; + rotation = "1 0 0 0"; + scale = "0.969055 24.5129 6.21656"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + pzone = "21989"; + Target = "42"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-347.839 -148.116 119.983"; + rotation = "0 0 1 15.853"; + scale = "1 1 1"; + nameTag = "Outdoor"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "43"; + Trigger = "22285"; + team = "1"; + locked = "True"; + }; + }; + new SimGroup(SupplyBunker1) { + + new StaticShape() { + position = "-22.8827 -174.794 120.187"; + rotation = "0 0 -1 28.6479"; + scale = "1 1 1"; + nameTag = "Bunker"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "44"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-18.7571 -168.162 113.787"; + rotation = "0 0 1 61"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "45"; + Trigger = "23420"; + team = "1"; + locked = "True"; + }; + }; + new SimGroup(SupplyBunker2) { + + new StaticShape() { + position = "179.709 479.844 144.456"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + nameTag = "Bunker"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "46"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "175.659 487.248 138.056"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "47"; + Trigger = "24747"; + team = "1"; + locked = "True"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(flag1) { + position = "-395.316 -210.379 171.587"; + rotation = "0.205498 -0.131718 0.969753 66.9263"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + }; + new TSStatic() { + position = "-665.332 438.397 120.19"; + rotation = "0 0 1 23"; + scale = "1.9 1.9 1.9"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.100000 0.100000 0.100000 1.000000"; + fogDistance = "400"; + fogColor = "0.150000 0.150000 0.150000 1.000000"; + fogVolume1 = "100 0 100"; + fogVolume2 = "500 100 200"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "0 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 -57501876.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -51974240.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -0.000020"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 0 1.97348e-39"; + high_fogVolume2 = "-1 -2.87572e+38 -0.0730211"; + high_fogVolume3 = "-1 -0.000779272 -3.60032e-33"; + + cloudSpeed0 = "0.0000003 0.0000003"; + locked = "true"; + }; + new TSStatic() { + position = "-271.394 -137.416 114.577"; + rotation = "0 0 -1 59.0003"; + scale = "1.2 1.2 1.2"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-10.9143 -195.684 113.608"; + rotation = "0 0 -1 55.0039"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-14.77 -194.934 113.594"; + rotation = "0 0 -1 8.02147"; + scale = "1 1 1"; + shapeName = "stackable2m.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-5.38134 -179.015 111.013"; + rotation = "0 0 -1 40.68"; + scale = "1 1 1"; + shapeName = "stackable1m.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-9.48984 -176.501 110.588"; + rotation = "0 0 1 61.8794"; + scale = "1 1 1"; + shapeName = "stackable1m.dts"; + + locked = "true"; + }; + new SimGroup(Enviornment) { + + powerCount = "0"; + + new SimGroup(Addition13SWTree22) { + + powerCount = "0"; + + new TSStatic() { + position = "212 708 112.562"; + rotation = "0 0 1 219"; + scale = "1.4 1.4 1.4"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-516 -572 126.672"; + rotation = "1 0 0 0"; + scale = "0.9 0.9 0.9"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "244 796 112.422"; + rotation = "0 0 1 219"; + scale = "1.5 1.5 1.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "364 -492 141.969"; + rotation = "0 0 -1 5.99979"; + scale = "0.5 0.5 0.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "252 -548 176.719"; + rotation = "0 0 -1 14"; + scale = "0.8 0.8 0.8"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "380 -220 124.891"; + rotation = "0 0 -1 100"; + scale = "0.5 0.5 0.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-468 140 139.531"; + rotation = "0 0 -1 71.0004"; + scale = "1 1 1"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "84 -92 115.984"; + rotation = "0 0 -1 72.0002"; + scale = "1.1 1.1 1.1"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "132 -516 159.219"; + rotation = "0 0 -1 101"; + scale = "1.2 1.2 1.2"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-740 116 149.891"; + rotation = "0 0 -1 115"; + scale = "0.6 0.6 0.6"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-316 364 127.672"; + rotation = "0 0 -1 14"; + scale = "1.4 1.4 1.4"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "436 188 142.578"; + rotation = "0 0 1 66.0002"; + scale = "0.9 0.9 0.9"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "292 -4 139.125"; + rotation = "0 0 -1 19.0001"; + scale = "1.5 1.5 1.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-84 -532 143.734"; + rotation = "0 0 1 206"; + scale = "1.2 1.2 1.2"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "508 428 117.922"; + rotation = "0 0 1 152"; + scale = "0.7 0.7 0.7"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "468 -452 130.875"; + rotation = "0 0 1 161"; + scale = "1.4 1.4 1.4"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-700 812 137.469"; + rotation = "0 0 1 70"; + scale = "0.7 0.7 0.7"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-716 -340 139.891"; + rotation = "0 0 1 212"; + scale = "0.5 0.5 0.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-644 244 170.516"; + rotation = "0 0 -1 111"; + scale = "1.5 1.5 1.5"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "292 540 129.078"; + rotation = "0 0 1 91.9998"; + scale = "0.9 0.9 0.9"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-412 564 162.984"; + rotation = "0 0 1 154"; + scale = "1.1 1.1 1.1"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "508 212 145.203"; + rotation = "0 0 1 26"; + scale = "1 1 1"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-28 420 114.047"; + rotation = "0 0 -1 116"; + scale = "0.8 0.8 0.8"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "380 -332 144.156"; + rotation = "0 0 1 137"; + scale = "0.7 0.7 0.7"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-692 812 136.703"; + rotation = "0 0 1 48"; + scale = "0.9 0.9 0.9"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-428 748 106.797"; + rotation = "0 0 -1 16.0002"; + scale = "1.3 1.3 1.3"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-684 -244 122.5"; + rotation = "0 0 -1 68.0003"; + scale = "1.4 1.4 1.4"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-540 812 98.6093"; + rotation = "0 0 1 236"; + scale = "0.9 0.9 0.9"; + shapeName = "sorg22.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition14DSPlant16) { + + powerCount = "0"; + + new TSStatic() { + position = "436 -212 129.016"; + rotation = "0 0 1 187"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "180 860 133.016"; + rotation = "0 0 -1 22.9999"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-452 292 142.609"; + rotation = "0 0 1 48"; + scale = "1.3 1.3 1.3"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-348 92 117.641"; + rotation = "0 0 1 70.9998"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "260 932 135.578"; + rotation = "0 0 -1 64.0005"; + scale = "1.6 1.6 1.6"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-564 756 97.422"; + rotation = "0 0 1 2.99997"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-532 -668 116.781"; + rotation = "0 0 1 60.0001"; + scale = "1.4 1.4 1.4"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "548 -308 108.484"; + rotation = "0 0 -1 86.0004"; + scale = "0.6 0.6 0.6"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "100 -316 112.172"; + rotation = "0 0 1 103"; + scale = "1.2 1.2 1.2"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-484 -188 141.016"; + rotation = "0 0 -1 69.0002"; + scale = "0.8 0.8 0.8"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-764 -588 144.094"; + rotation = "0 0 1 112"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "556 -108 116.344"; + rotation = "0 0 1 181"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "468 196 136.156"; + rotation = "0 0 1 200"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "340 716 104.156"; + rotation = "0 0 -1 53"; + scale = "2 2 2"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "276 -20 143.672"; + rotation = "0 0 -1 32"; + scale = "2 2 2"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "596 -436 114.891"; + rotation = "0 0 -1 70.0005"; + scale = "1.1 1.1 1.1"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "500 268 124.984"; + rotation = "0 0 -1 38"; + scale = "1.7 1.7 1.7"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-580 -476 125.484"; + rotation = "0 0 1 2.99997"; + scale = "1 1 1"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "476 164 143.141"; + rotation = "0 0 1 153"; + scale = "1.1 1.1 1.1"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "76 108 102.422"; + rotation = "0 0 1 198"; + scale = "1.4 1.4 1.4"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-940 156 131.203"; + rotation = "0 0 -1 22.9999"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-796 -52 132.016"; + rotation = "0 0 1 145"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "172 -196 131.234"; + rotation = "0 0 -1 53.9998"; + scale = "1.6 1.6 1.6"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-84 556 133.25"; + rotation = "0 0 1 174"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "252 -156 141.094"; + rotation = "0 0 1 224"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-476 -492 114.078"; + rotation = "0 0 -1 86.0004"; + scale = "0.9 0.9 0.9"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-156 -116 94.5156"; + rotation = "0 0 1 220"; + scale = "0.9 0.9 0.9"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "44.2101 2.90749 103.216"; + rotation = "0 0 1 113"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition15DSPlant18) { + + powerCount = "0"; + + new TSStatic() { + position = "412 756 128.812"; + rotation = "0 0 1 70"; + scale = "1.2 1.2 1.2"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-332 -52 82.3906"; + rotation = "0 0 -1 78.0002"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-636 876 108.156"; + rotation = "0 0 1 232"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-28 940 156.313"; + rotation = "0 0 1 28"; + scale = "1.4 1.4 1.4"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-700 -388 142.359"; + rotation = "0 0 1 106"; + scale = "1.9 1.9 1.9"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-844 692 124.359"; + rotation = "0 0 -1 116"; + scale = "0.8 0.8 0.8"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-420 12 114.078"; + rotation = "0 0 1 180"; + scale = "2 2 2"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-294.065 806.472 99.4218"; + rotation = "0 0 1 184"; + scale = "1.6 1.6 1.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "60 -588 123.5"; + rotation = "0 0 1 202"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-540 596 171.438"; + rotation = "0 0 -1 4.99997"; + scale = "1.9 1.9 1.9"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-780 60 140.125"; + rotation = "0 0 1 178"; + scale = "0.8 0.8 0.8"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-796 716 113.453"; + rotation = "0 0 1 156"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-356 -548 139.516"; + rotation = "0 0 1 149"; + scale = "0.6 0.6 0.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "756 316 112.094"; + rotation = "0 0 1 141"; + scale = "1.2 1.2 1.2"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "420 132 130.906"; + rotation = "0 0 1 84.0002"; + scale = "0.6 0.6 0.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "164 -380 133.422"; + rotation = "0 0 1 179"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-268 764 104.109"; + rotation = "0 0 -1 118"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "692 132 113.297"; + rotation = "0 0 1 124"; + scale = "0.6 0.6 0.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-196 708 148.703"; + rotation = "0 0 1 151"; + scale = "1 1 1"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-684 268 163.344"; + rotation = "0 0 1 124"; + scale = "1.3 1.3 1.3"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "556 892 86.8282"; + rotation = "0 0 -1 17.9998"; + scale = "1.6 1.6 1.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-804 812 118.991"; + rotation = "0 0 1 170"; + scale = "0.9 0.9 0.9"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-436 396 153.906"; + rotation = "0 0 1 235"; + scale = "2 2 2"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "724 796 114.516"; + rotation = "0 0 1 221"; + scale = "0.9 0.9 0.9"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-612 -476 115.938"; + rotation = "0 0 1 203"; + scale = "0.6 0.6 0.6"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-108 796 156.719"; + rotation = "0 0 1 197"; + scale = "2 2 2"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-52 628 131.812"; + rotation = "0 0 -1 110"; + scale = "0.8 0.8 0.8"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "540 -388 107.922"; + rotation = "0 0 1 222"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-684 -100 123.375"; + rotation = "0 0 -1 53.9998"; + scale = "0.5 0.5 0.5"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "692 -228 118.141"; + rotation = "0 0 1 47"; + scale = "1.8 1.8 1.8"; + shapeName = "dorg18.dts"; + + locked = "true"; + }; + }; + new SimGroup(Ambience) { + + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/fog.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10000"; + maxDistance = "10000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/coldwind1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "0.4"; + isLooping = "1"; + is3D = "1"; + minDistance = "10000"; + maxDistance = "10000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "0.4"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/yeti_howl2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "0.5"; + isLooping = "1"; + is3D = "1"; + minDistance = "5000"; + maxDistance = "5000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "0.5"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + }; + }; + new TSStatic() { + position = "-196.836 360.964 123.57"; + rotation = "0 0 1 70"; + scale = "1.5 1.5 1.5"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "281.833 354.807 155.642"; + rotation = "0 0 1 132"; + scale = "1.2 1.2 1.2"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-67.9388 -159.785 110.101"; + rotation = "0 0 1 104"; + scale = "0.7 0.7 0.7"; + shapeName = "dorg16.dts"; + + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/BattleGrounds.mis b/missions/BattleGrounds.mis new file mode 100644 index 0000000..b0e6f63 --- /dev/null +++ b/missions/BattleGrounds.mis @@ -0,0 +1,457 @@ +// DisplayName = Battlegrounds +// MissionTypes = CombatCon + +//--- MISSION QUOTE BEGIN --- +//A Fight to the Finish... +// --Nukem +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Beware of Environment - Lightning and Meteors +//Limited Visibility Due to Fog +//ServerSide Construction Mission +//Map By: Blnukem +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "ACCM_Main"; + powerCount = "0"; + cdTrack = "6"; + CTF_scoreLimit = "5"; + + new MissionArea(MissionArea) { + area = "-944 -1256 2624 2624"; + flightCeiling = "3000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sky(Sky) { + position = "120 32 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.300000 0.300000 0.300000 0.000000"; + fogDistance = "200"; + fogColor = "0.350000 0.350000 0.350000 1.000000"; + fogVolume1 = "50 10 45"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "Desert_l4.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 -190776.046875"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -62109.015625"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 1.73821e-33 1.41683e-33"; + high_fogVolume2 = "-1 0 0"; + high_fogVolume3 = "-1 1.73819e-33 -8.94073e-08"; + + cloudSpeed0 = "0.000000 0.000700"; + locked = "true"; + }; + new Sun() { + position = "120 32 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet1"; + terrainFile = "Reversion.ter"; + squareSize = "8"; + emptySquares = "100802 101570"; + + dObj = "12114"; + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "65"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + coverage = "0"; + conjoinBowlDev = "20"; + GraphFile = "Reversion.nav"; + position = "0 0 0 1"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-620.438 283.257 142.023"; + rotation = "0.932418 0.0841451 -0.351448 28.8022"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "-729.703 556.37 124.251"; + rotation = "0.0266336 -0.099798 0.994651 150.268"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "578.294 -468.308 149.149"; + rotation = "0.219564 -0.212904 0.952084 91.0485"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(team0) { + + powerCount = "1"; + + new SimGroup(AIObjectives) { + + powerCount = "1"; + }; + }; + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "350.724 986.731 67.0114"; + rotation = "0 0 1 190.977"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "35"; + sphereWeight = "50"; + indoorWeight = "50"; + outdoorWeight = "50"; + + locked = "True"; + }; + }; + new SimGroup(AIObjectives) { + + powerCount = "0"; + + new SimGroup() { + + powerCount = "0"; + }; + }; + new SimGroup(veh0) { + + powerCount = "1"; + + new InteriorInstance() { + position = "350.219 977.625 63.3888"; + rotation = "0 0 1 190.977"; + scale = "1 1 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "352.314 988.424 63.0888"; + rotation = "0 0 1 10.9775"; + scale = "1 1 1"; + nameTag = "\x01991"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "1"; + lastState = "1"; + Target = "40"; + station = "5151"; + locked = "True"; + }; + new StaticShape() { + position = "357.298 1013.42 65.4342"; + rotation = "0 0 1 10.8863"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "5118"; + team = "1"; + lastState = "1"; + Target = "41"; + locked = "True"; + }; + new StaticShape() { + position = "345.164 979.141 36.9347"; + rotation = "0 0 -1 79.0682"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + lastState = "1"; + Target = "42"; + locked = "True"; + }; + }; + }; + new SimGroup(Team2) { + + powerCount = "1"; + + new SimGroup(AIObjectives) { + + powerCount = "1"; + }; + new SpawnSphere() { + position = "360.834 -1100.55 66.1535"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "35"; + sphereWeight = "50"; + indoorWeight = "50"; + outdoorWeight = "50"; + + locked = "True"; + }; + new StaticShape() { + position = "361.054 -1101.51 62.2309"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + nameTag = "\x01991"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "2"; + lastState = "1"; + Target = "43"; + station = "5153"; + locked = "True"; + }; + new InteriorInstance() { + position = "361.054 -1090.51 62.5309"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new StaticShape() { + position = "362.277 -1126.88 64.5"; + rotation = "0 0 1 179.518"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "5129"; + team = "2"; + lastState = "1"; + Target = "44"; + locked = "True"; + }; + new StaticShape() { + position = "349.5 -1094.46 19.2552"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "2"; + lastState = "1"; + Target = "45"; + locked = "True"; + }; + }; + }; + new WaterBlock() { + position = "304 464 -158.107"; + rotation = "1 0 0 0"; + scale = "2048 2048 200"; + liquidType = "OceanWater"; + density = "1"; + viscosity = "5"; + waveMagnitude = "0.5"; + surfaceTexture = "LiquidTiles/BlueWater"; + surfaceOpacity = "0.4"; + envMapTexture = "lush/skies/lushcloud1"; + envMapIntensity = "0.2"; + removeWetEdges = "0"; + AudioEnvironment = "Underwater"; + }; + new InteriorInstance() { + position = "530.654 79.4509 166.963"; + rotation = "0 0 -1 13.1781"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "529 102.03 164.117"; + rotation = "0 0 -1 111.727"; + scale = "1.11194 1 1.04078"; + interiorFile = "bspir2.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "531.408 95.388 164.825"; + rotation = "0 0 1 129.488"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "537.113 96.4395 163.494"; + rotation = "0 0 1 125.868"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "519.094 86.6545 167.802"; + rotation = "0 0 1 194.988"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "539.777 86.5611 170.618"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir1.dif"; + showTerrainInside = "0"; + }; + new Precipitation(Precipitation) { + position = "533.784 86.1574 176.087"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Rain"; + lockCount = "0"; + homingCount = "0"; + percentage = "1"; + color1 = "0.600000 0.600000 0.600000 1.000000"; + color2 = "-1.000000 0.000000 0.000000 1.000000"; + color3 = "-1.000000 0.000000 0.000000 1.000000"; + offsetSpeed = "0.25"; + minVelocity = "1.25"; + maxVelocity = "4"; + maxNumDrops = "2000"; + maxRadius = "80"; + }; + new Lightning() { + position = "533.784 86.1574 176.087"; + rotation = "1 0 0 0"; + scale = "2881.18 2755.12 300"; + dataBlock = "DefaultStorm"; + lockCount = "0"; + homingCount = "0"; + strikesPerMinute = "4"; + strikeWidth = "2.5"; + chanceToHitTarget = "0.5"; + strikeRadius = "20"; + boltStartRadius = "20"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.100000 0.100000 1.000000 1.000000"; + useFog = "1"; + }; + new FireballAtmosphere(FireballAtmosphere) { + position = "544.789 89.1176 458.85"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "fireball"; + lockCount = "0"; + homingCount = "0"; + dropRadius = "1000"; + dropsPerMinute = "5"; + minDropAngle = "0"; + maxDropAngle = "10"; + startVelocity = "300"; + dropHeight = "1000"; + dropDir = "0.212 0.212 -0.953998"; + }; + new WayPoint() { + position = "352.98 903.229 50.2574"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Team 1 Base"; + team = "1"; + + locked = "True"; + }; + new WayPoint() { + position = "382.526 -1019.83 50.1383"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Team 2 Base"; + team = "2"; + + locked = "True"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Flatland.mis b/missions/Flatland.mis new file mode 100644 index 0000000..ef6b9ef --- /dev/null +++ b/missions/Flatland.mis @@ -0,0 +1,196 @@ +// DisplayName = Flatland +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// Flat. Land. Build! +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "desert"; + cdTrack = "6"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "0 0 100"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + + locked = "1"; + }; + }; + }; + new SimGroup(team0) { + + providesPower = "1"; + powerCount = "1"; + + new StaticShape(InventoryStation) { + position = "0 0 100"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + hasExploded = "1"; + lastState = "0"; + damageTimeMS = "3374208"; + lastDamagedBy = "7217"; + lastDamagedByTeam = "1"; + team = "0"; + Target = "33"; + Trigger = "7229"; + notReady = "1"; + inUse = "Down"; + locked = "true"; + cloak = "0"; + }; + new StaticShape(VehicleStation) { + position = "50 0 97"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + damageTimeMS = "4104599"; + lastDamagedBy = "7217"; + Ready = "1"; + lastDamagedByTeam = "1"; + team = "0"; + Target = "34"; + inUse = "Down"; + station = "7249"; + locked = "true"; + cloak = "0"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatland.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + position = "0 0 0 1"; + coverage = "0"; + GraphFile = "Flatland.nav"; + rotation = "0 0 0 0"; + conjoinBowlDev = "20"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "750"; + fogColor = "0.600000 0.600000 0.600000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_blue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/FlatlandBig.mis b/missions/FlatlandBig.mis new file mode 100644 index 0000000..b50eea7 --- /dev/null +++ b/missions/FlatlandBig.mis @@ -0,0 +1,142 @@ +// DisplayName = Flatland Big +// MissionTypes = Construction + +//--- Mission Quote Begin --- +// Flat. Land. Big! +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + musicTrack = "desert"; + cdTrack = "6"; + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + locked = "true"; + }; + new SimGroup(Teams) { + new SimGroup(Team1) { + new SimGroup(spawnspheres) { + new SpawnSphere() { + position = "0 0 100"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + locked = "1"; + }; + }; + }; + new SimGroup(team0) { + providesPower = "1"; + new StaticShape(InventoryStation) { + position = "0 0 100"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationInventory"; + locked = "true"; + }; + new StaticShape(VehicleStation) { + position = "50 0 97"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationVehiclePad"; + locked = "true"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatland.ter"; + squareSize = "32"; + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + rotation = "0 0 0 0"; + GraphFile = "FlatlandBig.nav"; + position = "0 0 0 1"; + scale = "1 1 1"; + coverage = "0"; + conjoinBowlDev = "20"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "5000"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "4500"; + fogColor = "0.600000 0.600000 0.600000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_blue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/FlatlandBigNight.mis b/missions/FlatlandBigNight.mis new file mode 100644 index 0000000..a6807cc --- /dev/null +++ b/missions/FlatlandBigNight.mis @@ -0,0 +1,142 @@ +// DisplayName = Flatland Big Night +// MissionTypes = Construction + +//--- Mission Quote Begin --- +// Flat. Land. Big! Dark! +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + musicTrack = "desert"; + cdTrack = "6"; + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + locked = "true"; + }; + new SimGroup(Teams) { + new SimGroup(Team1) { + new SimGroup(spawnspheres) { + new SpawnSphere() { + position = "0 0 100"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + locked = "1"; + }; + }; + }; + new SimGroup(team0) { + providesPower = "1"; + new StaticShape(InventoryStation) { + position = "0 0 100"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationInventory"; + locked = "true"; + }; + new StaticShape(VehicleStation) { + position = "50 0 97"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationVehiclePad"; + locked = "true"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.000000 0.000000 0.000000 1.000000"; + ambient = "0.300000 0.150000 0.000000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatland.ter"; + squareSize = "32"; + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + rotation = "0 0 0 0"; + GraphFile = "FlatlandBigNight.nav"; + position = "0 0 0 1"; + scale = "1 1 1"; + coverage = "0"; + conjoinBowlDev = "20"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "5000"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.600000 0.160000 0.000000 0.000000"; + fogDistance = "4500"; + fogColor = "0.1250000 0.0500000 0.0250000 1.000000"; + fogVolume1 = "1250 99 101"; + fogVolume2 = "1875 101 110"; + fogVolume3 = "2500 110 150"; + materialList = "sky_lush_morestars.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/FlatlandCanyon.mis b/missions/FlatlandCanyon.mis new file mode 100644 index 0000000..10923db --- /dev/null +++ b/missions/FlatlandCanyon.mis @@ -0,0 +1,331 @@ +// DisplayName = Flatland Canyon +// MissionTypes = Construction Infection +//--- MISSION QUOTE BEGIN --- +// Life is but a dream +// - Spike +// +//Map by Riavan +//Dedicated to Xeno +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Serverside! No download required! +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "2"; + CTF_timeLimit = "25"; + CTF_scoreLimit = "6"; + musicTrack = "lush"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-1512 -1952 992 3872"; + flightCeiling = "500"; + flightCeilingRange = "40"; + }; + new Sun() { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.600000 0.600000 0.600000 1.000000"; + ambient = "0.600000 0.600000 0.650000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet2"; + terrainFile = "PhasmaDust.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + coverage = "0"; + position = "0 0 0 1"; + YDimOverSize = "0"; + XDimOverSize = "0"; + locked = "true"; + rotation = "0 0 0 0"; + GraphFile = "Lakefront.nav"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new Sky(Sky) { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.1"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.01"; + cloudSpeed2 = "0.02"; + cloudSpeed3 = "0.03"; + visibleDistance = "1000"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.490000 0.490000 0.490000 0.000000"; + fogDistance = "800"; + fogColor = "0.900000 0.900000 0.920000 1.000000"; + fogVolume1 = "0 0 0"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "SOM_TR2_WinterBlue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -198748244414614883000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -222768174765569861000000000000000000000.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3.27833e-28 3148.48"; + high_fogVolume2 = "-1 2.24208e-44 2.51673e-42"; + high_fogVolume3 = "-1 7.74718e+31 1.49413e-07"; + + cloudSpeed0 = "0.000000 0.000000"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-1023.13 -66.5454 22.7821"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + new SimGroup(base) { + + powerCount = "1"; + + new StaticShape() { + position = "-1014.43 -65.8246 7.98396"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "1"; + station = "4812"; + Target = "33"; + }; + new StaticShape() { + position = "-1025.05 -50.3477 11.2342"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + inUse = "Down"; + Trigger = "4789"; + team = "1"; + notReady = "1"; + Target = "34"; + }; + new StaticShape() { + position = "-1005.25 -51.5687 -0.142809"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "35"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(Cam1) { + position = "-857.287 -171.935 71.6891"; + rotation = "0.375856 0.206574 -0.90336 62.6331"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; + new SimGroup(Ambiance) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-336.483 345.97 227.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/frog1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "45"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-464.05 -493.691 224.89"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + }; + new SimGroup(RandomOrganics) { + + powerCount = "0"; + + new SimGroup(Addition1BELgTree18) { + + powerCount = "0"; + }; + new SimGroup(Addition1BEPlant1) { + + powerCount = "0"; + }; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; +}; +//--- OBJECT WRITE END --- + +// Makes sure that the original values are maintained (for modified servers) + +$DefaultVehicleRespawnTime = $VehicleRespawnTime; +$DefaultVehicleMax[ScoutVehicle] = $VehicleMax[ScoutVehicle]; +$DefaultVehicleMax[AssaultVehicle] = $VehicleMax[AssaultVehicle]; +$DefaultVehicleMax[MobileBaseVehicle] = $VehicleMax[MobileBaseVehicle]; +$DefaultVehicleMax[ScoutFlyer] = $VehicleMax[ScoutFlyer]; +$DefaultVehicleMax[BomberFlyer] = $VehicleMax[BomberFlyer]; +$DefaultVehicleMax[HAPCFlyer] = $VehicleMax[HAPCFlyer]; + +// Set the values the way you want them! +$VehicleRespawnTime = 15000; // 15 seconds to respawn placed vehicles +$Vehiclemax[ScoutVehicle] = 8; // Wildcat Grav Cycle +$VehicleMax[AssaultVehicle] = 0; // Beowulf Assault Tank +$VehicleMax[MobileBaseVehicle] = 0; // MPB Mobile Base +$VehicleMax[ScoutFlyer] = 8; // Shrike Turbograv +$VehicleMax[BomberFlyer] = 2; // Thundersword Bomber +$VehicleMax[HAPCFlyer] = 2; // Havoc Heavy Transport + +package ShatteredFaith { + +function DefaultGame::gameOver(%game) +{ + // Put the values back the way they were + $VehicleRespawnTime = $DefaultVehicleRespawnTime; + $VehicleMax[ScoutVehicle] = $DefaultVehicleMax[ScoutVehicle]; + $VehicleMax[AssaultVehicle] = $DefaultVehicleMax[AssaultVehicle]; + $VehicleMax[MobileBaseVehicle] = $DefaultVehicleMax[MobileBaseVehicle]; + $VehicleMax[ScoutFlyer] = $DefaultVehicleMax[ScoutFlyer]; + $VehicleMax[BomberFlyer] = $DefaultVehicleMax[BomberFlyer]; + $VehicleMax[HAPCFlyer] = $DefaultVehicleMax[HAPCFlyer]; + Parent::gameOver(%game); + deactivatepackage(ShatteredFaith); +} + +}; + +activatepackage(ShatteredFaith); diff --git a/missions/FlatlandCanyonInfected.mis b/missions/FlatlandCanyonInfected.mis new file mode 100644 index 0000000..707e7c6 --- /dev/null +++ b/missions/FlatlandCanyonInfected.mis @@ -0,0 +1,331 @@ +// DisplayName = Flatland Canyon Infected +// MissionTypes = Infection +//--- MISSION QUOTE BEGIN --- +// Life is but a nightmare... +// - Nukem +// +//Map remade by: Blnukem. +//Map by Riavan. +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Serverside! No download required! +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "2"; + CTF_timeLimit = "25"; + CTF_scoreLimit = "6"; + musicTrack = "lush"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-1512 -1952 992 3872"; + flightCeiling = "5000"; + flightCeilingRange = "40"; + }; + new Sun() { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.500000 0.500000 0.500000 1.000000"; + ambient = "0.500000 0.500000 0.550000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet2"; + terrainFile = "PhasmaDust.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + coverage = "0"; + position = "0 0 0 1"; + YDimOverSize = "0"; + XDimOverSize = "0"; + locked = "true"; + rotation = "0 0 0 0"; + GraphFile = "Lakefront.nav"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new Sky(Sky) { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.1"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "1000"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.500000 0.500000 0.500000 0"; + fogDistance = "200"; + fogColor = "0.400000 0.400000 0.400000 1.000000"; + fogVolume1 = "8000 0 1500"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -198748244414614883000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -222768174765569861000000000000000000000.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3.27833e-28 3148.48"; + high_fogVolume2 = "-1 2.24208e-44 2.51673e-42"; + high_fogVolume3 = "-1 7.74718e+31 1.49413e-07"; + + cloudSpeed0 = "0.000000 0.000000"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-1023.13 -66.5454 22.7821"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + new SimGroup(base) { + + powerCount = "1"; + + new StaticShape() { + position = "-1014.43 -65.8246 7.98396"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "1"; + station = "4812"; + Target = "33"; + }; + new StaticShape() { + position = "-1025.05 -50.3477 11.2342"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + inUse = "Down"; + Trigger = "4789"; + team = "1"; + notReady = "1"; + Target = "34"; + }; + new StaticShape() { + position = "-1005.25 -51.5687 -0.142809"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "35"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(Cam1) { + position = "-857.287 -171.935 71.6891"; + rotation = "0.375856 0.206574 -0.90336 62.6331"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; + new SimGroup(Ambiance) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-336.483 345.97 227.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/frog1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "45"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-464.05 -493.691 224.89"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + }; + new SimGroup(RandomOrganics) { + + powerCount = "0"; + + new SimGroup(Addition1BELgTree18) { + + powerCount = "0"; + }; + new SimGroup(Addition1BEPlant1) { + + powerCount = "0"; + }; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; +}; +//--- OBJECT WRITE END --- + +// Makes sure that the original values are maintained (for modified servers) + +$DefaultVehicleRespawnTime = $VehicleRespawnTime; +$DefaultVehicleMax[ScoutVehicle] = $VehicleMax[ScoutVehicle]; +$DefaultVehicleMax[AssaultVehicle] = $VehicleMax[AssaultVehicle]; +$DefaultVehicleMax[MobileBaseVehicle] = $VehicleMax[MobileBaseVehicle]; +$DefaultVehicleMax[ScoutFlyer] = $VehicleMax[ScoutFlyer]; +$DefaultVehicleMax[BomberFlyer] = $VehicleMax[BomberFlyer]; +$DefaultVehicleMax[HAPCFlyer] = $VehicleMax[HAPCFlyer]; + +// Set the values the way you want them! +$VehicleRespawnTime = 15000; // 15 seconds to respawn placed vehicles +$Vehiclemax[ScoutVehicle] = 8; // Wildcat Grav Cycle +$VehicleMax[AssaultVehicle] = 0; // Beowulf Assault Tank +$VehicleMax[MobileBaseVehicle] = 0; // MPB Mobile Base +$VehicleMax[ScoutFlyer] = 8; // Shrike Turbograv +$VehicleMax[BomberFlyer] = 2; // Thundersword Bomber +$VehicleMax[HAPCFlyer] = 2; // Havoc Heavy Transport + +package ShatteredFaith { + +function DefaultGame::gameOver(%game) +{ + // Put the values back the way they were + $VehicleRespawnTime = $DefaultVehicleRespawnTime; + $VehicleMax[ScoutVehicle] = $DefaultVehicleMax[ScoutVehicle]; + $VehicleMax[AssaultVehicle] = $DefaultVehicleMax[AssaultVehicle]; + $VehicleMax[MobileBaseVehicle] = $DefaultVehicleMax[MobileBaseVehicle]; + $VehicleMax[ScoutFlyer] = $DefaultVehicleMax[ScoutFlyer]; + $VehicleMax[BomberFlyer] = $DefaultVehicleMax[BomberFlyer]; + $VehicleMax[HAPCFlyer] = $DefaultVehicleMax[HAPCFlyer]; + Parent::gameOver(%game); + deactivatepackage(ShatteredFaith); +} + +}; + +activatepackage(ShatteredFaith); diff --git a/missions/FlatlandDesert.mis b/missions/FlatlandDesert.mis new file mode 100644 index 0000000..3077329 --- /dev/null +++ b/missions/FlatlandDesert.mis @@ -0,0 +1,330 @@ +// DisplayName = Flatland Desert +// MissionTypes = Construction Infection + +//--- MISSION QUOTE BEGIN --- +//How do you define reality? +// +//Dedicated to Xenoc +//Map by Riavan +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Serverside! No download required! +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "2"; + CTF_scoreLimit = "6"; + powerCount = "0"; + musicTrack = "lush"; + CTF_timeLimit = "25"; + + new MissionArea(MissionArea) { + area = "-1656 328 3472 1376"; + flightCeiling = "900"; + flightCeilingRange = "40"; + }; + new Sun() { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.600000 0.600000 0.600000 1.000000"; + ambient = "0.600000 0.600000 0.650000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lavadet2"; + terrainFile = "SolsDescent.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + coverage = "0"; + YDimOverSize = "0"; + position = "0 0 0 1"; + XDimOverSize = "0"; + locked = "true"; + rotation = "0 0 0 0"; + GraphFile = "Lakefront.nav"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new Sky(Sky) { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.1"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0"; + cloudSpeed2 = "0"; + cloudSpeed3 = "0.01"; + visibleDistance = "1500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.490000 0.490000 0.490000 0.000000"; + fogDistance = "1200"; + fogColor = "0.900000 0.900000 0.320000 1.000000"; + fogVolume1 = "0 0 0"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "TR1_Nef.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -198748244414614883000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -222768174765569861000000000000000000000.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3.27833e-28 3148.48"; + high_fogVolume2 = "-1 2.24208e-44 2.51673e-42"; + high_fogVolume3 = "-1 7.74718e+31 1.49413e-07"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "232.66 604.854 75.2956"; + rotation = "0 0 1 179.336"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "40"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + new SimGroup(base) { + + powerCount = "1"; + + new StaticShape() { + position = "229.741 605.174 48.485"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + station = "7692"; + team = "1"; + Ready = "1"; + Target = "33"; + inUse = "Down"; + }; + new StaticShape() { + position = "241.362 589.723 51.6776"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "7668"; + team = "1"; + notReady = "1"; + Target = "34"; + inUse = "Down"; + }; + new StaticShape() { + position = "221.696 611.224 43.1862"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "35"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(Cam) { + position = "308.64 571.186 104.096"; + rotation = "0.267985 0.210062 -0.940244 79.634"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; + new SimGroup(Ambiance) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-336.483 345.97 227.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/frog1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "45"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-464.05 -493.691 224.89"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + }; + new SimGroup(RandomOrganics) { + + powerCount = "0"; + + new SimGroup(Addition1BELgTree18) { + + powerCount = "0"; + }; + new SimGroup(Addition1BEPlant1) { + + powerCount = "0"; + }; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; +}; +//--- OBJECT WRITE END --- + +// Makes sure that the original values are maintained (for modified servers) + +$DefaultVehicleRespawnTime = $VehicleRespawnTime; +$DefaultVehicleMax[ScoutVehicle] = $VehicleMax[ScoutVehicle]; +$DefaultVehicleMax[AssaultVehicle] = $VehicleMax[AssaultVehicle]; +$DefaultVehicleMax[MobileBaseVehicle] = $VehicleMax[MobileBaseVehicle]; +$DefaultVehicleMax[ScoutFlyer] = $VehicleMax[ScoutFlyer]; +$DefaultVehicleMax[BomberFlyer] = $VehicleMax[BomberFlyer]; +$DefaultVehicleMax[HAPCFlyer] = $VehicleMax[HAPCFlyer]; + +// Set the values the way you want them! +$VehicleRespawnTime = 15000; // 15 seconds to respawn placed vehicles +$Vehiclemax[ScoutVehicle] = 8; // Wildcat Grav Cycle +$VehicleMax[AssaultVehicle] = 0; // Beowulf Assault Tank +$VehicleMax[MobileBaseVehicle] = 0; // MPB Mobile Base +$VehicleMax[ScoutFlyer] = 8; // Shrike Turbograv +$VehicleMax[BomberFlyer] = 2; // Thundersword Bomber +$VehicleMax[HAPCFlyer] = 2; // Havoc Heavy Transport + +package ShatteredFaith { + +function DefaultGame::gameOver(%game) +{ + // Put the values back the way they were + $VehicleRespawnTime = $DefaultVehicleRespawnTime; + $VehicleMax[ScoutVehicle] = $DefaultVehicleMax[ScoutVehicle]; + $VehicleMax[AssaultVehicle] = $DefaultVehicleMax[AssaultVehicle]; + $VehicleMax[MobileBaseVehicle] = $DefaultVehicleMax[MobileBaseVehicle]; + $VehicleMax[ScoutFlyer] = $DefaultVehicleMax[ScoutFlyer]; + $VehicleMax[BomberFlyer] = $DefaultVehicleMax[BomberFlyer]; + $VehicleMax[HAPCFlyer] = $DefaultVehicleMax[HAPCFlyer]; + Parent::gameOver(%game); + deactivatepackage(ShatteredFaith); +} + +}; + +activatepackage(ShatteredFaith); diff --git a/missions/FlatlandDesertInfected.mis b/missions/FlatlandDesertInfected.mis new file mode 100644 index 0000000..c4097f7 --- /dev/null +++ b/missions/FlatlandDesertInfected.mis @@ -0,0 +1,331 @@ +// DisplayName = Flatland Desert Infected +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +// How do you define terror? +// - Nukem +// +//Map remade by: Blnukem. +//Map by Riavan. +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Serverside! No download required! +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "2"; + CTF_scoreLimit = "6"; + powerCount = "0"; + musicTrack = "lush"; + CTF_timeLimit = "25"; + + new MissionArea(MissionArea) { + area = "-1656 328 3472 1376"; + flightCeiling = "5000"; + flightCeilingRange = "40"; + }; + new Sun() { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0 0.0995037 0.995037"; + color = "0.500000 0.500000 0.500000 1.000000"; + ambient = "0.500000 0.500000 0.550000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lavadet2"; + terrainFile = "SolsDescent.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + coverage = "0"; + YDimOverSize = "0"; + position = "0 0 0 1"; + XDimOverSize = "0"; + locked = "true"; + rotation = "0 0 0 0"; + GraphFile = "Lakefront.nav"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new Sky(Sky) { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.1"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "1500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.490000 0.490000 0.490000 0.000000"; + fogDistance = "200"; + fogColor = "0.300000 0.280000 0.250000 1.000000"; + fogVolume1 = "5000 0 1500"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_brown.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -198748244414614883000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -222768174765569861000000000000000000000.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3.27833e-28 3148.48"; + high_fogVolume2 = "-1 2.24208e-44 2.51673e-42"; + high_fogVolume3 = "-1 7.74718e+31 1.49413e-07"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "232.66 604.854 75.2956"; + rotation = "0 0 1 179.336"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "40"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + new SimGroup(base) { + + powerCount = "1"; + + new StaticShape() { + position = "229.741 605.174 48.485"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + station = "7692"; + team = "1"; + Ready = "1"; + Target = "33"; + inUse = "Down"; + }; + new StaticShape() { + position = "241.362 589.723 51.6776"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "7668"; + team = "1"; + notReady = "1"; + Target = "34"; + inUse = "Down"; + }; + new StaticShape() { + position = "221.696 611.224 43.1862"; + rotation = "0 0 1 177.617"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "35"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(Cam) { + position = "308.64 571.186 104.096"; + rotation = "0.267985 0.210062 -0.940244 79.634"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; + new SimGroup(Ambiance) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-336.483 345.97 227.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/frog1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "45"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-464.05 -493.691 224.89"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + }; + new SimGroup(RandomOrganics) { + + powerCount = "0"; + + new SimGroup(Addition1BELgTree18) { + + powerCount = "0"; + }; + new SimGroup(Addition1BEPlant1) { + + powerCount = "0"; + }; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new SimGroup() { + + powerCount = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new SimGroup() { + + powerCount = "0"; + }; +}; +//--- OBJECT WRITE END --- + +// Makes sure that the original values are maintained (for modified servers) + +$DefaultVehicleRespawnTime = $VehicleRespawnTime; +$DefaultVehicleMax[ScoutVehicle] = $VehicleMax[ScoutVehicle]; +$DefaultVehicleMax[AssaultVehicle] = $VehicleMax[AssaultVehicle]; +$DefaultVehicleMax[MobileBaseVehicle] = $VehicleMax[MobileBaseVehicle]; +$DefaultVehicleMax[ScoutFlyer] = $VehicleMax[ScoutFlyer]; +$DefaultVehicleMax[BomberFlyer] = $VehicleMax[BomberFlyer]; +$DefaultVehicleMax[HAPCFlyer] = $VehicleMax[HAPCFlyer]; + +// Set the values the way you want them! +$VehicleRespawnTime = 15000; // 15 seconds to respawn placed vehicles +$Vehiclemax[ScoutVehicle] = 8; // Wildcat Grav Cycle +$VehicleMax[AssaultVehicle] = 0; // Beowulf Assault Tank +$VehicleMax[MobileBaseVehicle] = 0; // MPB Mobile Base +$VehicleMax[ScoutFlyer] = 8; // Shrike Turbograv +$VehicleMax[BomberFlyer] = 2; // Thundersword Bomber +$VehicleMax[HAPCFlyer] = 2; // Havoc Heavy Transport + +package ShatteredFaith { + +function DefaultGame::gameOver(%game) +{ + // Put the values back the way they were + $VehicleRespawnTime = $DefaultVehicleRespawnTime; + $VehicleMax[ScoutVehicle] = $DefaultVehicleMax[ScoutVehicle]; + $VehicleMax[AssaultVehicle] = $DefaultVehicleMax[AssaultVehicle]; + $VehicleMax[MobileBaseVehicle] = $DefaultVehicleMax[MobileBaseVehicle]; + $VehicleMax[ScoutFlyer] = $DefaultVehicleMax[ScoutFlyer]; + $VehicleMax[BomberFlyer] = $DefaultVehicleMax[BomberFlyer]; + $VehicleMax[HAPCFlyer] = $DefaultVehicleMax[HAPCFlyer]; + Parent::gameOver(%game); + deactivatepackage(ShatteredFaith); +} + +}; + +activatepackage(ShatteredFaith); diff --git a/missions/FlatlandNight.mis b/missions/FlatlandNight.mis new file mode 100644 index 0000000..e23e8bf --- /dev/null +++ b/missions/FlatlandNight.mis @@ -0,0 +1,157 @@ +// DisplayName = Flatland Night +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// Flat. Land. Dark! +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + musicTrack = "desert"; + cdTrack = "6"; + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + locked = "true"; + }; + new SimGroup(Teams) { + new SimGroup(Team1) { + new SimGroup(spawnspheres) { + new SpawnSphere() { + position = "0 0 100"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + locked = "1"; + }; + }; + }; + new SimGroup(team0) { + providesPower = "1"; + new StaticShape(InventoryStation) { + position = "0 0 100"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationInventory"; + locked = "true"; + }; + new StaticShape(VehicleStation) { + position = "50 0 97"; + rotation = "0 0 1 -90"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationVehiclePad"; + locked = "true"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.000000 0.000000 0.000000 1.000000"; + ambient = "0.300000 0.150000 0.000000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatland.ter"; + squareSize = "8"; + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + rotation = "0 0 0 0"; + GraphFile = "FlatlandNight.nav"; + position = "0 0 0 1"; + scale = "1 1 1"; + coverage = "0"; + conjoinBowlDev = "20"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.600000 0.160000 0.000000 0.000000"; + fogDistance = "750"; + fogColor = "0.250000 0.100000 0.050000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "300 101 110"; + fogVolume3 = "400 110 150"; + materialList = "sky_lush_morestars.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new WaterBlock(Water) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "2048 2048 100.1"; + liquidType = "OceanWater"; + density = "1"; + viscosity = "1.5"; + waveMagnitude = "0"; + surfaceTexture = "LiquidTiles/BlueWater"; + surfaceOpacity = "0"; + envMapTexture = "lush/skies/lushcloud1"; + envMapIntensity = "0.2"; + removeWetEdges = "0"; + AudioEnvironment = "Underwater"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Flatlands Tunnels.mis b/missions/Flatlands Tunnels.mis new file mode 100644 index 0000000..ba52473 --- /dev/null +++ b/missions/Flatlands Tunnels.mis @@ -0,0 +1,255 @@ +//--- OBJECT WRITE BEGIN --- +// DisplayName = Flatlands Tunnels +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// Tunnels have supprises find the tiny tunnel and find the best place to build... +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Third map made same as always started with a flatland map. +//Map made by Cheesed off +//top of tunnels goes on for a long time have fun. +//--- Mission String End --- + +new SimGroup(MissionGroup) { + + cdTrack = "6"; + powerCount = "1"; + musicTrack = "desert"; + + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "1"; + + new SimGroup(Team1) { + + powerCount = "1"; + + new SimGroup(spawnspheres) { + + powerCount = "1"; + }; + }; + new SimGroup(team0) { + + providesPower = "1"; + powerCount = "2"; + + new StaticShape(InventoryStation) { + position = "0 0 -8.25524e-06"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + inUse = "Down"; + Target = "33"; + notReady = "1"; + team = "0"; + Trigger = "7954"; + }; + new StaticShape(VehicleStation) { + position = "50 0 -3.00001"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + inUse = "Down"; + Target = "34"; + Ready = "1"; + team = "0"; + station = "7977"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Flatlands Tunnels.ter"; + squareSize = "32"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + GraphFile = "FlatlandBig.nav"; + coverage = "0"; + position = "0 0 0 1"; + conjoinBowlDev = "20"; + locked = "true"; + rotation = "0 0 0 0"; + scale = "1 1 1"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "1"; + + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "5000"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "4500"; + fogColor = "0.600000 0.600000 0.600000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_blue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new ScriptObject(DerkVicKillList) { + }; + new TSStatic() { + position = "6.37674 -45.1663 97.6"; + rotation = "1 0 0 0"; + scale = "10000 10000 1"; + shapeName = "smiscf.dts"; + }; + new TSStatic() { + position = "-0.350904 0.0987333 99.06"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "dmiscf.dts"; + }; + new TSStatic() { + position = "-0.480051 -1.34348 97.85"; + rotation = "1 0 0 0"; + scale = "1.1 0.01 700"; + shapeName = "smiscf.dts"; + }; + new TSStatic() { + position = "1.5804 0.0920193 97.85"; + rotation = "1 0 0 0"; + scale = "0.01 1 700"; + shapeName = "smiscf.dts"; + }; + new TSStatic() { + position = "-2.46928 0.0696096 97.85"; + rotation = "1 0 0 0"; + scale = "0.01 1 700"; + shapeName = "smiscf.dts"; + }; + new TSStatic() { + position = "-0.875066 0.596214 446.85"; + rotation = "1 0 0 0"; + scale = "2 2 2"; + shapeName = "smiscf.dts"; + }; + new TSStatic() { + position = "-0.306176 1.54661 97.85"; + rotation = "1 0 0 0"; + scale = "1.1 0.01 700"; + shapeName = "smiscf.dts"; + }; + new StaticShape() { + position = "-0.362985 0.0886609 99.3596"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Teleporter"; + lockCount = "0"; + homingCount = "0"; + + logoscale = " 0"; + Target = "35"; + noflag = " 1"; + Trigger = "7971"; + oneway = "0"; + message = "Going Down"; + }; + new StaticShape() { + position = "9.31721 18.3036 -0.200367"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Teleporter"; + lockCount = "0"; + homingCount = "0"; + + logoscale = " 0"; + providesPower = "1"; + Target = "36"; + noflag = " 1"; + Trigger = "7973"; + oneway = "1"; + message = " "; + }; + new StaticShape() { + position = "0.930094 -5.65265 97.657"; + rotation = "1 0 0 0"; + scale = "0 0 0"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "37"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Gaunt.mis b/missions/Gaunt.mis new file mode 100644 index 0000000..cd77890 --- /dev/null +++ b/missions/Gaunt.mis @@ -0,0 +1,642 @@ +// DisplayName = Gaunt +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +//"The Darkness is spreading... We are in times of war, not only for revenge, but also for our species." +//--Gaunt Installation Log: 1421B, Log by: >ERROR RETREIVING INFORMATION< +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Lightning Strike Hazard. +//Map made by: Blnukem. +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + musicTrack = "ACCM_The_Fallen"; + cdTrack = "6"; + CTF_scoreLimit = "10"; + CTF_timeLimit = "25"; + + new MissionArea(MissionArea) { + area = "-832 -768 1264 912"; + flightCeiling = "2000"; + flightCeilingRange = "50"; + + locked = "true"; + }; + new Sun() { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.600000 0.600000 0.600000 1.000000"; + ambient = "0.200000 0.200000 0.200000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/baddet1"; + terrainFile = "Minotaur.ter"; + squareSize = "8"; + emptySquares = "150887 151143 154232 154488"; + + hazeDistance = "250"; + position = "-1024 -1024 0"; + visibleDistance = "1200"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + coverage = "0"; + XDimOverSize = "0"; + YDimOverSize = "0"; + position = "0 0 0 1"; + GraphFile = "Minotaur.nav"; + conjoinBowlDev = "20"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new Sky(Sky) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "750"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.190000 0.235000 0.210000 0.000000"; + fogDistance = "600"; + fogColor = "0.200000 0.200000 0.200000 1.000000"; + fogVolume1 = "0 0 0"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_badlands_cloudy.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 nan"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 148120713330651839000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 nan"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 1.1038e-13 1.33983e-26"; + high_fogVolume2 = "-1 2.23578e+36 0.216979"; + high_fogVolume3 = "-1 2.61668e+24 0.000574978"; + + cloudSpeed0 = "0.002000 0.003000"; + locked = "true"; + }; + new SimGroup(Nature) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-201.38 -415.042 156.501"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/moaningwind1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "0"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new InteriorInstance() { + position = "-537.858 -546.888 208.008"; + rotation = "0 0 1 81.36"; + scale = "1 1 1"; + interiorFile = "xspir3.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-328.837 -472.132 129.661"; + rotation = "-0.130785 -0.201361 -0.970747 64.3635"; + scale = "1 1 1"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-427.935 -395.666 180.627"; + rotation = "0 0 -1 34.9504"; + scale = "1.26849 1 0.573936"; + interiorFile = "xrocka.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-183.867 -399.23 115.978"; + rotation = "-0.899693 0.208551 0.383484 91.2699"; + scale = "1 1 1"; + interiorFile = "xrock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-197.231 -406.673 110.968"; + rotation = "-0.365615 0.387076 0.846462 71.8315"; + scale = "1 1 1"; + interiorFile = "xrock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-193.066 -401.686 109.888"; + rotation = "-0.365615 0.387076 0.846462 71.8315"; + scale = "1 1 1"; + interiorFile = "xrock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-199.551 -406.353 112.839"; + rotation = "-0.365615 0.387076 0.846462 71.8315"; + scale = "1 1 1"; + interiorFile = "xrock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-191.054 -410.173 119.413"; + rotation = "-0.407141 0.675186 0.615111 225.072"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-198.312 -394.959 107.861"; + rotation = "0.278872 -0.320765 0.905174 100.284"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-199.708 -400.383 106.37"; + rotation = "0.796908 0.404996 0.448237 95.1194"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-192.382 -399.749 115.16"; + rotation = "0.929053 -0.345396 0.132525 161.676"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-193.404 -396.097 104.513"; + rotation = "0.78662 -0.376256 0.489551 179.057"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-202.241 -398.157 116.298"; + rotation = "0.936991 0.324415 0.129626 180.332"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-201.848 -392.348 117.047"; + rotation = "0.838104 -0.539195 0.0827603 168.504"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-191.054 -391.021 115.971"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xrock6.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-199.99 -406.986 114.981"; + rotation = "-0.365615 0.387076 0.846462 71.8315"; + scale = "1 1 1"; + interiorFile = "xrock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-196.221 -395.884 115.391"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xrock8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "390.719 -428.066 131.933"; + rotation = "0 0 1 50.9932"; + scale = "1.45595 1 0.716772"; + interiorFile = "xrocka.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "251.737 -386.372 146.423"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir2.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-277.211 -424.89 156.626"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "xmiscb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-517.885 -395.653 201.615"; + rotation = "0 0 1 79.2507"; + scale = "1 1 1"; + interiorFile = "xmisca.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-48.0454 -130.674 191.833"; + rotation = "0 0 -1 109.045"; + scale = "1 1 1"; + interiorFile = "xmiscb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-57.7832 -543.349 193.157"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xmiscb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new Lightning() { + position = "0 -9.91141 0"; + rotation = "1 0 0 0"; + scale = "1831.13 1169.28 764.521"; + dataBlock = "DefaultStorm"; + lockCount = "0"; + homingCount = "0"; + strikesPerMinute = "5"; + strikeWidth = "2.5"; + chanceToHitTarget = "0.5"; + strikeRadius = "20"; + boltStartRadius = "20"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.100000 0.100000 1.000000 1.000000"; + useFog = "1"; + + locked = "True"; + }; + new InteriorInstance() { + position = "256.905 -374.424 135.053"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-726.814 -544.838 141.557"; + rotation = "0.244986 -0.458902 0.854044 46.1236"; + scale = "1 1 1"; + interiorFile = "xtowr1.dif"; + showTerrainInside = "1"; + AudioProfile = "Universal_Base_Pulse_1"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-71.6027 -473.051 173.104"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir2.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-61.3132 -458.803 167.701"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir3.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-192.344 -519.179 113.211"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-178.161 -125.071 165.729"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir1.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-403.625 -218.783 133.4"; + rotation = "0 0 1 50.9932"; + scale = "1 1 1"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-429.169 -604.505 138.543"; + rotation = "-0.128535 -0.12993 0.983157 90.3547"; + scale = "1 1 1"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-614.659 -579.811 165.54"; + rotation = "0.0973311 0.690674 -0.716586 22.2513"; + scale = "1.50238 1.25618 1.19883"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-522.003 -543.51 214.731"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir1.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "511.673 -510.719 218.683"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir3.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "500.196 -533.597 222.365"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir1.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "486.704 -599.358 182.427"; + rotation = "0 0 1 91.6732"; + scale = "1 1 1"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "378.042 -774.725 155.295"; + rotation = "-0.160083 0.219114 -0.962477 42.6871"; + scale = "1 1 1"; + interiorFile = "xrockb.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "263.516 -760.787 132.64"; + rotation = "0 0 -1 17.7617"; + scale = "1 1 1"; + interiorFile = "xspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "558.886 -228.911 227.378"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "xspir1.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-56.0115 -296.166 131.624"; + rotation = "0 0 -1 37.2423"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "50"; + outdoorWeight = "50"; + + team = "1"; + locked = "True"; + }; + }; + new SimGroup(base0) { + + powerCount = "2"; + providesPower = "1"; + + new InteriorInstance() { + position = "-56.0115 -296.166 131.624"; + rotation = "0 0 1 22.9183"; + scale = "1 1 1"; + interiorFile = "xtowr7.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_2"; + + team = "1"; + locked = "True"; + }; + new WayPoint() { + position = "-56.0115 -296.166 131.624"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Gaunt Installation"; + team = "1"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-155.704 -256.035 146.177"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + interiorFile = "xvpad.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-55.8391 -295.76 194.624"; + rotation = "0 0 1 203.01"; + scale = "1 1 1"; + dataBlock = "SolarPanel"; + lockCount = "0"; + homingCount = "0"; + + Target = "33"; + lastState = "1"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-155.704 -256.035 145.577"; + rotation = "1 0 0 0.500458"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + station = "5058"; + Target = "34"; + Ready = "1"; + lastState = "1"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-155.704 -219.235 148.177"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "35"; + lastState = "1"; + Trigger = "5042"; + team = "1"; + locked = "True"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-185.089 -393.477 170.446"; + rotation = "0 0 1 38.9611"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "True"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/LostWorld.mis b/missions/LostWorld.mis new file mode 100644 index 0000000..0e4b929 --- /dev/null +++ b/missions/LostWorld.mis @@ -0,0 +1,915 @@ +// DisplayName = Lost World +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +//Explorers live a dangerous life... +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//ServerSide Infection Mission. +//A Map by: Blnukem +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + musicTrack = "ACCM_Zombie_Horde"; + cdTrack = "6"; + + new MissionArea(MissionArea) { + area = "-832 -1920 1568 2512"; + flightCeiling = "5000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sun(Sun) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.622506 0.622506 -0.474313"; + color = "0.800000 0.800000 0.800000 1.000000"; + ambient = "0.400000 0.400000 0.400000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet2"; + terrainFile = "Slapdash.ter"; + squareSize = "8"; + emptySquares = "94579 99875"; + + hazeDistance = "250"; + position = "-1024 -1024 0"; + visibleDistance = "1200"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + XDimOverSize = "0"; + GraphFile = "Slapdash.nav"; + YDimOverSize = "0"; + conjoinBowlDev = "20"; + position = "0 0 0 1"; + coverage = "0"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new Sky(Sky) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.390000 0.390000 0.390000 0.000000"; + fogDistance = "280"; + fogColor = "0.300000 0.300000 0.300000 1.000000"; + fogVolume1 = "100 0 125"; + fogVolume2 = "700 125 300"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "0 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 -0.040112"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.742938"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 1.52539e-29 1.5254e-29"; + high_fogVolume2 = "-1 7.35209e-31 7.35194e-31"; + high_fogVolume3 = "-1 0 4.5988e+24"; + + cloudSpeed0 = "0.000000 0.000000"; + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "314.501 -1266.9 130.288"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "40"; + sphereWeight = "10"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + new SpawnSphere() { + position = "413.763 -1464.68 131.58"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "70"; + sphereWeight = "90"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + }; + new SimGroup(MainBase) { + + new InteriorInstance() { + position = "428.363 -1462.68 131.98"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + interiorFile = "bbase4cm.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_1"; + + locked = "True"; + }; + new InteriorInstance() { + position = "325.438 -1400.04 188.418"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc9.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new StaticShape() { + position = "325.42 -1400.02 198.418"; + rotation = "0 0 1 90"; + scale = "1 1 1"; + nameTag = "Remote"; + dataBlock = "SensorLargePulse"; + lockCount = "0"; + homingCount = "0"; + + Target = "39"; + team = "1"; + locked = "True"; + }; + new WayPoint() { + position = "413.763 -1464.68 131.58"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Main Base"; + team = "1"; + + locked = "True"; + }; + new StaticShape() { + position = "385.792 -1464.22 132.5"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + nameTag = "Main"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "33"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "368.096 -1435.69 146"; + rotation = "0 0 -1 45"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "34"; + Trigger = "76260"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "443.354 -1438.82 146"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "35"; + Trigger = "77280"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "443.354 -1489.62 146"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "36"; + Trigger = "77538"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "444.554 -1435.82 140"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "37"; + Trigger = "77725"; + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "444.554 -1492.22 140"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "38"; + Trigger = "77859"; + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "221.06 -1223.28 137.721"; + rotation = "0 0 -1 25.7831"; + scale = "1 0.743799 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new StaticShape() { + position = "223.091 -1227.5 136.921"; + rotation = "0 0 1 154.308"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Target = "40"; + station = "80339"; + Ready = "1"; + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "314.501 -1266.9 130.288"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + interiorFile = "bbunk9.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new StaticShape() { + position = "314.501 -1279.5 132.288"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "42"; + Trigger = "82532"; + team = "1"; + locked = "True"; + }; + }; + new SimGroup(Crates) { + + new TSStatic() { + position = "370.931 -1435.61 132.4"; + rotation = "0 0 -1 1.71915"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.901 -1435.31 135.4"; + rotation = "0 0 1 4.01071"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "371.067 -1439.21 132.4"; + rotation = "0 0 1 6.30239"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.967 -1439.2 135.4"; + rotation = "0 0 -1 1.71915"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.931 -1435.61 138.4"; + rotation = "0 0 -1 5.72983"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "376.07 -1437.22 132.4"; + rotation = "0 0 -1 94.538"; + scale = "1 1 1"; + shapeName = "stackable3m.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "375.922 -1437.26 134.4"; + rotation = "0 0 -1 88.8085"; + scale = "1 1 1"; + shapeName = "stackable3m.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.167 -1441.97 132.4"; + rotation = "0 0 -1 5.72969"; + scale = "1 1 1"; + shapeName = "stackable3m.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "373.471 -1442.02 132.4"; + rotation = "0 0 -1 20.6266"; + scale = "1 1 1"; + shapeName = "stackable3s.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.154 -1438.88 138.4"; + rotation = "0 0 1 9.16737"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "370.343 -1441.99 134.4"; + rotation = "0 0 1 12.0322"; + scale = "1 1 1"; + shapeName = "stackable1m.dts"; + + locked = "True"; + }; + new Item() { + position = "366.679 -1434.89 132.488"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPack"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + Target = "-1"; + locked = "True"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "515.067 -1499.22 183.327"; + rotation = "0.264839 0.167891 -0.949565 67.4548"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + new Camera() { + position = "230.694 -1175.94 171.702"; + rotation = "-0.011837 -0.232827 0.972446 185.661"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + }; + new SimGroup(Enviornment) { + + powerCount = "0"; + + new SimGroup(Ambience) { + + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/moaningwind1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "0.8"; + isLooping = "1"; + is3D = "1"; + minDistance = "10000"; + maxDistance = "10000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/fog.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "500"; + maxDistance = "1200"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + }; + new AudioEmitter() { + position = "536.087 -1424.74 130.952"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "100"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "449.053 -1554.73 129.681"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "452.31 -1558.5 130.379"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "80"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "547.767 -1413.58 129.28"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo5.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "456.261 -1555.83 128.761"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo4.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "444.962 -1560.3 128.223"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo4.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "608.099 -1496.76 127.605"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "250"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "487.567 -1592.87 143.92"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "504.736 -1595.97 154.222"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo5.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "157.863 -1173.14 132.711"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "250"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + }; + new SimGroup(RuinedStructures) { + + new InteriorInstance() { + position = "-742.423 237.582 118.385"; + rotation = "-0.0968398 0.31685 -0.943519 67.9651"; + scale = "1 1 1"; + interiorFile = "btowr8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-112.92 20.4838 113.873"; + rotation = "-0.445515 -0.585616 0.677178 31.9195"; + scale = "1 1 1"; + interiorFile = "bbunkc.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + }; + new SimGroup(Nature) { + + new Lightning() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "2407.14 1860.51 1365.51"; + dataBlock = "DefaultStorm"; + lockCount = "0"; + homingCount = "0"; + strikesPerMinute = "1"; + strikeWidth = "2.5"; + chanceToHitTarget = "0.5"; + strikeRadius = "20"; + boltStartRadius = "20"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.100000 0.100000 1.000000 1.000000"; + useFog = "1"; + + locked = "True"; + }; + new FireballAtmosphere(FireballAtmosphere) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "fireball"; + lockCount = "0"; + homingCount = "0"; + dropRadius = "900"; + dropsPerMinute = "10"; + minDropAngle = "0"; + maxDropAngle = "10"; + startVelocity = "300"; + dropHeight = "1000"; + dropDir = "0.212 0.212 -0.953998"; + + locked = "True"; + }; + }; + new SimGroup(Organics) { + + new TSStatic() { + position = "542.88 -1418.54 130.964"; + rotation = "0 0 1 4.01051"; + scale = "0.429126 0.548931 0.378096"; + shapeName = "borg16.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "547.767 -1413.58 129.28"; + rotation = "0 0 -1 15.4698"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "456.261 -1555.83 128.761"; + rotation = "0 0 1 68.7549"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "444.962 -1560.3 128.223"; + rotation = "0 0 1 159.282"; + scale = "0.697187 0.793915 0.759344"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "608.099 -1496.76 127.605"; + rotation = "0 0 1 97.4028"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "449.053 -1554.73 129.681"; + rotation = "0.664014 0.746375 -0.0448363 6.89836"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "452.31 -1558.5 130.379"; + rotation = "0.76284 0.645222 -0.0419986 9.75433"; + scale = "0.809066 0.803038 0.699882"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "536.087 -1424.74 130.952"; + rotation = "-0.976217 0.215892 -0.0197619 12.924"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "504.736 -1595.97 154.222"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "487.567 -1592.87 143.92"; + rotation = "0 0 1 208.739"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "157.863 -1173.14 132.711"; + rotation = "0 0 -1 59.0147"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + }; + new SimGroup(Rocks) { + + new InteriorInstance() { + position = "-100.74 -115.315 124.964"; + rotation = "0 0 -1 37.2422"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-741.66 49.9078 127.769"; + rotation = "0 0 1 18.3347"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Martyrdom.mis b/missions/Martyrdom.mis new file mode 100644 index 0000000..699a259 --- /dev/null +++ b/missions/Martyrdom.mis @@ -0,0 +1,1099 @@ +// DisplayName = Martyrdom +// MissionTypes = Infection + +//--- Mission Quote Begin --- +//"And in those days shall men seek death, and shall not find it; +//and shall desire to die, and death shall flee from them." +//--Book of Revelation +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Low visibility due to stormy weather. +//Map by: Blnukem. +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "6"; + musicTrack = "ACCM_Sentinel"; + powerCount = "0"; + CTF_timeLimit = "25"; + CTF_scoreLimit = "10"; + + new MissionArea(MissionArea) { + area = "-2040 -2040 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-1004.8 -929.6 50.9292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "90"; + indoorWeight = "100"; + outdoorWeight = "100"; + + locked = "1"; + team = "1"; + }; + new SpawnSphere() { + position = "-238.356 -232.806 89.8069"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "70"; + sphereWeight = "30"; + indoorWeight = "100"; + outdoorWeight = "0"; + + locked = "1"; + team = "1"; + }; + }; + new SimGroup(MainBase) { + + powerCount = "0"; + + new InteriorInstance() { + position = "-259.498 -264.382 77.2069"; + rotation = "0 0 1 33.8045"; + scale = "1 1 1"; + interiorFile = "dbunk_nefcliffside.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_1"; + + locked = "1"; + team = "1"; + }; + new InteriorInstance() { + position = "-959.8 -925.6 69.5292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "dbase_nefRaindance.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_1"; + + locked = "1"; + team = "1"; + }; + new InteriorInstance() { + position = "-736.204 -1441.71 116.625"; + rotation = "0 0 1 60.7335"; + scale = "1 1 1"; + interiorFile = "dmisc_nefflagstand3.dif"; + showTerrainInside = "0"; + + locked = "1"; + team = "1"; + }; + new WayPoint() { + position = "-1004.8 -929.6 50.9292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Main Base"; + team = "1"; + + locked = "1"; + }; + new WayPoint() { + position = "-238.356 -232.806 89.8069"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Power Station"; + team = "1"; + + locked = "1"; + }; + new StaticShape() { + position = "-259.279 -199.322 79.2033"; + rotation = "0 0 1 33.8045"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "38"; + team = "1"; + }; + new StaticShape() { + position = "-199.451 -239.38 79.2033"; + rotation = "0 0 1 33.8045"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "39"; + team = "1"; + }; + new StaticShape() { + position = "-263.517 -224.329 80.2411"; + rotation = "0 0 -1 29.7938"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "40"; + Trigger = "14421"; + team = "1"; + }; + new StaticShape() { + position = "-251.888 -217.671 80.2411"; + rotation = "0 0 -1 29.7938"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "41"; + Trigger = "14726"; + team = "1"; + }; + new StaticShape() { + position = "-221.018 -252.999 80.2411"; + rotation = "0 0 1 97.4028"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "42"; + Trigger = "15110"; + team = "1"; + }; + new StaticShape() { + position = "-219.266 -239.512 80.2411"; + rotation = "0 0 1 97.4028"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "43"; + Trigger = "15310"; + team = "1"; + }; + new StaticShape() { + position = "-723.716 -1439.24 109.625"; + rotation = "0 0 1 196.616"; + scale = "1 1 1"; + nameTag = "Remote Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "44"; + Trigger = "15638"; + team = "1"; + }; + new ForceFieldBare() { + position = "-973.794 -937.159 66.5455"; + rotation = "1 0 0 0"; + scale = "0.984436 15.4285 6.05019"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "45"; + pzone = "16007"; + team = "1"; + }; + new TSStatic() { + position = "-980.757 -932.193 66.1958"; + rotation = "-1 0 0 90"; + scale = "1.58932 0.218004 6.97175"; + shapeName = "bmiscf.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-978.899 -926.694 62.9036"; + rotation = "-1 0 0 59.0603"; + scale = "0.660105 0.185553 13.1643"; + shapeName = "bmiscf.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-980.757 -923.393 62.9983"; + rotation = "-1 0 0 90"; + scale = "1.58932 0.230085 6.97175"; + shapeName = "bmiscf.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-982.498 -926.72 63.2165"; + rotation = "1 0 0 217.288"; + scale = "0.660105 0.134357 20.6096"; + shapeName = "bmiscf.dts"; + + locked = "1"; + }; + new StaticShape() { + position = "-1014.6 -929.6 56.5292"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "46"; + Trigger = "17073"; + team = "1"; + }; + new StaticShape() { + position = "-1004.8 -939.2 56.5292"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "47"; + Trigger = "17079"; + team = "1"; + }; + new StaticShape() { + position = "-1004.8 -919.8 56.5292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "48"; + Trigger = "17082"; + team = "1"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "1"; + providesPower = "1"; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.200000 0.200000 0.200000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet1"; + terrainFile = "Broadside_nef.ter"; + squareSize = "8"; + emptySquares = "330240 396032 396288 265472 89441 351838 352093 221277 155997"; + + locked = "true"; + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + coverage = "0"; + GraphFile = "Broadside_nef.nav"; + locked = "true"; + conjoinBowlDev = "20"; + position = "0 0 0 1"; + rotation = "0 0 0 0"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-855.72 -884.764 122"; + rotation = "0.132426 0.201774 -0.970438 115.013"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + team = "0"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "750"; + fogColor = "0.400000 0.400000 0.400000 1.000000"; + fogVolume1 = "60 0 40"; + fogVolume2 = "300 40 120"; + fogVolume3 = "600 120 180"; + materialList = "Lush_l4.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "1"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + locked = "true"; + cloudSpeed0 = "0.000503 0.000020"; + }; + new Lightning() { + position = "-1024 -1024 61"; + rotation = "1 0 0 0"; + scale = "2048 2048 800"; + dataBlock = "DefaultStorm"; + lockCount = "0"; + homingCount = "0"; + strikesPerMinute = "1"; + strikeWidth = "2.5"; + chanceToHitTarget = "5.5"; + strikeRadius = "20"; + boltStartRadius = "20"; + color = "1.000000 1.000000 1.000000 1.000000"; + fadeColor = "0.100000 0.100000 1.000000 1.000000"; + useFog = "1"; + + locked = "1"; + }; + new SimGroup(Enviornment) { + + powerCount = "0"; + + new SimGroup(Nature) { + + powerCount = "0"; + + new Precipitation(Precipitation) { + position = "-1004.8 -929.6 50.9292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Rain"; + lockCount = "0"; + homingCount = "0"; + percentage = "1"; + color1 = "0.600000 0.600000 0.600000 1.000000"; + color2 = "-1.000000 0.000000 0.000000 1.000000"; + color3 = "-1.000000 0.000000 0.000000 1.000000"; + offsetSpeed = "0.25"; + minVelocity = "1.25"; + maxVelocity = "4"; + maxNumDrops = "2000"; + maxRadius = "80"; + + locked = "1"; + }; + new TSStatic() { + position = "-336.457 -490.486 32.7819"; + rotation = "0.379686 -0.539022 0.75186 14.021"; + scale = "1 1 1"; + shapeName = "borg7.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-339.207 -482.392 33.8004"; + rotation = "-0.101848 -0.243128 0.964632 144.417"; + scale = "1 1 1"; + shapeName = "borg7.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-333.947 -483.951 33.9042"; + rotation = "-0.276839 0.467545 -0.839501 29.2208"; + scale = "0.54037 0.618647 0.755792"; + shapeName = "borg7.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-277.498 -214.503 106.133"; + rotation = "0 0 1 29.7938"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-284.074 -203.026 111.131"; + rotation = "0 0 -1 65.8901"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-224.405 -248.618 115.994"; + rotation = "0 0 1 84.2248"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-500.056 -639.291 85.7755"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-489.793 -647.996 82.8085"; + rotation = "0 0 -1 16.6158"; + scale = "1.61917 2.13842 1.41866"; + shapeName = "borg18.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-610.242 -1147.84 83.9007"; + rotation = "0 0 1 104.278"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "1"; + }; + new TSStatic() { + position = "-613.783 -1158.95 83.2654"; + rotation = "0 0 1 178.763"; + scale = "0.716828 0.9374 0.663525"; + shapeName = "borg17.dts"; + + locked = "1"; + }; + }; + new SimGroup(Structures) { + + powerCount = "0"; + + new InteriorInstance() { + position = "-334.561 -279.169 37.3868"; + rotation = "0.0706889 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-349.006 -285.62 39.7777"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-363.451 -292.07 42.1687"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-377.897 -298.521 44.5597"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-392.342 -304.972 46.9507"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-406.787 -311.423 49.3416"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-421.232 -317.874 51.7327"; + rotation = "0.070689 -0.331651 -0.94075 25.5316"; + scale = "1 1 1"; + interiorFile = "dbrdg1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-342.535 -177.16 86.4726"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "dmisc_neftower3.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-333.454 -485.105 31.9852"; + rotation = "-0.451583 -0.0394 0.891359 87.5353"; + scale = "1 1 1"; + interiorFile = "dtowr1.dif"; + showTerrainInside = "1"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-2160.09 -1896.36 19.5788"; + rotation = "-0.579775 0.777728 0.242899 25.0841"; + scale = "1 1 1"; + interiorFile = "dbase4.dif"; + showTerrainInside = "1"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-1544.53 -1488.19 124.165"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "dmisc_neftower3.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-627.784 -1354.31 156.247"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "dmisc_neftower3.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + }; + new SimGroup(Rocks) { + + powerCount = "0"; + + new InteriorInstance() { + position = "-1024.76 -1025.15 59.7916"; + rotation = "0 0 1 34.3775"; + scale = "1.38814 1.51868 1.30987"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-278.34 -263.074 69.3652"; + rotation = "-0.453271 0.117763 0.883559 96.859"; + scale = "1.28263 1.05101 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-246.938 -279.127 66.9781"; + rotation = "0 1 0 239.679"; + scale = "1.54886 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-241.489 -280.286 72.8413"; + rotation = "0.992499 -0.119675 0.0249823 203.411"; + scale = "1.18878 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-242.572 -256.646 102.455"; + rotation = "0 0 1 166.913"; + scale = "1.62666 2.01651 1.65055"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-240.821 -259.961 100.91"; + rotation = "0 0 1 157.563"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-238.19 -254.846 104.044"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-235.212 -267.162 100.717"; + rotation = "0 0 1 193.842"; + scale = "1.62666 2.01651 1.65055"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-232.502 -269.742 97.0021"; + rotation = "0.393149 -0.651831 0.648498 68.3407"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-232.108 -264.319 105.32"; + rotation = "0 0 1 157.563"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-234.796 -264.642 102.914"; + rotation = "0.980663 0.160021 -0.112667 201.778"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-775.399 -478.862 101.43"; + rotation = "0 0 1 120.894"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-175.116 -297.732 94.8308"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-840.683 -277.279 131.021"; + rotation = "0 0 1 165.585"; + scale = "1 1 1"; + interiorFile = "bspir2.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-1390.18 -497.042 204.099"; + rotation = "0 0 -1 16.0429"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-482.734 -1162.49 143.336"; + rotation = "0 0 -1 33.8045"; + scale = "1 1 1"; + interiorFile = "bspir1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-1352.22 -655.887 131.876"; + rotation = "0 0 -1 81.9329"; + scale = "1 1 1"; + interiorFile = "bspir2.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-1354.3 -347.374 141.727"; + rotation = "0 0 1 36.0964"; + scale = "1 1 1"; + interiorFile = "bspir1.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-597.448 -831.839 149.691"; + rotation = "0 0 1 236.241"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-1357.08 -1696.49 149.531"; + rotation = "0 0 1 4.58384"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + new InteriorInstance() { + position = "-748.389 -1515.04 112.415"; + rotation = "0 0 1 41.8259"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; + }; + new SimGroup(Sounds) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-1004.8 -929.6 50.9292"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/moaningwind1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "100000"; + maxDistance = "100000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "1"; + }; + }; + }; + new WaterBlock() { + position = "0 0 -12.3931"; + rotation = "1 0 0 0"; + scale = "2048 2048 50"; + liquidType = "OceanWater"; + density = "1"; + viscosity = "5"; + waveMagnitude = "0.5"; + surfaceTexture = "LiquidTiles/BlueWater"; + surfaceOpacity = "0.4"; + envMapTexture = "lush/skies/lushcloud1"; + envMapIntensity = "0.2"; + removeWetEdges = "0"; + AudioEnvironment = "Underwater"; + + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Map Functions +//------------------------------------------------------------------------------ +// Zombie Spawns +//------------------------------------------------------------------------------ + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-593.591 -759.176 114.548 0"; +%obj.type = "regular"; +%obj.timeout = "30"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-689.18 -745.442 103.003 0"; +%obj.type = "regular"; +%obj.timeout = "45"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-633.773 -1230.39 77.8934 0"; +%obj.type = "regular"; +%obj.timeout = "30"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-1146.84 -515.719 64.4526 0"; +%obj.type = "regular"; +%obj.timeout = "30"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-1034.05 -249.365 58.9471 0"; +%obj.type = "regular"; +%obj.timeout = "30"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-855.038 -367.456 83.3463 0"; +%obj.type = "regular"; +%obj.timeout = "23"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-1285.7 -1271.69 60.9632 0"; +%obj.type = "regular"; +%obj.timeout = "20"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-725.042 -533.809 64.046 0"; +%obj.type = "regular"; +%obj.timeout = "20"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-281.726 -1045.06 61.646 0"; +%obj.type = "regular"; +%obj.timeout = "18"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-380.778 -1486.59 62.6178 0"; +%obj.type = "regular"; +%obj.timeout = "28"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-552.242 -1649.3 62.364 0"; +%obj.type = "regular"; +%obj.timeout = "32"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-982.97 -1757.67 62.9513 0"; +%obj.type = "regular"; +%obj.timeout = "32"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-1157.61 -1912.35 63.3971 0"; +%obj.type = "regular"; +%obj.timeout = "35"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); + +%obj = new ScriptObject() { + class = "ZombiePoint"; +}; +%obj.spawnTransform = "-1409.45 -1557.08 64.3455 0"; +%obj.type = "regular"; +%obj.timeout = "41"; +%obj.label = "Spawn"; +%obj.maximumZombies = "-1"; +%obj.zombieLimit = "1"; +%obj.disabled = "0"; +%obj.spawnedZombies = 0; +%obj.StartSpawnLoop(); +addToZombiePointGroup(%obj); diff --git a/missions/RIA039.mis b/missions/RIA039.mis new file mode 100644 index 0000000..23aa778 --- /dev/null +++ b/missions/RIA039.mis @@ -0,0 +1,1016 @@ +// DisplayName = Remote Installation A039 +// MissionTypes = Infection + +//--- MISSION QUOTE BEGIN --- +//Sometimes, it is best that the dead remain dead. +//--Nukem +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//No vehicle stations. +//Low visibility. +//Made by: Blnukem. +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "6"; + musicTrack = "ACCM_Sentinel"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-216 -512 1200 944"; + flightCeiling = "300"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sky(Sky) { + position = "40 -768 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "600"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.365000 0.390000 0.420000 0.000000"; + fogDistance = "100"; + fogColor = "0.200000 0.200000 0.200000 1.000000"; + fogVolume1 = "100 0 1000"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 1.42884e-29 1.42885e-29"; + high_fogVolume2 = "-1 1.42886e-29 1.42887e-29"; + high_fogVolume3 = "-1 1.42889e-29 1.42889e-29"; + + cloudSpeed0 = "0.000400 0.000000"; + locked = "true"; + }; + new Sun() { + position = "40 -768 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.800000 0.800000 0.800000 1.000000"; + ambient = "0.400000 0.400000 0.400000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet1"; + terrainFile = "Escalade.ter"; + squareSize = "8"; + emptySquares = "89025 154817 155073 89787 89800 155841"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + XDimOverSize = "0"; + scale = "1 1 1"; + GraphFile = "Escalade.nav"; + YDimOverSize = "0"; + coverage = "0"; + position = "0 0 0 1"; + conjoinBowlDev = "20"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "535.796 -192.563 108.8"; + rotation = "-0.0213924 -0.275087 0.961181 188.549"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "True"; + }; + new Camera() { + position = "545.828 -290.898 51.73"; + rotation = "-0.0166108 -0.0299914 0.999412 237.931"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "708.366 -9.06354 113.656"; + rotation = "0.0176782 -0.135536 0.990615 165.275"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "True"; + }; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(team0) { + + powerCount = "1"; + providesPower = "1"; + + new Item() { + position = "396.285 84.5895 146.563"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Nexus"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + Target = "33"; + missionTypesList = "Hunters TeamHunters"; + team = "0"; + flashThreadDir = "1"; + locked = "true"; + }; + new StaticShape() { + position = "396.285 84.5895 146.608"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "NexusBase"; + lockCount = "0"; + homingCount = "0"; + + Target = "-1"; + missionTypesList = "Hunters TeamHunters"; + team = "0"; + lastState = "1"; + locked = "true"; + }; + new StaticShape() { + position = "396.285 84.5895 154.563"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "NexusCap"; + lockCount = "0"; + homingCount = "0"; + + Target = "-1"; + missionTypesList = "Hunters TeamHunters"; + team = "0"; + lastState = "1"; + locked = "true"; + }; + new Item() { + position = "396.285 84.5895 148"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "FLAG"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "0"; + rotate = "0"; + + Target = "34"; + missionTypesList = "Rabbit"; + team = "0"; + locked = "true"; + }; + new SimGroup(Tower3) { + + powerCount = "1"; + + new InteriorInstance() { + position = "717.326 -67.4132 86.0886"; + rotation = "-0.725414 -0.627316 0.283281 26.3494"; + scale = "1 1 1"; + interiorFile = "bbunk2.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_Pulse_2"; + AudioEnvironment = "SmallRoom"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "737.525 -30.5481 75.3614"; + rotation = "0.0980509 0.154653 0.983091 79.7261"; + scale = "1 1 1"; + interiorFile = "bbrdg9.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "734.91 -12.2017 68.2587"; + rotation = "0.296201 0.203738 0.933143 83.0044"; + scale = "1 1 1"; + interiorFile = "bbrdg8.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + }; + new SimGroup(Tower4) { + + powerCount = "1"; + + new InteriorInstance() { + position = "528 -264 55.9096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bbase1.dif"; + showTerrainInside = "0"; + AudioProfile = "Universal_Base_1"; + AudioEnvironment = "BigRoom"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "518.529 -301.951 47.3939"; + rotation = "0 0 1 93.392"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "517.431 -303.999 44.3939"; + rotation = "0 0 1 29.2208"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "519.116 -301.246 44.3939"; + rotation = "0 0 1 29.2208"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "516.223 -283.103 44.4025"; + rotation = "0 0 1 2.8649"; + scale = "1 1 1"; + shapeName = "stackable2m.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "515.165 -283.063 44.4025"; + rotation = "0 0 1 2.8649"; + scale = "1 1 1"; + shapeName = "stackable2m.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "526.061 -293.425 45.4357"; + rotation = "0 1 0 48.1284"; + scale = "1 1 1"; + shapeName = "stackable1m.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "530.696 -240.961 60.4578"; + rotation = "0 0 -1 90.5273"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "517.338 -283.085 44.4025"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "stackable2m.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "515.542 -283.133 45.4428"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "stackable2m.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "532.932 -240.958 60.4578"; + rotation = "0 0 -1 90.5273"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "530.747 -241.163 58.4078"; + rotation = "0 0 -1 90.5273"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "532.983 -241.159 58.4078"; + rotation = "0 0 -1 90.5273"; + scale = "1 1 1"; + shapeName = "stackable1l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "541.167 -307.516 44.4025"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "540.398 -304.381 44.4025"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "539.649 -301.209 44.4025"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "541.213 -306.551 47.4025"; + rotation = "0 0 -1 25.7832"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "539.773 -302.092 47.4025"; + rotation = "0 0 -1 14.897"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + new TSStatic() { + position = "520.786 -298.234 44.3939"; + rotation = "0 0 1 32.6586"; + scale = "1 1 1"; + shapeName = "stackable3l.dts"; + + team = "0"; + locked = "true"; + }; + }; + }; + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "528 -264 55.9096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "0"; + outdoorWeight = "100"; + + locked = "True"; + }; + }; + new SimGroup(MainBase) { + + new StaticShape() { + position = "541.656 -264.016 44.4066"; + rotation = "0 0 1 90"; + scale = "1 1 1"; + nameTag = "GT-104A"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "33"; + Trigger = "14967"; + locked = "True"; + }; + new WayPoint() { + position = "528 -264 55.9096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Base"; + team = "1"; + + locked = "True"; + }; + new StaticShape() { + position = "570.087 -318.522 46.4049"; + rotation = "0 0 1 90"; + scale = "1 1 1"; + nameTag = "Main"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + Target = "34"; + locked = "True"; + }; + new ForceFieldBare() { + position = "566.866 -324.913 46.4034"; + rotation = "1 0 0 0"; + scale = "10.2389 1.81633 8.37728"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + Target = "35"; + pzone = "16063"; + locked = "True"; + }; + new ForceFieldBare() { + position = "566.928 -313.901 46.31"; + rotation = "1 0 0 0"; + scale = "10.2389 1.81633 8.37728"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + Target = "36"; + pzone = "16181"; + locked = "True"; + }; + new StaticShape() { + position = "577.639 -329.35 46.4049"; + rotation = "0 0 1 90"; + scale = "1 1 1"; + nameTag = "Generator Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "37"; + Trigger = "16451"; + locked = "True"; + }; + new StaticShape() { + position = "514.856 -264.016 44.4066"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + nameTag = "XL-032T"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "38"; + Trigger = "16993"; + locked = "True"; + }; + new ForceFieldBare() { + position = "472.075 -271.548 68.4299"; + rotation = "1 0 0 0"; + scale = "7.53418 7.5842 1"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + Target = "39"; + pzone = "17332"; + locked = "True"; + }; + new TSStatic() { + position = "580 -268.065 55.4145"; + rotation = "1 0 0 0"; + scale = "2.12175 3.05042 29.9687"; + shapeName = "bmiscf.dts"; + + locked = "True"; + }; + new StaticShape() { + position = "580 -268.065 70.3929"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + nameTag = "Outdoor"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Target = "40"; + Trigger = "17565"; + locked = "True"; + }; + new TSStatic() { + position = "581.034 -266.546 49.3107"; + rotation = "0 0 -1 5.72969"; + scale = "1 1 1"; + shapeName = "stackable3m.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "581.146 -266.554 51.3107"; + rotation = "0 0 -1 14.324"; + scale = "1 1 1"; + shapeName = "stackable3m.dts"; + + locked = "True"; + }; + new Item() { + position = "578.609 -265.487 49.4515"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + dataBlock = "RepairPack"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + Target = "-1"; + locked = "True"; + }; + new TSStatic() { + position = "577.218 -267.348 50.5656"; + rotation = "0 -1 0 56.1499"; + scale = "1 1 1"; + shapeName = "stackable1m.dts"; + + locked = "True"; + }; + new ForceFieldBare() { + position = "525.487 -295.959 58.35"; + rotation = "1 0 0 0"; + scale = "4.54456 0.611816 4.21857"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + Target = "41"; + pzone = "18735"; + locked = "True"; + }; + new ForceFieldBare() { + position = "525.788 -232.676 58.0905"; + rotation = "1 0 0 0"; + scale = "4.54456 0.647232 4.4781"; + dataBlock = "defaultTeamSlowFieldBare"; + lockCount = "0"; + homingCount = "0"; + + Target = "42"; + pzone = "19104"; + locked = "True"; + }; + }; + }; + }; + new AudioEmitter() { + position = "424.809 117.98 92.8088"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/fog.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "0.5"; + isLooping = "1"; + is3D = "0"; + minDistance = "20"; + maxDistance = "1280"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new SimGroup(Environmental) { + + powerCount = "0"; + + new InteriorInstance() { + position = "763.785 -55.3552 96.4762"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "491.934 -327.482 68.363"; + rotation = "0 0 -1 77.9223"; + scale = "1 1 1"; + interiorFile = "bspir1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "478.946 -133.639 106.46"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "592.575 46.1623 125.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "182.143 207.594 74.8187"; + rotation = "0 0 1 83.0789"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "144.998 48.3066 67.5383"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir2.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "12.8101 -125.73 141.655"; + rotation = "0 0 1 56.1498"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "167.407 -283.042 174.129"; + rotation = "0 0 1 47.5555"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "266.965 -384.194 84.278"; + rotation = "0 0 1 211.604"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "475.994 305.509 150.58"; + rotation = "0 0 1 134.072"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "847.143 37.8342 111.179"; + rotation = "0 0 -1 25.21"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "736.171 -360.369 110.211"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir3.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "576.812 -24.2527 107.694"; + rotation = "0 0 1 45.2637"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new TSStatic() { + position = "718.467 23.5689 81.4184"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "560.3 -203.103 60.7361"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "564.017 -206.88 61.4791"; + rotation = "0 0 1 72.7656"; + scale = "0.835054 0.544838 0.715121"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new AudioEmitter() { + position = "560.3 -203.103 60.7361"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "564.017 -206.88 61.4791"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "90"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "478.946 -133.639 106.46"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/yeti_howl2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new FireballAtmosphere(FireballAtmosphere) { + position = "528 -264 55.9096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "fireball"; + lockCount = "0"; + homingCount = "0"; + dropRadius = "900"; + dropsPerMinute = "10"; + minDropAngle = "0"; + maxDropAngle = "10"; + startVelocity = "300"; + dropHeight = "1000"; + dropDir = "0.212 0.212 -0.953998"; + + locked = "True"; + }; + new AudioEmitter() { + position = "592.575 46.1623 125.94"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/Yeti_Howl1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new TSStatic() { + position = "387.114 185.088 106.529"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "171.139 20.4092 58.7165"; + rotation = "0.215628 -0.36589 0.905334 48.5527"; + scale = "1 1 1"; + shapeName = "vehicle_land_assault_wreck.dts"; + + locked = "True"; + }; + new InteriorInstance() { + position = "99.0241 237.883 43.6837"; + rotation = "-0.78211 0.00808529 -0.623088 38.2924"; + scale = "1 1 1"; + interiorFile = "bbunkc.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "46.5082 -51.6457 99.9538"; + rotation = "0 0 -1 17.7617"; + scale = "1 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "24.6354 -14.1111 94.4903"; + rotation = "-0.135939 0.119108 -0.983531 52.9798"; + scale = "1 1 1"; + interiorFile = "brocka.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "283.236 114.908 100.768"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "279.269 114.519 100.443"; + rotation = "0 1 0 51.5662"; + scale = "1 1 1"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "282.118 113.142 99.8048"; + rotation = "0 1 0 23.4912"; + scale = "0.839763 0.836464 0.712704"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "277.873 105.867 99.4567"; + rotation = "0 1 0 51.5662"; + scale = "1 0.752091 0.674348"; + interiorFile = "brock7.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new TSStatic() { + position = "271.776 115.747 99.8627"; + rotation = "1 0 0 0"; + scale = "0.663955 0.834768 0.61037"; + shapeName = "borg16.dts"; + + locked = "True"; + }; + }; + new TSStatic() { + position = "717.734 -68.4077 88.5157"; + rotation = "-0.725414 -0.627316 0.283281 26.3494"; + scale = "2.02374 1.95783 2.33095"; + shapeName = "bmiscf.dts"; + + locked = "True"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Tactical Assault.mis b/missions/Tactical Assault.mis new file mode 100644 index 0000000..0309f39 --- /dev/null +++ b/missions/Tactical Assault.mis @@ -0,0 +1,1419 @@ +// DisplayName = Tactical Assault +// MissionTypes = CombatCon + +//--- MISSION QUOTE BEGIN --- +//The Best Defense is a good Offense. +// -- Unknown/Popular Saying +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//Defend Your Base from Possible Threats +//Map by: Blnukem +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "ACCM_Commando"; + powerCount = "0"; + CTF_scoreLimit = "10"; + cdTrack = "6"; + + new MissionArea(MissionArea) { + area = "-1400 -808 1920 1808"; + flightCeiling = "800"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sky(Sky) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0003"; + cloudSpeed2 = "0.0004"; + cloudSpeed3 = "0.0005"; + visibleDistance = "500"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.200000 0.200000 0.200000 1.000000"; + fogDistance = "300"; + fogColor = "0.500000 0.600000 0.700000 1.000000"; + fogVolume1 = "200 50 245"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "nef_BlueClear.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 9.01226e-28 8.96831e-44"; + high_fogVolume2 = "-1 0 0"; + high_fogVolume3 = "-1 1.83194e-39 9.01238e-28"; + + cloudSpeed0 = "0.000500 0.005000"; + locked = "true"; + }; + new Sun() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.600000 0.600000 0.600000 1.000000"; + ambient = "0.600000 0.600000 0.600000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Alcatraz.ter"; + squareSize = "8"; + + dObj = "13082 31778 31849"; + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "65"; + cullDensity = "0.1"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + GraphFile = "Alcatraz.nav"; + coverage = "0"; + position = "0 0 0 1"; + conjoinBowlDev = "20"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new WaterBlock() { + position = "-1016 -1024 -9.4"; + rotation = "1 0 0 0"; + scale = "2048 2048 250"; + liquidType = "RiverWater"; + density = "1"; + viscosity = "5"; + waveMagnitude = "1"; + surfaceTexture = "LiquidTiles/IslandWater02"; + surfaceOpacity = "0.9"; + envMapTexture = "lush/skies/lush_day_emap"; + envMapIntensity = "0.15"; + removeWetEdges = "0"; + AudioEnvironment = "Underwater"; + + locked = "True"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team0) { + + powerCount = "0"; + + new InteriorInstance() { + position = "-981.046 105.581 245.911"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bbunkc.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new SimGroup(Mountain) { + + powerCount = "0"; + + new InteriorInstance() { + position = "-483.943 -280.365 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-499.943 -280.352 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-467.943 -280.378 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-435.943 -280.403 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-419.943 -280.416 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-451.943 -280.39 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-403.943 -280.429 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-371.943 -280.454 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-355.943 -280.467 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-387.943 -280.441 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-291.943 -280.518 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-307.943 -280.505 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-323.943 -280.492 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-339.943 -280.48 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-275.943 -280.531 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-259.943 -280.543 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-243.943 -280.556 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-227.943 -280.569 247.286"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbrdgo.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-28.5707 -67.8739 324.85"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc_-nef_flagstand1_x.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new InteriorInstance() { + position = "-85.5054 -65.5041 309.447"; + rotation = "-0.491677 0.866427 0.0869327 23.0676"; + scale = "1 1 1"; + interiorFile = "bbunkc.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + }; + }; + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-1041.22 736.437 253.852"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "40"; + sphereWeight = "20"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + new SpawnSphere() { + position = "-835.6 788 267.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "70"; + sphereWeight = "90"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + }; + new SimGroup(MainBase) { + + powerCount = "1"; + + new StaticShape() { + position = "-835.602 792.888 266.965"; + rotation = "0 0 1 0.181308"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + damageTimeMS = "3836828"; + station = "10285"; + team = "1"; + lastDamagedBy = "41473"; + inUse = "Down"; + Ready = "1"; + Target = "33"; + locked = "True"; + }; + new InteriorInstance() { + position = "-835.6 788 267.6"; + rotation = "0 0 1 180"; + scale = "1 1 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "-1041.22 736.437 253.852"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bbunk9.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-1041.2 748.437 255.852"; + rotation = "0 0 1 180.091"; + scale = "1 1 1"; + nameTag = "Main"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "58"; + locked = "True"; + }; + new StaticShape() { + position = "-1128.34 528.576 283.522"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + nameTag = "Generator Bunker"; + dataBlock = "SensorLargePulse"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "37"; + locked = "True"; + }; + new Turret() { + position = "-1108.39 669.39 268.352"; + rotation = "0 0 1 180.091"; + scale = "1 1 1"; + nameTag = "Generator Bunker"; + dataBlock = "TurretBaseLarge"; + lockCount = "0"; + homingCount = "0"; + initialBarrel = "PlasmaBarrelLarge"; + + team = "1"; + Target = "38"; + locked = "True"; + }; + new InteriorInstance() { + position = "-723.675 762.2 275.853"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "-1107.94 668.763 258.352"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "-1128.34 528.504 273.522"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new InteriorInstance() { + position = "-542.091 507.805 302.961"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new StaticShape() { + position = "-1034.62 743.454 255.852"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "13058"; + team = "1"; + Target = "34"; + locked = "True"; + }; + new StaticShape() { + position = "-819.8 824 269.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "14107"; + team = "1"; + Target = "36"; + locked = "True"; + }; + new StaticShape() { + position = "-851.6 824 269.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "14327"; + team = "1"; + Target = "39"; + locked = "True"; + }; + new StaticShape() { + position = "-1048.22 743.443 255.852"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "13309"; + team = "1"; + Target = "35"; + locked = "True"; + }; + new WayPoint() { + position = "-1041.22 736.437 253.852"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Generator Bunker"; + team = "1"; + + locked = "True"; + }; + new WayPoint() { + position = "-835.6 788 267.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Main Base"; + team = "1"; + + locked = "True"; + }; + new Item() { + position = "-824.007 774.441 253.982"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPack"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + team = "1"; + Target = "-1"; + locked = "True"; + }; + new StaticShape() { + position = "-542.091 507.877 312.961"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + nameTag = "Forward"; + dataBlock = "SensorLargePulse"; + lockCount = "0"; + homingCount = "0"; + + team = "1"; + Target = "41"; + locked = "True"; + }; + new Turret() { + position = "-724.123 763.026 285.853"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + nameTag = "Forward Defense"; + dataBlock = "TurretBaseLarge"; + lockCount = "0"; + homingCount = "0"; + initialBarrel = "AABarrelLarge"; + + team = "1"; + Target = "40"; + locked = "True"; + }; + }; + }; + new SimGroup(Team2) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-866.115 -599.864 267"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "70"; + sphereWeight = "90"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "2"; + locked = "True"; + }; + new SpawnSphere() { + position = "-630.096 -486.884 254.427"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "40"; + sphereWeight = "20"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "2"; + locked = "True"; + }; + }; + new SimGroup(MainBase) { + + powerCount = "1"; + + new InteriorInstance() { + position = "-866.115 -599.864 267"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bvpad.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new InteriorInstance() { + position = "-630.096 -486.884 254.427"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "bbunk9.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new StaticShape() { + position = "-462.581 -533.436 324.024"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Generator Bunker"; + dataBlock = "SensorLargePulse"; + lockCount = "0"; + homingCount = "0"; + + team = "2"; + Target = "42"; + locked = "True"; + }; + new StaticShape() { + position = "-866.358 -598.082 266.165"; + rotation = "0 0 1 180.091"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + damageTimeMS = "5024507"; + station = "10287"; + lastDamagedByTeam = "1"; + team = "2"; + lastDamagedBy = "32407"; + inUse = "Down"; + Ready = "1"; + Target = "44"; + locked = "True"; + }; + new InteriorInstance() { + position = "-771.85 -570.527 263.055"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new InteriorInstance() { + position = "-1018.97 -355.811 274.22"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new Item() { + position = "-855.415 -586.459 258.476"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + dataBlock = "RepairPack"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + team = "2"; + Target = "-1"; + locked = "True"; + }; + new WayPoint() { + position = "-630.096 -486.884 254.427"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Generator Bunker"; + team = "2"; + + locked = "True"; + }; + new WayPoint() { + position = "-866.115 -599.864 267"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Main Base"; + team = "2"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-462.581 -533.472 314.024"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "2"; + locked = "True"; + }; + new StaticShape() { + position = "-1018.97 -355.775 284.22"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Forward"; + dataBlock = "SensorLargePulse"; + lockCount = "0"; + homingCount = "0"; + + team = "2"; + Target = "59"; + locked = "True"; + }; + new Turret() { + position = "-657.962 -529.438 267.002"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Generator Bunker"; + dataBlock = "TurretBaseLarge"; + lockCount = "0"; + homingCount = "0"; + initialBarrel = "PlasmaBarrelLarge"; + + team = "2"; + Target = "49"; + locked = "True"; + }; + new StaticShape() { + position = "-621.675 -493.484 256.427"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "17929"; + team = "2"; + Target = "50"; + locked = "True"; + }; + new StaticShape() { + position = "-850.104 -635.854 269.075"; + rotation = "0 0 1 180.091"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "15950"; + team = "2"; + Target = "45"; + locked = "True"; + }; + new Turret() { + position = "-771.85 -571.127 273.055"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Forward Defense"; + dataBlock = "TurretBaseLarge"; + lockCount = "0"; + homingCount = "0"; + initialBarrel = "AABarrelLarge"; + + team = "2"; + Target = "48"; + locked = "True"; + }; + new StaticShape() { + position = "-618.169 -487.297 256.427"; + rotation = "0 0 -1 89.9544"; + scale = "1 1 1"; + nameTag = "Main"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + team = "2"; + Target = "46"; + locked = "True"; + }; + new StaticShape() { + position = "-882.104 -635.854 269.075"; + rotation = "0 0 1 180.091"; + scale = "1 1 1"; + nameTag = "V-Pad Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "15561"; + team = "2"; + Target = "43"; + locked = "True"; + }; + new StaticShape() { + position = "-621.696 -480.284 256.427"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + nameTag = "Bunker Inventory"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "18199"; + team = "2"; + Target = "47"; + locked = "True"; + }; + }; + }; + }; + new SimGroup(Sounds) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-735.282 150.31 273.97"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "80"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-1091.46 308.646 252.759"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "80"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-1118.29 -78.1929 310.683"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "500"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-876.401 -4.29293 622.709"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "9"; + maxDistance = "2000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-1086.41 172.163 288.891"; + rotation = "0.0369045 -0.0698952 0.996871 124.48"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + new Camera() { + position = "-758.781 -495.958 289.698"; + rotation = "-0.0296241 -0.0699121 0.997113 225.809"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + new Camera() { + position = "-796.633 626.683 271.785"; + rotation = "0 0 -1 16.0429"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "True"; + }; + }; + new SimGroup(Enviornment) { + + powerCount = "0"; + + new TSStatic() { + position = "-852.08 -105.214 266.09"; + rotation = "0 1 0 3.43793"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-1091.46 308.646 252.759"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-798.22 389.646 269.115"; + rotation = "-1 -1.45667e-07 -5.78644e-09 2.86517"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-729.918 159.152 275.387"; + rotation = "0 1 0 1.71915"; + scale = "1 1 1"; + shapeName = "borg13.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-735.282 150.31 273.97"; + rotation = "-0.56886 0.815951 -0.103061 20.937"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-930.263 -49.2552 253.921"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-1069.57 -82.841 270.701"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-595.05 370.751 257.407"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-153.861 -281.366 256.898"; + rotation = "0.0517905 -0.720909 0.691092 71.1619"; + scale = "1 1 1"; + interiorFile = "btowr2.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new TSStatic() { + position = "-205.797 -250.469 253.278"; + rotation = "-0.0503369 -0.372079 0.926835 76.9592"; + scale = "1 1 1"; + shapeName = "vehicle_land_assault_wreck.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-131.33 -311.388 256.963"; + rotation = "0.559426 -0.65514 0.507774 21.9032"; + scale = "1 1 1"; + shapeName = "vehicle_land_assault_wreck.dts"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-1036.61 -700.279 246.694"; + rotation = "0.862471 -0.148049 -0.483968 70.5773"; + scale = "1 1 1"; + interiorFile = "btowr8.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "-657.962 -529.038 257.002"; + rotation = "0 0 1 89.9544"; + scale = "1 1 1"; + interiorFile = "bmisc1.dif"; + showTerrainInside = "0"; + + team = "0"; + locked = "True"; + }; + new TSStatic() { + position = "-1020.69 204.773 257.524"; + rotation = "-0.76284 -0.645222 0.0419986 9.75442"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-686.571 369.624 280.659"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-533.664 -113.721 252.97"; + rotation = "0.976119 -0.216358 0.019525 10.5642"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-747.087 845.99 253.469"; + rotation = "0 0 1 57.2958"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-933.013 787.927 270.975"; + rotation = "0.743873 -0.643051 -0.182038 19.4913"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "-865.573 843.942 252.274"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-933.013 787.927 270.975"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-865.573 843.942 252.274"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-997.586 571.768 267.081"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-904.681 -665.179 273.857"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-1021 5.38879 259.058"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-793.619 19.5914 260.589"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-974.42 223.015 257.054"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-831.732 -227.158 253.946"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-1050.83 376.009 256.607"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/crickets_drygrass.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "5"; + maxDistance = "120"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/Testing Map.mis b/missions/Testing Map.mis new file mode 100644 index 0000000..04f4040 --- /dev/null +++ b/missions/Testing Map.mis @@ -0,0 +1,202 @@ +// DisplayName = Testing Grounds +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// A place to test stuff. +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Serveride Mission. +//Limited lighting for performance enhancement. +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "6"; + powerCount = "0"; + musicTrack = "desert"; + + new MissionArea(MissionArea) { + area = "-1024 -1024 2048 2048"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + providesPower = "1"; + powerCount = "1"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "0 0 100.45"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "100"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + + locked = "1"; + }; + }; + new StaticShape() { + position = "0 0 99.85"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Ready = "1"; + Target = "33"; + station = "13838"; + }; + new StaticShape() { + position = "-36 1.23223e-06 102.45"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "1"; + Target = "40"; + Trigger = "14401"; + }; + new WayPoint() { + position = "0 0 100.45"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Spawning Area"; + team = "1"; + + locked = "1"; + }; + }; + new SimGroup(team0) { + + providesPower = "1"; + powerCount = "1"; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0 0 1"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet2"; + terrainFile = "Flatland.ter"; + squareSize = "8"; + + locked = "true"; + position = "-1024 -1024 0"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + locked = "true"; + position = "0 0 0 1"; + coverage = "0"; + rotation = "0 0 0 0"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + GraphFile = "Flatland.nav"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + team = "0"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "1200"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.000000 0.000000 0.000000 1.000000"; + fogDistance = "1000"; + fogColor = "0.000000 0.000000 0.000000 1.000000"; + fogVolume1 = "0 0 0"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "lava_night.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + locked = "true"; + cloudSpeed0 = "0.000503 0.000020"; + }; + new InteriorInstance() { + position = "0 0 100.45"; + rotation = "-0 -0 1 90"; + scale = "1 1 1"; + interiorFile = "dvpad.dif"; + showTerrainInside = "0"; + + locked = "1"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/The Watering Hole.mis b/missions/The Watering Hole.mis new file mode 100644 index 0000000..a99d882 --- /dev/null +++ b/missions/The Watering Hole.mis @@ -0,0 +1,553 @@ +// DisplayName = The Watering Hole +// MissionTypes = Construction + +//--- MISSION QUOTE BEGIN --- +//A peaceful watering hole for all to drink. +//No predators, No worries, Just Peace... +//--Nukem +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//No vehicle station. +//Inventory Bunker is accessable by all teams. +//Map made by: Blnukem +//Mission touched up by Eolk +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + CTF_scoreLimit = "6"; + powerCount = "0"; + musicTrack = "lush"; + cdTrack = "2"; + CTF_timeLimit = "25"; + + new MissionArea(MissionArea) { + area = "-688 -640 1360 1280"; + flightCeiling = "5000"; + flightCeilingRange = "50"; + + locked = "true"; + }; + new WaterBlock() { + position = "-1024 -1024 -26.4"; + rotation = "1 0 0 0"; + scale = "2048 2048 69"; + liquidType = "Water"; + density = "0"; + viscosity = "15"; + waveMagnitude = "0.5"; + surfaceTexture = "terrain/seawaterfull2"; + surfaceOpacity = "0.5"; + envMapTexture = "LiquidTiles/archipelago_emap_cloudsground"; + envMapIntensity = "0.4"; + removeWetEdges = "0"; + + extent = "100 100 10"; + params1 = "0.63 -2.41 0.33 0.21"; + params0 = "0.32 -0.67 0.066 0.5"; + params3 = "1.21 -0.61 0.13 -0.33"; + seedPoints = "0 0 1 0 1 1 0 1"; + floodFill = "1"; + params2 = "0.39 0.39 0.2 0.133"; + textureSize = "32 32"; + locked = "TRUE"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet1"; + terrainFile = "SkinnyDip.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + hazeDistance = "150"; + visibleDistance = "850"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "45"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + GraphFile = "SkinnyDip.nav"; + scale = "1 1 1"; + XDimOverSize = "0"; + conjoinBowlDev = "20"; + position = "0 0 0 1"; + coverage = "0"; + YDimOverSize = "0"; + rotation = "0 0 0 0"; + locked = "true"; + }; + new Sky(Sky) { + position = "-1196 -820 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.01"; + cloudSpeed2 = "0.02"; + cloudSpeed3 = "0.03"; + visibleDistance = "1000"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.200000 0.200000 0.200000 0.000000"; + fogDistance = "850"; + fogColor = "0.500000 0.600000 0.600000 1.000000"; + fogVolume1 = "500 0 45"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "nef_Surreal1.dml"; + windVelocity = "0 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 -198748244414614883000000000000000000000.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 -222768174765569861000000000000000000000.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3.27833e-28 3148.48"; + high_fogVolume2 = "-1 2.24208e-44 2.51673e-42"; + high_fogVolume3 = "-1 7.74718e+31 1.49413e-07"; + + cloudSpeed0 = "0.000000 0.000000"; + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + providesPower = "1"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "3.59498 215.024 67.0999"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + + team = "1"; + locked = "True"; + }; + }; + new SimGroup(Base0) { + + powerCount = "0"; + + new InteriorInstance() { + position = "3.59498 222.424 73.2999"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bbunk1.dif"; + showTerrainInside = "0"; + + team = "1"; + locked = "True"; + }; + new WayPoint() { + position = "3.59498 215.024 67.0999"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = "Inventory Bunker"; + team = "1"; + + locked = "True"; + }; + new StaticShape() { + position = "-1.21538 209.228 67.2999"; + rotation = "0 0 -1 90"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "12831"; + team = "1"; + Target = "34"; + locked = "True"; + }; + new StaticShape() { + position = "8.39626 220.828 67.2999"; + rotation = "0 0 1 90"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "13086"; + team = "1"; + Target = "35"; + locked = "True"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-20.2479 608.29 286.973"; + rotation = "0 0 1 179.518"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "TRUE"; + }; + new Camera() { + position = "129.177 8.26169 233.147"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "true"; + }; + }; + new Sun() { + position = "-13.3917 17.5838 -5.8"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.800000 0.800000 0.800000 1.000000"; + ambient = "0.200000 0.200000 0.200000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + }; + new SimGroup(Enviorment) { + + powerCount = "0"; + + new TSStatic() { + position = "10.8244 -7.8483 50.7992"; + rotation = "-0.0199068 -0.0957422 0.995207 23.6013"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + + locked = "True"; + }; + new TSStatic() { + position = "129.071 -93.5386 47.3364"; + rotation = "-0.868171 -0.495961 -0.0173657 4.61922"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + + locked = "True"; + }; + new AudioEmitter() { + position = "10.8244 -7.8483 50.7992"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/rumblingthunder.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "100000"; + maxDistance = "100000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "30000"; + maxLoopGap = "60000"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "10.8244 -7.8483 50.7992"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/moaningwind1.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "100000"; + maxDistance = "100000"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "1"; + }; + new AudioEmitter() { + position = "-10.9796 128.39 41.6198"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "89.2546 160.244 43.3263"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "238.176 165.464 42.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "298.196 39.4907 42.7479"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "214.949 -75.4058 42.7786"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "88.187 -160.324 42.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "-41.0417 -79.4459 43.209"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "16.6086 21.0583 43.7248"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new AudioEmitter() { + position = "113.682 64.9883 42.6"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/river2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "10"; + maxDistance = "150"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "True"; + }; + new InteriorInstance() { + position = "607.358 63.2132 109.417"; + rotation = "0.964416 -0.242434 -0.105484 43.8273"; + scale = "1 1 1"; + interiorFile = "btowr6.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "340.38 -344.118 189.032"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bspir4.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + new InteriorInstance() { + position = "549.464 199.982 179.859"; + rotation = "0 0 1 221.917"; + scale = "1 1 1"; + interiorFile = "bspir5.dif"; + showTerrainInside = "0"; + + locked = "True"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/TitanII.mis b/missions/TitanII.mis new file mode 100644 index 0000000..323d30f --- /dev/null +++ b/missions/TitanII.mis @@ -0,0 +1,1516 @@ +// MissionTypes = CTF + +//--- MISSION QUOTE BEGIN --- +//"It is good that war is so grouseoum, for if it wasnt we might become to fond of it." +//--Ulysses S. Grant, Civil War +//--- MISSION QUOTE END --- + +//--- MISSION STRING BEGIN --- +//made for Combat construction mod +//--- MISSION STRING END --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + cdTrack = "2"; + musicTrack = "lush"; + CTF_scoreLimit = "6"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-704 -472 1504 1376"; + flightCeiling = "2000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "250"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.710000 0.710000 0.710000 1.000000"; + fogDistance = "350"; + fogColor = "0.000000 0.000000 0.000000 1.000000"; + fogVolume1 = "150 -10 500"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_starrynight.dml"; + windVelocity = "1 1 0"; + windEffectPrecipitation = "1"; + fogVolumeColor1 = "25.000000 25.000000 25.000000 1.000000"; + fogVolumeColor2 = "0.000000 0.000000 0.000000 0.000000"; + fogVolumeColor3 = "0.000000 0.000000 0.000000 0.000000"; + high_visibleDistance = "300"; + high_fogDistance = "250"; + high_fogVolume1 = "250 0 75"; + high_fogVolume2 = "0 0 0"; + high_fogVolume3 = "0 0 0"; + + locked = "true"; + }; + new Sun() { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.000000 0.000000 0.000000 1.000000"; + ambient = "0.200000 0.200000 0.200000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/lushdet2"; + terrainFile = "Titan.ter"; + squareSize = "8"; + emptySquares = "104569 104595 170324 104825 104851 104887 301651 105081 105107 236214 170836 105337 105363 236470"; + + locked = "true"; + position = "-1024 -1024 0"; + dObj = "13217 23751 23756 23764 28028 28033 28816 29030 29282 29819 30614 30904 114408 122773 122773 122951"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.1"; + customArea = "0 0 0 0"; + + locked = "true"; + coverage = "0"; + position = "0 0 0 1"; + GraphFile = "Titan.nav"; + rotation = "0 0 0 0"; + XDimOverSize = "0"; + scale = "1 1 1"; + YDimOverSize = "0"; + conjoinBowlDev = "20"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "515.891 174.074 165.989"; + rotation = "0.236077 0.159882 -0.958491 70.4883"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "-408.591 190.441 169.408"; + rotation = "0.305418 -0.251493 0.918407 83.7585"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "444.265 210.821 105.678"; + rotation = "0.209539 -0.155723 0.965321 75.183"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + }; + new Camera() { + position = "-352.861 213.445 167.668"; + rotation = "0.115987 0.0926002 -0.988925 77.8284"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + }; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-103.979 273.628 25.542"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "25"; + outdoorWeight = "75"; + }; + }; + new SimGroup(Base1) { + + powerCount = "1"; + + new StaticShape() { + position = "-351.314 254.647 100.351"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "34"; + team = "1"; + }; + new StaticShape() { + position = "-96.1353 257.252 14.134"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "35"; + lastDamagedBy = "4258"; + Trigger = "705251"; + team = "1"; + damageTimeMS = "2549278"; + lastDamagedByTeam = "1"; + notReady = "1"; + inUse = "Down"; + }; + new StaticShape() { + position = "-49.9094 201.596 35.0732"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "36"; + Trigger = "705253"; + team = "1"; + notReady = "1"; + inUse = "Down"; + }; + }; + }; + new SimGroup(Team2) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "206.791 142.119 18.342"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "50"; + sphereWeight = "100"; + indoorWeight = "25"; + outdoorWeight = "75"; + }; + }; + new SimGroup(Base2) { + + powerCount = "1"; + + new StaticShape() { + position = "153.281 215.164 35.0835"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "37"; + lastDamagedBy = "6456"; + Trigger = "705259"; + team = "2"; + hasExploded = "1"; + damageTimeMS = "2760411"; + wasDisabled = "1"; + lastDamagedByTeam = "1"; + notReady = "1"; + inUse = "Down"; + repairedBy = "9587"; + }; + new StaticShape() { + position = "174.241 221.416 11.4725"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "38"; + team = "2"; + }; + new StaticShape() { + position = "208.889 168.257 13.943"; + rotation = "0 0 1 179.909"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "39"; + lastDamagedBy = "-1"; + Trigger = "705262"; + team = "2"; + hasExploded = "1"; + damageTimeMS = "2351106"; + wasDisabled = "1"; + notReady = "1"; + inUse = "Down"; + repairedBy = "73268"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + }; + }; + new SimGroup(Neutral) { + + powerCount = "1"; + + new InteriorInstance() { + position = "30.596 212.62 15.039"; + rotation = "0 0 1 90"; + scale = "1 1.27 1"; + interiorFile = "t_bmisc_tunl_ccb1.dif"; + showTerrainInside = "0"; + + team = "0"; + dObj = "31049 89531 91198 91763 122385 122385 122592 122592 125726 125878 125892 705856 705938 705946 705956 706712 706717 706912 707610 707621 708293 708419 708723 709162 709691 710376"; + }; + new Item() { + position = "129.661 208.621 13.2237"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPatch"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + locked = "true"; + Target = "-1"; + }; + new Item() { + position = "91.561 208.621 13.2237"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPatch"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + locked = "true"; + Target = "-1"; + }; + new Item() { + position = "10.281 208.621 13.2237"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPatch"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + locked = "true"; + Target = "-1"; + }; + new Item() { + position = "50.931 208.621 13.2237"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPatch"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + locked = "true"; + Target = "-1"; + }; + new Item() { + position = "-27.839 208.621 13.2237"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "RepairPatch"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "1"; + rotate = "0"; + + locked = "true"; + Target = "-1"; + }; + new StaticShape() { + position = "50.7538 222.592 17.0109"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GeneratorLarge"; + lockCount = "0"; + homingCount = "0"; + + lastState = "1"; + Target = "40"; + lastDamagedBy = "146942"; + hasExploded = "1"; + damageTimeMS = "2399203"; + wasDisabled = "1"; + lastDamagedByTeam = "1"; + soiledByEnemyRepair = "1"; + repairedBy = "20774"; + }; + }; + new SimGroup(Ambiance) { + + powerCount = "0"; + + new AudioEmitter() { + position = "-154.035 701.115 98.8816"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "872.708 653.718 34.5472"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "283.128 -467.935 36.468"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-265.314 -706.895 118.534"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-784.917 103.399 105.463"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/bird_echo2.wav"; + useProfileDescription = "0"; + outsideAmbient = "1"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "141.989 208.854 20.5929"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/base_1.wav"; + useProfileDescription = "0"; + outsideAmbient = "0"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "35.9821 209.314 19.9535"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/base_1.wav"; + useProfileDescription = "0"; + outsideAmbient = "0"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + new AudioEmitter() { + position = "-49.2331 208.9 18.955"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + fileName = "fx/environment/base_1.wav"; + useProfileDescription = "0"; + outsideAmbient = "0"; + volume = "1"; + isLooping = "1"; + is3D = "1"; + minDistance = "35"; + maxDistance = "2240"; + coneInsideAngle = "360"; + coneOutsideAngle = "360"; + coneOutsideVolume = "1"; + coneVector = "0 0 1"; + loopCount = "-1"; + minLoopGap = "0"; + maxLoopGap = "0"; + type = "EffectAudioType"; + + locked = "true"; + }; + }; + new SimGroup(randomObjects) { + + powerCount = "0"; + + new SimGroup(Addition2BEPlant1) { + + powerCount = "0"; + + new TSStatic() { + position = "220 -476 19.4751"; + rotation = "-0.0619354 -0.469834 -0.880579 46.0111"; + scale = "1.3 1.3 1.3"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "836 364 84.6469"; + rotation = "0.107857 -0.166852 0.980065 158.428"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-676 36 60.2876"; + rotation = "-0.0420833 0.139405 -0.989341 109.579"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-676 28 59.6626"; + rotation = "0.010354 -0.011007 0.999886 187"; + scale = "1.9 1.9 1.9"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-684 -28 87.2096"; + rotation = "0.137937 0.0032154 0.990436 100.542"; + scale = "1.8 1.8 1.8"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "220 268 21.1157"; + rotation = "-0.212585 0.0588196 -0.975371 43.9836"; + scale = "1.1 1.1 1.1"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "748 -220 41.1626"; + rotation = "0.0421622 0.108361 0.993217 152.183"; + scale = "1.1 1.1 1.1"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "876 -84 51.0221"; + rotation = "-0.218229 -0.0862698 0.972077 40.0324"; + scale = "1.2 1.2 1.2"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-156 612 107.225"; + rotation = "0.281003 0.111516 0.953206 78.6786"; + scale = "1.8 1.8 1.8"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "236 -356 15.272"; + rotation = "0.101492 0.243842 -0.96449 37.2353"; + scale = "1.3 1.3 1.3"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-828 132 109.006"; + rotation = "-0.0822154 0.00890967 0.996575 86.1958"; + scale = "1.6 1.6 1.6"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-644 -4 62.0845"; + rotation = "-0.0504487 0.093975 0.994296 123.274"; + scale = "1.7 1.7 1.7"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-68 892 58.9283"; + rotation = "0.103194 -0.142236 -0.984439 75.8694"; + scale = "1.8 1.8 1.8"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-172 -540 109.663"; + rotation = "0.0655961 -0.0197932 0.99765 158.05"; + scale = "1.6 1.6 1.6"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-540 1068 81.022"; + rotation = "0.0117686 -0.045284 0.998905 175.006"; + scale = "1.9 1.9 1.9"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-268 108 112.569"; + rotation = "0.130642 0.0951836 0.98685 165.195"; + scale = "1.9 1.9 1.9"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-636 628 38.8657"; + rotation = "0.0679809 -0.116834 -0.990822 71.5005"; + scale = "1.7 1.7 1.7"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-92 108 6.25638"; + rotation = "-0.770669 -0.0888065 0.631017 15.7871"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "108 -572 170.834"; + rotation = "-0.120277 0.100319 0.987659 149.364"; + scale = "1.4 1.4 1.4"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "636 -212 53.6939"; + rotation = "-0.00151716 0.0027444 0.999995 201"; + scale = "1.2 1.2 1.2"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-724 924 119.866"; + rotation = "0.0570432 0.0675137 0.996086 91.2246"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "388 -596 107.1"; + rotation = "-0.191423 -0.531619 0.825069 33.6286"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "500 -428 77.4282"; + rotation = "-0.140459 -0.150112 -0.978641 116.116"; + scale = "1.3 1.3 1.3"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-620 -668 52.4907"; + rotation = "-0.206892 -0.0125599 0.978283 116.135"; + scale = "1 1 1"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "452 508 130.194"; + rotation = "-0.04678 -0.195645 0.979558 111.108"; + scale = "1.9 1.9 1.9"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-52 -36 37.2095"; + rotation = "-0.412201 -0.150072 0.898648 41.9298"; + scale = "1.2 1.2 1.2"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-20 -628 21.772"; + rotation = "-0.135054 -0.334087 -0.932816 23.5419"; + scale = "1.8 1.8 1.8"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "108 -588 172.584"; + rotation = "-0.186757 -0.0787747 0.979243 239.954"; + scale = "1.9 1.9 1.9"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "268 1044 33.1782"; + rotation = "0.260129 -0.0787291 -0.962359 99.1761"; + scale = "1.7 1.7 1.7"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-100 188 16.022"; + rotation = "0.0742071 0.210448 0.974785 29.7173"; + scale = "1.7 1.7 1.7"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-276 1084 40.8813"; + rotation = "0.0197627 0.205347 -0.97849 63.1056"; + scale = "1.6 1.6 1.6"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-92 1020 37.9439"; + rotation = "-0.29285 0.621186 0.726889 29.9427"; + scale = "2 2 2"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-692 364 82.5377"; + rotation = "-0.0380199 0.0657915 0.997109 139.109"; + scale = "1.5 1.5 1.5"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-604 1036 67.4751"; + rotation = "-0.00469803 -0.00563678 0.999973 212.999"; + scale = "1.8 1.8 1.8"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-508 -172 48.8345"; + rotation = "-0.0165448 0.0560557 0.998291 151.048"; + scale = "1 1 1"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "652 -596 74.3032"; + rotation = "0.052264 -0.0828748 0.995189 121.237"; + scale = "1.4 1.4 1.4"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "340 788 107.506"; + rotation = "0.184802 0.0937937 -0.97829 87.2558"; + scale = "1.7 1.7 1.7"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-204 20 69.1782"; + rotation = "0.294869 0.143488 -0.944703 45.2693"; + scale = "1.4 1.4 1.4"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "548 1076 38.8657"; + rotation = "-0.0596132 0.113072 0.991797 195.871"; + scale = "1 1 1"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-804 -484 102.256"; + rotation = "-0.0554243 -0.0758691 0.995576 156.103"; + scale = "1.6 1.6 1.6"; + shapeName = "borg1.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition3BEPlant5) { + + powerCount = "0"; + + new TSStatic() { + position = "-84 940 41.0626"; + rotation = "0.145161 0.224507 0.9636 47.5479"; + scale = "1.5 1.5 1.5"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "68 268 167.375"; + rotation = "-0.129944 0.0563715 -0.989918 69.5428"; + scale = "2.3 2.3 2.3"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-668 916 119.609"; + rotation = "0.00618828 -0.0134854 0.99989 182.999"; + scale = "1.2 1.2 1.2"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "251.991 -148.008 16.5135"; + rotation = "-0.22446 0.143075 0.963923 34.1644"; + scale = "0.5 0.5 0.5"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-844 644 116.938"; + rotation = "0.0333969 -0.0728298 -0.996785 34.1035"; + scale = "2.7 2.7 2.7"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "84.0365 -116.025 175.767"; + rotation = "0.169144 0.119248 -0.978351 41.8295"; + scale = "2.2 2.2 2.2"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "828 148 139.563"; + rotation = "0.0874888 0.0435482 0.995213 164.076"; + scale = "1.9 1.9 1.9"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-76 956 40.1563"; + rotation = "-0.0994176 0.024115 0.994754 199.898"; + scale = "0.6 0.6 0.6"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "60 60 167.25"; + rotation = "-0.023334 -0.00980043 0.99968 113.017"; + scale = "1.6 1.6 1.6"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-308 1044 36.3282"; + rotation = "-0.0997445 -0.00899458 0.994972 236.758"; + scale = "1.8 1.8 1.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-500 -332 115.359"; + rotation = "-0.00512002 0.362882 -0.931821 41.6162"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "100 604 164.156"; + rotation = "0.0307095 0.147618 0.988568 36.3891"; + scale = "2.6 2.6 2.6"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "500 -180 47.6094"; + rotation = "0.0378594 -0.0507505 0.997994 123.096"; + scale = "1.7 1.7 1.7"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-44 -452 42.3125"; + rotation = "-0.0276217 0.054789 0.998116 61.0945"; + scale = "2.7 2.7 2.7"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "756 -212 38.9688"; + rotation = "0.209896 -0.0545199 -0.976202 31.7183"; + scale = "2.9 2.9 2.9"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-52 -452 42.7187"; + rotation = "0.755597 -0.591989 -0.280398 7.124"; + scale = "2.5 2.5 2.5"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "260 436 24.0468"; + rotation = "-0.0825084 0.00717972 0.996565 166.048"; + scale = "2.8 2.8 2.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "540 388 59.3594"; + rotation = "0.0137753 0.012002 -0.999833 61.0085"; + scale = "2.1 2.1 2.1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-828 636 116.219"; + rotation = "0.0408396 0.0148944 0.999055 149.028"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "764 -452 88.6094"; + rotation = "0.458021 -0.367773 -0.809296 20.9256"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-828 628 116.516"; + rotation = "0.037596 0.00311729 0.999288 132.03"; + scale = "0.7 0.7 0.7"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "508 -316 24.3438"; + rotation = "-0.0978413 -0.0225715 -0.994946 72.2763"; + scale = "1.1 1.1 1.1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "620 500 156.188"; + rotation = "-0.170897 -0.00990428 -0.985239 86.8507"; + scale = "1.2 1.2 1.2"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "612 284 64.5469"; + rotation = "0.0375894 -0.0166852 0.999154 79.0475"; + scale = "2.3 2.3 2.3"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "476 532 132.172"; + rotation = "-0.626917 -0.721872 -0.293046 10.2116"; + scale = "1.2 1.2 1.2"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-820 -196 99.9219"; + rotation = "-0.00282076 0.252238 -0.967661 58.5935"; + scale = "1.4 1.4 1.4"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-732 44 61.9062"; + rotation = "0.0651787 -0.105146 -0.992319 104.429"; + scale = "1 1 1"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-620 -220 62.5782"; + rotation = "-0.112154 -0.0655775 0.991525 45.3459"; + scale = "1.5 1.5 1.5"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-100 -364 14.1875"; + rotation = "-0.0478932 0.0595414 0.997076 70.1575"; + scale = "2.8 2.8 2.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "52 100 172.25"; + rotation = "-0.198685 0.0420952 -0.979159 46.8746"; + scale = "0.9 0.9 0.9"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-260 972 83.8594"; + rotation = "0.000216615 0.108577 0.994088 217.792"; + scale = "2 2 2"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-532 924 78.1094"; + rotation = "0.0549848 -0.00244251 0.998484 89.0869"; + scale = "0.6 0.6 0.6"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-324 1092 39.2344"; + rotation = "-5.13069e-05 0.00588003 -0.999983 89.001"; + scale = "1.3 1.3 1.3"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-204.091 -67.9863 117.955"; + rotation = "0.100149 -0.11135 0.988722 101.637"; + scale = "2.8 2.8 2.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "420 1004 109.094"; + rotation = "-0.0816891 -0.0509707 -0.995354 109.252"; + scale = "0.8 0.8 0.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-588 1012 67.9688"; + rotation = "0.0753848 0.129974 0.988648 83.6497"; + scale = "3 3 3"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-772 -164 109.5"; + rotation = "0.0104555 0.0408091 -0.999112 74.049"; + scale = "2.8 2.8 2.8"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-500 852 31"; + rotation = "0.0961827 -0.0278696 0.994973 209.856"; + scale = "2.7 2.7 2.7"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-52 1132 14.8594"; + rotation = "-0.0465322 -0.0922325 -0.99465 72.2924"; + scale = "1.3 1.3 1.3"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-460 1028 109.235"; + rotation = "0.0530417 0.0242213 0.998299 226.929"; + scale = "1.5 1.5 1.5"; + shapeName = "borg5.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition4BELgTree16) { + + powerCount = "0"; + + new TSStatic() { + position = "580 380 56.0938"; + rotation = "0 0 1 192"; + scale = "0.7 0.7 0.7"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "860 652 23.6093"; + rotation = "0 0 -1 103"; + scale = "0.7 0.7 0.7"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-828 -660 41.1875"; + rotation = "0 0 -1 49.0002"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "236 4 26.1593"; + rotation = "0 0 1 139"; + scale = "0.8 0.8 0.8"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-100 -556 51.5156"; + rotation = "0 0 -1 100"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-156 740 106.75"; + rotation = "0 0 1 50"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "50.4952 -216.25 177.953"; + rotation = "0 0 1 150"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "948 -188 71.5781"; + rotation = "0 0 1 76.9998"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-444 -52 147.969"; + rotation = "0 0 1 17"; + scale = "0.9 0.9 0.9"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "971.289 -47.278 109.375"; + rotation = "0 0 -1 53"; + scale = "1.2 1.2 1.2"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition5BELgTree19) { + + powerCount = "0"; + + new TSStatic() { + position = "-660 620 37.1719"; + rotation = "0 0 1 79"; + scale = "0.7 0.7 0.7"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-68 964 38.5469"; + rotation = "0 0 -1 41"; + scale = "0.5 0.5 0.5"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "828 -420 48.4688"; + rotation = "0 0 1 60.0001"; + scale = "0.7 0.7 0.7"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "540 -652 35.4687"; + rotation = "0 0 1 33"; + scale = "0.6 0.6 0.6"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "500 388 58.3281"; + rotation = "0 0 -1 28.0002"; + scale = "0.7 0.7 0.7"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-436 -220 30.7812"; + rotation = "0 0 1 148"; + scale = "0.6 0.6 0.6"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-724 52 58.875"; + rotation = "0 0 1 119"; + scale = "0.7 0.7 0.7"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-412 -284 65.8125"; + rotation = "0 0 1 170"; + scale = "0.6 0.6 0.6"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "812 636 47.7031"; + rotation = "0 0 -1 34.0002"; + scale = "0.5 0.5 0.5"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "125.922 737.579 175.352"; + rotation = "0 0 1 85.9998"; + scale = "0.5 0.5 0.5"; + shapeName = "borg19.dts"; + + locked = "true"; + }; + }; + new SimGroup(Addition6BELgTree18) { + + powerCount = "0"; + + new TSStatic() { + position = "-140 660 73.8281"; + rotation = "0 0 1 187"; + scale = "2.6 2.6 2.6"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "788 -300 22.2031"; + rotation = "0 0 1 197"; + scale = "2.4 2.4 2.4"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-764 116 88.8282"; + rotation = "0 0 1 198"; + scale = "2.2 2.2 2.2"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "308 -484 10.75"; + rotation = "0 0 1 32"; + scale = "2.6 2.6 2.6"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-260 -692 109.578"; + rotation = "0 0 1 178"; + scale = "2.2 2.2 2.2"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "684 940 91.6719"; + rotation = "0 0 1 200"; + scale = "2.6 2.6 2.6"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-634.719 -588.854 26.8091"; + rotation = "0 0 1 213"; + scale = "2.6 2.6 2.6"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "523.281 -65.1077 100.148"; + rotation = "0 0 -1 32"; + scale = "2.5 2.5 2.5"; + shapeName = "borg18.dts"; + + locked = "true"; + }; + }; + }; + new SimGroup() { + + powerCount = "0"; + }; + new Item() { + position = "-71.5493 226.382 17.6616"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "GrenadeThrown"; + lockCount = "0"; + homingCount = "0"; + collideable = "0"; + static = "0"; + rotate = "0"; + + Target = "-1"; + team = "1"; + sourceObject = "712862"; + detThread = "834826"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/mapscripts/Teleporters.Cs b/missions/mapscripts/Teleporters.Cs new file mode 100644 index 0000000..1cee844 --- /dev/null +++ b/missions/mapscripts/Teleporters.Cs @@ -0,0 +1,577 @@ + +deleteFile("missions/mapscripts/teleporters.cs.dso"); +//------------teleport code by sparky---------- +//-------------------V2.0.0--------------------- +//------------special effects by TseTse-------- + + + +datablock StaticShapeData(Teleporter) +{ + + catagory = "Teleporters"; +// shapefile = "Nexusbase.dts"; + shapefile = "nexusbase.dts"; + mass = 10; + elasticity = 0.2; + friction = 0.6; + pickupRadius = 2; + targetNameTag = ''; + targetTypeTag = 'Teleporter'; +//---------------------------------- + maxDamage = 1.00; + destroyedLevel = 1.00; + disabledLevel = 0.70; + explosion = ShapeExplosion; + expDmgRadius = 8.0; + expDamage = 0.4; + expImpulse = 1500.0; + // don't allow this object to be damaged in non-team-based + // mission types (DM, Rabbit, Bounty, Hunters) + noIndividualDamage = true; + + dynamicType = $TypeMasks::StationObjectType; + isShielded = true; + energyPerDamagePoint = 75; + maxEnergy = 50; + rechargeRate = 0.35; + doesRepair = true; + humSound = StationInventoryHumSound; + + cmdCategory = "Support"; + cmdIcon = CMDStationIcon; + cmdMiniIconName = "commander/MiniIcons/com_inventory_grey"; + + debrisShapeName = "debris_generic.dts"; + debris = StationDebris; +//---------------------------------------- +}; +datablock Staticshapedata(teledestroyed) : teleporter +{ + shapefile = "station_teleport.dts"; + +}; + + +$playerreject = 6; +function Teleporter::onDestroyed(%data, %obj, %prevState) +{ + //set the animations + %obj.playThread(1, "transition"); + %obj.setThreadDir(1, true); + %obj.setDamageState(Destroyed); + %obj.setDatablock(teledestroyed); + %obj.getDataBlock().onLosePowerDisabled(%obj); + +} +//---this is where I create the triggers and put them right over the nexus base's +function teleporter::onEnabled(%data, %obj, %prevState) +{ + echo(%obj); + + %obj.setDatablock(teleporter); + Parent::onEnabled(%data, %obj, %prevState); +} +function teledestroyed::onEnabled(%data, %obj, %prevState) +{ + echo(%obj); + %level = %obj.getdamagelevel(); + %obj.setDatablock(teleporter); + %obj.setdamagelevel(%level); +if(%obj.ispowered()) + { + %obj.playthread(1, "transition"); + %obj.setThreadDir(1, false); + %obj.playThread(0, "ambient"); + %obj.setThreadDir(0, true); + } + else + { + %obj.playThread(0, "transition"); + %obj.setThreadDir(0, false); + } + Parent::onEnabled(%data, %obj, %prevState); +} + +function Teleporter::gainPower(%data, %obj) +{ + + %obj.setDatablock(teleporter); + Parent::gainPower(%data, %obj); + %obj.playthread(1, "transition"); + %obj.setThreadDir(1, false); + %obj.playThread(0, "ambient"); + %obj.setThreadDir(0, true); + } +function Teleporter::losePower(%data, %obj) +{ + %obj.playThread(0, "transition"); + %obj.setThreadDir(0, false); + Parent::losePower(%data, %obj); +} +//---this is where I create the triggers and put them right over the nexus base's +function Teleporter::onAdd(%this, %obj) +{ + Parent::onAdd(%this, %obj); + + %tp = %obj; + %trigger = new Trigger() + { + dataBlock = NewTeleportTrigger; + polyhedron = "-0.75 0.75 0.1 1.5 0.0 0.0 0.0 -1.5 0.0 0.0 0.0 2.3"; + }; + + MissionCleanup.add(%trigger); + if(%tp.logoscale $= "") + %tp.logoscale = " "; + if(%tp.noflag $= "") + %tp.noflag = " "; + if(%tp.oneway $= "") + %tp.oneway = " "; + if(%tp.message $= "") + %tp.message = " "; + + %trigger.setTransform(%tp.getTransform()); + %trigger.targetbase = $lastteleporter; + %trigger.team = %tp.team; + %trigger.motherObj = %tp; // tag relative object + %trigger.sourcebase = %tp; // tag relative object + %trigger.message = %tp.message; + %trigger.logoscale = %tp.logoscale; + %trigger.noflag = %tp.noflag; + %trigger.oneway = %tp.oneway; + %tp.trigger = %trigger; + $lastteleporter.targetbase = %tp.trigger; + $lastteleporter = %tp.trigger; + $linkflag = $linkflag + 1; + if($linkflag == 2) + { + $lastteleporter = 0; + $linkflag = 0; + } + //--------------do we need power?----------------------- + %tp.playThread(1, "ambient"); + %tp.playThread(0, "transition"); + %tp.playThread(0, "ambient"); + + + %newHolo = getTaggedString(game.getTeamSkin(%trigger.team)) @ "Logo"; + %teleholo1 = getTaggedString(game.getTeamSkin(1)) @ "Logo"; + %teleholo2 = getTaggedString(game.getTeamSkin(2)) @ "Logo"; + + %pos = %trigger.position; + + if(%trigger.logoscale !$= " ") + { + if(%tp.team != 0) + { + %holo = new StaticShape() + { + rotation = rotfromtransform(%trigger.gettransform()); + position = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 1; + dataBlock = %newHolo; + scale = %trigger.logoscale; + }; + + + MissionCleanup.add(%holo); + %trigger.sourcebase.holo = %holo; + + } + } + + if(%tp.waypoint !$= "") + { + %wp = new WayPoint() + { + scale = "1 1 1"; + nameTag = %tp.waypoint; + dataBlock = "WayPointMarker"; + lockCount = "0"; + homingCount = "0"; + name = %tp.waypoint; + }; + + MissionCleanup.add(%wp); + %wp.setTransform(%tp.getTransform()); + } + if(%tp.team == 0) + %logostatus = schedule(1000,0,checklogostatus,%tp); +} + + +datablock TriggerData(NewTeleportTrigger) +{ + tickPeriodMS = 30; +}; +//--------------teleporter code here------------------ + + + +function setPlayersPosition2(%data, %obj, %trigger, %colObj) +{ + %vel = getWords(%colObj.getVelocity(), 0, 1) @ " 0"; + if((VectorLen(%vel) < 22) && (%obj.triggeredBy != %colObj)) + { + %pos = %trigger.position; + %colObj.setvelocity("0 0 0"); + %rot = getWords(%colObj.getTransform(),3, 6); + %colObj.setTransform(getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 0.8 @ " " @ %rot); //center player on object + %colObj.setMoveState(true); + return true; + } + return false; +} + +function NewTeleportTrigger::onEnterTrigger(%data, %trigger, %player) +{ + %colObj = %player; + %client = %player.client; + + + if(%player.transported $= "1") // if this player was just transported + { + %player.transported = "0"; + %colObj.setMoveState(false); + %player.llamathread = schedule(3000,0,"playerdamage",%player, true); + return; // then get out or it will never stop + } +//--------------do we have power?----------------------- + if(%trigger.sourcebase.ispowered() == 0) + { + messageClient(%Player.client, 'MsgClient', "\c0Teleporter is not powered."); + return; + } + + +//----------------------disabled?----------------------- +if(%trigger.sourcebase.isDisabled()) + { + messageClient(%colObj.client, 'msgStationDisabled', '\c2Teleporter is disabled.'); + return; + } + + +//--------------are we on the right team?----------------------- + if(%player.team != %trigger.sourcebase.team) + { + if(%trigger.sourcebase.team != 0) + { + %playervelocity = vectorscale(vectornormalize(%player.getvelocity()), 0.5); + %xv = getword(%playervelocity, 0); + %yv = getword(%playervelocity, 1); + %zv = getword(%playervelocity, 2); + + if(getSubStr(%xv,0,1) !$= "-") + %xv = "-" @ %xv + $playerreject; + else + %xv = getsubstr(%xv,1,strlen(%xv - 1)) + $playerreject; + + if(getSubStr(%yv,0,1) !$= "-") + %yv = "-" @ %yv + $playerreject; + else + %yv = getsubstr(%yv,1,strlen(%yv - 1)) + $playerreject; + + if(getSubStr(%zv,0,1) !$= "-") + %zv = "-" @ %zv + $playerreject; + else + %zv = getsubstr(%zv,1,strlen(%zv - 1)) + $playerreject; + + %player.setVelocity(%xv SPC %yv SPC %zv ); + messageClient(%Player.client, 'MsgClient', "\c0Wrong team."); + %player.llamathread = schedule(1000,0,"playerdamage",%player, true); + return; + } + } + + +//------------are we teleporting?----------------------- + if(%trigger.teleporting == "1") + { + %playervelocity = vectorscale(vectornormalize(%player.getvelocity()), 0.5); + %xv = getword(%playervelocity, 0); + %yv = getword(%playervelocity, 1); + %zv = getword(%playervelocity, 2); + + if(getSubStr(%xv,0,1) !$= "-") + %xv = "-" @ %xv + $playerreject; + else + %xv = getsubstr(%xv,1,strlen(%xv - 1)) + $playerreject; + + if(getSubStr(%yv,0,1) !$= "-") + %yv = "-" @ %yv + $playerreject; + else + %yv = getsubstr(%yv,1,strlen(%yv - 1)) + $playerreject; + + if(getSubStr(%zv,0,1) !$= "-") + %zv = "-" @ %zv + $playerreject; + else + %zv = getsubstr(%zv,1,strlen(%zv - 1)) + $playerreject; + + + %player.setVelocity(%xv SPC %yv SPC %zv ); + + messageClient(%Player.client, 'MsgClient', "\c0Teleporter in use."); + return; + } +//-------------is this a oneway teleporter?------------------------ + if(%trigger.oneway == "1") + { + %playervelocity = vectorscale(vectornormalize(%player.getvelocity()), 0.5); + %xv = getword(%playervelocity, 0); + %yv = getword(%playervelocity, 1); + %zv = getword(%playervelocity, 2); + + if(getSubStr(%xv,0,1) !$= "-") + %xv = "-" @ %xv + $playerreject; + else + %xv = getsubstr(%xv,1,strlen(%xv - 1)) + $playerreject; + + if(getSubStr(%yv,0,1) !$= "-") + %yv = "-" @ %yv + $playerreject; + else + %yv = getsubstr(%yv,1,strlen(%yv - 1)) + $playerreject; + + if(getSubStr(%zv,0,1) !$= "-") + %zv = "-" @ %zv + $playerreject; + else + %zv = getsubstr(%zv,1,strlen(%zv - 1)) + $playerreject; + + + %player.setVelocity(%xv SPC %yv SPC %zv ); + + messageClient(%player.client, 'MsgLeaveMissionArea', '\c1This teleporter is oneway only.~wfx/misc/warning_beep.wav'); + return; + } + +//-------------are we teleporting with flag?---------------------------------------- + + %flag = %player.holdingflag; + + if(%player.holdingFlag > 0) + { + if(%trigger.noflag $= "1") + { + if(%flag.team == 1) + %otherTeam = 2; + else + %otherTeam = 1; + game.flagReset(%player.holdingflag); + messageTeam(%otherTeam, 'MsgCTFFlagReturned', '\c2 %1 tried to teleport with the %2 flag.~wfx/misc/flag_return.wav', %client.name, $teamName[%flag.team], %flag.team); + messageTeam(%flag.team, 'MsgCTFFlagReturned', '\c2Your flag was returned.~wfx/misc/flag_return.wav', 0, 0, %flag.team); + messageTeam(0, 'MsgCTFFlagReturned', '\c2The %2 flag was returned to base.~wfx/misc/flag_return.wav', 0, $teamName[%flag.team], %flag.team); + } + } + + setPlayersPosition2(%data, %obj, %trigger, %colObj); + + + + + + + %player.transported = "1"; + + if(%trigger.message !$= " ") + messageClient(%Player.client, 'MsgClient', "\c0 " @ %trigger.message); + + + + %rot = rotFromTransform( %player.getTransform() ); + %player.startfade(500,0,true); + teleporteffect(posfromtransform(%player.gettransform())); + %pos = posFromTransform(%trigger.targetbase.getTransform() ); + + %x = getword(%pos, 0); // left of center + %y = getword(%pos, 1); // "forward" adjustment + %z = getword(%pos, 2) + 0.5; // VERTICLE + %exitpos = %x SPC %y SPC %z; // new position + teleporteffect(%exitpos); + %trigger.teleporting = "1"; + %player.sourcetrigger = %trigger; + %player.desttrigger = %trigger.targetbase; + %player.desttrigger.teleporting = "1"; + %player.schedule(500, "settransform", %exitpos @ " " @ %rot); + %player.schedule(500, "startfade", 500, 0, false); + %colObj.schedule(500, "setmovestate", false); + if(%trigger.sourcebase == %trigger.targetbase) + { + NewTeleportTrigger::onleaveTrigger(%data, %trigger, %player); + NewTeleportTrigger::onEnterTrigger(%data, %trigger, %player); + } + + +//-----hope this works?!?!?-------------------------------------------------- + + +} + +function NewTeleportTrigger::onleaveTrigger(%data, %trigger, %player) +{ +if(%player.team !$= %trigger.team) + cancel(%player.llamaThread); + +if(%player.desttrigger == %trigger) + { + cancel(%player.llamaThread); + %player.sourcetrigger.teleporting = "0"; + %player.desttrigger.teleporting = "0"; + %player.sourcetrigger = "0"; + %player.desttrigger = "0"; + } + + + + +} + +function NewTeleportTrigger::onTickTrigger(%data, %obj) +{ + Game.onTickTrigger(%obj.name, %data, %obj); +} + +function teleporteffect(%position) +{ + %pos = %position; + + %effect1 = new ParticleEmissionDummy() + { + position = %pos; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "doubleTimeEmissionDummy"; + lockCount = "0"; + homingCount = "0"; + emitter = "AABulletExplosionEmitter2"; + velocity = "1"; + }; + %effect2 = new ParticleEmissionDummy() + { + position = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 0.5; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "doubleTimeEmissionDummy"; + lockCount = "0"; + homingCount = "0"; + emitter = "AABulletExplosionEmitter2"; + velocity = "1"; + }; + + %effect3 = new ParticleEmissionDummy() + { + position = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 1; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "doubleTimeEmissionDummy"; + lockCount = "0"; + homingCount = "0"; + emitter = "AABulletExplosionEmitter2"; + velocity = "1"; + }; + %effect4 = new ParticleEmissionDummy() + { + position = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 1.5; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "doubleTimeEmissionDummy"; + lockCount = "0"; + homingCount = "0"; + emitter = "AABulletExplosionEmitter2"; + velocity = "1"; + }; + MissionCleanup.add(%effect1); + MissionCleanup.add(%effect2); + MissionCleanup.add(%effect3); + MissionCleanup.add(%effect4); + %effect1.schedule(2000, "delete"); + %effect2.schedule(2000, "delete"); + %effect3.schedule(2000, "delete"); + %effect4.schedule(2000, "delete"); + +} +function playerdamage(%this, %firstWarning) +{ +%player = %this; + + if (!isObject(%player) || %player.getState() $= "Dead") + { + messageClient(%player.client, 'MsgClient', "\c0Camping on a teleporter is not cool."); + return; + } + + if(%firstwarning) + { + messageClient(%player.client, 'MsgLeaveMissionArea', '\c1Move off the teleporter or take damage.~wfx/misc/warning_beep.wav'); + %player.llamathread = schedule(3000,0,"playerdamage",%this, false); + return; + + } + + + %this.setdamageflash(0.3); + + %this.damage(0, %player.position, 0.04, $DamageType::radiation); + + +%player.llamathread = schedule(1000,0,"playerdamage",%this, false); + + +} + +function checklogostatus(%logostat) +{ + + if(!isObject(%logostat)) + return; + if(%logostat.logoscale $= " ") + return; + + + %status = schedule(1000,0,checklogostatus,%logostat); + %tp = %logostat; + + if(%tp.team != %tp.lastteam) + { + %tp.lastteam = %tp.team; + if(%tp.holo > 0) + %tp.holo.delete(); + + if(%tp.targetbase1 !$= "") + { + if(%tp.team $= "1") + { + %tp.trigger.targetbase = %tp.targetbase1; + } + if(%tp.team $= "2") + { + %tp.trigger.targetbase = %tp.targetbase2; + } + } + + %newHolo = getTaggedString(game.getTeamSkin(%tp.team)) @ "Logo"; + %pos = %tp.trigger.position; + + %holo = new StaticShape() + { + rotation = rotfromtransform(%tp.gettransform()); + position = getWord(%pos,0) @ " " @ getWord(%pos,1) @ " " @ getWord(%pos,2) + 1; + dataBlock = %newHolo; + scale = %tp.logoscale; + }; +// dump the hologram into MissionCleanup + MissionCleanup.add(%holo); +// associate the holo with the teleporter + %tp.holo = %holo; + + + } + + + + + +} + +//Teleport code ends here---------------------------------------- + + diff --git a/missions/rasplands.mis b/missions/rasplands.mis new file mode 100644 index 0000000..c882621 --- /dev/null +++ b/missions/rasplands.mis @@ -0,0 +1,227 @@ +// DisplayName = Rasplands +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// It's not flatland, that's for sure. +// Serverside Construction Mission +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Serverside Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "lush"; + powerCount = "0"; + cdTrack = "6"; + + new MissionArea(MissionArea) { + area = "-1000 -1008 1984 1984"; //"-1000 -1008 1984 1984"; + flightCeiling = "100000"; + flightCeilingRange = "200"; + + locked = "true"; + }; + new Sun() { + position = "1024 512 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.622506 0.622506 -0.474313"; + color = "0.900000 0.900000 1.000000 1.000000"; + ambient = "0.300000 0.400000 0.475000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.6"; + lensFlareIntensity = "0.8"; + frontFlareSize = "300"; + backFlareSize = "500"; + flareColor = "0.200000 0.350000 0.400000 1.000000"; + + //texture2 = "special/LensFlare/flare01"; + locked = "false"; + //texture0 = "special/sunFlare"; + //texture4 = "special/LensFlare/flare03"; + //texture1 = "special/sunFlare02"; + //texture3 = "special/LensFlare/flare02"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + rotation = "0 0 0 0"; + YDimOverSize = "0"; + GraphFile = "Rasp.nav"; + conjoinBowlDev = "20"; + locked = "true"; + scale = "1 1 1"; + coverage = "0"; + position = "0 0 0 1"; + XDimOverSize = "0"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet2"; + terrainFile = "Rasp.ter"; + squareSize = "8"; + emptySquares = ""; + + visibleDistance = "1000"; + locked = "true"; + dObj = "16529 16531"; + position = "-1024 -1024 0"; + hazeDistance = "800"; + }; + new Sky(Sky) { + position = "728 -288 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "100"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.01"; + cloudSpeed2 = "0.02"; + cloudSpeed3 = "0.03"; + visibleDistance = "750"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = 97/255 SPC 113/255 SPC 137/255 SPC "1.000000"; + fogDistance = "350"; + fogColor = 97/255 SPC 113/255 SPC 137/255 SPC "1.000000"; + fogVolume1 = "33 -20 103.596"; + fogVolume2 = "0 0 0";//134.5 132.2796 135"; + fogVolume3 = "0 0 0"; + materialList = "nef_Surreal1.dml"; + windVelocity = "0 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "10.000000 128.000000 48.000000 0.000000"; + fogVolumeColor2 = "5.000000 40.000000 20.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 3344 6.27351e-39"; + high_fogVolume2 = "-1 3.51342e-39 2.46878e+27"; + high_fogVolume3 = "-1 5.3766e+08 -3.21499e+06"; + + locked = "false"; + cloudSpeed0 = "0.000150 0.000050"; + }; + new WaterBlock() { + position = "-1024 -816 -20"; + rotation = "1 0 0 0"; + scale = "2048 2048 123.25"; + liquidType = "OceanWater"; + density = "1"; + viscosity = "5"; + waveMagnitude = "1"; + surfaceTexture = "LiquidTiles/BlueWater"; + surfaceOpacity = "0.3"; + envMapTexture = "nef/skies/Surreal_Cloud2"; + envMapIntensity = "0.4"; + removeWetEdges = "0"; + + locked = "false"; + hidden = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(SpawnSpheres) { + + providesPower = "1"; + powerCount = "1"; + + new SpawnSphere() { + position = "174.273 43.6931 183"; + rotation = "0 0 1 177.409"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "1"; + homingCount = "0"; + radius = "90"; + sphereWeight = "100"; + indoorWeight = "25"; + outdoorWeight = "75"; + team = "1"; + }; + }; + }; + + new SimGroup(team0) { + + powerCount = "0"; + + new SimGroup(base0) { + + providesPower = "1"; + powerCount = "1"; + + new TSStatic() { + position = "119.966 217.776 179.222"; + rotation = "-0.0797224 0.0693312 -0.994403 98.2941"; + scale = "2 2 2"; + shapeName = "porg6.dts"; + + locked = "true"; + team = "0"; + }; + + new StaticShape(VehicleStation) { + position = "174.273 43.6931 180"; + rotation = "0 0 1 233.95"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + lastState = "1"; + Ready = "1"; + Target = "33"; + team = "0"; + station = "9838"; + }; + new StaticShape(InventoryStation) { + position = "157.583 42.9802 183.128"; + rotation = "0.00252565 0.0049819 0.999984 232.985"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + lastState = "1"; + Target = "34"; + Trigger = "9825"; + team = "0"; + }; + }; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "-605.649 -381.017 135.806"; + rotation = "0.00475689 -0.0714684 0.997432 172.404"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + locked = "true"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/slapmydash infected.mis b/missions/slapmydash infected.mis new file mode 100644 index 0000000..92bc93f --- /dev/null +++ b/missions/slapmydash infected.mis @@ -0,0 +1,205 @@ +// DisplayName = Flatdash Doomsday +// MissionTypes = Infection + +//--- Mission Quote Begin --- +// Flat. Dash. Infected! +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Serverside Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "lush"; + cdTrack = "2"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-825 -900 1200 1500"; + flightCeiling = "5000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sun(Sun) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.622506 0.622506 -0.474313"; + color = "0.500000 0.500000 0.500000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + conjoinBowlDev = "20"; + XDimOverSize = "0"; + position = "-1024 -1024 0 1"; + squareSize = "75"; + YDimOverSize = "0"; + rotation = "0 0 0 0"; + GraphFile = "slapmydash.nav"; + locked = "true"; + scale = "1 1 1"; + coverage = "0"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Slapdash.ter"; + squareSize = "75"; + emptySquares = "94579 99875"; + + position = "-1024 -1024 0"; + hazeDistance = "250"; + visibleDistance = "1200"; + locked = "true"; + }; + new Sky(Sky) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0012"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.390000 0.390000 0.390000 0.000000"; + fogDistance = "600"; + fogColor = "0.300000 0.300000 0.300000 1.000000"; + fogVolume1 = "300 0 160"; + fogVolume2 = "8000 160 2500"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "0 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 -0.040112"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.742938"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + locked = "true"; + cloudSpeed0 = "0.000503 0.000020"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "1"; + providesPower = "1"; + + new SpawnSphere() { + position = "-60.1702 -231.369 129.675"; + rotation = "0 0 1 177.409"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "1"; + homingCount = "0"; + radius = "30"; + sphereWeight = "100"; + indoorWeight = "25"; + outdoorWeight = "75"; + + team = "1"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + + new SimGroup(base0) { + + powerCount = "1"; + providesPower = "1"; + + new TSStatic() { + position = "-16.5982 -360.09 128.145"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + team = "0"; + locked = "true"; + }; + new StaticShape(VehicleStation) { + position = "-62.3581 -259.598 126.2"; + rotation = "0 0 1 0.573347"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + Ready = "1"; + lastState = "1"; + station = "17855"; + locked = "true"; + Target = "33"; + }; + new StaticShape(InventoryStation) { + position = "-61.7752 -222.35 129.128"; + rotation = "-0.826342 0.00281586 -0.563161 0.69323"; + scale = "1 1 1"; + nameTag = "\x011096"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + Trigger = "17850"; + team = "0"; + lastState = "1"; + locked = "true"; + Target = "34"; + }; + }; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(TreeCam) { + position = "-11.8305 -349.906 139.298"; + rotation = "0 0 -1 16.6157"; + scale = "1 0.739761 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/slapmydash night.mis b/missions/slapmydash night.mis new file mode 100644 index 0000000..344a73d --- /dev/null +++ b/missions/slapmydash night.mis @@ -0,0 +1,185 @@ +// DisplayName = Flatdash Night +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// Flat. Dash. Build! +// Serverside Construction Mission +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Serverside Construction Mission +//--- Mission String End --- + + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + cdTrack = "2"; + CTF_scoreLimit = "4"; + CTF_timeLimit = "25"; + musicTrack = "lush"; + + new MissionArea(MissionArea) { + area = "-825 -900 1200 1500"; + flightCeiling = "5000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "0.000000 0.000000 0.000000 1.000000"; + ambient = "0.300000 0.150000 0.000000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + scale = "1 1 1"; + YDimOverSize = "0"; + locked = "true"; + conjoinBowlDev = "20"; + coverage = "0"; + position = "0 0 0 1"; + GraphFile = "Slapdash.nav"; + XDimOverSize = "0"; + rotation = "0 0 0 0"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Slapdash.ter"; + squareSize = "75"; + emptySquares = "94579 99875"; + + visibleDistance = "1200"; + locked = "true"; + hazeDistance = "250"; + position = "-1024 -1024 0"; + }; + + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.600000 0.160000 0.000000 0.000000"; + fogDistance = "750"; + fogColor = "0.250000 0.100000 0.050000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "300 101 110"; + fogVolume3 = "400 110 150"; + materialList = "sky_lush_morestars.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "-81.6634 -286.438 134.475"; + rotation = "0 0 1 19.0906"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "85"; + sphereWeight = "100"; + indoorWeight = "0"; + outdoorWeight = "100"; + + locked = "true"; + }; + }; + new SimGroup(base0) { + providesPower = "1"; + new TSStatic() { + position = "-16.5982 -360.09 128.145"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + locked = "true"; + team = "0"; + }; + new StaticShape(VehicleStation) { + position = "-62.3581 -259.598 126.2"; + rotation = "0 0 1 0.573347"; + scale = "1 1 1"; + team = "0"; + dataBlock = "StationVehiclePad"; + locked = "true"; + station = "10132"; + }; + new StaticShape(InventoryStation) { + position = "-61.7752 -222.35 129.128"; + rotation = "-0.826342 0.00281586 -0.563161 0.69323"; + scale = "1 1 1"; + nameTag = "Construction"; + dataBlock = "StationInventory"; + Trigger = "10053"; + team = "0"; + locked = "true"; + }; + + }; + }; + new SimGroup(team0) { + + powerCount = "0"; + + new SimGroup(AIObjectives) { + + powerCount = "0"; + }; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/slapmydash.mis b/missions/slapmydash.mis new file mode 100644 index 0000000..e47ba28 --- /dev/null +++ b/missions/slapmydash.mis @@ -0,0 +1,209 @@ +// DisplayName = Flatdash +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// Flat. Dash. Build! +// Serverside Construction Mission +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Serverside Construction Mission +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + musicTrack = "lush"; + cdTrack = "2"; + powerCount = "0"; + + new MissionArea(MissionArea) { + area = "-825 -900 1200 1500"; + flightCeiling = "5000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new Sun(Sun) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.622506 0.622506 -0.474313"; + color = "0.800000 0.800000 0.800000 1.000000"; + ambient = "0.400000 0.400000 0.400000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "70"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + conjoinBowlDev = "20"; + locked = "true"; + coverage = "0"; + scale = "1 1 1"; + GraphFile = "slapmydash.nav"; + squareSize = "75"; + XDimOverSize = "0"; + position = "-1024 -1024 0 1"; + YDimOverSize = "0"; + rotation = "0 0 0 0"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "Slapdash.ter"; + squareSize = "75"; + emptySquares = "94579 99875"; + + locked = "true"; + hazeDistance = "250"; + position = "-1024 -1024 0"; + visibleDistance = "1200"; + }; + + new Sky(Sky) { + position = "-1024 -1024 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0012"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.390000 0.390000 0.390000 0.000000"; + fogDistance = "750"; + fogColor = "0.500000 0.500000 0.500000 1.000000"; + fogVolume1 = "0 0 0"; + fogVolume2 = "100 100 120"; + fogVolume3 = "0 0 0"; + materialList = "Lush_l4.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 -0.040112"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.742938"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + locked = "true"; + cloudSpeed0 = "0.000503 0.000020"; + }; + + + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(SpawnSpheres) { + + providesPower = "1"; + powerCount = "1"; + + new SpawnSphere() { + position = "-60.1702 -231.369 129.675"; + rotation = "0 0 1 177.409"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "1"; + homingCount = "0"; + radius = "30"; + sphereWeight = "100"; + indoorWeight = "25"; + outdoorWeight = "75"; + team = "1"; + }; + }; + }; + + new SimGroup(team0) { + + powerCount = "0"; + + new SimGroup(base0) { + + providesPower = "1"; + powerCount = "1"; + + new TSStatic() { + position = "-16.5982 -360.09 128.145"; + rotation = "-0 0 -1 16.0428"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "true"; + team = "0"; + }; + new StaticShape(VehicleStation) { + position = "-62.3581 -259.598 126.2"; + rotation = "0 0 1 0.573347"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + lastState = "1"; + Ready = "1"; + Target = "33"; + team = "0"; + station = "9838"; + }; + new StaticShape(InventoryStation) { + position = "-61.7752 -222.35 129.128"; + rotation = "-0.826342 0.00281586 -0.563161 0.69323"; + scale = "1 1 1"; + nameTag = "\x01792"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + locked = "true"; + lastState = "1"; + Target = "34"; + Trigger = "9825"; + team = "0"; + }; + }; + }; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera(TreeCam) { + position = "-11.8305 -349.906 139.298"; + rotation = "0 0 1 -16.6157"; + scale = "1 0.739761 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + }; + }; + +}; +//--- OBJECT WRITE END --- diff --git a/missions/z0r Infected.mis b/missions/z0r Infected.mis new file mode 100644 index 0000000..51db65d --- /dev/null +++ b/missions/z0r Infected.mis @@ -0,0 +1,480 @@ +// DisplayName = z0r Extinction +// MissionTypes = Infection + +//--- Mission Quote Begin --- +// "Don't forget to bring a weapon." +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Map remade by: Blnukem. +//Mission touched up by Eolk +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + cdTrack = "6"; + musicTrack = "desert"; + + new MissionArea(MissionArea) { + area = "-2000 -1024 5008 5008"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "424.8 -118 110.4"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "25"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "1"; + providesPower = "1"; + + new StaticShape(InventoryStation) { + position = "487.481 -92.0753 117"; + rotation = "0 0 1 46.9369"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + notReady = "1"; + team = "0"; + Trigger = "4353"; + lastState = "1"; + Target = "33"; + locked = "true"; + inUse = "Down"; + }; + new StaticShape(VehicleStation) { + position = "467.377 -119.804 114.4"; + rotation = "0 0 1 89.9087"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "0"; + lastState = "1"; + Target = "34"; + station = "4424"; + locked = "true"; + inUse = "Down"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "z0r Revisited.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + GraphFile = "Flatland.nav"; + coverage = "0"; + position = "0 0 0 1"; + rotation = "0 0 0 0"; + locked = "true"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "1"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "750"; + fogColor = "0.500000 0.500000 0.500000 1.000000"; + fogVolume1 = "500 0 100"; + fogVolume2 = "800 100 200"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_blue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new WaterBlock() { + position = "248 -184 -22.9"; + rotation = "1 0 0 0"; + scale = "2048 2048 69"; + liquidType = "Water"; + density = "1"; + viscosity = "10"; + waveMagnitude = "1"; + surfaceTexture = "terrain/wateregypt1"; + surfaceOpacity = "0.7"; + envMapTexture = "LiquidTiles/archipelago_emap_cloudsground"; + envMapIntensity = "0.4"; + removeWetEdges = "0"; + + params3 = "1.21 -0.61 0.13 -0.33"; + params1 = "0.63 -2.41 0.33 0.21"; + floodFill = "1"; + textureSize = "32 32"; + seedPoints = "0 0 1 0 1 1 0 1"; + params0 = "0.32 -0.67 0.066 0.5"; + params2 = "0.39 0.39 0.2 0.133"; + extent = "100 100 10"; + }; + new InteriorInstance() { + position = "-392.801 951.662 50.25"; + rotation = "0 0 -1 119.358"; + scale = "1 1 1"; + interiorFile = "bwall3.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-328.172 972.489 50.25"; + rotation = "0 0 -1 84.4076"; + scale = "1 1 1"; + interiorFile = "bwall3.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-298.6 971.021 50.25"; + rotation = "0 0 1 89.5639"; + scale = "1 1 1"; + interiorFile = "bwall2.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-287.364 928.048 50.25"; + rotation = "0 0 -1 20.6264"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-412.073 912.839 50.25"; + rotation = "0 0 1 13.1781"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-248.398 908.481 50.25"; + rotation = "0 0 -1 115.165"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-300.011 970.864 50.15"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-404.568 944.272 49.9038"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-318.88 825.744 50.3004"; + rotation = "0 0 1 73.3386"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-283.637 835.662 50.5218"; + rotation = "0 0 1 46.4095"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-386.629 847.426 50.25"; + rotation = "0 0 1 136.364"; + scale = "1 6 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-419.162 881.984 48.95"; + rotation = "0 0 1 36.0963"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-355.926 815.277 50.657"; + rotation = "0 0 1 36.0963"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-274.462 897.382 50.1019"; + rotation = "0 0 -1 14.3239"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-470.556 752.438 25.6702"; + rotation = "0 0 1 37.2423"; + scale = "1 2 1"; + interiorFile = "sbrdg1.dif"; + showTerrainInside = "0"; + }; + new TSStatic() { + position = "-81.24 -128.982 50.9634"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg1.dts"; + }; + new TSStatic() { + position = "40.3252 25.3226 66.0471"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-64.8647 -432.422 52.1065"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-181.594 225.917 48.1096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-347.044 977.892 50.9016"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + }; + new TSStatic() { + position = "-381.402 963.728 50.7202"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + }; + new InteriorInstance() { + position = "-251.899 823.72 50.3477"; + rotation = "0 0 1 111.154"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-204.523 841.59 50.4881"; + rotation = "-0.0047231 0.027515 0.99961 20.6345"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-204.469 898.333 50.6603"; + rotation = "0 0 -1 29.2212"; + scale = "1 3.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-218.993 923.022 50.3538"; + rotation = "0 0 -1 14.3239"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new InteriorInstance() { + position = "112.436 -216.188 49.1219"; + rotation = "0 0 1 180.664"; + scale = "1 2 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.399 -288.619 49.121"; + rotation = "0 0 1 180.664"; + scale = "1 2 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.357 -235.178 48.8669"; + rotation = "0 0 1 90.5273"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.37 -269.598 48.8663"; + rotation = "0 0 1 90.5273"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "275.162 -46.5505 83.5891"; + rotation = "0 0 -1 114.592"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "212.234 -116.814 83.589"; + rotation = "0 0 1 213.322"; + scale = "1 5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "329.178 -109.27 84.3373"; + rotation = "0.00840956 -0.00339846 0.999959 179.914"; + scale = "2 2 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "310.428 -89.0741 65.9134"; + rotation = "0.266047 0.451928 0.851458 94.9182"; + scale = "2 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "318.148 -128.214 78.5252"; + rotation = "-0.206292 0.278726 -0.937953 116.926"; + scale = "1 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "370.976 -110.695 97.9"; + rotation = "0 0 1 187.539"; + scale = "1 4.5 2"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new TSStatic() { + position = "-167.625 -307.853 48.318"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/missions/z0r Revisited.mis b/missions/z0r Revisited.mis new file mode 100644 index 0000000..f961e38 --- /dev/null +++ b/missions/z0r Revisited.mis @@ -0,0 +1,488 @@ +// DisplayName = z0r Revisited +// MissionTypes = Construction Infection + +//--- Mission Quote Begin --- +// "Don't forget to bring a towl!" +//--- Mission Quote End --- + +//--- Mission String Begin --- +//Thanks to the makers of T2RPG Ironsphere for the original terrain. +//Modified and expanded upon by Hobthebob +//--- Mission String End --- + +//--- OBJECT WRITE BEGIN --- +new SimGroup(MissionGroup) { + + powerCount = "0"; + cdTrack = "6"; + musicTrack = "desert"; + + new MissionArea(MissionArea) { + area = "-2000 -1024 5008 5008"; + flightCeiling = "4000"; + flightCeilingRange = "20"; + + locked = "true"; + }; + new SimGroup(Teams) { + + powerCount = "0"; + + new SimGroup(Team1) { + + powerCount = "0"; + + new SimGroup(spawnspheres) { + + powerCount = "0"; + + new SpawnSphere() { + position = "424.8 -118 110.4"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "SpawnSphereMarker"; + lockCount = "0"; + homingCount = "0"; + radius = "25"; + sphereWeight = "100"; + indoorWeight = "100"; + outdoorWeight = "100"; + }; + }; + }; + new SimGroup(team0) { + + powerCount = "1"; + providesPower = "1"; + + new StaticShape(InventoryStation) { + position = "487.481 -92.0753 117"; + rotation = "0 0 1 46.9369"; + scale = "1 1 1"; + dataBlock = "StationInventory"; + lockCount = "0"; + homingCount = "0"; + + notReady = "1"; + team = "0"; + Trigger = "4353"; + lastState = "1"; + Target = "33"; + locked = "true"; + inUse = "Down"; + }; + new StaticShape(VehicleStation) { + position = "467.377 -119.804 114.4"; + rotation = "0 0 1 89.9087"; + scale = "1 1 1"; + dataBlock = "StationVehiclePad"; + lockCount = "0"; + homingCount = "0"; + + Ready = "1"; + team = "0"; + lastState = "1"; + Target = "34"; + station = "4424"; + locked = "true"; + inUse = "Down"; + }; + }; + }; + new Sun(Sun) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + direction = "0.57735 0.57735 -0.57735"; + color = "1.000000 1.000000 1.000000 1.000000"; + ambient = "0.500000 0.500000 0.500000 1.000000"; + texture[0] = "special/sunFlare"; + texture[1] = "special/sunFlare02"; + texture[2] = "special/LensFlare/flare01"; + texture[3] = "special/LensFlare/flare02"; + texture[4] = "special/LensFlare/flare03"; + lensFlareScale = "0.7"; + lensFlareIntensity = "1"; + frontFlareSize = "300"; + backFlareSize = "450"; + flareColor = "1.000000 1.000000 1.000000 1.000000"; + + locked = "true"; + }; + new TerrainBlock(Terrain) { + rotation = "1 0 0 0"; + scale = "1 1 1"; + detailTexture = "details/desertdet1"; + terrainFile = "z0r Revisited.ter"; + squareSize = "8"; + + position = "-1024 -1024 0"; + locked = "true"; + }; + new NavigationGraph(NavGraph) { + conjoinAngleDev = "50"; + cullDensity = "0.3"; + customArea = "0 0 0 0"; + + GraphFile = "Flatland.nav"; + coverage = "0"; + position = "0 0 0 1"; + rotation = "0 0 0 0"; + locked = "true"; + conjoinBowlDev = "20"; + scale = "1 1 1"; + }; + new SimGroup(ObserverDropPoints) { + + powerCount = "0"; + + new Camera() { + position = "0 0 200"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + dataBlock = "Observer"; + lockCount = "0"; + homingCount = "0"; + + team = "0"; + locked = "true"; + }; + }; + new Sky(Sky) { + position = "0 0 0"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + cloudHeightPer[0] = "0.349971"; + cloudHeightPer[1] = "0.25"; + cloudHeightPer[2] = "0.199973"; + cloudSpeed1 = "0.0001"; + cloudSpeed2 = "0.0002"; + cloudSpeed3 = "0.0003"; + visibleDistance = "800"; + useSkyTextures = "0"; + renderBottomTexture = "0"; + SkySolidColor = "0.250000 0.750000 1.000000 1.000000"; + fogDistance = "750"; + fogColor = "0.600000 0.600000 0.600000 1.000000"; + fogVolume1 = "200 99 101"; + fogVolume2 = "0 0 0"; + fogVolume3 = "0 0 0"; + materialList = "sky_desert_blue.dml"; + windVelocity = "1 0 0"; + windEffectPrecipitation = "0"; + fogVolumeColor1 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor2 = "128.000000 128.000000 128.000000 0.000000"; + fogVolumeColor3 = "128.000000 128.000000 128.000000 0.000000"; + high_visibleDistance = "-1"; + high_fogDistance = "-1"; + high_fogVolume1 = "-1 2.33105e-09 6.40969e-10"; + high_fogVolume2 = "-1 1.07461e-38 0"; + high_fogVolume3 = "-1 7.9874e-44 5.9061e-32"; + + cloudSpeed0 = "0.000503 0.000020"; + locked = "true"; + }; + new WaterBlock() { + position = "248 -184 -22.9"; + rotation = "1 0 0 0"; + scale = "2048 2048 69"; + liquidType = "Water"; + density = "1"; + viscosity = "10"; + waveMagnitude = "1"; + surfaceTexture = "terrain/wateregypt1"; + surfaceOpacity = "0.7"; + envMapTexture = "LiquidTiles/archipelago_emap_cloudsground"; + envMapIntensity = "0.4"; + removeWetEdges = "0"; + + params3 = "1.21 -0.61 0.13 -0.33"; + params1 = "0.63 -2.41 0.33 0.21"; + floodFill = "1"; + textureSize = "32 32"; + seedPoints = "0 0 1 0 1 1 0 1"; + params0 = "0.32 -0.67 0.066 0.5"; + params2 = "0.39 0.39 0.2 0.133"; + extent = "100 100 10"; + }; + new InteriorInstance() { + position = "-392.801 951.662 50.25"; + rotation = "0 0 -1 119.358"; + scale = "1 1 1"; + interiorFile = "bwall3.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-328.172 972.489 50.25"; + rotation = "0 0 -1 84.4076"; + scale = "1 1 1"; + interiorFile = "bwall3.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-298.6 971.021 50.25"; + rotation = "0 0 1 89.5639"; + scale = "1 1 1"; + interiorFile = "bwall2.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-287.364 928.048 50.25"; + rotation = "0 0 -1 20.6264"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-412.073 912.839 50.25"; + rotation = "0 0 1 13.1781"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-248.398 908.481 50.25"; + rotation = "0 0 -1 115.165"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-300.011 970.864 50.15"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-404.568 944.272 49.9038"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-318.88 825.744 50.3004"; + rotation = "0 0 1 73.3386"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-283.637 835.662 50.5218"; + rotation = "0 0 1 46.4095"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-386.629 847.426 50.25"; + rotation = "0 0 1 136.364"; + scale = "1 6 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-419.162 881.984 48.95"; + rotation = "0 0 1 36.0963"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-355.926 815.277 50.657"; + rotation = "0 0 1 36.0963"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-274.462 897.382 50.1019"; + rotation = "0 0 -1 14.3239"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-470.556 752.438 25.6702"; + rotation = "0 0 1 37.2423"; + scale = "1 2 1"; + interiorFile = "sbrdg1.dif"; + showTerrainInside = "0"; + }; + new TSStatic() { + position = "-81.24 -128.982 50.9634"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg1.dts"; + }; + new TSStatic() { + position = "40.3252 25.3226 66.0471"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-64.8647 -432.422 52.1065"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg17.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-181.594 225.917 48.1096"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; + new TSStatic() { + position = "-347.044 977.892 50.9016"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg19.dts"; + }; + new TSStatic() { + position = "-381.402 963.728 50.7202"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg18.dts"; + }; + new InteriorInstance() { + position = "-251.899 823.72 50.3477"; + rotation = "0 0 1 111.154"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-204.523 841.59 50.4881"; + rotation = "-0.0047231 0.027515 0.99961 20.6345"; + scale = "1 4.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-204.469 898.333 50.6603"; + rotation = "0 0 -1 29.2212"; + scale = "1 3.5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "-218.993 923.022 50.3538"; + rotation = "0 0 -1 14.3239"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + }; + new FileObject() { + }; + new FileObject() { + }; + new InteriorInstance() { + position = "112.436 -216.188 49.1219"; + rotation = "0 0 1 180.664"; + scale = "1 2 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.399 -288.619 49.121"; + rotation = "0 0 1 180.664"; + scale = "1 2 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.357 -235.178 48.8669"; + rotation = "0 0 1 90.5273"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "112.37 -269.598 48.8663"; + rotation = "0 0 1 90.5273"; + scale = "1 1 1"; + interiorFile = "bwall4.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "275.162 -46.5505 83.5891"; + rotation = "0 0 -1 114.592"; + scale = "1 4 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new InteriorInstance() { + position = "212.234 -116.814 83.589"; + rotation = "0 0 1 213.322"; + scale = "1 5 1"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new FileObject() { + }; + new InteriorInstance() { + position = "329.178 -109.27 84.3373"; + rotation = "0.00840956 -0.00339846 0.999959 179.914"; + scale = "2 2 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "310.428 -89.0741 65.9134"; + rotation = "0.266047 0.451928 0.851458 94.9182"; + scale = "2 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "318.148 -128.214 78.5252"; + rotation = "-0.206292 0.278726 -0.937953 116.926"; + scale = "1 1 1"; + interiorFile = "brockc.dif"; + showTerrainInside = "0"; + }; + new InteriorInstance() { + position = "370.976 -110.695 97.9"; + rotation = "0 0 1 187.539"; + scale = "1 4.5 2"; + interiorFile = "bwall1.dif"; + showTerrainInside = "0"; + + locked = "true"; + }; + new TSStatic() { + position = "-167.625 -307.853 48.318"; + rotation = "1 0 0 0"; + scale = "1 1 1"; + shapeName = "borg16.dts"; + + locked = "true"; + }; +}; +//--- OBJECT WRITE END --- diff --git a/prefs/ServerPrefs.cs b/prefs/ServerPrefs.cs new file mode 100644 index 0000000..efae6a0 --- /dev/null +++ b/prefs/ServerPrefs.cs @@ -0,0 +1,165 @@ +$Host::ACCMChatLogging = 1; +$Host::ACCMConnectionLogging = 1; +$Host::ACCMEchoChat = 1; +$Host::AdminOnlyFadeObject = 0; +$Host::allowAdminPlayerVotes = "1"; +$Host::AllowKeeperPlayerVotes = 1; +$Host::AllowMapScript = 1; +$Host::AllowUnderground = 0; +$Host::AntidoteStationMaxAntidotes = 20; +$Host::BanTime = 1800; +$Host::BotCount = 2; +$Host::BotsEnabled = "0"; +$Host::Cascade = 0; +$Host::ClientSaving = 1; +$Host::CRCTextures = 0; +$Host::Dedicated = 1; +$Host::ExpertMode = 1; +$Host::FloodProtectionEnabled = 1; +$Host::GameName = "Tribes 2 Server"; +$Host::Hazard::Enabled = 0; +$Host::HiVisibility = "0"; +$Host::holoName1 = "Storm"; +$Host::holoName2 = "Inferno"; +$Host::holoName3 = "Starwolf"; +$Host::holoName4 = "DSword"; +$Host::holoName5 = "BloodEagle"; +$Host::holoName6 = "Harbinger"; +$Host::Info = "This is a Tribes 2 Server."; +$Host::InvincibleArmors = 0; +$Host::InvincibleDeployables = 1; +$Host::KeepersGetMakerAbility = 1; +$Host::KickBanTime = 300; +$Host::LockedTeams = "0"; +$Host::Map = "ACCMFlatland"; +$Host::MapPlayerLimitsAbominable_CnH = "-1 -1"; +$Host::MapPlayerLimitsAgentsOfFortune_TeamHunters = "-1 32"; +$Host::MapPlayerLimitsAlcatraz_Siege = "-1 48"; +$Host::MapPlayerLimitsArchipelago_CTF = "16 -1"; +$Host::MapPlayerLimitsAshesToAshes_CnH = "16 -1"; +$Host::MapPlayerLimitsBeggarsRun_CTF = "-1 32"; +$Host::MapPlayerLimitsCaldera_Siege = "-1 48"; +$Host::MapPlayerLimitsCasernCavite_Bounty = "-1 32"; +$Host::MapPlayerLimitsCasernCavite_DM = "-1 32"; +$Host::MapPlayerLimitsCasernCavite_Hunters = "-1 32"; +$Host::MapPlayerLimitsDamnation_CTF = "-1 32"; +$Host::MapPlayerLimitsDeathBirdsFly_CTF = "8 -1"; +$Host::MapPlayerLimitsDesiccator_CTF = "-1 -1"; +$Host::MapPlayerLimitsDustToDust_CTF = "-1 32"; +$Host::MapPlayerLimitsDustToDust_Hunters = "-1 32"; +$Host::MapPlayerLimitsDustToDust_TeamHunters = "-1 32"; +$Host::MapPlayerLimitsEquinox_CnH = "-1 -1"; +$Host::MapPlayerLimitsEquinox_DM = "-1 32"; +$Host::MapPlayerLimitsEscalade_Bounty = "16 32"; +$Host::MapPlayerLimitsEscalade_DM = "16 -1"; +$Host::MapPlayerLimitsEscalade_Hunters = "8 -1"; +$Host::MapPlayerLimitsEscalade_Rabbit = "16 -1"; +$Host::MapPlayerLimitsEscalade_TeamHunters = "8 -1"; +$Host::MapPlayerLimitsFirestorm_CnH = "-1 24"; +$Host::MapPlayerLimitsFirestorm_CTF = "-1 24"; +$Host::MapPlayerLimitsFlashpoint_CnH = "-1 -1"; +$Host::MapPlayerLimitsGauntlet_Siege = "-1 32"; +$Host::MapPlayerLimitsGehenna_Hunters = "-1 -1"; +$Host::MapPlayerLimitsGehenna_TeamHunters = "-1 -1"; +$Host::MapPlayerLimitsIcebound_Siege = "-1 -1"; +$Host::MapPlayerLimitsInsalubria_CnH = "-1 32"; +$Host::MapPlayerLimitsJacobsLadder_CnH = "-1 -1"; +$Host::MapPlayerLimitsKatabatic_CTF = "-1 48"; +$Host::MapPlayerLimitsMasada_Siege = "-1 32"; +$Host::MapPlayerLimitsMinotaur_CTF = "-1 32"; +$Host::MapPlayerLimitsMyrkwood_DM = "-1 32"; +$Host::MapPlayerLimitsMyrkwood_Hunters = "-1 32"; +$Host::MapPlayerLimitsMyrkwood_Rabbit = "-1 32"; +$Host::MapPlayerLimitsOasis_DM = "-1 32"; +$Host::MapPlayerLimitsOverreach_CnH = "8 -1"; +$Host::MapPlayerLimitsQuagmire_CTF = "-1 -1"; +$Host::MapPlayerLimitsRasp_Bounty = "-1 32"; +$Host::MapPlayerLimitsRasp_TeamHunters = "-1 32"; +$Host::MapPlayerLimitsRecalescence_CTF = "16 -1"; +$Host::MapPlayerLimitsRespite_Siege = "-1 32"; +$Host::MapPlayerLimitsReversion_CTF = "-1 -1"; +$Host::MapPlayerLimitsRimehold_Hunters = "8 -1"; +$Host::MapPlayerLimitsRiverdance_CTF = "-1 -1"; +$Host::MapPlayerLimitsSanctuary_CTF = "-1 -1"; +$Host::MapPlayerLimitsSirocco_CnH = "8 -1"; +$Host::MapPlayerLimitsSlapdash_CTF = "-1 -1"; +$Host::MapPlayerLimitsSunDried_Bounty = "8 -1"; +$Host::MapPlayerLimitsSunDried_DM = "8 -1"; +$Host::MapPlayerLimitsTalus_Bounty = "-1 32"; +$Host::MapPlayerLimitsThinIce_CTF = "-1 -1"; +$Host::MapPlayerLimitsTombstone_CTF = "-1 -1"; +$Host::MapPlayerLimitsUltimaThule_Siege = "8 -1"; +$Host::MapPlayerLimitsUnderhill_Bounty = "-1 32"; +$Host::MapPlayerLimitsUnderhill_DM = "-1 -1"; +$Host::MapPlayerLimitsWhiteout_Bounty = "8 -1"; +$Host::MapPlayerLimitsWhiteout_DM = "8 -1"; +$Host::MarkDnDObjectives = 1; +$Host::MaxBotDifficulty = "0.75"; +$Host::MaxClientSaves = 10; +$Host::MaxMessageLen = 120; +$Host::MaxPieceVote = 400; +$Host::MaxPlayers = "64"; +$Host::MaxZombies = 35; +$Host::MinBotDifficulty = "0.5"; +$Host::MissionType = "Infection"; +$Host::NoAnnoyingVoiceChatSpam = 0; +$Host::NoDeployEffects = 1; +$Host::NoInfection = 0; +$Host::NoPulseSCG = 0; +$Host::NoSmurfs = 1; +$Host::ObserversCannotChat = 1; +$Host::OnlyOwnerCascade = 1; +$Host::OnlyOwnerCubicReplace = 1; +$Host::OnlyOwnerDeconstruct = 1; +$Host::OnlyOwnerRotate = 1; +$Host::PlayerRespawnTimeout = "60"; +$Host::Port = "28000"; +$Host::Prison::DeploySpam = 0; +$Host::Prison::DeploySpamCheckTimeMS = 1000; +$Host::Prison::DeploySpamMaxTime = 300; +$Host::Prison::DeploySpamMultiply = 1; +$Host::Prison::DeploySpamRemoveRecentMS = 15000; +$Host::Prison::DeploySpamResetWarnCountTime = 30; +$Host::Prison::DeploySpamTime = 60; +$Host::Prison::DeploySpamWarnings = 10; +$Host::Prison::Enabled = 0; +$Host::Prison::JailMode = 0; +$Host::Prison::Kill = 0; +$Host::Prison::KillTime = 120; +$Host::Prison::ReleaseMode = 1; +$Host::Prison::TeamKill = 0; +$Host::Purebuild = 1; +$Host::PureServer = 1; +$Host::RepairPatchOnDeath = 0; +$Host::SADProtection = 1; +$Host::SatchelChargeEnabled = 0; +$Host::SentinelProtection = 0; +$Host::Siege::Halftime = 20000; +$host::slowmodist = 100; +$Host::StationHoldTime = 1600; +$Host::SuperAdminList = "2000181"; +$Host::SuperAdminPassword = "gay"; +$Host::TeamDamageOn = "1"; +$Host::TeamName0 = "Unassigned"; +$Host::TeamName1 = "Storm"; +$Host::TeamName2 = "Inferno"; +$Host::TeamName3 = "Starwolf"; +$Host::TeamName4 = "Diamond Sword"; +$Host::TeamName5 = "Blood Eagle"; +$Host::TeamName6 = "Phoenix"; +$Host::TeamSkin0 = "blank"; +$Host::TeamSkin1 = "base"; +$Host::TeamSkin2 = "baseb"; +$Host::TeamSkin3 = "swolf"; +$Host::TeamSkin4 = "dsword"; +$Host::TeamSkin5 = "beagle"; +$Host::TeamSkin6 = "cotp"; +$Host::TimeLimit = "10080"; +$Host::TN::beat = 3; +$Host::TN::echo = 1; +$Host::TournamentMode = "0"; +$Host::Vehicles = 1; +$Host::VotePassPercent = "60"; +$Host::VoteSpread = 20; +$Host::VoteTime = "30"; +$Host::warmupTime = "20"; diff --git a/terrains/ACCMFlatland.spn b/terrains/ACCMFlatland.spn new file mode 100644 index 0000000..ea21dd3 Binary files /dev/null and b/terrains/ACCMFlatland.spn differ diff --git a/terrains/Affliction.spn b/terrains/Affliction.spn new file mode 100644 index 0000000..74e166a Binary files /dev/null and b/terrains/Affliction.spn differ diff --git a/terrains/Apocalypse.spn b/terrains/Apocalypse.spn new file mode 100644 index 0000000..405068e Binary files /dev/null and b/terrains/Apocalypse.spn differ diff --git a/terrains/BattleGrounds.spn b/terrains/BattleGrounds.spn new file mode 100644 index 0000000..3515b21 Binary files /dev/null and b/terrains/BattleGrounds.spn differ diff --git a/terrains/BlankMap.spn b/terrains/BlankMap.spn new file mode 100644 index 0000000..20e207b Binary files /dev/null and b/terrains/BlankMap.spn differ diff --git a/terrains/DragonDragonDXTerrain.ter b/terrains/DragonDragonDXTerrain.ter new file mode 100644 index 0000000..f4b05e7 Binary files /dev/null and b/terrains/DragonDragonDXTerrain.ter differ diff --git a/terrains/Flatland.nav b/terrains/Flatland.nav new file mode 100644 index 0000000..80cef75 Binary files /dev/null and b/terrains/Flatland.nav differ diff --git a/terrains/Flatland.spn b/terrains/Flatland.spn new file mode 100644 index 0000000..ea21dd3 Binary files /dev/null and b/terrains/Flatland.spn differ diff --git a/terrains/Flatland.ter b/terrains/Flatland.ter new file mode 100644 index 0000000..d1eda4c --- /dev/null +++ b/terrains/Flatland.ter @@ -0,0 +1 @@ +€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € à € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € DesertWorld.SandOrangeDesertWorld.RockSmoothÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/terrains/FlatlandBig.spn b/terrains/FlatlandBig.spn new file mode 100644 index 0000000..ea21dd3 Binary files /dev/null and b/terrains/FlatlandBig.spn differ diff --git a/terrains/FlatlandBigNight.spn b/terrains/FlatlandBigNight.spn new file mode 100644 index 0000000..ea21dd3 Binary files /dev/null and b/terrains/FlatlandBigNight.spn differ diff --git a/terrains/FlatlandCanyon.spn b/terrains/FlatlandCanyon.spn new file mode 100644 index 0000000..3790255 Binary files /dev/null and b/terrains/FlatlandCanyon.spn differ diff --git a/terrains/FlatlandCanyonInfected.spn b/terrains/FlatlandCanyonInfected.spn new file mode 100644 index 0000000..3790255 Binary files /dev/null and b/terrains/FlatlandCanyonInfected.spn differ diff --git a/terrains/FlatlandDesert.spn b/terrains/FlatlandDesert.spn new file mode 100644 index 0000000..2f7108d Binary files /dev/null and b/terrains/FlatlandDesert.spn differ diff --git a/terrains/FlatlandDesertInfected.spn b/terrains/FlatlandDesertInfected.spn new file mode 100644 index 0000000..2f7108d Binary files /dev/null and b/terrains/FlatlandDesertInfected.spn differ diff --git a/terrains/FlatlandNight.nav b/terrains/FlatlandNight.nav new file mode 100644 index 0000000..80cef75 Binary files /dev/null and b/terrains/FlatlandNight.nav differ diff --git a/terrains/FlatlandNight.spn b/terrains/FlatlandNight.spn new file mode 100644 index 0000000..ea21dd3 Binary files /dev/null and b/terrains/FlatlandNight.spn differ diff --git a/terrains/Flatlands Tunnels.spn b/terrains/Flatlands Tunnels.spn new file mode 100644 index 0000000..20e207b Binary files /dev/null and b/terrains/Flatlands Tunnels.spn differ diff --git a/terrains/Flatlands Tunnels.ter b/terrains/Flatlands Tunnels.ter new file mode 100644 index 0000000..f2726c8 Binary files /dev/null and b/terrains/Flatlands Tunnels.ter differ diff --git a/terrains/Gaunt.spn b/terrains/Gaunt.spn new file mode 100644 index 0000000..8318c4a Binary files /dev/null and b/terrains/Gaunt.spn differ diff --git a/terrains/LostWorld.spn b/terrains/LostWorld.spn new file mode 100644 index 0000000..19133c4 Binary files /dev/null and b/terrains/LostWorld.spn differ diff --git a/terrains/Martyrdom.spn b/terrains/Martyrdom.spn new file mode 100644 index 0000000..b9ce292 Binary files /dev/null and b/terrains/Martyrdom.spn differ diff --git a/terrains/Outpost26.spn b/terrains/Outpost26.spn new file mode 100644 index 0000000..47837e6 Binary files /dev/null and b/terrains/Outpost26.spn differ diff --git a/terrains/RIA039.spn b/terrains/RIA039.spn new file mode 100644 index 0000000..d5ee7d7 Binary files /dev/null and b/terrains/RIA039.spn differ diff --git a/terrains/Stiletto.spn b/terrains/Stiletto.spn new file mode 100644 index 0000000..20e207b Binary files /dev/null and b/terrains/Stiletto.spn differ diff --git a/terrains/Stiletto.ter b/terrains/Stiletto.ter new file mode 100644 index 0000000..1d255b4 Binary files /dev/null and b/terrains/Stiletto.ter differ diff --git a/terrains/Tactical Assault.spn b/terrains/Tactical Assault.spn new file mode 100644 index 0000000..3d6d74a Binary files /dev/null and b/terrains/Tactical Assault.spn differ diff --git a/terrains/TestingGrounds.spn b/terrains/TestingGrounds.spn new file mode 100644 index 0000000..20e207b Binary files /dev/null and b/terrains/TestingGrounds.spn differ diff --git a/terrains/TestingGrounds.ter b/terrains/TestingGrounds.ter new file mode 100644 index 0000000..86565bf --- /dev/null +++ b/terrains/TestingGrounds.ter @@ -0,0 +1,11 @@ +€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € à € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € µ Ò2Òµ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ®Š¨¨Š®€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ò+Å¡¿¿¡Å+Ò€ € € € € j C‹Cj € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € +„úwú„+€ € ² «í5í«² € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ®Å¹•²²•¹Å®€ CÌÅ.O.ÅÌC€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € µ Š¡ú•pŽíŽp•ú¡ŠçÌVO¸Ø¸OV̲ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ò¨¿²Ž¬ !”!M!¹×§¨ýÅOH±‰Ñ‰±HOÅ«€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ú1–wž #&ì&&¼#Ú ªö.¸±ò:ò±¸.j € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ï kÏ € € € ŠåK¼7s$ + Å$ )˜+i,~+Ø(x$`¥„ƉòËËò‰íC€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € 6 A¨A 6Ñ,“~8¢"Œ$„%†(Î,X/*0>/™,8("T4”Ñ:[:ÑØO5‹€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¤vz¯¯zv®U"~äUÏUú"'í(Ü+—,Ó.^1/2F1Ÿ.>*!$¬A¶‰òËËò‰íC€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € éMéMéMéMéM€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € 6¤DO·O©P4Ú@!°"+#°";%E),/<0/©1z21ê.‰*’%´Éi™ò:ò±¸.j € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ;1;1;1;1;1€ € € € € € € € éMéMéMéMéM€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € vèì!ˆ!ìwá ‰"l"@!¦#%‘%Ç%'c) -w021=/90 +10ù.-¸(·#" "n ÿ1‰Ñ‰±HOÅ«€ € € € € € € € € € € € € € € € € € € € € € ²9²9²9²9²9€ € € € € ;1;1;1;1;1€ € € € € € € € éMéMéMéMéM€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ï zìð%!Œ!%!ðë V$ý%à%ÿ#%‡&'Ü'2)&)R+¼.w/- -ß-(/¯/É-6+„(S%9$ "1ÄØ¸OV̲ € € € € € € € € € € € € € € € € € € € € € € ²9²9²9²9²9€ € € € € ;1;1;1;1;1€ € € € € € € € éMéMéMéMéM€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € A¯O!%!Z"Á"Z"%!›"&­''¯%‘%I)ß+£-F.,&*ø* +)((w*o-ž.P/Q.¡+?'–$û"Œ½iÏ.ÅÌC€ € € € € € € € € € € € € € € € € € € € € € € ²9²9²9²9²9€ € € € € ;1;1;1;1;1€ € € € € € € € éMéMéMéMéM€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € k¨·ˆŒ!Á")#Á"Œ!‡"ó%™'}'–(ù,ì1‚46²6‹4‚0j,O(;%g$)¦- 0¼0½/ -©(u$ƒ!È-í«² € € € € € € € € € € € € € € € € € € € € € € € € ²9²9²9²9²9€ € € € € ;1;1;1;1;1€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € A¯O!%!Z"Á"Z"%!° $Â%*v.A328È:²;U<-:=5§05)À!‚#I)`-Â/v0u/Å,b(Ò$¯ Å6âGhj € € € € € € € € € € € € € € € € € € € € € € € € € € ²9²9²9²9²9€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ï zìð%!Œ!%!ðì !%»,+1+7<²>ë>.?=8‰1*% Š!R'g+Ê-}.~-Í*Ì&ì#ÈÔ[.L´€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € vèì!ˆ!ìè ª$E,i1·8ª=?@x@$z( +,{+6)I%´Lˆâ€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € h"h"h"h"h"€ € € € € € € ?????€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¦µœtítãl#Ñ'*Õ*W.r0%1r0â/»2X3„1A-&ª%8(·*x+®*i(|$çª[â´€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € h"h"h"h"h"€ € € € € € € ?????€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ² ~ßtLgÆgLtÂ!(&i(…(Õ*ð,£-ð,.1¹1ç/¢++&'ç'·(x)z(õ%"r4x€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € h"h"h"h"h"€ € € € € € € ?????€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ι¨úg‚ á ‚ gõZ"›$¹$ì%(»(4(*-«-×+”''g(Á((Z&¾$"êT€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € h"h"h"h"h"€ € € € € € € ?????€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ,øXíÆá ?!á ÆíXiªl Ç"d%–%Ô##&,'Y%j$Ö&8(’(ã'+&i#¡á¦x€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € h"h"h"h"h"€ € € € € € € ?????€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ι¨ú>¸ "2"1!gúÏHî¨!E$˜$ê"G Æ=¿3#ž%'['«&ó$2"h–9 € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ² ~r?â\"­#Ö#Õ"¬ Y +IÇ„» .!S8,ó ]#À$%j$²"ò(UÙ€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ÷í¹\ Ö"($O$O#&!û!! âh ê µ º§u\© w! "³":!:¯ …€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € e I> +®("y#¡#¡"x Ù"ò#Þ"w!o"õ#w$7$<#)!ü¯˜WÒ÷‘!¢!* )ŸŒ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € à2g3ÖP ¡!Ê!É E ‹#¤$#o"ù$~&' &¦%’#e Æ39^ø ‘‘6R€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € / î@'2ÕO¡ÉÈÊ"(#Ö#¤$~&(…((ö&â$¶!p!k;ÖèpDU”R € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ñ âÉCQ&x Ÿ"»"ú$È%'…()…(-'%í!§d¹Úf[»„UR€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Æÿyˆ*`*ˆ¤Âp"®$|%~&(…((y'&<#xsÂäš»F<›˜W–T’P € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ñ âÉCQô*ôQè´ ò"À#ù$~&I(å)ä)®'$ä Zx呇YWS€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € / î@'¡¯QˆQ¯¡Û‰Ç• o"Ù%)ï)®(+&c")=p¼»ZšZ™X—U“Q€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € à2“¡CyC¡“@î,Y+ %$'('ç%c#œ!³º…ZYWS€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € e ¸ž'ÉÿÉ'žã!ɹÑ!##Ü!Y‘U±x~ÅJ—™Y˜W–T’P € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ñ ¸2@ââ@2¸Ñ §/¨‚ÊÍ BŸ·}ƒÈNXWUR€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € e àîÆîàe € € ddð7;ú[ ¥úÁÇ ’XW–U”R € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € / Ñ Ñ / € € € € ±„ŠPÕ åj¯EK‘Û8TR€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € d ÔZŸ¥jï49 TØžIS’R € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € { NTŸåê¯4y~CÇ Ü ¡ 4 P € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € SY¤éï´9~ƒGÌ  ì € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € É ãh®³yýCG Õ € +€ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¢ hî28ý‚Ç Ì  +Ë ¾ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¬ 2 w | B Æ  Õ Ë c € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¾ { F Ë + + € +¾ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ì c 6 c ì € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Y œ Y € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € “ x Å x “  d-o-d € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € , ªÝº²{½{²d € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € “ ª)[WŽC†C{-Y € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € x ôAô+µ†É†½oœ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Å Ý[AA9Ï+X›‹-Y € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € œ îz…A$ºßkæ(;‚ô € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ƒ Ä1»ÇSc¯€¬$I…˜yøz ³ j ’ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ` ÄÉn~‰“&Ìî}9ÔB¬d‹" € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €   $¥$  € € € € € € € €  GwÖËo³Ì\PÖ35vd w z–Íõ‹’ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € £ )­/­)£ € € € € € € š \š>‘U;¥ylh!|Ã1‹´Ídj € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €   )°4µ4°)  € € € ¨ “ >’›úïx˜}´Ù0*'ß„ ÷d§¬³ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € $­4¸9¸4­$€ € ï ãWüì÷Ž‚\“sž‹±iÒUo˜Ídj € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¥/µ9»9µOQ°T@QQÝüPÚeòˆ€Áé™-°l"Ò°ó†õ‹’ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €  € € € € € € € € € $­4¸9¸\ilLÚÆc6 ,€RÏf-TV¼ï„8ñWâóûèN+‚‹" € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €  € € € € € € € € €   )°4µTly|àýšé’å•úÉüÁ‡Êñ©È—æÑ4™þÝB’ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € €  € € € € € € € € € „ Û så3YqèlúæƒÑÓK¥ÿdÖrpnÑ%ø‡’·[=zX?Èm› € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € § þ 3 d ×#{làl𔀃Ùxš"y‹W§½¥°“ç{f‡w*X € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € „ þ U Š › Š U ªÌ\èlèË@p‰£(sÚÁøÐÞ9ðéÄÞ¼£3µÖ¯b € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¸ 3 Š ¾ Ð ¾ – ð F£¯Íw‹Wøvù:e âšâ6Á[Æ”£6Wärb € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Ê D › Ð á =úZŒÙ.YšÝ2㨛YYC¢³#òÃDžÇˆm € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € ¸ 3 Š ¾ Ü à™‹‚«ÕÁ:ýñZÄâ`{¥¯gâtR b NS € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € „ þ U Š 8{n)“ÿ¤„íù?ñûׄBÇ¥Àâ¾<â]¯¯¨]• € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € § þ 3 TYe÷cæþ @½$¸Ðïí }´Õs:°&g&bS € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € „ ¸ g © %“ÿ¤„´õ;µ<óIÔwŸï¸ó |A·b£bžX € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Œ ß ÖÜakÖ{Z…4­™a¹ p®QB}žiù X]X € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € í ß –æR Ù t Ë œ È hÜh%QÎ ™ € S • S € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € Œ  R S Ó ¾ ñ  í ˆ € € „ È$˜$È„ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € œ ø lø œ € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € € DefaultÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿDDefault\t1001\nFractal Distortion\ttab_DistortMask\t1002\t0\tdmask_interval\t20\tdmask_rough\t0\tdmask_seed\t422386101\tdmask_filter\t0.00000 0.00000 0.13750 0.487500 0.86250 1.00000 1.00000\nPlace by Height\ttab_HeightMask\t1003\t1002\ttextureHeightFilter\t1.00000 1.00000 1.00000 1.00000 1.00000 1.00000 \theightDistort\t1~General\tTab_general\tgeneral_min_height\t50\tgeneral_scale\t300\tgeneral_water\t0.000\tgeneral_centerx\t0\tgeneral_centery\t0 \ No newline at end of file diff --git a/terrains/The Watering Hole.spn b/terrains/The Watering Hole.spn new file mode 100644 index 0000000..a3b2865 Binary files /dev/null and b/terrains/The Watering Hole.spn differ diff --git a/terrains/rasplands.spn b/terrains/rasplands.spn new file mode 100644 index 0000000..d2c9df9 Binary files /dev/null and b/terrains/rasplands.spn differ diff --git a/terrains/slapmydash infected.spn b/terrains/slapmydash infected.spn new file mode 100644 index 0000000..6dacf77 Binary files /dev/null and b/terrains/slapmydash infected.spn differ diff --git a/terrains/slapmydash night.nav b/terrains/slapmydash night.nav new file mode 100644 index 0000000..642a35f Binary files /dev/null and b/terrains/slapmydash night.nav differ diff --git a/terrains/slapmydash night.spn b/terrains/slapmydash night.spn new file mode 100644 index 0000000..6dacf77 Binary files /dev/null and b/terrains/slapmydash night.spn differ diff --git a/terrains/slapmydash.nav b/terrains/slapmydash.nav new file mode 100644 index 0000000..642a35f Binary files /dev/null and b/terrains/slapmydash.nav differ diff --git a/terrains/slapmydash.spn b/terrains/slapmydash.spn new file mode 100644 index 0000000..6dacf77 Binary files /dev/null and b/terrains/slapmydash.spn differ diff --git a/terrains/z0r Infected.spn b/terrains/z0r Infected.spn new file mode 100644 index 0000000..06ae615 Binary files /dev/null and b/terrains/z0r Infected.spn differ diff --git a/terrains/z0r Revisited.spn b/terrains/z0r Revisited.spn new file mode 100644 index 0000000..06ae615 Binary files /dev/null and b/terrains/z0r Revisited.spn differ diff --git a/terrains/z0r Revisited.ter b/terrains/z0r Revisited.ter new file mode 100644 index 0000000..aa45538 Binary files /dev/null and b/terrains/z0r Revisited.ter differ