diff --git a/.travis.yml b/.travis.yml
index 9b43c37..07433dd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,7 @@ before_install:
- sudo apt-get install -y g++-multilib
install:
- - wget http://www.sourcemod.net/smdrop/1.6/sourcemod-1.6.1-git4552-linux.tar.gz -O /tmp/sourcemod.tar.gz
+ - wget http://www.sourcemod.net/smdrop/1.8/sourcemod-1.8.0-git5657-linux.tar.gz -O /tmp/sourcemod.tar.gz
- tar -xzvf /tmp/sourcemod.tar.gz
before_script:
@@ -16,5 +16,4 @@ before_script:
script:
- ./spcomp prophunt.sp
- - ./spcomp prophunt-stats.sp
\ No newline at end of file
diff --git a/addons/sourcemod/data/prophunt/maps/ph_abandoned.cfg b/addons/sourcemod/data/prophunt/maps/ph_abandoned_prison.cfg
similarity index 100%
rename from addons/sourcemod/data/prophunt/maps/ph_abandoned.cfg
rename to addons/sourcemod/data/prophunt/maps/ph_abandoned_prison.cfg
diff --git a/addons/sourcemod/data/prophunt/maps/ph_nightclub.cfg b/addons/sourcemod/data/prophunt/maps/ph_nightclub.cfg
index 6dc344d..7f34ecc 100644
--- a/addons/sourcemod/data/prophunt/maps/ph_nightclub.cfg
+++ b/addons/sourcemod/data/prophunt/maps/ph_nightclub.cfg
@@ -2,33 +2,25 @@
{
"Props"
{
+"models/props_badlands/barrel01.mdl" {}
"models/props_badlands/barrel02.mdl" {}
+"models/props_badlands/barrel03.mdl" {}
"models/props_farm/wooden_barrel.mdl" {}
"models/props_farm/wood_pile.mdl" {}
"models/props_farm/metal_pile.mdl" {}
-"models/props_farm/pallet001.mdl" {}
-"models/props_farm/welding_machine01.mdl" {}
"models/props_hydro/water_barrel.mdl" {}
"models/props_hydro/water_barrel_cluster.mdl" {}
"models/props_hydro/barrel_crate_half.mdl" {}
"models/props_spytech/bench001a.mdl" {}
"models/props_trainyard/beer_keg001.mdl" {}
-"models/props_trainyard/distillery_barrel001.mdl" {}
"models/props_well/computer_cart01.mdl" {}
"models/props_well/hand_truck01.mdl" {}
"models/props_2fort/propane_tank_tall01.mdl" {}
"models/props_2fort/miningcrate001.mdl" {}
"models/props_2fort/miningcrate002.mdl" {}
+"models/props_2fort/oildrum.mdl" {}
+"models/props_2fort/tire003.mdl" {}
"models/props_2fort/wastebasket01.mdl" {}
-"models/props_2fort/handrail001_i1.mdl" {}
-"models/props_2fort/locker001.mdl" {}
-"models/props_2fort/frog.mdl" {}
-"models/props_2fort/mop_and_bucket.mdl" {}
-"models/props_manor/chair_01.mdl" {}
-"models/props_manor/couch_01.mdl" {}
-"models/props_manor/table_01.mdl" {}
-"models/props_urban/urban_chair.mdl" {}
-"models/props_medical/beer_barrels.mdl" {}
}
"Settings"
{
diff --git a/addons/sourcemod/data/prophunt/maps/ph_nightclub_v2.cfg b/addons/sourcemod/data/prophunt/maps/ph_nightclub_v2.cfg
new file mode 100644
index 0000000..6dc344d
--- /dev/null
+++ b/addons/sourcemod/data/prophunt/maps/ph_nightclub_v2.cfg
@@ -0,0 +1,41 @@
+"prophuntmapconfig"
+{
+"Props"
+{
+"models/props_badlands/barrel02.mdl" {}
+"models/props_farm/wooden_barrel.mdl" {}
+"models/props_farm/wood_pile.mdl" {}
+"models/props_farm/metal_pile.mdl" {}
+"models/props_farm/pallet001.mdl" {}
+"models/props_farm/welding_machine01.mdl" {}
+"models/props_hydro/water_barrel.mdl" {}
+"models/props_hydro/water_barrel_cluster.mdl" {}
+"models/props_hydro/barrel_crate_half.mdl" {}
+"models/props_spytech/bench001a.mdl" {}
+"models/props_trainyard/beer_keg001.mdl" {}
+"models/props_trainyard/distillery_barrel001.mdl" {}
+"models/props_well/computer_cart01.mdl" {}
+"models/props_well/hand_truck01.mdl" {}
+"models/props_2fort/propane_tank_tall01.mdl" {}
+"models/props_2fort/miningcrate001.mdl" {}
+"models/props_2fort/miningcrate002.mdl" {}
+"models/props_2fort/wastebasket01.mdl" {}
+"models/props_2fort/handrail001_i1.mdl" {}
+"models/props_2fort/locker001.mdl" {}
+"models/props_2fort/frog.mdl" {}
+"models/props_2fort/mop_and_bucket.mdl" {}
+"models/props_manor/chair_01.mdl" {}
+"models/props_manor/couch_01.mdl" {}
+"models/props_manor/table_01.mdl" {}
+"models/props_urban/urban_chair.mdl" {}
+"models/props_medical/beer_barrels.mdl" {}
+}
+"Settings"
+{
+"doors" "0"
+"relay" "0"
+"round" "250"
+"freeze" "1"
+"alt" ""
+}
+}
\ No newline at end of file
diff --git a/addons/sourcemod/data/prophunt/prophunt_config.cfg b/addons/sourcemod/data/prophunt/prophunt_config.cfg
index b2d58eb..7d227c0 100644
--- a/addons/sourcemod/data/prophunt/prophunt_config.cfg
+++ b/addons/sourcemod/data/prophunt/prophunt_config.cfg
@@ -46,7 +46,7 @@
"6"
{
"name" "Heavy"
- "hunter_limit" "2"
+ "hunter_limit" "-1"
"base_speed" "230.0"
"max_speed" "400.0"
"speed_increment" "50.0"
@@ -105,8 +105,32 @@
{
"sound" "vo/mvm_bonus01.mp3"
// "game" "Announcer.MVM_Bonus"
-// "channel" "7"
+ "channel" "7"
+ }
+ "FirstBlood"
+ {
+ "sound" "vo/announcer_AM_FirstBlood01.wav"
+ "game" "Announcer.AM_FirstBloodRandom"
+ "channel" "7"
+ "soundlevel" "95"
+ }
+
+ "FirstBloodFast"
+ {
+ "sound" "vo/announcer_AM_FirstBlood06.wav"
+ "game" "Announcer.AM_FirstBloodFast"
+ "channel" "7"
+ "soundlevel" "95"
+ }
+
+ "FirstBloodFinally"
+ {
+ "sound" "vo/announcer_AM_FirstBlood03.wav"
+ "game" "Announcer.AM_FirstBloodFinally"
+ "channel" "7"
+ "soundlevel" "95"
}
+
"Internet"
{
"sound" "vo/pyro_positivevocalization01.mp3"
@@ -222,3207 +246,614 @@
"sound" "buttons/button3.wav"
}
}
+
+ // Class bits for onlyclass specifiers
+ // Add these up to apply things to specific classes
+ // 1 = Scout
+ // 2 = Sniper
+ // 4 = Soldier
+ // 8 = DemoMan
+ // 16 = Medic
+ // 32 = Heavy
+ // 64 = Pyro
+ // 128 = Spy
+ // 256 = Engineer
+ // i.e. to replace a weapon for both DemoMan and Soldier, but no one else:
+ // "replace_onlyclasses" "12"
- "items"
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+//////////////Items by Classname//////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+ "item_classes"
{
-
+
//////////////////////////////////////////////
-////////////////Melee Weapons/////////////////
+////////////////////Shared////////////////////
//////////////////////////////////////////////
-////////////////Scout Melee///////////////////
- "0"
+ "saxxy"
{
- "name" "TF_WEAPON_BAT"
- "item_name" "#TF_Weapon_Bat"
- }
-
- "44"
- {
- "name" "The Sandman"
- "item_name" "#TF_Unique_Achievement_Bat"
- "propername" "1"
+ "name" "Generic Attributeless Melee"
}
- "190"
+ // Blocked Slot for Demo, replaced by Shotgun for Soldier
+ "tf_weapon_parachute"
{
- "name" "Upgradeable TF_WEAPON_BAT"
- "item_name" "#TF_Weapon_Bat"
+ "name" "B.A.S.E. Jumper"
+ "removed_hunters" "1"
+ "replace_onlyclasses" "4"
+ "replace" "tf_weapon_shotgun_soldier : 10 : 0 : 1"
}
- "221"
+ "tf_weapon_katana"
{
- "name" "The Holy Mackerel"
- "item_name" "#TF_TheHolyMackerel"
- "propername" "1"
+ "name" "Half-Zatoichi"
}
- "317"
+ // All shotguns
+ // Note the class-specific restriction for Pyro
+ "tf_weapon_shotgun"
{
- "name" "The Candy Cane"
- "item_name" "#TF_CandyCane"
- "propername" "1"
+ "name" "Generic Shotgun"
+ "damage_hunters" "0.8"
+ "self_damage_hunters" "8.0"
+ "addattribs" "3 ; -4.0 ; 37 ; 6.25"
+ "attribs_onlyclasses" "64"
}
- "325"
+ // Remove all shared pistols from Hunters
+ "tf_weapon_pistol"
{
- "name" "The Boston Basher"
- "item_name" "#TF_BostonBasher"
- "propername" "1"
+ "name" "Generic Pistol"
+ "removed_hunters" "1"
}
- "349"
+ "tf_weapon_spellbook"
{
- "name" "Sun-on-a-Stick"
- "item_name" "#TF_Unique_RiftFireMace"
+ "removed_hunters" "1"
+ "removed_props" "1"
}
-
- "355"
+
+//////////////////////////////////////////////
+///////////////////Scout//////////////////////
+//////////////////////////////////////////////
+
+///////////////Scout Primary///////////////////
+
+ "tf_weapon_scattergun"
{
- "name" "The Fan O'War"
- "item_name" "#TF_Gunbai"
- "propername" "1"
+ "name" "Generic Scout Primary"
}
- "450"
+ "tf_weapon_handgun_scout_primary"
{
- "name" "The Atomizer"
- "item_name" "#TF_Atomizer"
- "propername" "1"
+ "name" "The Shortstop"
}
- "452"
+ "tf_weapon_pep_brawler_blaster"
{
- "name" "Three-Rune Blade"
- "item_name" "#TF_ScoutSword"
+ "name" "Baby Face's Blaster"
}
-
- "572"
+
+//////////////Scout Secondary//////////////////
+
+ // Replace Bonk and Crit-a-Cola with pistol
+ "tf_weapon_lunchbox_drink"
{
- "name" "Unarmed Combat"
- "item_name" "#TF_UnarmedCombat"
+ "name" "Bonk or Crit-a-Cola"
+ "replace" "tf_weapon_pistol : 23 : 0 : 1"
}
-
- "648"
+
+ "tf_weapon_jar_milk"
{
- "name" "The Wrap Assassin"
- "item_name" "#TF_BallBuster"
- "propername" "1"
+ "name" "Mad or Mutated Milk"
}
- "660"
+ "tf_weapon_handgun_scout_secondary"
{
- "name" "Festive Bat 2011"
- "item_name" "#TF_Weapon_Bat_Festive2011"
+ "name" "Winger or Pocket Pistol"
}
- "999"
+ "tf_weapon_cleaver"
{
- "name" "Festive Holy Mackeral"
- "item_name" "#TF_Weapon HolyMackeral_Festive2012"
+ "name" "Flying Guillotine"
}
- "30667"
+////////////////Scout Melee////////////////////
+
+ "tf_weapon_bat"
{
- "name" "Batsaber"
- "item_name" "#TF_invasion_bat"
+ "name" "Generic Scout Melee"
}
-///////////////Demo Melee/////////////////////
- "1"
+ "tf_weapon_bat_wood"
{
- "name" "TF_WEAPON_BOTTLE"
- "item_name" "#TF_Weapon_Bottle"
+ "name" "Sandman"
}
- "132"
+ "tf_weapon_bat_fish"
{
- "name" "The Eyelander"
- "item_name" "#TF_Unique_Achievement_Sword"
- "propername" "1"
+ "name" "Holy Mackeral or Unarmed Combat"
}
- "172"
+ "tf_weapon_bat_giftwrap"
{
- "name" "The Scotsman's Skullcutter"
- "item_name" "#TF_Unique_BattleAxe"
- "propername" "1"
+ "name" "Wrap Assassin"
}
+
+//////////////////////////////////////////////
+////////////////////Soldier///////////////////
+//////////////////////////////////////////////
+
+////////////////Soldier Primary///////////////
- "191"
+ "tf_weapon_rocketlauncher"
{
- "name" "Upgradeable TF_WEAPON_BOTTLE"
- "item_name" "#TF_Weapon_Bottle"
+ "name" "Generic Rocket Launcher"
+ "damage_hunters" "0.75"
}
- "266"
+ "tf_weapon_rocketlauncher_directhit"
{
- "name" "The Horseless Headless Horseman's Headtaker"
- "item_name" "#TF_HalloweenBoss_Axe"
- "propername" "1"
+ "name" "Direct Hit"
+ "damage_hunters" "0.75"
}
-
- "307"
+
+// Replace with rocket launcher due to charge shot and unlimit ammo being direct upgrade
+ "tf_weapon_particle_cannon"
{
- "name" "The Ullapool Caber"
- "item_name" "#TF_UllapoolCaber"
- "propername" "1"
+ "name" "Cow Mangler 5000"
+ "replace" "tf_weapon_rocketlauncher : 18 : 0 : 1"
}
- "327"
+ "tf_weapon_rocketlauncher_airstrike"
{
- "name" "The Claidheamohmor"
- "item_name" "#TF_Claidheamohmor"
- "propername" "1"
+ "name" "Air Strike"
+ "damage_hunters" "0.75"
}
- "357"
+///////////////Soldier Secondary//////////////
+// Note: Boots cannot be represented by class
+
+ "tf_weapon_shotgun_soldier"
{
- "name" "The Half-Zatoichi"
- "item_name" "#TF_SoldierKatana"
- "propername" "1"
+ "name" "Soldier's Shotgun"
}
- "404"
+ "tf_weapon_buff_item"
{
- "name" "The Persian Persuader"
- "item_name" "#TF_PersianPersuader"
- "propername" "1"
+ "name" "Buff Banner, Battalion's Backup, Concheror"
}
- "482"
+ "tf_weapon_raygun"
{
- "name" "Nessie's Nine Iron"
- "item_name" "#TF_NineIron"
+ "name" "Righteous Bison"
}
- "609"
- {
- "name" "The Scottish Handshake"
- "item_name" "#TF_ScottishHandshake"
- "propername" "1"
- }
-
- "1082"
+/////////////////Soldier Melee////////////////
+
+ "tf_weapon_shovel"
{
- "name" "Festive Eyelander"
- "item_name" "#TF_Weapon_Eyelander_Festive2013"
- "propername" "1"
+ "name" "Generic Soldier Melee"
}
+//////////////////////////////////////////////
+/////////////////////Pyro/////////////////////
+//////////////////////////////////////////////
-///////////////Pyro Melee/////////////////////
+/////////////////Pyro Primary/////////////////
- "2"
+ "tf_weapon_flamethrower"
{
- "name" "TF_WEAPON_FIREAXE"
- "item_name" "#TF_Weapon_FireAxe"
- "self_damage_hunters" "5.0"
+ "name" "Generic Flamethrower"
+ "self_damage_hunters" "1.0"
+ // Remove airblast
+ "addattribs" "354 ; 1.0"
}
- "38"
+///////////////Pyro Secondary/////////////////
+
+ "tf_weapon_shotgun_pyro"
{
- "name" "The Axtinguisher"
- "item_name" "#TF_Unique_Achievement_FireAxe1"
- "propername" "1"
+ "name" "Pyro's Shotgun"
+ "damage_hunters" "0.8"
+ "self_damage_hunters" "8.0"
+ // Clipsize 2, 625% ammo (unlimited in PH 3.x and below)
+ "addattribs" "3 ; -4.0 ; 37 ; 6.25"
}
-
- "153"
+
+ "tf_weapon_flaregun"
{
- "name" "The Homewrecker"
- "item_name" "#TF_Unique_SledgeHammer"
- "propername" "1"
+ "name" "Generic Flare Gun"
+ "self_damage_hunters" "5.0"
}
- "192"
+ "tf_weapon_flaregun_revenge"
{
- "name" "Upgradeable TF_WEAPON_FIREAXE"
- "item_name" "#TF_Weapon_FireAxe"
+ "name" "Manmelter"
+ "self_damage_hunters" "5.0"
}
- "214"
+/////////////////Pyro Melee///////////////////
+
+ "tf_weapon_fireaxe"
{
- "name" "The Powerjack"
- "item_name" "#TF_ThePowerjack"
- "propername" "1"
+ "name" "Generic Pyro Melee"
}
-
- "326"
+
+//////////////////////////////////////////////
+////////////////////DemoMan///////////////////
+//////////////////////////////////////////////
+
+////////////////DemoMan Primary///////////////
+// Note: Boots cannot be represented by class
+// Blocked slot
+
+ "tf_weapon_grenadelauncher"
{
- "name" "The Back Scratcher"
- "item_name" "#TF_BackScratcher"
- "propername" "1"
+ "name" "Generic Grenade Launcher"
+ "removed_hunters" "1"
}
- "348"
+ "tf_weapon_cannon"
{
- "name" "Sharpened Volcano Fragment"
- "item_name" "#TF_Unique_RiftFireAxe"
+ "name" "Loose Cannon"
+ "removed_hunters" "1"
}
- "457"
- {
- "name" "The Postal Pummeler"
- "item_name" "#TF_Mailbox"
- "propername" "1"
- }
+///////////////DemoMan Secondary//////////////
- "466"
+ "tf_weapon_pipebomblauncher"
{
- "name" "The Maul"
- "item_name" "#TF_RFAHammer"
- "propername" "1"
+ "name" "Generic Stickybomb Launcher"
+ "preserveattribs" "0"
+ // Turn into Stickybomb Jumper
+ "addattribs" "1 ; 0.0 ; 15 ; 1.0 ; 181 ; 1.0 ; 78 ; 3.0 ; 280 ; 14.0 ; 89 ; -6.0 ; 400 ; 1.0"
}
- "593"
+ "tf_wearable_demoshield"
{
- "name" "The Third Degree"
- "item_name" "#TF_ThirdDegree"
- "propername" "1"
+ "name" "Chargin' Targe, Splendid Screen, or Tide Turner"
}
- "739"
- {
- "name" "The Lollichop"
- "item_name" "#TF_Lollichop"
- "propername" "1"
- "self_damage_hunters" "5.0"
- }
+/////////////////DemoMan Melee////////////////
- "813"
+ "tf_weapon_bottle"
{
- "name" "The Neon Annihilator"
- "item_name" "#TF_SD_Sign"
- "propername" "1"
- "self_damage_hunters" "8.0"
+ "name" "Generic DemoMan Melee"
}
- "834"
+ "tf_weapon_sword"
{
- "name" "Promo Neon Annihilator"
- "item_name" "#TF_SD_Sign"
- "propername" "1"
- "self_damage_hunters" "8.0"
+ "name" "Eyelander, HHHH, Calidheamh Mor, Persian Persuader, Nessie's Nine Iron"
}
- "1000"
+//////////////////////////////////////////////
+////////////////////Heavy/////////////////////
+//////////////////////////////////////////////
+
+////////////////Heavy Primary/////////////////
+// Blocked slot
+
+ "tf_weapon_minigun"
{
- "name" "Festive Axstinguisher"
- "item_name" "#TF_Weapon_Axtinguisher_Festive2012"
+ "name" "Generic Minigun"
+ "removed_hunters" "1"
}
-//////////////Sniper Melee/////////////////////
- "3"
+///////////////Heavy Secondary////////////////
+
+ "tf_weapon_shotgun_hwg"
{
- "name" "TF_WEAPON_CLUB"
- "item_name" "#TF_Weapon_Club"
+ "name" "Heavy's Shotgun"
}
-
- "171"
+
+ "tf_weapon_lunchbox"
{
- "name" "The Tribalman's Shiv"
- "item_name" "#TF_Unique_TribalmanKukri"
- "propername" "1"
+ "name" "Sandvich, Dalokohs Bar, Buffalo Steak"
}
- "193"
+/////////////////Heavy Melee//////////////////
+
+ "tf_weapon_fists"
{
- "name" "Upgradeable TF_WEAPON_CLUB"
- "item_name" "#TF_Weapon_Club"
+ "name" "Generic Heavy Melee"
}
-
- "232"
+
+//////////////////////////////////////////////
+///////////////////Engineer///////////////////
+//////////////////////////////////////////////
+
+///////////////Engineer Primary///////////////
+
+ "tf_weapon_shotgun_primary"
{
- "name" "The Bushwacka"
- "item_name" "#TF_TheBushwacka"
- "propername" "1"
+ "name" "Engineer's Shotgun"
}
-
- "401"
+
+ "tf_weapon_sentry_revenge"
{
- "name" "The Shahanshah"
- "item_name" "#TF_Shahanshah"
- "propername" "1"
+ "name" "Frontier Justice"
}
-//////////////Spy Melee////////////////////////
- "4"
+ "tf_weapon_drg_pomson"
{
- "name" "TF_WEAPON_KNIFE"
- "item_name" "#TF_Weapon_Knife"
+ "name" "Pomson 6000"
}
- "194"
+ "tf_weapon_shotgun_building_rescue"
{
- "name" "Upgradeable TF_WEAPON_KNIFE"
- "item_name" "#TF_Weapon_Knife"
+ "name" "Rescue Ranger"
}
- "225"
- {
- "name" "Your Eternal Reward"
- "item_name" "#TF_EternalReward"
- }
+//////////////Engineer Secondary//////////////
+// Blocked slot
- "356"
+ "tf_weapon_laser_pointer"
{
- "name" "Conniver's Kunai"
- "item_name" "#TF_Kunai"
+ "name" "Wrangler, Gigar Counter"
+ "removed_hunters" "1"
}
- "461"
+ "tf_weapon_mechanical_arm"
{
- "name" "The Big Earner"
- "item_name" "#TF_BigEarner"
- "propername" "1"
+ "name" "Short-Circuit"
+ "removed_hunters" "1"
}
- "574"
- {
- "name" "The Wanga Prick"
- "item_name" "#TF_VoodooPin"
- }
+////////////////Engineer Melee////////////////
- "638"
+ "tf_weapon_wrench"
{
- "name" "The Sharp Dresser"
- "item_name" "#TF_SharpDresser"
- "propername" "1"
+ "name" "Generic Engineer Melee"
}
- "649"
+ "tf_weapon_robot_arm"
{
- "name" "The Spy-cicle"
- "item_name" "#TF_SpyCicle"
- "propername" "1"
+ "name" "Gunslinger"
}
- "665"
+//////////////////////////////////////////////
+////////////////////Medic/////////////////////
+//////////////////////////////////////////////
+
+////////////////Medic Primary/////////////////
+
+ "tf_weapon_syringegun_medic"
{
- "name" "Festive Knife 2011"
- "item_name" "#TF_Weapon_Knife_Festive2011"
+ "name" "Generic Syringe Gun"
+ "self_damage_hunters" "4.0"
}
-
- "727"
+
+// Replace with syringe gun
+ "tf_weapon_crossbow"
{
- "name" "The Black Rose"
- "item_name" "#TF_BlackRose"
- "propername" "1"
+ "name" "Crusader's Crossbow"
+ "replace" "tf_weapon_syringegun_medic : 17 : 0 : 1"
}
+
+///////////////Medic Secondary////////////////
+// Blocked slot
- "794"
+ "tf_weapon_medigun"
{
- "name" "Silver Botkiller Knife Mk.I"
- "item_name" "#TF_Weapon_Knife_MVMLoot"
- "propername" "1"
+ "name" "Generic Medigun"
+ "removed_hunters" "1"
}
+
+/////////////////Medic Melee//////////////////
- "803"
+ "tf_weapon_bonesaw"
{
- "name" "Gold Botkiller Knife Mk.I"
- "item_name" "#TF_Weapon_Knife_MVMLootGold"
- "propername" "1"
+ "name" "Generic Bonesaw"
}
+
+//////////////////////////////////////////////
+///////////////////Sniper/////////////////////
+//////////////////////////////////////////////
+
+///////////////Sniper Primary/////////////////
- "883"
+ //Replace with Huntsman
+ "tf_weapon_sniperrifle"
{
- "name" "Rust Botkiller Knife"
- "item_name" "#TF_Weapon_Knife_IntermediateMVMLoot"
- "propername" "1"
+ "name" "Generic Sniper Rifle"
+ "replace" "tf_weapon_compound_bow : 56 : 10 : 6"
}
- "892"
+ "tf_weapon_compound_bow"
{
- "name" "Blood Botkiller Knife"
- "item_name" "#TF_Weapon_Knife_IntermediateMVMRareLoot"
- "propername" "1"
+ "name" "Huntsman or Fortified Compound"
+ "damage_hunters" "0.85"
}
- "901"
+ "tf_weapon_sniperrifle_decap"
{
- "name" "Carbonado Botkiller Knife"
- "item_name" "#TF_Weapon_Knife_ExpertMVMLoot"
- "propername" "1"
+ "name" "Bazaar Bargain"
+ "replace" "tf_weapon_compound_bow : 56 : 10 : 6"
}
- "910"
+ "tf_weapon_sniperrifle_classic"
{
- "name" "Diamond Botkiller Knife"
- "item_name" "#TF_Weapon_Knife_ExpertMVMRareLoot"
- "propername" "1"
+ "name" "The Classic"
+ "replace" "tf_weapon_compound_bow : 56 : 10 : 6"
}
+
+//////////////Sniper Secondary////////////////
+// Note: Shields cannot be represented by class
- "959"
+ "tf_weapon_smg"
{
- "name" "Silver Botkiller Knife Mk.II"
- "item_name" "#TF_Weapon_Knife_AdvancedMVMLoot_Engineer"
- "propername" "1"
+ "name" "Generic SMG"
+ "replace" "tf_weapon_jar : 58 : 5 : 6"
}
- "968"
- {
- "name" "Gold Botkiller Knife Mk.II"
- "item_name" "#TF_Weapon_Knife_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- }
-
-//////////////Heavy Melee//////////////////////
- "5"
+ "tf_weapon_jar"
{
- "name" "TF_WEAPON_FISTS"
- "item_name" "#TF_Weapon_Fists"
+ "name" "Jarate or Self-Aware Beauty Mark"
}
- "43"
- {
- "name" "The Killing Gloves of Boxing"
- "item_name" "#TF_Unique_Achievement_Fists"
- "propername" "1"
- }
+////////////////Sniper Melee//////////////////
- "195"
+ "tf_weapon_club"
{
- "name" "Upgradeable TF_WEAPON_FISTS"
- "item_name" "#TF_Weapon_Fists"
+ "name" "Generic Sniper Melee"
}
+
+//////////////////////////////////////////////
+//////////////////////Spy/////////////////////
+//////////////////////////////////////////////
+
+// Spy is not allowed in PropHunt, so don't waste time listing his items.
- "239"
+ }
+
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+////////////////Items by Index////////////////
+//////////////////////////////////////////////
+//////////////////////////////////////////////
+
+// This will override the classname section above
+ "items"
+ {
+
+////////////Shared Secondary////////////////////
+
+// Pyro gets Shotgun instead of Reserve Shooter
+ "415"
{
- "name" "Gloves of Running Urgently"
- "item_name" "#TF_Unique_Gloves_of_Running_Urgently"
+ "name" "The Reserve Shooter"
+ "damage_hunters" "0.8"
+ "self_damage_hunters" "8.0"
+ "replace" "tf_weapon_shotgun : 12 : 0 : 1"
+ "replace_onlyclasses" "64"
}
- "310"
+/////////////Scout Primary//////////////////////
+
+//Backscatter is cheesy in PropHunt
+ "1103"
{
- "name" "Warrior's Spirit"
- "item_name" "#TF_WarriorsSpirit"
+ "name" "The Back Scatter"
+ "item_name" "#TF_Weapon_BackScatter_Desc"
+ "propername" "1"
+ "replace" "tf_weapon_scattergun : 13 : 0 : 1"
}
- "331"
+////////////Soldier Primary//////////////////////
+
+// Override rocket launcher changes for Rocket Jumper
+ "237"
{
- "name" "Fists of Steel"
- "item_name" "#TF_FistsOfSteel"
+ "name" "Rocket Jumper"
+ "item_name" "#TF_Weapon_RocketLauncher_Jump"
}
- "426"
+////////////Soldier Secondary////////////////////
+
+ "133"
{
- "name" "The Eviction Notice"
- "item_name" "#TF_EvictionNotice"
- "propername" "1"
+ "name" "The Gunboats"
}
- "587"
+ "444"
{
- "name" "Apoco-Fists"
- "item_name" "#TF_Apocofists"
- "propername" "1"
+ "name" "The Mantreads"
}
-
- "656"
+
+/////////////Demo Primary////////////////////////
+// Boots can't be matched by class
+// Blocked Slot
+
+ "405"
{
- "name" "The Holiday Punch"
- "item_name" "#TF_MasculineMittens"
- "propername" "1"
+ "name" "Ali Baba's Wee Booties"
+ "removed_hunters" "1"
}
- "1084"
+ "608"
{
- "name" "Festive Gloves of Running Urgently"
- "item_name" "#TF_Weapon_GRU_Festive2013"
- "propername" "1"
+ "name" "The Bootlegger"
+ "removed_hunters" "1"
}
- "1100"
+////////////Demo Secondary///////////////////////
+
+// Override stickybomb launcher changes for Stickybomb Jumper
+ "265"
{
- "name" "The Bread Bite"
- "item_name" "#TF_Weapon_BreadBite"
- "propername" "1"
+ "name" "Stickybomb Jumper"
+ "item_name" "#TF_Weapon_StickyBomb_Jump"
}
-//////////////Soldier Melee////////////////////
- "6"
+////////////Engineer Primary/////////////////////
+
+// Reduce shots per clip for Panic Attack
+ "1153"
{
- "name" "TF_WEAPON_SHOVEL"
- "item_name" "#TF_Weapon_Shovel"
+ "name" "Panic Attack"
+ "item_name" "#TF_Weapon_PanicAttack"
+ "damage_hunters" "0.8"
+ "self_damage_hunters" "8.0"
+ "addattribs" "424 ; 0.5"
}
+
- "128"
+//////////////Engineer Melee////////////////////
+
+// Eureka Effect is broken in Arena
+ "589"
{
- "name" "The Equalizer"
- "item_name" "#TF_Unique_Achievement_Pickaxe"
+ "name" "The Eureka Effect"
+ "item_name" "#TF_Wrenchmotron"
"propername" "1"
+ "replace" "tf_weapon_wrench : 7 : 0 : 1"
}
-
- "154"
+
+//////////////Medic Melee///////////////////////
+
+// Amputator heals
+ "304"
{
- "name" "The Pain Train"
- "item_name" "#TF_Unique_Makeshiftclub"
+ "name" "The Amputator"
+ "item_name" "#TF_Amputator"
"propername" "1"
+ "replace" "tf_weapon_bonesaw : 8 : 0 : 1"
}
- "196"
- {
- "name" "Upgradeable TF_WEAPON_SHOVEL"
- "item_name" "#TF_Weapon_Shovel"
- }
-
- "416"
+// Solemn Vow breaks PropHunt
+ "413"
{
- "name" "The Market Gardener"
- "item_name" "#TF_MarketGardener"
+ "name" "The Solemn Vow"
+ "item_name" "#TF_SolemnVow"
"propername" "1"
+ "replace" "tf_weapon_bonesaw : 8 : 0 : 1"
}
- "447"
+////////////Sniper Secondary/////////////////////
+// Note: Shields cannot be represented by class
+
+// Razorback is useless in PropHunt
+// Give them the same thing we give SMG users
+ "57"
{
- "name" "The Disciplinary Action"
- "item_name" "#TF_DisciplinaryAction"
- "propername" "1"
+ "name" "The Razorback"
+ "replace" "tf_weapon_jar : 58 : 5 : 6"
}
- "775"
+ "231"
{
- "name" "The Escape Plan"
- "item_name" "#TF_Unique_Pickaxe_EscapePlan"
- "propername" "1"
+ "name" "Darwin's Danger Shield"
}
- "880"
+ "642"
{
- "name" "The Freedom Staff"
- "item_name" "#TF_TW_Staff"
- "propername" "1"
+ "name" "Cozy Camper"
}
-//////////////Engineer Melee////////////////////
- "7"
- {
- "name" "TF_WEAPON_WRENCH"
- "item_name" "#TF_Weapon_Wrench"
- }
-
- "142"
- {
- "name" "The Gunslinger"
- "item_name" "#TF_Unique_Robot_Arm"
- "propername" "1"
- }
-
- "155"
- {
- "name" "The Southern Hospitality"
- "item_name" "#TF_Unique_Combat_Wrench"
- "propername" "1"
- }
-
- "169"
- {
- "name" "Golden Wrench"
- "item_name" "#TF_Unique_Golden_Wrench"
- }
-
- "197"
- {
- "name" "Upgradeable TF_WEAPON_WRENCH"
- "item_name" "#TF_Weapon_Wrench"
- }
-
- "329"
- {
- "name" "The Jag"
- "item_name" "#TF_Jag"
- "propername" "1"
- }
-
- "589"
- {
- "name" "The Eureka Effect"
- "item_name" "#TF_Wrenchmotron"
- "propername" "1"
- "replace" "tf_weapon_wrench:7:0:1"
- }
-
- "662"
- {
- "name" "Festive Wrench 2011"
- "item_name" "#TF_Weapon_Wrench_Festive2011"
- }
-
- "795"
- {
- "name" "Silver Botkiller Wrench Mk.I"
- "item_name" "#TF_Weapon_Wrench_MVMLoot"
- "propername" "1"
- }
-
- "804"
- {
- "name" "Gold Botkiller Wrench Mk.I"
- "item_name" "#TF_Weapon_Wrench_MVMLootGold"
- "propername" "1"
- }
-
- "884"
- {
- "name" "Rust Botkiller Wrench"
- "item_name" "#TF_Weapon_Wrench_IntermediateMVMLoot"
- "propername" "1"
- }
-
- "893"
- {
- "name" "Blood Botkiller Wrench"
- "item_name" "#TF_Weapon_Wrench_IntermediateMVMRareLoot"
- "propername" "1"
- }
-
- "902"
- {
- "name" "Carbonado Botkiller Wrench"
- "item_name" "#TF_Weapon_Wrench_ExpertMVMLoot"
- "propername" "1"
- }
-
- "911"
- {
- "name" "Diamond Botkiller Wrench"
- "item_name" "#TF_Weapon_Wrench_ExpertMVMRareLoot"
- "propername" "1"
- }
-
- "960"
- {
- "name" "Silver Botkiller Wrench Mk.II"
- "item_name" "#TF_Weapon_Wrench_AdvancedMVMLoot_Engineer"
- "propername" "1"
- }
-
- "969"
- {
- "name" "Gold Botkiller Wrench Mk.II"
- "item_name" "#TF_Weapon_Wrench_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- }
-
-//////////////Medic Melee///////////////////////
- "8"
- {
- "name" "TF_WEAPON_BONESAW"
- "item_name" "#TF_Weapon_Bonesaw"
- }
-
- "37"
- {
- "name" "The Ubersaw"
- "item_name" "#TF_Unique_Achievement_Bonesaw1"
- "propername" "1"
- }
-
- "173"
- {
- "name" "The Vita-Saw"
- "item_name" "#TF_Unique_BattleSaw"
- "propername" "1"
- }
-
- "198"
- {
- "name" "Upgradeable TF_WEAPON_BONESAW"
- "item_name" "#TF_Weapon_Bonesaw"
- }
-
- "304"
- {
- "name" "The Amputator"
- "item_name" "#TF_Amputator"
- "propername" "1"
- "replace" "tf_weapon_bonesaw:8:0:1"
- }
-
- "413"
- {
- "name" "The Solemn Vow"
- "item_name" "#TF_SolemnVow"
- "propername" "1"
- "replace" "tf_weapon_bonesaw:8:0:1"
- }
-
- "1003"
- {
- "name" "Festive Ubersaw"
- "item_name" "#TF_Weapon_Ubersaw_Festive2012"
- }
-
- "1143"
- {
- "name" "Festive Bonesaw"
- "item_name" "#TF_Weapon_FestiveBonesaw"
- }
-
-//////////////Multi-class Melee///////////////////////
- "264"
- {
- "name" "Frying Pan"
- "item_name" "#TF_Weapon_FryingPan"
- }
-
- "423"
- {
- "name" "Saxxy"
- "item_name" "#TF_Saxxy"
- }
-
- "474"
- {
- "name" "The Conscientious Objector "
- "item_name" "#TF_ConscientiousObjector"
- }
-
- "880"
- {
- "name" "The Freedom Staff"
- "item_name" "#TF_TW_Staff"
- }
-
- "939"
- {
- "name" "The Bat Outta Hell"
- "item_name" "#TF_BatOuttaHell"
- }
-
- "954"
- {
- "name" "The Memory Maker"
- "item_name" "#TF_Memory_Maker"
- }
-
- "1013"
- {
- "name" "The Ham Shank"
- "item_name" "#TF_HamShank"
- }
-
- "1071"
- {
- "name" "Gold Frying Pan"
- "item_name" "#TF_Gold_FryingPan"
- }
-
- "1123"
- {
- "name" "Necro Smasher"
- "item_name" "#TF_Weapon_Necro_Smasher"
- }
-
- "1127"
- {
- "name" "The Crossing Guard"
- "item_name" "#TF_CrossingGuard"
- }
-
-
-
-////////////////////////////////////////////////
-/////////////Primary Weapons////////////////////
-////////////////////////////////////////////////
-
-/////////////Scout Primary//////////////////////
-
- "13"
- {
- "name" "TF_WEAPON_SCATTERGUN"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "45"
- {
- "name" "The Force-a-Nature"
- "item_name" "#TF_Unique_Achievement_Scattergun_Double"
- "propername" "1"
- }
-
- "200"
- {
- "name" "Upgradeable TF_WEAPON_SCATTERGUN"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "220"
- {
- "name" "The Shortstop"
- "item_name" "#TF_TheShortstop"
- "propername" "1"
- }
-
- "448"
- {
- "name" "The Soda Popper"
- "item_name" "#TF_SodaPopper"
- "propername" "1"
- }
-
- "669"
- {
- "name" "Festive Scattergun 2011"
- "item_name" "#TF_Weapon_Scattergun_Festive2011"
- }
-
- "772"
- {
- "name" "Baby Face's Blaster"
- "item_name" "#TF_Weapon_PEP_Scattergun"
- }
-
- "799"
- {
- "name" "Silver Botkiller Scattergun Mk.I"
- "item_name" "#TF_Weapon_Scattergun_MVMLoot"
- "propername" "1"
- }
-
- "808"
- {
- "name" "Gold Botkiller Scattergun Mk.I"
- "item_name" "#TF_Weapon_Scattergun_MVMLootGold"
- "propername" "1"
- }
-
- "888"
- {
- "name" "Rust Botkiller Scattergun"
- "item_name" "#TF_Weapon_Scattergun_IntermediateMVMLoot"
- "propername" "1"
- }
-
- "897"
- {
- "name" "Blood Botkiller Scattergun"
- "item_name" "#TF_Weapon_Scattergun_IntermediateMVMRareLoot"
- "propername" "1"
- }
-
- "906"
- {
- "name" "Carbonado Botkiller Scattergun"
- "item_name" "#TF_Weapon_Scattergun_ExpertMVMLoot"
- "propername" "1"
- }
-
- "915"
- {
- "name" "Diamond Botkiller Scattergun"
- "item_name" "#TF_Weapon_Scattergun_ExpertMVMRareLoot"
- "propername" "1"
- }
-
- "964"
- {
- "name" "Silver Botkiller Scattergun Mk.II"
- "item_name" "#TF_Weapon_Scattergun_AdvancedMVMLoot_Engineer"
- "propername" "1"
- }
-
- "973"
- {
- "name" "Gold Botkiller Scattergun Mk.II"
- "item_name" "#TF_Weapon_Scattergun_ADVANCEDMVMRareLoot_Engineer"
- "propername" "1"
- }
-
- "1078"
- {
- "name" "Festive Force-a-Nature"
- "item_name" "#TF_Weapon_ForceANature_Festive2013"
- "propername" "1"
- }
-
- "1103"
- {
- "name" "The Back Scatter"
- "item_name" "#TF_Weapon_BackScatter_Desc"
- "propername" "1"
- "replace" "tf_weapon_scattergun:13:0:1"
- }
-
- "15002"
- {
- "name" "concealedkiller_scattergun_nightterror"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "15015"
- {
- "name" "craftsmann_scattergun_tartantorpedo"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "15021"
- {
- "name" "craftsmann_scattergun_countrycrusher"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "15029"
- {
- "name" "craftsmann_scattergun_backcountryblaster"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "15036"
- {
- "name" "teufort_scattergun_sprucedeuce"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "15053"
- {
- "name" "powerhouse_scattergun_currentevent"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "16006"
- {
- "name" "template_civilian_scattergun"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "16018"
- {
- "name" "template_commando_scattergun"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
- "16029"
- {
- "name" "template_elite_scattergun"
- "item_name" "#TF_Weapon_Scattergun"
- }
-
-/////////////Demo Primary////////////////////////
-
- "19"
- {
- "name" "TF_WEAPON_GRENADELAUNCHER"
- "item_name" "#TF_Weapon_GrenadeLauncher"
- "removed_hunters" "1"
- }
-
- "206"
- {
- "name" "Upgradeable TF_WEAPON_GRENADELAUNCHER"
- "item_name" "#TF_Weapon_GrenadeLauncher"
- "removed_hunters" "1"
- }
-
- "308"
- {
- "name" "The Loch-n-Load"
- "item_name" "#TF_LochNLoad"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "405"
- {
- "name" "Ali Baba's Wee Booties"
- "item_name" "#TF_Ali_Babas_Wee_Booties"
- }
-
- "608"
- {
- "name" "The Bootlegger"
- "item_name" "#TF_Bootlegger"
- "propername" "1"
- }
-
- "996"
- {
- "name" "Loose Cannon"
- "item_name" "TF_Weapon_Cannon"
- "removed_hunters" "1"
- }
-
- "1007"
- {
- "name" "Festive Grenade Launcher"
- "item_name" "#TF_Weapon_GrenadeLauncher_Festive2012"
- "removed_hunters" "1"
- }
-
- "1151"
- {
- "name" "The Iron Bomber"
- "item_name" "#TF_Weapon_Iron_bomber"
- "removed_hunters" "1"
- }
-
-/////////////Pyro Primary////////////////////////
-
- "21"
- {
- "name" "TF_WEAPON_FLAMETHROWER"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "40"
- {
- "name" "The Backburner"
- "item_name" "#TF_Unique_Achievement_Flamethrower"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "1.0"
- }
-
- "208"
- {
- "name" "Upgradeable TF_WEAPON_FLAMETHROWER"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "215"
- {
- "name" "The Degreaser"
- "item_name" "#TF_TheDegreaser"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "594"
- {
- "name" "The Phlogistinator"
- "item_name" "#TF_Phlogistinator"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "659"
- {
- "name" "Festive Flamethrower 2011"
- "item_name" "#TF_Weapon_Flamethrower_Festive2011"
- "self_damage_hunters" "1.0"
- }
-
- "741"
- {
- "name" "The Rainblower"
- "item_name" "#TF_Rainblower"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "798"
- {
- "name" "Silver Botkiller Flame Thrower Mk.I"
- "item_name" "#TF_Weapon_Flamethrower_MVMLoot"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "807"
- {
- "name" "Gold Botkiller Flame Thrower Mk.I"
- "item_name" "#TF_Weapon_Flamethrower_MVMLootGold"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "887"
- {
- "name" "Rust Botkiller Flame Thrower"
- "item_name" "#TF_Weapon_Flamethrower_IntermediateMVMLoot"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "896"
- {
- "name" "Blood Botkiller Flame Thrower"
- "item_name" "#TF_Weapon_Flamethrower_IntermediateMVMRareLoot"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "905"
- {
- "name" "Carbonado Botkiller Flame Thrower"
- "item_name" "#TF_Weapon_Flamethrower_ExpertMVMLoot"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "914"
- {
- "name" "Diamond Botkiller Flame Thrower"
- "item_name" "#TF_Weapon_Flamethrower_ExpertMVMRareLoot"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "963"
- {
- "name" "Silver Botkiller Flame Thrower Mk.II"
- "item_name" "#TF_Weapon_Flamethrower_AdvancedMVMLoot_Engineer"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "972"
- {
- "name" "Gold Botkiller Flame Thrower Mk.II"
- "item_name" "#TF_Weapon_Flamethrower_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "30474"
- {
- "name" "The Nostromo Napalmer"
- "item_name" "#TF_NostromoNapalmer"
- "propername" "1"
- "self_damage_hunters" "1.0"
- }
-
- "1146"
- {
- "name" "Festive Backburner"
- "item_name" "#TF_Weapon_FestiveBackburner"
- "damage_hunters" "0.8"
- "self_damage_hunters" "1.0"
- }
-
- "15005"
- {
- "name" "concealedkiller_flamethrower_forestfire"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "15017"
- {
- "name" "craftsmann_flamethrower_barnburner"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "15030"
- {
- "name" "teufort_flamethrower_bovineblazemaker"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "15034"
- {
- "name" "teufort_flamethrower_earthskyandfire"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "15049"
- {
- "name" "powerhouse_flamethrower_flashfryer"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "15054"
- {
- "name" "powerhouse_flamethrower_turbinetorcher"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "16000"
- {
- "name" "template_civilian_flamethrower"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "16012"
- {
- "name" "template_commando_flamethrower"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
- "16023"
- {
- "name" "template_elite_flamethrower"
- "item_name" "#TF_Weapon_FlameThrower"
- "self_damage_hunters" "1.0"
- }
-
-/////////////Sniper Primary//////////////////////
-
- "14"
- {
- "name" "TF_WEAPON_SNIPERRIFLE"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "56"
- {
- "name" "The Huntsman"
- "item_name" "#TF_Unique_Achievement_CompoundBow"
- "propername" "1"
- "damage_hunters" "0.85"
- }
-
- "201"
- {
- "name" "Upgradeable TF_WEAPON_SNIPERRIFLE"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "230"
- {
- "name" "The Sydney Sleeper"
- "item_name" "#TF_SydneySleeper"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "402"
- {
- "name" "The Bazaar Bargain"
- "item_name" "#TF_BazaarBargain"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "526"
- {
- "name" "The Machina"
- "item_name" "#TF_DEX_Rifle"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "664"
- {
- "name" "Festive Sniper Rifle 2011"
- "item_name" "#TF_Weapon_SniperRifle_Festive2011"
- "removed_hunters" "1"
- }
-
- "752"
- {
- "name" "The Hitman's Heatmaker"
- "item_name" "#TF_Pro_SniperRifle"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "792"
- {
- "name" "Silver Botkiller Sniper Rifle Mk.I"
- "item_name" "#TF_Weapon_SniperRifle_MVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "801"
- {
- "name" "Gold Botkiller Sniper Rifle Mk.I"
- "item_name" "#TF_Weapon_SniperRifle_MVMLootGold"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "851"
- {
- "name" "The AWPer Hand"
- "item_name" "#TF_CSGO_AWP"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "881"
- {
- "name" "Rust Botkiller Sniper Rifle"
- "item_name" "#TF_Weapon_SniperRifle_IntermediateMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "890"
- {
- "name" "Blood Botkiller Sniper Rifle"
- "item_name" "#TF_Weapon_SniperRifle_IntermediateMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "899"
- {
- "name" "Carbonado Botkiller Sniper Rifle"
- "item_name" "#TF_Weapon_SniperRifle_ExpertMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "908"
- {
- "name" "Diamond Botkiller Sniper Rifle"
- "item_name" "#TF_Weapon_SniperRifle_ExpertMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "957"
- {
- "name" "Silver Botkiller Sniper Rifle Mk.II"
- "item_name" "#TF_Weapon_SniperRifle_AdvancedMVMLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "966"
- {
- "name" "Gold Botkiller Sniper Rifle Mk.II"
- "item_name" "#TF_Weapon_SniperRifle_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "1005"
- {
- "name" "Festive Huntsman"
- "item_name" "#TF_Weapon_Huntsman_Festive2012"
- "propername" "1"
- "damage_hunters" "0.85"
- }
-
- "1092"
- {
- "name" "The Fortified Compound"
- "item_name" "#TF_FortifiedCompound"
- "propername" "1"
- "damage_hunters" "0.85"
- }
-
- "1098"
- {
- "name" "The Classic"
- "item_name" "#TF_ClassicSniperRifle"
- "removed_hunters" "1"
- }
-
- "15000"
- {
- "name" "concealedkiller_sniperrifle_nightowl"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "15007"
- {
- "name" "concealedkiller_sniperrifle_purplerange"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "15019"
- {
- "name" "craftsmann_sniperrifle_lumberfromdownunder"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "15023"
- {
- "name" "craftsmann_sniperrifle_shotinthedark"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "15033"
- {
- "name" "teufort_sniperrifle_bogtrotter"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "15059"
- {
- "name" "powerhouse_sniperrifle_thunderbolt"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "16008"
- {
- "name" "template_civilian_sniperrifle"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "16020"
- {
- "name" "template_commando_sniperrifle"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "16031"
- {
- "name" "template_elite_sniperrifle"
- "item_name" "#TF_Weapon_SniperRifle"
- "removed_hunters" "1"
- }
-
- "30665"
- {
- "name" "Shooting Star"
- "item_name" "#TF_invasion_sniperrifle"
- "removed_hunters" "1"
- }
-
-/////////////Heavy Primary///////////////////////
-
- "15"
- {
- "name" "TF_WEAPON_MINIGUN"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "41"
- {
- "name" "Natascha"
- "item_name" "#TF_Unique_Achievement_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "202"
- {
- "name" "Upgradeable TF_WEAPON_MINIGUN"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "298"
- {
- "name" "Iron Curtain"
- "item_name" "#TF_Iron_Curtain"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "312"
- {
- "name" "The Brass Beast"
- "item_name" "#TF_GatlingGun"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "424"
- {
- "name" "Tomislav"
- "item_name" "#TF_Tomislav"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "654"
- {
- "name" "Festive Minigun 2011"
- "item_name" "#TF_Weapon_Minigun_Festive2011"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "793"
- {
- "name" "Silver Botkiller Minigun Mk.I"
- "item_name" "#TF_Weapon_Minigun_MVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "802"
- {
- "name" "Gold Botkiller Minigun Mk.I"
- "item_name" "#TF_Weapon_Minigun_MVMLootGold"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "811"
- {
- "name" "The Huo Long Heatmaker"
- "item_name" "#TF_SD_Minigun"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "832"
- {
- "name" "Promo Huo Long Heatmaker"
- "item_name" "#TF_SD_Minigun"
- "removed_hunters" "1"
- }
-
- "882"
- {
- "name" "Rust Botkiller Minigun"
- "item_name" "#TF_Weapon_Minigun_IntermediateMVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "891"
- {
- "name" "Blood Botkiller Minigun"
- "item_name" "#TF_Weapon_Minigun_IntermediateMVMRareLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "900"
- {
- "name" "Carbonado Botkiller Minigun"
- "item_name" "#TF_Weapon_Minigun_ExpertMVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "909"
- {
- "name" "Diamond Botkiller Minigun"
- "item_name" "#TF_Weapon_Minigun_ExpertMVMRareLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "958"
- {
- "name" "Silver Botkiller Minigun Mk.II"
- "propername" "1"
- "item_name" "#TF_Weapon_Minigun_AdvancedMVMLoot_Engineer"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "967"
- {
- "name" "Gold Botkiller Minigun Mk.II"
- "propername" "1"
- "item_name" "#TF_Weapon_Minigun_AdvancedMVMRareLoot_Engineer"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15004"
- {
- "name" "concealedkiller_minigun_kingofthejungle"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15020"
- {
- "name" "craftsmann_minigun_ironwood"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15026"
- {
- "name" "craftsmann_minigun_antiqueannihilator"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15031"
- {
- "name" "teufort_minigun_warroom"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15040"
- {
- "name" "teufort_minigun_citizenpain"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "15055"
- {
- "name" "powerhouse_minigun_brickhouse"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "16002"
- {
- "name" "template_civilian_minigun"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "16014"
- {
- "name" "template_commando_minigun"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
- "16025"
- {
- "name" "template_elite_minigun"
- "item_name" "#TF_Weapon_Minigun"
- "damage_hunters" "0.75"
- "self_damage_hunters" "2.0"
- "removed_hunters" "1"
- }
-
-////////////Soldier Primary//////////////////////
-
- "18"
- {
- "name" "TF_WEAPON_ROCKETLAUNCHER"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "127"
- {
- "name" "The Direct Hit"
- "item_name" "#TF_Unique_Achievement_RocketLauncher"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "205"
- {
- "name" "Upgradeable TF_WEAPON_ROCKETLAUNCHER"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "228"
- {
- "name" "The Black Box"
- "item_name" "#TF_TheBlackBox"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "237"
- {
- "name" "Rocket Jumper"
- "item_name" "#TF_Weapon_RocketLauncher_Jump"
- }
-
- "414"
- {
- "name" "The Liberty Launcher"
- "item_name" "#TF_LibertyLauncher"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "441"
- {
- "name" "The Cow Mangler 5000"
- "item_name" "#TF_CowMangler"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "513"
- {
- "name" "The Original"
- "item_name" "#TF_TheOriginal"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "658"
- {
- "name" "Festive Rocket Launcher 2011"
- "item_name" "#TF_Weapon_RocketLauncher_Festive2011"
- "damage_hunters" "0.75"
- }
-
- "730"
- {
- "name" "The Beggar's Bazooka"
- "item_name" "#TF_DS_DumpsterDevice"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "800"
- {
- "name" "Silver Botkiller Rocket Launcher Mk.I"
- "item_name" "#TF_Weapon_RocketLauncher_MVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "809"
- {
- "name" "Gold Botkiller Rocket Launcher Mk.I"
- "item_name" "#TF_Weapon_RocketLauncher_MVMLootGold"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "889"
- {
- "name" "Rust Botkiller Rocket Launcher"
- "item_name" "#TF_Weapon_RocketLauncher_IntermediateMVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "898"
- {
- "name" "Blood Botkiller Rocket Launcher"
- "item_name" "#TF_Weapon_RocketLauncher_IntermediateMVMRareLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "907"
- {
- "name" "Carbonado Botkiller Rocket Launcher"
- "item_name" "#TF_Weapon_RocketLauncher_ExpertMVMLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "916"
- {
- "name" "Diamond Botkiller Rocket Launcher"
- "item_name" "#TF_Weapon_RocketLauncher_ExpertMVMRareLoot"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "965"
- {
- "name" "Silver Botkiller Rocket Launcher Mk.II"
- "item_name" "#TF_Weapon_RocketLauncher_AdvancedMVMLoot_Engineer"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "974"
- {
- "name" "Gold Botkiller Rocket Launcher Mk.II"
- "item_name" "#TF_Weapon_RocketLauncher_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "1085"
- {
- "name" "Festive Black Box"
- "item_name" "#TF_Weapon_Blackbox_Festive2013"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "1104"
- {
- "name" "The Air Strike"
- "item_name" "#TF_Weapon_AirStrike"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "15006"
- {
- "name" "concealedkiller_rocketlauncher_woodlandwarrior"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "15014"
- {
- "name" "concealedkiller_rocketlauncher_sandcannon"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "15028"
- {
- "name" "craftsmann_rocketlauncher_americanpastoral"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "15043"
- {
- "name" "teufort_rocketlauncher_smalltownbringdown"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "15052"
- {
- "name" "powerhouse_rocketlauncher_shellshocker"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "15057"
- {
- "name" "powerhouse_rocketlauncher_aquamarine"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "16005"
- {
- "name" "template_civilian_rocketlauncher"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "16017"
- {
- "name" "template_commando_rocketlauncher"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
- "16028"
- {
- "name" "template_elite_rocketlauncher"
- "item_name" "#TF_Weapon_RocketLauncher"
- "damage_hunters" "0.75"
- }
-
-////////////Engineer Primary/////////////////////
-
- "9"
- {
- "name" "TF_WEAPON_SHOTGUN_PRIMARY"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "141"
- {
- "name" "The Frontier Justice"
- "item_name" "#TF_Unique_Sentry_Shotgun"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "199"
- {
- "name" "Upgradeable TF_WEAPON_SHOTGUN_PRIMARY"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "527"
- {
- "name" "The Widowmaker"
- "item_name" "#TF_DEX_Shotgun"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "588"
- {
- "name" "The Pomson 6000"
- "item_name" "#TF_Pomson"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "997"
- {
- "name" "Rescue Ranger"
- "item_name" "#TF_Weapon_Shotgun_Building_Rescue"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "1004"
- {
- "name" "Festive Frontier Justice"
- "item_name" "#TF_Weapon_FrontierJustice_Festive2012"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "1141"
- {
- "name" "Festive Shotgun"
- "item_name" "#TF_Weapon_FestiveShotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "1153"
- {
- "name" "Panic Attack"
- "item_name" "#TF_Weapon_PanicAttack"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- "addattribs" "424 ; 0.5"
- }
-
- "15003"
- {
- "name" "concealedkiller_shotgun_backwoodsboomstick"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "15016"
- {
- "name" "craftsmann_shotgun_rusticruiner"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "15044"
- {
- "name" "teufort_shotgun_civicduty"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "15047"
- {
- "name" "powerhouse_shotgun_lightningrod"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "16007"
- {
- "name" "template_civilian_shotgun"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "16019"
- {
- "name" "template_commando_shotgun"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "16030"
- {
- "name" "template_elite_shotgun"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
-////////////Medic Primary////////////////////////
-
- "17"
- {
- "name" "TF_WEAPON_SYRINGEGUN_MEDIC"
- "item_name" "#TF_Weapon_SyringeGun"
- "self_damage_hunters" "4.0"
- }
-
- "36"
- {
- "name" "The Blutsauger"
- "item_name" "#TF_Unique_Achievement_Syringegun1"
- "propername" "1"
- "removed_hunters" "1"
- "self_damage_hunters" "4.0"
- }
-
- "204"
- {
- "name" "Upgradeable TF_WEAPON_SYRINGEGUN_MEDIC"
- "item_name" "#TF_Weapon_SyringeGun"
- "self_damage_hunters" "4.0"
- }
-
- "305"
- {
- "name" "The Crusader's Crossbow"
- "item_name" "#TF_CrusadersCrossbow"
- "propername" "1"
- "replace" "tf_weapon_syringegun_medic:17:0:1"
- }
-
- "412"
- {
- "name" "The Overdose"
- "item_name" "#TF_Overdose"
- "self_damage_hunters" "4.0"
- "propername" "1"
- }
-
- "1079"
- {
- "name" "Festive Crusader's Crossbow"
- "item_name" "#TF_Weapon_CrusaderCrossbow_Festive2013"
- "propername" "1"
- "replace" "tf_weapon_syringegun_medic:17:0:1"
- }
-
-/////////////////////////////////////////////////
-////////////Secondary Wepons/////////////////////
-/////////////////////////////////////////////////
-
-////////////Scout Secondary//////////////////////
-
- "23"
- {
- "name" "TF_WEAPON_PISTOL_SCOUT"
- "item_name" "#TF_Weapon_Pistol"
- }
-
- "46"
- {
- "name" "Bonk! Atomic Punch"
- "item_name" "#TF_Unique_Achievement_EnergyDrink"
- "removed_hunters" "1"
- }
-
- "160"
- {
- "name" "Vintage Lugermorph"
- "item_name" "#TF_TTG_MaxGun"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "163"
- {
- "name" "Crit-a-Cola"
- "item_name" "#TF_Unique_EnergyDrink_CritCola"
- "removed_hunters" "1"
- }
-
- "222"
- {
- "name" "Mad Milk"
- "item_name" "#TF_MadMilk"
- "propername" "1"
- }
-
- "294"
- {
- "name" "Lugermorph"
- "item_name" "#TF_TTG_MaxGun"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "449"
- {
- "name" "The Winger"
- "item_name" "#TF_Winger"
- "propername" "1"
- }
-
- "773"
- {
- "name" "Pretty Boy's Pocket Pistol"
- "item_name" "#TF_Weapon_PEP_Pistol"
- "removed_hunters" "1"
- }
-
- "812"
- {
- "name" "The Flying Guillotine"
- "item_name" "#TF_SD_Cleaver"
- "propername" "1"
- }
-
- "833"
- {
- "name" "Promo Flying Guillotine"
- "item_name" "#TF_SD_Cleaver"
- "propername" "1"
- }
-
- "1121"
- {
- "name" "Mutated Milk"
- "item_name" "#TF_MadMilk"
- "propername" "1"
- }
-
- "1145"
- {
- "name" "Festive Bonk"
- "item_name" "#TF_Weapon_FestiveBonk"
- "removed_hunters" "1"
- }
-
-////////////Demo Secondary///////////////////////
-
- "20"
- {
- "name" "TF_WEAPON_PIPEBOMBLAUNCHER"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "207"
- {
- "name" "Upgradeable TF_WEAPON_PIPEBOMBLAUNCHER"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "130"
- {
- "name" "The Scottish Resistance"
- "item_name" "#TF_Unique_Achievement_StickyLauncher"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "131"
- {
- "name" "The Chargin' Targe"
- "item_name" "#TF_Unique_Achievement_Shield"
- "propername" "1"
- }
-
- "265"
- {
- "name" "Stickybomb Jumper"
- "item_name" "#TF_Weapon_StickyBomb_Jump"
- }
-
- "406"
- {
- "name" "The Splendid Screen"
- "item_name" "#TF_SplendidScreen"
- "propername" "1"
- "damage_hunters" "0.75"
- }
-
- "661"
- {
- "name" "Festive Stickybomb Launcher 2011"
- "item_name" "#TF_Weapon_StickybombLauncher_Festive2011"
- "removed_hunters" "1"
- }
-
- "797"
- {
- "name" "Silver Botkiller Stickybomb Launcher Mk.I"
- "item_name" "#TF_Weapon_StickybombLauncher_MVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "806"
- {
- "name" "Gold Botkiller Stickybomb Launcher Mk.I"
- "item_name" "#TF_Weapon_StickybombLauncher_MVMLootGold"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "886"
- {
- "name" "Rust Botkiller Stickybomb Launcher"
- "item_name" "#TF_Weapon_StickybombLauncher_IntermediateMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "895"
- {
- "name" "Blood Botkiller Stickybomb Launcher"
- "item_name" "#TF_Weapon_StickybombLauncher_IntermediateMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "904"
- {
- "name" "Carbonado Botkiller Stickybomb Launcher"
- "item_name" "#TF_Weapon_StickybombLauncher_ExpertMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "913"
- {
- "name" "Diamond Botkiller Stickybomb Launcher"
- "item_name" "#TF_Weapon_StickybombLauncher_ExpertMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "962"
- {
- "name" "Silver Botkiller Stickybomb Launcher Mk.II"
- "item_name" "#TF_Weapon_Stickybomblauncher_AdvancedMVMLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "971"
- {
- "name" "Gold Botkiller Stickybomb Launcher Mk.II"
- "item_name" "#TF_Weapon_Stickybomblauncher_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "1099"
- {
- "name" "The Tide Turner"
- "item_name" "#TF_Wearable_Shield"
- "propername" "1"
- }
-
- "1144"
- {
- "name" "Festive Targe"
- "item_name" "#TF_Weapon_FestiveTarge"
- }
-
- "1150"
- {
- "name" "The Quickiebomb Launcher"
- "item_name" "#TF_Weapon_Sticky_Quickie"
- "removed_hunters" "1"
- }
-
- "15009"
- {
- "name" "concealedkiller_stickybomblauncher_suddenflurry"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "15012"
- {
- "name" "concealedkiller_stickybomblauncher_carpetbomber"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "15024"
- {
- "name" "craftsmann_stickybomblauncher_blastedbombardier"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "15038"
- {
- "name" "teufort_stickybomblauncher_rooftopwrangler"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "15045"
- {
- "name" "powerhouse_stickybomblauncher_liquidasset"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "15048"
- {
- "name" "powerhouse_stickybomblauncher_pinkelephant"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "16010"
- {
- "name" "template_civilian_stickybomb_launcher"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "16022"
- {
- "name" "template_commando_stickybomb_launcher"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
- "16033"
- {
- "name" "template_elite_stickybomb_launcher"
- "item_name" "#TF_Weapon_PipebombLauncher"
- "removed_hunters" "1"
- }
-
-////////////Pyro Secondary///////////////////////
-
- "12"
- {
- "name" "TF_WEAPON_SHOTGUN_PYRO"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "39"
- {
- "name" "The Flare Gun"
- "item_name" "#TF_Unique_Achievement_Flaregun"
- "propername" "1"
- "self_damage_hunters" "5.0"
- }
-
- "351"
- {
- "name" "The Detonator"
- "item_name" "#TF_Weapon_Flaregun_Detonator"
- "propername" "1"
- "damage_hunters" "0.95"
- "self_damage_hunters" "5.0"
- }
-
- "595"
- {
- "name" "The Manmelter"
- "item_name" "#TF_ManMelter"
- "propername" "1"
- "self_damage_hunters" "5.0"
- }
-
- "740"
- {
- "name" "The Scorch Shot"
- "item_name" "#TF_ScorchShot"
- "propername" "1"
- "self_damage_hunters" "5.0"
- }
-
- "1081"
- {
- "name" "Festive Flare Gun"
- "item_name" "#TF_Weapon_Flaregun_Festive2013"
- "propername" "1"
- "self_damage_hunters" "5.0"
- }
-
-////////////Sniper Secondary/////////////////////
-
- "16"
- {
- "name" "TF_WEAPON_SMG"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "57"
- {
- "name" "The Razorback"
- "item_name" "#TF_Unique_Backstab_Shield"
- "propername" "1"
- }
-
- "58"
- {
- "name" "Jarate"
- "item_name" "#TF_Unique_Achievement_Jar"
- }
-
- "203"
- {
- "name" "Upgradeable TF_WEAPON_SMG"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "231"
- {
- "name" "Darwin's Danger Shield"
- "item_name" "#TF_DarwinsDangerShield"
- }
-
- "751"
- {
- "name" "The Cleaner's Carbine"
- "item_name" "#TF_Pro_SMG"
- "propername" "1"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "1083"
- {
- "name" "Festive Jarate"
- "item_name" "#TF_Weapon_Jarate_Festive2013"
- "propername" "1"
- }
-
- "1105"
- {
- "name" "The Self-Aware Beauty Mark"
- "item_name" "#TF_Weapon_SelfAwareBeautyMark"
- "propername" "1"
- }
-
- "1149"
- {
- "name" "Festive SMG"
- "item_name" "#TF_Weapon_FestiveSMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "15001"
- {
- "name" "concealedkiller_smg_woodsywidowmaker"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "15022"
- {
- "name" "craftsmann_smg_plaidpotshotter"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "15032"
- {
- "name" "teufort_smg_treadplatetormenter"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "15037"
- {
- "name" "teufort_smg_teamsprayer"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "15058"
- {
- "name" "powerhouse_smg_lowprofile"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "16009"
- {
- "name" "template_civilian_smg"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "16021"
- {
- "name" "template_commando_smg"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
- "16032"
- {
- "name" "template_elite_smg"
- "item_name" "#TF_Weapon_SMG"
- "removed_hunters" "1"
- "self_damage_hunters" "1.0"
- }
-
-/////////////Spy Secondary///////////////////////
-
- "24"
- {
- "name" "TF_WEAPON_REVOLVER"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "61"
- {
- "name" "The Ambassador"
- "item_name" "#TF_Unique_Achievement_Revolver"
- "propername" "1"
- }
-
- "161"
- {
- "name" "TTG Sam Revolver"
- "item_name" "#TF_TTG_SamRevolver"
- "propername" "1"
- }
-
- "210"
- {
- "name" "Upgradeable TF_WEAPON_REVOLVER"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "224"
- {
- "name" "L'Etranger"
- "item_name" "#TF_LEtranger"
- }
-
- "460"
- {
- "name" "The Enforcer"
- "item_name" "#TF_Enforcer"
- "propername" "1"
- }
-
- "525"
- {
- "name" "The Diamondback"
- "item_name" "#TF_DEX_Revolver"
- "propername" "1"
- }
-
- "1006"
- {
- "name" "Festive Ambassador"
- "item_name" "#TF_Weapon_Ambassador_Festive2012"
- }
-
- "1142"
- {
- "name" "Festive Revolver"
- "item_name" "#TF_Weapon_FestiveRevolver"
- }
-
- "15011"
- {
- "name" "concealedkiller_revolver_psychedelicslugger"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "15027"
- {
- "name" "craftsmann_revolver_oldcountry"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "15042"
- {
- "name" "teufort_revolver_mayor"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "15051"
- {
- "name" "powerhouse_revolver_deadreckoner"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "16004"
- {
- "name" "template_civilian_revolver"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "16016"
- {
- "name" "template_commando_revolver"
- "item_name" "#TF_Weapon_Revolver"
- }
-
- "16027"
- {
- "name" "template_elite_revolver"
- "item_name" "#TF_Weapon_Revolver"
- }
-
-////////////Heavy Secondary//////////////////////
-
- "11"
- {
- "name" "TF_WEAPON_SHOTGUN_HWG"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "42"
- {
- "name" "The Sandvich"
- "item_name" "#TF_Unique_Achievement_LunchBox"
- "propername" "1"
- }
-
- "159"
- {
- "name" "The Dalokohs Bar"
- "item_name" "#TF_Unique_Lunchbox_Chocolate"
- "propername" "1"
- }
-
- "311"
- {
- "name" "The Buffalo Steak Sandvich"
- "item_name" "#TF_BuffaloSteak"
- "propername" "1"
- }
-
- "425"
- {
- "name" "The Family Business"
- "item_name" "#TF_RussianRiot"
- "propername" "1"
- "self_damage_hunters" "8.0"
- "removed_hunters" "1"
- }
-
- "433"
- {
- "name" "Fishcake"
- "item_name" "#TF_SpaceChem_Fishcake"
- }
-
- "863"
- {
- "name" "The Robo-Sandvich"
- "item_name" "#TF_Robot_Sandvich"
- "propername" "1"
- }
-
- "1002"
- {
- "name" "Festive Sandvich"
- "item_name" "#TF_Weapon_Sandvich_Festive2012"
- }
-
-////////////Soldier Secondary////////////////////
-
- "10"
- {
- "name" "TF_WEAPON_SHOTGUN_SOLDIER"
- "item_name" "#TF_Weapon_Shotgun"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- }
-
- "129"
- {
- "name" "The Buff Banner"
- "item_name" "#TF_Unique_Achievement_SoldierBuff"
- "propername" "1"
- }
-
- "133"
- {
- "name" "The Gunboats"
- "item_name" "#TF_Unique_Blast_Boots"
- "propername" "1"
- }
-
- "226"
- {
- "name" "The Battalion's Backup"
- "item_name" "#TF_TheBattalionsBackup"
- "propername" "1"
- }
-
- "354"
- {
- "name" "The Concheror"
- "item_name" "#TF_SoldierSashimono"
- "propername" "1"
- }
-
- "415"
- {
- "name" "The Reserve Shooter"
- "item_name" "#TF_ReserveShooter"
- "propername" "1"
- "damage_hunters" "0.8"
- "self_damage_hunters" "8.0"
- "replace" "tf_weapon_shotgun:12:0:1"
- "replace_onlyclasses" "64"
- }
-
- "442"
- {
- "name" "The Righteous Bison"
- "item_name" "#TF_RighteousBison"
- "propername" "1"
- }
-
- "444"
- {
- "name" "The Mantreads"
- "item_name" "#TF_Mantreads"
- "propername" "1"
- }
-
- "1001"
- {
- "name" "Festive Buff Banner"
- "item_name" "#TF_Weapon_BuffBanner_Festive2012"
- "propername" "1"
- }
-
- "1101"
- {
- "name" "The B.A.S.E. Jumper"
- "item_name" "#TF_Weapon_Parachute"
- "propername" "1"
- }
-
-////////////Engineer Secondary////////////////////
-
- "22"
- {
- "name" "TF_WEAPON_PISTOL"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "140"
- {
- "name" "The Wrangler"
- "item_name" "#TF_Unique_Achievement_Laser_Pointer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "209"
- {
- "name" "Upgradeable TF_WEAPON_PISTOL"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "528"
- {
- "name" "The Short Circuit"
- "item_name" "#TF_DEX_Pistol"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "1086"
- {
- "name" "Festive Wrangler"
- "item_name" "#TF_Weapon_Wrangler_Festive2013"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "15013"
- {
- "name" "concealedkiller_pistol_redrockroscoe"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "15018"
- {
- "name" "craftsmann_pistol_homemadeheater"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "15035"
- {
- "name" "teufort_pistol_hickoryholepuncher"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "15041"
- {
- "name" "teufort_pistol_localhero"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "15046"
- {
- "name" "powerhouse_pistol_blackdahlia"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "15056"
- {
- "name" "powerhouse_pistol_sandstonespecial"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "16003"
- {
- "name" "template_civilian_pistol"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "16015"
- {
- "name" "template_commando_pistol"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "16026"
- {
- "name" "template_elite_pistol"
- "item_name" "#TF_Weapon_Pistol"
- "removed_hunters" "1"
- }
-
- "30666"
- {
- "name" "The C.A.P.P.E.R."
- "item_name" "#TF_invasion_pistol"
- "removed_hunters" "1"
- }
-
- "30668"
- {
- "name" "The Gigar Counter"
- "item_name" "#TF_invasion_wrangler"
- "propername" "1"
- "removed_hunters" "1"
- }
-
-///////////Medic Secondary////////////////////////
-
- "29"
- {
- "name" "TF_WEAPON_MEDIGUN"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "35"
- {
- "name" "The Kritzkrieg"
- "item_name" "#TF_Unique_Achievement_Medigun1"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "211"
- {
- "name" "Upgradeable TF_WEAPON_MEDIGUN"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "411"
- {
- "name" "The Quick-Fix"
- "item_name" "#TF_Unique_MediGun_QuickFix"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "663"
- {
- "name" "Festive Medigun 2011"
- "item_name" "#TF_Weapon_Medigun_Festive2011"
- "removed_hunters" "1"
- }
-
- "796"
- {
- "name" "Silver Botkiller Medi Gun Mk.I"
- "item_name" "#TF_Weapon_Medigun_MVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "805"
- {
- "name" "Gold Botkiller Medi Gun Mk.I"
- "item_name" "#TF_Weapon_Medigun_MVMLootGold"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "885"
- {
- "name" "Rust Botkiller Medi Gun"
- "item_name" "#TF_Weapon_Medigun_IntermediateMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "894"
- {
- "name" "Blood Botkiller Medi Gun"
- "item_name" "#TF_Weapon_Medigun_IntermediateMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "903"
- {
- "name" "Carbonado Botkiller Medi Gun"
- "item_name" "#TF_Weapon_Medigun_ExpertMVMLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "912"
- {
- "name" "Diamond Botkiller Medi Gun"
- "item_name" "#TF_Weapon_Medigun_ExpertMVMRareLoot"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "961"
- {
- "name" "Silver Botkiller Medi gun Mk.II"
- "item_name" "#TF_Weapon_Medigun_AdvancedMVMLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "970"
- {
- "name" "Gold Botkiller Medi Gun Mk.II"
- "item_name" "#TF_Weapon_Medigun_AdvancedMVMRareLoot_Engineer"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "998"
- {
- "name" "Vaccinator"
- "item_name" "#TF_Unique_Medigun_Resist"
- "propername" "1"
- "removed_hunters" "1"
- }
-
- "15008"
- {
- "name" "concealedkiller_medigun_maskedmender"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "15010"
- {
- "name" "concealedkiller_medigun_wrappedreviver"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "15025"
- {
- "name" "craftsmann_medigun_reclaimedreanimator"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "15039"
- {
- "name" "teufort_medigun_civilservant"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "15050"
- {
- "name" "powerhouse_medigun_sparkoflife"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "16001"
- {
- "name" "template_civilian_medigun"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "16013"
- {
- "name" "template_commando_medigun"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
- "16024"
- {
- "name" "template_elite_medigun"
- "item_name" "#TF_Weapon_Medigun"
- "removed_hunters" "1"
- }
-
-//////////////////////////////////////////////////
-//////////////////PDAs////////////////////////////
-//////////////////////////////////////////////////
-
-///////////////Engineer PDAs//////////////////////
-
- "25"
- {
- "name" "TF_WEAPON_PDA_ENGINEER_BUILD"
- "item_name" "#TF_Weapon_PDA_Engineer"
- }
-
- "26"
- {
- "name" "TF_WEAPON_PDA_ENGINEER_DESTROY"
- "item_name" "#TF_Weapon_PDA_Engineer"
- }
-
- "28"
- {
- "name" "TF_WEAPON_BUILDER"
- "item_name" "#TF_Weapon_PDA_Engineer"
- }
-
-//////////////Spy's PDAs//////////////////////////
-
- "27"
- {
- "name" "TF_WEAPON_PDA_SPY"
- "item_name" "#TF_Weapon_PDA_Engineer"
- }
-
- "30"
- {
- "name" "TF_WEAPON_INVIS"
- "item_name" "#TF_Weapon_Watch"
- }
-
- "59"
- {
- "name" "The Dead Ringer"
- "item_name" "#TF_Unique_Achievement_FeignWatch"
- "propername" "1"
- }
-
- "60"
- {
- "name" "The Cloak and Dagger"
- "item_name" "#TF_Unique_Achievement_CloakWatch"
- "propername" "1"
- }
-
- "212"
- {
- "name" "Upgradeable TF_WEAPON_INVIS"
- "item_name" "#TF_Weapon_Watch"
- }
-
- "735"
- {
- "name" "TF_WEAPON_BUILDER_SPY"
- "item_name" "#TF_Weapon_Spy_Sapper"
- }
-
- "736"
- {
- "name" "Upgradeable TF_WEAPON_BUILDER_SPY"
- "item_name" "#TF_Weapon_Spy_Sapper"
- }
-
- "810"
- {
- "name" "The Red-Tape Recorder"
- "item_name" "#TF_SD_Sapper"
- "propername" "1"
- }
-
- "831"
- {
- "name" "Promo Red-Tape Recorder"
- "item_name" "#TF_SD_Sapper"
- }
-
- "1080"
- {
- "name" "Festive Sapper"
- "item_name" "#TF_Weapon_Sapper_Festive2013"
- "propername" "1"
- }
-
- "1102"
- {
- "name" "The Snack Attack"
- "item_name" "#TF_Weapon_SnackAttack"
- "propername" "1"
- }
-
-//////////Misc???///////////////////////////////
-
}
}
\ No newline at end of file
diff --git a/addons/sourcemod/gamedata/tf2-roundend.games.txt b/addons/sourcemod/gamedata/tf2-roundend.games.txt
deleted file mode 100644
index 7fb0007..0000000
--- a/addons/sourcemod/gamedata/tf2-roundend.games.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-"Games"
-{
-
- "tf"
- {
- "Offsets"
-
- {
- "SetWinningTeam"
- {
- "windows" "160"
- "linux" "161"
- }
- }
- }
-}
diff --git a/addons/sourcemod/gamedata/tf2-switch-teams.txt b/addons/sourcemod/gamedata/tf2-switch-teams.txt
new file mode 100644
index 0000000..a00c31d
--- /dev/null
+++ b/addons/sourcemod/gamedata/tf2-switch-teams.txt
@@ -0,0 +1,28 @@
+"Games"
+{
+
+ "tf"
+ {
+ "Offsets"
+
+ {
+ "CTFGameRules::SetSwitchTeams"
+ {
+ "windows" "162"
+ "linux" "163"
+ }
+
+ "CTFGameRules::ShouldSwitchTeams"
+ {
+ "windows" "163"
+ "linux" "164"
+ }
+
+ "CTFGameRules::HandleSwitchTeams"
+ {
+ "windows" "164"
+ "linux" "165"
+ }
+ }
+ }
+}
diff --git a/addons/sourcemod/scripting/include/SteamWorks.inc b/addons/sourcemod/scripting/include/SteamWorks.inc
new file mode 100644
index 0000000..02e69af
--- /dev/null
+++ b/addons/sourcemod/scripting/include/SteamWorks.inc
@@ -0,0 +1,311 @@
+#if defined _SteamWorks_Included
+ #endinput
+#endif
+#define _SteamWorks_Included
+
+/* results from UserHasLicenseForApp */
+enum EUserHasLicenseForAppResult
+{
+ k_EUserHasLicenseResultHasLicense = 0, // User has a license for specified app
+ k_EUserHasLicenseResultDoesNotHaveLicense = 1, // User does not have a license for the specified app
+ k_EUserHasLicenseResultNoAuth = 2, // User has not been authenticated
+};
+
+/* General result codes */
+enum EResult
+{
+ k_EResultOK = 1, // success
+ k_EResultFail = 2, // generic failure
+ k_EResultNoConnection = 3, // no/failed network connection
+// k_EResultNoConnectionRetry = 4, // OBSOLETE - removed
+ k_EResultInvalidPassword = 5, // password/ticket is invalid
+ k_EResultLoggedInElsewhere = 6, // same user logged in elsewhere
+ k_EResultInvalidProtocolVer = 7, // protocol version is incorrect
+ k_EResultInvalidParam = 8, // a parameter is incorrect
+ k_EResultFileNotFound = 9, // file was not found
+ k_EResultBusy = 10, // called method busy - action not taken
+ k_EResultInvalidState = 11, // called object was in an invalid state
+ k_EResultInvalidName = 12, // name is invalid
+ k_EResultInvalidEmail = 13, // email is invalid
+ k_EResultDuplicateName = 14, // name is not unique
+ k_EResultAccessDenied = 15, // access is denied
+ k_EResultTimeout = 16, // operation timed out
+ k_EResultBanned = 17, // VAC2 banned
+ k_EResultAccountNotFound = 18, // account not found
+ k_EResultInvalidSteamID = 19, // steamID is invalid
+ k_EResultServiceUnavailable = 20, // The requested service is currently unavailable
+ k_EResultNotLoggedOn = 21, // The user is not logged on
+ k_EResultPending = 22, // Request is pending (may be in process, or waiting on third party)
+ k_EResultEncryptionFailure = 23, // Encryption or Decryption failed
+ k_EResultInsufficientPrivilege = 24, // Insufficient privilege
+ k_EResultLimitExceeded = 25, // Too much of a good thing
+ k_EResultRevoked = 26, // Access has been revoked (used for revoked guest passes)
+ k_EResultExpired = 27, // License/Guest pass the user is trying to access is expired
+ k_EResultAlreadyRedeemed = 28, // Guest pass has already been redeemed by account, cannot be acked again
+ k_EResultDuplicateRequest = 29, // The request is a duplicate and the action has already occurred in the past, ignored this time
+ k_EResultAlreadyOwned = 30, // All the games in this guest pass redemption request are already owned by the user
+ k_EResultIPNotFound = 31, // IP address not found
+ k_EResultPersistFailed = 32, // failed to write change to the data store
+ k_EResultLockingFailed = 33, // failed to acquire access lock for this operation
+ k_EResultLogonSessionReplaced = 34,
+ k_EResultConnectFailed = 35,
+ k_EResultHandshakeFailed = 36,
+ k_EResultIOFailure = 37,
+ k_EResultRemoteDisconnect = 38,
+ k_EResultShoppingCartNotFound = 39, // failed to find the shopping cart requested
+ k_EResultBlocked = 40, // a user didn't allow it
+ k_EResultIgnored = 41, // target is ignoring sender
+ k_EResultNoMatch = 42, // nothing matching the request found
+ k_EResultAccountDisabled = 43,
+ k_EResultServiceReadOnly = 44, // this service is not accepting content changes right now
+ k_EResultAccountNotFeatured = 45, // account doesn't have value, so this feature isn't available
+ k_EResultAdministratorOK = 46, // allowed to take this action, but only because requester is admin
+ k_EResultContentVersion = 47, // A Version mismatch in content transmitted within the Steam protocol.
+ k_EResultTryAnotherCM = 48, // The current CM can't service the user making a request, user should try another.
+ k_EResultPasswordRequiredToKickSession = 49,// You are already logged in elsewhere, this cached credential login has failed.
+ k_EResultAlreadyLoggedInElsewhere = 50, // You are already logged in elsewhere, you must wait
+ k_EResultSuspended = 51, // Long running operation (content download) suspended/paused
+ k_EResultCancelled = 52, // Operation canceled (typically by user: content download)
+ k_EResultDataCorruption = 53, // Operation canceled because data is ill formed or unrecoverable
+ k_EResultDiskFull = 54, // Operation canceled - not enough disk space.
+ k_EResultRemoteCallFailed = 55, // an remote call or IPC call failed
+ k_EResultPasswordUnset = 56, // Password could not be verified as it's unset server side
+ k_EResultExternalAccountUnlinked = 57, // External account (PSN, Facebook...) is not linked to a Steam account
+ k_EResultPSNTicketInvalid = 58, // PSN ticket was invalid
+ k_EResultExternalAccountAlreadyLinked = 59, // External account (PSN, Facebook...) is already linked to some other account, must explicitly request to replace/delete the link first
+ k_EResultRemoteFileConflict = 60, // The sync cannot resume due to a conflict between the local and remote files
+ k_EResultIllegalPassword = 61, // The requested new password is not legal
+ k_EResultSameAsPreviousValue = 62, // new value is the same as the old one ( secret question and answer )
+ k_EResultAccountLogonDenied = 63, // account login denied due to 2nd factor authentication failure
+ k_EResultCannotUseOldPassword = 64, // The requested new password is not legal
+ k_EResultInvalidLoginAuthCode = 65, // account login denied due to auth code invalid
+ k_EResultAccountLogonDeniedNoMail = 66, // account login denied due to 2nd factor auth failure - and no mail has been sent
+ k_EResultHardwareNotCapableOfIPT = 67, //
+ k_EResultIPTInitError = 68, //
+ k_EResultParentalControlRestricted = 69, // operation failed due to parental control restrictions for current user
+ k_EResultFacebookQueryError = 70, // Facebook query returned an error
+ k_EResultExpiredLoginAuthCode = 71, // account login denied due to auth code expired
+ k_EResultIPLoginRestrictionFailed = 72,
+ k_EResultAccountLockedDown = 73,
+ k_EResultAccountLogonDeniedVerifiedEmailRequired = 74,
+ k_EResultNoMatchingURL = 75,
+ k_EResultBadResponse = 76, // parse failure, missing field, etc.
+ k_EResultRequirePasswordReEntry = 77, // The user cannot complete the action until they re-enter their password
+ k_EResultValueOutOfRange = 78 // the value entered is outside the acceptable range
+};
+
+/* This enum is used in client API methods, do not re-number existing values. */
+enum EHTTPMethod
+{
+ k_EHTTPMethodInvalid = 0,
+ k_EHTTPMethodGET,
+ k_EHTTPMethodHEAD,
+ k_EHTTPMethodPOST,
+
+ // The remaining HTTP methods are not yet supported, per rfc2616 section 5.1.1 only GET and HEAD are required for
+ // a compliant general purpose server. We'll likely add more as we find uses for them.
+
+ // k_EHTTPMethodOPTIONS,
+ k_EHTTPMethodPUT,
+ k_EHTTPMethodDELETE,
+ // k_EHTTPMethodTRACE,
+ // k_EHTTPMethodCONNECT
+};
+
+
+/* HTTP Status codes that the server can send in response to a request, see rfc2616 section 10.3 for descriptions
+ of each of these. */
+enum EHTTPStatusCode
+{
+ // Invalid status code (this isn't defined in HTTP, used to indicate unset in our code)
+ k_EHTTPStatusCodeInvalid = 0,
+
+ // Informational codes
+ k_EHTTPStatusCode100Continue = 100,
+ k_EHTTPStatusCode101SwitchingProtocols = 101,
+
+ // Success codes
+ k_EHTTPStatusCode200OK = 200,
+ k_EHTTPStatusCode201Created = 201,
+ k_EHTTPStatusCode202Accepted = 202,
+ k_EHTTPStatusCode203NonAuthoritative = 203,
+ k_EHTTPStatusCode204NoContent = 204,
+ k_EHTTPStatusCode205ResetContent = 205,
+ k_EHTTPStatusCode206PartialContent = 206,
+
+ // Redirection codes
+ k_EHTTPStatusCode300MultipleChoices = 300,
+ k_EHTTPStatusCode301MovedPermanently = 301,
+ k_EHTTPStatusCode302Found = 302,
+ k_EHTTPStatusCode303SeeOther = 303,
+ k_EHTTPStatusCode304NotModified = 304,
+ k_EHTTPStatusCode305UseProxy = 305,
+ //k_EHTTPStatusCode306Unused = 306, (used in old HTTP spec, now unused in 1.1)
+ k_EHTTPStatusCode307TemporaryRedirect = 307,
+
+ // Error codes
+ k_EHTTPStatusCode400BadRequest = 400,
+ k_EHTTPStatusCode401Unauthorized = 401,
+ k_EHTTPStatusCode402PaymentRequired = 402, // This is reserved for future HTTP specs, not really supported by clients
+ k_EHTTPStatusCode403Forbidden = 403,
+ k_EHTTPStatusCode404NotFound = 404,
+ k_EHTTPStatusCode405MethodNotAllowed = 405,
+ k_EHTTPStatusCode406NotAcceptable = 406,
+ k_EHTTPStatusCode407ProxyAuthRequired = 407,
+ k_EHTTPStatusCode408RequestTimeout = 408,
+ k_EHTTPStatusCode409Conflict = 409,
+ k_EHTTPStatusCode410Gone = 410,
+ k_EHTTPStatusCode411LengthRequired = 411,
+ k_EHTTPStatusCode412PreconditionFailed = 412,
+ k_EHTTPStatusCode413RequestEntityTooLarge = 413,
+ k_EHTTPStatusCode414RequestURITooLong = 414,
+ k_EHTTPStatusCode415UnsupportedMediaType = 415,
+ k_EHTTPStatusCode416RequestedRangeNotSatisfiable = 416,
+ k_EHTTPStatusCode417ExpectationFailed = 417,
+
+ // Server error codes
+ k_EHTTPStatusCode500InternalServerError = 500,
+ k_EHTTPStatusCode501NotImplemented = 501,
+ k_EHTTPStatusCode502BadGateway = 502,
+ k_EHTTPStatusCode503ServiceUnavailable = 503,
+ k_EHTTPStatusCode504GatewayTimeout = 504,
+ k_EHTTPStatusCode505HTTPVersionNotSupported = 505,
+};
+
+native bool:SteamWorks_IsVACEnabled();
+native bool:SteamWorks_GetPublicIP(ipaddr[4]);
+native SteamWorks_GetPublicIPCell();
+native bool:SteamWorks_IsLoaded();
+native bool:SteamWorks_SetGameDescription(String:sDesc[]);
+native bool:SteamWorks_IsConnected();
+native bool:SteamWorks_SetRule(const String:sKey[], const String:sValue[]);
+native bool:SteamWorks_ClearRules();
+native bool:SteamWorks_ForceHeartbeat();
+
+native EUserHasLicenseForAppResult:SteamWorks_HasLicenseForApp(client, app);
+native SteamWorks_GetClientSteamID(client, String:sSteamID[], length);
+
+native bool:SteamWorks_RequestStatsAuthID(authid, appid);
+native bool:SteamWorks_RequestStats(client, appid);
+native bool:SteamWorks_GetStatCell(client, const String:sKey[], &value);
+native bool:SteamWorks_GetStatAuthIDCell(authid, const String:sKey[], &value);
+native bool:SteamWorks_GetStatFloat(client, const String:sKey[], &Float:value);
+native bool:SteamWorks_GetStatAuthIDFloat(authid, const String:sKey[], &Float:value);
+
+native Handle:SteamWorks_CreateHTTPRequest(EHTTPMethod:method, const String:sURL[]);
+native bool:SteamWorks_SetHTTPRequestContextValue(Handle:hHandle, any:data1, any:data2=0);
+native bool:SteamWorks_SetHTTPRequestNetworkActivityTimeout(Handle:hHandle, timeout);
+native bool:SteamWorks_SetHTTPRequestHeaderValue(Handle:hHandle, const String:sName[], const String:sValue[]);
+native bool:SteamWorks_SetHTTPRequestGetOrPostParameter(Handle:hHandle, const String:sName[], const String:sValue[]);
+
+funcenum SteamWorksHTTPRequestCompleted
+{
+ public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode),
+ public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode, any:data1),
+ public(Handle:hRequest, bool:bFailure, bool:bRequestSuccessful, EHTTPStatusCode:eStatusCode, any:data1, any:data2)
+};
+
+funcenum SteamWorksHTTPHeadersReceived
+{
+ public(Handle:hRequest, bool:bFailure),
+ public(Handle:hRequest, bool:bFailure, any:data1),
+ public(Handle:hRequest, bool:bFailure, any:data1, any:data2)
+};
+
+funcenum SteamWorksHTTPDataReceived
+{
+ public(Handle:hRequest, bool:bFailure, offset, bytesreceived),
+ public(Handle:hRequest, bool:bFailure, offset, bytesreceived, any:data1),
+ public(Handle:hRequest, bool:bFailure, offset, bytesreceived, any:data1, any:data2)
+};
+
+native bool:SteamWorks_SetHTTPCallbacks(Handle:hHandle, SteamWorksHTTPRequestCompleted:fCompleted = INVALID_FUNCTION, SteamWorksHTTPHeadersReceived:fHeaders = INVALID_FUNCTION, SteamWorksHTTPDataReceived:fData = INVALID_FUNCTION, Handle:hCalling = INVALID_HANDLE);
+native bool:SteamWorks_SendHTTPRequest(Handle:hRequest);
+native bool:SteamWorks_DeferHTTPRequest(Handle:hRequest);
+native bool:SteamWorks_PrioritizeHTTPRequest(Handle:hRequest);
+native bool:SteamWorks_GetHTTPResponseHeaderSize(Handle:hRequest, const String:sHeader[], &size);
+native bool:SteamWorks_GetHTTPResponseHeaderValue(Handle:hRequest, const String:sHeader[], String:sValue[], size);
+native bool:SteamWorks_GetHTTPResponseBodySize(Handle:hRequest, &size);
+native bool:SteamWorks_GetHTTPResponseBodyData(Handle:hRequest, String:sBody[], length);
+native bool:SteamWorks_GetHTTPDownloadProgressPct(Handle:hRequest, &Float:percent);
+native bool:SteamWorks_SetHTTPRequestRawPostBody(Handle:hRequest, const String:sContentType[], const String:sBody[], bodylen);
+
+funcenum SteamWorksHTTPBodyCallback
+{
+ public(const String:sData[]),
+ public(const String:sData[], any:value),
+ public(const data[], any:value, datalen)
+};
+
+native bool:SteamWorks_GetHTTPResponseBodyCallback(Handle:hRequest, SteamWorksHTTPBodyCallback:fCallback, any:data = 0, Handle:hPlugin = INVALID_HANDLE);
+native bool:SteamWorks_WriteHTTPResponseBodyToFile(Handle:hRequest, const String:sFileName[]);
+
+forward SW_OnValidateClient(ownerauthid, authid);
+forward SteamWorks_OnValidateClient(ownerauthid, authid);
+forward SteamWorks_SteamServersConnected();
+forward SteamWorks_SteamServersConnectFailure(EResult:result);
+forward SteamWorks_SteamServersDisconnected(EResult:result);
+
+forward Action:SteamWorks_RestartRequested();
+forward SteamWorks_TokenRequested(String:sToken[], maxlen);
+
+public Extension:__ext_SteamWorks =
+{
+ name = "SteamWorks",
+ file = "SteamWorks.ext",
+#if defined AUTOLOAD_EXTENSIONS
+ autoload = 1,
+#else
+ autoload = 0,
+#endif
+#if defined REQUIRE_EXTENSIONS
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+#if !defined REQUIRE_EXTENSIONS
+public __ext_SteamWorks_SetNTVOptional()
+{
+ MarkNativeAsOptional("SteamWorks_IsVACEnabled");
+ MarkNativeAsOptional("SteamWorks_GetPublicIP");
+ MarkNativeAsOptional("SteamWorks_GetPublicIPCell");
+ MarkNativeAsOptional("SteamWorks_IsLoaded");
+ MarkNativeAsOptional("SteamWorks_SetGameDescription");
+ MarkNativeAsOptional("SteamWorks_IsConnected");
+ MarkNativeAsOptional("SteamWorks_SetRule");
+ MarkNativeAsOptional("SteamWorks_ClearRules");
+ MarkNativeAsOptional("SteamWorks_ForceHeartbeat");
+
+ MarkNativeAsOptional("SteamWorks_HasLicenseForApp");
+ MarkNativeAsOptional("SteamWorks_GetClientSteamID");
+
+ MarkNativeAsOptional("SteamWorks_RequestStatsAuthID");
+ MarkNativeAsOptional("SteamWorks_RequestStats");
+ MarkNativeAsOptional("SteamWorks_GetStatCell");
+ MarkNativeAsOptional("SteamWorks_GetStatAuthIDCell");
+ MarkNativeAsOptional("SteamWorks_GetStatFloat");
+ MarkNativeAsOptional("SteamWorks_GetStatAuthIDFloat");
+
+ MarkNativeAsOptional("SteamWorks_CreateHTTPRequest");
+ MarkNativeAsOptional("SteamWorks_SetHTTPRequestContextValue");
+ MarkNativeAsOptional("SteamWorks_SetHTTPRequestNetworkActivityTimeout");
+ MarkNativeAsOptional("SteamWorks_SetHTTPRequestHeaderValue");
+ MarkNativeAsOptional("SteamWorks_SetHTTPRequestGetOrPostParameter");
+
+ MarkNativeAsOptional("SteamWorks_SetHTTPCallbacks");
+ MarkNativeAsOptional("SteamWorks_SendHTTPRequest");
+ MarkNativeAsOptional("SteamWorks_DeferHTTPRequest");
+ MarkNativeAsOptional("SteamWorks_PrioritizeHTTPRequest");
+ MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderSize");
+ MarkNativeAsOptional("SteamWorks_GetHTTPResponseHeaderValue");
+ MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodySize");
+ MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyData");
+ MarkNativeAsOptional("SteamWorks_GetHTTPDownloadProgressPct");
+ MarkNativeAsOptional("SteamWorks_SetHTTPRequestRawPostBody");
+
+ MarkNativeAsOptional("SteamWorks_GetHTTPResponseBodyCallback");
+ MarkNativeAsOptional("SteamWorks_WriteHTTPResponseBodyToFile");
+}
+#endif
diff --git a/addons/sourcemod/scripting/include/dhooks.inc b/addons/sourcemod/scripting/include/dhooks.inc
deleted file mode 100644
index 48d3184..0000000
--- a/addons/sourcemod/scripting/include/dhooks.inc
+++ /dev/null
@@ -1,459 +0,0 @@
-#if defined _dhooks_included
-#endinput
-#endif
-#define _dhooks_included
-enum ObjectValueType
-{
- ObjectValueType_Int = 0,
- ObjectValueType_Bool,
- ObjectValueType_Ehandle,
- ObjectValueType_Float,
- ObjectValueType_CBaseEntityPtr,
- ObjectValueType_IntPtr,
- ObjectValueType_BoolPtr,
- ObjectValueType_EhandlePtr,
- ObjectValueType_FloatPtr,
- ObjectValueType_Vector,
- ObjectValueType_VectorPtr,
- ObjectValueType_CharPtr,
- ObjectValueType_String
-};
-enum ListenType
-{
- ListenType_Created,
- ListenType_Deleted
-};
-enum ReturnType
-{
- ReturnType_Unknown,
- ReturnType_Void,
- ReturnType_Int,
- ReturnType_Bool,
- ReturnType_Float,
- ReturnType_String, //Note this is a string_t
- ReturnType_StringPtr, //Note this is a string_t *
- ReturnType_CharPtr,
- ReturnType_Vector,
- ReturnType_VectorPtr,
- ReturnType_CBaseEntity,
- ReturnType_Edict
-};
-enum HookParamType
-{
- HookParamType_Unknown,
- HookParamType_Int,
- HookParamType_Bool,
- HookParamType_Float,
- HookParamType_String, //Note this is a string_t
- HookParamType_StringPtr, //Note this is a string_t *
- HookParamType_CharPtr,
- HookParamType_VectorPtr,
- HookParamType_CBaseEntity,
- HookParamType_ObjectPtr,
- HookParamType_Edict,
- HookParamType_Object
-};
-enum ThisPointerType
-{
- ThisPointer_Ignore,
- ThisPointer_CBaseEntity,
- ThisPointer_Address
-};
-enum HookType
-{
- HookType_Entity,
- HookType_GameRules,
- HookType_Raw
-};
-enum MRESReturn
-{
- MRES_ChangedHandled = -2, // Use changed values and return MRES_Handled
- MRES_ChangedOverride, // Use changed values and return MRES_Override
- MRES_Ignored, // plugin didn't take any action
- MRES_Handled, // plugin did something, but real function should still be called
- MRES_Override, // call real function, but use my return value
- MRES_Supercede // skip real function; use my return value
-};
-enum DHookPassFlag
-{
- DHookPass_ByVal = (1<<0),
- DHookPass_ByRef = (1<<1)
-};
-funcenum ListenCB
-{
- //Deleted
- public (entity),
- //Created
- public (entity, const String:classname[])
-}
-funcenum DHookRemovalCB
-{
- public (hookid)
-};
-funcenum DHookCallback
-{
- //Function Example: void Ham::Test() with this pointer ignore
- MRESReturn:public(),
-
- //Function Example: void Ham::Test() with this pointer passed
- MRESReturn:public(this),
-
- //Function Example: void Ham::Test(int cake) with this pointer ignore
- MRESReturn:public(Handle:hParams),
-
- //Function Example: void Ham::Test(int cake) with this pointer passed
- MRESReturn:public(this, Handle:hParams),
-
- //Function Example: int Ham::Test() with this pointer ignore
- MRESReturn:public(Handle:hReturn),
-
- //Function Example: int Ham::Test() with this pointer passed
- MRESReturn:public(this, Handle:hReturn),
-
- //Function Example: int Ham::Test(int cake) with this pointer ignore
- MRESReturn:public(Handle:hReturn, Handle:hParams),
-
- //Function Example: int Ham::Test(int cake) with this pointer passed
- MRESReturn:public(this, Handle:hReturn, Handle:hParams),
-
- //Address NOW
-
- //Function Example: void Ham::Test() with this pointer passed
- MRESReturn:public(Address:this),
-
- //Function Example: void Ham::Test(int cake) with this pointer passed
- MRESReturn:public(Address:this, Handle:hParams),
-
- //Function Example: int Ham::Test() with this pointer passed
- MRESReturn:public(Address:this, Handle:hReturn),
-
- //Function Example: int Ham::Test(int cake) with this pointer passed
- MRESReturn:public(Address:this, Handle:hReturn, Handle:hParams)
-
-};
-/* Adds an entity listener hook
- *
- * @param type Type of listener to add
- * @param callback Callback to use
- *
- * @noreturn
-*/
-native DHookAddEntityListener(ListenType:type, ListenCB:callback);
-
-/* Removes an entity listener hook
- *
- * @param type Type of listener to remove
- * @param callback Callback this listener was using
- *
- * @return True if one was removed false otherwise.
-*/
-native bool:DHookRemoveEntityListener(ListenType:type, ListenCB:callback);
-
-/* Creates a hook
- *
- * @param offset vtable offset for function to hook
- * @param hooktype Type of hook
- * @param returntype Type type of return
- * @param thistype Type of this pointer or ignore (ignore can be used if not needed)
- * @param callback Callback function
- *
- * @return Returns setup handle for the hook or INVALID_HANDLE.
-*/
-native Handle:DHookCreate(offset, HookType:hooktype, ReturnType:returntype, ThisPointerType:thistype, DHookCallback:callback);
-
-/* Adds param to a hook setup
- *
- * @param setup Setup handle to add the param to.
- * @param type Param type
- * @param size Used for Objects (not Object ptr) to define the size of the object.
- * @param flag Used to change the pass type.
- *
- * @error Invalid setup handle or too many params added (request upping the max in thread)
- * @noreturn
-*/
-native DHookAddParam(Handle:setup, HookParamType:type, size=-1, DHookPassFlag:flag=DHookPass_ByVal);
-//native DHookAddParam(Handle:setup, HookParamType:type);
-
-/* Hook entity
- *
- * @param setup Setup handle to use to add the hook.
- * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!)
- * @param entity Entity index to hook on.
- * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback)
- *
- * @error Invalid setup handle, invalid entity or invalid hook type.
- * @return -1 on fail a hookid on success
-*/
-native DHookEntity(Handle:setup, bool:post, entity, DHookRemovalCB:removalcb=DHookRemovalCB:-1);
-
-/* Hook gamerules
- *
- * @param setup Setup handle to use to add the hook.
- * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!)
- * @param removalcb Callback for when the hook is removed (Game rules hooks are auto-removed on map end and will call this callback)
- *
- * @error Invalid setup handle, failing to get gamerules pointer or invalid hook type.
- * @return -1 on fail a hookid on success
-*/
-native DHookGamerules(Handle:setup, bool:post, DHookRemovalCB:removalcb=DHookRemovalCB:-1);
-
-/* Hook a raw pointer
- *
- * @param setup Setup handle to use to add the hook.
- * @param post True to make the hook a post hook. (If you need to change the retunr value or need the return value use a post hook! If you need to change params and return use a pre and post hook!)
- * @param addr This pointer address.
- * @param removalcb Callback for when the hook is removed (Entity hooks are auto-removed on entity destroyed and will call this callback)
- *
- * @error Invalid setup handle, invalid address or invalid hook type.
- * @return -1 on fail a hookid on success
-*/
-native DHookRaw(Handle:setup, bool:post, Address:addr, DHookRemovalCB:removalcb=DHookRemovalCB:-1);
-
-/* Remove hook by hook id
- *
- * @param hookid Hook id to remove
- *
- * @return true on success false otherwise
- * @note This will not fire the removal callback!
-*/
-native bool:DHookRemoveHookID(hookid);
-
-/* Get param value (Only use for: int, entity, bool or float param types)
- *
- * @param hParams Handle to params structure
- * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored)
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @return value if num greater than 0. If 0 returns paramcount.
-*/
-native any:DHookGetParam(Handle:hParams, num);
-
-/* Get vector param value
- *
- * @param hParams Handle to params structure
- * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.)
- * @param vec Vector buffer to store result.
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @noreturn
-*/
-native DHookGetParamVector(Handle:hParams, num, Float:vec[3]);
-
-/* Get string param value
- *
- * @param hParams Handle to params structure
- * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1.)
- * @param buffer String buffer to store result
- * @param size Buffer size
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @return value if num greater than 0.
-*/
-native DHookGetParamString(Handle:hParams, num, String:buffer[], size);
-
-/* Set param value (Only use for: int, entity, bool or float param types)
- *
- * @param hParams Handle to params structure
- * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.)
- * @param value Value to set it as (only pass int, bool, float or entity index)
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @noreturn
-*/
-native DHookSetParam(Handle:hParams, num, any:value);
-
-/* Set vector param value
- *
- * @param hParams Handle to params structure
- * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.)
- * @param vec Value to set vector as.
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @noreturn
-*/
-native DHookSetParamVector(Handle:hParams, num, Float:vec[3]);
-
-/* Set string param value
- *
- * @param hParams Handle to params structure
- * @params num Param number to set (Example if the function has 2 params and you need to set the value of the first param num would be 1.)
- * @param value Value to set string as.
- *
- * @error Invalid handle. Invalid param number. Invalid param type.
- * @noreturn
-*/
-native DHookSetParamString(Handle:hParams, num, String:value[]);
-
-/* Get return value (Only use for: int, entity, bool or float return types)
- *
- * @param hReturn Handle to return structure
- *
- * @error Invalid Handle, invalid type.
- * @return Returns default value if prehook returns actual value if post hook.
-*/
-native any:DHookGetReturn(Handle:hReturn);
-
-/* Get return vector value
- *
- * @param hReturn Handle to return structure
- * @param vec Vector buffer to store result in. (In pre hooks will be default value (0.0,0.0,0.0))
- *
- * @error Invalid Handle, invalid type.
- * @noreturn
-*/
-native DHookGetReturnVector(Handle:hReturn, Float:vec[3]);
-
-/* Get return string value
- *
- * @param hReturn Handle to return structure
- * @param buffer String buffer to store result in. (In pre hooks will be default value "")
- * @param size String buffer size
- *
- * @error Invalid Handle, invalid type.
- * @noreturn
-*/
-native DHookGetReturnString(Handle:hReturn, String:buffer[], size);
-
-/* Set return value (Only use for: int, entity, bool or float return types)
- *
- * @param hReturn Handle to return structure
- * @param value Value to set return as
- *
- * @error Invalid Handle, invalid type.
- * @noreturn
-*/
-native DHookSetReturn(Handle:hReturn, any:value);
-
-/* Set return vector value
- *
- * @param hReturn Handle to return structure
- * @param vec Value to set return vector as
- *
- * @error Invalid Handle, invalid type.
- * @noreturn
-*/
-native DHookSetReturnVector(Handle:hReturn, Float:vec[3]);
-
-/* Set return string value
- *
- * @param hReturn Handle to return structure
- * @param value Value to set return string as
- *
- * @error Invalid Handle, invalid type.
- * @noreturn
-*/
-native DHookSetReturnString(Handle:hReturn, String:value[]);
-
-/* Gets an objects variable value
- *
- * @param hParams Handle to params structure
- * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored)
- * @param offset Offset within the object to the var to get.
- * @param type Type of var it is
- *
- * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type.
- * @return Value of the objects var. If EHANDLE type or entity returns entity index.
-*/
-native any:DHookGetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type);
-
-/* Sets an objects variable value
- *
- * @param hParams Handle to params structure
- * @param num Param number to set. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored)
- * @param offset Offset within the object to the var to set.
- * @param type Type of var it is
- * @param value The value to set the var to.
- *
- * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type.
- * @noreturn
-*/
-native DHookSetParamObjectPtrVar(Handle:hParams, num, offset, ObjectValueType:type, any:value);
-
-/* Gets an objects vector variable value
- *
- * @param hParams Handle to params structure
- * @param num Param number to get. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored)
- * @param offset Offset within the object to the var to get.
- * @param type Type of var it is
- * @param buffer Buffer to store the result vector
- *
- * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type.
- * @return Value of the objects var.
-*/
-native DHookGetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:buffer[3]);
-
-/* Sets an objects vector variable value
- *
- * @param hParams Handle to params structure
- * @param num Param number to set. (Example if the function has 2 params and you need the value of the first param num would be 1. 0 Will return the number of params stored)
- * @param offset Offset within the object to the var to set.
- * @param type Type of var it is
- * @param value The value to set the vector var to.
- *
- * @error Invalid handle. Invalid param number. Invalid param type. Invalid Object type.
- * @noreturn
-*/
-native DHookSetParamObjectPtrVarVector(Handle:hParams, num, offset, ObjectValueType:type, Float:value[3]);
-
-
-//ADD DOCS OR ELSE
-//WE SHOULD WRAP THESE AROUND STOCKS FOR NON PTR AS WE SUPPORT BOTH WITH THIS NATIVE
-native DHookGetParamObjectPtrString(Handle:hParams, num, offset, ObjectValueType:type, String:buffer[], size);
-
-/* Checks if a pointer param is null
- *
- * @param hParams Handle to params structure
- * @param num Param number to check.
- *
- * @error Non pointer param
- * @return True if null false otherwise.
-*/
-native bool:DHookIsNullParam(Handle:hParams, num);
-
-public Extension:__ext_dhooks =
-{
- name = "dhooks",
- file = "dhooks.ext",
-#if defined AUTOLOAD_EXTENSIONS
- autoload = 1,
-#else
- autoload = 0,
-#endif
-#if defined REQUIRE_EXTENSIONS
- required = 1,
-#else
- required = 0,
-#endif
-};
-
-#if !defined REQUIRE_EXTENSIONS
-public __ext_dhooks_SetNTVOptional()
-{
- MarkNativeAsOptional("DHookAddEntityListener");
- MarkNativeAsOptional("DHookRemoveEntityListener");
- MarkNativeAsOptional("DHookCreate");
- MarkNativeAsOptional("DHookAddParam");
- MarkNativeAsOptional("DHookEntity");
- MarkNativeAsOptional("DHookGamerules");
- MarkNativeAsOptional("DHookRaw");
- MarkNativeAsOptional("DHookRemoveHookID");
- MarkNativeAsOptional("DHookGetParam");
- MarkNativeAsOptional("DHookGetParamVector");
- MarkNativeAsOptional("DHookGetParamString");
- MarkNativeAsOptional("DHookSetParam");
- MarkNativeAsOptional("DHookSetParamVector");
- MarkNativeAsOptional("DHookSetParamString");
- MarkNativeAsOptional("DHookGetReturn");
- MarkNativeAsOptional("DHookGetReturnVector");
- MarkNativeAsOptional("DHookGetReturnString");
- MarkNativeAsOptional("DHookSetReturn");
- MarkNativeAsOptional("DHookSetReturnVector");
- MarkNativeAsOptional("DHookSetReturnString");
- MarkNativeAsOptional("DHookGetParamObjectPtrVar");
- MarkNativeAsOptional("DHookSetParamObjectPtrVar");
- MarkNativeAsOptional("DHookGetParamObjectPtrVarVector");
- MarkNativeAsOptional("DHookSetParamObjectPtrVarVector");
- MarkNativeAsOptional("DHookIsNullParam");
- MarkNativeAsOptional("DHookGetParamObjectPtrString");
-}
-#endif
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/include/prophunt.inc b/addons/sourcemod/scripting/include/prophunt.inc
new file mode 100644
index 0000000..577912b
--- /dev/null
+++ b/addons/sourcemod/scripting/include/prophunt.inc
@@ -0,0 +1,121 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * TF2 PropHunt Redux
+ * Hide as a prop from the evil Pyro menace... or hunt down the hidden prop scum
+ *
+ * TF2 PropHunt (C)2007-2015 Darkimmortal. All rights reserved.
+ * TF2 PropHunt Redux (C)2013-2015 Powerlord (Ross Bemrose). All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: 4.0.0
+ */
+// PropHunt Redux by Powerlord
+// Based on
+// PropHunt by Darkimmortal
+// - GamingMasters.org -
+#if defined _prophunt_redux_included_
+ #endinput
+#endif
+#define _prophunt_redux_included_
+
+// PropHunt Redux 4.0+ series
+
+#include
+
+#define TEAM_PROP view_as(TFTeam_Red)
+#define TEAM_HUNTER view_as(TFTeam_Blue)
+#define TEAM_SPECTATOR view_as(TFTeam_Spectator)
+
+/**
+ * Is PropHunt Redux currently running?
+ *
+ * This should be valid for teamplay_round_start post-hooks (but possibly not pre-hooks)
+ *
+ * @return true if PropHunt Redux is running, false otherwise
+ */
+native bool PropHuntRedux_IsRunning();
+
+/**
+ * Is the named map a valid PropHunt map?
+ *
+ * @return true if yes, false if no
+ */
+native bool PropHuntRedux_ValidateMap(const char[] map);
+
+/**
+ * Get the model of a prop player.
+ *
+ * @return true if client is/was a prop and has a model, false otherwise
+ */
+native bool PropHuntRedux_GetPropModel(int client, char[] model, int maxlen);
+
+/**
+ * Get the name of the model of a prop player.
+ *
+ * Note: Defaults to the server language (likely English)
+ *
+ * @return true if client is/was a prop and has a model, false otherwise
+ */
+native bool PropHuntRedux_GetPropModelName(int client, char[] name, int maxlen, int targetClient=LANG_SERVER);
+
+/**
+ * Retrieve whether we're currently in Last Prop Mode.
+ *
+ * @return true if this is last prop mode, false otherwise.
+ */
+native bool PropHuntRedux_IsLastPropMode();
+
+/**
+ * Callback called when the game description is being updated
+ *
+ * Stat plugins can use this to append data to the end of the string.
+ *
+ * @param description The current / updated description. Maxes out at 128 characters.
+ * @return Plugin_Changed if your plugin changed the game description.
+ * Plugin_Continued otherwise.
+ */
+forward Action PropHuntRedux_UpdateGameDescription(char[] description, int maxlength);
+
+public SharedPlugin __pl_prophunt_redux =
+{
+ name = "Prophunt Redux",
+ file = "prophunt.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+#if !defined REQUIRE_PLUGIN
+public void __pl_prophunt_redux_SetNTVOptional()
+{
+ MarkNativeAsOptional("PropHuntRedux_IsRunning");
+ MarkNativeAsOptional("PropHuntRedux_ValidateMap");
+ MarkNativeAsOptional("PropHuntRedux_GetPropModel");
+ MarkNativeAsOptional("PropHuntRedux_GetPropModelName");
+ MarkNativeAsOptional("PropHuntRedux_IsLastPropMode");
+}
+#endif
diff --git a/addons/sourcemod/scripting/include/updater.inc b/addons/sourcemod/scripting/include/updater.inc
new file mode 100644
index 0000000..f37bdf2
--- /dev/null
+++ b/addons/sourcemod/scripting/include/updater.inc
@@ -0,0 +1,97 @@
+#if defined _updater_included
+ #endinput
+#endif
+#define _updater_included
+
+/**
+ * Adds your plugin to the updater. The URL will be updated if
+ * your plugin was previously added.
+ *
+ * @param url URL to your plugin's update file.
+ * @noreturn
+ */
+native Updater_AddPlugin(const String:url[]);
+
+/**
+ * Removes your plugin from the updater. This does not need to
+ * be called during OnPluginEnd.
+ *
+ * @noreturn
+ */
+native Updater_RemovePlugin();
+
+/**
+ * Forces your plugin to be checked for updates. The behaviour
+ * of the update is dependant on the server's configuration.
+ *
+ * @return True if an update was triggered. False otherwise.
+ * @error Plugin not found in updater.
+ */
+native bool:Updater_ForceUpdate();
+
+/**
+ * Called when your plugin is about to be checked for updates.
+ *
+ * @return Plugin_Handled to prevent checking, Plugin_Continue to allow it.
+ */
+forward Action:Updater_OnPluginChecking();
+
+/**
+ * Called when your plugin is about to begin downloading an available update.
+ *
+ * @return Plugin_Handled to prevent downloading, Plugin_Continue to allow it.
+ */
+forward Action:Updater_OnPluginDownloading();
+
+/**
+ * Called when your plugin's update files have been fully downloaded
+ * and are about to write to their proper location. This should be used
+ * to free read-only resources that require write access for your update.
+ *
+ * @note OnPluginUpdated will be called later during the same frame.
+ *
+ * @noreturn
+ */
+forward Updater_OnPluginUpdating();
+
+/**
+ * Called when your plugin's update has been completed. It is safe
+ * to reload your plugin at this time.
+ *
+ * @noreturn
+ */
+forward Updater_OnPluginUpdated();
+
+/**
+ * @brief Reloads a plugin.
+ *
+ * @param plugin Plugin Handle (INVALID_HANDLE uses the calling plugin).
+ * @noreturn
+ */
+stock ReloadPlugin(Handle:plugin=INVALID_HANDLE)
+{
+ decl String:filename[64];
+ GetPluginFilename(plugin, filename, sizeof(filename));
+ ServerCommand("sm plugins reload %s", filename);
+}
+
+
+public SharedPlugin:__pl_updater =
+{
+ name = "updater",
+ file = "updater.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+#if !defined REQUIRE_PLUGIN
+public __pl_updater_SetNTVOptional()
+{
+ MarkNativeAsOptional("Updater_AddPlugin");
+ MarkNativeAsOptional("Updater_RemovePlugin");
+ MarkNativeAsOptional("Updater_ForceUpdate");
+}
+#endif
diff --git a/addons/sourcemod/scripting/prophunt-stats.sp b/addons/sourcemod/scripting/prophunt-stats.sp
deleted file mode 100644
index acd81cf..0000000
--- a/addons/sourcemod/scripting/prophunt-stats.sp
+++ /dev/null
@@ -1,298 +0,0 @@
-// PropHunt Redux Stats by Powerlord
-// - reddit.com/r/RUGC_Midwest -
-
-// This plugin just has the commands to create and upgrade the Local Stats database
-
-// This is based heavily on the SourceMod SQLAdmins code because I'm lazy.
-
-#pragma semicolon 1
-#include
-
-#define CURRENT_SCHEMA_VERSION 1409
-#define SCHEMA_UPGRADE_1 1409
-
-new current_version[4] = {3, 1, 0, CURRENT_SCHEMA_VERSION};
-
-#define MYSQL "mysql"
-#define SQLITE "sqlite"
-
-#define PL_VERSION "3.1.0 alpha 1"
-
-public Plugin:myinfo =
-{
- name = "PropHunt Redux Local Stats Manager",
- author = "Powerlord",
- description = "Create/Update PropHunt Redux's local stats DB",
- version = PL_VERSION,
- url = "https://forums.alliedmods.net/showthread.php?t=228086"
-};
-
-public OnPluginStart()
-{
- LoadTranslations("common.phrases");
-
- RegServerCmd("ph_create_stats_tables", Command_CreateTables);
- RegServerCmd("ph_update_stats_tables", Command_UpdateTables);
-
-}
-
-Handle:Connect()
-{
- decl String:error[255];
- new Handle:db;
-
- if (SQL_CheckConfig("prophunt_local"))
- {
- db = SQL_Connect("prophunt_local", true, error, sizeof(error));
- } else {
- db = SQL_Connect("default", true, error, sizeof(error));
- }
-
- if (db == INVALID_HANDLE)
- {
- LogError("Could not connect to database: %s", error);
- }
-
- return db;
-}
-
-CreateMySQL(client, Handle:db)
-{
- new String:queries[7][1024];
-
- Format(queries[0], sizeof(queries[]), "CREATE TABLE ph_servers (id int(10) unsigned NOT NULL auto_increment, ip varchar(65), points int(10), time int(10), PRIMARY KEY(id) )");
- Format(queries[1], sizeof(queries[]), "CREATE TABLE ph_players (steamid varchar(25), name varchar(%d), points int(10), wins int(10), losses int(10), time int(10), PRIMARY KEY(steamid) )", MAX_NAME_LENGTH);
- Format(queries[2], sizeof(queries[]), "CREATE TABLE ph_deaths (id int(10) unsigned NOT NULL auto_increment, victimid varchar(25), killerid varchar(20), killerteam int(1), weapon varchar(64), assisterid varchar(25), ip varchar(65), map varchar(%d), prop varchar(%d), victim_position_x float, victim_position_y float, victim_position_z float, killer_position_x float, killer_position_y float, killer_position_z float, victim_class int(1), killer_class int(1), survival_time int(4), PRIMARY KEY(id) )", PLATFORM_MAX_PATH, PLATFORM_MAX_PATH);
- Format(queries[3], sizeof(queries[]), "CREATE TABLE ph_survivals (id int(10) unsigned NOT NULL auto_increment, steamid varchar(25), prop varchar(%d), ip varchar(65), map varchar(%d), position_x float, position_y float, position_z float, class int(1), team int(1), survival_time int(4), PRIMARY KEY(id) )", PLATFORM_MAX_PATH, PLATFORM_MAX_PATH);
- Format(queries[4], sizeof(queries[]), "CREATE TABLE ph_rounds (id int(10) unsigned NOT NULL auto_increment, team char(3), map varchar(%d), ip varchar(65), PRIMARY KEY(id) )", PLATFORM_MAX_PATH);
- Format(queries[5], sizeof(queries[]), "CREATE TABLE ph_props (id int(10) unsigned NOT NULL auto_increment, name varchar(%d), deaths int(10), survivals int(10))", PLATFORM_MAX_PATH);
- Format(queries[6], sizeof(queries[]), "CREATE TABLE IF NOT EXISTS ph_config (cfg_key varchar(32) NOT NULL, cfg_value varchar(255) NOT NULL, PRIMARY KEY (cfg_key))");
-
- for (new i = 0; i < 7; i++)
- {
- if (!DoQuery(client, db, queries[i]))
- {
- return;
- }
- }
-
- decl String:query[256];
- Format(query,
- sizeof(query),
- "INSERT INTO ph_config (cfg_key, cfg_value) VALUES ('schema_version', '3.1.0.%d') ON DUPLICATE KEY UPDATE cfg_value = '3.1.0.%d'",
- CURRENT_SCHEMA_VERSION,
- CURRENT_SCHEMA_VERSION);
-
- if (!DoQuery(client, db, query))
- {
- return;
- }
-
- ReplyToCommand(client, "[PH] Stats tables have been created.");
-}
-
-public Action:Command_CreateTables(args)
-{
- new client = 0;
- new Handle:db = Connect();
- if (db == INVALID_HANDLE)
- {
- ReplyToCommand(client, "[PH] %t", "Could not connect to database");
- return Plugin_Handled;
- }
-
- new String:ident[16];
- SQL_ReadDriver(db, ident, sizeof(ident));
-
- if (strcmp(ident, "mysql") == 0)
- {
- CreateMySQL(client, db);
- } else if (strcmp(ident, "sqlite") == 0) {
- ReplyToCommand(client, "[PH] SQLite is not supported at this time.");
- } else {
- ReplyToCommand(client, "[PH] Unknown driver type '%s', cannot create tables.", ident);
- }
-
- CloseHandle(db);
-
- return Plugin_Handled;
-}
-
-bool:GetUpdateVersion(client, Handle:db, versions[4])
-{
- decl String:query[256];
- new Handle:hQuery;
-
- Format(query, sizeof(query), "SELECT cfg_value FROM ph_config WHERE cfg_key = 'schema_version'");
- if ((hQuery = SQL_Query(db, query)) == INVALID_HANDLE)
- {
- DoError(client, db, query, "Version lookup query failed");
- return false;
- }
- if (SQL_FetchRow(hQuery))
- {
- decl String:version_string[255];
- SQL_FetchString(hQuery, 0, version_string, sizeof(version_string));
-
- decl String:version_numbers[4][12];
- if (ExplodeString(version_string, ".", version_numbers, 4, 12) == 4)
- {
- for (new i = 0; i < 4; i++)
- {
- versions[i] = StringToInt(version_numbers[i]);
- }
- }
- }
-
- CloseHandle(hQuery);
-
- if (current_version[3] < versions[3])
- {
- ReplyToCommand(client, "[PH] The database is newer than the expected version.");
- return false;
- }
-
- if (current_version[3] == versions[3])
- {
- ReplyToCommand(client, "[PH] Your tables are already up to date.");
- return false;
- }
-
-
- return true;
-}
-
-UpdateMySQL(client, Handle:db)
-{
- decl String:query[512];
- new Handle:hQuery;
-
- Format(query, sizeof(query), "SHOW TABLES");
- if ((hQuery = SQL_Query(db, query)) == INVALID_HANDLE)
- {
- DoError(client, db, query, "Table lookup query failed");
- return;
- }
-
- decl String:table[64];
- new bool:found = false;
- while (SQL_FetchRow(hQuery))
- {
- SQL_FetchString(hQuery, 0, table, sizeof(table));
- if (strcmp(table, "ph_config") == 0)
- {
- found = true;
- }
- }
- CloseHandle(hQuery);
-
- new versions[4];
-
- if (found && !GetUpdateVersion(client, db, versions))
- {
- return;
- }
-
- /*
- * There are presently no upgrades
- */
- if (versions[3] < SCHEMA_UPGRADE_1)
- {
- /*
- new String:queries[6][] =
- {
- "CREATE TABLE IF NOT EXISTS sm_config (cfg_key varchar(32) NOT NULL, cfg_value varchar(255) NOT NULL, PRIMARY KEY (cfg_key))",
- "ALTER TABLE sm_admins ADD immunity INT UNSIGNED NOT NULL",
- "ALTER TABLE sm_groups ADD immunity_level INT UNSIGNED NOT NULL",
- "UPDATE sm_groups SET immunity_level = 2 WHERE immunity = 'default'",
- "UPDATE sm_groups SET immunity_level = 1 WHERE immunity = 'global'",
- "ALTER TABLE sm_groups DROP immunity"
- };
-
- for (new i = 0; i < 6; i++)
- {
- if (!DoQuery(client, db, queries[i]))
- {
- return;
- }
- }
-
- decl String:upgr[48];
- Format(upgr, sizeof(upgr), "1.0.0.%d", SCHEMA_UPGRADE_1);
-
- Format(query, sizeof(query), "INSERT INTO ph_config (cfg_key, cfg_value) VALUES ('schema_version', '%s') ON DUPLICATE KEY UPDATE cfg_value = '%s'", upgr, upgr);
- if (!DoQuery(client, db, query))
- {
- return;
- }
-
- versions[3] = SCHEMA_UPGRADE_1;
- */
- }
-
- //ReplyToCommand(client, "[PH] Your tables are now up to date.");
- ReplyToCommand(client, "[PH] There are no upgrades at this time.");
-}
-
-public Action:Command_UpdateTables(args)
-{
- new client = 0;
- new Handle:db = Connect();
- if (db == INVALID_HANDLE)
- {
- ReplyToCommand(client, "[PH] %t", "Could not connect to database");
- return Plugin_Handled;
- }
-
- new String:ident[16];
- SQL_ReadDriver(db, ident, sizeof(ident));
-
- if (strcmp(ident, "mysql") == 0)
- {
- UpdateMySQL(client, db);
- } else if (strcmp(ident, "sqlite") == 0) {
- ReplyToCommand(client, "[PH] SQLite is not supported at this time.");
- } else {
- ReplyToCommand(client, "[PH] Unknown driver type, cannot upgrade.");
- }
-
- CloseHandle(db);
-
- return Plugin_Handled;
-}
-
-stock bool:DoQuery(client, Handle:db, const String:query[])
-{
- if (!SQL_FastQuery(db, query))
- {
- decl String:error[255];
- SQL_GetError(db, error, sizeof(error));
- LogError("Query failed: %s", error);
- LogError("Query dump: %s", query);
- ReplyToCommand(client, "[PH] %t", "Failed to query database");
- return false;
- }
-
- return true;
-}
-
-stock Action:DoError(client, Handle:db, const String:query[], const String:msg[])
-{
- decl String:error[255];
- SQL_GetError(db, error, sizeof(error));
- LogError("%s: %s", msg, error);
- LogError("Query dump: %s", query);
- CloseHandle(db);
- ReplyToCommand(client, "[PH] %t", "Failed to query database");
- return Plugin_Handled;
-}
-
-stock Action:DoStmtError(client, Handle:db, const String:query[], const String:error[], const String:msg[])
-{
- LogError("%s: %s", msg, error);
- LogError("Query dump: %s", query);
- CloseHandle(db);
- ReplyToCommand(client, "[PH] %t", "Failed to query database");
- return Plugin_Handled;
-}
-
diff --git a/addons/sourcemod/scripting/prophunt.sp b/addons/sourcemod/scripting/prophunt.sp
index 8f872b3..f32e70e 100644
--- a/addons/sourcemod/scripting/prophunt.sp
+++ b/addons/sourcemod/scripting/prophunt.sp
@@ -29,14 +29,13 @@
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
* or .
*
- * Version: 3.3.4
+ * Version: 4.0.0
*/
// PropHunt Redux by Powerlord
// Based on
// PropHunt by Darkimmortal
// - GamingMasters.org -
-#pragma semicolon 1
#include
#include
#include
@@ -47,9 +46,15 @@
#undef REQUIRE_EXTENSIONS
#include
+#include
#undef REQUIRE_PLUGIN
-#include
+#include
+
+#define REQUIRE_EXTENSIONS
+#define REQUIRE_PLUGIN
+#pragma semicolon 1
+#pragma newdecls required
#if !defined SNDCHAN_VOICE2
#define SNDCHAN_VOICE2 7
@@ -60,19 +65,15 @@
#define MAXLANGUAGECODE 4
-#define PL_VERSION "3.3.4"
+#define PL_VERSION "4.0.0.0 alpha 1"
+
+// sv_tags has a 255 limit
+#define SV_TAGS_SIZE 255
+
//--------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------- MAIN PROPHUNT CONFIGURATION -------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
-// Enable for global stats support (.inc file available on request due to potential for cheating and database abuse)
-// Default: OFF
-//#define STATS
-
-// Enable for local stats support
-// Default: OFF
-//#define LOCALSTATS
-
// GM only stuff
//#define GM
@@ -81,15 +82,19 @@
#include
#endif
+// Include support for Workshop maps
+// Requires SourceMod 1.8 or later
+// Default: ON
+#define WORKSHOP_SUPPORT
+
// Include support for Opt-In MultiMod
// Default: OFF
//#define OIMM
-// Include support for DHooks to switch teams on round end
-// You only really want to disable this if the SetWinningTeam offset breaks
-// and Powerlord (or someone else) hasn't fixed it yet
+// Include support for switching teams using gamedata
+// You only really want to disable this if team switchs aren't working (i.e. gamedata hasn't yet been updated)
// Default: ON
-#define DHOOKS
+#define SWITCH_TEAMS
// Give last prop a scattergun and apply jarate to all pyros on last prop alive
// Default: ON
@@ -135,39 +140,29 @@
#define SETUP_MIN 30
#define SETUP_MAX 120
+// "Production" URL (3.3 branch)
+//#define UPDATE_URL "http://tf2.rbemrose.com/sourcemod/prophunt/prophunt.txt"
+
+// "Dev" URL (3.4 branch)
+#define UPDATE_URL "http://tf2.rbemrose.com/sourcemod/prophunt/dev/prophunt.txt"
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
-#if defined DHOOKS
-#include
-#endif
-
#if defined OIMM
#include
#endif
// Needed for stats2.inc compatibility
-#define TEAM_BLUE _:TFTeam_Blue
-#define TEAM_RED _:TFTeam_Red
-#define TEAM_SPEC _:TFTeam_Spectator
-#define TEAM_UNASSIGNED _:TFTeam_Unassigned
+#define TEAM_BLUE view_as(TFTeam_Blue)
+#define TEAM_RED view_as(TFTeam_Red)
+#define TEAM_SPEC view_as(TFTeam_Spectator)
+#define TEAM_UNASSIGNED view_as(TFTeam_Unassigned)
#define TEAM_PROP TEAM_RED
#define TEAM_HUNTER TEAM_BLUE
-#define FLAMETHROWER "models/weapons/w_models/w_flamethrower.mdl"
-
-#define STATE_WAT -1
-#define STATE_IDLE 0
-#define STATE_RUNNING 1
-#define STATE_SWING 2
-#define STATE_CROUCH 3
-
-// Not sure what this is for, but pretty sure it's wrong.. player conditions are no longer defined like this
-#define PLAYER_ONFIRE (1 << 14)
-
// Weapon Indexes
#define WEP_SHOTGUN_UNIQUE 199
@@ -179,10 +174,6 @@
#define LOCKVOL 0.7
#define UNBALANCE_LIMIT 1
#define MAXMODELNAME 96
-// Not sure what this is for, but pretty sure it's wrong.. player conditions are no longer defined like this
-#define TF2_PLAYERCOND_ONFIREALERT (1<<20)
-
-#define IN_MOVEMENT IN_MOVELEFT | IN_MOVERIGHT | IN_FORWARD | IN_BACK | IN_JUMP
#define TIMER_NAME "prop_hunt_timer"
@@ -195,16 +186,6 @@ enum
Item_Attributes,
}
-enum ScReason
-{
- ScReason_TeamWin = 0,
- ScReason_TeamLose,
- ScReason_Death,
- ScReason_Kill,
- ScReason_Time,
- ScReason_Friendly
-};
-
enum PropData
{
String:PropData_Name[MAXMODELNAME],
@@ -219,30 +200,6 @@ enum RoundChange
RoundChange_Disable,
}
-enum
-{
- ScoreData_Captures,
- ScoreData_Defenses,
- ScoreData_Kills,
- ScoreData_Deaths,
- ScoreData_Suicides,
- ScoreData_Dominations,
- ScoreData_Revenge,
- ScoreData_BuildingsBuilt,
- ScoreData_BuildingsDestroyed,
- ScoreData_Headshots,
- ScoreData_Backstabs,
- ScoreData_HealPoints,
- ScoreData_Invulns,
- ScoreData_Teleports,
- ScoreData_DamageDone,
- ScoreData_Crits,
- ScoreData_ResupplyPoints,
- ScoreData_KillAssists,
- ScoreData_BonusPoints,
- ScoreData_Points
-}
-
enum
{
TFClassBits_None = 0,
@@ -257,190 +214,205 @@ enum
TFClassBits_Engineer = (1 << 8) // 256
}
-new bool:g_RoundOver = true;
-new bool:g_inPreRound = true;
-
-new bool:g_LastProp;
-new bool:g_Attacking[MAXPLAYERS+1];
-new bool:g_SetClass[MAXPLAYERS+1];
-new bool:g_Spawned[MAXPLAYERS+1];
-new bool:g_TouchingCP[MAXPLAYERS+1];
-new bool:g_Charge[MAXPLAYERS+1];
-new bool:g_First[MAXPLAYERS+1];
-new bool:g_HoldingLMB[MAXPLAYERS+1];
-new bool:g_HoldingRMB[MAXPLAYERS+1];
-new bool:g_AllowedSpawn[MAXPLAYERS+1];
-new bool:g_RotLocked[MAXPLAYERS+1];
-new bool:g_Hit[MAXPLAYERS+1];
-new bool:g_Spec[MAXPLAYERS+1];
-new String:g_PlayerModel[MAXPLAYERS+1][PLATFORM_MAX_PATH];
-
-new String:g_Mapname[PLATFORM_MAX_PATH];
-new String:g_ServerIP[32];
-new String:g_Version[16];
-
-new g_Message_red;
-new g_Message_blue;
-new g_RoundTime = 175;
-new g_Message_bit = 0;
-new g_Setup = 0;
-//new g_iVelocity = -1;
-
-#if defined STATS || defined LOCALSTATS
-new bool:g_MapChanging = false;
-new g_StartTime;
-#endif
+bool g_RoundOver = true;
+bool g_inSetup = false;
+bool g_inPreRound = true;
+bool g_PlayerDied = false;
+float g_flRoundStart = 0.0;
+
+bool g_LastProp;
+bool g_Attacking[MAXPLAYERS+1];
+bool g_SetClass[MAXPLAYERS+1];
+bool g_Spawned[MAXPLAYERS+1];
+bool g_TouchingCP[MAXPLAYERS+1];
+bool g_Charge[MAXPLAYERS+1];
+bool g_First[MAXPLAYERS+1];
+bool g_HoldingLMB[MAXPLAYERS+1];
+bool g_HoldingRMB[MAXPLAYERS+1];
+bool g_AllowedSpawn[MAXPLAYERS+1];
+bool g_RotLocked[MAXPLAYERS+1];
+bool g_Hit[MAXPLAYERS+1];
+bool g_Spec[MAXPLAYERS+1];
+char g_PlayerModel[MAXPLAYERS+1][PLATFORM_MAX_PATH];
+
+char g_Mapname[PLATFORM_MAX_PATH];
+char g_ServerIP[32];
+
+int g_Message_red;
+int g_Message_blue;
+int g_RoundTime = 175;
+int g_Message_bit = 0;
+int g_Setup = 0;
//new Handle:g_TimerStart = INVALID_HANDLE;
-new Handle:g_Sounds = INVALID_HANDLE;
-new Handle:g_BroadcastSounds = INVALID_HANDLE;
-
-new bool:g_Doors = false;
-new bool:g_Relay = false;
-new bool:g_Freeze = true;
-
-//new bool:g_weaponRemovals[MAXITEMS];
-//new Float:g_weaponNerfs[MAXITEMS];
-//new Float:g_weaponSelfDamage[MAXITEMS];
-
-new Handle:g_hWeaponRemovals;
-new Handle:g_hWeaponNerfs;
-new Handle:g_hWeaponSelfDamage;
-new Handle:g_hWeaponStripAttribs;
-new Handle:g_hWeaponAddAttribs;
-new Handle:g_hWeaponReplacements;
-new Handle:g_hWeaponReplacementPlayerClasses;
-
-new g_classLimits[2][10];
-new TFClassType:g_defaultClass[2];
-new Float:g_classSpeeds[10][3]; //0 - Base speed, 1 - Max Speed, 2 - Increment Value
-new Float:g_currentSpeed[MAXPLAYERS+1];
-
-//new g_oFOV;
-//new g_oDefFOV;
-
-new Handle:g_PropData = INVALID_HANDLE;
+StringMap g_Sounds;
+StringMap g_BroadcastSounds;
+
+bool g_Doors = false;
+bool g_Relay = false;
+bool g_Freeze = true;
+
+ArrayList g_hWeaponRemovals;
+ArrayList g_hPropWeaponRemovals;
+StringMap g_hWeaponNerfs;
+StringMap g_hWeaponSelfDamage;
+ArrayList g_hWeaponStripAttribs;
+StringMap g_hWeaponAddAttribs;
+StringMap g_hWeaponReplacements;
+StringMap g_hWeaponReplacementPlayerClasses;
+
+int g_classLimits[2][10];
+TFClassType g_defaultClass[2];
+float g_classSpeeds[10][3]; //0 - Base speed, 1 - Max Speed, 2 - Increment Value
+float g_currentSpeed[MAXPLAYERS+1];
+
+StringMap g_PropData;
// Multi-language support
-new Handle:g_ModelLanguages = INVALID_HANDLE;
-new Handle:g_PropNames = INVALID_HANDLE;
-new Handle:g_PropNamesIndex = INVALID_HANDLE;
-
-new Handle:g_ConfigKeyValues = INVALID_HANDLE;
-new Handle:g_ModelName = INVALID_HANDLE;
-new Handle:g_ModelOffset = INVALID_HANDLE;
-new Handle:g_ModelRotation = INVALID_HANDLE;
-new Handle:g_ModelSkin = INVALID_HANDLE;
-new Handle:g_Text1 = INVALID_HANDLE;
-new Handle:g_Text2 = INVALID_HANDLE;
-new Handle:g_Text3 = INVALID_HANDLE;
-new Handle:g_Text4 = INVALID_HANDLE;
-
+ArrayList g_ModelLanguages;
+StringMap g_PropNames;
+ArrayList g_PropNamesIndex;
+
+KeyValues g_ConfigKeyValues;
+ArrayList g_ModelName;
+ArrayList g_ModelOffset;
+ArrayList g_ModelRotation;
+ArrayList g_ModelSkin;
+Handle g_Text1;
+Handle g_Text2;
+Handle g_Text3;
+Handle g_Text4;
+
+// PropHunt Redux Menus
//new Handle:g_RoundTimer = INVALID_HANDLE;
-new Handle:g_PropMenu = INVALID_HANDLE;
-new Handle:g_ConfigMenu = INVALID_HANDLE;
-
-new Handle:g_PHEnable = INVALID_HANDLE;
-new Handle:g_PHPropMenu = INVALID_HANDLE;
-new Handle:g_PHPropMenuRestrict = INVALID_HANDLE;
-//new Handle:g_PHAdmFlag = INVALID_HANDLE;
-new Handle:g_PHAdvertisements = INVALID_HANDLE;
-new Handle:g_PHPreventFallDamage = INVALID_HANDLE;
-new Handle:g_PHGameDescription = INVALID_HANDLE;
-new Handle:g_PHAirblast = INVALID_HANDLE;
-new Handle:g_PHAntiHack = INVALID_HANDLE;
-new Handle:g_PHReroll = INVALID_HANDLE;
-new Handle:g_PHStaticPropInfo = INVALID_HANDLE;
-new Handle:g_PHSetupLength = INVALID_HANDLE;
-new Handle:g_PHDamageBlocksPropChange = INVALID_HANDLE;
-new Handle:g_PHPropMenuNames = INVALID_HANDLE;
-new Handle:g_PHMultilingual = INVALID_HANDLE;
-
-new String:g_AdText[128] = "";
-
-new bool:g_MapStarted = false;
-
-new bool:g_SteamTools = false;
-new bool:g_TF2Attribs = false;
+Menu g_PropMenu;
+Menu g_ConfigMenu;
+
+// PropHunt Redux CVars
+ConVar g_PHEnable;
+ConVar g_PHPropMenu;
+ConVar g_PHPropMenuRestrict;
+ConVar g_PHAdvertisements;
+ConVar g_PHPreventFallDamage;
+ConVar g_PHGameDescription;
+ConVar g_PHAirblast;
+ConVar g_PHAntiHack;
+ConVar g_PHReroll;
+ConVar g_PHStaticPropInfo;
+ConVar g_PHSetupLength;
+ConVar g_PHDamageBlocksPropChange;
+ConVar g_PHPropMenuNames;
+ConVar g_PHMultilingual;
+ConVar g_PHRespawnDuringSetup;
+ConVar g_PHUseUpdater;
+ConVar g_PHAllowTaunts;
+
+char g_AdText[128] = "";
+
+bool g_MapStarted = false;
+
+// Track optional dependencies
+bool g_SteamTools = false;
+bool g_SteamWorks = false;
+bool g_Updater = false;
+
#if defined OIMM
-new bool:g_OptinMultiMod = false;
+bool g_OptinMultiMod = false;
#endif
-new bool:g_Enabled = true;
+bool g_Enabled = true;
// Timers
-new Handle:g_hAntiHack;
-new Handle:g_hLocked;
-new Handle:g_hScore;
+Handle g_hAntiHack;
+Handle g_hLocked;
+Handle g_hScore;
// Valve CVars we're going to save and adjust
-new Handle:g_hArenaRoundTime;
-new g_ArenaRoundTime;
-new Handle:g_hWeaponCriticals;
-new g_WeaponCriticals;
-new Handle:g_hIdledealmethod;
-new g_Idledealmethod;
-new Handle:g_hTournamentStopwatch;
-new g_TournamentStopwatch;
-new Handle:g_hTournamentHideDominationIcons;
-new g_TournamentHideDominationIcons;
-new Handle:g_hFriendlyfire;
-new g_Friendlyfire;
-new Handle:g_hGravity;
-new g_Gravity;
-new Handle:g_hForcecamera;
-new g_Forcecamera;
-new Handle:g_hArenaCapEnableTime;
-new g_ArenaCapEnableTime;
-new Handle:g_hTeamsUnbalanceLimit;
-new g_TeamsUnbalanceLimit;
-new Handle:g_hArenaMaxStreak;
-new g_ArenaMaxStreak;
-new Handle:g_hEnableRoundWaitTime;
-new g_EnableRoundWaitTime;
-new Handle:g_hWaitingForPlayerTime;
-new g_WaitingForPlayerTime;
-new Handle:g_hArenaUseQueue;
-new g_ArenaUseQueue;
-new Handle:g_hShowVoiceIcons;
-new g_ShowVoiceIcons;
-new Handle:g_hSolidObjects;
-new g_SolidObjects;
-new Handle:g_hArenaPreroundTime;
-new g_ArenaPreroundTime;
-
-new Handle:g_hBonusRoundTime;
-
-new g_Replacements[MAXPLAYERS+1][6];
-new g_ReplacementCount[MAXPLAYERS+1];
-new bool:g_Rerolled[MAXPLAYERS+1] = { false, ... };
-
-new bool:g_CvarsSet;
-
-new RoundChange:g_RoundChange;
-
-new bool:g_CurrentlyFlaming[MAXPLAYERS+1];
-new g_FlameCount[MAXPLAYERS+1];
+ConVar g_hArenaRoundTime;
+int g_ArenaRoundTime;
+ConVar g_hWeaponCriticals;
+bool g_WeaponCriticals;
+ConVar g_hIdledealmethod;
+int g_Idledealmethod;
+ConVar g_hTournamentStopwatch;
+bool g_TournamentStopwatch;
+ConVar g_hTournamentHideDominationIcons;
+bool g_TournamentHideDominationIcons;
+ConVar g_hFriendlyfire;
+bool g_Friendlyfire;
+ConVar g_hGravity;
+int g_Gravity;
+ConVar g_hForcecamera;
+int g_Forcecamera;
+ConVar g_hArenaCapEnableTime;
+int g_ArenaCapEnableTime;
+ConVar g_hTeamsUnbalanceLimit;
+int g_TeamsUnbalanceLimit;
+ConVar g_hArenaMaxStreak;
+int g_ArenaMaxStreak;
+ConVar g_hEnableRoundWaitTime;
+bool g_EnableRoundWaitTime;
+ConVar g_hWaitingForPlayerTime;
+int g_WaitingForPlayerTime;
+ConVar g_hArenaUseQueue;
+bool g_ArenaUseQueue;
+ConVar g_hShowVoiceIcons;
+bool g_ShowVoiceIcons;
+ConVar g_hSolidObjects;
+bool g_SolidObjects;
+ConVar g_hArenaPreroundTime;
+int g_ArenaPreroundTime;
+ConVar g_hArenaFirstBlood;
+bool g_ArenaFirstBlood;
+
+ConVar g_hWeaponDropTime;
+int g_WeaponDropTime;
+
+// Regular convars
+#if !defined SWITCH_TEAMS
+ConVar g_hBonusRoundTime;
+#endif
+ConVar g_hTags;
+
+int g_Replacements[MAXPLAYERS+1][6];
+int g_ReplacementCount[MAXPLAYERS+1];
+bool g_Rerolled[MAXPLAYERS+1] = { false, ... };
+
+bool g_CvarsSet;
+
+RoundChange g_RoundChange;
+
+bool g_CurrentlyFlaming[MAXPLAYERS+1];
+int g_FlameCount[MAXPLAYERS+1];
#define FLY_COUNT 3
-new g_LastPropDamageTime[MAXPLAYERS+1] = { -1, ... };
-new g_LastPropPlayer = 0;
+int g_LastPropDamageTime[MAXPLAYERS+1] = { -1, ... };
+int g_LastPropPlayer = 0;
-new bool:g_PHMap;
+bool g_PHMap;
-new bool:g_RoundStartMessageSent[MAXPLAYERS+1];
+bool g_RoundStartMessageSent[MAXPLAYERS+1];
// Multi-round support
-new g_RoundCount = 0;
-new g_RoundCurrent = 0;
-new g_RoundSwitchAlways = false;
+int g_RoundCount = 0;
+int g_RoundCurrent = 0;
+int g_RoundSwitchAlways = false;
// New override support for propmenu and stuff
-new g_PropMenuOldFlags = 0;
-new bool:g_PropMenuOverrideInstalled = false;
-new g_PropRerollOldFlags = 0;
-new bool:g_PropRerollOverrideInstalled = false;
+int g_PropMenuOldFlags = 0;
+bool g_PropMenuOverrideInstalled = false;
+int g_PropRerollOldFlags = 0;
+bool g_PropRerollOverrideInstalled = false;
+
+int g_GameRulesProxy = INVALID_ENT_REFERENCE;
+
+#if defined SHINX
+TFClassType g_PreferredHunterClass[MAXPLAYERS+1] = { TFClass_Unknown, ... };
+#endif
+
+// Forward for subplugins
+Handle g_GameDescriptionForward;
-public Plugin:myinfo =
+public Plugin myinfo =
{
name = "PropHunt Redux",
author = "Darkimmortal, Geit, and Powerlord",
@@ -479,96 +451,97 @@ enum
LAST_SHARED_COLLISION_GROUP
}
-#if defined STATS
-
-#include "prophunt\stats2.inc"
-/*
-public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
+enum
{
- decl String:hostname[255], String:ip[32], String:port[8]; //, String:map[92];
- GetConVarString(FindConVar("hostname"), hostname, sizeof(hostname));
- GetConVarString(FindConVar("ip"), ip, sizeof(ip));
- GetConVarString(FindConVar("hostport"), port, sizeof(port));
+ SOLID_NONE = 0, // no solid model
+ SOLID_BSP = 1, // a BSP tree
+ SOLID_BBOX = 2, // an AABB
+ SOLID_OBB = 3, // an OBB (not implemented yet)
+ SOLID_OBB_YAW = 4, // an OBB, constrained so that it can only yaw
+ SOLID_CUSTOM = 5, // Always call into the entity for tests
+ SOLID_VPHYSICS = 6, // solid vphysics object, get vcollide from the model and collide with that
+ SOLID_LAST,
+};
- if(StrContains(hostname, "GamingMasters.co.uk", false) != -1)
+Handle g_hSwitchTeams;
+
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
+{
+ if (GetEngineVersion() != Engine_TF2)
{
- if(StrContains(hostname, "PropHunt", false) == -1 && StrContains(hostname, "Arena", false) == -1 && StrContains(hostname, "Dark", false) == -1 &&
- StrContains(ip, "8.9.4.169", false) == -1)
- return APLRes_SilentFailure;
+ strcopy(error, err_max, "PropHunt Redux only works on Team Fortress 2.");
+ return APLRes_Failure;
}
- return APLRes_Success;
-}
-*/
-#endif
-
-#if defined LOCALSTATS
-
-#include "prophunt\localstats2.inc"
+
+ // This SHOULD be done in steamtools.inc, but isn't.
+ MarkNativeAsOptional("Steam_SetGameDescription");
+#if defined WORKSHOP_SUPPORT
+ // Part of SM 1.8
+ MarkNativeAsOptional("GetMapDisplayName");
#endif
-// DHooks stuff
-#if defined DHOOKS
-new Handle:hWinning = INVALID_HANDLE;
-new bool:g_DHooks = false;
-new g_SetWinningTeamOffset = -1;
-new g_SetWinningTeamHook = -1;
-#endif
+ CreateNative("PropHuntRedux_ValidateMap", Native_ValidateMap);
+ CreateNative("PropHuntRedux_IsRunning", Native_IsRunning);
+ CreateNative("PropHuntRedux_GetPropModel", Native_GetModel);
+ CreateNative("PropHuntRedux_GetPropModelName", Native_GetModelName);
+ CreateNative("PropHuntRedux_IsLastPropMode", Native_LastPropMode);
+
+ RegPluginLibrary("prophuntredux");
-public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max)
-{
- MarkNativeAsOptional("Steam_SetGameDescription");
return APLRes_Success;
}
-public OnPluginStart()
+public void OnPluginStart()
{
-#if defined DHOOKS
- new Handle:gc = LoadGameConfigFile("tf2-roundend.games");
- if (gc != INVALID_HANDLE)
- {
- g_SetWinningTeamOffset = GameConfGetOffset(gc, "SetWinningTeam");
- CloseHandle(gc);
- }
+ Handle gc;
+
+#if defined SWITCH_TEAMS
+ gc = LoadGameConfigFile("tf2-switch-teams");
+
+ StartPrepSDKCall(SDKCall_GameRules);
+ PrepSDKCall_SetFromConf(gc, SDKConf_Virtual, "CTFGameRules::SetSwitchTeams");
+ PrepSDKCall_AddParameter(SDKType_Bool, SDKPass_Plain);
+ g_hSwitchTeams = EndPrepSDKCall();
+
+#if defined LOG
+ LogMessage("[PH] Created call to SetSwitchTeams at vtable offset %d", GameConfGetOffset(gc, "CTeamplayRules::SetSwitchTeams"));
+#endif
+
+ delete gc;
#if defined LOG
else
{
- LogMessage("Failed to load gamedata");
+ LogMessage("[PH] Failed to load gamedata");
}
#endif
#endif
- decl String:hostname[255], String:ip[32], String:port[8]; //, String:map[92];
+ char hostname[255], ip[32], port[8]; //, String:map[92];
GetConVarString(FindConVar("hostname"), hostname, sizeof(hostname));
GetConVarString(FindConVar("ip"), ip, sizeof(ip));
GetConVarString(FindConVar("hostport"), port, sizeof(port));
Format(g_ServerIP, sizeof(g_ServerIP), "%s:%s", ip, port);
- new bool:statsbool = false;
-#if defined STATS || defined LOCALSTATS
- statsbool = true;
-#endif
-
- g_hWeaponRemovals = CreateArray();
- g_hWeaponNerfs = CreateTrie();
- g_hWeaponSelfDamage = CreateTrie();
- g_hWeaponStripAttribs = CreateArray();
- g_hWeaponAddAttribs = CreateTrie();
- g_hWeaponReplacements = CreateTrie();
- g_hWeaponReplacementPlayerClasses = CreateTrie();
+ g_hWeaponRemovals = new ArrayList();
+ g_hPropWeaponRemovals = new ArrayList();
+ g_hWeaponNerfs = new StringMap();
+ g_hWeaponSelfDamage = new StringMap();
+ g_hWeaponStripAttribs = new ArrayList();
+ g_hWeaponAddAttribs = new StringMap();
+ g_hWeaponReplacements = new StringMap();
+ g_hWeaponReplacementPlayerClasses = new StringMap();
- Format(g_Version, sizeof(g_Version), "%s%s", PL_VERSION, statsbool ? "s":"");
// PropHunt Redux now lies and pretends to be PropHunt as well
- CreateConVar("sm_prophunt_version", g_Version, "PropHunt Version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
- CreateConVar("prophunt_redux_version", g_Version, "PropHunt Redux Version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
+ CreateConVar("sm_prophunt_version", PL_VERSION, "PropHunt Version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
+ CreateConVar("prophunt_redux_version", PL_VERSION, "PropHunt Redux Version", FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD);
-// g_PHAdmFlag = CreateConVar("ph_propmenu_flag", "c", "Flag to use for the PropMenu");
g_PHEnable = CreateConVar("ph_enable", "1", "Enables the plugin", FCVAR_DONTRECORD);
g_PHPropMenu = CreateConVar("ph_propmenu", "0", "Control use of the propmenu command: -1 = Disabled, 0 = admins or people with the propmenu override, 1 = all players", _, true, -1.0, true, 1.0);
g_PHPropMenuRestrict = CreateConVar("ph_propmenurestrict", "0", "If ph_propmenu is allowed, restrict typed props to the propmenu list? Defaults to 0 (no).", _, true, 0.0, true, 1.0);
g_PHAdvertisements = CreateConVar("ph_adtext", g_AdText, "Controls the text used for Advertisements");
g_PHPreventFallDamage = CreateConVar("ph_preventfalldamage", "0", "Set to 1 to prevent fall damage. Will use TF2Attributes if available due to client prediction", _, true, 0.0, true, 1.0);
- g_PHGameDescription = CreateConVar("ph_gamedescription", "1", "If SteamTools is loaded, set the Game Description to PropHunt Redux?", _, true, 0.0, true, 1.0);
+ g_PHGameDescription = CreateConVar("ph_gamedescription", "1", "If SteamTools/SteamWorks is loaded, set the Game Description to PropHunt Redux?", _, true, 0.0, true, 1.0);
g_PHAirblast = CreateConVar("ph_airblast", "0", "Allow Pyros to airblast? Takes effect on round change unless TF2Attributes is installed.", _, true, 0.0, true, 1.0);
g_PHAntiHack = CreateConVar("ph_antihack", "1", "Make sure props don't have weapons. Leave this on unless you're having issues with other plugins.", _, true, 0.0, true, 1.0);
g_PHReroll = CreateConVar("ph_propreroll", "0", "Control use of the propreroll command: -1 = Disabled, 0 = admins or people with the propreroll override, 1 = all players", _, true, -1.0, true, 1.0);
@@ -577,6 +550,9 @@ public OnPluginStart()
g_PHDamageBlocksPropChange = CreateConVar("ph_burningblockspropchange", "1", "Block Prop Change while players are bleeding, jarated, or on fire? (Fixes bugs)", _, true, 0.0, true, 1.0);
g_PHPropMenuNames = CreateConVar("ph_propmenuusenames", "0", "Use names for Prop Menu? This is disabled by default for compatibility reasons.", _, true, 0.0, true, 1.0);
g_PHMultilingual = CreateConVar("ph_multilingual", "0", "Use multilingual support? Uses more Handles if enabled. Disabled by default as we have no alternate languages (yet)", _, true, 0.0, true, 1.0);
+ g_PHRespawnDuringSetup = CreateConVar("ph_respawnduringsetup", "1", "If a player dies during setup, should we respawn them?", _, true, 0.0, true, 1.0);
+ g_PHUseUpdater = CreateConVar("ph_useupdater", "1", "Use Updater to keep PropHunt Redux up to date? Only applies if the Updater plugin is installed.", _, true, 0.0, true, 1.0);
+ g_PHAllowTaunts = CreateConVar("ph_allowproptaunts", "0", "Allow props to use taunt items", _, true, 0.0, true, 1.0);
// These are expensive and should be done just once at plugin start.
g_hArenaRoundTime = FindConVar("tf_arena_round_time");
@@ -596,19 +572,25 @@ public OnPluginStart()
g_hShowVoiceIcons = FindConVar("mp_show_voice_icons");
g_hSolidObjects = FindConVar("tf_solidobjects");
g_hArenaPreroundTime = FindConVar("tf_arena_preround_time");
+ g_hArenaFirstBlood = FindConVar("tf_arena_first_blood");
+
+ g_hWeaponDropTime = FindConVar("tf_dropped_weapon_lifetime");
+#if !defined SWITCH_TEAMS
g_hBonusRoundTime = FindConVar("mp_bonusroundtime");
+#endif
+
+ g_hTags = FindConVar("sv_tags");
- HookConVarChange(g_PHEnable, OnEnabledChanged);
- HookConVarChange(g_PHAdvertisements, OnAdTextChanged);
- HookConVarChange(g_PHGameDescription, OnGameDescriptionChanged);
- HookConVarChange(g_PHAntiHack, OnAntiHackChanged);
- HookConVarChange(g_PHStaticPropInfo, OnAntiHackChanged);
- HookConVarChange(g_PHAirblast, OnAirblastChanged);
- HookConVarChange(g_PHPreventFallDamage, OnFallDamageChanged);
- HookConVarChange(g_PHPropMenu, OnPropMenuChanged);
- HookConVarChange(g_PHReroll, OnPropRerollChanged);
- HookConVarChange(g_PHMultilingual, OnMultilingualChanged);
+ g_PHEnable.AddChangeHook(OnEnabledChanged);
+ g_PHAdvertisements.AddChangeHook(OnAdTextChanged);
+ g_PHGameDescription.AddChangeHook(OnGameDescriptionChanged);
+ g_PHAntiHack.AddChangeHook(OnAntiHackChanged);
+ g_PHStaticPropInfo.AddChangeHook(OnAntiHackChanged);
+ g_PHPropMenu.AddChangeHook(OnPropMenuChanged);
+ g_PHReroll.AddChangeHook(OnPropRerollChanged);
+ g_PHMultilingual.AddChangeHook(OnMultilingualChanged);
+ g_PHUseUpdater.AddChangeHook(OnUseUpdaterChanged);
g_Text1 = CreateHudSynchronizer();
g_Text2 = CreateHudSynchronizer();
@@ -617,6 +599,7 @@ public OnPluginStart()
AddServerTag("PropHunt");
+ // Events
HookEvent("player_spawn", Event_player_spawn);
HookEvent("player_team", Event_player_team);
HookEvent("player_death", Event_player_death, EventHookMode_Pre);
@@ -624,18 +607,11 @@ public OnPluginStart()
HookEvent("arena_win_panel", Event_arena_win_panel);
HookEvent("post_inventory_application", Event_post_inventory_application);
HookEvent("teamplay_broadcast_audio", Event_teamplay_broadcast_audio, EventHookMode_Pre);
+ HookEvent("teamplay_round_start", Event_teamplay_round_start_pre, EventHookMode_Pre);
HookEvent("teamplay_round_start", Event_teamplay_round_start);
HookEvent("teamplay_restart_round", Event_teamplay_restart_round);
//HookEvent("teamplay_setup_finished", Event_teamplay_setup_finished); // No longer used since 2.0.3 or so because of issues with certain maps
-#if defined STATS
- Stats_Init();
-#endif
-
-#if defined LOCALSTATS
- LocalStats_Init();
-#endif
-
RegConsoleCmd("help", Command_motd);
RegConsoleCmd("phstats", Command_motd);
RegConsoleCmd("ph_stats", Command_motd);
@@ -653,9 +629,6 @@ public OnPluginStart()
LoadTranslations("prophunt.phrases");
LoadTranslations("common.phrases");
- //g_oFOV = FindSendPropOffs("CBasePlayer", "m_iFOV");
- //g_oDefFOV = FindSendPropOffs("CBasePlayer", "m_iDefaultFOV");
-
g_Sounds = CreateTrie();
g_BroadcastSounds = CreateTrie();
@@ -668,22 +641,11 @@ public OnPluginStart()
RegAdminCmd("ph_pyro", Command_pyro, ADMFLAG_BAN, "Switches to BLU");
RegAdminCmd("ph_reloadconfig", Command_ReloadConfig, ADMFLAG_BAN, "Reloads the PropHunt configuration");
- //if((g_iVelocity = FindSendPropOffs("CBasePlayer", "m_vecVelocity[0]")) == -1)
- //LogError("Could not find offset for CBasePlayer::m_vecVelocity[0]");
-
- //CreateTimer(7.0, Timer_AntiHack, 0, TIMER_REPEAT);
- //CreateTimer(0.6, Timer_Locked, 0, TIMER_REPEAT);
- //CreateTimer(55.0, Timer_Score, 0, TIMER_REPEAT);
-
-
- for(new client=1; client <= MaxClients; client++)
+ for(int client = 1; client <= MaxClients; client++)
{
if(IsClientInGame(client))
{
ForcePlayerSuicide(client);
-#if defined STATS || defined LOCALSTATS
- OnClientPostAdminCheck(client);
-#endif
}
}
g_PropData = CreateTrie();
@@ -711,33 +673,22 @@ public OnPluginStart()
AddMenuItem(g_ConfigMenu, "#propreroll", "PropReroll");
AddMenuItem(g_ConfigMenu, "#preventfalldamage", "Prevent Fall Damage");
AddMenuItem(g_ConfigMenu, "#setuptime", "Setup Time");
-#if defined STATS
- AddMenuItem(g_ConfigMenu, "#stats", "Stats");
-#endif
-}
-// Unfortunately, until we rewrite stats2.inc, this check is going to cause problems.
-/*
-public OnClientPostAdminCheck(client)
-{
-#if defined LOCALSTATS
- LocalStats_OnClientPostAdminCheck(client);
-#endif
+ g_GameDescriptionForward = CreateGlobalForward("PropHuntRedux_UpdateGameDescription", ET_Event, Param_String);
}
-*/
-ReadCommonPropData(bool:onlyLanguageRefresh = false)
+void ReadCommonPropData(bool onlyLanguageRefresh = false)
{
- decl String:Path[PLATFORM_MAX_PATH];
+ char Path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, Path, sizeof(Path), "data/prophunt/prop_common.txt");
- new Handle:propCommon = CreateKeyValues("propcommon");
- if (!FileToKeyValues(propCommon, Path))
+ KeyValues propCommon = CreateKeyValues("propcommon");
+ if (!propCommon.ImportFromFile(Path))
{
LogError("Could not load the g_PropData file!");
return;
}
- if (!KvGotoFirstSubKey(propCommon))
+ if (!propCommon.GotoFirstSubKey())
{
LogError("Prop Common file is empty!");
return;
@@ -747,26 +698,26 @@ ReadCommonPropData(bool:onlyLanguageRefresh = false)
if (!onlyLanguageRefresh)
{
- ClearTrie(g_PropData);
+ g_PropData.Clear();
}
- ClearArray(g_ModelLanguages);
+ g_ModelLanguages.Clear();
- new counter = 0;
+ int counter = 0;
do
{
counter++;
- decl String:modelPath[PLATFORM_MAX_PATH];
+ char modelPath[PLATFORM_MAX_PATH];
- new propData[PropData];
+ int propData[PropData];
- KvGetSectionName(propCommon, modelPath, PLATFORM_MAX_PATH);
- KvGetString(propCommon, "name", propData[PropData_Name], sizeof(propData[PropData_Name]), ""); // Still around for compat reasons
+ propCommon.GetSectionName(modelPath, sizeof(modelPath));
+ propCommon.GetString("name", propData[PropData_Name], sizeof(propData[PropData_Name]), ""); // Still around for compat reasons
if (strlen(propData[PropData_Name]) == 0)
{
- KvGetString(propCommon, "en", propData[PropData_Name], sizeof(propData[PropData_Name]), ""); // Default this to English otherwise
+ propCommon.GetString("en", propData[PropData_Name], sizeof(propData[PropData_Name]), ""); // Default this to English otherwise
}
- KvGetString(propCommon, "offset", propData[PropData_Offset], sizeof(propData[PropData_Offset]), "0 0 0");
- KvGetString(propCommon, "rotation", propData[PropData_Rotation], sizeof(propData[PropData_Rotation]), "0 0 0");
+ propCommon.GetString("offset", propData[PropData_Offset], sizeof(propData[PropData_Offset]), "0 0 0");
+ propCommon.GetString("rotation", propData[PropData_Rotation], sizeof(propData[PropData_Rotation]), "0 0 0");
if (strlen(propData[PropData_Name]) == 0)
{
@@ -776,24 +727,24 @@ ReadCommonPropData(bool:onlyLanguageRefresh = false)
if (!onlyLanguageRefresh)
{
- if (!SetTrieArray(g_PropData, modelPath, propData[0], sizeof(propData), false))
+ if (!g_PropData.SetArray(modelPath, propData[0], sizeof(propData), false))
{
- LogError("Error saving prop data for %s", modelPath);
+ LogError("Error saving prop data for %s, probably a duplicate prop in data/prophunt/prop_common.txt", modelPath);
continue;
}
}
- if (GetConVarBool(g_PHMultilingual))
+ if (g_PHMultilingual.BoolValue)
{
- new Handle:languageTrie = CreateTrie();
+ StringMap languageTrie = CreateTrie();
- for (new i=0;i 0)
{
//language new?
- if (FindStringInArray(g_ModelLanguages, lang) == -1)
+ if (g_ModelLanguages.FindString(lang) == -1)
{
#if defined LOG
LogMessage("[PH] Adding language \"%s\" to languages list", lang);
#endif
- PushArrayString(g_ModelLanguages, lang);
+ g_ModelLanguages.PushString(lang);
}
- SetTrieString(languageTrie, lang, name);
+ languageTrie.SetString(lang, name);
}
}
- if (!SetTrieValue(g_PropNames, modelPath, languageTrie, false))
+ if (!g_PropNames.SetValue(modelPath, languageTrie, false))
{
LogError("Error saving prop names for %s", modelPath);
}
else
{
- PushArrayString(g_PropNamesIndex, modelPath);
+ g_PropNamesIndex.PushString(modelPath);
}
}
- } while (KvGotoNextKey(propCommon));
+ } while (propCommon.GotoNextKey());
- CloseHandle(propCommon);
+ delete propCommon;
LogMessage("Loaded %d props from props_common.txt", counter);
#if defined LOG
- LogMessage("[PH] Loaded %d language(s)", GetArraySize(g_ModelLanguages));
+ LogMessage("[PH] Loaded %d language(s)", g_ModelLanguages.Length);
#endif
}
-ClearPropNames()
+void ClearPropNames()
{
- new arraySize = GetArraySize(g_PropNamesIndex);
- for (new i = 0; i < arraySize; i++)
+ int arraySize = g_PropNamesIndex.Length;
+ for (int i = 0; i < arraySize; i++)
{
- decl String:modelName[PLATFORM_MAX_PATH];
- new Handle:languageTrie = INVALID_HANDLE;
- GetArrayString(g_PropNamesIndex, i, modelName, sizeof(modelName));
- if (GetTrieValue(g_PropNames, modelName, languageTrie) && languageTrie != INVALID_HANDLE)
+ char modelName[PLATFORM_MAX_PATH];
+ StringMap languageTrie;
+ g_PropNamesIndex.GetString(i, modelName, sizeof(modelName));
+ if (g_PropNames.GetValue(modelName, languageTrie) && languageTrie != null)
{
- CloseHandle(languageTrie);
+ delete languageTrie;
}
}
- ClearTrie(g_PropNames);
- ClearArray(g_PropNamesIndex);
+ g_PropNames.Clear();
+ g_PropNamesIndex.Clear();
}
-public OnAllPluginsLoaded()
+public void OnAllPluginsLoaded()
{
g_SteamTools = LibraryExists("SteamTools");
- if (g_SteamTools)
+ g_SteamWorks = LibraryExists("SteamWorks");
+ if (g_SteamTools || g_SteamWorks)
{
#if defined LOG
- LogMessage("[PH] Found SteamTools on startup.");
+ if (g_SteamTools)
+ LogMessage("[PH] Found SteamTools on startup.");
+
+ if (g_SteamWorks)
+ LogMessage("[PH] Found SteamWorks on startup.");
#endif
UpdateGameDescription();
}
+
#if defined OIMM
g_OptinMultiMod = LibraryExists("optin_multimod");
if (g_OptinMultiMod)
@@ -875,111 +832,21 @@ public OnAllPluginsLoaded()
}
#endif
- g_TF2Attribs = LibraryExists("tf2attributes");
-
-#if defined LOG
- if (g_TF2Attribs)
- {
- LogMessage("[PH] Found TF2Attributes on startup.");
- }
-#endif
-
-#if defined DHOOKS
- g_DHooks = LibraryExists("dhooks");
-
- if (g_DHooks)
- {
-#if defined LOG
- LogMessage("[PH] Found DHooks on startup.");
-#endif
- InitializeDHooks();
- }
-
-#endif
-}
-
-#if defined DHOOKS
-public Action:LateLoadDHooks(Handle:timer)
-{
- if (!g_DHooks)
- return Plugin_Stop;
-
- LogMessage("[PH] DHooks Loaded ");
-
- g_DHooks = true;
- InitializeDHooks();
-
- if (g_Enabled && g_MapStarted)
- RegisterDHooks();
-
- return Plugin_Stop;
-}
-
-InitializeDHooks()
-{
- if (!g_DHooks || g_SetWinningTeamOffset == -1)
- return;
-
-#if defined LOG
- LogMessage("[PH] Creating SetWinningTeam hook using offset %d", g_SetWinningTeamOffset);
-#endif
- hWinning = DHookCreate(g_SetWinningTeamOffset, HookType_GameRules, ReturnType_Void, ThisPointer_Ignore, ForceSwitchTeams);
- DHookAddParam(hWinning, HookParamType_Int);
- DHookAddParam(hWinning, HookParamType_Int);
- DHookAddParam(hWinning, HookParamType_Bool);
- DHookAddParam(hWinning, HookParamType_Bool);
- DHookAddParam(hWinning, HookParamType_Bool);
- DHookAddParam(hWinning, HookParamType_Bool);
-}
-
-RegisterDHooks()
-{
- if (!g_DHooks || g_SetWinningTeamHook > -1)
- return;
+ g_Updater = LibraryExists("updater");
- g_SetWinningTeamHook = DHookGamerules(hWinning, false, UnloadForceSwitchTeamsHook);
-
-#if defined LOG
- LogMessage("[PH] Hooking Gamerules SetWinningTeam team switch override using hookid %d", g_SetWinningTeamHook);
-#endif
-}
-
-UnregisterDHooks()
-{
- if (!g_DHooks || g_SetWinningTeamHook == -1)
- return;
-
- DHookRemoveHookID(g_SetWinningTeamHook);
#if defined LOG
- LogMessage("[PH] Manually unhooking Gamerules SetWinningTeam team switch override from hookid %d", g_SetWinningTeamHook);
-#endif
- g_SetWinningTeamHook = -1;
-}
-
-// virtual void SetWinningTeam( int team, int iWinReason, bool bForceMapReset = true, bool bSwitchTeams = false, bool bDontAddScore = false );
-public MRESReturn:ForceSwitchTeams(Handle:hParams)
-{
- if (ShouldSwitchTeams())
+ if (g_Updater)
{
- // params are 1-based
- DHookSetParam(hParams, 4, true);
- return MRES_ChangedHandled;
+ LogMessage("[PH] Found Updater on startup.");
}
- return MRES_Ignored;
-}
-
-public UnloadForceSwitchTeamsHook(hookid)
-{
- g_SetWinningTeamHook = -1;
-#if defined LOG
- LogMessage("[PH] Automatically unhooking Gamerules SetWinningTeam team switch override from hookid %d", hookid);
#endif
}
-#endif
-bool:ShouldSwitchTeams()
+// Should we switch teams this round?
+// Note: Don't confuse this with the games ShouldSwitchTeams
+bool Internal_ShouldSwitchTeams()
{
- new bool:lastRound = (g_RoundCurrent == g_RoundCount);
+ bool lastRound = (g_RoundCurrent == g_RoundCount);
if (lastRound)
{
#if defined LOG
@@ -1000,16 +867,16 @@ bool:ShouldSwitchTeams()
return false;
}
-loadGlobalConfig()
+void loadGlobalConfig()
{
- decl String:Path[PLATFORM_MAX_PATH];
+ char Path[PLATFORM_MAX_PATH];
BuildPath(Path_SM, Path, sizeof(Path), "data/prophunt/prophunt_config.cfg");
- if (g_ConfigKeyValues != INVALID_HANDLE)
+ if (g_ConfigKeyValues != null)
{
- CloseHandle(g_ConfigKeyValues);
+ delete g_ConfigKeyValues;
}
g_ConfigKeyValues = CreateKeyValues("prophunt_config");
- if (!FileToKeyValues(g_ConfigKeyValues, Path))
+ if (!g_ConfigKeyValues.ImportFromFile(Path))
{
LogError("Could not load the PropHunt config file!");
}
@@ -1021,7 +888,7 @@ loadGlobalConfig()
ReadCommonPropData(false);
}
-public OnLibraryAdded(const String:name[])
+public void OnLibraryAdded(const char[] name)
{
if (StrEqual(name, "SteamTools", false))
{
@@ -1031,6 +898,15 @@ public OnLibraryAdded(const String:name[])
g_SteamTools = true;
UpdateGameDescription();
}
+ else
+ if (StrEqual(name, "SteamWorks", false))
+ {
+#if defined LOG
+ LogMessage("[PH] SteamWorks Loaded ");
+#endif
+ g_SteamWorks = true;
+ UpdateGameDescription();
+ }
#if defined OIMM
else
if (StrEqual(name, "optin_multimod", false))
@@ -1042,23 +918,17 @@ public OnLibraryAdded(const String:name[])
}
#endif
else
- if (StrEqual(name, "tf2attributes", false))
+ if (StrEqual(name, "updater", false))
{
#if defined LOG
- LogMessage("[PH] TF2Attributes Loaded ");
+ LogMessage("[PH] Updater Loaded.");
#endif
- g_TF2Attribs = true;
- }
-#if defined DHOOKS
- else
- if (StrEqual(name, "dhooks", false))
- {
- CreateTimer(0.5, LateLoadDHooks);
+ g_Updater = true;
+ Updater_AddPlugin(UPDATE_URL);
}
-#endif
}
-public OnLibraryRemoved(const String:name[])
+public void OnLibraryRemoved(const char[] name)
{
if (StrEqual(name, "SteamTools", false))
{
@@ -1067,121 +937,85 @@ public OnLibraryRemoved(const String:name[])
#endif
g_SteamTools = false;
}
-#if defined OIMM
else
- if (StrEqual(name, "optin_multimod", false))
+ if (StrEqual(name, "SteamWorks", false))
{
#if defined LOG
- LogMessage("[PH] Opt-In Multimod Unloaded ");
+ LogMessage("[PH] SteamWorks Unloaded ");
#endif
- g_OptinMultiMod = false;
+ g_SteamWorks = false;
}
-#endif
+#if defined OIMM
else
- if (StrEqual(name, "tf2attributes", false))
+ if (StrEqual(name, "optin_multimod", false))
{
#if defined LOG
- LogMessage("[PH] TF2Attributes Unloaded ");
+ LogMessage("[PH] Opt-In Multimod Unloaded ");
#endif
- g_TF2Attribs = false;
+ g_OptinMultiMod = false;
}
-#if defined DHOOKS
+#endif
else
- if (StrEqual(name, "dhooks", false))
+ if (StrEqual(name, "updater", false))
{
#if defined LOG
- LogMessage("[PH] DHooks Unloaded ");
-#endif
-
- g_DHooks = false;
- g_SetWinningTeamHook = -1;
- hWinning = INVALID_HANDLE; // Don't attempt to close this Handle as the extension unloader will have already done so
- }
+ LogMessage("[PH] Updater Unloaded.");
#endif
+ g_Updater = false;
+ }
}
-public OnGameDescriptionChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnGameDescriptionChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
UpdateGameDescription();
}
-public OnAntiHackChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnAntiHackChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
if (!g_Enabled)
return;
- if ((GetConVarBool(g_PHAntiHack) || GetConVarBool(g_PHStaticPropInfo)) && g_hAntiHack == INVALID_HANDLE)
+ if (g_PHAntiHack.BoolValue || g_PHStaticPropInfo.BoolValue && g_hAntiHack == null)
{
g_hAntiHack = CreateTimer(7.0, Timer_AntiHack, _, TIMER_REPEAT);
// Also run said timer 0.1 seconds after round start.
CreateTimer(0.1, Timer_AntiHack, _, TIMER_FLAG_NO_MAPCHANGE);
}
- else if (!GetConVarBool(g_PHAntiHack) && !GetConVarBool(g_PHStaticPropInfo) && g_hAntiHack != INVALID_HANDLE)
- {
- CloseHandle(g_hAntiHack);
- g_hAntiHack = INVALID_HANDLE;
- }
-}
-
-public OnAirblastChanged(Handle:convar, const String:oldValue[], const String:newValue[])
-{
- if (!g_Enabled || !g_TF2Attribs)
- return;
-
- new bool:airblast = GetConVarBool(g_PHAirblast);
-
- new flamethrower = -1;
-
- while ((flamethrower = FindEntityByClassname(flamethrower, "tf_weapon_flamethrower")) != -1)
- {
- new iItemDefinitionIndex = GetEntProp(flamethrower, Prop_Send, "m_iItemDefinitionIndex");
- if (iItemDefinitionIndex != WEP_PHLOGISTINATOR || FindValueInArray(g_hWeaponStripAttribs, WEP_PHLOGISTINATOR) >= 0)
- {
- if (airblast)
- {
- TF2Attrib_RemoveByName(flamethrower, "airblast disabled");
- }
- else
- {
- TF2Attrib_SetByName(flamethrower, "airblast disabled", 1.0);
- }
- }
- }
-}
-
-public OnFallDamageChanged(Handle:convar, const String:oldValue[], const String:newValue[])
-{
- if (!g_Enabled || !g_TF2Attribs)
- return;
-
- new Float:fall = GetConVarFloat(g_PHPreventFallDamage);
-
- for (new i = 1; i <= MaxClients; ++i)
+ else if (!g_PHAntiHack.BoolValue && !g_PHStaticPropInfo.BoolValue && g_hAntiHack != null)
{
- if (IsClientInGame(i))
- {
- TF2Attrib_SetByName(i, "cancel falling damage", fall);
- }
+ delete g_hAntiHack;
}
}
-UpdateGameDescription(bool:bAddOnly=false)
+void UpdateGameDescription(bool bAddOnly=false)
{
- if (!g_SteamTools)
+ if (!g_SteamTools && !g_SteamWorks)
{
return;
}
- decl String:gamemode[128];
- if (g_Enabled && GetConVarBool(g_PHGameDescription))
+ char gamemode[128];
+ if (g_Enabled && g_PHGameDescription.BoolValue)
{
if (strlen(g_AdText) > 0)
{
- Format(gamemode, sizeof(gamemode), "PropHunt Redux %s (%s)", g_Version, g_AdText);
+ Format(gamemode, sizeof(gamemode), "PropHunt Redux %s (%s)", PL_VERSION, g_AdText);
}
else
{
- Format(gamemode, sizeof(gamemode), "PropHunt Redux %s", g_Version);
+ Format(gamemode, sizeof(gamemode), "PropHunt Redux %s", PL_VERSION);
+ }
+
+ // Global forward for subplugins to change the game description.
+ Action result = Plugin_Continue;
+ char tempGamemode[128];
+ strcopy(tempGamemode, sizeof(tempGamemode), gamemode);
+ Call_StartForward(g_GameDescriptionForward);
+ Call_PushStringEx(tempGamemode, sizeof(tempGamemode), SM_PARAM_COPYBACK, SM_PARAM_STRING_COPY);
+ Call_Finish(result);
+ if (result == Plugin_Changed)
+ {
+ strcopy(gamemode, sizeof(gamemode), tempGamemode);
}
}
else if (bAddOnly)
@@ -1194,96 +1028,105 @@ UpdateGameDescription(bool:bAddOnly=false)
strcopy(gamemode, sizeof(gamemode), "Team Fortress");
}
- Steam_SetGameDescription(gamemode);
+
+
+ if (g_SteamTools)
+ {
+ Steam_SetGameDescription(gamemode);
+ }
+ else
+ if (g_SteamWorks)
+ {
+ SteamWorks_SetGameDescription(gamemode);
+ }
}
-config_parseWeapons()
+void config_parseWeapons()
{
- /*
- for(new i = 0; i < MAXITEMS; i++)
- {
- g_weaponNerfs[i] = 1.0;
- g_weaponSelfDamage[i] = 10.0;
- g_weaponRemovals[i] = false;
- }
- */
-
- ClearArray(g_hWeaponRemovals);
- ClearTrie(g_hWeaponNerfs);
- ClearTrie(g_hWeaponSelfDamage);
- ClearArray(g_hWeaponStripAttribs);
- ClearTrie(g_hWeaponAddAttribs);
- ClearTrie(g_hWeaponReplacements);
- ClearTrie(g_hWeaponReplacementPlayerClasses);
+ g_hWeaponRemovals.Clear();
+ g_hPropWeaponRemovals.Clear();
+ g_hWeaponNerfs.Clear();
+ g_hWeaponSelfDamage.Clear();
+ g_hWeaponStripAttribs.Clear();
+ g_hWeaponAddAttribs.Clear();
+ g_hWeaponReplacements.Clear();
+ g_hWeaponReplacementPlayerClasses.Clear();
- if (g_ConfigKeyValues == INVALID_HANDLE)
+ if (g_ConfigKeyValues == null)
{
return;
}
- while(KvGoBack(g_ConfigKeyValues))
+ while(g_ConfigKeyValues.GoBack())
{
continue;
}
- if(KvJumpToKey(g_ConfigKeyValues, "items"))
+ if(g_ConfigKeyValues.JumpToKey("items"))
{
do
{
- decl String:SectionName[128];
- KvGotoFirstSubKey(g_ConfigKeyValues);
- KvGetSectionName(g_ConfigKeyValues, SectionName, sizeof(SectionName));
- if(KvGetDataType(g_ConfigKeyValues, "damage_hunters") == KvData_Float)
+ char SectionName[128];
+ g_ConfigKeyValues.GotoFirstSubKey();
+ g_ConfigKeyValues.GetSectionName(SectionName, sizeof(SectionName));
+ if(g_ConfigKeyValues.GetDataType("damage_hunters") == KvData_Float)
{
- SetTrieValue(g_hWeaponNerfs, SectionName, KvGetFloat(g_ConfigKeyValues, "damage_hunters"));
+ g_hWeaponNerfs.SetValue(SectionName, g_ConfigKeyValues.GetFloat("damage_hunters"));
}
- if(KvGetDataType(g_ConfigKeyValues, "removed_hunters") == KvData_Int)
+ if(g_ConfigKeyValues.GetDataType("removed_hunters") == KvData_Int)
{
- if (bool:KvGetNum(g_ConfigKeyValues, "removed_hunters"))
+ if (g_ConfigKeyValues.GetNum("removed_hunters"))
{
- PushArrayCell(g_hWeaponRemovals, StringToInt(SectionName));
+ g_hWeaponRemovals.Push(StringToInt(SectionName));
}
}
- if(KvGetDataType(g_ConfigKeyValues, "self_damage_hunters") == KvData_Float)
+ if(g_ConfigKeyValues.GetDataType("removed_props") == KvData_Int)
{
- SetTrieValue(g_hWeaponSelfDamage, SectionName, KvGetFloat(g_ConfigKeyValues, "self_damage_hunters"));
+ if (g_ConfigKeyValues.GetNum("removed_props"))
+ {
+ g_hPropWeaponRemovals.Push(StringToInt(SectionName));
+ }
+ }
+ if(g_ConfigKeyValues.GetDataType("self_damage_hunters") == KvData_Float)
+ {
+ g_hWeaponSelfDamage.SetValue(SectionName, KvGetFloat(g_ConfigKeyValues, "self_damage_hunters"));
}
- if(KvGetDataType(g_ConfigKeyValues, "stripattribs") == KvData_Int)
+ if(g_ConfigKeyValues.GetDataType("stripattribs") == KvData_Int)
{
- if (bool:KvGetNum(g_ConfigKeyValues, "stripattribs"))
+ if (g_ConfigKeyValues.GetNum("stripattribs"))
{
- PushArrayCell(g_hWeaponStripAttribs, StringToInt(SectionName));
+ g_hWeaponStripAttribs.Push(StringToInt(SectionName));
}
}
- if(KvGetDataType(g_ConfigKeyValues, "addattribs") == KvData_String)
+ if(g_ConfigKeyValues.GetDataType("addattribs") == KvData_String)
{
- new String:attribs[128];
- KvGetString(g_ConfigKeyValues, "addattribs", attribs, sizeof(attribs));
+ char attribs[128];
+ g_ConfigKeyValues.GetString("addattribs", attribs, sizeof(attribs));
if (attribs[0] != '\0')
{
- SetTrieString(g_hWeaponAddAttribs, SectionName, attribs);
+ g_hWeaponAddAttribs.SetString(SectionName, attribs);
}
}
- if(KvGetDataType(g_ConfigKeyValues, "replace") == KvData_String)
+ if(g_ConfigKeyValues.GetDataType("replace") == KvData_String)
{
- new String:attribs[128];
- KvGetString(g_ConfigKeyValues, "replace", attribs, sizeof(attribs));
+ char attribs[128];
+ g_ConfigKeyValues.GetString("replace", attribs, sizeof(attribs));
- new class = KvGetNum(g_ConfigKeyValues, "replace_onlyclasses", TFClassBits_None);
+ int class = g_ConfigKeyValues.GetNum("replace_onlyclasses", TFClassBits_None);
if (attribs[0] != '\0')
{
- SetTrieString(g_hWeaponReplacements, SectionName, attribs);
+ g_hWeaponReplacements.SetString(SectionName, attribs);
}
if (class != TFClassBits_None)
{
- SetTrieValue(g_hWeaponReplacementPlayerClasses, SectionName, class);
+ g_hWeaponReplacementPlayerClasses.SetValue(SectionName, class);
}
}
}
- while(KvGotoNextKey(g_ConfigKeyValues));
+ while(g_ConfigKeyValues.GotoNextKey());
}
else
{
@@ -1291,14 +1134,14 @@ config_parseWeapons()
}
}
-config_parseClasses()
+void config_parseClasses()
{
- new red = TEAM_PROP-2;
- new blue = TEAM_HUNTER-2;
+ int red = TEAM_PROP-2;
+ int blue = TEAM_HUNTER-2;
g_defaultClass[red] = TFClass_Scout;
g_defaultClass[blue] = TFClass_Pyro;
- for(new i = 0; i < 10; i++)
+ for(int i = 0; i < 10; i++)
{
g_classLimits[blue][i] = -1;
g_classLimits[red][i] = -1;
@@ -1307,54 +1150,54 @@ config_parseClasses()
g_classSpeeds[i][2] = 15.0;
}
- if (g_ConfigKeyValues == INVALID_HANDLE)
+ if (g_ConfigKeyValues == null)
{
return;
}
- while(KvGoBack(g_ConfigKeyValues))
+ while(g_ConfigKeyValues.GoBack())
{
continue;
}
- if(KvJumpToKey(g_ConfigKeyValues, "classes"))
+ if(g_ConfigKeyValues.JumpToKey("classes"))
{
do
{
- decl String:SectionName[128];
- KvGotoFirstSubKey(g_ConfigKeyValues);
- KvGetSectionName(g_ConfigKeyValues, SectionName, sizeof(SectionName));
- if(KvGetDataType(g_ConfigKeyValues, "hunter_limit") == KvData_Int)
+ char SectionName[128];
+ g_ConfigKeyValues.GotoFirstSubKey();
+ g_ConfigKeyValues.GetSectionName(SectionName, sizeof(SectionName));
+ if(g_ConfigKeyValues.GetDataType("hunter_limit") == KvData_Int)
{
- g_classLimits[blue][StringToInt(SectionName)] = KvGetNum(g_ConfigKeyValues, "hunter_limit");
+ g_classLimits[blue][StringToInt(SectionName)] = g_ConfigKeyValues.GetNum("hunter_limit");
}
- if(KvGetDataType(g_ConfigKeyValues, "prop_limit") == KvData_Int)
+ if(g_ConfigKeyValues.GetDataType("prop_limit") == KvData_Int)
{
- g_classLimits[red][StringToInt(SectionName)] = KvGetNum(g_ConfigKeyValues, "prop_limit");
+ g_classLimits[red][StringToInt(SectionName)] = g_ConfigKeyValues.GetNum("prop_limit");
}
- if(KvGetDataType(g_ConfigKeyValues, "hunter_default_class") == KvData_Int)
+ if(g_ConfigKeyValues.GetDataType("hunter_default_class") == KvData_Int)
{
- g_defaultClass[blue] = TFClassType:StringToInt(SectionName);
+ g_defaultClass[blue] = view_as(StringToInt(SectionName));
}
- if(KvGetDataType(g_ConfigKeyValues, "prop_default_class") == KvData_Int)
+ if(g_ConfigKeyValues.GetDataType("prop_default_class") == KvData_Int)
{
- g_defaultClass[red] = TFClassType:StringToInt(SectionName);
+ g_defaultClass[red] = view_as(StringToInt(SectionName));
}
- if(KvGetDataType(g_ConfigKeyValues, "base_speed") == KvData_Float)
+ if(g_ConfigKeyValues.GetDataType("base_speed") == KvData_Float)
{
- g_classSpeeds[StringToInt(SectionName)][0] = KvGetFloat(g_ConfigKeyValues, "base_speed");
+ g_classSpeeds[StringToInt(SectionName)][0] = g_ConfigKeyValues.GetFloat("base_speed");
}
- if(KvGetDataType(g_ConfigKeyValues, "max_speed") == KvData_Float)
+ if(g_ConfigKeyValues.GetDataType("max_speed") == KvData_Float)
{
- g_classSpeeds[StringToInt(SectionName)][1] = KvGetFloat(g_ConfigKeyValues, "max_speed");
+ g_classSpeeds[StringToInt(SectionName)][1] = g_ConfigKeyValues.GetFloat("max_speed");
}
- if(KvGetDataType(g_ConfigKeyValues, "speed_increment") == KvData_Float)
+ if(g_ConfigKeyValues.GetDataType("speed_increment") == KvData_Float)
{
- g_classSpeeds[StringToInt(SectionName)][2] = KvGetFloat(g_ConfigKeyValues, "speed_increment");
+ g_classSpeeds[StringToInt(SectionName)][2] = g_ConfigKeyValues.GetFloat("speed_increment");
}
}
- while(KvGotoNextKey(g_ConfigKeyValues));
+ while(g_ConfigKeyValues.GotoNextKey());
}
else
{
@@ -1362,60 +1205,62 @@ config_parseClasses()
}
}
-config_parseSounds()
+void config_parseSounds()
{
- ClearTrie(g_Sounds);
- ClearTrie(g_BroadcastSounds);
+ g_Sounds.Clear();
+ g_BroadcastSounds.Clear();
- if (g_ConfigKeyValues == INVALID_HANDLE)
+ if (g_ConfigKeyValues == null)
{
return;
}
- while(KvGoBack(g_ConfigKeyValues))
+ while(g_ConfigKeyValues.GoBack())
{
continue;
}
- if(KvJumpToKey(g_ConfigKeyValues, "sounds"))
+ if(g_ConfigKeyValues.JumpToKey("sounds"))
{
do
{
- decl String:SectionName[128];
- KvGotoFirstSubKey(g_ConfigKeyValues);
- KvGetSectionName(g_ConfigKeyValues, SectionName, sizeof(SectionName));
- if(KvGetDataType(g_ConfigKeyValues, "sound") == KvData_String)
+ char SectionName[128];
+ g_ConfigKeyValues.GotoFirstSubKey();
+ g_ConfigKeyValues.GetSectionName(SectionName, sizeof(SectionName));
+ if(g_ConfigKeyValues.GetDataType("sound") == KvData_String)
{
- decl String:soundString[PLATFORM_MAX_PATH];
- KvGetString(g_ConfigKeyValues, "sound", soundString, sizeof(soundString));
+ char soundString[PLATFORM_MAX_PATH];
+ g_ConfigKeyValues.GetString("sound", soundString, sizeof(soundString));
if(PrecacheSound(soundString))
{
- decl String:downloadString[PLATFORM_MAX_PATH];
+ char downloadString[PLATFORM_MAX_PATH];
Format(downloadString, sizeof(downloadString), "sound/%s", soundString);
AddFileToDownloadsTable(downloadString);
- SetTrieString(g_Sounds, SectionName, soundString, true);
+ g_Sounds.SetString(SectionName, soundString, true);
}
}
- if(KvGetDataType(g_ConfigKeyValues, "broadcast") == KvData_String)
+ if(g_ConfigKeyValues.GetDataType("broadcast") == KvData_String)
{
- decl String:soundString[128];
- KvGetString(g_ConfigKeyValues, "broadcast", soundString, sizeof(soundString));
+ char soundString[128];
+ g_ConfigKeyValues.GetString("broadcast", soundString, sizeof(soundString));
PrecacheScriptSound(soundString);
- SetTrieString(g_BroadcastSounds, SectionName, soundString, true);
+ g_BroadcastSounds.SetString(SectionName, soundString, true);
}
- if(KvGetDataType(g_ConfigKeyValues, "game") == KvData_String)
+ if(g_ConfigKeyValues.GetDataType("game") == KvData_String)
{
- decl String:soundString[128];
- KvGetString(g_ConfigKeyValues, "game", soundString, sizeof(soundString));
+ char soundString[128];
+ g_ConfigKeyValues.GetString("game", soundString, sizeof(soundString));
- SetTrieString(g_BroadcastSounds, SectionName, soundString, true);
+ PrecacheScriptSound(soundString);
+
+ g_BroadcastSounds.SetString(SectionName, soundString, true);
}
}
- while(KvGotoNextKey(g_ConfigKeyValues));
+ while(g_ConfigKeyValues.GotoNextKey());
}
else
{
@@ -1423,115 +1268,144 @@ config_parseSounds()
}
}
-SetCVars(){
+void SetCVars(){
- SetConVarFlags(g_hArenaRoundTime, GetConVarFlags(g_hArenaRoundTime) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaUseQueue, GetConVarFlags(g_hArenaUseQueue) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaMaxStreak, GetConVarFlags(g_hArenaMaxStreak) & ~(FCVAR_NOTIFY));
- //cvar = FindConVar("mp_tournament");
- //SetConVarFlags(cvar, GetConVarFlags(cvar) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hTournamentStopwatch, GetConVarFlags(g_hTournamentStopwatch) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hTournamentHideDominationIcons, GetConVarFlags(g_hTournamentHideDominationIcons) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hTeamsUnbalanceLimit, GetConVarFlags(g_hTeamsUnbalanceLimit) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaPreroundTime, GetConVarFlags(g_hArenaPreroundTime) & ~(FCVAR_NOTIFY));
+ g_hArenaRoundTime.Flags = g_hArenaRoundTime.Flags & ~FCVAR_NOTIFY;
+ g_hArenaUseQueue.Flags = g_hArenaUseQueue.Flags & ~FCVAR_NOTIFY;
+ g_hArenaMaxStreak.Flags = g_hArenaMaxStreak.Flags & ~FCVAR_NOTIFY;
+ g_hTournamentStopwatch.Flags = g_hTournamentStopwatch.Flags & ~FCVAR_NOTIFY;
+ g_hTournamentHideDominationIcons.Flags = g_hTournamentHideDominationIcons.Flags & ~FCVAR_NOTIFY;
+ g_hTeamsUnbalanceLimit.Flags = g_hTeamsUnbalanceLimit.Flags & ~FCVAR_NOTIFY;
+ g_hArenaPreroundTime.Flags = g_hArenaPreroundTime.Flags & ~FCVAR_NOTIFY;
- g_ArenaRoundTime = GetConVarInt(g_hArenaRoundTime);
- SetConVarInt(g_hArenaRoundTime, 0, true);
+ g_ArenaRoundTime = g_hArenaRoundTime.IntValue;
+ g_hArenaRoundTime.IntValue = 0;
- g_ArenaUseQueue = GetConVarInt(g_hArenaUseQueue);
- SetConVarInt(g_hArenaUseQueue, 0, true);
+ g_ArenaUseQueue = g_hArenaUseQueue.BoolValue;
+ g_hArenaUseQueue.BoolValue = false;
- g_ArenaMaxStreak = GetConVarInt(g_hArenaMaxStreak);
- SetConVarInt(g_hArenaMaxStreak, 4, true);
+ g_ArenaMaxStreak = g_hArenaMaxStreak.IntValue;
+ g_hArenaMaxStreak.IntValue = 2;
- g_TournamentStopwatch = GetConVarInt(g_hTournamentStopwatch);
- SetConVarInt(g_hTournamentStopwatch, 0, true);
+ g_TournamentStopwatch = g_hTournamentStopwatch.BoolValue;
+ g_hTournamentStopwatch.BoolValue = false;
- g_TournamentHideDominationIcons = GetConVarInt(g_hTournamentHideDominationIcons);
- SetConVarInt(g_hTournamentHideDominationIcons, 0, true);
+ g_TournamentHideDominationIcons = g_hTournamentHideDominationIcons.BoolValue;
+ g_hTournamentHideDominationIcons.BoolValue = true;
- g_TeamsUnbalanceLimit = GetConVarInt(g_hTeamsUnbalanceLimit);
- SetConVarInt(g_hTeamsUnbalanceLimit, UNBALANCE_LIMIT, true);
-
- SetConVarBounds(g_hArenaPreroundTime, ConVarBound_Upper, false);
- g_ArenaPreroundTime = GetConVarInt(g_hArenaPreroundTime);
- SetConVarInt(g_hArenaPreroundTime, IsDedicatedServer() ? 20:5, true);
+ g_TeamsUnbalanceLimit = g_hTeamsUnbalanceLimit.IntValue;
+ g_hTeamsUnbalanceLimit.IntValue = UNBALANCE_LIMIT;
+
+ g_hArenaPreroundTime.SetBounds(ConVarBound_Upper, false);
+ g_ArenaPreroundTime = g_hArenaPreroundTime.IntValue;
+ g_hArenaPreroundTime.IntValue = IsDedicatedServer() ? 20 : 5;
- g_WeaponCriticals = GetConVarInt(g_hWeaponCriticals);
- SetConVarInt(g_hWeaponCriticals, 0, true);
+ g_WeaponCriticals = g_hWeaponCriticals.BoolValue;
+ g_hWeaponCriticals.BoolValue = false;
- g_Idledealmethod = GetConVarInt(g_hIdledealmethod);
- SetConVarInt(g_hIdledealmethod, 0, true);
+ // Idle Deal Method is buggy on Arena sometimes
+ g_Idledealmethod = g_hIdledealmethod.IntValue;
+ g_hIdledealmethod.IntValue = 0;
- g_Friendlyfire = GetConVarInt(g_hFriendlyfire);
- SetConVarInt(g_hFriendlyfire, 0, true);
+ g_Friendlyfire = g_hFriendlyfire.BoolValue;
+ g_hFriendlyfire.BoolValue = false;
- g_Gravity = GetConVarInt(g_hGravity);
- SetConVarInt(g_hGravity, 500, true);
+ // Lower gravity to 500 for PropHunt
+ g_Gravity = g_hGravity.IntValue;
+ g_hGravity.IntValue = 500;
- g_Forcecamera = GetConVarInt(g_hForcecamera);
- SetConVarInt(g_hForcecamera, 1, true);
+ g_Forcecamera = g_hForcecamera.IntValue;
+ g_hForcecamera.IntValue = 1;
- g_ArenaCapEnableTime = GetConVarInt(g_hArenaCapEnableTime);
- SetConVarInt(g_hArenaCapEnableTime, 3600, true); // Set really high
+ g_ArenaCapEnableTime = g_hArenaCapEnableTime.IntValue;
+ g_hArenaCapEnableTime.IntValue = 3600; // Set really high
- g_EnableRoundWaitTime = GetConVarInt(g_hEnableRoundWaitTime);
- SetConVarInt(g_hEnableRoundWaitTime, 0, true);
+ g_EnableRoundWaitTime = g_hEnableRoundWaitTime.BoolValue;
+ g_hEnableRoundWaitTime.BoolValue = false;
- g_WaitingForPlayerTime = GetConVarInt(g_hWaitingForPlayerTime);
- SetConVarInt(g_hWaitingForPlayerTime, 40, true);
+ g_WaitingForPlayerTime = g_hWaitingForPlayerTime.IntValue;
+ g_hWaitingForPlayerTime.IntValue = 40;
- g_ShowVoiceIcons = GetConVarInt(g_hShowVoiceIcons);
- SetConVarInt(g_hShowVoiceIcons, 0, true);
+ g_ShowVoiceIcons = g_hShowVoiceIcons.BoolValue;
+ g_hShowVoiceIcons.BoolValue = false;
- g_SolidObjects = GetConVarInt(g_hSolidObjects);
- SetConVarInt(g_hSolidObjects, 0, true);
+ g_SolidObjects = g_hSolidObjects.BoolValue;
+ g_hSolidObjects.BoolValue = false;
+
+ g_ArenaFirstBlood = g_hArenaFirstBlood.BoolValue;
+ g_hArenaFirstBlood.BoolValue = false;
+
+ // Force weapons to immediately vanish when dropped
+ g_WeaponDropTime = g_hWeaponDropTime.IntValue;
+ g_hWeaponDropTime.IntValue = 0;
+
+ g_hArenaRoundTime.Flags = g_hArenaRoundTime.Flags |= FCVAR_NOTIFY;
+ g_hArenaUseQueue.Flags = g_hArenaUseQueue.Flags |= FCVAR_NOTIFY;
+ g_hArenaMaxStreak.Flags = g_hArenaMaxStreak.Flags |= FCVAR_NOTIFY;
+ g_hTournamentStopwatch.Flags = g_hTournamentStopwatch.Flags |= FCVAR_NOTIFY;
+ g_hTournamentHideDominationIcons.Flags = g_hTournamentHideDominationIcons.Flags |= FCVAR_NOTIFY;
+ g_hTeamsUnbalanceLimit.Flags = g_hTeamsUnbalanceLimit.Flags |= FCVAR_NOTIFY;
+ g_hArenaPreroundTime.Flags = g_hArenaPreroundTime.Flags |= FCVAR_NOTIFY;
g_CvarsSet = true;
}
-ResetCVars()
+void ResetCVars()
{
if (!g_CvarsSet)
return;
- SetConVarFlags(g_hArenaRoundTime, GetConVarFlags(g_hArenaRoundTime) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaUseQueue, GetConVarFlags(g_hArenaUseQueue) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaMaxStreak, GetConVarFlags(g_hArenaMaxStreak) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hTeamsUnbalanceLimit, GetConVarFlags(g_hTeamsUnbalanceLimit) & ~(FCVAR_NOTIFY));
- SetConVarFlags(g_hArenaPreroundTime, GetConVarFlags(g_hArenaPreroundTime) & ~(FCVAR_NOTIFY));
-
- SetConVarInt(g_hArenaRoundTime, g_ArenaRoundTime, true);
- SetConVarInt(g_hArenaUseQueue, g_ArenaUseQueue, true);
- SetConVarInt(g_hArenaMaxStreak, g_ArenaMaxStreak, true);
- SetConVarInt(g_hTournamentStopwatch, g_TournamentStopwatch, true);
- SetConVarInt(g_hTournamentHideDominationIcons, g_TournamentHideDominationIcons, true);
- SetConVarInt(g_hTeamsUnbalanceLimit, g_TeamsUnbalanceLimit, true);
- SetConVarInt(g_hArenaPreroundTime, g_ArenaPreroundTime, true);
- SetConVarInt(g_hWeaponCriticals, g_WeaponCriticals, true);
- SetConVarInt(g_hIdledealmethod, g_Idledealmethod, true);
- SetConVarInt(g_hFriendlyfire, g_Friendlyfire, true);
- SetConVarInt(g_hGravity, g_Gravity, true);
- SetConVarInt(g_hForcecamera, g_Forcecamera, true);
- SetConVarInt(g_hArenaCapEnableTime, g_ArenaCapEnableTime, true);
- SetConVarInt(g_hEnableRoundWaitTime, g_EnableRoundWaitTime, true);
- SetConVarInt(g_hWaitingForPlayerTime, g_WaitingForPlayerTime, true);
- SetConVarInt(g_hShowVoiceIcons, g_ShowVoiceIcons, true);
- SetConVarInt(g_hSolidObjects, g_SolidObjects, true);
+ g_hArenaRoundTime.Flags = g_hArenaRoundTime.Flags & ~FCVAR_NOTIFY;
+ g_hArenaUseQueue.Flags = g_hArenaUseQueue.Flags & ~FCVAR_NOTIFY;
+ g_hArenaMaxStreak.Flags = g_hArenaMaxStreak.Flags & ~FCVAR_NOTIFY;
+ g_hTournamentStopwatch.Flags = g_hTournamentStopwatch.Flags & ~FCVAR_NOTIFY;
+ g_hTournamentHideDominationIcons.Flags = g_hTournamentHideDominationIcons.Flags & ~FCVAR_NOTIFY;
+ g_hTeamsUnbalanceLimit.Flags = g_hTeamsUnbalanceLimit.Flags & ~FCVAR_NOTIFY;
+ g_hArenaPreroundTime.Flags = g_hArenaPreroundTime.Flags & ~FCVAR_NOTIFY;
+
+ g_hArenaRoundTime.IntValue = g_ArenaRoundTime;
+ g_hArenaUseQueue.BoolValue = g_ArenaUseQueue;
+ g_hArenaMaxStreak.IntValue = g_ArenaMaxStreak;
+ g_hTournamentStopwatch.BoolValue = g_TournamentStopwatch;
+ g_hTournamentHideDominationIcons.BoolValue = g_TournamentHideDominationIcons;
+ g_hTeamsUnbalanceLimit.IntValue = g_TeamsUnbalanceLimit;
+ g_hArenaPreroundTime.IntValue = g_ArenaPreroundTime;
+ g_hWeaponCriticals.BoolValue = g_WeaponCriticals;
+ g_hIdledealmethod.IntValue = g_Idledealmethod;
+ g_hFriendlyfire.BoolValue = g_Friendlyfire;
+ g_hGravity.IntValue = g_Gravity;
+ g_hForcecamera.IntValue = g_Forcecamera;
+ g_hArenaCapEnableTime.IntValue = g_ArenaCapEnableTime;
+ g_hEnableRoundWaitTime.BoolValue = g_EnableRoundWaitTime;
+ g_hWaitingForPlayerTime.IntValue = g_WaitingForPlayerTime;
+ g_hShowVoiceIcons.BoolValue = g_ShowVoiceIcons;
+ g_hSolidObjects.BoolValue = g_SolidObjects;
+ g_hArenaFirstBlood.BoolValue = g_ArenaFirstBlood;
+ g_hWeaponDropTime.IntValue = g_WeaponDropTime;
+
+ g_hArenaRoundTime.Flags = g_hArenaRoundTime.Flags |= FCVAR_NOTIFY;
+ g_hArenaUseQueue.Flags = g_hArenaUseQueue.Flags |= FCVAR_NOTIFY;
+ g_hArenaMaxStreak.Flags = g_hArenaMaxStreak.Flags |= FCVAR_NOTIFY;
+ g_hTournamentStopwatch.Flags = g_hTournamentStopwatch.Flags |= FCVAR_NOTIFY;
+ g_hTournamentHideDominationIcons.Flags = g_hTournamentHideDominationIcons.Flags |= FCVAR_NOTIFY;
+ g_hTeamsUnbalanceLimit.Flags = g_hTeamsUnbalanceLimit.Flags |= FCVAR_NOTIFY;
+ g_hArenaPreroundTime.Flags = g_hArenaPreroundTime.Flags |= FCVAR_NOTIFY;
g_CvarsSet = false;
}
-public OnConfigsExecuted()
+public void OnConfigsExecuted()
{
- g_Enabled = GetConVarBool(g_PHEnable) && g_PHMap;
+ g_Enabled = g_PHEnable.BoolValue && g_PHMap;
+
+ g_GameRulesProxy = EntIndexToEntRef(FindEntityByClassname(-1, "tf_gamerules"));
- if (GetConVarInt(g_PHPropMenu) == 1 && !g_PropMenuOverrideInstalled)
+ if (g_PHPropMenu.IntValue == 1 && !g_PropMenuOverrideInstalled)
{
InstallPropMenuOverride();
}
- if (GetConVarInt(g_PHReroll) == 1 && !g_PropRerollOverrideInstalled)
+ if (g_PHReroll.IntValue == 1 && !g_PropRerollOverrideInstalled)
{
InstallPropRerollOverride();
}
@@ -1539,10 +1413,11 @@ public OnConfigsExecuted()
if (g_Enabled)
{
SetCVars();
-// Moved to round start
-//#if defined DHOOKS
-// RegisterDHooks();
-//#endif
+ Internal_AddServerTag();
+ }
+ else
+ {
+ Internal_RemoveServerTag();
}
UpdateGameDescription(true);
@@ -1550,9 +1425,9 @@ public OnConfigsExecuted()
CountRounds();
}
-InstallPropMenuOverride()
+void InstallPropMenuOverride()
{
- new tempFlags;
+ int tempFlags;
if (GetCommandOverride("propmenu", Override_Command, tempFlags))
{
g_PropMenuOldFlags = tempFlags;
@@ -1562,7 +1437,7 @@ InstallPropMenuOverride()
g_PropMenuOverrideInstalled = true;
}
-RemovePropMenuOverride()
+void RemovePropMenuOverride()
{
if (g_PropMenuOldFlags == ADMFLAG_NONE)
{
@@ -1577,9 +1452,9 @@ RemovePropMenuOverride()
g_PropMenuOverrideInstalled = false;
}
-InstallPropRerollOverride()
+void InstallPropRerollOverride()
{
- new tempFlags;
+ int tempFlags;
if (GetCommandOverride("propreroll", Override_Command, tempFlags))
{
g_PropRerollOldFlags = tempFlags;
@@ -1589,7 +1464,7 @@ InstallPropRerollOverride()
g_PropRerollOverrideInstalled = true;
}
-RemovePropRerollOverride()
+void RemovePropRerollOverride()
{
if (g_PropRerollOldFlags == ADMFLAG_NONE)
{
@@ -1604,9 +1479,9 @@ RemovePropRerollOverride()
g_PropRerollOverrideInstalled = false;
}
-public OnPropMenuChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnPropMenuChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
- new newVal = GetConVarInt(g_PHPropMenu);
+ int newVal = g_PHPropMenu.IntValue;
if (g_PropMenuOverrideInstalled && newVal < 1)
{
RemovePropMenuOverride();
@@ -1617,9 +1492,9 @@ public OnPropMenuChanged(Handle:convar, const String:oldValue[], const String:ne
}
}
-public OnPropRerollChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnPropRerollChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
- new newVal = GetConVarInt(g_PHReroll);
+ int newVal = g_PHReroll.IntValue;
if (g_PropRerollOverrideInstalled && newVal < 1)
{
RemovePropRerollOverride();
@@ -1630,9 +1505,9 @@ public OnPropRerollChanged(Handle:convar, const String:oldValue[], const String:
}
}
-public OnMultilingualChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnMultilingualChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
- if (GetConVarBool(convar))
+ if (convar.BoolValue)
{
ReadCommonPropData(true);
}
@@ -1643,34 +1518,34 @@ public OnMultilingualChanged(Handle:convar, const String:oldValue[], const Strin
}
-public OnRebuildAdminCache(AdminCachePart:part)
+public void OnRebuildAdminCache(AdminCachePart part)
{
- if (part == AdminCache_Overrides && (GetConVarInt(g_PHPropMenu) == 1 || GetConVarInt(g_PHReroll) == 1))
+ if (part == AdminCache_Overrides && (g_PHPropMenu.IntValue == 1 || g_PHReroll.IntValue == 1))
{
CreateTimer(0.2, Timer_RestoreOverrides);
}
}
-public Action:Timer_RestoreOverrides(Handle:timer)
+public Action Timer_RestoreOverrides(Handle timer)
{
- if (GetConVarInt(g_PHPropMenu) == 1)
+ if (g_PHPropMenu.IntValue == 1)
{
InstallPropMenuOverride();
}
- if (GetConVarInt(g_PHReroll) == 1)
+ if (g_PHReroll.IntValue == 1)
{
InstallPropRerollOverride();
}
}
-CountRounds()
+void CountRounds()
{
g_RoundCurrent = 0;
g_RoundCount = 0;
- new entity = -1;
+ int entity = -1;
// new prevPriority = 0;
- new bool:roundSwitchAlways = true;
+ bool roundSwitchAlways = true;
while ((entity = FindEntityByClassname(entity, "team_control_point_round")) != -1)
{
@@ -1712,19 +1587,19 @@ CountRounds()
}
-StartTimers(bool:noScoreTimer = false)
+void StartTimers(bool noScoreTimer = false)
{
- if (g_hLocked == INVALID_HANDLE)
+ if (g_hLocked == null)
{
g_hLocked = CreateTimer(0.6, Timer_Locked, _, TIMER_REPEAT);
}
- if (!noScoreTimer && g_hScore == INVALID_HANDLE)
+ if (!noScoreTimer && g_hScore == null)
{
g_hScore = CreateTimer(55.0, Timer_Score, _, TIMER_REPEAT);
}
- if ((GetConVarBool(g_PHAntiHack) || GetConVarBool(g_PHStaticPropInfo)) && g_hAntiHack == INVALID_HANDLE)
+ if ((g_PHAntiHack.BoolValue || g_PHStaticPropInfo.BoolValue) && g_hAntiHack == null)
{
g_hAntiHack = CreateTimer(7.0, Timer_AntiHack, _, TIMER_REPEAT);
// Also run said timer 0.1 seconds after round start.
@@ -1732,35 +1607,32 @@ StartTimers(bool:noScoreTimer = false)
}
}
-StopTimers()
+void StopTimers()
{
- if (g_hAntiHack != INVALID_HANDLE)
+ if (g_hAntiHack != null)
{
- CloseHandle(g_hAntiHack);
- g_hAntiHack = INVALID_HANDLE;
+ delete g_hAntiHack;
}
- if (g_hLocked != INVALID_HANDLE)
+ if (g_hLocked != null)
{
- CloseHandle(g_hLocked);
- g_hLocked = INVALID_HANDLE;
+ delete g_hLocked;
}
- if (g_hScore != INVALID_HANDLE)
+ if (g_hScore != null)
{
- CloseHandle(g_hScore);
- g_hScore = INVALID_HANDLE;
+ delete g_hScore;
}
}
-public OnEnabledChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnEnabledChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
if (!g_MapStarted)
{
return;
}
- if (GetConVarBool(g_PHEnable))
+ if (g_PHEnable.BoolValue)
{
if (g_Enabled)
{
@@ -1768,7 +1640,7 @@ public OnEnabledChanged(Handle:convar, const String:oldValue[], const String:new
}
else
{
- new bool:enabled = IsPropHuntMap();
+ bool enabled = IsPropHuntMap();
if (enabled)
{
g_RoundChange = RoundChange_Enable;
@@ -1792,12 +1664,34 @@ public OnEnabledChanged(Handle:convar, const String:oldValue[], const String:new
}
}
-public OnAdTextChanged(Handle:convar, const String:oldValue[], const String:newValue[])
+public void OnAdTextChanged(ConVar convar, const char[] oldValue, const char[] newValue)
{
strcopy(g_AdText, sizeof(g_AdText), newValue);
}
-public StartTouchHook(entity, other)
+public void OnUseUpdaterChanged(ConVar convar, const char[] oldValue, const char[] newValue)
+{
+ if (!g_Updater)
+ return;
+
+ if (convar.BoolValue)
+ {
+ Updater_ForceUpdate();
+ }
+}
+
+public Action Updater_OnPluginDownloading()
+{
+ return g_PHUseUpdater.BoolValue ? Plugin_Continue : Plugin_Handled;
+}
+
+public int Updater_OnPluginUpdated()
+{
+ PrintCenterTextAll("PropHunt Redux Updated, server restart may be required.");
+ LogMessage("[PH] PropHunt Redux Updated, server restart may be required.");
+}
+
+public void StartTouchHook(int entity, int other)
{
if(other <= MaxClients && other > 0 && !g_TouchingCP[other] && IsClientInGame(other) && IsPlayerAlive(other))
{
@@ -1809,7 +1703,7 @@ public StartTouchHook(entity, other)
}
}
-stock FillHealth (entity)
+stock void FillHealth (int entity)
{
if(IsValidEntity(entity))
{
@@ -1817,25 +1711,7 @@ stock FillHealth (entity)
}
}
-/*
-stock bool:IsValidAdmin(client)
-{
- decl String:flags[26];
- GetConVarString(g_PHAdmFlag, flags, sizeof(flags));
- if (GetUserFlagBits(client) & ADMFLAG_ROOT)
- {
- return true;
- }
- new iFlags = ReadFlagString(flags);
- if (GetUserFlagBits(client) & iFlags)
- {
- return true;
- }
- return false;
-}
-*/
-
-stock ExtinguishPlayer (client){
+stock void ExtinguishPlayer (int client){
if(IsClientInGame(client) && IsPlayerAlive(client) && IsValidEntity(client))
{
ExtinguishEntity(client);
@@ -1843,7 +1719,7 @@ stock ExtinguishPlayer (client){
}
}
-public OnEntityCreated(entity, const String:classname[])
+public void OnEntityCreated(int entity, const char[] classname)
{
if (!g_Enabled)
{
@@ -1866,22 +1742,16 @@ public OnEntityCreated(entity, const String:classname[])
SDKHook(entity, SDKHook_Spawn, OnBullshitEntitySpawned);
}
else
- if(strcmp(classname, "prop_dynamic") == 0 || strcmp(classname, "prop_static") == 0)
+ if(strcmp(classname, "prop_dynamic") == 0)
{
SDKHook(entity, SDKHook_SpawnPost, OnCPEntitySpawned);
}
else
if(strcmp(classname, "team_control_point_master") == 0)
{
- SDKHook(entity, SDKHook_Spawn, OnCPMasterSpawned);
SDKHook(entity, SDKHook_SpawnPost, OnCPMasterSpawnedPost);
}
else
- if(strcmp(classname, "tf_weapon_builder") == 0)
- {
- SDKHook(entity, SDKHook_SpawnPost, OnBuilderSpawned);
- }
- else
if (strcmp(classname, "team_round_timer") == 0)
{
SDKHook(entity, SDKHook_SpawnPost, OnTimerSpawned);
@@ -1893,7 +1763,7 @@ public OnEntityCreated(entity, const String:classname[])
}
}
-public Action:OnBullshitEntitySpawned(entity)
+public Action OnBullshitEntitySpawned(int entity)
{
if(IsValidEntity(entity))
AcceptEntityInput(entity, "Kill");
@@ -1901,35 +1771,37 @@ public Action:OnBullshitEntitySpawned(entity)
return Plugin_Continue;
}
-// This doesn't actually work, sadly.
-// It SHOULD work, but doesn't
-public Action:OnBuilderSpawned(entity)
-{
- if(IsValidEntity(entity))
- {
- SetEntProp(entity, Prop_Send, "m_aBuildableObjectTypes", 0, _, _:TFObject_Sentry);
- }
- return Plugin_Continue;
-}
-
-public OnCPEntitySpawned(entity)
+public void OnCPEntitySpawned(int entity)
{
- decl String:propName[500];
+ char propName[500];
GetEntPropString(entity, Prop_Data, "m_ModelName", propName, sizeof(propName));
if(StrEqual(propName, "models/props_gameplay/cap_point_base.mdl"))
{
// Reset the skin to neutral. I'm looking at you, cp_manor_event
SetVariantInt(0);
AcceptEntityInput(entity, "Skin");
+
+ // Check if the control point is using VPhysics.
+ // If not, make it so it does (fixes issues with CP touch)
+ int curSolidType = GetEntProp(entity, Prop_Data, "m_nSolidType");
+ if (curSolidType != SOLID_VPHYSICS)
+ {
+ char solidType[2];
+ IntToString(SOLID_VPHYSICS, solidType, sizeof(solidType));
+ DispatchKeyValue(entity, "solid", solidType);
+#if defined LOG
+ LogMessage("[PH] Converting control point to VPhysics.");
+#endif
+ }
// Also, hook it for the heal touch hook
SDKHook(entity, SDKHook_StartTouch, StartTouchHook);
}
}
-public Action:OnTimerSpawned(entity)
+public Action OnTimerSpawned(int entity)
{
// Attempt to shut the pre-round timer up at start, unless 5 secs or less are left
- decl String:name[64];
+ char name[64];
GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name));
if (!StrEqual(name, TIMER_NAME))
@@ -1946,50 +1818,104 @@ public Action:OnTimerSpawned(entity)
}
}
-public Action:OnCPMasterSpawned(entity)
+public void OnCPMasterSpawnedPost(int entity)
{
#if defined LOG
LogMessage("[PH] cpmaster spawned");
#endif
- DispatchKeyValue(entity, "switch_teams", "0");
- //SetEntProp(entity, Prop_Data, "m_bSwitchTeamsOnWin", 0); // Changed in 3.0.0 beta 6, now forced off instead of on.
-
- return Plugin_Continue;
-}
+ if (!g_MapStarted)
+ {
+ return;
+ }
+
+ char name[64];
+ if (GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name)) == 0)
+ {
+ DispatchKeyValue(entity, "targetname", "master_control_point");
+ }
+
+ CreateRoundTimer(entity);
+}
-public OnCPMasterSpawnedPost(entity)
+public Action OnArenaSpawned(int entity)
{
- if (!g_MapStarted)
+ char finishedCommand[256];
+
+ Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:ShowInHUD:1:0:-1", TIMER_NAME);
+ SetVariantString(finishedCommand);
+ AcceptEntityInput(entity, "AddOutput");
+
+ Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:Resume:0:0:-1", TIMER_NAME);
+ SetVariantString(finishedCommand);
+ AcceptEntityInput(entity, "AddOutput");
+
+ Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:Enable:0:0:-1", TIMER_NAME);
+ SetVariantString(finishedCommand);
+ AcceptEntityInput(entity, "AddOutput");
+}
+
+void CheckRoundTimer()
+{
+ int entity = -1;
+
+ while ((entity = FindEntityByClassname(entity, "team_round_timer")) != -1)
{
+ char name[64];
+
+ GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name));
+
+ if (StrEqual(name, TIMER_NAME))
+ {
+#if defined LOG
+ LogMessage("[PH] Found timer: %d", entity);
+#endif
return;
}
+ }
+
+ // No timer found, create it
+ LogMessage("[PH] PropHunt timer is missing. Attempting to recreate it...");
+ int cpMaster = FindEntityByClassname(-1, "team_control_point_master");
+
+ if (cpMaster > -1)
+ {
+ CreateRoundTimer(cpMaster);
+ }
+ else
+ {
+ LogError("[PH] Could not locate team_control_point_master when recreating PropHunt timer");
+ }
+}
+void CreateRoundTimer(int entity)
+{
// We need to subtract 30 from the round time for compatibility with older PropHunt Versions
- decl String:time[5];
+ char time[5];
IntToString(g_RoundTime - 30, time, sizeof(time));
- decl String:name[64];
- if (GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name)) == 0)
+ char name[64];
+ GetEntPropString(entity, Prop_Data, "m_iName", name, sizeof(name));
+
+ if (name[0] == '\0')
{
- DispatchKeyValue(entity, "targetname", "master_control_point");
- strcopy(name, sizeof(name), "master_control_point");
+ LogError("team_control_point_master %d has no name", entity);
+ return;
}
// Create our timer.
- new timer = CreateEntityByName("team_round_timer");
+ int timer = CreateEntityByName("team_round_timer");
DispatchKeyValue(timer, "targetname", TIMER_NAME);
- new String:setupLength[5];
+ char setupLength[5];
if (g_Setup >= SETUP_MIN && g_Setup <= SETUP_MAX)
{
IntToString(g_Setup, setupLength, sizeof(setupLength));
}
else
{
- GetConVarString(g_PHSetupLength, setupLength, sizeof(setupLength));
+ g_PHSetupLength.GetString(setupLength, sizeof(setupLength));
}
DispatchKeyValue(timer, "setup_length", setupLength);
- //DispatchKeyValue(timer, "setup_length", "30");
DispatchKeyValue(timer, "reset_time", "1");
DispatchKeyValue(timer, "auto_countdown", "1");
DispatchKeyValue(timer, "timer_length", time);
@@ -1999,219 +1925,197 @@ public OnCPMasterSpawnedPost(entity)
LogMessage("[PH] setting up cpmaster \"%s\" (%d) with team round timer \"%s\" (%d) ", name, entity, TIMER_NAME, timer);
#endif
- decl String:finishedCommand[256];
+ char finishedCommand[256];
Format(finishedCommand, sizeof(finishedCommand), "OnFinished %s:SetWinnerAndForceCaps:%d:0:-1", name, TEAM_PROP);
SetVariantString(finishedCommand);
AcceptEntityInput(timer, "AddOutput");
+ HookSingleEntityOutput(timer, "OnSetupStart", OnSetupStart);
HookSingleEntityOutput(timer, "OnSetupFinished", OnSetupFinished);
}
-public Action:OnArenaSpawned(entity)
+public void OnMapEnd()
{
- decl String:finishedCommand[256];
-
- Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:ShowInHUD:1:0:-1", TIMER_NAME);
- SetVariantString(finishedCommand);
- AcceptEntityInput(entity, "AddOutput");
-
- Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:Resume:0:0:-1", TIMER_NAME);
- SetVariantString(finishedCommand);
- AcceptEntityInput(entity, "AddOutput");
-
- Format(finishedCommand, sizeof(finishedCommand), "OnArenaRoundStart %s:Enable:0:0:-1", TIMER_NAME);
- SetVariantString(finishedCommand);
- AcceptEntityInput(entity, "AddOutput");
-}
-
-public OnMapEnd()
-{
-#if defined STATS || defined LOCALSTATS
- g_MapChanging = true;
-#endif
-
// workaround for CreateEntityByName
g_MapStarted = false;
ResetCVars();
StopTimers();
- new bool:remove = g_Enabled; // Save the enabled value
+ bool remove = g_Enabled; // Save the enabled value
g_Enabled = false;
if (remove)
{
UpdateGameDescription();
}
- for (new client = 1; client<=MaxClients; client++)
+ for (int client = 1; client<=MaxClients; client++)
{
g_CurrentlyFlaming[client] = false;
g_FlameCount[client] = 0;
}
- ClearArray(g_ModelName);
- ClearArray(g_ModelOffset);
- ClearArray(g_ModelRotation);
- ClearArray(g_ModelSkin);
+ g_ModelName.Clear();
+ g_ModelOffset.Clear();
+ g_ModelRotation.Clear();
+ g_ModelSkin.Clear();
- ClearArray(g_hWeaponRemovals);
- ClearTrie(g_hWeaponNerfs);
- ClearTrie(g_hWeaponSelfDamage);
- ClearArray(g_hWeaponStripAttribs);
- ClearTrie(g_hWeaponAddAttribs);
- ClearTrie(g_hWeaponReplacements);
- ClearTrie(g_hWeaponReplacementPlayerClasses);
+ g_hWeaponRemovals.Clear();
+ g_hPropWeaponRemovals.Clear();
+ g_hWeaponNerfs.Clear();
+ g_hWeaponSelfDamage.Clear();
+ g_hWeaponStripAttribs.Clear();
+ g_hWeaponAddAttribs.Clear();
+ g_hWeaponReplacements.Clear();
+ g_hWeaponReplacementPlayerClasses.Clear();
- ClearTrie(g_Sounds);
- ClearTrie(g_BroadcastSounds);
+ g_Sounds.Clear();
+ g_BroadcastSounds.Clear();
}
-public OnMapStart()
+public void OnMapStart()
{
+ g_GameRulesProxy = INVALID_ENT_REFERENCE;
GetCurrentMap(g_Mapname, sizeof(g_Mapname));
g_PHMap = IsPropHuntMap();
if (g_PHMap)
{
- decl String:confil[PLATFORM_MAX_PATH], String:buffer[256], String:offset[32], String:rotation[32];
+ char confil[PLATFORM_MAX_PATH], buffer[256], offset[32], rotation[32];
- new Handle:fl;
+ KeyValues fl;
- if (g_PropMenu != INVALID_HANDLE)
+ if (g_PropMenu != null)
{
- CloseHandle(g_PropMenu);
- g_PropMenu = INVALID_HANDLE;
+ delete g_PropMenu;
}
- g_PropMenu = CreateMenu(Handler_PropMenu, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
- SetMenuTitle(g_PropMenu, "PropHunt Prop Menu");
- SetMenuExitButton(g_PropMenu, true);
+ g_PropMenu = new Menu(Handler_PropMenu, MENU_ACTIONS_DEFAULT|MenuAction_DisplayItem);
+ g_PropMenu.SetTitle("PropHunt Prop Menu");
+ g_PropMenu.ExitButton = true;
BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/prop_menu.txt");
fl = CreateKeyValues("propmenu");
- if (FileToKeyValues(fl, confil))
+ if (fl.ImportFromFile(confil))
{
- new count = 0;
+ int count = 0;
PrintToServer("Successfully loaded %s", confil);
- KvGotoFirstSubKey(fl);
+ fl.GotoFirstSubKey();
do
{
- KvGetSectionName(fl, buffer, sizeof(buffer));
- AddMenuItem(g_PropMenu, buffer, buffer);
+ fl.GetSectionName(buffer, sizeof(buffer));
+ if (!FileExists(buffer, true))
+ {
+ LogError("prop_menu.txt: Prop does not exist: %s", buffer);
+ continue;
+ }
+ g_PropMenu.AddItem(buffer, buffer);
count++;
}
- while (KvGotoNextKey(fl));
+ while (fl.GotoNextKey());
PrintToServer("Successfully parsed %s", confil);
PrintToServer("Added %i models to prop menu.", GetMenuItemCount(g_PropMenu));
}
- CloseHandle(fl);
+ delete fl;
- new sharedCount = 0;
+ int sharedCount = 0;
BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/props_allmaps.txt");
fl = CreateKeyValues("sharedprops");
- if (FileToKeyValues(fl, confil))
+ if (fl.ImportFromFile(confil))
{
PrintToServer("Successfully loaded %s", confil);
- KvGotoFirstSubKey(fl);
+ fl.GotoFirstSubKey();
do
{
- KvGetSectionName(fl, buffer, sizeof(buffer));
- PushArrayString(g_ModelName, buffer);
- AddMenuItem(g_PropMenu, buffer, buffer);
- KvGetString(fl, "offset", offset, sizeof(offset), "0 0 0");
- PushArrayString(g_ModelOffset, offset);
- KvGetString(fl, "rotation", rotation, sizeof(rotation), "0 0 0");
- PushArrayString(g_ModelRotation, rotation);
- PushArrayCell(g_ModelSkin, KvGetNum(fl, "skin", 0));
+ fl.GetSectionName(buffer, sizeof(buffer));
+ if (!FileExists(buffer, true))
+ {
+ LogError("props_allmaps.txt: Prop does not exist: %s", buffer);
+ continue;
+ }
+ g_ModelName.PushString(buffer);
+ g_PropMenu.AddItem(buffer, buffer);
+ fl.GetString("offset", offset, sizeof(offset), "0 0 0");
+ g_ModelOffset.PushString(offset);
+ fl.GetString("rotation", rotation, sizeof(rotation), "0 0 0");
+ g_ModelRotation.PushString(rotation);
+ g_ModelSkin.Push(KvGetNum(fl, "skin", 0));
}
- while (KvGotoNextKey(fl));
+ while (fl.GotoNextKey());
PrintToServer("Successfully parsed %s", confil);
- sharedCount = GetArraySize(g_ModelName);
+ sharedCount = g_ModelName.Length;
PrintToServer("Loaded %i shared models.", sharedCount);
}
- CloseHandle(fl);
+ delete fl;
- decl String:tidyname[2][PLATFORM_MAX_PATH], String:maptidyname[PLATFORM_MAX_PATH];
- ExplodeString(g_Mapname, "_", tidyname, 2, 32);
- if (strncmp("workshop/", tidyname[0], 9) == 0)
- {
- ReplaceString(tidyname[0], sizeof(tidyname[]), "workshop/", "");
- }
- else if (strncmp("workshop\\", tidyname[0], 9) == 0)
+ if (!FindConfigFileForMap(g_Mapname, confil, sizeof(confil)))
{
- ReplaceString(tidyname[0], sizeof(tidyname[]), "workshop\\", "");
+ LogMessage("[PH] Config file for map %s not found. Disabling plugin.", g_Mapname);
+ g_Enabled = false;
+ return;
}
- // Because we only care about the first two pieces of the map name, we can ignore the ugc stuff
- Format(maptidyname, sizeof(maptidyname), "%s_%s", tidyname[0], tidyname[1]);
- BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/maps/%s.cfg", maptidyname);
+
fl = CreateKeyValues("prophuntmapconfig");
- if(!FileToKeyValues(fl, confil))
+ if(!fl.ImportFromFile(confil))
{
- LogMessage("[PH] Config file for map %s not found at %s. Disabling plugin.", maptidyname, confil);
- CloseHandle(fl);
+ LogMessage("[PH] Config file for map %s at %s could not be opened. Disabling plugin.", g_Mapname, confil);
+ delete fl;
g_Enabled = false;
return;
}
else
{
PrintToServer("Successfully loaded %s", confil);
- KvGotoFirstSubKey(fl);
- KvJumpToKey(fl, "Props", false);
- KvGotoFirstSubKey(fl);
+ fl.GotoFirstSubKey();
+ fl.JumpToKey("Props", false);
+ fl.GotoFirstSubKey();
do
{
- KvGetSectionName(fl, buffer, sizeof(buffer));
- PushArrayString(g_ModelName, buffer);
- AddMenuItem(g_PropMenu, buffer, buffer);
- KvGetString(fl, "offset", offset, sizeof(offset), "0 0 0");
- PushArrayString(g_ModelOffset, offset);
- KvGetString(fl, "rotation", rotation, sizeof(rotation), "0 0 0");
- PushArrayString(g_ModelRotation, rotation);
- PushArrayCell(g_ModelSkin, KvGetNum(fl, "skin", 0));
+ fl.GetSectionName(buffer, sizeof(buffer));
+ if (!FileExists(buffer, true))
+ {
+ LogError("%s: Prop does not exist: %s", confil, buffer);
+ continue;
+ }
+ g_ModelName.PushString(buffer);
+ g_PropMenu.AddItem(buffer, buffer);
+ fl.GetString("offset", offset, sizeof(offset), "0 0 0");
+ g_ModelOffset.PushString(offset);
+ fl.GetString("rotation", rotation, sizeof(rotation), "0 0 0");
+ g_ModelRotation.PushString(rotation);
+ g_ModelSkin.Push(fl.GetNum("skin", 0));
}
- while (KvGotoNextKey(fl));
- KvRewind(fl);
- KvJumpToKey(fl, "Settings", false);
+ while (fl.GotoNextKey());
+ fl.Rewind();
+ fl.JumpToKey("Settings", false);
- g_Doors = bool:KvGetNum(fl, "doors", 0);
- g_Relay = bool:KvGetNum(fl, "relay", 0);
- g_Freeze = bool:KvGetNum(fl, "freeze", 1);
- g_RoundTime = KvGetNum(fl, "round", 175);
- g_Setup = KvGetNum(fl, "setup", 30);
+ g_Doors = view_as(fl.GetNum("doors", 0));
+ g_Relay = view_as(fl.GetNum("relay", 0));
+ g_Freeze = view_as(fl.GetNum("freeze", 1));
+ g_RoundTime = fl.GetNum("round", 175);
+ g_Setup = fl.GetNum("setup", 30);
PrintToServer("Successfully parsed %s", confil);
- PrintToServer("Loaded %i models, doors: %i, relay: %i, freeze: %i, round time: %i, setup: %i.", GetArraySize(g_ModelName)-sharedCount, g_Doors ? 1:0, g_Relay ? 1:0, g_Freeze ? 1:0, g_RoundTime, g_Setup);
+ PrintToServer("Loaded %i models, doors: %i, relay: %i, freeze: %i, round time: %i, setup: %i.", g_ModelName.Length-sharedCount, g_Doors ? 1:0, g_Relay ? 1:0, g_Freeze ? 1:0, g_RoundTime, g_Setup);
}
- CloseHandle(fl);
+ delete fl;
- decl String:model[100];
+ char model[100];
- for(new i = 0; i < GetArraySize(g_ModelName); i++)
+ for(int i = 0; i < g_ModelName.Length; i++)
{
- GetArrayString(g_ModelName, i, model, sizeof(model));
+ g_ModelName.GetString(i, model, sizeof(model));
if(PrecacheModel(model, true) == 0)
{
- RemoveFromArray(g_ModelName, i);
+ g_ModelName.Erase(i);
}
}
- PrecacheModel(FLAMETHROWER, true);
-
- /*new ent = FindEntityByClassname(-1, "team_control_point_master");
- if (ent == 1)
- {
- AcceptEntityInput(ent, "Kill");
- }
- ent = CreateEntityByName("team_control_point_master");
- DispatchKeyValue(ent, "switch_teams", "1");
- DispatchSpawn(ent);
- AcceptEntityInput(ent, "Enable");*/
-
// workaround for CreateEntityByNsme
g_MapStarted = true;
@@ -2220,57 +2124,42 @@ public OnMapStart()
// workaround no win panel event - admin changes, rtv, etc.
g_LastProp = false;
- for (new client = 1; client <= MaxClients; client++)
+ for (int client = 1; client <= MaxClients; client++)
{
g_LastPropDamageTime[client] = -1;
}
g_LastPropPlayer = 0;
g_RoundOver = true;
+ g_inSetup = false;
//g_inPreRound = true;
+ g_PlayerDied = false;
+ g_flRoundStart = 0.0;
// Clear the replacement weapon list
- for (new i = 1; i <= MaxClients; ++i)
+ for (int i = 1; i <= MaxClients; ++i)
{
- for (new j = 0; j < sizeof(g_Replacements[]); ++j)
+ for (int j = 0; j < sizeof(g_Replacements[]); ++j)
{
g_Replacements[i][j] = -1;
}
g_ReplacementCount[i] = 0;
}
-
-#if defined STATS || defined LOCALSTATS
- g_MapChanging = false;
-#endif
-
-}
-
-/*
-public Action:OnGetGameDescription(String:gameDesc[64])
-{
- if (strlen(g_AdText) > 0)
- Format(gameDesc, sizeof(gameDesc), "PropHunt %s (%s)", g_Version, g_AdText);
- else
- Format(gameDesc, sizeof(gameDesc), "PropHunt %s", g_Version);
-
- return Plugin_Changed;
}
-*/
-public OnPluginEnd()
+public void OnPluginEnd()
{
PrintCenterTextAll("%t", "#TF_PH_PluginReload");
-#if defined STATS
- Stats_Uninit();
-#endif
-#if defined LOCALSTATS
- LocalStats_Uninit();
-#endif
ResetCVars();
if (g_SteamTools)
{
Steam_SetGameDescription("Team Fortress");
}
+ else
+ if (g_SteamWorks)
+ {
+ SteamWorks_SetGameDescription("Team Fortress");
+ }
#if defined OIMM
if (g_OptinMultiMod)
{
@@ -2279,7 +2168,7 @@ public OnPluginEnd()
#endif
}
-public Action:TakeDamageHook(victim, &attacker, &inflictor, &Float:damage, &damagetype, &weapon, Float:damageForce[3], Float:damagePosition[3], damagecustom)
+public Action TakeDamageHook(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
{
if (!g_Enabled)
{
@@ -2290,7 +2179,7 @@ public Action:TakeDamageHook(victim, &attacker, &inflictor, &Float:damage, &dama
{
if (IsPlayerAlive(victim) && GetClientTeam(victim) == TEAM_PROP && GetClientTeam(attacker) == TEAM_HUNTER && !g_Hit[victim])
{
- new Float:pos[3];
+ float pos[3];
GetClientAbsOrigin(victim, pos);
PH_EmitSoundToClient(victim, "PropFound", _, SNDCHAN_WEAPON, _, _, 0.8, _, victim, pos);
PH_EmitSoundToClient(attacker, "PropFound", _, SNDCHAN_WEAPON, _, _, 0.8, _, victim, pos);
@@ -2304,11 +2193,11 @@ public Action:TakeDamageHook(victim, &attacker, &inflictor, &Float:damage, &dama
if(weapon > MaxClients && IsValidEntity(weapon))
{
- new String:weaponIndex[10];
+ char weaponIndex[10];
IntToString(GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex"), weaponIndex, sizeof(weaponIndex));
- new Float:multiplier;
- if (GetTrieValue(g_hWeaponNerfs, weaponIndex, multiplier))
+ float multiplier;
+ if (g_hWeaponNerfs.GetValue(weaponIndex, multiplier))
{
damage *= multiplier;
return Plugin_Changed;
@@ -2322,7 +2211,8 @@ public Action:TakeDamageHook(victim, &attacker, &inflictor, &Float:damage, &dama
return Plugin_Changed;
}
- if(GetConVarBool(g_PHPreventFallDamage) && damagetype & DMG_FALL && victim > 0 && victim <= MaxClients && IsClientInGame(victim) && attacker == 0)
+ // block fall damage if set
+ if(g_PHPreventFallDamage.BoolValue && damagetype & DMG_FALL && victim > 0 && victim <= MaxClients && IsClientInGame(victim) && attacker == 0)
{
damage = 0.0;
return Plugin_Changed;
@@ -2330,7 +2220,7 @@ public Action:TakeDamageHook(victim, &attacker, &inflictor, &Float:damage, &dama
return Plugin_Continue;
}
-stock RemoveAnimeModel (client){
+stock void RemovePropModel (int client){
if(IsClientInGame(client) && IsPlayerAlive(client) && IsValidEntity(client))
{
SetVariantString("0 0 0");
@@ -2340,41 +2230,19 @@ stock RemoveAnimeModel (client){
SetVariantString("");
AcceptEntityInput(client, "SetCustomModel");
-
+ RemoveValveHat(client, false);
SetEntProp(client, Prop_Send, "m_bForcedSkin", false);
SetEntProp(client, Prop_Send, "m_nForcedSkin", 0);
}
}
-public OnClientDisconnect(client)
-{
-#if defined STATS
- OCD(client);
-#endif
-
-#if defined LOCALSTATS
- LocalStats_OnClientDisconnect(client);
-#endif
-}
-
-public OnClientDisconnect_Post(client)
+public void OnClientDisconnect_Post(int client)
{
ResetPlayer(client);
-#if defined STATS
- OCD_Post(client);
-#endif
-
-#if defined LOCALSTATS
- LocalStats_OnClientDisconnect_Post(client);
-#endif
}
-stock SwitchView (target, bool:observer, bool:viewmodel){
+stock void SwitchView (int target, bool observer, bool viewmodel){
g_First[target] = !observer;
- /*SetEntPropEnt(target, Prop_Send, "m_hObserverTarget", observer ? target:-1);
- SetEntProp(target, Prop_Send, "m_iObserverMode", observer ? 1:0);
- SetEntData(target, g_oFOV, observer ? 100:GetEntData(target, g_oDefFOV, 4), 4, true);
- SetEntProp(target, Prop_Send, "m_bDrawViewmodel", viewmodel ? 1:0);*/
SetVariantInt(observer ? 1 : 0);
AcceptEntityInput(target, "SetForcedTauntCam");
@@ -2383,25 +2251,9 @@ stock SwitchView (target, bool:observer, bool:viewmodel){
AcceptEntityInput(target, "SetCustomModelVisibletoSelf");
}
-/*
-stock ForceTeamWin (team){
- new ent = FindEntityByClassname(-1, "team_control_point_master");
- if (ent == -1)
- {
- ent = CreateEntityByName("team_control_point_master");
- DispatchKeyValue(ent, "targetname", "master_control_point");
- DispatchKeyValue(ent, "StartDisabled", "0");
- DispatchSpawn(ent);
- AcceptEntityInput(ent, "Enable");
- }
- SetVariantInt(team);
- AcceptEntityInput(ent, "SetWinner");
-}
-*/
-
-public Action:Command_jointeam(client, args)
+public Action Command_jointeam(int client, int args)
{
- decl String:argstr[16];
+ char argstr[16];
GetCmdArgString(argstr, sizeof(argstr));
if(StrEqual(argstr, "spectatearena"))
{
@@ -2414,14 +2266,14 @@ public Action:Command_jointeam(client, args)
return Plugin_Continue;
}
-public Action:Command_ReloadConfig(client, args)
+public Action Command_ReloadConfig(int client, int args)
{
loadGlobalConfig();
CReplyToCommand(client, "PropHunt Config reloaded");
return Plugin_Handled;
}
-public Action:Command_propmenu(client, args)
+public Action Command_propmenu(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
@@ -2431,7 +2283,7 @@ public Action:Command_propmenu(client, args)
CReplyToCommand(client, "%t", "Command is in-game only");
return Plugin_Handled;
}
- if(GetConVarInt(g_PHPropMenu) >= 0)
+ if(g_PHPropMenu.IntValue >= 0)
{
if(GetClientTeam(client) == TEAM_PROP && IsPlayerAlive(client))
{
@@ -2439,7 +2291,7 @@ public Action:Command_propmenu(client, args)
{
if (CanPropChange(client))
{
- decl String:model[PLATFORM_MAX_PATH];
+ char model[PLATFORM_MAX_PATH];
GetCmdArg(1, model, sizeof(model));
if (!FileExists(model, true))
@@ -2448,18 +2300,18 @@ public Action:Command_propmenu(client, args)
return Plugin_Handled;
}
- new bool:restrict = true;
+ bool restrict = true;
- restrict = GetConVarBool(g_PHPropMenuRestrict);
+ restrict = g_PHPropMenuRestrict.BoolValue;
if (restrict)
{
- new found = false;
+ bool found = false;
- new count = GetMenuItemCount(g_PropMenu);
- for (new i = 0; i < count; i++)
+ int count = GetMenuItemCount(g_PropMenu);
+ for (int i = 0; i < count; i++)
{
- decl String:otherModel[PLATFORM_MAX_PATH];
- GetMenuItem(g_PropMenu, i, otherModel, sizeof(otherModel));
+ char otherModel[PLATFORM_MAX_PATH];
+ g_PropMenu.GetItem(i, otherModel, sizeof(otherModel));
if (strcmp(model, otherModel, false) == 0)
{
found = true;
@@ -2502,7 +2354,7 @@ public Action:Command_propmenu(client, args)
return Plugin_Handled;
}
-public Action:Command_propreroll(client, args)
+public Action Command_propreroll(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
@@ -2512,7 +2364,7 @@ public Action:Command_propreroll(client, args)
CReplyToCommand(client, "%t", "Command is in-game only");
return Plugin_Handled;
}
- if(GetConVarInt(g_PHReroll) >= 0)
+ if(g_PHReroll.IntValue >= 0)
{
if(GetClientTeam(client) == TEAM_PROP && IsPlayerAlive(client))
{
@@ -2547,38 +2399,38 @@ public Action:Command_propreroll(client, args)
return Plugin_Handled;
}
-public Handler_PropMenu(Handle:menu, MenuAction:action, param1, param2)
+public int Handler_PropMenu(Menu menu, MenuAction action, int param1, int param2)
{
switch (action)
{
case MenuAction_Display:
{
- decl String:buffer[255];
+ char buffer[255];
Format(buffer, sizeof(buffer), "%T", "#TF_PH_PropMenuName", param1);
- new Handle:panel = Handle:param2;
- SetPanelTitle(panel, buffer);
+ Panel panel = view_as(param2);
+ panel.SetTitle(buffer);
}
case MenuAction_Select:
{
if(IsClientInGame(param1))
{
- if(GetConVarInt(g_PHPropMenu) == 1 || CheckCommandAccess(param1, "propmenu", ADMFLAG_KICK))
+ if(g_PHPropMenu.IntValue == 1 || CheckCommandAccess(param1, "propmenu", ADMFLAG_KICK))
{
if(GetClientTeam(param1) == TEAM_PROP && IsPlayerAlive(param1))
{
if (CanPropChange(param1))
{
- GetMenuItem(menu, param2, g_PlayerModel[param1], PLATFORM_MAX_PATH);
+ menu.GetItem(param2, g_PlayerModel[param1], sizeof(g_PlayerModel[]));
g_RoundStartMessageSent[param1] = false;
RequestFrame(DoEquipProp, GetClientUserId(param1));
}
else
{
CPrintToChat(param1, "%t", "#TF_PH_PropCantChange");
- new pos = GetMenuSelectionPosition();
- DisplayMenuAtItem(menu, param1, pos, MENU_TIME_FOREVER);
+ int pos = menu.Selection;
+ menu.DisplayAt(param1, pos, MENU_TIME_FOREVER);
}
}
else
@@ -2595,15 +2447,15 @@ public Handler_PropMenu(Handle:menu, MenuAction:action, param1, param2)
case MenuAction_DisplayItem:
{
- if (!GetConVarBool(g_PHPropMenuNames))
+ if (!g_PHPropMenuNames.BoolValue)
{
return 0;
}
- new String:model[PLATFORM_MAX_PATH];
- GetMenuItem(menu, param2, model, sizeof(model));
+ char model[PLATFORM_MAX_PATH];
+ menu.GetItem(param2, model, sizeof(model));
- new String:modelName[MAXMODELNAME];
+ char modelName[MAXMODELNAME];
if (GetModelNameForClient(param1, model, modelName, sizeof(modelName)))
{
return RedrawMenuItem(modelName);
@@ -2613,9 +2465,9 @@ public Handler_PropMenu(Handle:menu, MenuAction:action, param1, param2)
return 0;
}
-bool:CanPropChange(client)
+bool CanPropChange(int client)
{
- if (!GetConVarBool(g_PHDamageBlocksPropChange))
+ if (!g_PHDamageBlocksPropChange.BoolValue)
{
return true;
}
@@ -2627,7 +2479,7 @@ bool:CanPropChange(client)
return true;
}
-public OnClientPutInServer(client)
+public void OnClientPutInServer(int client)
{
SDKHook(client, SDKHook_WeaponSwitchPost, WeaponSwitch);
@@ -2641,7 +2493,7 @@ public OnClientPutInServer(client)
ResetPlayer(client);
}
-public ResetPlayer(client)
+public void ResetPlayer(int client)
{
g_Spawned[client] = false;
g_Charge[client] = false;
@@ -2661,7 +2513,7 @@ public ResetPlayer(client)
g_RoundStartMessageSent[client] = false;
}
-public Action: Command_respawn(client, args)
+public Action Command_respawn(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
@@ -2675,66 +2527,143 @@ public Action: Command_respawn(client, args)
return Plugin_Handled;
}
-public Action:Command_internet(client, args)
+public Action Command_internet(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
- decl String:name[255];
- for(new i = 0; i < 3; i++)
+ //char name[PLATFORM_MAX_PATH];
+ for(int i = 0; i < 3; i++)
{
PH_EmitSoundToAll("Internet", _, _, SNDLEVEL_AIRCRAFT);
}
- GetClientName(client, name, sizeof(name));
+ //GetClientName(client, name, sizeof(name));
return Plugin_Handled;
}
-PH_EmitSoundToAll(const String:soundid[], entity = SOUND_FROM_PLAYER, channel = SNDCHAN_AUTO, level = SNDLEVEL_NORMAL, flags = SND_NOFLAGS, Float:volume = SNDVOL_NORMAL, pitch = SNDPITCH_NORMAL, speakerentity = -1, const Float:origin[3] = NULL_VECTOR, const Float:dir[3] = NULL_VECTOR, bool:updatePos = true, Float:soundtime = 0.0)
+// This is a fallback for if other sound playing methods fail
+void BroadcastAudio(const char[] sample, int team = -1)
+{
+ Event event = CreateEvent("teamplay_broadcast_audio");
+
+ if (event == null)
+ return;
+
+ event.SetString("sound", sample);
+ event.SetInt("team", view_as(team));
+ event.SetInt("additional_flags", 0);
+ event.Fire();
+}
+
+void PH_EmitSoundToAll(const char[] soundid, int entity = SOUND_FROM_PLAYER, int channel = SNDCHAN_AUTO, int level = SNDLEVEL_NORMAL, int flags = SND_NOFLAGS, float volume = SNDVOL_NORMAL,
+ int pitch = SNDPITCH_NORMAL, int speakerentity = -1, const float origin[3] = NULL_VECTOR, const float dir[3] = NULL_VECTOR, bool updatePos = true, float soundtime = 0.0)
+{
+ char sample[128];
+
+ if(g_BroadcastSounds.GetString(soundid, sample, sizeof(sample)))
+ {
+ if (EntRefToEntIndex(g_GameRulesProxy) != INVALID_ENT_REFERENCE)
+ {
+ SetVariantString(sample);
+ AcceptEntityInput(g_GameRulesProxy, "PlayVO");
+ }
+ else if (PrecacheScriptSound(sample))
+ {
+ EmitGameSoundToAll(sample, entity, flags, speakerentity, origin, dir, updatePos, soundtime);
+ }
+ else
+ {
+ BroadcastAudio(sample);
+ }
+ }
+ else if(g_Sounds.GetString(soundid, sample, sizeof(sample)))
+ {
+ EmitSoundToAll(sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime);
+ }
+}
+
+stock void PH_EmitSoundToTeam(TFTeam team, const char[] soundid, int entity = SOUND_FROM_PLAYER, int channel = SNDCHAN_AUTO, int level = SNDLEVEL_NORMAL, int flags = SND_NOFLAGS,
+ float volume = SNDVOL_NORMAL, int pitch = SNDPITCH_NORMAL, int speakerentity = -1, const float origin[3] = NULL_VECTOR, const float dir[3] = NULL_VECTOR, bool updatePos = true,
+ float soundtime = 0.0)
{
- decl String:sample[128];
+ char sample[128];
- if(GetTrieString(g_BroadcastSounds, soundid, sample, sizeof(sample)))
+ if(g_BroadcastSounds.GetString(soundid, sample, sizeof(sample)))
{
- if (!EmitGameSoundToAll(sample, entity, flags, speakerentity, origin, dir, updatePos, soundtime))
+ if (EntRefToEntIndex(g_GameRulesProxy) != INVALID_ENT_REFERENCE)
+ {
+ SetVariantString(sample);
+ if (team == TFTeam_Red)
+ {
+ AcceptEntityInput(g_GameRulesProxy, "PlayVORed");
+ }
+ else if (team == TFTeam_Blue)
+ {
+ AcceptEntityInput(g_GameRulesProxy, "PlayVOBlue");
+ }
+ }
+ else if (PrecacheScriptSound(sample))
+ {
+ int count = 0;
+ int clients = new int[MaxClients];
+
+ for (int client = 1; client <= MaxClients; client++)
+ {
+ if (IsClientInGame(client) && !IsFakeClient(client) && GetClientTeam(client) == view_as(team))
+ {
+ clients[count++] = client;
+ }
+ }
+
+ if (count > 0)
+ {
+ EmitGameSound(clients, count, sample, entity, flags, speakerentity, origin, dir, updatePos, soundtime);
+ }
+ }
+ else
{
- new Handle:broadcastEvent = CreateEvent("teamplay_broadcast_audio");
- SetEventInt(broadcastEvent, "team", 0); // despite documentation saying otherwise, it's team -1 for all (docs say team 0). Edit: changed from -1 to 0 in a 2014 update
- SetEventString(broadcastEvent, "sound", sample);
- FireEvent(broadcastEvent);
+ BroadcastAudio(sample, view_as(team));
}
}
- else if(GetTrieString(g_Sounds, soundid, sample, sizeof(sample)))
+ else if(g_Sounds.GetString(soundid, sample, sizeof(sample)))
{
- if(!IsSoundPrecached(sample))
+ int count = 0;
+ int clients = new int[MaxClients];
+
+ for (int client = 1; client <= MaxClients; client++)
{
- PrecacheSound(sample);
+ if (IsClientInGame(client) && !IsFakeClient(client) && GetClientTeam(client) == view_as(team))
+ {
+ clients[count++] = client;
+ }
}
- EmitSoundToAll(sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime);
+
+ EmitSound(clients, count, sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime);
}
+
}
-PH_EmitSoundToClient(client, const String:soundid[], entity = SOUND_FROM_PLAYER, channel = SNDCHAN_AUTO, level = SNDLEVEL_NORMAL, flags = SND_NOFLAGS, Float:volume = SNDVOL_NORMAL, pitch = SNDPITCH_NORMAL, speakerentity = -1, const Float:origin[3] = NULL_VECTOR, const Float:dir[3] = NULL_VECTOR, bool:updatePos = true, Float:soundtime = 0.0)
+
+void PH_EmitSoundToClient(int client, const char[] soundid, int entity = SOUND_FROM_PLAYER, int channel = SNDCHAN_AUTO, int level = SNDLEVEL_NORMAL, int flags = SND_NOFLAGS,
+ float volume = SNDVOL_NORMAL, int pitch = SNDPITCH_NORMAL, int speakerentity = -1, const float origin[3] = NULL_VECTOR, const float dir[3] = NULL_VECTOR, bool updatePos = true,
+ float soundtime = 0.0)
{
- decl String:sample[128];
+ char sample[128];
- new bool:emitted = false;
+ bool emitted = false;
- if(GetTrieString(g_BroadcastSounds, soundid, sample, sizeof(sample)))
+ if(g_BroadcastSounds.GetString(soundid, sample, sizeof(sample)))
{
emitted = EmitGameSoundToClient(client, sample, entity, flags, speakerentity, origin, dir, updatePos, soundtime);
}
- if(!emitted && GetTrieString(g_Sounds, soundid, sample, sizeof(sample)))
+ if(!emitted && g_Sounds.GetString(soundid, sample, sizeof(sample)))
{
- if(!IsSoundPrecached(sample))
- {
- PrecacheSound(sample);
- }
EmitSoundToClient(client, sample, entity, channel, level, flags, volume, pitch, speakerentity, origin, dir, updatePos, soundtime);
}
}
-public Action:Command_switch(client, args)
+public Action Command_switch(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
@@ -2751,7 +2680,7 @@ public Action:Command_switch(client, args)
return Plugin_Handled;
}
-public Action:Command_pyro(client, args)
+public Action Command_pyro(int client, int args)
{
if (!g_Enabled)
return Plugin_Continue;
@@ -2769,9 +2698,9 @@ public Action:Command_pyro(client, args)
CreateTimer(0.8, Timer_Unfreeze, GetClientUserId(client));
return Plugin_Handled;
}
-stock PlayersAlive (){
- new alive = 0;
- for(new i=1; i <= MaxClients; i++)
+stock int PlayersAlive (){
+ int alive = 0;
+ for(int i=1; i <= MaxClients; i++)
{
if(IsClientInGame(i) && IsPlayerAlive(i))
alive++;
@@ -2779,39 +2708,16 @@ stock PlayersAlive (){
return alive;
}
-/*
-public StopPreroundTimers(bool:instant)
-{
- StopTimer(g_TimerStart);
- for (new i = 0; i < sizeof(g_CountdownSoundTimers); i++)
- {
- if(g_CountdownSoundTimers[i] != INVALID_HANDLE)
- {
- StopTimer(g_CountdownSoundTimers[i]);
- }
- }
- if(instant)
- {
- StopTimer(g_RoundTimer);
- }
- else
- {
- CreateTimer(2.0, Timer_AfterWinPanel);
- }
-}
-*/
-
-
-stock ChangeClientTeamAlive (client, team)
+stock void ChangeClientTeamAlive (int client, int team)
{
SetEntProp(client, Prop_Send, "m_lifeState", 2);
ChangeClientTeam(client, team);
SetEntProp(client, Prop_Send, "m_lifeState", 0);
}
-stock GetRandomPlayer (team)
+stock int GetRandomPlayer (int team)
{
- new client, totalclients;
+ int client, totalclients;
for(client=1; client <= MaxClients; client++)
{
@@ -2821,7 +2727,7 @@ stock GetRandomPlayer (team)
}
}
- new clientarray[totalclients], i;
+ int clientarray[totalclients], i;
for(client=1; client <= MaxClients; client++)
{
if(IsClientInGame(client))
@@ -2839,19 +2745,12 @@ stock GetRandomPlayer (team)
return client;
}
-stock StopTimer (Handle:timer)
-{
- if(timer != INVALID_HANDLE) KillTimer(timer);
- timer = INVALID_HANDLE;
-}
-
-stock bool:IsPropHuntMap ()
+stock bool IsPropHuntMap()
{
return ValidateMap(g_Mapname);
}
-
-public Action:TF2_CalcIsAttackCritical(client, weapon, String:weaponname[], &bool:result)
+public Action TF2_CalcIsAttackCritical(int client, int weapon, char[] weaponname, bool &result)
{
if(!g_Enabled || g_RoundOver)
return Plugin_Continue;
@@ -2867,12 +2766,12 @@ public Action:TF2_CalcIsAttackCritical(client, weapon, String:weaponname[], &boo
DoSelfDamage(client, weapon);
result = false;
- return Plugin_Handled;
+ return Plugin_Changed;
}
return Plugin_Continue;
}
-public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:angles[3], &weapon, &subtype, &cmdnum, &tickcount, &seed, mouse[2])
+public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2])
{
if (!g_Enabled || g_RoundOver || !g_CurrentlyFlaming[client])
{
@@ -2889,21 +2788,21 @@ public Action:OnPlayerRunCmd(client, &buttons, &impulse, Float:vel[3], Float:ang
return Plugin_Continue;
}
-public OnGameFrame()
+public void OnGameFrame()
{
if (!g_Enabled || g_RoundOver)
{
return;
}
- for (new client = 1; client <= MaxClients; client++)
+ for (int client = 1; client <= MaxClients; client++)
{
if (!IsClientInGame(client) || !g_CurrentlyFlaming[client] || GetClientTeam(client) != TEAM_HUNTER || !IsPlayerAlive(client) || g_FlameCount[client]++ % FLY_COUNT != 0)
{
continue;
}
- new weapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
+ int weapon = GetEntPropEnt(client, Prop_Send, "m_hActiveWeapon");
if (IsValidEntity(weapon))
{
@@ -2913,14 +2812,14 @@ public OnGameFrame()
}
}
-public WeaponSwitch(client, weapon)
+public void WeaponSwitch(int client, int weapon)
{
if (!g_Enabled || g_RoundOver || !g_CurrentlyFlaming[client])
{
return;
}
- new String:weaponname[64];
+ char weaponname[64];
GetEntityClassname(weapon, weaponname, sizeof(weaponname));
if(strcmp(weaponname, "tf_weapon_flamethrower") != 0)
@@ -2930,18 +2829,18 @@ public WeaponSwitch(client, weapon)
}
}
-stock DoSelfDamage(client, weapon)
+stock void DoSelfDamage(int client, int weapon)
{
- new Float:damage;
- new attacker = client;
+ float damage;
+ int attacker = client;
- new String:weaponIndex[10];
+ char weaponIndex[10];
IntToString(GetEntProp(weapon, Prop_Send, "m_iItemDefinitionIndex"), weaponIndex, sizeof(weaponIndex));
- new String:weaponname[64];
+ char weaponname[64];
GetEntityClassname(weapon, weaponname, sizeof(weaponname));
- if (!GetTrieValue(g_hWeaponSelfDamage, weaponIndex, damage))
+ if (!g_hWeaponSelfDamage.GetValue(weaponIndex, damage))
{
damage = 10.0;
}
@@ -2958,10 +2857,9 @@ stock DoSelfDamage(client, weapon)
SDKHooks_TakeDamage(client, client, attacker, damage, DMG_PREVENT_PHYSICS_FORCE);
}
-stock AddVelocity (client, Float:speed){
- new Float:velocity[3];
+stock void AddVelocity(int client, float speed){
+ float velocity[3];
GetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity);
- //GetEntDataVector(client, g_iVelocity, velocity);
// fucking win
if(velocity[0] < 200 && velocity[0] > -200)
@@ -2974,7 +2872,7 @@ stock AddVelocity (client, Float:speed){
TeleportEntity(client, NULL_VECTOR, NULL_VECTOR, velocity);
}
-public Action:SetTransmitHook(entity, client)
+public Action SetTransmitHook(int entity, int client)
{
if(g_First[client] && client == entity)
{
@@ -2983,7 +2881,7 @@ public Action:SetTransmitHook(entity, client)
return Plugin_Continue;
}
-public PreThinkHook(client)
+public void PreThinkHook(int client)
{
if (!g_Enabled)
return;
@@ -3000,7 +2898,7 @@ public PreThinkHook(client)
ResetSpeed(client);
}
- new buttons = GetClientButtons(client);
+ int buttons = GetClientButtons(client);
if((buttons & IN_ATTACK) == IN_ATTACK && GetClientTeam(client) == TEAM_HUNTER)
{
g_Attacking[client] = true;
@@ -3024,7 +2922,7 @@ public PreThinkHook(client)
if(!g_RotLocked[client])
{
- new Float:velocity[3];
+ float velocity[3];
GetEntPropVector(client, Prop_Data, "m_vecVelocity", velocity);
//GetEntDataVector(client, g_iVelocity, velocity);
// if the client is moving, don't allow them to lock in place
@@ -3094,24 +2992,14 @@ public PreThinkHook(client)
else
if(GetClientTeam(client) == TEAM_HUNTER && TF2_GetPlayerClass(client) == TFClass_Pyro)
{
- /*
- if(IsValidEntity(GetPlayerWeaponSlot(client, 1)) && GetEntProp(GetPlayerWeaponSlot(client, 1), Prop_Send, "m_iItemDefinitionIndex") == WEP_SHOTGUNPYRO || IsValidEntity(GetPlayerWeaponSlot(client, 1)) && GetEntProp(GetPlayerWeaponSlot(client, 1), Prop_Send, "m_iItemDefinitionIndex") == WEP_SHOTGUN_UNIQUE)
- {
- SetEntData(client, FindDataMapOffs(client, "m_iAmmo") + 8, SHOTGUN_MAX_AMMO-GetEntData(GetPlayerWeaponSlot(client, 1), FindSendPropInfo("CBaseCombatWeapon", "m_iClip1")));
- if(GetEntData(GetPlayerWeaponSlot(client, 1), FindSendPropInfo("CBaseCombatWeapon", "m_iClip1")) > SHOTGUN_MAX_AMMO)
- {
- SetEntData(GetPlayerWeaponSlot(client, 1), FindSendPropInfo("CBaseCombatWeapon", "m_iClip1"), SHOTGUN_MAX_AMMO);
- }
- }
- */
- new shotgun = GetPlayerWeaponSlot(client, 1);
+ int shotgun = GetPlayerWeaponSlot(client, 1);
if(IsValidEntity(shotgun))
{
- new index = GetEntProp(shotgun, Prop_Send, "m_iItemDefinitionIndex");
+ int index = GetEntProp(shotgun, Prop_Send, "m_iItemDefinitionIndex");
if (index == WEP_SHOTGUNPYRO || index == WEP_SHOTGUN_UNIQUE)
{
- new ammoOffset = GetEntProp(shotgun, Prop_Send, "m_iPrimaryAmmoType");
- new clip = GetEntProp(shotgun, Prop_Send, "m_iClip1");
+ int ammoOffset = GetEntProp(shotgun, Prop_Send, "m_iPrimaryAmmoType");
+ int clip = GetEntProp(shotgun, Prop_Send, "m_iClip1");
SetEntProp(client, Prop_Send, "m_iAmmo", SHOTGUN_MAX_AMMO - clip, _, ammoOffset);
if (clip > SHOTGUN_MAX_AMMO)
{
@@ -3126,18 +3014,10 @@ public PreThinkHook(client)
} // in game
}
-/*
-public SetupRoundTime(time)
-{
- g_RoundTimer = CreateTimer(float(time-1), Timer_TimeUp, _, TIMER_FLAG_NO_MAPCHANGE);
- SetConVarInt(g_hArenaRoundTime, time, true, false);
-}
-*/
-
-public GetClassCount(TFClassType:class, team)
+public int GetClassCount(TFClassType class, int team)
{
- new classCount;
- for(new client = 1; client <= MaxClients; client++)
+ int classCount;
+ for(int client = 1; client <= MaxClients; client++)
{
if (IsClientInGame(client) && GetClientTeam(client) == team && TF2_GetPlayerClass(client) == class)
{
@@ -3147,8 +3027,7 @@ public GetClassCount(TFClassType:class, team)
return classCount;
}
-
-public Action:Command_motd(client, args)
+public Action Command_motd(int client, int args)
{
if (client <= 0)
{
@@ -3162,7 +3041,7 @@ public Action:Command_motd(client, args)
return Plugin_Handled;
}
-public Action:Command_config(client, args)
+public Action Command_config(int client, int args)
{
if (client == 0)
{
@@ -3179,18 +3058,18 @@ public Action:Command_config(client, args)
return Plugin_Handled;
}
-DisplayConfigToConsole(client)
+void DisplayConfigToConsole(int client)
{
- decl String:propMenuStatus[18];
- decl String:propRerollStatus[18];
- decl String:preventFallDamage[4];
- decl String:propChangeRestrict[4];
- decl String:airblast[4];
- decl String:propMenuRestrict[4];
- new propMenu = GetConVarInt(g_PHPropMenu);
- new propReroll = GetConVarInt(g_PHReroll);
+ char propMenuStatus[18];
+ char propRerollStatus[18];
+ char preventFallDamage[4];
+ char propChangeRestrict[4];
+ char airblast[4];
+ char propMenuRestrict[4];
+ int propMenu = g_PHPropMenu.IntValue;
+ int propReroll = g_PHReroll.IntValue;
- if (GetConVarBool(g_PHAirblast))
+ if (g_PHAirblast.BoolValue)
{
airblast = "On";
}
@@ -3217,7 +3096,7 @@ DisplayConfigToConsole(client)
}
}
- if (GetConVarBool(g_PHPropMenuRestrict))
+ if (g_PHPropMenuRestrict.BoolValue)
{
propMenuRestrict = "On";
}
@@ -3226,7 +3105,7 @@ DisplayConfigToConsole(client)
propMenuRestrict = "Off";
}
- if (GetConVarBool(g_PHDamageBlocksPropChange))
+ if (g_PHDamageBlocksPropChange.BoolValue)
{
propChangeRestrict = "On";
}
@@ -3253,7 +3132,7 @@ DisplayConfigToConsole(client)
}
}
- if (GetConVarBool(g_PHPreventFallDamage))
+ if (g_PHPreventFallDamage.BoolValue)
{
preventFallDamage = "On";
}
@@ -3262,32 +3141,29 @@ DisplayConfigToConsole(client)
preventFallDamage = "Off";
}
- ReplyToCommand(client, "%t", "#TF_PH_ConfigName");
- ReplyToCommand(client, "---------------------");
- ReplyToCommand(client, "%t", "#TF_PH_ConfigVersion", PL_VERSION);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigAirblast", airblast);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigPropMenu", propMenuStatus);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigPropMenuRestrict", propMenuRestrict);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigPropChange", propChangeRestrict);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigPropReroll", propRerollStatus);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigPreventFallDamage", preventFallDamage);
- ReplyToCommand(client, "%t", "#TF_PH_ConfigSetupTime", GetConVarInt(g_PHSetupLength));
-#if defined STATS
- ReplyToCommand(client, "%t", "#TF_PH_ConfigStats");
-#endif
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigName");
+ CReplyToCommand(client, "---------------------");
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigVersion", PL_VERSION);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigAirblast", airblast);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigPropMenu", propMenuStatus);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigPropMenuRestrict", propMenuRestrict);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigPropChange", propChangeRestrict);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigPropReroll", propRerollStatus);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigPreventFallDamage", preventFallDamage);
+ CReplyToCommand(client, "%t", "#TF_PH_ConfigSetupTime", g_PHSetupLength.IntValue);
}
-public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
+public int Handler_ConfigMenu(Menu menu, MenuAction action, int param1, int param2)
{
switch (action)
{
case MenuAction_Display:
{
- decl String:buffer[255];
+ char buffer[255];
Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigName", param1);
- new Handle:panel = Handle:param2;
- SetPanelTitle(panel, buffer);
+ Panel panel = view_as(param2);
+ panel.SetTitle(buffer);
}
case MenuAction_Select:
@@ -3297,9 +3173,9 @@ public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
case MenuAction_DisplayItem:
{
- decl String:infoBuf[64];
- GetMenuItem(menu, param2, infoBuf, sizeof(infoBuf));
- new String:buffer[255];
+ char infoBuf[64];
+ menu.GetItem(param2, infoBuf, sizeof(infoBuf));
+ char buffer[255];
if (StrEqual(infoBuf, "#version"))
{
@@ -3308,24 +3184,14 @@ public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
else
if (StrEqual(infoBuf, "#airblast"))
{
- decl String:airblast[4];
- if (GetConVarBool(g_PHAirblast))
- {
- airblast = "On";
- }
- else
- {
- airblast = "Off";
- }
-
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigAirblast", param1, airblast);
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigAirblast", param1, g_PHAirblast.BoolValue ? "On" : "Off");
}
else
if (StrEqual(infoBuf, "#propmenu"))
{
- new propMenu = GetConVarInt(g_PHPropMenu);
+ int propMenu = g_PHPropMenu.IntValue;
- decl String:propMenuStatus[25];
+ char propMenuStatus[25];
switch (propMenu)
{
@@ -3357,39 +3223,19 @@ public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
else
if (StrEqual(infoBuf, "#proprestrict"))
{
- decl String:propMenuRestrict[4];
- if (GetConVarBool(g_PHPropMenuRestrict))
- {
- propMenuRestrict = "On";
- }
- else
- {
- propMenuRestrict = "Off";
- }
-
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPropMenuRestrict", param1, propMenuRestrict);
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPropMenuRestrict", param1, g_PHPropMenuRestrict.BoolValue ? "On" : "Off");
}
else
if (StrEqual(infoBuf, "#propdamage"))
{
- decl String:propChangeRestrict[4];
- if (GetConVarBool(g_PHDamageBlocksPropChange))
- {
- propChangeRestrict = "On";
- }
- else
- {
- propChangeRestrict = "Off";
- }
-
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPropChange", param1, propChangeRestrict);
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPropChange", param1, g_PHDamageBlocksPropChange.BoolValue ? "On" : "Off");
}
else
if (StrEqual(infoBuf, "#propreroll"))
{
- new propReroll = GetConVarInt(g_PHReroll);
+ int propReroll = g_PHReroll.IntValue;
- decl String:propRerollStatus[25];
+ char propRerollStatus[25];
switch (propReroll)
{
@@ -3421,30 +3267,13 @@ public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
else
if (StrEqual(infoBuf, "#preventfalldamage"))
{
- decl String:preventFallDamage[4];
- if (GetConVarBool(g_PHPreventFallDamage))
- {
- preventFallDamage = "On";
- }
- else
- {
- preventFallDamage = "Off";
- }
-
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPreventFallDamage", param1, preventFallDamage);
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigPreventFallDamage", param1, g_PHPreventFallDamage.BoolValue ? "On" : "Off");
}
else
if (StrEqual(infoBuf, "#setuptime"))
{
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigSetupTime", param1, GetConVarInt(g_PHSetupLength));
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigSetupTime", param1, g_PHSetupLength.IntValue);
}
-#if defined STATS
- else
- if (StrEqual(infoBuf, "#stats"))
- {
- Format(buffer, sizeof(buffer), "%T", "#TF_PH_ConfigStats", param1);
- }
-#endif
return RedrawMenuItem(buffer);
}
@@ -3452,100 +3281,50 @@ public Handler_ConfigMenu(Handle:menu, MenuAction:action, param1, param2)
return 0;
}
-stock SetAlpha (target, alpha){
+stock void SetAlpha(int target, int alpha) {
SetWeaponsAlpha(target,alpha);
SetEntityRenderMode(target, RENDER_TRANSCOLOR);
SetEntityRenderColor(target, 255, 255, 255, alpha);
}
-stock SetWeaponsAlpha (target, alpha){
+stock void SetWeaponsAlpha(int target, int alpha) {
if(IsPlayerAlive(target))
{
- //decl String:classname[64];
-
- /*
- // Old version
- new m_hMyWeapons = FindSendPropOffs("CBasePlayer", "m_hMyWeapons");
- for(new i = 0, weapon; i < 47; i += 4)
- {
- weapon = GetEntDataEnt2(target, m_hMyWeapons + i);
- if(weapon > -1 && IsValidEdict(weapon))
- {
- GetEdictClassname(weapon, classname, sizeof(classname));
- if(StrContains(classname, "tf_weapon", false) != -1)
- {
- SetEntityRenderMode(weapon, RENDER_TRANSCOLOR);
- SetEntityRenderColor(weapon, 255, 255, 255, alpha);
- }
- }
- }
- */
-
- /*
- // "New" old version
- // There are 47 weapon slots of offsets 0 - 188, the previous code was unfortunately wrong.
- for(new i = 0, weapon; i <= 47; ++i)
- {
- weapon = GetEntPropEnt(target, Prop_Send, "m_hMyWeapons", i);
- if(weapon > -1 && IsValidEdict(weapon))
- {
- GetEdictClassname(weapon, classname, sizeof(classname));
- if(StrContains(classname, "tf_weapon", false) != -1)
- {
- SetEntityRenderMode(weapon, RENDER_TRANSCOLOR);
- SetEntityRenderColor(weapon, 255, 255, 255, alpha);
- }
- }
- }
- */
-
// TF2 only supports 1 weapon per slot, so save time and just check all 6 slots.
// Engy is the only class with 6 items (3 weapons, 2 tools, and an invisible builder)
- for(new i = 0; i <= 5; ++i)
+ for(int i = 0; i <= 5; ++i)
{
- new weapon = GetPlayerWeaponSlot(target, i);
+ int weapon = GetPlayerWeaponSlot(target, i);
SetItemAlpha(weapon, alpha);
}
}
}
-stock SetItemAlpha(item, alpha)
+stock void SetItemAlpha(int item, int alpha)
{
if(item > -1 && IsValidEdict(item))
{
- // Don't bother checking the classname, it's always tf_weapon_[something] in TF2 for GetPlayerWeaponSlot
SetEntityRenderMode(item, RENDER_TRANSCOLOR);
SetEntityRenderColor(item, 255, 255, 255, alpha);
- new String:classname[65];
+ char classname[65];
GetEntityClassname(item, classname, sizeof(classname));
+ // If it's a weapon, lets adjust the alpha on its extra wearable and its viewmodel too
if (strncmp(classname, "tf_weapon_", 10) == 0)
{
- // It's a weapon, lets hide the extra wearables too
- new extraWearable = GetEntPropEnt(item, Prop_Send, "m_hExtraWearable");
- if(extraWearable > -1 && IsValidEdict(extraWearable))
- {
- SetEntityRenderMode(extraWearable, RENDER_TRANSCOLOR);
- SetEntityRenderColor(extraWearable, 255, 255, 255, alpha);
- }
-
- extraWearable = GetEntPropEnt(item, Prop_Send, "m_hExtraWearableViewModel");
- if(extraWearable > -1 && IsValidEdict(extraWearable))
- {
- SetEntityRenderMode(extraWearable, RENDER_TRANSCOLOR);
- SetEntityRenderColor(extraWearable, 255, 255, 255, alpha);
- }
+ SetItemAlpha(GetEntPropEnt(item, Prop_Send, "m_hExtraWearable"), alpha);
+ SetItemAlpha(GetEntPropEnt(item, Prop_Send, "m_hExtraWearableViewModel"), alpha);
}
}
}
-public Speedup (client)
+public void Speedup(int client)
{
- new TFClassType:clientClass = TF2_GetPlayerClass(client);
+ TFClassType clientClass = TF2_GetPlayerClass(client);
- new Float:clientSpeed = g_currentSpeed[client] + g_classSpeeds[clientClass][2];
+ float clientSpeed = g_currentSpeed[client] + g_classSpeeds[clientClass][2];
if(clientSpeed < g_classSpeeds[clientClass][0])
{
@@ -3561,23 +3340,23 @@ public Speedup (client)
g_currentSpeed[client] = clientSpeed;
}
-ResetSpeed (client)
+void ResetSpeed(int client)
{
SetEntPropFloat(client, Prop_Send, "m_flMaxspeed", g_currentSpeed[client]);
}
-public bool:TraceRayDontHitSelf(entity, mask, any:data)
+public bool TraceRayDontHitSelf(int entity, int mask, any data)
{
return entity != data;
}
-public Action:Event_teamplay_broadcast_audio(Handle:event, const String:name[], bool:dontBroadcast)
+public Action Event_teamplay_broadcast_audio(Event event, const char[] name, bool dontBroadcast)
{
if (!g_Enabled)
return Plugin_Continue;
- decl String:sound[64];
- GetEventString(event, "sound", sound, sizeof(sound));
+ char sound[64];
+ event.GetString("sound", sound, sizeof(sound));
if (StrEqual(sound, "Announcer.AM_RoundStartRandom", false))
{
@@ -3587,17 +3366,17 @@ public Action:Event_teamplay_broadcast_audio(Handle:event, const String:name[],
return Plugin_Continue;
}
-public Event_player_team(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_player_team(Event event, const char[] name, bool dontBroadcast)
{
- new client = GetClientOfUserId(GetEventInt(event, "userid"));
- if(GetEventInt(event, "team") > 1)
+ int client = GetClientOfUserId(GetEventInt(event, "userid"));
+ if(event.GetInt("team") > 1)
{
g_Spec[client] = false;
}
g_RoundStartMessageSent[client] = false;
}
-public Event_arena_win_panel(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_arena_win_panel(Event event, const char[] name, bool dontBroadcast)
{
StopTimers();
#if defined LOG
@@ -3611,111 +3390,30 @@ public Event_arena_win_panel(Handle:event, const String:name[], bool:dontBroadca
if (!g_Enabled)
return;
-#if defined STATS || defined LOCALSTATS
- new winner = GetEventInt(event, "winning_team");
-#if defined STATS
- DbRound(winner);
+ if (Internal_ShouldSwitchTeams())
+ {
+#if defined SWITCH_TEAMS
+ // This is OK as arena_win_panel is fired *after* SetWinningTeam is called.
+ SetSwitchTeams(true);
+#else
+ CreateTimer(g_hBonusRoundTime.FloatValue - TEAM_CHANGE_TIME, Timer_ChangeTeam, _, TIMER_FLAG_NO_MAPCHANGE);
#endif
+ }
-#if defined LOCALSTATS
- LocalStats_DbRound(winner);
-#endif
+ g_hTeamsUnbalanceLimit.IntValue = 0;
-#endif
+ // Players are now reset on round start instead of round end
+ g_hTeamsUnbalanceLimit.Flags = g_hTeamsUnbalanceLimit.Flags & ~FCVAR_NOTIFY;
+ g_hTeamsUnbalanceLimit.IntValue = UNBALANCE_LIMIT;
+}
-#if defined DHOOKS
- if (!g_DHooks || g_SetWinningTeamHook == -1)
+public Action Timer_ChangeTeam(Handle timer)
+{
+ for (int client = 1; client <= MaxClients; ++client)
{
-#endif
- if (ShouldSwitchTeams())
+ if (!IsClientInGame(client))
{
- CreateTimer(GetConVarFloat(g_hBonusRoundTime) - TEAM_CHANGE_TIME, Timer_ChangeTeam, _, TIMER_FLAG_NO_MAPCHANGE);
- }
-#if defined DHOOKS
- }
-#endif
-
- SetConVarInt(g_hTeamsUnbalanceLimit, 0, true);
-
- for(new client=1; client <= MaxClients; client++)
- {
- if(IsClientInGame(client))
- {
-#if defined STATS || defined LOCALSTATS
- if(GetClientTeam(client) == winner)
- {
-#if defined STATS
- AlterScore(client, 3, ScReason_TeamWin, 0);
-#endif
-
-#if defined LOCALSTATS
- LocalStats_AlterScore(client, 3, ScReason_TeamWin, 0);
-#endif
- }
- else
- if(GetClientTeam(client) != _:TFTeam_Spectator)
- {
-#if defined STATS
- AlterScore(client, -1, ScReason_TeamLose, 0);
-#endif
-
-#if defined LOCALSTATS
- LocalStats_AlterScore(client, -1, ScReason_TeamLose, 0);
-#endif
- }
-#endif
- // bit annoying when testing the plugin and/or maps on a listen server
- /*
- if(IsDedicatedServer())
- {
- team = GetClientTeam(client);
- if(team == TEAM_PROP || team == TEAM_HUNTER)
- {
- team = team == TEAM_PROP ? TEAM_HUNTER:TEAM_PROP;
- ChangeClientTeamAlive(client, team);
- }
- }
- */
- }
- //ResetPlayer(client); // Players are now reset on round start instead of round end
- }
-/*
-#if defined LOG
- LogMessage("Team balancing...");
-#endif
- decl String:cname[64];
- while(GetTeamClientCount(TEAM_PROP) > GetTeamClientCount(TEAM_HUNTER) + 1 )
- {
- client = GetRandomPlayer(TEAM_PROP);
- GetClientName(client, cname, sizeof(cname));
- CPrintToChatAll("%t", "#TF_PH_BalanceBlu", cname);
- ChangeClientTeamAlive(client, TEAM_HUNTER);
- }
- while(GetTeamClientCount(TEAM_HUNTER) > GetTeamClientCount(TEAM_PROP) +1 )
- {
- client = GetRandomPlayer(TEAM_HUNTER);
- GetClientName(client, cname, sizeof(cname));
- CPrintToChatAll("%t", "#TF_PH_BalanceRed", cname);
- ChangeClientTeamAlive(client, TEAM_PROP);
- }
-#if defined LOG
- LogMessage("Complete");
-#endif
-*/
-
- SetConVarFlags(g_hTeamsUnbalanceLimit, GetConVarFlags(g_hTeamsUnbalanceLimit) & ~(FCVAR_NOTIFY));
- SetConVarInt(g_hTeamsUnbalanceLimit, UNBALANCE_LIMIT, true);
-
- //StopPreroundTimers(false);
-}
-
-public Action:Timer_ChangeTeam(Handle:timer)
-{
- for (new client = 1; client <= MaxClients; ++client)
- {
- if (!IsClientInGame(client))
- {
- continue;
+ continue;
}
if (GetClientTeam(client) == TEAM_HUNTER)
@@ -3725,71 +3423,36 @@ public Action:Timer_ChangeTeam(Handle:timer)
else
if (GetClientTeam(client) == TEAM_PROP)
{
- RemoveAnimeModel(client);
+ RemovePropModel(client);
ChangeClientTeamAlive(client, TEAM_HUNTER);
}
}
-
return Plugin_Continue;
}
-/*
-public Action:Event_teamplay_round_start_pre(Handle:event, const String:name[], bool:dontBroadcast)
-{
- if (!g_Enabled)
- return;
-
- new bool:reset = GetEventBool(event, "full_reset");
-#if defined LOG
- LogMessage("[PH] teamplay round start: %i, %i", reset, g_RoundOver);
-#endif
- // checking for the first time this calls (pre-setup), i think
- if(reset && g_RoundOver)
- {
- new team, zteam=TEAM_HUNTER;
- for(new client=1; client <= MaxClients; client++)
- {
- if(IsClientInGame(client))
- {
-
- team = GetClientTeam(client);
-
- // prevent sitting out
- if(team == TEAM_SPEC && !g_Spec[client])
- {
- ChangeClientTeam(client, zteam);
- zteam = zteam == TEAM_HUNTER ? TEAM_PROP:TEAM_HUNTER;
- }
-
- }
- }
- }
-}
-*/
-
-public Event_post_inventory_application(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_post_inventory_application(Event event, const char[] name, bool dontBroadcast)
{
- new client = GetClientOfUserId(GetEventInt(event, "userid"));
+ int client = GetClientOfUserId(GetEventInt(event, "userid"));
if (g_ReplacementCount[client] > 0)
{
- for (new i = 0; i < g_ReplacementCount[client]; ++i)
+ for (int i = 0; i < g_ReplacementCount[client]; ++i)
{
// DON'T require FORCE_GENERATION here, since they could pass back tf_weapon_shotgun
- new Handle:weapon = TF2Items_CreateItem(OVERRIDE_ALL);
+ Handle weapon = TF2Items_CreateItem(OVERRIDE_ALL|PRESERVE_ATTRIBUTES);
- new String:defIndex[7];
+ char defIndex[7];
IntToString(g_Replacements[client][i], defIndex, sizeof(defIndex));
- new String:replacement[140];
- if (!GetTrieString(g_hWeaponReplacements, defIndex, replacement, sizeof(replacement)))
+ char replacement[140];
+ if (!g_hWeaponReplacements.GetString(defIndex, replacement, sizeof(replacement)))
{
continue;
}
- new String:pieces[5][128];
+ char pieces[5][128];
ExplodeString(replacement, ":", pieces, sizeof(pieces), sizeof(pieces[]));
@@ -3799,127 +3462,117 @@ public Event_post_inventory_application(Handle:event, const String:name[], bool:
TrimString(pieces[Item_Level]);
TrimString(pieces[Item_Attributes]);
- new index = StringToInt(pieces[Item_Index]);
- new quality = StringToInt(pieces[Item_Quality]);
- new level = StringToInt(pieces[Item_Level]);
+ int index = StringToInt(pieces[Item_Index]);
+ int quality = StringToInt(pieces[Item_Quality]);
+ int level = StringToInt(pieces[Item_Level]);
TF2Items_SetClassname(weapon, pieces[Item_Classname]);
TF2Items_SetItemIndex(weapon, index);
TF2Items_SetQuality(weapon, quality);
TF2Items_SetLevel(weapon, level);
- new attribCount = 0;
+ int attribCount = 0;
if (strlen(pieces[Item_Attributes]) > 0)
{
- new String:newAttribs[32][6];
- new count = ExplodeString(pieces[Item_Attributes], ";", newAttribs, sizeof(newAttribs), sizeof(newAttribs[]));
+ char newAttribs[32][6];
+ int count = ExplodeString(pieces[Item_Attributes], ";", newAttribs, sizeof(newAttribs), sizeof(newAttribs[]));
if (count % 2 > 0)
{
LogError("Error parsing replacement attributes for item definition index %d", g_Replacements[client][i]);
return;
}
- for (new j = 0; j < count && attribCount < 16; j += 2)
+ for (int j = 0; j < count && attribCount < 16; j += 2)
{
- new attrib = StringToInt(newAttribs[i]);
- new Float:value = StringToFloat(newAttribs[i+1]);
+ TrimString(newAttribs[i]);
+ TrimString(newAttribs[i+1]);
+ int attrib = StringToInt(newAttribs[i]);
+ float value = StringToFloat(newAttribs[i+1]);
TF2Items_SetAttribute(weapon, attribCount++, attrib, value);
}
}
TF2Items_SetNumAttributes(weapon, attribCount);
- new item = TF2Items_GiveNamedItem(client, weapon);
+ int item = TF2Items_GiveNamedItem(client, weapon);
EquipPlayerWeapon(client, item);
g_Replacements[client][i] = -1;
- CloseHandle(weapon);
+ delete weapon;
}
g_ReplacementCount[client] = 0;
- }
-
- TF2Attrib_ChangeBoolAttrib(client, "cancel falling damage", GetConVarBool(g_PHPreventFallDamage));
-}
-
-TF2Attrib_ChangeBoolAttrib(entity, String:attribute[], bool:value)
-{
- if (!g_TF2Attribs)
- {
- return;
- }
-
- if (value)
- {
- TF2Attrib_SetByName(entity, attribute, 1.0);
- }
- else if (TF2Attrib_GetByName(entity, attribute) != Address_Null)
- {
- TF2Attrib_RemoveByName(entity, attribute);
+ // Now that we're adjusting weapons, this needs to happen to fix max ammo counts
+ //TF2_RegeneratePlayer(client);
}
}
-public Event_teamplay_round_start(Handle:event, const String:name[], bool:dontBroadcast)
+public Action Event_teamplay_round_start_pre(Event event, const char[] name, bool dontBroadcast)
{
- StopTimers();
-
switch (g_RoundChange)
{
case RoundChange_Enable:
{
g_Enabled = true;
SetCVars();
-// Moved to after g_Enabled check
-//#if defined DHOOKS
-// RegisterDHooks();
-//#endif
UpdateGameDescription();
g_RoundChange = RoundChange_NoChange;
g_RoundCurrent = 0;
+
+ Internal_AddServerTag();
}
case RoundChange_Disable:
{
g_Enabled = false;
ResetCVars();
-#if defined DHOOKS
- UnregisterDHooks();
-#endif
UpdateGameDescription();
g_RoundChange = RoundChange_NoChange;
+
+ Internal_RemoveServerTag();
}
}
+ return Plugin_Continue;
+}
+
+public void Event_teamplay_round_start(Event event, const char[] name, bool dontBroadcast)
+{
+ if (HasSwitchedTeams())
+ {
+ SwitchTeamScoresClassic();
+ }
+
+ StopTimers();
+
if (!g_Enabled)
return;
- // This is being called because TF2 sometimes destroys the gamerules object when there are 0-1 players at round end.
- // This has the side effect of unsetting our hooks. Luckily, we have checks in place to prevent double hooking.
-#if defined DHOOKS
- RegisterDHooks();
-#endif
+ CheckRoundTimer();
g_inPreRound = true;
+ g_PlayerDied = false;
+ g_flRoundStart = 0.0;
// This is now in round start after an issue was reported with last prop not resetting in 3.0.2
g_LastProp = false;
g_LastPropPlayer = 0;
g_RoundOver = true;
- for (new client = 1; client <= MaxClients; client++)
+ for (int client = 1; client <= MaxClients; client++)
{
ResetPlayer(client);
if (IsClientInGame(client) && !IsFakeClient(client))
{
// For some reason, this has to be set every round or the player GUI messes up
- SendConVarValue(client, g_hArenaRoundTime, "600");
+ g_hArenaRoundTime.ReplicateToClient(client, "600");
}
}
// Delay freeze by a frame
RequestFrame(Delay_teamplay_round_start);
// Arena maps should have a team_control_point_master already, but just in case...
- new ent = FindEntityByClassname(-1, "team_control_point_master");
+ int ent = FindEntityByClassname(-1, "team_control_point_master");
if (ent == -1)
{
ent = CreateEntityByName("team_control_point_master");
@@ -3929,27 +3582,28 @@ public Event_teamplay_round_start(Handle:event, const String:name[], bool:dontBr
}
//GameMode Explanation
- decl String:message[256];
- ent = FindEntityByClassname(-1, "tf_gamerules"); // Can't use sdktools_gamerules for this
+ char message[256];
+ // Not sure this has to be done every round, but we'll keep it here just in case.
+
//BLU
Format(message, sizeof(message), "%T", "#TF_PH_BluHelp", LANG_SERVER);
SetVariantString(message);
- AcceptEntityInput(ent, "SetBlueTeamGoalString");
+ AcceptEntityInput(g_GameRulesProxy, "SetBlueTeamGoalString");
SetVariantString("2");
- AcceptEntityInput(ent, "SetBlueTeamRole");
+ AcceptEntityInput(g_GameRulesProxy, "SetBlueTeamRole");
//RED
Format(message, sizeof(message), "%T", "#TF_PH_RedHelp", LANG_SERVER);
SetVariantString(message);
- AcceptEntityInput(ent, "SetRedTeamGoalString");
+ AcceptEntityInput(g_GameRulesProxy, "SetRedTeamGoalString");
SetVariantString("1");
- AcceptEntityInput(ent, "SetRedTeamRole");
+ AcceptEntityInput(g_GameRulesProxy, "SetRedTeamRole");
}
-public Delay_teamplay_round_start(any:data)
+public void Delay_teamplay_round_start(any data)
{
- for (new i = 1; i <= MaxClients; i++)
+ for (int i = 1; i <= MaxClients; i++)
{
if (IsClientInGame(i) && !IsClientObserver(i))
{
@@ -3958,12 +3612,12 @@ public Delay_teamplay_round_start(any:data)
}
}
-public Event_teamplay_restart_round(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_teamplay_restart_round(Event event, const char[] name, bool dontBroadcast)
{
// I'm not sure what needs to be here... does teamplay_round_start get called on round restart?
}
-public Event_arena_round_start(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_arena_round_start(Event event, const char[] name, bool dontBroadcast)
{
#if defined LOG
LogMessage("[PH] round start - %i", g_RoundOver );
@@ -3973,6 +3627,8 @@ public Event_arena_round_start(Handle:event, const String:name[], bool:dontBroad
if (!g_Enabled)
return;
+ g_inSetup = true;
+
g_RoundCurrent++;
StartTimers(true);
@@ -3982,19 +3638,14 @@ public Event_arena_round_start(Handle:event, const String:name[], bool:dontBroad
// bl4nk mentions arena_round_start, but I think he meant teamplay_round_start
RequestFrame(Delay_arena_round_start);
- //SetupRoundTime(g_RoundTime);
-
CreateTimer(0.1, Timer_Info);
-#if defined STATS || defined LOCALSTATS
- g_StartTime = GetTime();
-#endif
}
}
-public Delay_arena_round_start(any:data)
+public void Delay_arena_round_start(any data)
{
- for(new client=1; client <= MaxClients; client++)
+ for(int client=1; client <= MaxClients; client++)
{
if(IsClientInGame(client) && IsPlayerAlive(client))
{
@@ -4016,7 +3667,7 @@ public Delay_arena_round_start(any:data)
}
}
-stock bool:IsValidClient(client, bool:replaycheck = true)
+stock bool IsValidClient(int client, bool replaycheck = true)
{
if (client <= 0 || client > MaxClients) return false;
if (!IsClientInGame(client)) return false;
@@ -4028,22 +3679,15 @@ stock bool:IsValidClient(client, bool:replaycheck = true)
return true;
}
-public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
+public void Event_player_spawn(Event event, const char[] name, bool dontBroadcast)
{
if (!g_Enabled)
return;
- new red = TEAM_PROP - 2;
- new blue = TEAM_HUNTER - 2;
- new client = GetClientOfUserId(GetEventInt(event, "userid"));
+ int red = TEAM_PROP - 2;
+ int blue = TEAM_HUNTER - 2;
+ int client = GetClientOfUserId(event.GetInt("userid"));
- // Lets remove this since we wipe their model later anyway
- /*
- if (IsValidClient(client)){
- RemoveAnimeModel(client);
- }
- */
-
g_currentSpeed[client] = g_classSpeeds[TF2_GetPlayerClass(client)][0]; // Reset to default speed.
if(IsClientInGame(client) && IsPlayerAlive(client))
@@ -4052,12 +3696,13 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
if(!g_RoundOver && !g_AllowedSpawn[client])
{
#if defined LOG
- LogMessage("%N spawned outside of a round");
+ LogMessage("[PH] %N spawned outside of a round");
#endif
ForcePlayerSuicide(client);
return;
}
- RemoveAnimeModel(client);
+ // Wipe their model in case they were a prop last round and lived
+ RemovePropModel(client);
SDKHook(client, SDKHook_OnTakeDamage, TakeDamageHook);
SDKHook(client, SDKHook_PreThink, PreThinkHook);
#if defined LOG
@@ -4065,7 +3710,7 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
#endif
g_Hit[client] = false;
- new team = GetClientTeam(client);
+ int team = GetClientTeam(client);
if(team == TEAM_HUNTER)
{
if (!g_RoundStartMessageSent[client])
@@ -4075,24 +3720,35 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
}
#if defined SHINX
- new TFClassType:clientClass = TF2_GetPlayerClass(client);
+ TFClassType clientClass = TF2_GetPlayerClass(client);
if (g_classLimits[blue][clientClass] != -1 && GetClassCount(clientClass, TEAM_HUNTER) > g_classLimits[blue][clientClass])
{
if(g_classLimits[blue][clientClass] == 0)
{
+ // By default, this fires when they're Scout
//CPrintToChat(client, "%t", "#TF_PH_ClassBlocked");
}
else
{
CPrintToChat(client, "%t", "#TF_PH_ClassFull");
}
+
+ if (g_PreferredHunterClass[client] != TFClass_Unknown && g_PreferredHunterClass[client] != clientClass)
+ {
+ TF2_SetPlayerClass(client, g_PreferredHunterClass[client]);
+ }
+ else
+ {
+ TF2_SetPlayerClass(client, g_defaultClass[blue]);
+ }
- TF2_SetPlayerClass(client, g_defaultClass[blue]);
TF2_RespawnPlayer(client);
return;
}
+ // If we didn't just change their class, store it for later
+ g_PreferredHunterClass[client] = clientClass;
#else
if(TF2_GetPlayerClass(client) != g_defaultClass[blue])
{
@@ -4117,7 +3773,7 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
if(TF2_GetPlayerClass(client) != g_defaultClass[red])
{
- TF2_SetPlayerClass(client, TFClassType:g_defaultClass[red]);
+ TF2_SetPlayerClass(client, view_as(g_defaultClass[red]), false, false);
TF2_RespawnPlayer(client);
return; // This was missing prior to PHR 3.3.4
}
@@ -4128,7 +3784,7 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
{
// Players are spawning on a non-player team?
#if defined LOG
- LogMessage("%N spawned on a non-player team: %d, slaying...", client, team);
+ LogMessage("[PH] %N spawned on a non-player team: %d, slaying...", client, team);
#endif
ForcePlayerSuicide(client);
}
@@ -4140,11 +3796,17 @@ public Event_player_spawn(Handle:event, const String:name[], bool:dontBroadcast)
}
}
-public Action:Event_player_death(Handle:event, const String:name[], bool:dontBroadcast)
+public Action Event_player_death(Event event, const char[] name, bool dontBroadcast)
{
if (!g_Enabled || g_inPreRound)
return Plugin_Continue;
+ if (event.GetInt("death_flags") & TF_DEATHFLAG_DEADRINGER)
+ {
+ // This shouldn't fire since Spy is usually disabled, but just in case...
+ return Plugin_Continue;
+ }
+
// This should be a separate event now, but we're leaving this in just in case
if (GetEventInt(event, "weaponid") == TF_WEAPON_BAT_FISH && GetEventInt(event, "customkill") == TF_CUSTOM_FISH_KILL)
{
@@ -4152,7 +3814,10 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
}
//new bool:changed = false;
- new client = GetClientOfUserId(GetEventInt(event, "userid"));
+ int client = GetClientOfUserId(event.GetInt("userid"));
+ if (client == 0)
+ return Plugin_Continue;
+
g_CurrentlyFlaming[client] = false;
g_FlameCount[client] = 0;
@@ -4161,7 +3826,7 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
#if defined LOG
LogMessage("[PH] Player death %N", client);
#endif
- //RemoveAnimeModel(client);
+ //RemovePropModel(client);
CreateTimer(0.1, Timer_Ragdoll, GetClientUserId(client));
@@ -4169,26 +3834,23 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
SDKUnhook(client, SDKHook_PreThink, PreThinkHook);
}
- new attacker = GetClientOfUserId(GetEventInt(event, "attacker"));
- new assister = GetClientOfUserId(GetEventInt(event, "assister"));
+ if(g_inSetup && g_PHRespawnDuringSetup.BoolValue)
+ {
+ CreateTimer(0.1, Timer_Respawn, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
+ return Plugin_Continue;
+ }
+
+ int attacker = GetClientOfUserId(event.GetInt("attacker"));
+ int assister = GetClientOfUserId(event.GetInt("assister"));
-#if defined STATS || defined LOCALSTATS
- decl String:weapon[64];
- new attackerID = GetEventInt(event, "attacker");
- new assisterID = GetEventInt(event, "assister");
- new clientID = GetEventInt(event, "userid");
- new weaponid = GetEventInt(event, "weaponid");
- GetEventString(event, "weapon", weapon, sizeof(weapon));
-#endif
-
if(!g_RoundOver)
g_Spawned[client] = false;
g_Hit[client] = false;
// I would move this, but I'm not sure if it's used by STATS
- new playas = 0;
- for(new i=1; i <= MaxClients; i++)
+ int playas = 0;
+ for(int i=1; i <= MaxClients; i++)
{
if(IsClientInGame(i) /*&& !IsFakeClient(i)*/ && IsPlayerAlive(i) && GetClientTeam(i) == TEAM_PROP)
{
@@ -4205,6 +3867,7 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
// This is to kill the particle effects from the Harvest Ghost prop and the like
// Moved to RED-only section so we don't kill unusual effects.
+ // Reference: https://github.com/ValveSoftware/source-sdk-2013/blob/master/sp/src/game/shared/particle_parse.cpp#L482
SetVariantString("ParticleEffectStop");
AcceptEntityInput(client, "DispatchEffect");
}
@@ -4213,13 +3876,6 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
{
if(client > 0 && attacker > 0 && IsClientInGame(client) && IsClientInGame(attacker) && client != attacker)
{
-#if defined STATS
- PlayerKilled(clientID, attackerID, assisterID, weaponid, weapon);
-#endif
-
-#if defined LOCALSTATS
- LocalStats_PlayerKilled(clientID, attackerID, assisterID, weaponid, weapon);
-#endif
if(IsPlayerAlive(attacker))
{
Speedup(attacker);
@@ -4236,34 +3892,56 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
}
}
- }
+
+ // We now manually handle First Blood and avoid having the crit effect.
+ // We also adjust the various sound times to be more in line with PropHunt.
+ if (!g_PlayerDied)
+ {
+ // Fast is 0-30, regular is 31-90, finally is 91+
+ int expendedTime = RoundToNearest(GetGameTime() - g_flRoundStart);
+ if (expendedTime <= 30)
+ {
+ PH_EmitSoundToAll("FirstBloodFast");
+ }
+ else if (expendedTime <= 90)
+ {
+ PH_EmitSoundToAll("FirstBlood");
+ }
+ else
+ {
+ PH_EmitSoundToAll("FirstBloodFinally");
+ }
+
+ g_PlayerDied = true;
+ }
- if(!g_LastProp && playas == 2 && !g_RoundOver && GetClientTeam(client) == TEAM_PROP)
- {
- g_LastProp = true;
- PH_EmitSoundToAll("OneAndOnly", _, _, SNDLEVEL_AIRCRAFT);
-#if defined SCATTERGUN
- for(new client2=1; client2 <= MaxClients; client2++)
+ if(!g_LastProp && playas == 2 && GetClientTeam(client) == TEAM_PROP)
{
- if(IsClientInGame(client2) && !IsFakeClient(client2) && IsPlayerAlive(client2))
+ g_LastProp = true;
+ PH_EmitSoundToAll("OneAndOnly", _, _, SNDLEVEL_AIRCRAFT);
+ #if defined SCATTERGUN
+ for(int client2=1; client2 <= MaxClients; client2++)
{
- if(GetClientTeam(client2) == TEAM_PROP)
+ if(IsClientInGame(client2) && !IsFakeClient(client2) && IsPlayerAlive(client2))
{
- g_LastPropPlayer = client2;
- TF2_RegeneratePlayer(client2);
+ if(GetClientTeam(client2) == TEAM_PROP)
+ {
+ g_LastPropPlayer = client2;
+ TF2_RegeneratePlayer(client2);
// Replaced by TF2Items_OnGiveNamedItem_Post
//CreateTimer(0.1, Timer_WeaponAlpha, GetClientUserId(client2));
- }
- else
- if(GetClientTeam(client2) == TEAM_HUNTER)
- {
- TF2_AddCondition(client2, TFCond_Jarated, 15.0);
+ }
+ else
+ if(GetClientTeam(client2) == TEAM_HUNTER)
+ {
+ TF2_AddCondition(client2, TFCond_Jarated, 15.0);
+ }
}
}
+ #endif
}
-#endif
}
-
+
return Plugin_Continue;
}
@@ -4271,34 +3949,43 @@ public Action:Event_player_death(Handle:event, const String:name[], bool:dontBro
/////////////////// TIMERS ////////////////////////
////////////////////////////////////////////////////
-/*
-public Action:Timer_WeaponAlpha(Handle:timer, any:userid)
+public Action Timer_Respawn(Handle timer, any userid)
{
- new client = GetClientOfUserId(userid);
+ int client = GetClientOfUserId(userid);
+ if (client > 0)
+ {
+ g_AllowedSpawn[client] = true;
+ TF2_RespawnPlayer(client);
+ }
+}
+
+public Action Timer_WeaponAlpha(Handle timer, any userid)
+{
+ int client = GetClientOfUserId(userid);
if(client > 0 && IsClientInGame(client) && IsPlayerAlive(client))
SetWeaponsAlpha(client, 0);
}
-*/
-public Action:Timer_Info(Handle:timer)
+// This is used for the fading in of the game mode name and stuff
+public Action Timer_Info(Handle timer)
{
g_Message_bit++;
if(g_Message_bit == 2)
{
SetHudTextParamsEx(-1.0, 0.22, 5.0, {0,204,255,255}, {0,0,0,255}, 2, 1.0, 0.05, 0.5);
- for(new i=1; i <= MaxClients; i++)
+ for(int i=1; i <= MaxClients; i++)
{
if(IsClientInGame(i) && !IsFakeClient(i))
{
- ShowSyncHudText(i, g_Text1, "PropHunt %s", g_Version);
+ ShowSyncHudText(i, g_Text1, "PropHunt %s", PL_VERSION);
}
}
}
else if(g_Message_bit == 3)
{
SetHudTextParamsEx(-1.0, 0.25, 4.0, {255,128,0,255}, {0,0,0,255}, 2, 1.0, 0.05, 0.5);
- for(new i=1; i <= MaxClients; i++)
+ for(int i=1; i <= MaxClients; i++)
{
if(IsClientInGame(i) && !IsFakeClient(i))
{
@@ -4309,7 +3996,7 @@ public Action:Timer_Info(Handle:timer)
else if(g_Message_bit == 4 && strlen(g_AdText) > 0)
{
SetHudTextParamsEx(-1.0, 0.3, 3.0, {0,220,0,255}, {0,0,0,255}, 2, 1.0, 0.05, 0.5);
- for(new i=1; i <= MaxClients; i++)
+ for(int i=1; i <= MaxClients; i++)
{
if(IsClientInGame(i) && !IsFakeClient(i))
{
@@ -4327,9 +4014,9 @@ public Action:Timer_Info(Handle:timer)
}
//public Action:Timer_DoEquipBlu(Handle:timer, any:UserId)
-public DoEquipHunter(any:UserId)
+public void DoEquipHunter(any UserId)
{
- new client = GetClientOfUserId(UserId);
+ int client = GetClientOfUserId(UserId);
if(client > 0 && IsClientInGame(client) && IsPlayerAlive(client))
{
if(g_inPreRound)
@@ -4340,11 +4027,11 @@ public DoEquipHunter(any:UserId)
SwitchView(client, false, true);
SetAlpha(client, 255);
- new validWeapons;
+ int validWeapons;
- for (new i = 0; i < 3; i++)
+ for (int i = 0; i < 3; i++)
{
- new playerItemSlot = GetPlayerWeaponSlot(client, i);
+ int playerItemSlot = GetPlayerWeaponSlot(client, i);
if(playerItemSlot > MaxClients && IsValidEntity(playerItemSlot))
{
@@ -4362,9 +4049,9 @@ public DoEquipHunter(any:UserId)
}
//public Action:Timer_DoEquip(Handle:timer, any:UserId)
-public DoEquipProp(any:UserId)
+public void DoEquipProp(any UserId)
{
- new client = GetClientOfUserId(UserId);
+ int client = GetClientOfUserId(UserId);
if(client > 0 && IsClientInGame(client) && IsPlayerAlive(client))
{
//TF2_RegeneratePlayer(client);
@@ -4372,47 +4059,38 @@ public DoEquipProp(any:UserId)
#if defined LOG
LogMessage("[PH] do equip %N", client);
#endif
- // Lets comment this out since we don't block RED weapons with TF2Items
- // slot commands fix "remember last weapon" glitch, despite their client console spam
- /*
- FakeClientCommand(client, "slot0");
- FakeClientCommand(client, "slot3");
- TF2_RemoveAllWeapons(client);
- FakeClientCommand(client, "slot3");
- FakeClientCommand(client, "slot0");
- */
- decl String:pname[32];
+ char pname[32];
Format(pname, sizeof(pname), "ph_player_%i", client);
DispatchKeyValue(client, "targetname", pname);
#if defined LOG
LogMessage("[PH] do equip_2 %N", client);
#endif
- new propData[PropData];
+ int propData[PropData];
// fire in a nice random model
- decl String:model[PLATFORM_MAX_PATH];
- new String:offset[32] = "0 0 0";
- new String:rotation[32] = "0 0 0";
- new skin = 0;
- new modelIndex = -1;
+ char model[PLATFORM_MAX_PATH];
+ char offset[32] = "0 0 0";
+ char rotation[32] = "0 0 0";
+ int skin = 0;
+ int modelIndex = -1;
if(strlen(g_PlayerModel[client]) > 1)
{
model = g_PlayerModel[client];
- modelIndex = FindStringInArray(g_ModelName, model);
+ modelIndex = g_ModelName.FindString(model);
#if defined LOG
- LogMessage("Change user model to %s", model);
+ LogMessage("[PH] Change \"%N\"'s user model to %s", client, model);
#endif
}
else
{
- modelIndex = GetRandomInt(0, GetArraySize(g_ModelName)-1);
- GetArrayString(g_ModelName, modelIndex, model, sizeof(model));
+ modelIndex = GetRandomInt(0, g_ModelName.Length-1);
+ g_ModelName.GetString(modelIndex, model, sizeof(model));
}
// This wackiness with [0] is required when dealing with enums containing strings
- if (GetTrieArray(g_PropData, model, propData[0], sizeof(propData)))
+ if (g_PropData.GetArray(model, propData[0], sizeof(propData)))
{
strcopy(offset, sizeof(offset), propData[PropData_Offset]);
strcopy(rotation, sizeof(rotation), propData[PropData_Rotation]);
@@ -4420,7 +4098,7 @@ public DoEquipProp(any:UserId)
if (!g_RoundStartMessageSent[client])
{
- new String:modelName[MAXMODELNAME];
+ char modelName[MAXMODELNAME];
GetModelNameForClient(client, model, modelName, sizeof(modelName));
CPrintToChat(client, "%t", "#TF_PH_NowDisguised", modelName);
g_RoundStartMessageSent[client] = true;
@@ -4428,10 +4106,10 @@ public DoEquipProp(any:UserId)
if (modelIndex > -1)
{
- new String:tempOffset[32];
- new String:tempRotation[32];
- GetArrayString(g_ModelOffset, modelIndex, tempOffset, sizeof(tempOffset));
- GetArrayString(g_ModelRotation, modelIndex, tempRotation, sizeof(tempRotation));
+ char tempOffset[32];
+ char tempRotation[32];
+ g_ModelOffset.GetString(modelIndex, tempOffset, sizeof(tempOffset));
+ g_ModelRotation.GetString(modelIndex, tempRotation, sizeof(tempRotation));
TrimString(tempOffset);
TrimString(tempRotation);
// We don't want to override the default value unless it's set to something other than "0 0 0"
@@ -4443,7 +4121,7 @@ public DoEquipProp(any:UserId)
{
strcopy(rotation, sizeof(rotation), tempRotation);
}
- skin = GetArrayCell(g_ModelSkin, modelIndex);
+ skin = g_ModelSkin.Get(modelIndex);
}
#if defined LOG
@@ -4491,9 +4169,9 @@ public DoEquipProp(any:UserId)
}
}
-public Action:Timer_Locked(Handle:timer, any:entity)
+public Action Timer_Locked(Handle timer, any entity)
{
- for(new client=1; client <= MaxClients; client++)
+ for(int client=1; client <= MaxClients; client++)
{
if(g_RotLocked[client] && IsClientInGame(client) && !IsFakeClient(client) && IsPlayerAlive(client) && GetClientTeam(client) == TEAM_PROP)
{
@@ -4503,22 +4181,22 @@ public Action:Timer_Locked(Handle:timer, any:entity)
}
}
-public Action:Timer_AntiHack(Handle:timer, any:entity)
+public Action Timer_AntiHack(Handle timer, any entity)
{
- new red = TEAM_PROP - 2;
+ int red = TEAM_PROP - 2;
if(!g_RoundOver)
{
- decl String:name[MAX_NAME_LENGTH];
- for(new client=1; client <= MaxClients; client++)
+ char name[MAX_NAME_LENGTH];
+ for(int client=1; client <= MaxClients; client++)
{
if(IsClientInGame(client) && IsPlayerAlive(client))
{
- if (GetConVarBool(g_PHStaticPropInfo) && !IsFakeClient(client))
+ if (g_PHStaticPropInfo.BoolValue && !IsFakeClient(client))
{
QueryClientConVar(client, "r_staticpropinfo", QueryStaticProp);
}
- if(!g_LastProp && GetConVarBool(g_PHAntiHack) && GetClientTeam(client) == TEAM_PROP && TF2_GetPlayerClass(client) == g_defaultClass[red])
+ if(!g_LastProp && g_PHAntiHack.BoolValue && GetClientTeam(client) == TEAM_PROP && TF2_GetPlayerClass(client) == g_defaultClass[red])
{
if(GetPlayerWeaponSlot(client, 1) != -1 || GetPlayerWeaponSlot(client, 0) != -1 || GetPlayerWeaponSlot(client, 2) != -1)
{
@@ -4540,22 +4218,25 @@ public Action:Timer_AntiHack(Handle:timer, any:entity)
// Fix a prop player who still had weapons
// One frame shouldn't be enough to change players, but just in case...
-public FixPropPlayer(any:userid)
+public void FixPropPlayer(any userid)
{
- new client = GetClientOfUserId(userid);
- if (client < 1 || GetClientTeam(client) != TEAM_PROP)
+ int client = GetClientOfUserId(userid);
+ if (client < 1 || GetClientTeam(client) != TEAM_PROP || g_LastProp)
return;
-
- TF2_RegeneratePlayer(client);
+
+ //TF2_RegeneratePlayer(client);
+
+ TF2_RemoveAllWeapons(client);
+
//Timer_DoEquip(INVALID_HANDLE, userid);
RequestFrame(DoEquipProp, userid);
}
-public QueryStaticProp(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[])
+public void QueryStaticProp(QueryCookie cookie, int client, ConVarQueryResult result, const char[] cvarName, const char[] cvarValue)
{
if (result == ConVarQuery_Okay)
{
- new value = StringToInt(cvarValue);
+ int value = StringToInt(cvarValue);
if (value == 0)
{
return;
@@ -4566,56 +4247,44 @@ public QueryStaticProp(QueryCookie:cookie, client, ConVarQueryResult:result, con
KickClient(client, "r_staticpropinfo detection was blocked");
}
-public Action:Timer_Ragdoll(Handle:timer, any:userid)
+public Action Timer_Ragdoll(Handle timer, any userid)
{
- new client = GetClientOfUserId(userid);
+ int client = GetClientOfUserId(userid);
if (client < 1)
return Plugin_Handled;
- new rag = GetEntPropEnt(client, Prop_Send, "m_hRagdoll");
+ int rag = GetEntPropEnt(client, Prop_Send, "m_hRagdoll");
if(rag > MaxClients && IsValidEntity(rag))
AcceptEntityInput(rag, "Kill");
- RemoveAnimeModel(client);
+ RemovePropModel(client);
return Plugin_Handled;
}
-public Action:Timer_Score(Handle:timer)
+public Action Timer_Score(Handle timer)
{
- for(new client=1; client <= MaxClients; client++)
+ for(int client=1; client <= MaxClients; client++)
{
-#if defined STATS || defined LOCALSTATS
- if(IsClientInGame(client) && IsPlayerAlive(client) && GetClientTeam(client) == TEAM_PROP)
- {
- #if defined STATS
- AlterScore(client, 2, ScReason_Time, 0);
- #endif
-
- #if defined LOCALSTATS
- LocalStats_AlterScore(client, 2, ScReason_Time, 0);
- #endif
- }
-#endif
g_TouchingCP[client] = false;
}
CPrintToChatAll("%t", "#TF_PH_CPBonusRefreshed");
}
-public OnSetupStart(const String:output[], caller, activator, Float:delay)
+public void OnSetupStart(const char[] output, int caller, int activator, float delay)
{
- new Handle:event = CreateEvent("teamplay_update_timer");
- if (event != INVALID_HANDLE)
- FireEvent(event);
+ g_inSetup = true;
+ Event event = CreateEvent("teamplay_update_timer");
+ if (event != null)
+ event.Fire();
}
// This used to hook the teamplay_setup_finished event, but ph_kakariko messes with that
-//public Action:Event_teamplay_setup_finished(Handle:event, const String:name[], bool:dontBroadcast)
-public OnSetupFinished(const String:output[], caller, activator, Float:delay)
+public void OnSetupFinished(const char[] output, int caller, int activator, float delay)
{
- if (g_hScore != INVALID_HANDLE)
+ if (g_hScore != null)
{
- CloseHandle(g_hScore);
+ delete g_hScore;
}
g_hScore = CreateTimer(55.0, Timer_Score, _, TIMER_REPEAT);
TriggerTimer(g_hScore);
@@ -4624,8 +4293,10 @@ public OnSetupFinished(const String:output[], caller, activator, Float:delay)
LogMessage("[PH] Setup_Finish");
#endif
g_RoundOver = false;
+ g_inSetup = false;
+ g_flRoundStart = GetGameTime();
- for(new client2=1; client2 <= MaxClients; client2++)
+ for(int client2=1; client2 <= MaxClients; client2++)
{
if(IsClientInGame(client2) && IsPlayerAlive(client2))
{
@@ -4635,7 +4306,7 @@ public OnSetupFinished(const String:output[], caller, activator, Float:delay)
CPrintToChatAll("%t", "#TF_PH_PyrosReleased");
PH_EmitSoundToAll("RoundStart", _, _, SNDLEVEL_AIRCRAFT);
- new ent;
+ int ent;
if(g_Doors)
{
while ((ent = FindEntityByClassname(ent, "func_door")) != -1)
@@ -4646,7 +4317,7 @@ public OnSetupFinished(const String:output[], caller, activator, Float:delay)
if(g_Relay)
{
- decl String:relayName[128];
+ char relayName[128];
while ((ent = FindEntityByClassname(ent, "logic_relay")) != -1)
{
GetEntPropString(ent, Prop_Data, "m_iName", relayName, sizeof(relayName));
@@ -4660,13 +4331,12 @@ public OnSetupFinished(const String:output[], caller, activator, Float:delay)
}
#if defined CHARGE
-public Action:Timer_Charge(Handle:timer, any:userid)
+public Action Timer_Charge(Handle timer, any userid)
{
- new client = GetClientOfUserId(userid);
- new red = TEAM_PROP-2;
+ int client = GetClientOfUserId(userid);
+ int red = TEAM_PROP-2;
if(client > 0 && IsPlayerAlive(client))
{
- //SetEntData(client, g_offsCollisionGroup, COLLISION_GROUP_PLAYER, _, true);
SetEntProp(client, Prop_Send, "m_CollisionGroup", COLLISION_GROUP_PLAYER);
TF2_SetPlayerClass(client, g_defaultClass[red], false);
}
@@ -4674,65 +4344,23 @@ public Action:Timer_Charge(Handle:timer, any:userid)
}
#endif
-/*
-public TimeEnd(const String:output[], caller, activator, Float:delay)
-{
-#if defined LOG
- LogMessage("[PH] Time Up");
-#endif
- if(!g_RoundOver)
- {
- ForceTeamWin(TEAM_PROP);
- g_RoundOver = true;
- g_inPreRound = true;
- }
-}
-*/
-
-/*
-public Action:Timer_TimeUp(Handle:timer, any:lol)
-{
-#if defined LOG
- LogMessage("[PH] Time Up");
-#endif
- if(!g_RoundOver)
- {
- ForceTeamWin(TEAM_PROP);
- g_RoundOver = true;
- g_inPreRound = true;
- }
- g_RoundTimer = INVALID_HANDLE;
- return Plugin_Handled;
-}
-*/
-
-/*
-public Action:Timer_AfterWinPanel(Handle:timer, any:lol)
-{
-#if defined LOG
- LogMessage("[PH] After Win Panel");
-#endif
- StopTimer(g_RoundTimer);
-}
-*/
-
-public Action:Timer_Unfreeze(Handle:timer, any:userid)
+public Action Timer_Unfreeze(Handle timer, any userid)
{
- new client = GetClientOfUserId(userid);
+ int client = GetClientOfUserId(userid);
if(client > 0 && IsPlayerAlive(client))
SetEntityMoveType(client, MOVETYPE_WALK);
return Plugin_Handled;
}
-public Action:Timer_Move(Handle:timer, any:userid)
+public Action Timer_Move(Handle timer, any userid)
{
- new client = GetClientOfUserId(userid);
+ int client = GetClientOfUserId(userid);
if (client > 0)
{
g_AllowedSpawn[client] = false;
if(IsPlayerAlive(client))
{
- new rag = GetEntPropEnt(client, Prop_Send, "m_hRagdoll");
+ int rag = GetEntPropEnt(client, Prop_Send, "m_hRagdoll");
if(IsValidEntity(rag))
AcceptEntityInput(rag, "Kill");
SetEntityMoveType(client, MOVETYPE_WALK);
@@ -4750,14 +4378,13 @@ public Action:Timer_Move(Handle:timer, any:userid)
return Plugin_Handled;
}
-public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefinitionIndex, &Handle:hItem)
+public Action TF2Items_OnGiveNamedItem(int client, char[] classname, int iItemDefinitionIndex, Handle &hItem)
{
// This section is to prevent Handle leaks
- static Handle:weapon = INVALID_HANDLE;
- if (weapon != INVALID_HANDLE)
+ static Handle weapon = null;
+ if (weapon != null)
{
- CloseHandle(weapon);
- weapon = INVALID_HANDLE;
+ delete weapon;
}
if (!g_Enabled)
@@ -4776,8 +4403,16 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
return Plugin_Stop;
}
- if (GetClientTeam(client) == TEAM_PROP)
+ int team = GetClientTeam(client);
+
+ if (team == TEAM_PROP)
{
+ // Taunt items have the classname "no_entity" now
+ if (g_PHAllowTaunts.BoolValue && StrEqual(classname, "no_entity", false))
+ {
+ return Plugin_Continue;
+ }
+
// If they're not the last prop, don't give them anything
if (!g_LastProp)
{
@@ -4792,26 +4427,30 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
{
return Plugin_Stop;
}
+
+ if (g_hPropWeaponRemovals.FindValue(iItemDefinitionIndex) >= 0)
+ {
+ return Plugin_Stop;
+ }
}
- new String:defIndex[7];
+ char defIndex[7];
IntToString(iItemDefinitionIndex, defIndex, sizeof(defIndex));
- new flags;
+ int flags;
- new String:replacement[140];
- new String:addAttributes[128];
- new bool:replace = GetTrieString(g_hWeaponReplacements, defIndex, replacement, sizeof(replacement));
- new bool:stripattribs = FindValueInArray(g_hWeaponStripAttribs , iItemDefinitionIndex) >= 0;
- new bool:addattribs = GetTrieString(g_hWeaponAddAttribs, defIndex, addAttributes, sizeof(addAttributes));
- new bool:removeAirblast = !GetConVarBool(g_PHAirblast) && StrEqual(classname, "tf_weapon_flamethrower");
-// new String:pieces[5][128];
+ char replacement[140];
+ char addAttributes[128];
+ bool replace = g_hWeaponReplacements.GetString(defIndex, replacement, sizeof(replacement));
+ bool stripattribs = g_hWeaponStripAttribs.FindValue(iItemDefinitionIndex) >= 0;
+ bool addattribs = g_hWeaponAddAttribs.GetString(defIndex, addAttributes, sizeof(addAttributes));
+ bool removeAirblast = !g_PHAirblast.BoolValue && StrEqual(classname, "tf_weapon_flamethrower");
if (replace)
{
- new classBits;
+ int classBits;
- if (!GetTrieValue(g_hWeaponReplacementPlayerClasses, defIndex, classBits))
+ if (!g_hWeaponReplacementPlayerClasses.GetValue(defIndex, classBits))
{
g_Replacements[client][g_ReplacementCount[client]++] = iItemDefinitionIndex;
return Plugin_Stop;
@@ -4819,7 +4458,7 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
else
{
// We subtract 1 here because we're left shifting a 1, so 1 is intrinsically added to the class.
- new class = _:TF2_GetPlayerClass(client) - 1;
+ int class = view_as(TF2_GetPlayerClass(client)) - 1;
if (classBits & (1 << class))
{
g_Replacements[client][g_ReplacementCount[client]++] = iItemDefinitionIndex;
@@ -4830,7 +4469,7 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
}
// If we're supposed to remove it, just block it here
- if (FindValueInArray(g_hWeaponRemovals, iItemDefinitionIndex) >= 0)
+ if (team == TEAM_HUNTER && g_hWeaponRemovals.FindValue(iItemDefinitionIndex) >= 0)
{
return Plugin_Stop;
}
@@ -4840,7 +4479,7 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
return Plugin_Continue;
}
- new bool:weaponChanged = false;
+ bool weaponChanged = false;
if (stripattribs)
{
@@ -4855,7 +4494,7 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
weapon = TF2Items_CreateItem(flags);
- new attribCount = 0;
+ int attribCount = 0;
// 594 is Phlogistinator and already has airblast disabled
if (removeAirblast && (iItemDefinitionIndex != WEP_PHLOGISTINATOR || stripattribs))
{
@@ -4863,58 +4502,23 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
weaponChanged = true;
}
- // This block isn't used as all weapon replacements are now done on spawn.
- /*
- if (replace)
- {
- TrimString(pieces[Item_Index]);
- TrimString(pieces[Item_Quality]);
- TrimString(pieces[Item_Level]);
- TrimString(pieces[Item_Attributes]);
-
- new index = StringToInt(pieces[Item_Index]);
- new quality = StringToInt(pieces[Item_Quality]);
- new level = StringToInt(pieces[Item_Level]);
- TF2Items_SetItemIndex(weapon, index);
- TF2Items_SetQuality(weapon, quality);
- TF2Items_SetLevel(weapon, level);
-
- if (strlen(pieces[Item_Attributes]) > 0)
- {
- new String:newAttribs[32][6];
- new count = ExplodeString(pieces[Item_Attributes], ";", newAttribs, sizeof(newAttribs), sizeof(newAttribs[]));
- if (count % 2 > 0)
- {
- LogError("Error parsing replacement attributes for item definition index %d", iItemDefinitionIndex);
- return Plugin_Continue;
- }
-
- for (new i = 0; i < count && attribCount < 16; i += 2)
- {
- new attrib = StringToInt(newAttribs[i]);
- new Float:value = StringToFloat(newAttribs[i+1]);
- TF2Items_SetAttribute(weapon, attribCount++, attrib, value);
- }
- }
- weaponChanged = true;
- }
- */
-
if (addattribs)
{
// Pawn is dumb and this "shadows" a preceding variable despite being at a different block level
- new String:newAttribs2[32][6];
- new count = ExplodeString(addAttributes, ";", newAttribs2, sizeof(newAttribs2), sizeof(newAttribs2[]));
+ char newAttribs2[32][6];
+ int count = ExplodeString(addAttributes, ";", newAttribs2, sizeof(newAttribs2), sizeof(newAttribs2[]));
if (count % 2 > 0)
{
LogError("Error parsing additional attributes for item definition index %d", iItemDefinitionIndex);
return Plugin_Continue;
}
- for (new i = 0; i < count && attribCount < 16; i += 2)
+ for (int i = 0; i < count && attribCount < 16; i += 2)
{
- new attrib = StringToInt(newAttribs2[i]);
- new Float:value = StringToFloat(newAttribs2[i+1]);
+ TrimString(newAttribs2[i]);
+ TrimString(newAttribs2[i+1]);
+ int attrib = StringToInt(newAttribs2[i]);
+ float value = StringToFloat(newAttribs2[i+1]);
TF2Items_SetAttribute(weapon, attribCount++, attrib, value);
}
}
@@ -4935,53 +4539,47 @@ public Action:TF2Items_OnGiveNamedItem(client, String:classname[], iItemDefiniti
}
#if defined OIMM
-public MultiMod_Status(bool:enabled)
+public void MultiMod_Status(bool enabled)
{
- SetConVarBool(g_PHEnable, enabled);
+ g_PHEnable.BoolValue = enabled
}
-public MultiMod_TranslateName(client, String:translation[], maxlength)
+public void MultiMod_TranslateName(int client, char[] translation, int maxlength)
{
Format(translation, maxlength, "%T", "#TF_PH_ModeName", client);
}
#endif
-public bool:ValidateMap(const String:map[])
+public bool ValidateMap(const char[] map)
{
- // As per SourceMod standard, anything dealing with map names should now be PLATFORM_MAX_PATH long
- new String:confil[PLATFORM_MAX_PATH], String:tidyname[2][PLATFORM_MAX_PATH], String:maptidyname[PLATFORM_MAX_PATH];
- ExplodeString(map, "_", tidyname, sizeof(tidyname), sizeof(tidyname[]));
- if (strncmp("workshop/", tidyname[0], 9))
- {
- ReplaceString(tidyname[0], sizeof(tidyname[]), "workshop/", "");
- }
- else if (strncmp("workshop\\", tidyname[0], 9))
- {
- ReplaceString(tidyname[0], sizeof(tidyname[]), "workshop\\", "");
- }
- // Because we only care about the first two pieces of the map name, we can ignore the ugc stuff
-
- Format(maptidyname, sizeof(maptidyname), "%s_%s", tidyname[0], tidyname[1]);
- BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/maps/%s.cfg", maptidyname);
-
- return FileExists(confil, true);
+ return FindConfigFileForMap(map);
}
// These functions are based on versions taken from CS:S/CS:GO Hide and Seek
-GetLanguageID(const String:langCode[])
+int GetLanguageID(const char[] langCode)
{
- return FindStringInArray(g_ModelLanguages, langCode);
+ return g_ModelLanguages.FindString(langCode);
}
-GetClientLanguageID(client, String:languageCode[]="", maxlen=0)
+int GetClientLanguageID(int client, char[] languageCode="", int maxlen=0)
{
- decl String:langCode[MAXLANGUAGECODE];
- GetLanguageInfo(GetClientLanguage(client), langCode, sizeof(langCode));
+ char langCode[MAXLANGUAGECODE];
+ int languageID;
+ if (client == LANG_SERVER)
+ {
+ languageID = GetServerLanguage();
+ }
+ else
+ {
+ languageID = GetClientLanguage(client);
+ }
+
+ GetLanguageInfo(languageID, langCode, sizeof(langCode));
#if defined LOG
- LogMessage("Client is using language code %s", langCode);
+ LogMessage("[PH] Client is using language code %s", langCode);
#endif
// is client's prefered language available?
- new langID = GetLanguageID(langCode);
+ int langID = GetLanguageID(langCode);
if(langID != -1)
{
strcopy(languageCode, maxlen, langCode);
@@ -4990,11 +4588,11 @@ GetClientLanguageID(client, String:languageCode[]="", maxlen=0)
else
{
#if defined LOG
- LogMessage("PH language code \"%s\" not found.", langCode);
+ LogMessage("[PH] Language code \"%s\" not found.", langCode);
#endif
GetLanguageInfo(GetServerLanguage(), langCode, sizeof(langCode));
#if defined LOG
- LogMessage("Falling back to server language code \"%s\".", langCode);
+ LogMessage("[PH] Falling back to server language code \"%s\".", langCode);
#endif
// is default server language available?
langID = GetLanguageID(langCode);
@@ -5020,12 +4618,12 @@ GetClientLanguageID(client, String:languageCode[]="", maxlen=0)
// english not found? happens on custom map configs e.g.
// use the first language available
// this should always work, since we would have SetFailState() on parse
- if(GetArraySize(g_ModelLanguages) > 0)
+ if(g_ModelLanguages.Length > 0)
{
#if defined LOG
LogMessage("PH language \"en\" not found, Falling back to lang 0.");
#endif
- GetArrayString(g_ModelLanguages, 0, languageCode, maxlen);
+ g_ModelLanguages.GetString(0, languageCode, maxlen);
return 0;
}
}
@@ -5034,18 +4632,18 @@ GetClientLanguageID(client, String:languageCode[]="", maxlen=0)
return -1;
}
-bool:GetModelNameForClient(client, const String:modelName[], String:name[], maxlen)
+bool GetModelNameForClient(int client, const char[] modelName, char[] name, int maxlen)
{
- if (GetConVarBool(g_PHMultilingual))
+ if (g_PHMultilingual.BoolValue)
{
- decl String:langCode[MAXLANGUAGECODE];
+ char langCode[MAXLANGUAGECODE];
GetClientLanguageID(client, langCode, sizeof(langCode));
#if defined LOG
LogMessage("[PH] Retrieving %s name for %s", langCode, modelName);
#endif
- new Handle:languageTrie;
- if (strlen(langCode) > 0 && GetTrieValue(g_PropNames, modelName, languageTrie) && languageTrie != INVALID_HANDLE && GetTrieString(languageTrie, langCode, name, maxlen))
+ StringMap languageTrie;
+ if (strlen(langCode) > 0 && g_PropNames.GetValue(modelName, languageTrie) && languageTrie != null && languageTrie.GetString(langCode, name, maxlen))
{
return true;
}
@@ -5057,9 +4655,9 @@ bool:GetModelNameForClient(client, const String:modelName[], String:name[], maxl
}
else
{
- new propData[PropData];
+ int propData[PropData];
- if (GetTrieArray(g_PropData, modelName, propData[0], sizeof(propData)))
+ if (g_PropData.GetArray(modelName, propData[0], sizeof(propData)))
{
strcopy(name, maxlen, propData[PropData_Name]);
return true;
@@ -5073,10 +4671,243 @@ bool:GetModelNameForClient(client, const String:modelName[], String:name[], maxl
}
// Fix for weapon alphas for last prop
-public TF2Items_OnGiveNamedItem_Post(client, String:classname[], itemDefinitionIndex, itemLevel, itemQuality, entityIndex)
+public int TF2Items_OnGiveNamedItem_Post(int client, char[] classname, int itemDefinitionIndex, int itemLevel, int itemQuality, int entityIndex)
{
if (g_LastProp && g_LastPropPlayer == client && IsValidEntity(entityIndex))
{
SetItemAlpha(entityIndex, 0);
}
}
+
+bool HasSwitchedTeams()
+{
+ return view_as(GameRules_GetProp("m_bSwitchedTeamsThisRound"));
+}
+
+void SetSwitchTeams(bool bSwitchTeams)
+{
+ // Note, this doesn't switch team scores... those are switched when SetWinner is called in the game
+ // Also, SetWinner will override our selection here, so this must be sent AFTER arena_win_panel fires
+ SDKCall(g_hSwitchTeams, bSwitchTeams);
+}
+
+// Manually switch the scores.
+// The game only does this if SetWinningTeam is called with bSwitchTeams set to true.
+// This is what DHooks did, but Arena confused it anyway and it only sometimes worked
+void SwitchTeamScoresClassic()
+{
+ int propScore = GetTeamScore(TEAM_PROP);
+ int hunterScore = GetTeamScore(TEAM_HUNTER);
+
+ if (propScore == 0 && hunterScore == 0)
+ {
+ return;
+ }
+
+#if defined LOG
+ LogMessage("[PH] Swapping team scores: Props: %d, Hunters: %d", propScore, hunterScore);
+#endif
+
+ SetTeamScore(TEAM_PROP, hunterScore);
+ SetTeamScore(TEAM_HUNTER, propScore);
+}
+
+bool FindConfigFileForMap(const char[] map, char[] destination = "", int maxlen = 0)
+{
+ char mapPiece[PLATFORM_MAX_PATH];
+
+#if defined WORKSHOP_SUPPORT
+ // Handle workshop maps
+ if (GetFeatureStatus(FeatureType_Native, "GetMapDisplayName") == FeatureStatus_Available)
+ {
+ if (!GetMapDisplayName(map, mapPiece, sizeof(mapPiece)))
+ {
+ return false;
+ }
+ }
+ else
+ {
+#endif
+ strcopy(mapPiece, sizeof(mapPiece), map);
+
+ if (!IsMapValid(mapPiece))
+ {
+ return false;
+ }
+#if defined WORKSHOP_SUPPORT
+ }
+#endif
+ char confil[PLATFORM_MAX_PATH];
+
+ // Optimization so we don't immediately rebuild the whole string after ExplodeString
+ BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/maps/%s.cfg", mapPiece);
+ if (FileExists(confil, true))
+ {
+ strcopy(destination, maxlen, confil);
+#if defined LOG
+ LogMessage("[PH] Using config file \"%s\"", confil);
+#endif
+ return true;
+ }
+
+ char fileParts[4][PLATFORM_MAX_PATH];
+ int count = ExplodeString(mapPiece, "_", fileParts, sizeof(fileParts), sizeof(fileParts[])) - 1;
+
+ while (count > 1)
+ {
+ mapPiece[0] = '\0';
+ ImplodeStrings(fileParts, count, "_", mapPiece, sizeof(mapPiece));
+
+ BuildPath(Path_SM, confil, sizeof(confil), "data/prophunt/maps/%s.cfg", mapPiece);
+
+ if (FileExists(confil, true))
+ {
+ strcopy(destination, maxlen, confil);
+#if defined LOG
+ LogMessage("[PH] Using config file \"%s\"", confil);
+#endif
+ return true;
+ }
+
+ count--;
+ }
+
+ destination[0] = '\0'; // In case of decl
+ return false;
+}
+
+// Natives
+
+public int Native_ValidateMap(Handle plugin, int numParams)
+{
+ int mapLength;
+ GetNativeStringLength(1, mapLength);
+
+ char[] map = new char[mapLength+1];
+ GetNativeString(1, map, mapLength+1);
+
+ return ValidateMap(map);
+}
+
+public int Native_IsRunning(Handle plugin, int numParams)
+{
+ return g_Enabled;
+}
+
+public int Native_GetModel(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (!IsClientInGame(client) || GetClientTeam(client) != TEAM_PROP || strlen(g_PlayerModel[client]) == 0)
+ {
+ return false;
+ }
+
+ SetNativeString(2, g_PlayerModel[client], GetNativeCell(3));
+
+ return true;
+}
+
+public int Native_GetModelName(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (!IsClientInGame(client) || GetClientTeam(client) != TEAM_PROP || strlen(g_PlayerModel[client]) == 0)
+ {
+ return false;
+ }
+
+ int targetClient = GetNativeCell(4);
+
+ int length = GetNativeCell(3);
+ char[] model = new char[length];
+
+ GetModelNameForClient(targetClient, g_PlayerModel[client], model, length);
+
+ SetNativeString(2, model, length);
+ return true;
+}
+
+public int Native_LastPropMode(Handle plugin, int numParams)
+{
+ return g_LastProp;
+}
+
+void Internal_AddServerTag()
+{
+ char tags[SV_TAGS_SIZE+1];
+ g_hTags.GetString(tags, sizeof(tags));
+
+ if (StrContains(tags, "PropHunt", false) == -1 && strlen(tags) + 9 <= SV_TAGS_SIZE)
+ {
+ char tagArray[20][SV_TAGS_SIZE+1];
+ int count = ExplodeString(tags, ",", tagArray, sizeof(tagArray), sizeof(tagArray[]));
+
+ if (count < sizeof(tagArray))
+ {
+ tagArray[count] = "PropHunt";
+
+ ImplodeStrings(tagArray, count+1, ",", tags, sizeof(tags));
+ SetConVarString(g_hTags, tags);
+ }
+ }
+}
+
+void Internal_RemoveServerTag()
+{
+ char tags[SV_TAGS_SIZE+1];
+ g_hTags.GetString(tags, sizeof(tags));
+
+ if (StrContains(tags, "PropHunt", false) > -1)
+ {
+ char tagArray[20][SV_TAGS_SIZE+1];
+ int count = ExplodeString(tags, ",", tagArray, sizeof(tagArray), sizeof(tagArray[]));
+
+ for (int i = count - 1; i >= 0; i--)
+ {
+ TrimString(tagArray[i]);
+ if (StrEqual(tagArray[i], "PropHunt", false))
+ {
+ count--;
+
+ // Move all elements above this one down by one
+ for (int j = i; j < count; j++)
+ {
+ tagArray[j] = tagArray[j+1];
+ }
+ tagArray[count][0] = '\0';
+ }
+ }
+
+ ImplodeStrings(tagArray, count, ",", tags, sizeof(tags));
+ g_hTags.SetString(tags);
+ }
+
+}
+
+stock RemoveValveHat(client, bool:unhide = false)
+{
+ new edict = MaxClients+1;
+ while((edict = FindEntityByClassnameSafe(edict, "tf_wearable")) != -1)
+ {
+ decl String:netclass[32];
+ if (GetEntityNetClass(edict, netclass, sizeof(netclass)) && strcmp(netclass, "CTFWearable") == 0)
+ {
+ new idx = GetEntProp(edict, Prop_Send, "m_iItemDefinitionIndex");
+ if (idx != 57 && idx != 133 && idx != 231 && idx != 444 && idx != 405 && idx != 608 && GetEntPropEnt(edict, Prop_Send, "m_hOwnerEntity") == client)
+ {
+ SetEntityRenderMode(edict, (unhide ? RENDER_NORMAL : RENDER_TRANSCOLOR));
+ SetEntityRenderColor(edict, 255, 255, 255, (unhide ? 255 : 0));
+ }
+ }
+ }
+}
+int FindEntityByClassnameSafe(int iStart, char[] sClassName)
+{
+ while (iStart > -1 && !IsValidEntity(iStart))
+ {
+ iStart--;
+ }
+
+ return FindEntityByClassname(iStart, sClassName);
+}
diff --git a/addons/sourcemod/scripting/prophunt/localstats2.inc b/addons/sourcemod/scripting/prophunt/localstats2.inc
deleted file mode 100644
index e652d18..0000000
--- a/addons/sourcemod/scripting/prophunt/localstats2.inc
+++ /dev/null
@@ -1,89 +0,0 @@
-// PropHunt Redux Local Stats by Powerlord
-// - reddit.com/r/RUGC_Midwest -
-
-// Don't use this code yet, it's not finished
-/*
- * NOTE: Queries used when connecting to the DB are non-threaded because we want them to complete before
- * any threaded queries would run
- */
-
-#define CURRENT_SCHEMA_VERSION 1
-#define SCHEMA_UPGRADE_1 1
-
-#define DATABASE "prophunt_local"
-new Handle:g_LocalStatsDb;
-
-LocalStats_Init()
-{
- RegConsoleCmd("phrank", Command_localstatsme);
- RegConsoleCmd("phstatsme", Command_localstats);
- RegConsoleCmd("phtop10", Command_localtop10);
-
- decl String:error[255], String:query[392];
- new Handle:db;
-
- if (!SQL_CheckConfig(DATABASE))
- {
- LogError("[PH] No %s database found in configs/databases.cfg", DATABASE);
- return;
- }
-
- PrintToServer("Connecting to Local PropHunt database...");
-
- db = LocalStats_Connect(error);
-
- if (db != INVALID_HANDLE)
- {
- PrintToServer("Connected successfully.");
- }
- else
- {
- PrintToServer("Connection failed: %s", error);
- return;
- }
-
- RegConsoleCmd("phstats", Command_motdlocal, "Local Prop Hunt Stats page");
-}
-
-//SQL_TConnect doesn't support persistent connections, so just use SQL_Connect
-static Handle:LocalStats_Connect(String:error[]="")
-{
- new Handle:db = INVALID_HANDLE;
- decl String:error[255];
-
- if (SQL_CheckConfig(DATABASE))
- {
- db = SQL_Connect(DATABASE, true, error, sizeof(error));
- if (db != INVALID_HANDLE)
- {
- SQL_FastQuery("SET NAMES 'utf8'");
- }
- }
-
- return db;
-}
-
-LocalStats_OnClientDisconnect(client)
-{
- // TODO: Do something with disconnecting clients
-}
-
-LocalStats_OnClientDisconnect_Post(client)
-{
- // TODO: Do something with disconnecting clients
-}
-
-LocalStats_DbRound(winner)
-{
- // TODO: Do something with round winners
-}
-
-LocalStats_AlterScore(client, points, ScReason:reason, time)
-{
- // TODO: Do something with round winners
-}
-
-LocalStats_OnClientPostAdminCheck(client)
-{
- // TODO: Post-logon stuff
-}
\ No newline at end of file
diff --git a/addons/sourcemod/scripting/prophunt_stats_example.sp b/addons/sourcemod/scripting/prophunt_stats_example.sp
new file mode 100644
index 0000000..b1251a8
--- /dev/null
+++ b/addons/sourcemod/scripting/prophunt_stats_example.sp
@@ -0,0 +1,924 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * PropHunt Stats Example
+ * Example on how to do PropHunt Stats Stuff using a DB connection
+ *
+ * PropHunt Stats Example (C)2015 Powerlord (Ross Bemrose). All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+#include
+#include "include/prophunt"
+#include
+#include
+#include
+#include
+
+#pragma semicolon 1
+#pragma newdecls required
+
+#define VERSION "4.0.0 alpha 1"
+
+#define DATABASE "prophuntstats"
+
+enum ScReason
+{
+ ScReason_TeamWin = 0,
+ ScReason_TeamLose,
+ ScReason_Death, // Not used in default scoring
+ ScReason_Kill, // Not used in default scoring
+ ScReason_Time,
+ ScReason_Friendly // Not used in default scoring, friendly fire has been disabled by default in PH for years.
+};
+
+//#define LOGSTATS 1
+
+// AuthID_Steam2 (STEAM_0:0:123456789) uses 19 characters (may be 20 soon)
+// AuthID_Steam3 ([U:1:1234567] uses 13 (may be 14) or more characters. I would recommend setting to at least 18.
+// AuthId_SteamID64 (765345678901234567) uses 17 characters
+const AuthIdType SteamIdType = AuthId_Steam2;
+const int SteamIdLength = 20; // STEAM_0:0:1234567890 <-- this is 1 larger than currently needed, but just to make sure...
+
+const int Points_Time = 2;
+const int Points_TeamWin = 3;
+const int Points_TeamLose = -1;
+
+// How many points are awarded for each action?
+// Note that victims lose half the killer's points
+// (Killer gains 2, victim loses 1; kiler gains 8, victim loses 4)
+const int Points_Killer_Min = 2;
+const int Points_Killer_Max = 8;
+const int Points_Assister_Min = 1;
+const int Points_Assister_Max = 8;
+
+// What lengths do we use for escaped strings?
+const int SQLNameLength = MAX_NAME_LENGTH*2+1;
+const int SQLSteamIdLength = SteamIdLength*2+1;
+const int SQLPathLength = PLATFORM_MAX_PATH*2+1;
+
+// Which field in a transaction is the SELECT query?
+const int TransactionData = 1;
+
+// 60.0 * 60.0
+const float SecondsPerHour = 3600.0;
+
+char ignore[16];
+
+ConVar g_Cvar_Enabled;
+
+Database g_StatsDB;
+Handle g_hScoreTimer;
+
+bool isPropHuntRound = false;
+
+int g_ServerPointCount;
+int g_PointCount[MAXPLAYERS+1];
+int g_ClientTime[MAXPLAYERS+1];
+int g_ServerTime;
+
+char g_ServerIP[32];
+//char g_ServerHostname[128];
+//int g_ServerPort;
+
+char g_SteamID[MAXPLAYERS+1][SteamIdLength+1];
+
+bool g_inRound = false;
+
+int g_StartTime;
+
+/*
+enum DBType
+{
+ DBType_Unknown,
+ DBType_MySQL,
+ DBType_SQLite,
+ DBType_PostgreSQL
+}
+
+DBType g_DBType = DBType_Unknown;
+*/
+
+public Plugin myinfo = {
+ name = "PropHunt Redux Stats",
+ author = "Powerlord",
+ description = "PropHunt stats using a database",
+ version = VERSION,
+ url = ""
+};
+
+// SOURCEMOD CALLBACKS
+
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
+{
+ if (GetEngineVersion() != Engine_TF2)
+ {
+ strcopy(error, err_max, "PropHunt Redux Stats only works on Team Fortress 2.");
+ return APLRes_Failure;
+ }
+
+ if (late)
+ {
+ strcopy(error, err_max, "PropHunt Redux Stats does not support late loading.");
+ return APLRes_Failure;
+ }
+
+ return APLRes_Success;
+}
+
+public void OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("prophunt.phrases");
+
+ CreateConVar("prophunt_stats_example_version", VERSION, "PropHunt Stats version", FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY);
+ g_Cvar_Enabled = CreateConVar("prophunt_stats_example_enable", "1", "Enable PropHunt Stats Example?", FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);
+
+ HookEvent("teamplay_round_start", Event_RoundStart);
+ HookEvent("arena_round_start", Event_ArenaRoundStart);
+ HookEvent("teamplay_round_win", Event_RoundWin);
+ HookEvent("player_death", Event_PlayerDeath);
+
+ RegConsoleCmd("rank", Cmd_Rank);
+ RegConsoleCmd("statsme", Cmd_StatsMe);
+ RegConsoleCmd("top10", Cmd_Top10);
+
+ char ip[16]; // 123.567.901.345\0
+ int port = FindConVar("hostport").IntValue;
+ FindConVar("ip").GetString(ip, sizeof(ip));
+ Format(g_ServerIP, sizeof(g_ServerIP), "%s:%d", ip, port);
+
+ Stats_Init();
+}
+
+
+public void OnPluginEnd()
+{
+ delete g_StatsDB;
+}
+
+public void OnMapStart()
+{
+ // assume false until round start
+ isPropHuntRound = false;
+
+ g_inRound = false;
+}
+
+public void OnClientConnected(int client)
+{
+ // This is to deal with their connect time in case this was a map change
+ g_ClientTime[client] = RoundFloat(GetClientTime(client));
+}
+
+public void OnClientPostAdminCheck(int client)
+{
+ // Get PropHunt stats even if this isn't a PH round
+ if (!IsClientInGame(client) || IsFakeClient(client))
+ {
+ return;
+ }
+
+ char steamid[SteamIdLength+1];
+ char name[MAX_NAME_LENGTH+1];
+
+ if (!GetClientAuthId(client, SteamIdType, g_SteamID[client], sizeof(g_SteamID[])))
+ {
+ LogMessage("Steam ID lookup failed for clent %d (\"%N\")", client, client);
+ return;
+ }
+
+ char escapedSteamid[SQLSteamIdLength];
+ g_StatsDB.Escape(steamid, escapedSteamid, sizeof(escapedSteamid));
+
+ char escapedName[SQLNameLength];
+ g_StatsDB.Escape(name, escapedName, sizeof(escapedName));
+
+ int userId = GetClientUserId(client);
+
+ Transaction tx = new Transaction();
+
+ char query[384];
+ Format(query, sizeof(query), "INSERT %s INTO players (steamid, name) VALUES('%s', '%s')", ignore, escapedSteamid, escapedName);
+ tx.AddQuery(query);
+
+ Format(query, sizeof(query), "SELECT points FROM players WHERE steamid='%s'", escapedSteamid);
+ tx.AddQuery(query);
+
+ g_StatsDB.Execute(tx, Tx_PlayerConnect, Tx_Error, userId, DBPrio_High);
+}
+
+public void OnClientDisconnect(int client)
+{
+ if (IsClientInGame(client) && !IsFakeClient(client) && DatabaseIntact())
+ {
+ char query[392], ip[32], geoip[3], name[MAX_NAME_LENGTH+1];
+
+ GetClientName(client, name, sizeof(name));
+
+ char escapedSteamid[SQLSteamIdLength];
+ g_StatsDB.Escape(g_SteamID[client], escapedSteamid, sizeof(escapedSteamid));
+
+ char escapedName[SQLNameLength];
+ g_StatsDB.Escape(name, escapedName, sizeof(escapedName));
+
+ Transaction tx = new Transaction();
+ Format(query, sizeof(query), "INSERT %s INTO players (steamid, name) VALUES('%s', '%s')", ignore, escapedSteamid, escapedName);
+ tx.AddQuery(query);
+
+ int clientTime = RoundFloat(GetClientTime(client));
+ Format(query, sizeof(query), "UPDATE players SET lastserver = '%s', time = time + %d, ip = '%s', geoip = '%s', name = '%s' WHERE steamid = '%s'",
+ g_ServerIP, clientTime - g_ClientTime[client], ip, geoip, escapedName, escapedSteamid);
+ tx.AddQuery(query);
+
+ g_StatsDB.Execute(tx, .onError=Tx_Error);
+ }
+
+ g_ClientTime[client] = 0;
+ g_SteamID[client][0] = '\0';
+}
+
+// COMMANDS
+
+public Action Cmd_StatsMe(int client, int args)
+{
+ if (client == 0)
+ {
+ ReplyToCommand(client, "%t", "Command is in-game only");
+ return Plugin_Handled;
+ }
+
+ if (IsClientInGame(client) && DatabaseIntact())
+ {
+ char query[392];
+
+ char escapedSteamid[SQLSteamIdLength];
+ g_StatsDB.Escape(g_SteamID[client], escapedSteamid, sizeof(escapedSteamid));
+
+ Format(query, sizeof(query), "SELECT points, wins, losses, a.rank, time FROM players, (SELECT count(*)+1 as rank FROM players WHERE points > (SELECT points FROM players WHERE steamid = '%s')) as a WHERE steamid = '%s'", escapedSteamid, escapedSteamid);
+ g_StatsDB.Query(T_Statsme, query, GetClientUserId(client));
+ }
+
+ return Plugin_Handled;
+}
+
+public Action Cmd_Rank(int client, int args)
+{
+ if (client == 0)
+ {
+ ReplyToCommand(client, "%t", "Command is in-game only");
+ return Plugin_Handled;
+ }
+
+ if (IsClientInGame(client) && DatabaseIntact())
+ {
+ char query[392];
+
+ char escapedSteamid[SQLSteamIdLength];
+ g_StatsDB.Escape(g_SteamID[client], escapedSteamid, sizeof(escapedSteamid));
+
+ Format(query, sizeof(query), "SELECT points, wins, losses, a.rank, time FROM players, (SELECT count(*)+1 as rank FROM players WHERE points > (SELECT points FROM players WHERE steamid = '%s')) as a WHERE steamid = '%s'", escapedSteamid, escapedSteamid);
+ g_StatsDB.Query(T_Rank, query, GetClientUserId(client));
+ }
+
+ return Plugin_Handled;
+}
+
+public Action Cmd_Top10(int client, int args)
+{
+ if (client == 0)
+ {
+ ReplyToCommand(client, "%t", "Command is in-game only");
+ return Plugin_Handled;
+ }
+
+ if (IsClientInGame(client) && DatabaseIntact())
+ {
+ g_StatsDB.Query(T_Top10, "SELECT name, points FROM players ORDER BY points DESC LIMIT 10", GetClientUserId(client));
+ }
+
+ return Plugin_Handled;
+}
+
+public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast)
+{
+ isPropHuntRound = PropHuntRedux_IsRunning(); // This can change every round
+
+ g_inRound = false;
+
+ if (!g_Cvar_Enabled.BoolValue || !isPropHuntRound)
+ {
+ return;
+ }
+
+ g_hScoreTimer = CreateTimer(55.0, Timer_Score, _, TIMER_FLAG_NO_MAPCHANGE);
+}
+
+public void Event_ArenaRoundStart(Event event, const char[] name, bool dontBroadcast)
+{
+ g_inRound = true;
+ g_StartTime = GetTime();
+}
+
+public void Event_RoundWin(Event event, const char[] name, bool dontBroadcast)
+{
+ if (!g_Cvar_Enabled.BoolValue || !isPropHuntRound)
+ {
+ return;
+ }
+
+ g_inRound = false;
+ isPropHuntRound = false;
+
+ delete g_hScoreTimer;
+
+ int winner = event.GetInt("team");
+
+ DbRound(winner);
+
+ for (int client = 1; client <= MaxClients; client++)
+ {
+ if (!IsClientInGame(client))
+ {
+ continue;
+ }
+
+ int team = GetClientTeam(client);
+
+ if (team == winner) // either props or hunters
+ {
+ AlterScore(client, Points_TeamWin, ScReason_TeamWin);
+ }
+ else if (team > TEAM_SPECTATOR) // either props or hunters
+ {
+ AlterScore(client, Points_TeamLose, ScReason_TeamLose);
+ }
+ }
+}
+
+public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast)
+{
+ if (!g_Cvar_Enabled.BoolValue || !g_inRound || !isPropHuntRound)
+ {
+ return;
+ }
+
+ // PropHunt usually doesn't allow Spies, but just in case
+ if (event.GetInt("death_flags") & TF_DEATHFLAG_DEADRINGER)
+ {
+ return;
+ }
+
+ int client = GetClientOfUserId(event.GetInt("userid"));
+ int attacker = GetClientOfUserId(event.GetInt("attacker"));
+ int assister = GetClientOfUserId(event.GetInt("assister"));
+
+ char weapon[255];
+ event.GetString("weapon_logclassname", weapon, sizeof(weapon));
+
+ if (client > 0 && attacker > 0 && IsClientInGame(client) && IsClientInGame(attacker) && client != attacker)
+ {
+ PlayerKilled(client, attacker, assister, weapon);
+ }
+}
+
+// FUNCTIONS
+
+void PlayerKilled(int client, int attacker, int assister, char[] weaponname)
+{
+ int clientPoints, attackerPoints, assisterPoints;
+ char attackerName[MAX_NAME_LENGTH+1], clientName[MAX_NAME_LENGTH+1];
+
+ GetClientName(attacker, attackerName, sizeof(attackerName));
+ GetClientName(client, clientName, sizeof(clientName));
+
+ // This algorithm has corrects a long-standing bug in PropHunt stats where the float conversion was done after
+ // int math had already taken place
+ attackerPoints = RoundFloat(float(g_PointCount[client]) / float(g_PointCount[attacker]));
+
+ // Point caps
+ if (attackerPoints < Points_Killer_Min)
+ attackerPoints = Points_Killer_Min;
+ else if (attackerPoints > Points_Killer_Max)
+ attackerPoints = Points_Killer_Max;
+
+ //Assister point caps
+ if (assister > 0)
+ {
+ assisterPoints = RoundFloat(float(g_PointCount[client]) / float(g_PointCount[assister]) / 2.0);
+
+ if (assisterPoints < Points_Assister_Min)
+ assisterPoints = Points_Assister_Min;
+ else if (assisterPoints > Points_Assister_Max)
+ assisterPoints = Points_Assister_Max;
+ }
+
+ // victim points are between -1 and -4
+ clientPoints = 0 - RoundFloat(attackerPoints * 0.5);
+
+ if (IsClientInGame(attacker))
+ {
+ if (GetClientTeam(client) == TEAM_PROP)
+ {
+ char model[PLATFORM_MAX_PATH];
+ PropHuntRedux_GetPropModel(client, model, sizeof(model));
+ DbProp(model, "death", 1);
+ }
+ CPrintToChat(client, "%t", "#TF_PH_AlterScore_Death", attackerName, clientPoints*-1);
+ DbInt(client, "points", clientPoints);
+ }
+
+ CPrintToChat(attacker, "%t", "#TF_PH_AlterScore_Kill", clientName, attackerPoints);
+ if (assister > 0)
+ CPrintToChat(assister, "%t", "#TF_PH_AlterScore_Assist", clientName, assisterPoints);
+
+ DbInt(attacker, "points", attackerPoints);
+ if (assister > 0)
+ DbInt(assister, "points", assisterPoints);
+
+ // This is commented as we already subtract points from clients
+ // DbInt(client, "points", -2);
+ DbDeaths(client, attacker, assister, weaponname);
+}
+
+void DbDeaths(int client, int attacker, int assister, char[] weaponname)
+{
+ if (!DatabaseIntact() || !g_inRound)
+ {
+ return;
+ }
+
+ char query[1024], map[PLATFORM_MAX_PATH], escapedMap[SQLPathLength], model[PLATFORM_MAX_PATH], escapedModel[SQLPathLength];
+ int team, time;
+ float killerPos[3], victimPos[3];
+ char escapedVictimId[SQLSteamIdLength], escapedKillerId[SQLSteamIdLength], escapedAssisterId[SQLSteamIdLength];
+
+ // Ditch the legacy code dealing with weapon names
+
+ if (client > 0 && !IsFakeClient(client) && IsClientInGame(client))
+ {
+ g_StatsDB.Escape(g_SteamID[client], escapedVictimId, sizeof(escapedVictimId));
+
+ GetClientEyePosition(client, victimPos);
+ time - GetTime() - g_StartTime;
+ }
+
+ if (attacker > 0 && !IsFakeClient(attacker) && IsClientInGame(attacker))
+ {
+ g_StatsDB.Escape(g_SteamID[attacker], escapedKillerId, sizeof(escapedKillerId));
+
+ GetClientEyePosition(attacker, killerPos);
+ team = GetClientTeam(attacker);
+ }
+
+ if (assister > 0 && !IsFakeClient(assister) && IsClientInGame(assister))
+ {
+ g_StatsDB.Escape(g_SteamID[assister], escapedAssisterId, sizeof(escapedAssisterId));
+ }
+
+ GetCurrentMap(map, sizeof(map));
+ GetMapDisplayName(map, map, sizeof(map));
+ g_StatsDB.Escape(map, escapedMap, sizeof(escapedMap));
+
+ PropHuntRedux_GetPropModel(client, model, sizeof(model));
+ g_StatsDB.Escape(model, escapedModel, sizeof(escapedModel));
+
+ Format(query, sizeof(query), "INSERT INTO deaths (victimid, killerid, killerteam, weapon, assisterid, ip, map, prop, victim_position_x, victim_position_y, victim_position_z, killer_position_x, killer_position_y, killer_position_z, victim_class, killer_class, survival_time) VALUES('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %.0f, %.0f, %.0f, %.0f, %.0f, %.0f, %i, %i, %i)",
+ escapedVictimId, escapedKillerId, (team == TEAM_PROP ? "RED" : "BLU"), weaponname, escapedAssisterId, g_ServerIP, escapedMap, escapedModel, victimPos[0], victimPos[1], victimPos[2],
+ killerPos[0], killerPos[1], killerPos[2], TF2_GetPlayerClass(client), TF2_GetPlayerClass(attacker), time);
+
+ g_StatsDB.Query(T_ErrorOnly, query);
+
+#if defined LOGSTATS
+ LogMessage("[PH] DbDeaths [%s]", query);
+#endif
+}
+
+void DbSurvivals(int client)
+{
+ if (!DatabaseIntact())
+ {
+ return;
+ }
+
+ char query[1024], map[PLATFORM_MAX_PATH], escapedMap[SQLPathLength], model[PLATFORM_MAX_PATH], escapedModel[SQLPathLength], escapedSteamid[SQLSteamIdLength];
+ int time;
+ float pos[3];
+
+ if (client > 0 && !IsFakeClient(client) && IsClientInGame(client))
+ {
+ g_StatsDB.Escape(g_SteamID[client], escapedSteamid, sizeof(escapedSteamid));
+ GetClientEyePosition(client, pos);
+ time = GetTime() - g_StartTime;
+ }
+
+ GetCurrentMap(map, sizeof(map));
+ GetMapDisplayName(map, map, sizeof(map));
+ g_StatsDB.Escape(map, escapedMap, sizeof(escapedMap));
+
+ PropHuntRedux_GetPropModel(client, model, sizeof(model));
+ g_StatsDB.Escape(model, escapedModel, sizeof(escapedModel));
+
+ Format(query, sizeof(query), "INSERT INTO survivals (steamid, prop, ip, map, lastprop, position_x, position_y, position_z, class, team, survival_time) VALUES('%s', '%s', '%s', '%s', '%s', %.0f, %.0f, %.0f, %i, '%s', %i)",
+ escapedSteamid, escapedModel, g_ServerIP, escapedMap, PropHuntRedux_IsLastPropMode() ? "1" : "0" , pos[0], pos[1], pos[2], TF2_GetPlayerClass(client),
+ GetClientTeam(client) == TEAM_PROP ? "RED" : "BLU", time);
+
+ g_StatsDB.Query(T_ErrorOnly, query);
+
+#if defined LOGSTATS
+ LogMessage("[PH] DbSurvivals [%s]", query);
+#endif
+}
+
+void AlterScore(int client, int sc, ScReason reason)
+{
+ switch(reason)
+ {
+ case ScReason_TeamWin:
+ {
+ DbSurvivals(client);
+ CPrintToChat(client, "%t", "#TF_PH_AlterScore_TeamWin", sc);
+ DbInt(client, "wins", 1);
+
+ if (GetClientTeam(client) == TEAM_PROP && IsPlayerAlive(client))
+ {
+ char model[PLATFORM_MAX_PATH];
+ PropHuntRedux_GetPropModel(client, model, sizeof(model));
+ DbProp(model, "survivals", 1);
+ }
+ }
+
+ case ScReason_Time:
+ {
+ CPrintToChat(client, "%t", "#TF_PH_AlterScore_TimeAward", sc);
+ }
+
+ case ScReason_TeamLose:
+ {
+ CPrintToChat(client, "%t", "#TF_PH_AlterScore_TeamLose", sc*-1);
+ DbInt(client, "losses", 1);
+ }
+ }
+ DbInt(client, "points", sc);
+}
+
+// MENU HANDLING
+
+public int MenuHandler(Menu menu, MenuAction action, int param1, int param2)
+{
+ switch (action)
+ {
+ case MenuAction_End:
+ {
+ delete menu;
+ }
+ }
+}
+
+// TIMERS
+
+public Action Timer_UpdateServerScore(Handle timer)
+{
+ char query[384];
+
+ Format(query, sizeof(query), "UPDATE servers SET points = %d, time = %d WHERE ip = '%s'", g_ServerPointCount, g_ServerTime, g_ServerIP);
+ g_StatsDB.Query(T_ErrorOnly, query);
+}
+
+public Action Timer_Score(Handle timer)
+{
+ for (int client = 1; client <= MaxClients; client++)
+ {
+ if (IsClientInGame(client) && IsPlayerAlive(client) && GetClientTeam(client) == TEAM_PROP)
+ {
+ AlterScore(client, Points_Time, ScReason_Time);
+ }
+ }
+}
+
+// SQL
+
+void DbInt(int client, const char[] what, int points, Transaction tx=null)
+{
+ if (!IsClientConnected(client) || IsFakeClient(client))
+ {
+ return;
+ }
+
+ char escapedSteamid[SQLSteamIdLength];
+ g_StatsDB.Escape(g_SteamID[client], escapedSteamid, sizeof(escapedSteamid));
+
+ char name[MAX_NAME_LENGTH+1];
+ GetClientName(client, name, sizeof(name));
+
+ char escapedName[SQLNameLength];
+ g_StatsDB.Escape(name, escapedName, sizeof(escapedName));
+
+ char query[384];
+ Format(query, sizeof(query), "UPDATE players SET %s = %s + %d, name = '%s' WHERE steamdi = '%s'", what, what, points, escapedName, escapedSteamid);
+ if (tx == null)
+ {
+ g_StatsDB.Query(T_ErrorOnly, query);
+ }
+ else
+ {
+ tx.AddQuery(query);
+ }
+ g_ServerPointCount += points;
+ g_PointCount[client] += points;
+
+#if defined LOGSTATS
+ LogMessage("[PH] DbInt [%s]", query);
+#endif
+}
+
+void DbRound(int team)
+{
+ if (!DatabaseIntact())
+ {
+ return;
+ }
+
+ char query[384];
+
+ char map[PLATFORM_MAX_PATH], escapedMap[SQLPathLength];
+ GetCurrentMap(map, sizeof(map));
+ GetMapDisplayName(map, map, sizeof(map));
+ g_StatsDB.Escape(map, escapedMap, sizeof(escapedMap));
+
+ Format(query, sizeof(query), "INSERT INTO rounds (team, server, map) VALUES('%s', '%s', '%s')", team == TEAM_PROP ? "RED" : "BLU", g_ServerIP, escapedMap);
+ g_StatsDB.Query(T_ErrorOnly, query);
+
+#if defined LOGSTATS
+ LogMessage("[PH] DbRound [%s]", query);
+#endif
+}
+
+void DbProp(const char[] prop, const char[] what, int points)
+{
+ if (!DatabaseIntact())
+ {
+ return;
+ }
+
+ char query[384], escapedProp[SQLPathLength];
+
+ g_StatsDB.Escape(prop, escapedProp, sizeof(escapedProp));
+
+ Transaction tx = new Transaction();
+
+ Format(query, sizeof(query), "INSERT %s INTO props (name) VALUES('%s')", ignore, escapedProp);
+ tx.AddQuery(query);
+
+ Format(query, sizeof(query), "UPDATE props SET %s = %s + %d WHERE name = '%s'", what, what, points, escapedProp);
+ tx.AddQuery(query);
+
+ g_StatsDB.Execute(tx, .onError=Tx_Error);
+
+#if defined LOGSTATS
+ LogMessage("[PH] DbProp [%s]", query);
+#endif
+
+}
+
+public bool DatabaseIntact()
+{
+ return (g_StatsDB != null);
+}
+
+//THREADED QUERY CALLBACKS
+
+public void T_ErrorOnly(Database db, DBResultSet result, const char[] error, any data)
+{
+ if (result == null)
+ {
+ LogError("[PH] DATABASE ERROR (error: %s)", error);
+ PrintToChatAll("[PH] DATABASE ERROR (error: %s)", error);
+ }
+}
+
+public void T_DBConnect(Database db, const char[] error, any data)
+{
+ if (db == null)
+ {
+ SetFailState("%T: %s", "Could not connect to database", LANG_SERVER, error);
+ return;
+ }
+
+ PrintToServer("Connected successfully.");
+
+ g_StatsDB = db;
+
+ db.SetCharset("utf8");
+
+ char driver[64];
+ db.Driver.GetIdentifier(driver, sizeof(driver));
+
+ if (StrEqual(driver, "mysql", false))
+ {
+ //g_DBType = DBType_MySQL;
+ ignore = "IGNORE";
+ }
+ else if (StrEqual(driver, "sqlite", false))
+ {
+ //g_DBType = DBType_SQLite;
+ ignore = "OR IGNORE";
+ }
+ else if (StrEqual(driver, "pgsql", false))
+ {
+ //g_DBType = DBType_PostgreSQL;
+ // In Postgres, use a rule to ignore dupes: http://stackoverflow.com/a/6176044/15880
+ }
+
+ Transaction tx = new Transaction();
+
+ char query[384];
+
+ Format(query, sizeof(query), "INSERT %s INTO servers (ip) VALUES('%s')", ignore, g_ServerIP);
+ tx.AddQuery(query);
+
+ Format(query, sizeof(query), "SELECT points, time FROM servers WHERE ip='%s'", g_ServerIP);
+ tx.AddQuery(query);
+
+ db.Execute(tx, Tx_GetServerPoints, Tx_Error, _, DBPrio_High);
+
+ CreateTimer(120.0, Timer_UpdateServerScore, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
+}
+
+public void T_Statsme(Database db, DBResultSet result, const char[] error, any userid)
+{
+ int client = GetClientOfUserId(userid);
+
+ if (client == 0)
+ {
+ return;
+ }
+
+ if (result == null || !result.HasResults || result.RowCount < 1 || !result.FetchRow())
+ {
+ PrintToChatAll("Failed to query (error: %s)", error);
+ return;
+ }
+
+ char name[MAX_NAME_LENGTH+1];
+ GetClientName(client, name, sizeof(name));
+
+ // "\x04%s is on rank %i with %i score (%i wins and %i losses). Time played %i hours."
+ CPrintToChatAll("\x04%t", "#TF_PH_Stats_StatsMe", name, result.FetchInt(3), result.FetchInt(0), result.FetchInt(1),
+ result.FetchInt(2), RoundFloat(float(result.FetchInt(4)) / SecondsPerHour));
+}
+
+public void T_Rank(Database db, DBResultSet result, const char[] error, any userid)
+{
+ int client = GetClientOfUserId(userid);
+
+ if (client == 0)
+ {
+ return;
+ }
+
+ if (result == null || !result.HasResults || result.RowCount < 1 || !result.FetchRow())
+ {
+ PrintToChatAll("Failed to query (error: %s)", error);
+ return;
+ }
+
+ Menu smenu = new Menu(MenuHandler);
+
+ char buffer[255];
+ char winloss[10];
+
+ smenu.Pagination = MENU_NO_PAGINATION;
+ smenu.SetTitle("%T", "#TF_PH_Stats_PersonalHeader", client);
+
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalRank", client, result.FetchInt(3));
+ smenu.AddItem("#TF_PH_Stats_PersonalRank", buffer, ITEMDRAW_DISABLED);
+
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalWins", client, result.FetchInt(1));
+ smenu.AddItem("#TF_PH_Stats_PersonalWins", buffer, ITEMDRAW_DISABLED);
+
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalLosses", client, result.FetchInt(2));
+ smenu.AddItem("#TF_PH_Stats_PersonalLosses", buffer, ITEMDRAW_DISABLED);
+
+ Format(winloss, sizeof(winloss), "%.2f", float(result.FetchInt(1)) / float(result.FetchInt(2)));
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalWinLoss", client, winloss);
+ smenu.AddItem("#TF_PH_Stats_PersonalWinLoss", buffer, ITEMDRAW_DISABLED);
+
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalScore", client, result.FetchInt(0));
+ smenu.AddItem("#TF_PH_Stats_PersonalScore", buffer, ITEMDRAW_DISABLED);
+
+ Format(buffer, sizeof(buffer), "%T", "#TF_PH_Stats_PersonalTime", client, RoundFloat(float(result.FetchInt(4)) / SecondsPerHour));
+ smenu.AddItem("#TF_PH_Stats_PersonalTime", buffer, ITEMDRAW_DISABLED);
+
+ smenu.Display(client, MENU_TIME_FOREVER);
+}
+
+public void T_Top10(Database db, DBResultSet result, const char[] error, any userid)
+{
+ int client = GetClientOfUserId(userid);
+
+ if (client == 0)
+ {
+ return;
+ }
+
+ if (result == null || !result.HasResults)
+ {
+ PrintToChatAll("Failed to query (error: %s)", error);
+ return;
+ }
+
+ Menu smenu = new Menu(MenuHandler);
+ // Top 10 needs pagination
+ //smenu.Pagination = MENU_NO_PAGINATION;
+ smenu.SetTitle("%T", "#TF_PH_Stats_Top10", client);
+
+ char buffer[256];
+ while (result.FetchRow())
+ {
+ char name[MAX_NAME_LENGTH+1];
+ result.FetchString(0, name, sizeof(name));
+
+ Format(buffer, sizeof(buffer), "(%i) %s", result.FetchInt(1));
+ smenu.AddItem(buffer, buffer, ITEMDRAW_DISABLED);
+ }
+
+ smenu.Display(client, MENU_TIME_FOREVER);
+}
+
+// THREADED TRANSACTION CALLBACKS
+
+public void Tx_Error(Database db, any data, int numQueries, const char[] error, int failindex, any[] queryData)
+{
+ LogError("[PH] DATABASE ERROR (error: %s)", error);
+ PrintToChatAll("DATABASE ERROR (error: %s)", error);
+}
+
+public void Tx_GetServerPoints(Database db, any data, int numQueriest, DBResultSet[] results, any[] queryData)
+{
+ if (results[TransactionData] == null || results[TransactionData].RowCount < 1 || !results[TransactionData].FetchRow())
+ {
+ return;
+ }
+
+ g_ServerPointCount = results[TransactionData].FetchInt(0);
+ g_ServerTime = results[TransactionData].FetchInt(1);
+}
+
+public void Tx_PlayerConnect(Database db, any userid, int numQueries, DBResultSet[] results, any[] queryData)
+{
+ int client = GetClientOfUserId(userid);
+ if (client == 0 || results[TransactionData] == null || results[TransactionData].RowCount < 1 || !results[TransactionData].FetchRow())
+ {
+ return;
+ }
+
+ g_PointCount[client] = results[TransactionData].FetchInt(0);
+}
+
+//STOCK FUNCTIONS
+
+void Stats_Init()
+{
+ // DB Connect logic here
+ if (!SQL_CheckConfig(DATABASE))
+ {
+ SetFailState("No database configuration for %s", DATABASE);
+ }
+
+ PrintToServer("Connecting to PropHunt database...");
+
+ Database.Connect(T_DBConnect, DATABASE);
+}
+
+// EXTERNAL CALLBACKS
+
+// Add s to the game description
+public Action PropHuntRedux_UpdateGameDescription(char[] description, int maxlength)
+{
+ StrCat(description, maxlength, "s");
+ return Plugin_Changed;
+}
+
diff --git a/addons/sourcemod/scripting/prophunt_stats_example_ajax.sp b/addons/sourcemod/scripting/prophunt_stats_example_ajax.sp
new file mode 100644
index 0000000..cbd9199
--- /dev/null
+++ b/addons/sourcemod/scripting/prophunt_stats_example_ajax.sp
@@ -0,0 +1,430 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * PropHunt Stats Ajax Example
+ * Example on how to do PropHunt Stats Stuff using REST calls
+ *
+ * This plugin is used to exhibit more control over the stats system.
+ * The remote web application can do heuristics to tell if a server is being
+ * used to cheat at stats.
+ *
+ * It also makes it so the central stats system controls the point values for
+ * actions as they are no longer sent.
+ *
+ * It forces Steam 2 IDs for consistency. This way, you don't have to deal
+ * with Valve suddenly changing the Steam ID types (like they did last year).
+ *
+ * It's a good idea if the stats system ignores any kills on STEAM_ID_BOT
+ * or STEAM_ID_LAN for example.
+ *
+ * PropHunt Stats Ajax Example (C)2015 Powerlord (Ross Bemrose).
+ * All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+#include
+#include "include/prophunt"
+#include
+#include
+#include
+#include
+#pragma semicolon 1
+
+#define VERSION "1.0.0"
+
+#define DATABASE "prophuntstats"
+
+new const String:server[] = "https://www.example.com/prophunt/stats";
+const SQLNameLength = MAX_NAME_LENGTH*2+1;
+const Steam2IdLength = 20; // STEAM_0:0:1234567890 <-- this is 1 larger than currently needed, but just to make sure...
+
+new Handle:g_Cvar_Enabled;
+new Handle:g_Cvar_ServerID;
+
+new Handle:g_hScoreTimer;
+
+new bool:isPropHuntRound = false;
+
+new String:g_sServerID[256];
+
+new String:g_sSteam2IDs[MAXPLAYERS+1][Steam2IdLength+1];
+
+public Plugin:myinfo = {
+ name = "PropHunt Stats REST Example",
+ author = "Powerlord",
+ description = "Example on how to do PropHunt Stats Stuff using REST endpoints",
+ version = VERSION,
+ url = ""
+};
+
+public OnPluginStart()
+{
+ LoadTranslations("common.phrases");
+ LoadTranslations("prophunt.phrases");
+
+ CreateConVar("prophunt_stats_example_ajax_version", VERSION, "PropHunt Stats Example version", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD|FCVAR_SPONLY);
+ g_Cvar_Enabled = CreateConVar("prophunt_stats_example_ajax_enable", "1", "Enable PropHunt Stats Example?", FCVAR_PLUGIN|FCVAR_NOTIFY|FCVAR_DONTRECORD, true, 0.0, true, 1.0);
+ g_Cvar_ServerID = CreateConVar("prophunt_stats_example_ajax_serverid", "", "Server ID for PropHunt Stats", FCVAR_PLUGIN|FCVAR_PROTECTED);
+
+ HookEvent("teamplay_round_start", Event_RoundStart);
+ HookEvent("teamplay_round_win", Event_RoundWin);
+ HookEvent("player_death", Event_PlayerDeath);
+
+ RegConsoleCmd("rank", Cmd_Rank);
+ RegConsoleCmd("statsme", Cmd_StatsMe);
+ RegConsoleCmd("top10", Cmd_Top10);
+ RegConsoleCmd("stats", Cmd_Stats);
+}
+
+public OnAllPluginsLoaded()
+{
+ // We do this to record the server's current IP address
+ new String:url[255];
+ Format(url, sizeof(url), "%s/serverStart", server);
+
+ // PropHunt Stats methods are not idempotent and thus should use POST
+ new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
+
+ // All requests require a server ID
+ Steam_SetHTTPRequestGetOrPostParameter(request, "serverid", g_sServerID);
+ Steam_SendHTTPRequest(request, Response_ErrorOnly);
+
+}
+
+public OnMapStart()
+{
+ // assume false until round start
+ isPropHuntRound = false;
+}
+
+public OnConfigsExecuted()
+{
+ GetConVarString(g_Cvar_ServerID, g_sServerID, sizeof(g_sServerID));
+}
+
+public OnClientAuthorized(client, const String:auth[])
+{
+ if (!IsClientConnected(client) || IsFakeClient(client))
+ {
+ return;
+ }
+
+ // Ignore auth and use the Steam2 ID
+ if (!GetClientAuthId(client, AuthId_Steam2, g_sSteam2IDs[client], sizeof(g_sSteam2IDs[])))
+ {
+ return;
+ }
+
+ new String:url[255];
+ Format(url, sizeof(url), "%s/connected", server);
+
+ // PropHunt Stats methods are not idempotent and thus should use POST
+ new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
+ Steam_SetHTTPRequestGetOrPostParameter(request, "serverid", g_sServerID);
+ // Steam_SetHTTPRequestGetOrPostParameter(request, "format", "vdf");
+ Steam_SetHTTPRequestGetOrPostParameter(request, "steamid", g_sSteam2IDs[client]);
+ Steam_PrioritizeHTTPRequest(request); // Connected needs to be high priority because it may need to create the stats user on the DB side
+ Steam_SendHTTPRequest(request, Response_ErrorOnly);
+}
+
+public OnClientDisconnect(client)
+{
+ new String:url[255];
+ Format(url, sizeof(url), "%s/disconnected", server);
+
+ new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
+ Steam_SetHTTPRequestGetOrPostParameter(request, "serverid", g_sServerID);
+ // Steam_SetHTTPRequestGetOrPostParameter(request, "format", "vdf");
+ Steam_SetHTTPRequestGetOrPostParameter(request, "steamid", g_sSteam2IDs[client]);
+ Steam_SendHTTPRequest(request, Response_ErrorOnly);
+
+ g_sSteam2IDs[client][0] = '\0';
+}
+
+public Response_ErrorOnly(HTTPRequestHandle:HTTPRequest, bool:requestSuccessful, HTTPStatusCode:statusCode)
+{
+ if (!requestSuccessful)
+ {
+ LogError("Stats server returned error. Status code: %d", statusCode);
+ return;
+ }
+}
+
+public Action:Cmd_Rank(client, args)
+{
+ if (client == 0)
+ {
+ ReplyToCommand(client, "%t", "Command is in-game only");
+ return Plugin_Handled;
+ }
+
+
+ if (g_sSteam2IDs[client][0] == '\0')
+ {
+ ReplyToCommand(client, "%t", "Target is not in game");
+ return Plugin_Handled;
+ }
+
+ // Do REST call here
+
+ return Plugin_Handled;
+}
+
+public Action:Cmd_StatsMe(client, args)
+{
+}
+
+public Action:Cmd_Top10(client, args)
+{
+}
+
+public Action:Cmd_Stats(client, args)
+{
+}
+
+public Event_RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
+{
+ isPropHuntRound = PropHuntRedux_IsRunning(); // This can change every round
+
+ if (!GetConVarBool(g_Cvar_Enabled) || !isPropHuntRound)
+ {
+ return;
+ }
+
+ g_hScoreTimer = CreateTimer(55.0, Timer_Score, _, TIMER_FLAG_NO_MAPCHANGE);
+}
+
+public Event_RoundWin(Handle:event, const String:name[], bool:dontBroadcast)
+{
+ if (!GetConVarBool(g_Cvar_Enabled) || !isPropHuntRound)
+ {
+ return;
+ }
+
+ isPropHuntRound = false;
+
+ CloseHandle(g_hScoreTimer);
+
+ new winner = GetEventInt(event, "team");
+
+ for (new client = 1; client <= MaxClients; client++)
+ {
+ if (!IsClientInGame(client))
+ {
+ continue;
+ }
+
+ new team = GetClientTeam(client);
+
+ if (team == winner) // either props or hunters
+ {
+ // REST logic to add 3 points
+ }
+ else if (team > _:TFTeam_Spectator) // either props or hunters
+ {
+ // REST logic to subtract 1 point
+ }
+ }
+
+ new String:map[PLATFORM_MAX_PATH];
+ GetCurrentMap(map, sizeof(map));
+ // REST logic to record map and team win
+}
+
+public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
+{
+ if (!GetConVarBool(g_Cvar_Enabled) || !isPropHuntRound)
+ {
+ return;
+ }
+
+ // PropHunt usually doesn't allow Spies, but just in case
+ if (GetEventInt(event, "death_flags") & TF_DEATHFLAG_DEADRINGER)
+ {
+ return;
+ }
+
+ new clientID = GetEventInt(event, "userid");
+ new attackerID = GetEventInt(event, "attacker");
+ new assisterID = GetEventInt(event, "assister");
+ new weaponID = GetEventInt(event, "weapon");
+ new String:weapon[255];
+ GetEventString(event, "weapon_logclassname", weapon, sizeof(weapon));
+
+ new client = GetClientOfUserId(clientID);
+ new attacker;
+ if (attackerID > 0)
+ {
+ attacker = GetClientOfUserId(attackerID);
+ }
+
+ new assister;
+ if (assisterID > 0)
+ {
+ assister = GetClientOfUserId(assisterID);
+ }
+
+ new String:url[255];
+ Format(url, sizeof(url), "%s/playerkill", server);
+
+ new String:clientName[MAX_NAME_LENGTH+1];
+ new String:attackerName[MAX_NAME_LENGTH+1];
+ new String:assisterName[MAX_NAME_LENGTH+1];
+
+ GetClientName(client, clientName, sizeof(clientName));
+
+ // Note that in the original, this method had no less than 3 DB calls in it. However, I want to do just one REST call.
+
+ new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
+ Steam_SetHTTPRequestGetOrPostParameter(request, "serverid", g_sServerID);
+ Steam_SetHTTPRequestGetOrPostParameter(request, "format", "vdf");
+ Steam_SetHTTPRequestGetOrPostParameter(request, "clientSteamId", g_sSteam2IDs[client]);
+ if (attacker > 0)
+ {
+ GetClientName(attacker, attackerName, sizeof(attackerName));
+ Steam_SetHTTPRequestGetOrPostParameter(request, "attackerSteamId", g_sSteam2IDs[attacker]);
+ new team = GetClientTeam(attacker);
+ new String:strTeam[2];
+ IntToString(team, strTeam, sizeof(strTeam));
+
+ Steam_SetHTTPRequestGetOrPostParameter(request, "killerTeam", strTeam);
+
+ }
+
+ if (assister > 0)
+ {
+ GetClientName(assister, assisterName, sizeof(assisterName));
+ Steam_SetHTTPRequestGetOrPostParameter(request, "assisterSteamId", g_sSteam2IDs[assister]);
+ }
+
+ new weaponIndex = -1;
+
+ new String:strWeaponIndex[7];
+
+ if (weaponID > MaxClients)
+ {
+ weaponIndex = GetEntProp(weaponID, Prop_Send, "m_iItemDefinitionIndex");
+ }
+
+ IntToString(weaponIndex, strWeaponIndex, sizeof(strWeaponIndex));
+
+ // Definition index will allow lookup of weapons on remote web server.
+ Steam_SetHTTPRequestGetOrPostParameter(request, "weaponDefinitionIndex", strWeaponIndex);
+
+ new String:propModel[PLATFORM_MAX_PATH];
+ PropHuntRedux_GetPropModel(client, propModel, sizeof(propModel));
+
+ new Handle:data = CreateDataPack();
+
+ WritePackCell(data, clientID);
+ WritePackCell(data, attackerID);
+ WritePackCell(data, assisterID);
+ WritePackString(data, clientName);
+ WritePackString(data, attackerName);
+ WritePackString(data, assisterName);
+
+ // TODO Check to see what other fields we're missing.
+ Steam_SendHTTPRequest(request, Response_PlayerKilled, data); // different method so we can print score changes
+}
+
+public Response_PlayerKilled(HTTPRequestHandle:response, bool:requestSuccessful, HTTPStatusCode:statusCode, any:data)
+{
+ if (!requestSuccessful)
+ {
+ LogError("Stats server returned error. Status code: %d", statusCode);
+ return;
+ }
+
+ ResetPack(data);
+
+ new clientID = ReadPackCell(data);
+ new attackerID = ReadPackCell(data);
+ new assisterID = ReadPackCell(data);
+
+ new client = GetClientOfUserId(clientID);
+ new attacker;
+
+ if (attackerID > 0)
+ {
+ attacker = GetClientOfUserId(attackerID);
+ }
+
+ new assister;
+
+ if (assisterID > 0)
+ {
+ assister = GetClientOfUserId(assisterID);
+ }
+
+ new String:clientName[MAX_NAME_LENGTH+1];
+ new String:attackerName[MAX_NAME_LENGTH+1];
+ new String:assisterName[MAX_NAME_LENGTH+1];
+
+ ReadPackString(data, clientName, sizeof(clientName));
+ ReadPackString(data, attackerName, sizeof(attackerName));
+ ReadPackString(data, assisterName, sizeof(assisterName));
+
+ new bufferSize = Steam_GetHTTPResponseBodySize(response);
+ new String:buffer[bufferSize];
+ Steam_GetHTTPResponseBodyData(response, buffer, bufferSize);
+
+ new Handle:kvBuffer;
+ if (StringToKeyValues(kvBuffer, buffer, "PropHuntKillResponse"))
+ {
+ new clientPoints = KvGetNum(kvBuffer, "clientPoints", -1);
+ new killerPoints = KvGetNum(kvBuffer, "killerPoints", 2);
+ new assisterPoints = KvGetNum(kvBuffer, "assisterPoints", 1);
+
+ if (client > 0)
+ CPrintToChat(client, "%t", "#TF_PH_AlterScore_Death", attackerName, clientPoints*-1);
+
+ if (attacker > 0)
+ CPrintToChat(attacker, "%t", "#TF_PH_AlterScore_Kill", clientName, killerPoints);
+
+ if (assister > 0)
+ CPrintToChat(assister, "%t", "#TF_PH_AlterScore_Assist", clientName, assisterPoints);
+
+
+ // Do stuff with the keyvalues
+ }
+}
+
+public Action:Timer_Score(Handle:Timer)
+{
+ for (new client = 1; client <= MaxClients; client++)
+ {
+ if (IsClientInGame(client) && IsPlayerAlive(client) && GetClientTeam(client) == TEAM_PROP)
+ {
+ new String:url[255];
+ Format(url, sizeof(url), "%s/proptimescore", server);
+
+ new HTTPRequestHandle:request = Steam_CreateHTTPRequest(HTTPMethod_POST, url);
+ Steam_SetHTTPRequestGetOrPostParameter(request, "serverid", g_sServerID);
+ // Steam_SetHTTPRequestGetOrPostParameter(request, "format", "vdf");
+ Steam_SetHTTPRequestGetOrPostParameter(request, "steamid", g_sSteam2IDs[client]);
+ Steam_SendHTTPRequest(request, Response_ErrorOnly);
+ }
+ }
+}
diff --git a/updater/dev/prophunt.txt b/updater/dev/prophunt.txt
new file mode 100644
index 0000000..658df0b
--- /dev/null
+++ b/updater/dev/prophunt.txt
@@ -0,0 +1,116 @@
+"Updater"
+{
+ "Information"
+ {
+ "Version"
+ {
+ "Latest" "3.4.0.0"
+ "Previous" "3.3.3"
+ }
+
+ "Notes" "Added updater support"
+ "Notes" "Added workshop map support"
+ "Notes" "Removed DHooks dependency"
+ "Notes" "Removed TF2Attributes dependency"
+ "Notes" "Override First Blood with our own system"
+ "Notes" "Add an option to enable respawning during Setup (on by default)"
+
+ "Notes" "See https://forums.alliedmods.net/showthread.php?t=228086 for full changelog."
+ }
+
+ "Files"
+ {
+ "Patch"
+ {
+ "Plugin" "Path_SM/plugins/prophunt.smx"
+ "Plugin" "Path_SM/data/prophunt_config.cfg"
+ "Plugin" "Path_SM/gamedata/tf2-switch-teams.txt"
+
+ "Source" "Path_SM/scripting/prophunt.sp"
+ }
+
+ "Plugin" "Path_SM/plugins/prophunt.smx"
+
+ "Plugin" "Path_SM/translations/prophunt.phrases.txt"
+
+ "Plugin" "Path_SM/gamedata/tf2-switch-teams.txt"
+
+ "Plugin" "Path_SM/data/prophunt_config.cfg"
+ "Plugin" "Path_SM/data/prop_common.txt"
+ "Plugin" "Path_SM/data/prop_menu.txt"
+ "Plugin" "Path_SM/data/props_allmaps.txt"
+
+ "Plugin" "Path_SM/data/maps/arena_brawl.cfg"
+ "Plugin" "Path_SM/data/maps/arena_concord.cfg"
+ "Plugin" "Path_SM/data/maps/arena_desolation.cfg"
+ "Plugin" "Path_SM/data/maps/arena_devils.cfg"
+ "Plugin" "Path_SM/data/maps/arena_farm.cfg"
+ "Plugin" "Path_SM/data/maps/arena_goldtooth.cfg"
+ "Plugin" "Path_SM/data/maps/arena_harvest.cfg"
+ "Plugin" "Path_SM/data/maps/arena_ravage.cfg"
+ "Plugin" "Path_SM/data/maps/arena_sawmill.cfg"
+ "Plugin" "Path_SM/data/maps/arena_storm.cfg"
+ "Plugin" "Path_SM/data/maps/ph_007facility.cfg"
+ "Plugin" "Path_SM/data/maps/ph_amazon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_basalt.cfg"
+ "Plugin" "Path_SM/data/maps/ph_bigbutt.cfg"
+ "Plugin" "Path_SM/data/maps/ph_campsite.cfg"
+ "Plugin" "Path_SM/data/maps/ph_canyon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cargo.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cchotel.cfg"
+ "Plugin" "Path_SM/data/maps/ph_chapel.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cliffface.cfg"
+ "Plugin" "Path_SM/data/maps/ph_courtyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_crater.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cyberpunk.cfg"
+ "Plugin" "Path_SM/data/maps/ph_devils.cfg"
+ "Plugin" "Path_SM/data/maps/ph_dockyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_farm.cfg"
+ "Plugin" "Path_SM/data/maps/ph_goldtooth.cfg"
+ "Plugin" "Path_SM/data/maps/ph_gorge.cfg"
+ "Plugin" "Path_SM/data/maps/ph_grassland.cfg"
+ "Plugin" "Path_SM/data/maps/ph_hanger18.cfg"
+ "Plugin" "Path_SM/data/maps/ph_harvest.cfg"
+ "Plugin" "Path_SM/data/maps/ph_headquarters.cfg"
+ "Plugin" "Path_SM/data/maps/ph_hilltop.cfg"
+ "Plugin" "Path_SM/data/maps/ph_kakariko.cfg"
+ "Plugin" "Path_SM/data/maps/ph_laboratory.cfg"
+ "Plugin" "Path_SM/data/maps/ph_lakeside.cfg"
+ "Plugin" "Path_SM/data/maps/ph_lumberyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_manor.cfg"
+ "Plugin" "Path_SM/data/maps/ph_manorgrounds.cfg"
+ "Plugin" "Path_SM/data/maps/ph_maze.cfg"
+ "Plugin" "Path_SM/data/maps/ph_medieval.cfg"
+ "Plugin" "Path_SM/data/maps/ph_mountain.cfg"
+ "Plugin" "Path_SM/data/maps/ph_nightclub.cfg"
+ "Plugin" "Path_SM/data/maps/ph_northural.cfg"
+ "Plugin" "Path_SM/data/maps/ph_oasis.cfg"
+ "Plugin" "Path_SM/data/maps/ph_otherside.cfg"
+ "Plugin" "Path_SM/data/maps/ph_petitcourtyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_range.cfg"
+ "Plugin" "Path_SM/data/maps/ph_redquarters.cfg"
+ "Plugin" "Path_SM/data/maps/ph_redstone.cfg"
+ "Plugin" "Path_SM/data/maps/ph_sawmill.cfg"
+ "Plugin" "Path_SM/data/maps/ph_snowworks.cfg"
+ "Plugin" "Path_SM/data/maps/ph_spookycanyon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_spookyharvest.cfg"
+ "Plugin" "Path_SM/data/maps/ph_switcheroo.cfg"
+ "Plugin" "Path_SM/data/maps/ph_target.cfg"
+ "Plugin" "Path_SM/data/maps/ph_timbertown.cfg"
+ "Plugin" "Path_SM/data/maps/ph_town.cfg"
+ "Plugin" "Path_SM/data/maps/ph_trainset.cfg"
+ "Plugin" "Path_SM/data/maps/ph_tundra.cfg"
+ "Plugin" "Path_SM/data/maps/ph_warehouse.cfg"
+ "Plugin" "Path_SM/data/maps/ph_watchtower.cfg"
+ "Plugin" "Path_SM/data/maps/ph_watermill.cfg"
+ "Plugin" "Path_SM/data/maps/ph_well.cfg"
+ "Plugin" "Path_SM/data/maps/spooky_ravine.cfg"
+
+// Sounds never change
+// "Plugin" "Path_Mod/sound/prophunt/found.mp3"
+// "Plugin" "Path_Mod/sound/prophunt/oneandonly.mp3"
+// "Plugin" "Path_Mod/sound/prophunt/snaaake.mp3"
+
+ "Source" "Path_SM/scripting/prophunt.sp"
+ }
+}
\ No newline at end of file
diff --git a/updater/prophunt.txt b/updater/prophunt.txt
new file mode 100644
index 0000000..aee12b7
--- /dev/null
+++ b/updater/prophunt.txt
@@ -0,0 +1,114 @@
+"Updater"
+{
+ "Information"
+ {
+ "Version"
+ {
+ "Latest" "3.3.3"
+ "Previous" "3.3.2"
+ }
+
+ "Notes" "Fix issue with DHooks causing crashes / not team switching"
+
+ "Notes" "See https://forums.alliedmods.net/showthread.php?t=228086 for full changelog."
+ }
+
+ "Files"
+ {
+ "Patch"
+ {
+ "Plugin" "Path_SM/plugins/prophunt.smx"
+
+ "Plugin" "Path_SM/gamedata/tf2-roundend.games.txt"
+
+ "Plugin" "Path_SM/data/maps/ph_blockfort.cfg"
+ "Plugin" "Path_SM/data/maps/ph_buckethunt.cfg"
+ }
+
+ "Plugin" "Path_SM/plugins/prophunt.smx"
+
+ "Plugin" "Path_SM/translations/prophunt.phrases.txt"
+
+ "Plugin" "Path_SM/gamedata/tf2-roundend.games.txt"
+
+ "Plugin" "Path_SM/data/prophunt_config.cfg"
+ "Plugin" "Path_SM/data/prop_common.txt"
+ "Plugin" "Path_SM/data/prop_menu.txt"
+ "Plugin" "Path_SM/data/props_allmaps.txt"
+
+ "Plugin" "Path_SM/data/maps/arena_brawl.cfg"
+ "Plugin" "Path_SM/data/maps/arena_concord.cfg"
+ "Plugin" "Path_SM/data/maps/arena_desolation.cfg"
+ "Plugin" "Path_SM/data/maps/arena_devils.cfg"
+ "Plugin" "Path_SM/data/maps/arena_farm.cfg"
+ "Plugin" "Path_SM/data/maps/arena_goldtooth.cfg"
+ "Plugin" "Path_SM/data/maps/arena_harvest.cfg"
+ "Plugin" "Path_SM/data/maps/arena_ravage.cfg"
+ "Plugin" "Path_SM/data/maps/arena_sawmill.cfg"
+ "Plugin" "Path_SM/data/maps/arena_storm.cfg"
+ "Plugin" "Path_SM/data/maps/ph_007facility.cfg"
+ "Plugin" "Path_SM/data/maps/ph_amazon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_basalt.cfg"
+ "Plugin" "Path_SM/data/maps/ph_bigbutt.cfg"
+ "Plugin" "Path_SM/data/maps/ph_blockfort.cfg"
+ "Plugin" "Path_SM/data/maps/ph_buckethunt.cfg"
+ "Plugin" "Path_SM/data/maps/ph_campsite.cfg"
+ "Plugin" "Path_SM/data/maps/ph_canyon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cargo.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cchotel.cfg"
+ "Plugin" "Path_SM/data/maps/ph_chapel.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cliffface.cfg"
+ "Plugin" "Path_SM/data/maps/ph_courtyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_crater.cfg"
+ "Plugin" "Path_SM/data/maps/ph_cyberpunk.cfg"
+ "Plugin" "Path_SM/data/maps/ph_devils.cfg"
+ "Plugin" "Path_SM/data/maps/ph_dockyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_farm.cfg"
+ "Plugin" "Path_SM/data/maps/ph_goldtooth.cfg"
+ "Plugin" "Path_SM/data/maps/ph_gorge.cfg"
+ "Plugin" "Path_SM/data/maps/ph_grassland.cfg"
+ "Plugin" "Path_SM/data/maps/ph_hanger18.cfg"
+ "Plugin" "Path_SM/data/maps/ph_harvest.cfg"
+ "Plugin" "Path_SM/data/maps/ph_headquarters.cfg"
+ "Plugin" "Path_SM/data/maps/ph_hilltop.cfg"
+ "Plugin" "Path_SM/data/maps/ph_kakariko.cfg"
+ "Plugin" "Path_SM/data/maps/ph_laboratory.cfg"
+ "Plugin" "Path_SM/data/maps/ph_lakeside.cfg"
+ "Plugin" "Path_SM/data/maps/ph_lumberyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_manor.cfg"
+ "Plugin" "Path_SM/data/maps/ph_manorgrounds.cfg"
+ "Plugin" "Path_SM/data/maps/ph_maze.cfg"
+ "Plugin" "Path_SM/data/maps/ph_medieval.cfg"
+ "Plugin" "Path_SM/data/maps/ph_mountain.cfg"
+ "Plugin" "Path_SM/data/maps/ph_nightclub.cfg"
+ "Plugin" "Path_SM/data/maps/ph_northural.cfg"
+ "Plugin" "Path_SM/data/maps/ph_oasis.cfg"
+ "Plugin" "Path_SM/data/maps/ph_otherside.cfg"
+ "Plugin" "Path_SM/data/maps/ph_petitcourtyard.cfg"
+ "Plugin" "Path_SM/data/maps/ph_range.cfg"
+ "Plugin" "Path_SM/data/maps/ph_redquarters.cfg"
+ "Plugin" "Path_SM/data/maps/ph_redstone.cfg"
+ "Plugin" "Path_SM/data/maps/ph_sawmill.cfg"
+ "Plugin" "Path_SM/data/maps/ph_snowworks.cfg"
+ "Plugin" "Path_SM/data/maps/ph_spookycanyon.cfg"
+ "Plugin" "Path_SM/data/maps/ph_spookyharvest.cfg"
+ "Plugin" "Path_SM/data/maps/ph_switcheroo.cfg"
+ "Plugin" "Path_SM/data/maps/ph_target.cfg"
+ "Plugin" "Path_SM/data/maps/ph_timbertown.cfg"
+ "Plugin" "Path_SM/data/maps/ph_town.cfg"
+ "Plugin" "Path_SM/data/maps/ph_trainset.cfg"
+ "Plugin" "Path_SM/data/maps/ph_tundra.cfg"
+ "Plugin" "Path_SM/data/maps/ph_warehouse.cfg"
+ "Plugin" "Path_SM/data/maps/ph_watchtower.cfg"
+ "Plugin" "Path_SM/data/maps/ph_watermill.cfg"
+ "Plugin" "Path_SM/data/maps/ph_well.cfg"
+ "Plugin" "Path_SM/data/maps/spooky_ravine.cfg"
+
+// Sounds never change
+// "Plugin" "Path_Mod/sound/prophunt/found.mp3"
+// "Plugin" "Path_Mod/sound/prophunt/oneandonly.mp3"
+// "Plugin" "Path_Mod/sound/prophunt/snaaake.mp3"
+
+ "Source" "Path_SM/scripting/prophunt.sp"
+ }
+}
\ No newline at end of file