From 3e11ddc3bc9bf568422a1ab511634c0d4899e267 Mon Sep 17 00:00:00 2001 From: Altair Sossai Date: Mon, 14 Oct 2024 16:07:41 -0300 Subject: [PATCH] add hats plugin --- addons/sourcemod/data/l4d_hats.cfg | 686 ++++ .../sourcemod/plugins/optional/l4d_hats.smx | Bin 0 -> 35962 bytes addons/sourcemod/scripting/l4d_hats.sp | 3185 +++++++++++++++++ .../translations/chi/hatnames.phrases.txt | 383 ++ .../translations/chi/hats.phrases.txt | 143 + .../translations/de/hats.phrases.txt | 143 + .../translations/es/hatnames.phrases.txt | 383 ++ .../translations/es/hats.phrases.txt | 143 + .../translations/hatnames.phrases.txt | 383 ++ .../sourcemod/translations/hats.phrases.txt | 143 + .../translations/ru/hats.phrases.txt | 142 + .../translations/ua/hats.phrases.txt | 142 + .../translations/zho/hatnames.phrases.txt | 383 ++ .../translations/zho/hats.phrases.txt | 143 + 14 files changed, 6402 insertions(+) create mode 100644 addons/sourcemod/data/l4d_hats.cfg create mode 100644 addons/sourcemod/plugins/optional/l4d_hats.smx create mode 100644 addons/sourcemod/scripting/l4d_hats.sp create mode 100644 addons/sourcemod/translations/chi/hatnames.phrases.txt create mode 100644 addons/sourcemod/translations/chi/hats.phrases.txt create mode 100644 addons/sourcemod/translations/de/hats.phrases.txt create mode 100644 addons/sourcemod/translations/es/hatnames.phrases.txt create mode 100644 addons/sourcemod/translations/es/hats.phrases.txt create mode 100644 addons/sourcemod/translations/hatnames.phrases.txt create mode 100644 addons/sourcemod/translations/hats.phrases.txt create mode 100644 addons/sourcemod/translations/ru/hats.phrases.txt create mode 100644 addons/sourcemod/translations/ua/hats.phrases.txt create mode 100644 addons/sourcemod/translations/zho/hatnames.phrases.txt create mode 100644 addons/sourcemod/translations/zho/hats.phrases.txt diff --git a/addons/sourcemod/data/l4d_hats.cfg b/addons/sourcemod/data/l4d_hats.cfg new file mode 100644 index 000000000..6b346b950 --- /dev/null +++ b/addons/sourcemod/data/l4d_hats.cfg @@ -0,0 +1,686 @@ +"Models" +{ + "1" + { + "mod" "models/infected/gibs/gibs.mdl" + "loc" "2.000000 0.500000 0.500000" + "Name" "Hand Gib" + } + "2" + { + "mod" "models/infected/limbs/exploded_boomer_head.mdl" + "Name" "Boomer Head" + } + "3" + { + "mod" "models/infected/limbs/limb_male_head01.mdl" + "loc" "0.000000 0.000000 -5.000000" + "Name" "Head Gib" + } + "4" + { + "mod" "models/props/cs_militia/circularsaw01.mdl" + "loc" "-4.000000 1.500000 1.500000" + "ang" "0.000000 80.000000 0.000000" + "Name" "Circular Saw" + } + "5" + { + "mod" "models/props/de_nuke/emergency_lighta.mdl" + "loc" "-8.000000 -0.500000 11.500000" + "ang" "-30.000000 0.000000 0.000000" + "Name" "Red Emergency Light" + } + "6" + { + "mod" "models/props_fairgrounds/alligator.mdl" + "loc" "-3.000000 0.500000 0.500000" + "Name" "Alligator" + } + "7" + { + "mod" "models/props_fairgrounds/mr_mustachio.mdl" + "loc" "-2.000000 0.000000 5.000000" + "Name" "Mr Mustachio" + } + "8" + { + "mod" "models/props_fortifications/orange_cone001_clientside.mdl" + "ang" "0.000000 60.000000 -40.000000" + "loc" "-3.000000 0.000000 1.000000" + "Name" "Traffic Cone" + } + "9" + { + "mod" "models/props_interiors/teddy_bear.mdl" + "loc" "-10.000000 0.000000 8.000000" + "ang" "-10.000000 0.000000 0.000000" + "Name" "Teddy Bear" + } + "10" + { + "mod" "models/props_interiors/toilet_b_breakable01_part13.mdl" + "loc" "-34.000000 0.500000 -14.000000" + "Name" "Toilet Seat" + } + "11" + { + "mod" "models/props_interiors/waterbottle.mdl" + "ang" "180.000000 60.000000 0.000000" + "loc" "-2.500000 -0.500000 26.000000" + "Name" "Water bottle" + } + "12" + { + "mod" "models/props_urban/dock_pylon_cap001.mdl" + "loc" "-4.000000 0.000000 10.500000" + "Name" "Dock Pylon" + } + "13" + { + "mod" "models/props_urban/life_ring001.mdl" + "loc" "-2.500000 1.000000 6.000000" + "Name" "Life Ring" + } + "14" + { + "mod" "models/props_lighting/light_construction02.mdl" + "ang" "-200.000000 0.000000 0.000000" + "loc" "-7.000000 0.000000 9.000000" + "Name" "Construction Light" + } + "15" + { + "mod" "models/extras/info_speech.mdl" + "loc" "-3.500000 0.000000 18.500000" + "Name" "Speech Quote" + } + "16" + { + "mod" "models/infected/smoker_tongue_attach.mdl" + "ang" "0.000000 -60.000000 180.000000" + "loc" "-5.500000 -2.500000 48.000000" + "Name" "Smoker Tongue" + } + "17" + { + "mod" "models/props/de_inferno/ceiling_fan_blade.mdl" + "loc" "-2.000000 -0.500000 6.000000" + "ang" "0.000000 40.000000 0.000000" + "Name" "Ceiling Fan Blades" + } + "18" + { + "mod" "models/f18/f18_placeholder.mdl" + "ang" "0.000000 180.000000 0.000000" + "loc" "-5.500000 0.000000 6.000000" + "Name" "F-18 Jet Plane" + } + "19" + { + "mod" "models/deadbodies/dead_male_civilian_radio.mdl" + "ang" "30.000000 10.000000 -60.000000" + "loc" "-3.000000 -7.000000 -1.000000" + "Name" "Civilian Radio" + } + "20" + { + "mod" "models/infected/cim_riot_faceplate.mdl" + "ang" "90.000000 0.000000 0.000000" + "loc" "-5.500000 0.000000 -66.000000" + "Name" "Riot Faceplate" + } + "21" + { + "mod" "models/props_interiors/styrofoam_cups.mdl" + "ang" "-20.000000 0.000000 0.000000" + "loc" "-9.000000 0.000000 18.000000" + "Name" "Styrofoam Cups" + } + "22" + { + "mod" "models/props_shacks/bug_lamp01.mdl" + "ang" "0.000000 180.000000 0.000000" + "loc" "-4.000000 0.000000 5.500000" + "Name" "Bug Lamp Zapper" + } + "23" + { + "mod" "models/props_unique/grill_campground.mdl" + "ang" "0.000000 180.000000 0.000000" + "loc" "-3.500000 0.000000 -30.000000" + "Name" "BBQ Grill" + } + "24" + { + "mod" "models/props_unique/luggagecart01.mdl" + "ang" "90.000000 0.000000 0.000000" + "loc" "-20.000000 0.000000 -9.000000" + "Name" "Luggage Cart" + } + "25" + { + "mod" "models/props_unique/spawn_apartment/lantern.mdl" + "loc" "-4.000000 0.000000 5.500000" + "Name" "Lantern" + } + "26" + { + "mod" "models/props_urban/exit_sign002.mdl" + "loc" "-4.000000 0.000000 12.500000" + "Name" "Exit Sign" + } + "27" + { + "mod" "models/props_urban/garden_hose001.mdl" + "ang" "40.000000 0.000000 0.000000" + "loc" "-8.000000 0.000000 2.500000" + "Name" "Garden Hose" + } + "28" + { + "mod" "models/props_urban/plastic_flamingo001.mdl" + "loc" "-5.500000 0.000000 -12.000000" + "ang" "0.000000 0.000000 0.000000" + "Name" "Pink Flamingo" + } + "29" + { + "mod" "models/props_waterfront/money_pile.mdl" + "ang" "-20.000000 0.000000 0.000000" + "loc" "-6.000000 9.000000 5.000000" + "Name" "Money Pile" + } + "30" + { + "mod" "models/props_fairgrounds/elephant.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-2.500000 0.000000 6.500000" + "Name" "Elephant" + } + "31" + { + "mod" "models/props_fairgrounds/giraffe.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-2.500000 0.000000 3.500000" + "Name" "Giraffe" + } + "32" + { + "mod" "models/props_fairgrounds/garbage_popcorn_box.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-4.000000 0.000000 7.000000" + "Name" "Popcorn Box" + } + "33" + { + "mod" "models/props_fairgrounds/gnome_closet.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-8.500000 -9.000000 -20.000000" + "Name" "Gnome Closet" + } + "34" + { + "mod" "models/props_fairgrounds/snake.mdl" + "ang" "-80.000000 0.000000 0.000000" + "loc" "-10.000000 -5.500000 -37.500000" + "Name" "Snake" + } + "35" + { + "mod" "models/props_interiors/toaster.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-4.500000 0.000000 7.500000" + "Name" "Toaster" + } + "36" + { + "mod" "models/props_unique/doll01.mdl" + "ang" "60.000000 0.000000 0.000000" + "loc" "-6.000000 0.000000 8.500000" + "Name" "Doll" + } + "37" + { + "mod" "models/props_junk/garbage_hubcap01a.mdl" + "ang" "-30.000000 0.000000 0.000000" + "loc" "-6.500000 0.000000 8.500000" + "Name" "Hub Cap" + } + "38" + { + "mod" "models/props_mall/mall_mannequin_rhand.mdl" + "ang" "60.000000 30.000000 70.000000" + "loc" "-5.500000 5.500000 6.500000" + "Name" "Mannequin Hand" + } + "39" + { + "mod" "models/props_lab/desklamp01.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-11.500000 0.000000 6.000000" + "Name" "Desk Lamp" + } + "40" + { + "mod" "models/hybridphysx/news_helicoptor_map1_intro_v1.mdl" + "ang" "0.000000 10.000000 0.000000" + "loc" "21.000000 6.000000 -0.500000" + "Name" "News Helicopter" + "size" "0.060000" + } + "41" + { + "mod" "models/missiles/f18_agm65maverick.mdl" + "ang" "10.000000 0.000000 0.000000" + "loc" "-19.000000 1.000000 8.500000" + "Name" "Missile" + "size" "0.200000" + } + "42" + { + "mod" "models/props_vehicles/boat_fishing02.mdl" + "ang" "0.000000 -90.000000 0.000000" + "loc" "-5.500000 0.000000 8.500000" + "Name" "Fishing Boat" + "size" "0.040000" + } + "43" + { + "mod" "models/props_urban/big_wheel001.mdl" + "ang" "-130.000000 -190.000000 10.000000" + "loc" "-6.000000 -1.000000 16.000000" + "Name" "Childs Trike" + "size" "0.060000" + } + "44" + { + "mod" "models/props_interiors/mounteddeerhead01.mdl" + "ang" "-20.000000 -10.000000 0.000000" + "loc" "-7.500000 1.000000 -0.500000" + "Name" "Deer Head" + "size" "0.060000" + } + "45" + { + "mod" "models/props_misc/pot-1.mdl" + "ang" "150.000000 10.000000 10.000000" + "loc" "-4.500000 1.000000 3.500000" + "Name" "Saucepan" + "size" "0.060000" + } + "46" + { + "mod" "models/props_c17/metalpot002a.mdl" + "ang" "10.000000 150.000000 -200.000000" + "loc" "0.000000 -3.000000 4.500000" + "Name" "Frying Pan" + } + "47" + { + "mod" "models/gibs/hgibs.mdl" + "ang" "10.000000 0.000000 0.000000" + "loc" "-0.500000 0.000000 0.500000" + "Name" "Skull" + } + "48" + { + "mod" "models/props/cs_militia/caseofbeer01.mdl" + "ang" "-160.000000 0.000000 180.000000" + "loc" "-4.000000 0.000000 -5.500000" + "Name" "Case of Beer" + } + "49" + { + "mod" "models/props/cs_office/chair_office.mdl" + "ang" "0.000000 0.000000 180.000000" + "loc" "-4.000000 0.000000 24.500000" + "Name" "Office Chair" + } + "50" + { + "mod" "models/props_cemetery/crypts_top02.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-4.000000 0.000000 3.000000" + "Name" "Graveyard Cross" + } + "51" + { + "mod" "models/props_fairgrounds/kiddyland_riderocket_small.mdl" + "ang" "0.000000 100.000000 0.000000" + "loc" "-4.000000 0.000000 0.500000" + "Name" "Kiddyland Rocket" + "size" "0.230000" + } + "52" + { + "mod" "models/props_interiors/toilet_b_breakable01.mdl" + "ang" "0.000000 0.000000 180.000000" + "loc" "-29.500000 1.000000 20.500000" + "Name" "Toilet" + } + "53" + { + "mod" "models/props_misc/lamp-1_gib1.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-4.500000 1.000000 -13.000000" + "Name" "Lamp Shade" + } + "54" + { + "mod" "models/props_misc/saddle-1.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-5.500000 1.000000 -15.500000" + "Name" "Saddle" + } + "55" + { + "mod" "models/props_unique/wheelchair01.mdl" + "ang" "0.000000 0.000000 180.000000" + "loc" "-4.000000 0.000000 24.500000" + "Name" "Wheel Chair" + } + "56" + { + "mod" "models/props_urban/mega_phone001.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-16.500000 -0.500000 6.500000" + "Name" "Mega Phone" + } + "57" + { + "mod" "models/props/de_train/de_train_floodlights_01.mdl" + "ang" "-90.000000 -540.000000 -180.000000" + "loc" "-4.500000 0.000000 0.000000" + "Name" "Flood lights" + } + "58" + { + "mod" "models/props_vehicles/tire001c_car.mdl" + "ang" "50.000000 0.000000 0.000000" + "loc" "-4.000000 0.000000 1.500000" + "Name" "Car Tire" + } + "59" + { + "mod" "models/props/de_inferno/ceiling_fan.mdl" + "ang" "0.000000 0.000000 -180.000000" + "loc" "-3.000000 0.000000 2.000000" + "Name" "Ceiling Fan" + } + "60" + { + "mod" "models/props/de_prodigy/fan.mdl" + "ang" "0.000000 10.000000 0.000000" + "loc" "-3.000000 0.000000 5.500000" + "Name" "Fan" + } + "61" + { + "mod" "models/props_foliage/urban_pot_clay01.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-4.500000 0.500000 -6.500000" + "Name" "Pot Plant 1" + } + "62" + { + "mod" "models/props_foliage/urban_pot_clay02.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-5.000000 -0.500000 -5.500000" + "Name" "Pot Plant 2" + } + "63" + { + "mod" "models/props_downtown/ironing_board_flat.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-15.000000 0.000000 -23.500000" + "Name" "Ironing Board" + } + "64" + { + "mod" "models/props_collectables/coin.mdl" + "ang" "30.000000 -10.000000 -10.000000" + "loc" "-14.000000 -1.000000 -6.000000" + "Name" "Coin" + "size" "1.730000" + } + "65" + { + "mod" "models/infected/jockey.mdl" + "ang" "0.000000 0.000000 0.000000" + "loc" "-7.000000 0.000000 -6.000000" + "Name" "Jockey" + "size" "0.330000" + } + "66" + { + "mod" "models/items/l4d_gift.mdl" + "ang" "-10.000000 30.000000 -30.000000" + "loc" "-4.000000 0.000000 2.000000" + "Name" "Gift" + "size" "0.530000" + } + "67" + { + "mod" "models/props_street/firehydrant.mdl" + "ang" "-20.000000 0.000000 0.000000" + "loc" "-3.000000 0.000000 -2.500000" + "Name" "Fire Hydrant" + } + "68" + { + "mod" "models/props_junk/gnome.mdl" + "ang" "0.000000 -200.000000 0.000000" + "loc" "-6.500000 5.000000 9.000000" + "Name" "Gnome" + "size" "0.530000" + } + "69" + { + "mod" "models/bunny/b_sailboat.mdl" + "ang" "-10.000000 0.000000 0.000000" + "loc" "-4.500000 0.000000 4.000000" + "Name" "Sail Boat" + "size" "0.030000" + } + "70" + { + "mod" "models/props/de_nuke/light_red2.mdl" + "ang" "10.000000 -180.000000 180.000000" + "loc" "-5.500000 0.500000 3.000000" + "Name" "Red Bulkhead Light" + "size" "0.530000" + } + "71" + { + "mod" "models/props_buildables/mine.mdl" + "ang" "20.000000 -140.000000 20.000000" + "loc" "-4.000000 0.500000 3.000000" + "Name" "Wheel Hubcap" + "size" "1.230000" + } + "72" + { + "mod" "models/props_cemetery/crypts_top01.mdl" + "ang" "10.000000 -190.000000 0.000000" + "loc" "-2.500000 1.000000 0.500000" + "Name" "Graveyard Cross" + "size" "0.030000" + } + "73" + { + "mod" "models/props_misc/tea_pot-1.mdl" + "ang" "0.000000 -90.000000 30.000000" + "loc" "-3.000000 0.000000 4.000000" + "Name" "Tea Pot" + "size" "0.500000" + } + "74" + { + "mod" "models/props_fairgrounds/pyrotechnics_launcher.mdl" + "ang" "0.000000 -90.000000 20.000000" + "loc" "-4.000000 0.000000 1.000000" + "Name" "Pyrotechnics" + "size" "0.500000" + } + "75" + { + "mod" "models/props_interiors/coffee_maker.mdl" + "ang" "-10.000000 -10.000000 0.000000" + "loc" "-3.500000 0.000000 1.500000" + "Name" "Coffee Maker" + "size" "0.500000" + } + "76" + { + "mod" "models/props_interiors/pot01a.mdl" + "ang" "0.000000 -360.000000 0.000000" + "loc" "-3.500000 0.000000 6.000000" + "Name" "Kettle" + "size" "-0.100000" + } + "77" + { + "mod" "models/props_interiors/tv.mdl" + "ang" "0.000000 -280.000000 0.000000" + "loc" "-3.000000 0.000000 -6.000000" + "Name" "TV" + "size" "0.500000" + } + "78" + { + "mod" "models/props_interiors/lamp_floor_gib2.mdl" + "ang" "10.000000 -290.000000 -30.000000" + "loc" "27.000000 -21.500000 -62.000000" + "Name" "Lamp Shade 1" + "size" "0.500000" + } + "79" + { + "mod" "models/props_interiors/lamp_table02_gib2.mdl" + "ang" "0.000000 -280.000000 -40.000000" + "loc" "8.000000 -1.500000 -14.000000" + "Name" "Lamp Shade 2" + "size" "0.500000" + } + "80" + { + "mod" "models/props_lighting/semi_flush_001.mdl" + "ang" "0.000000 -280.000000 170.000000" + "loc" "-4.000000 0.000000 2.000000" + "Name" "Lamp Shade 3" + "size" "0.000000" + } + "81" + { + "mod" "models/props_junk/petfoodbag01.mdl" + "ang" "-50.000000 -180.000000 10.000000" + "loc" "-15.500000 -1.500000 -5.500000" + "Name" "Dog Food" + "size" "0.000000" + } + "82" + { + "mod" "models/props_lighting/flashlight_dropped_01.mdl" + "ang" "0.000000 -280.000000 170.000000" + "loc" "1.000000 -0.500000 4.000000" + "Name" "Dalek Flash Light" + "size" "0.500000" + } + "83" + { + "mod" "models/props_lighting/lightfixture03.mdl" + "ang" "-50.000000 -550.000000 -170.000000" + "loc" "3.000000 -1.500000 -8.000000" + "Name" "Ball Light" + "size" "0.500000" + } + "84" + { + "mod" "models/props_mall/cash_register.mdl" + "ang" "-10.000000 -360.000000 0.000000" + "loc" "-3.000000 0.000000 1.000000" + "Name" "Cash Register" + "size" "0.000000" + } + "85" + { + "mod" "models/props_placeable/tier1_guns_trophy.mdl" + "ang" "0.000000 -280.000000 0.000000" + "loc" "-3.500000 0.000000 1.500000" + "Name" "Gun Trophy 1" + "size" "0.700000" + } + "86" + { + "mod" "models/props_placeable/tier2_guns_trophy.mdl" + "ang" "10.000000 -270.000000 0.000000" + "loc" "-4.500000 -0.500000 -0.500000" + "Name" "Gun Trophy 2" + "size" "0.800000" + } + "87" + { + "mod" "models/weapons/arms/v_spitter_arms.mdl" + "ang" "-70.000000 0.000000 10.000000" + "loc" "-6.500000 -0.500000 1.500000" + "Name" "Spitter Arms" + "size" "0.300000" + } + "88" + { + "mod" "models/weapons/arms/v_jockey_arms.mdl" + "ang" "-70.000000 0.000000 10.000000" + "loc" "-7.500000 -0.500000 1.500000" + "Name" "Jockey Claws" + "size" "0.300000" + } + "89" + { + "mod" "models/v_models/weapons/v_claw_hulk.mdl" + "ang" "310.000000 540.000000 190.000000" + "loc" "-6.000000 0.000000 5.000000" + "Name" "Tank Claws" + "size" "0.200000" + } + "90" + { + "mod" "models/w_models/weapons/w_he_grenade.mdl" + "ang" "-20.000000 0.000000 10.000000" + "loc" "-3.500000 0.000000 5.500000" + "Name" "Grenade Shell" + "size" "2.300000" + } + "91" + { + "mod" "models/props_junk/propanecanister001a.mdl" + "ang" "10.000000 10.000000 0.000000" + "loc" "5.000000 -4.500000 1.000000" + "Name" "Propane Cylinder" + "size" "0.800000" + } + "92" + { + "mod" "models/w_models/weapons/w_eq_medkit.mdl" + "ang" "120.000000 -190.000000 260.000000" + "loc" "-5.000000 4.500000 2.000000" + "Name" "Medkit Cap" + "size" "1.100000" + } + "93" + { + "mod" "models/weapons/melee/w_riotshield.mdl" + "ang" "-10.000000 0.000000 0.000000" + "loc" "2.000000 -2.000000 0.000000" + "Name" "Riot Shield" + "size" "0.400000" + } + "94" + { + "mod" "models/props_skybox/boat_rescue_tug_sunshine.mdl" + "ang" "20.000000 -180.000000 0.000000" + "loc" "-3.500000 0.500000 2.500000" + "Name" "Tug Boat" + "size" "1.100000" + } +} diff --git a/addons/sourcemod/plugins/optional/l4d_hats.smx b/addons/sourcemod/plugins/optional/l4d_hats.smx new file mode 100644 index 0000000000000000000000000000000000000000..c3a89d2e8951d41339c2a4b9e3d903a59585a04a GIT binary patch literal 35962 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 Rbp>D1. +*/ + + + +#define PLUGIN_VERSION "1.51" + +/*====================================================================================== + Plugin Info: + +* Name : [L4D & L4D2] Hats +* Author : SilverShot +* Descrp : Attaches specified models to players above their head. +* Link : https://forums.alliedmods.net/showthread.php?t=153781 +* Plugins : https://sourcemod.net/plugins.php?exact=exact&sortby=title&search=1&author=Silvers + +======================================================================================== + Change Log: + +1.51 (31-May-2024) + - Fixed client not in game errors being thrown on player death. Thanks to "lzvs" for reporting. + - Fixed invalid entity errors being thrown. Thanks to "Voevoda" for reporting. + - Added Spanish translations. Thanks to "lechuga" for providing. + +1.50 (12-Mar-2024) + - Added a thirdperson camera detection for "m_hViewEntity". Thanks to "Marttt" for reporting. + - More fixes to keep the hat visible when changing between thirdperson modes (events, cvar, TP plugin). Thanks to "Yabi" for reporting. + +1.49 (27-Nov-2023) + - Fixed the hat showing when being healed by someone else. + - Fixed hats randomly showing in first person. Thanks to "Yabi" for reporting. + - Fixed the "l4d_hats_random" value "2" not saving across map changes. Thanks to "Yabi" for reporting. + +1.48 (24-Nov-2023) + - Fixed the hat showing after staggering when the stagger timer didn't reset (due to some plugins such as "Stagger Gravity"). + +1.47 (22-Nov-2023) + - Now shows hats in 3rd person view when healing someone, using a generator or opening a footlocker, and possibly more situations. Thanks to "Voevoda" for reporting. + - Fixed invalid handle errors caused by the last update. Should have worked, seems to be some weirdness with SourceMod. Thanks to "Voevoda" for reporting and "HarryPotter" for help. + +1.46 (07-Nov-2023) + - Now shows hats in 3rd person view when deploying upgrade ammo packs, staggering or recovering from a pounce/charge. Thanks to "Voevoda" for reporting. + +1.45a (27-Aug-2022) + - Updated "Russian" translation file. Thanks to "A1ekin" for making changes. + +1.45 (29-May-2022) + - Added public command "sm_hats" to display a menu of hat options. Thanks to "pan0s" for writing. + - Added public command "sm_hatall" to toggle the visibility of everyone's hats. Requested by "LordVGames". + - Added admin command "sm_hatallc" to toggle a clients visibility hats. Requested by "Krevik". + + - Added saving a players visibility of all hats from the new commands above. + - Added changes by "pan0s" to save the first and third person view of hats status. + - Added a new colors stock for printing text, supports {RED} and {BLUE} colors. Thanks to "pan0s" for providing. + - Added support for the "Ready-Up" plugin to hide or show the panel when using the Hats menu. Requested by "Krevik". + + - Changed commands "sm_hatshow", "sm_hatshowon" and "sm_hatshowoff" to only affect 1st person view, and 3rd person view with an optional argument "tp". + - Fixed crashing if a model was missing. Plugin now fails to load forcing a config fix. Thanks to "Dragokas" for reporting. + - Fixed hats not saving depending on the "l4d_hats_make" cvar value. + + - Thanks to "Krevik" and "pan0s" for help lots of help and testing. + - Thanks to "pan0s" for updating the Chinese translations. + - Thanks to "Impact" for updating the German translations. + - Thanks to "Dragokas" for updating the Russian and Ukrainian translations. + + - Translation files have updated. Please update or errors will occur and the plugin won't work. + +1.44 (15-Apr-2022) + - Changed command "sm_hat" to accept 3 letter or smaller words for partial matching, e.g. "sm_hat saw". + - Fixed command "sm_hatdel" not deleting the whole entry. + - Fixed command "sm_hat" changing other players hats after using "sm_hatc" command. Thanks to "kot4404" for reporting. + - Removed the "Big Helicopter" model from the data config. Too many reports of client-side glowing sprites remaining behind. + +1.43 (06-Feb-2022) + - Added a hackish fix for L4D2 not precaching models properly which has been causing stutter when using a model for the first time. + - Fixed menus closing when selecting a player with a userid greater than 999. Thanks to "Electr000999" for reporting. + +1.42 (16-Dec-2021) + - Fixed simple mistake from last update causing wrong menu listing when not using a "hatnames" translation. Thanks to "Mi.Cura" for reporting. + +1.41 (14-Dec-2021) + - Fixed spawning and respawning with a hat when it was turned off. Thanks to "kot4404" for reporting. + + - Changed the "hatnames.phrases.txt" translation file format for better modifications when adding or removing hats from the data config. + - Now supports adding hats and breaking the plugin when missing from the "hatnames.phrases.txt" translations file. + - New "hatnames" translations no longer uses indexes and only model names. + - Still supports the old version but suggest upgrading to the new. + - Included the script for converting the translation file based on the config. Search for "TRANSLATE CODE" in the source. + +1.40 (11-Dec-2021) + - Fixed not saving hat angles and origins correctly when "l4d_hats_wall" was set to "0". Thanks to "NoroHime" for reporting. + - Now saves when a hat was removed, if saving is enabled. Requested by "kot4404". + +1.39 (09-Dec-2021) + - Changed command "sm_hat" to accept "rand" or "random" as a parameter option to give a random hat. + - Updated the "chi" and "zho" translation "hatnames.phrases.txt" files to be correct. Thanks to "NoroHime". + +1.38 (03-Dec-2021) + - Added "Hat_Off" option to the menu. Requested by "kot4404". + - Fixed command "sm_hatadd" from not adding new entries. Thanks to "swiftswing1" for reporting. + - Changes to fix warnings when compiling on SourceMod 1.11. + +1.37 (09-Sep-2021) + - Plugin now deletes the client cookie and hat if they no longer have access to use hats. Requested by "Darkwob". + +1.36 (20-Jul-2021) + - Removed cvar "l4d_hats_view" - recommended to use "ThirdPersonShoulder_Detect" plugin to turn on/off the hat view when in 3rd/1st person view. + +1.35 (10-Jul-2021) + - Fixed giving random hats to players when the "l4d_hats_random" cvar was set to "0". Thanks to "XYZC" for reporting. + +1.34 (05-Jul-2021) + - Fixed giving random hats on round_start when "l4d_hats_save" cvar was set to "1". + +1.33 (04-Jul-2021) + - Fixed "sm_hatrand" and "sm_hatrandom" from not giving random hats. Not sure when this broke. + +1.32 (01-Jul-2021) + - Added a warning message to suggest installing the "Attachments API" and "Use Priority Patch" plugins if missing. + +1.31 (03-May-2021) + - Added Simplified Chinese (zho) and Traditional Chinese (chi) translations. Thanks to "pan0s" for providing. + - Fixed not giving random hats to clients who have no saved hats. Thanks to "pan0s" for reporting. + +1.30 (28-Apr-2021) + - Fixed client not in-game errors. Thanks to "HarryPotter" for reporting. + +1.29 (10-Apr-2021) + - Added cvar "l4d_hats_bots" to allow or disallow bots from spawning with hats. + - Added cvar "l4d_hats_make" to allow players with specific flags only to auto spawn with hats. + +1.28 (20-Mar-2021) + - Added cvar "l4d_hats_wall" to prevent hats glowing through walls. Thanks to "Marttt" for the method and "Dragokas" for requesting. + - Fixed personal hats not showing when changing hat in external view. + +1.27 (01-Mar-2021) + - Fixed invalid client errors due to the last update. Thanks to "ur5efj" for reporting. + +1.26 (01-Mar-2021) + - Now blocks showing hats when spectating someone in first person view. Thanks to "Alex101192" for reporting. + +1.25 (23-Feb-2021) + - Fixed hats not hiding after being revived. Thanks to "Alex101192" for reporting. + +1.24 (01-Oct-2020) + - Changed "l4d_hats_precache" cvar default value to blank. + - Changed the way "l4d_hats_detect" works. Now also detects if reviving someone (events were unreliable and causing bugs). + - Fixed 1st and 3rd person view of hats wrongfully toggling under certain conditions. Thanks to "Alex101192" for reporting. + - Fixed some spelling mistakes in the "data/l4d_hats.cfg" hat names. + +1.23 (16-Jun-2020) + - Added Russian and Ukrainian translations - Thanks to "Dragokas" for providing. + - Fixed changing hats when "l4d_hats_save" and "l4d_hats_random" were set. Random is superseded by saved if present. + - Fixed command "sm_hatclient" throwing an error when only a client was specified. + - Fixed hat view "ThirdPersonShoulder_Detect" and "Survivor Thirdperson" plugins clashing. + +1.22 (10-May-2020) + - Extra checks to prevent "IsAllowedGameMode" throwing errors. + - Fixed not always loading client cookies before creating hats. Thanks to "Alex101192" for reporting. + - Fixed potentially not translating some strings. + - Fixed some functions not working for more than 100 hats. + - Fixed hats affecting Survivor Thirdperson view under certain conditions. + - Various changes to tidy up code. + - Various optimizations and fixes. + +1.21 (30-Apr-2020) + - Added cvar "l4d_hats_detect" to enable clients to see their own hat when 3rd person view is detected. + - Optionally uses "ThirdPersonShoulder_Detect" plugin by "Lux" and "MasterMind420", if available. + + - Added bunch of maps to the default value of "l4d_hats_precache". Thanks to "Alex101192" for providing. + - Increased "l4d_hats_precache" cvar length, max usable length 490 (due to game limitations). + +1.20 (01-Apr-2020) + - Fixed "IsAllowedGameMode" from throwing errors when the "_tog" cvar was changed before MapStart. + - Removed "colors.inc" dependency. + - Updated these translation file encodings to UTF-8 (to display all characters correctly): German (de). + +1.19 (19-Dec-2019) + - Added command "sm_hatclient" to set a clients hat, requested by "foxhound27". + +1.18 (23-Oct-2019) + - Added commands "sm_hatshowon" and "sm_hatshowoff" to turn on/off personal hat visibility. + - Fixed cvar "l4d_hats_precache" from modifying the allow cvar. Now correctly disables on blocked maps. + +1.17 (10-Sep-2019) + - Added cvar "l4d_hats_precache" to prevent pre-caching models on specified maps. + +1.17b (19-Aug-2019) + - Fixed ghosts from having hats. + +1.16 (02-Aug-2019) + - Fixed "m_TimeForceExternalView not found" error for L4D1 - Thanks to "Ja-Forces" for reporting. + +1.15 (05-May-2018) + - Converted plugin source to the latest syntax utilizing methodmaps. Requires SourceMod 1.8 or newer. + - Changed cvar "l4d_hats_modes_tog" now supports L4D1. + +1.14 (25-Jun-2017) + - Added "Reset" option to the ang/pos/size menus, requested by "ZBzibing". + - Fixed depreciated FCVAR_PLUGIN and GetClientAuthString. + - Increased MAX_HATS value and added many extra L4D2 hats thanks to "Munch". + +1.13 (29-Mar-2015) + - Fixed the plugin not working in L4D1 due to a SetEntPropFloat property not found error. + +1.12 (07-Oct-2012) + - Fixed hats blocking players +USE by adding a single line of code - Thanks to "Machine". + +1.11 (02-Jul-2012) + - Fixed cvar "l4d_hats_random" from not working properly - Thanks to "Don't Fear The Reaper" for reporting. + +1.10 (20-Jun-2012) + - Added German translations - Thanks to "Don't Fear The Reaper". + - Small fixes. + +1.9.0 (22-May-2012) + - Fixed multiple hat changes only showing the first hat to players. + - Changing hats will no longer return the player to firstperson if thirdperson was already on. + +1.8.0 (21-May-2012) + - Fixed command "sm_hatc" making the client thirdpeson and not the target. + +1.7.0 (20-May-2012) + - Added cvar "l4d_hats_change" to put the player into thirdperson view when they select a hat, requested by "disawar1". + +1.6.1 (15-May-2012) + - Fixed a bug when printing to chat after changing someones hat. + - Fixed cvar "l4d_hats_menu" not allowing access if it was empty. + +1.6.0 (15-May-2012) + - Fixed the allow cvars not affecting everything. + +1.5.0 (10-May-2012) + - Added translations, required for the commands and menu title. + - Added optional translations for the hat names as requested by disawar1. + - Added cvar "l4d_hats_allow" to turn on/off the plugin. + - Added cvar "l4d_hats_modes" to control which game modes the plugin works in. + - Added cvar "l4d_hats_modes_off" same as above. + - Added cvar "l4d_hats_modes_tog" same as above, but only works for L4D2. + - Added cvar "l4d_hats_save" to save a players hat for next time they spawn or connect. + - Added command "sm_hatsize" to change the scale/size of hats as suggested by worminater. + - Fixed "l4d_hats_menu" flags not setting correctly. + - Optimized the plugin by hooking cvar changes. + - Selecting a hat from the menu no longer returns to the first page. + +1.4.3 (07-May-2011) + - Added "name" key to the config for reading hat names. + +1.4.2 (16-Apr-2011) + - Changed the way models are checked to exist and precached. + +1.4.1 (16-Apr-2011) + - Added new hat models to the config. Deleted and repositioned models blocking the "use" function. + - Changed the hat entity from prop_dynamic to prop_dynamic_override (allows physics models to be attached). + - Fixed command "sm_hatadd" causing crashes due to models not being pre-cached, cannot cache during a round, causes crash. + - Fixed pre-caching models which are missing (logs an error telling you an incorrect model is specified). + +1.4.0 (11-Apr-2011) + - Added cvar "l4d_hats_opaque" to set hat transparency. + - Changed cvar "l4d_hats_random" to create a random hat when survivors spawn. 0=Never. 1=On round start. 2=Only first spawn (keeps the same hat next round). + - Fixed hats changing when returning from idle. + - Replaced underscores (_) with spaces in the menu. + +1.3.4 (09-Apr-2011) + - Fixed hooking L4D2 events in L4D1. + +1.3.3 (07-Apr-2011) + - Fixed command "sm_hatc" not displaying for admins when they are dead/infected team. + - Minor bug fixes. + +1.3.2 (06-Apr-2011) + - Fixed command "sm_hatc" displaying invalid player. + +1.3.1 (05-Apr-2011) + - Fixed the fix of command "sm_hat" flags not applying. + +1.3.0 (05-Apr-2011) + - Fixed command "sm_hat" flags not applying. + +1.2.0 (03-Apr-2011) + - Added command "sm_hatoffc" for admins to disable hats on specific clients. + - Added cvar "l4d_hats_third" to control the previous update's addition. + +1.1.1a (03-Apr-2011) + - Added events to show / hide the hat when in third / first person view. + +1.1.1 (02-Apr-2011) + - Added cvar "l4d_hats_view" to toggle if a players hat is visible by default when they join. + - Resets variables for clients when they connect. + +1.1.0 (01-Apr-2011) + - Added command "sm_hatoff" - Toggle to turn on or off the ability of wearing hats. + - Added command "sm_hatadd" - To add models into the config. + - Added command "sm_hatdel" - To remove a model from the config. + - Added command "sm_hatlist" - To display a list of all models (for use with sm_hatdel). + +1.0.0 (29-Mar-2011) + - Initial release. + +======================================================================================*/ + +#pragma semicolon 1 + +#pragma newdecls required +#include +#include +#include +#include + +#define CVAR_FLAGS FCVAR_NOTIFY +#define CONFIG_SPAWNS "data/l4d_hats.cfg" +#define MAX_HATS 128 + + + +////////////////////////////////// +// Updated by pan0s +// #include +Handle g_hCookie_ThirdView, g_hCookie_FirstView; +bool g_bIsThirdPerson[MAXPLAYERS+1]; // View on TP +bool g_bHatViewTP[MAXPLAYERS+1]; // View on TP +////////////////////////////////// + +ConVar g_hCvarAllow, g_hCvarBots, g_hCvarChange, g_hCvarDetect, g_hCvarMake, g_hCvarMenu, g_hCvarModes, g_hCvarModesOff, g_hCvarModesTog, g_hCvarOpaq, g_hCvarPrecache, g_hCvarRand, g_hCvarSave, g_hCvarThird, g_hCvarWall; +ConVar g_hCvarMPGameMode, g_hPluginReadyUp; +Handle g_hCookie_Hat, g_hCookie_All; +Menu g_hMenu, g_hMenus[MAXPLAYERS+1]; +bool g_bCvarAllow, g_bMapStarted, g_bCvarBots, g_bCvarWall, g_bLeft4Dead2, g_bTranslation, g_bViewHooked, g_bValidMap; +int g_iCount, g_iCvarMake, g_iCvarMenu, g_iCvarOpaq, g_iCvarRand, g_iCvarSave, g_iCvarThird; +float g_fCvarChange, g_fCvarDetect; + +float g_fSize[MAX_HATS], g_vAng[MAX_HATS][3], g_vPos[MAX_HATS][3]; +char g_sModels[MAX_HATS][64], g_sNames[MAX_HATS][64]; +char g_sFlagsMake[32]; +char g_sFlagsMenu[32]; +char g_sSteamID[MAXPLAYERS+1][32]; // Stores client user id to determine if the blocked player is the same +int g_iHatIndex[MAXPLAYERS+1]; // Player hat entity reference +int g_iHatWalls[MAXPLAYERS+1]; // Hidden hat entity reference +int g_iSelected[MAXPLAYERS+1]; // The selected hat index (0 to MAX_HATS) +int g_iTarget[MAXPLAYERS+1]; // For admins to change clients hats +int g_iType[MAXPLAYERS+1]; // Stores selected hat to give players +int g_iMenuType[MAXPLAYERS+1]; // Admin var for menu +bool g_bHatAll[MAXPLAYERS+1] = {true, ...}; // Visibility of everyones hats (personal setting) +bool g_bHatView[MAXPLAYERS+1]; // Player view of hat on/off (personal setting) +bool g_bHatOff[MAXPLAYERS+1]; // Lets players turn their hats on/off +bool g_bBlocked[MAXPLAYERS+1]; // Determines if the player is blocked from hats +bool g_bExternalCvar[MAXPLAYERS+1]; // If thirdperson view was detected (thirdperson_shoulder cvar) +bool g_bExternalProp[MAXPLAYERS+1]; // If thirdperson view was detected (netprop or revive actions) +bool g_bExternalState[MAXPLAYERS+1]; // If thirdperson view was detected +bool g_bExternalChange[MAXPLAYERS+1]; // When changing hats, show in 3rd person +bool g_bCookieAuth[MAXPLAYERS+1]; // When cookies cached and client is authorized +Handle g_hTimerView[MAXPLAYERS+1]; // Thirdperson view when selecting hat +Handle g_hTimerDelay[MAXPLAYERS+1]; // Delayed return to 1st person +Handle g_hTimerDetect; + +// ReadyUP plugin +native bool ToggleReadyPanel(bool show, int target = 0); + + + +// ==================================================================================================== +// PLUGIN INFO / START / END +// ==================================================================================================== +public Plugin myinfo = +{ + name = "[L4D & L4D2] Hats", + author = "SilverShot", + description = "Attaches specified models to players above their head.", + version = PLUGIN_VERSION, + url = "https://forums.alliedmods.net/showthread.php?t=153781" +} + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +{ + EngineVersion test = GetEngineVersion(); + if( test == Engine_Left4Dead ) g_bLeft4Dead2 = false; + else if( test == Engine_Left4Dead2 ) g_bLeft4Dead2 = true; + else + { + strcopy(error, err_max, "Plugin only supports Left 4 Dead 1 & 2."); + return APLRes_SilentFailure; + } + + MarkNativeAsOptional("ToggleReadyPanel"); + + return APLRes_Success; +} + +public void OnAllPluginsLoaded() +{ + // Attachments API + if( FindConVar("attachments_api_version") == null && (FindConVar("l4d2_swap_characters_version") != null || FindConVar("l4d_csm_version") != null) ) + { + LogMessage("\n==========\nWarning: You should install \"[ANY] Attachments API\" to fix model attachments when changing character models: https://forums.alliedmods.net/showthread.php?t=325651\n==========\n"); + } + + // Use Priority Patch + if( FindConVar("l4d_use_priority_version") == null ) + { + LogMessage("\n==========\nWarning: You should install \"[L4D & L4D2] Use Priority Patch\" to fix attached models blocking +USE action: https://forums.alliedmods.net/showthread.php?t=327511\n==========\n"); + } + + g_hPluginReadyUp = FindConVar("l4d_ready_enabled"); +} + +public void OnPluginStart() +{ + // Load config + KeyValues hFile = OpenConfig(); + char sTemp[64]; + bool message; + + for( int i = 0; i < MAX_HATS; i++ ) + { + IntToString(i+1, sTemp, sizeof(sTemp)); + if( hFile.JumpToKey(sTemp) ) + { + hFile.GetString("mod", sTemp, sizeof(sTemp)); + + TrimString(sTemp); + if( sTemp[0] == 0 ) + break; + + if( FileExists(sTemp, true) ) + { + hFile.GetVector("ang", g_vAng[i]); + hFile.GetVector("loc", g_vPos[i]); + g_fSize[i] = hFile.GetFloat("size", 1.0); + g_iCount++; + + strcopy(g_sModels[i], sizeof(g_sModels[]), sTemp); + + hFile.GetString("name", g_sNames[i], sizeof(g_sNames[])); + + if( strlen(g_sNames[i]) == 0 ) + GetHatName(g_sNames[i], i); + } + else + { + message = true; + LogError("Cannot find the model '%s'.", sTemp); + } + + hFile.Rewind(); + } + } + + if( message ) + { + SetFailState("\n==========\nWarning: Please fix your \"%s\" config. Missing models detected.\n==========\n", CONFIG_SPAWNS); + } + + delete hFile; + + if( g_iCount == 0 ) + SetFailState("No models wtf?!"); + + + + // Transactions + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, PLATFORM_MAX_PATH, "translations/hatnames.phrases.txt"); + g_bTranslation = FileExists(sPath); + + if( g_bTranslation ) + LoadTranslations("hatnames.phrases"); + LoadTranslations("hats.phrases"); + LoadTranslations("core.phrases"); + LoadTranslations("common.phrases"); + + + + // Hats menu + if( g_bTranslation == false ) + { + g_hMenu = new Menu(HatMenuHandler); + g_hMenu.AddItem("HAT_DISABLED", "HAT_DISABLED"); + + for( int i = 0; i < g_iCount; i++ ) + g_hMenu.AddItem(g_sModels[i], g_sNames[i]); + g_hMenu.SetTitle("%t", "Hat_Menu_Title"); + g_hMenu.ExitBackButton = true; + g_hMenu.ExitButton = true; + } + + + + // Cvars + g_hCvarAllow = CreateConVar( "l4d_hats_allow", "1", "0=Plugin off, 1=Plugin on.", CVAR_FLAGS ); + g_hCvarBots = CreateConVar( "l4d_hats_bots", "1", "0=Disallow bots from spawning with Hats. 1=Allow bots to spawn with hats.", CVAR_FLAGS, true, 0.0, true, 1.0 ); + g_hCvarChange = CreateConVar( "l4d_hats_change", "1.3", "0=Off. Other value puts the player into thirdperson for this many seconds when selecting a hat.", CVAR_FLAGS ); + g_hCvarDetect = CreateConVar( "l4d_hats_detect", "0.3", "0.0=Off. How often to detect thirdperson view. Also uses ThirdPersonShoulder_Detect plugin if available.", CVAR_FLAGS ); + g_hCvarMake = CreateConVar( "l4d_hats_make", "", "Specify admin flags or blank to allow all players to spawn with a hat, requires the l4d_hats_random cvar to spawn.", CVAR_FLAGS ); + g_hCvarMenu = CreateConVar( "l4d_hats_menu", "", "Specify admin flags or blank to allow all players access to the hats menu.", CVAR_FLAGS ); + g_hCvarModes = CreateConVar( "l4d_hats_modes", "", "Turn on the plugin in these game modes, separate by commas (no spaces). (Empty = all).", CVAR_FLAGS ); + g_hCvarModesOff = CreateConVar( "l4d_hats_modes_off", "", "Turn off the plugin in these game modes, separate by commas (no spaces). (Empty = none).", CVAR_FLAGS ); + g_hCvarModesTog = CreateConVar( "l4d_hats_modes_tog", "", "Turn on the plugin in these game modes. 0=All, 1=Coop, 2=Survival, 4=Versus, 8=Scavenge. Add numbers together.", CVAR_FLAGS ); + g_hCvarOpaq = CreateConVar( "l4d_hats_opaque", "255", "How transparent or solid should the hats appear. 0=Translucent, 255=Opaque.", CVAR_FLAGS, true, 0.0, true, 255.0 ); + g_hCvarPrecache = CreateConVar( "l4d_hats_precache", "", "Prevent pre-caching models on these maps, separate by commas (no spaces). Enabling plugin on these maps will crash the server.", CVAR_FLAGS ); + g_hCvarRand = CreateConVar( "l4d_hats_random", "1", "Attach a random hat when survivors spawn. 0=Never. 1=On round start. 2=Only first spawn (keeps the same hat next round).", CVAR_FLAGS, true, 0.0, true, 2.0 ); + g_hCvarSave = CreateConVar( "l4d_hats_save", "1", "0=Off, 1=Save the players selected hats and attach when they spawn or rejoin the server. Overrides the random setting.", CVAR_FLAGS, true, 0.0, true, 1.0 ); + g_hCvarThird = CreateConVar( "l4d_hats_third", "1", "0=Off, 1=When a player is in third person view, display their hat. Hide when in first person view.", CVAR_FLAGS, true, 0.0, true, 1.0 ); + g_hCvarWall = CreateConVar( "l4d_hats_wall", "1", "0=Show hats glowing through walls, 1=Hide hats glowing when behind walls (creates 1 extra entity per hat).", CVAR_FLAGS, true, 0.0, true, 1.0 ); + CreateConVar( "l4d_hats_version", PLUGIN_VERSION, "Hats plugin version.", FCVAR_NOTIFY|FCVAR_DONTRECORD); + AutoExecConfig(true, "l4d_hats"); + + g_hCvarMPGameMode = FindConVar("mp_gamemode"); + g_hCvarMPGameMode.AddChangeHook(ConVarChanged_Allow); + g_hCvarAllow.AddChangeHook(ConVarChanged_Allow); + g_hCvarModes.AddChangeHook(ConVarChanged_Allow); + g_hCvarModesOff.AddChangeHook(ConVarChanged_Allow); + g_hCvarModesTog.AddChangeHook(ConVarChanged_Allow); + g_hCvarBots.AddChangeHook(ConVarChanged_Cvars); + g_hCvarChange.AddChangeHook(ConVarChanged_Cvars); + g_hCvarDetect.AddChangeHook(ConVarChanged_Cvars); + g_hCvarMake.AddChangeHook(ConVarChanged_Cvars); + g_hCvarMenu.AddChangeHook(ConVarChanged_Cvars); + g_hCvarRand.AddChangeHook(ConVarChanged_Cvars); + g_hCvarSave.AddChangeHook(ConVarChanged_Cvars); + g_hCvarWall.AddChangeHook(ConVarChanged_Cvars); + g_hCvarOpaq.AddChangeHook(CvarChangeOpac); + g_hCvarThird.AddChangeHook(CvarChangeThird); + + + + // Commands + RegConsoleCmd("sm_hats", CmdHatMain, "Displays a menu to customize various settings for hats."); + RegConsoleCmd("sm_hat", CmdHat, "Displays a menu of hats allowing players to change what they are wearing. Optional args: [0 - 128 or hat name or \"random\"]"); + RegConsoleCmd("sm_hatoff", CmdHatOff, "Toggle to turn on or off the ability of wearing hats."); + RegConsoleCmd("sm_hatshow", CmdHatShow, "Toggle to see or hide your own hat. Applies to first person view or third person using the optional command argument \"tp\" e.g. \"sm_hatshow tp\""); + RegConsoleCmd("sm_hatview", CmdHatShow, "Toggle to see or hide your own hat. Applies to first person view or third person using the optional command argument \"tp\" e.g. \"sm_hatview tp\""); + RegConsoleCmd("sm_hatshowon", CmdHatShowOn, "See your own hat. Applies to first person view or third person using the optional command argument \"tp\" e.g. \"sm_hatshowon tp\""); + RegConsoleCmd("sm_hatshowoff", CmdHatShowOff, "Hide your own hat. Applies to first person view or third person using the optional command argument \"tp\" e.g. \"sm_hatshowoff tp\""); + RegConsoleCmd("sm_hatall", CmdHatsToggle, "Toggles the visibility of everyone's hats."); + RegAdminCmd("sm_hatclient", CmdHatClient, ADMFLAG_ROOT, "Set a clients hat. Usage: sm_hatclient <#userid|name> [hat name or hat index: 0-128 (MAX_HATS)]."); + RegAdminCmd("sm_hatoffc", CmdHatOffTarget, ADMFLAG_ROOT, "Toggle the ability of wearing hats on specific players."); + RegAdminCmd("sm_hatallc", CmdHatAllTarget, ADMFLAG_ROOT, "Toggle the visibility of all hats on specific players."); + RegAdminCmd("sm_hatc", CmdHatTarget, ADMFLAG_ROOT, "Displays a menu listing players, select one to change their hat."); + RegAdminCmd("sm_hatrandom", CmdHatRand, ADMFLAG_ROOT, "Randomizes all players hats."); + RegAdminCmd("sm_hatrand", CmdHatRand, ADMFLAG_ROOT, "Randomizes all players hats."); + RegAdminCmd("sm_hatadd", CmdHatAdd, ADMFLAG_ROOT, "Adds specified model to the config (must be the full model path)."); + RegAdminCmd("sm_hatdel", CmdHatDel, ADMFLAG_ROOT, "Removes a model from the config (either by index or partial name matching)."); + RegAdminCmd("sm_hatlist", CmdHatList, ADMFLAG_ROOT, "Displays a list of all the hat models (for use with sm_hatdel)."); + RegAdminCmd("sm_hatsave", CmdHatSave, ADMFLAG_ROOT, "Saves the hat position and angels to the hat config."); + RegAdminCmd("sm_hatload", CmdHatLoad, ADMFLAG_ROOT, "Changes all players hats to the one you have."); + RegAdminCmd("sm_hatang", CmdAng, ADMFLAG_ROOT, "Shows a menu allowing you to adjust the hat angles (affects all hats/players)."); + RegAdminCmd("sm_hatpos", CmdPos, ADMFLAG_ROOT, "Shows a menu allowing you to adjust the hat position (affects all hats/players)."); + RegAdminCmd("sm_hatsize", CmdHatSize, ADMFLAG_ROOT, "Shows a menu allowing you to adjust the hat size (affects all hats/players)."); + + g_hCookie_Hat = RegClientCookie("l4d_hats", "Hat Type", CookieAccess_Protected); + g_hCookie_All = RegClientCookie("l4d_hats_all", "General Hats Visibility", CookieAccess_Protected); + + // Updated by pan0s + g_hCookie_FirstView = RegClientCookie("l4d_hats_fv", "Hats First person View", CookieAccess_Protected); + g_hCookie_ThirdView = RegClientCookie("l4d_hats_tv", "Hats Third person View", CookieAccess_Protected); +} + +public void OnPluginEnd() +{ + for( int i = 1; i <= MaxClients; i++ ) + RemoveHat(i); +} + + + +// ==================================================================================================== +// CVARS +// ==================================================================================================== +public void OnConfigsExecuted() +{ + IsAllowed(); +} + +void ConVarChanged_Allow(Handle convar, const char[] oldValue, const char[] newValue) +{ + IsAllowed(); +} + +void ConVarChanged_Cvars(Handle convar, const char[] oldValue, const char[] newValue) +{ + GetCvars(); +} + +void GetCvars() +{ + g_hCvarMake.GetString(g_sFlagsMake, sizeof(g_sFlagsMake)); + g_iCvarMake = ReadFlagString(g_sFlagsMake); + g_hCvarMenu.GetString(g_sFlagsMenu, sizeof(g_sFlagsMenu)); + g_iCvarMenu = ReadFlagString(g_sFlagsMenu); + g_bCvarBots = g_hCvarBots.BoolValue; + g_fCvarChange = g_hCvarChange.FloatValue; + g_fCvarDetect = g_hCvarDetect.FloatValue; + g_iCvarOpaq = g_hCvarOpaq.IntValue; + g_iCvarRand = g_hCvarRand.IntValue; + g_iCvarSave = g_hCvarSave.IntValue; + g_iCvarThird = g_hCvarThird.IntValue; + g_bCvarWall = g_hCvarWall.BoolValue; +} + +void IsAllowed() +{ + bool bCvarAllow = g_hCvarAllow.BoolValue; + bool bAllowMode = IsAllowedGameMode(); + GetCvars(); + + if( g_bCvarAllow == false && bCvarAllow == true && bAllowMode == true && g_bValidMap == true ) + { + g_bCvarAllow = true; + + if( g_iCvarThird ) + HookViewEvents(); + HookEvents(); + SpectatorHatHooks(); + + for( int i = 1; i <= MaxClients; i++ ) + { + g_bHatView[i] = false; + g_iSelected[i] = GetRandomInt(0, g_iCount -1); + } + + if( g_iCvarRand || g_iCvarSave ) + { + int clientID; + + for( int i = 1; i <= MaxClients; i++ ) + { + if( IsClientInGame(i) && GetClientTeam(i) == 2 ) + { + clientID = GetClientUserId(i); + + if( g_iCvarSave && !IsFakeClient(i) ) + { + OnClientCookiesCached(i); + CreateTimer(0.3, TimerDelayCreate, clientID); + } + else if( g_iCvarRand ) + { + CreateTimer(0.3, TimerDelayCreate, clientID); + } + } + } + } + + // if( g_bLeft4Dead2 && g_fCvarDetect ) + if( g_fCvarDetect ) + { + delete g_hTimerDetect; + g_hTimerDetect = CreateTimer(g_fCvarDetect, TimerDetect, _, TIMER_REPEAT); + } + } + + else if( g_bCvarAllow == true && (bCvarAllow == false || bAllowMode == false || g_bValidMap == false) ) + { + g_bCvarAllow = false; + + UnhookViewEvents(); + UnhookEvents(); + + for( int i = 1; i <= MaxClients; i++ ) + { + RemoveHat(i); + + if( IsValidEntRef(g_iHatIndex[i]) ) + { + for( int x = 1; x <= MaxClients; x++ ) + { + if( IsClientInGame(x) ) + { + SDKUnhook(g_iHatIndex[i], SDKHook_SetTransmit, Hook_SetSpecTransmit); + } + } + } + } + } +} + +int g_iCurrentMode; +bool IsAllowedGameMode() +{ + if( g_hCvarMPGameMode == null ) + return false; + + int iCvarModesTog = g_hCvarModesTog.IntValue; + if( iCvarModesTog != 0 ) + { + if( g_bMapStarted == false ) + return false; + + g_iCurrentMode = 0; + + int entity = CreateEntityByName("info_gamemode"); + if( IsValidEntity(entity) ) + { + DispatchSpawn(entity); + HookSingleEntityOutput(entity, "OnCoop", OnGamemode, true); + HookSingleEntityOutput(entity, "OnSurvival", OnGamemode, true); + HookSingleEntityOutput(entity, "OnVersus", OnGamemode, true); + HookSingleEntityOutput(entity, "OnScavenge", OnGamemode, true); + ActivateEntity(entity); + AcceptEntityInput(entity, "PostSpawnActivate"); + if( IsValidEntity(entity) ) // Because sometimes "PostSpawnActivate" seems to kill the ent. + RemoveEdict(entity); // Because multiple plugins creating at once, avoid too many duplicate ents in the same frame + } + + if( g_iCurrentMode == 0 ) + return false; + + if( !(iCvarModesTog & g_iCurrentMode) ) + return false; + } + + char sGameModes[64], sGameMode[64]; + g_hCvarMPGameMode.GetString(sGameMode, sizeof(sGameMode)); + Format(sGameMode, sizeof(sGameMode), ",%s,", sGameMode); + + g_hCvarModes.GetString(sGameModes, sizeof(sGameModes)); + if( sGameModes[0] ) + { + Format(sGameModes, sizeof(sGameModes), ",%s,", sGameModes); + if( StrContains(sGameModes, sGameMode, false) == -1 ) + return false; + } + + g_hCvarModesOff.GetString(sGameModes, sizeof(sGameModes)); + if( sGameModes[0] ) + { + Format(sGameModes, sizeof(sGameModes), ",%s,", sGameModes); + if( StrContains(sGameModes, sGameMode, false) != -1 ) + return false; + } + + return true; +} + +void OnGamemode(const char[] output, int caller, int activator, float delay) +{ + if( strcmp(output, "OnCoop") == 0 ) + g_iCurrentMode = 1; + else if( strcmp(output, "OnSurvival") == 0 ) + g_iCurrentMode = 2; + else if( strcmp(output, "OnVersus") == 0 ) + g_iCurrentMode = 4; + else if( strcmp(output, "OnScavenge") == 0 ) + g_iCurrentMode = 8; +} + + + +// ==================================================================================================== +// OTHER BITS +// ==================================================================================================== +public void OnMapStart() +{ + g_bMapStarted = true; + g_bValidMap = true; + + char sCvar[512]; + g_hCvarPrecache.GetString(sCvar, sizeof(sCvar)); + + if( sCvar[0] != '\0' ) + { + char sMap[64]; + GetCurrentMap(sMap, sizeof(sMap)); + + Format(sMap, sizeof(sMap), ",%s,", sMap); + Format(sCvar, sizeof(sCvar), ",%s,", sCvar); + + if( StrContains(sCvar, sMap, false) != -1 ) + g_bValidMap = false; + } + + if( g_bValidMap ) + { + for( int i = 0; i < g_iCount; i++ ) + { + PrecacheModel(g_sModels[i]); + } + + // Hackish precache since L4D2 does not cache models properly (client side?) any more since recent updates + if( g_bLeft4Dead2 ) + { + RequestFrame(OnFramePrecache); + } + } +} + +void OnFramePrecache() +{ + int entity; + for( int i = 0; i < g_iCount; i++ ) + { + entity = CreateEntityByName("prop_dynamic"); + SetEntityModel(entity, g_sModels[i]); + DispatchSpawn(entity); + RemoveEdict(entity); + } +} + +public void OnMapEnd() +{ + g_bMapStarted = false; +} + +public void OnClientPutInServer(int client) +{ + g_iMenuType[client] = 0; + g_bHatAll[client] = true; + g_bHatViewTP[client] = true; + g_bHatView[client] = false; +} + +public void OnClientAuthorized(int client, const char[] sSteamID) +{ + if( g_bBlocked[client] ) + { + if( IsFakeClient(client) ) + { + g_bBlocked[client] = false; + } + else if( strcmp(sSteamID, g_sSteamID[client]) ) + { + strcopy(g_sSteamID[client], sizeof(g_sSteamID[]), sSteamID); + g_bBlocked[client] = false; + } + } +} + +public void OnClientPostAdminCheck(int client) +{ + CookieAuthTest(client); +} + +public void OnClientCookiesCached(int client) +{ + if( g_bCvarAllow && g_iCvarSave && !IsFakeClient(client) ) + { + char sCookie[4]; + + GetClientCookie(client, g_hCookie_All, sCookie, sizeof(sCookie)); + if( sCookie[0] != 0 ) + { + g_bHatAll[client] = StringToInt(sCookie) == 1; + } + + ////////////////////////////////// + // Updated by pan0s + char s1On[2], s3On[2]; + GetClientCookie(client, g_hCookie_FirstView, s1On, sizeof(s1On)); + if( s1On[0] != 0 ) + { + g_bHatView[client] = StringToInt(s1On) == 1; + } + + GetClientCookie(client, g_hCookie_ThirdView, s3On, sizeof(s3On)); + if( s3On[0] != 0 ) + { + g_bHatViewTP[client] = StringToInt(s3On) == 1; + } + ////////////////////////////////// + + // Get client cookies, set type if available or default. + GetClientCookie(client, g_hCookie_Hat, sCookie, sizeof(sCookie)); + + if( sCookie[0] == 0 ) + { + g_iType[client] = 0; + } + else + { + int type = StringToInt(sCookie); + g_iType[client] = type; + } + + CookieAuthTest(client); + } +} + +void CookieAuthTest(int client) +{ + // Check if clients allowed to use hats otherwise delete cookie/hat + if( g_iCvarMake && g_bCookieAuth[client] && !IsFakeClient(client) ) + { + int flags = GetUserFlagBits(client); + + if( !(flags & ADMFLAG_ROOT) && !(flags & g_iCvarMake) ) + { + g_iType[client] = 0; + RemoveHat(client); + SetClientCookie(client, g_hCookie_Hat, "0"); + } + } else { + g_bCookieAuth[client] = true; + } +} + +public void OnClientDisconnect(int client) +{ + g_bExternalProp[client] = false; + g_bIsThirdPerson[client] = false; + g_bExternalCvar[client] = false; + g_bExternalChange[client] = false; + g_bCookieAuth[client] = false; + delete g_hTimerDelay[client]; + delete g_hTimerView[client]; +} + +KeyValues OpenConfig() +{ + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, sizeof(sPath), CONFIG_SPAWNS); + if( !FileExists(sPath) ) + SetFailState("Cannot find the file: \"%s\"", CONFIG_SPAWNS); + + KeyValues hFile = new KeyValues("models"); + if( !hFile.ImportFromFile(sPath) ) + { + delete hFile; + SetFailState("Cannot load the file: \"%s\"", CONFIG_SPAWNS); + } + return hFile; +} + +void SaveConfig(KeyValues hFile) +{ + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, sizeof(sPath), CONFIG_SPAWNS); + hFile.Rewind(); + hFile.ExportToFile(sPath); +} + +void GetHatName(char sTemp[64], int index) +{ + strcopy(sTemp, sizeof(sTemp), g_sModels[index]); + ReplaceString(sTemp, sizeof(sTemp), "_", " "); + int pos = FindCharInString(sTemp, '/', true) + 1; + int len = strlen(sTemp) - pos - 3; + strcopy(sTemp, len, sTemp[pos]); +} + +bool HatsValidClient(int client) +{ + if( client && IsClientInGame(client) && GetClientTeam(client) == 2 && IsPlayerAlive(client) ) + return true; + return false; +} + +void SetReadyUpPlugin(int client, bool show) +{ + // Readyup plugin, show or hide panel + if( client > 0 && g_hPluginReadyUp && g_hPluginReadyUp.BoolValue && HatsValidClient(client) ) + { + ToggleReadyPanel(show, client); + } +} + + + + +// ==================================================================================================== +// CVAR CHANGES +// ==================================================================================================== +void CvarChangeOpac(Handle convar, const char[] oldValue, const char[] newValue) +{ + g_iCvarOpaq = g_hCvarOpaq.IntValue; + + if( g_bCvarAllow ) + { + int entity; + for( int i = 1; i <= MaxClients; i++ ) + { + entity = g_iHatIndex[i]; + if( HatsValidClient(i) && IsValidEntRef(entity) ) + { + SetEntityRenderMode(entity, RENDER_TRANSCOLOR); + SetEntityRenderColor(entity, 255, 255, 255, g_iCvarOpaq); + } + } + } +} + +void CvarChangeThird(Handle convar, const char[] oldValue, const char[] newValue) +{ + g_iCvarThird = g_hCvarThird.IntValue; + + if( g_bCvarAllow ) + { + if( g_iCvarThird ) + HookViewEvents(); + else + UnhookViewEvents(); + } +} + + + +// ==================================================================================================== +// EVENTS +// ==================================================================================================== +void HookEvents() +{ + HookEvent("round_start", Event_Start); + HookEvent("round_end", Event_RoundEnd); + HookEvent("player_death", Event_PlayerDeath); + HookEvent("player_spawn", Event_PlayerSpawn); + HookEvent("player_team", Event_PlayerTeam); +} + +void UnhookEvents() +{ + UnhookEvent("round_start", Event_Start); + UnhookEvent("round_end", Event_RoundEnd); + UnhookEvent("player_death", Event_PlayerDeath); + UnhookEvent("player_spawn", Event_PlayerSpawn); + UnhookEvent("player_team", Event_PlayerTeam); +} + +void HookViewEvents() +{ + if( g_bViewHooked == false ) + { + g_bViewHooked = true; + + HookEvent("revive_success", Event_First2); + HookEvent("player_ledge_grab", Event_Third1); + HookEvent("lunge_pounce", Event_Third2); + HookEvent("pounce_end", Event_FirstDelay); + HookEvent("tongue_grab", Event_Third2); + HookEvent("tongue_release", Event_First1); + + if( g_bLeft4Dead2 ) + { + HookEvent("upgrade_pack_begin", Event_Third1); + HookEvent("upgrade_pack_used", Event_First3); + HookEvent("charger_pummel_start", Event_Third2); + HookEvent("charger_carry_start", Event_Third2); + HookEvent("charger_carry_end", Event_First1); + HookEvent("charger_pummel_end", Event_FirstDelay); + } + } +} + +void UnhookViewEvents() +{ + if( g_bViewHooked == false ) + { + g_bViewHooked = true; + + UnhookEvent("revive_success", Event_First2); + UnhookEvent("player_ledge_grab", Event_Third1); + UnhookEvent("lunge_pounce", Event_Third2); + UnhookEvent("pounce_end", Event_FirstDelay); + UnhookEvent("tongue_grab", Event_Third2); + UnhookEvent("tongue_release", Event_First1); + + if( g_bLeft4Dead2 ) + { + UnhookEvent("upgrade_pack_begin", Event_Third1); + UnhookEvent("upgrade_pack_used", Event_First3); + UnhookEvent("charger_pummel_start", Event_Third2); + UnhookEvent("charger_carry_start", Event_Third2); + UnhookEvent("charger_carry_end", Event_First1); + UnhookEvent("charger_pummel_end", Event_FirstDelay); + } + } +} + +void Event_Start(Event event, const char[] name, bool dontBroadcast) +{ + if( g_iCvarRand == 1 ) + CreateTimer(0.5, TimerRand, _, TIMER_FLAG_NO_MAPCHANGE); + + // if( g_bLeft4Dead2 && g_fCvarDetect ) + if( g_fCvarDetect ) + { + delete g_hTimerDetect; + g_hTimerDetect = CreateTimer(g_fCvarDetect, TimerDetect, _, TIMER_REPEAT); + } +} + +Action TimerRand(Handle timer) +{ + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) && g_iType[i] != -1 ) + { + CreateHat(i, g_iType[i] ? g_iType[i] - 1 : -1); + } + } + + return Plugin_Continue; +} + +void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) +{ + for( int i = 1; i <= MaxClients; i++ ) + RemoveHat(i); +} + +void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("userid")); + if( !client || !IsClientInGame(client) || GetClientTeam(client) != 2 ) + return; + + RemoveHat(client); + SpectatorHatHooks(); +} + +void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) +{ + if( g_iCvarRand == 2 || g_iCvarSave ) + { + int clientID = event.GetInt("userid"); + int client = GetClientOfUserId(clientID); + + if( client ) + { + RemoveHat(client); + CreateTimer(0.5, TimerDelayCreate, clientID); + } + } + + SpectatorHatHooks(); +} + +void Event_PlayerTeam(Event event, const char[] name, bool dontBroadcast) +{ + int clientID = event.GetInt("userid"); + int client = GetClientOfUserId(clientID); + + RemoveHat(client); + SpectatorHatHooks(); + + if( g_iCvarRand ) + CreateTimer(0.1, TimerDelayCreate, clientID); +} + +Action TimerDelayCreate(Handle timer, int client) +{ + client = GetClientOfUserId(client); + + if( HatsValidClient(client) && !g_bBlocked[client] ) + { + bool fake = IsFakeClient(client); + if( !g_bCvarBots && fake ) + { + return Plugin_Continue; + } + + if( !fake && g_iCvarMake != 0 ) + { + int flags = GetUserFlagBits(client); + + if( !(flags & ADMFLAG_ROOT) && !(flags & g_iCvarMake) ) + { + return Plugin_Continue; + } + } + + if( g_iCvarRand == 2 ) + CreateHat(client, -2); + else if( g_iCvarSave && !IsFakeClient(client) ) + CreateHat(client, -3); + else if( g_iCvarRand ) + CreateHat(client, -1); + } + + return Plugin_Continue; +} + +void Event_FirstDelay(Event event, const char[] name, bool dontBroadcast) +{ + int client = GetClientOfUserId(event.GetInt("victim")); + if( client ) + { + delete g_hTimerDelay[client]; + + if( name[0] == 'c' ) // charger_pummel_end + g_hTimerDelay[client] = CreateTimer(3.0, TimerDelayFirst, client); + else // pounce_end .. if( name[0] == 'p' ) + g_hTimerDelay[client] = CreateTimer(2.5, TimerDelayFirst, client); + } +} + +Action TimerDelayFirst(Handle timer, int client) +{ + g_hTimerDelay[client] = null; + + if( IsClientInGame(client) ) + { + EventHatView(client, false); + } + + return Plugin_Continue; +} + +void Event_First1(Event event, const char[] name, bool dontBroadcast) +{ + EventView(event.GetInt("victim"), false); +} + +void Event_First3(Event event, const char[] name, bool dontBroadcast) +{ + EventView(event.GetInt("userid"), false); +} + +void Event_First2(Event event, const char[] name, bool dontBroadcast) +{ + EventView(event.GetInt("subject"), false); +} + +void Event_Third1(Event event, const char[] name, bool dontBroadcast) +{ + EventView(event.GetInt("userid"), true); +} + +void Event_Third2(Event event, const char[] name, bool dontBroadcast) +{ + EventView(event.GetInt("victim"), true); +} + +void EventView(int client, bool bToThirdPerson) +{ + DataPack dPack = new DataPack(); + dPack.WriteCell(client); + dPack.WriteCell(bToThirdPerson); + RequestFrame(OnFrameEvent, dPack); +} + +void OnFrameEvent(DataPack dPack) +{ + dPack.Reset(); + int client = dPack.ReadCell(); + bool bToThirdPerson = dPack.ReadCell(); + delete dPack; + + client = GetClientOfUserId(client); + + if( client && IsClientInGame(client) ) + { + EventHatView(client, bToThirdPerson); + } +} + +void EventHatView(int client, bool bToThirdPerson) +{ + if( HatsValidClient(client) ) + { + if( bToThirdPerson ) delete g_hTimerDelay[client]; + + SetHatView(client, bToThirdPerson); + } +} + +// Show hat when thirdperson view +Action TimerDetect(Handle timer) +{ + if( g_bCvarAllow == false ) + { + g_hTimerDetect = null; + return Plugin_Stop; + } + + int target; + bool pass; + + for( int i = 1; i <= MaxClients; i++ ) + { + if( g_bExternalCvar[i] == false && g_iHatIndex[i] && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) ) + { + pass = false; + + if( g_bLeft4Dead2 ) + { + if( + GetEntPropFloat(i, Prop_Send, "m_TimeForceExternalView") > GetGameTime() || + (GetEntPropEnt(i, Prop_Send, "m_useActionTarget") != -1 && GetEntPropEnt(i, Prop_Send, "m_useActionOwner") == i) + ) + { + pass = true; + } + } + else + { + target = GetEntPropEnt(i, Prop_Send, "m_healTarget"); + if( target > 0 && target != i ) + { + pass = true; + } + } + + if( !pass ) + { + if( + GetEntPropEnt(i, Prop_Send, "m_hViewEntity") != -1 || + GetEntPropEnt(i, Prop_Send, "m_reviveTarget") != -1 || + GetEntPropFloat(i, Prop_Send, "m_staggerTimer", 1) > GetGameTime() + ) + { + pass = true; + } + } + + if( pass ) + { + // g_bIsThirdPerson[i] = true; + + if( g_bExternalProp[i] == false ) + { + g_bExternalProp[i] = true; + + if( g_bHatViewTP[i] ) + { + SetHatView(i, true); + } else { + SetHatView(i, false); + } + } + } + else + { + // g_bIsThirdPerson[i] = false; + + if( g_bExternalProp[i] == true ) + { + g_bExternalProp[i] = false; + + if( !g_bHatView[i] ) + { + SetHatView(i, false); + } + else + { + SetHatView(i, true); + } + } + } + } + } + + return Plugin_Continue; +} + +public void TP_OnThirdPersonChanged(int client, bool bIsThirdPerson) +{ + g_bIsThirdPerson[client] = bIsThirdPerson; + + if( g_fCvarDetect ) + { + // if( bIsThirdPerson && g_bExternalCvar[client] ) + // { + // SetHatView(client, false); + // } + // else if( bIsThirdPerson && !g_bExternalCvar[client] ) + if( bIsThirdPerson && !g_bExternalCvar[client] ) + { + g_bExternalCvar[client] = true; + if( g_bHatViewTP[client] ) SetHatView(client, true); + else SetHatView(client, false); + } + else if( !bIsThirdPerson && g_bExternalCvar[client] ) + { + g_bExternalCvar[client] = false; + SetHatView(client, false); + } + } +} + +void SetHatView(int client, bool bShowHat) +{ + if( bShowHat && !g_bExternalState[client] ) + { + // PrintToChatAll("HatStates On: %d %d %d %d %d %d %d", bShowHat, g_bExternalState[client], g_bExternalChange[client], g_bExternalProp[client], g_bHatView[client], g_bIsThirdPerson[client], g_bHatViewTP[client]); + g_bExternalState[client] = true; + + int entity = g_iHatIndex[client]; + if( entity && (entity = EntRefToEntIndex(entity)) != INVALID_ENT_REFERENCE ) + SDKUnhook(entity, SDKHook_SetTransmit, Hook_SetTransmit); + } + else if( !bShowHat && g_bExternalState[client] && !g_bExternalChange[client] && !g_bExternalProp[client] && ((!g_bHatView[client] && !g_bIsThirdPerson[client]) || (!g_bHatViewTP[client] && g_bIsThirdPerson[client])) ) + { + // Prevent hiding the hat if events are currently triggered to show: + if( + GetEntProp(client, Prop_Send, "m_isHangingFromLedge") == 1 || + GetEntPropEnt(client, Prop_Send, "m_reviveTarget") != -1 || + GetEntPropEnt(client, Prop_Send, "m_pounceAttacker") != -1 || + GetEntPropEnt(client, Prop_Send, "m_hViewEntity") != -1 || + ( + g_bLeft4Dead2 && + (GetEntPropEnt(client, Prop_Send, "m_carryAttacker") != -1 || GetEntPropEnt(client, Prop_Send, "m_pummelAttacker") != -1) + ) + ) + { + return; + } + + // PrintToChatAll("HatStates Off: %d %d %d %d %d %d %d", bShowHat, g_bExternalState[client], g_bExternalChange[client], g_bExternalProp[client], g_bHatView[client], g_bIsThirdPerson[client], g_bHatViewTP[client]); + g_bExternalState[client] = false; + + int entity = g_iHatIndex[client]; + if( entity && (entity = EntRefToEntIndex(entity)) != INVALID_ENT_REFERENCE ) + SDKHook(entity, SDKHook_SetTransmit, Hook_SetTransmit); + } +} + + + +// ==================================================================================================== +// BLOCK HATS - WHEN SPECTATING IN 1ST PERSON VIEW +// ==================================================================================================== +// Loop through hats, find valid ones, loop through for each client and add transmit hook for spectators +// Could be better instead of unhooking and hooking everyone each time, but quick and dirty addition... +void SpectatorHatHooks() +{ + for( int index = 1; index <= MaxClients; index++ ) + { + if( IsValidEntRef(g_iHatIndex[index]) ) + { + for( int i = 1; i <= MaxClients; i++ ) + { + if( IsClientInGame(i) ) + { + SDKUnhook(g_iHatIndex[index], SDKHook_SetTransmit, Hook_SetSpecTransmit); + + if( !IsPlayerAlive(i) ) + { + // Must hook 1 frame later because SDKUnhook first and then SDKHook doesn't work, it won't be hooked for some reason. + DataPack dPack = new DataPack(); + dPack.WriteCell(GetClientUserId(i)); + dPack.WriteCell(index); + RequestFrame(OnFrameHooks, dPack); + } + } + } + } + } +} + +void OnFrameHooks(DataPack dPack) +{ + dPack.Reset(); + + int client = dPack.ReadCell(); + client = GetClientOfUserId(client); + + if( client && IsClientInGame(client) && !IsPlayerAlive(client) ) + { + int index = dPack.ReadCell(); + int entity = EntRefToEntIndex(g_iHatIndex[index]); + if( entity != INVALID_ENT_REFERENCE ) + SDKHook(entity, SDKHook_SetTransmit, Hook_SetSpecTransmit); + } + + delete dPack; +} + +Action Hook_SetSpecTransmit(int entity, int client) +{ + if( !g_bHatAll[client] ) + { + return Plugin_Handled; + } + + if( !IsPlayerAlive(client) && GetEntProp(client, Prop_Send, "m_iObserverMode") == 4 ) + { + int target = GetEntPropEnt(client, Prop_Send, "m_hObserverTarget"); + if( target > 0 && target <= MaxClients && g_iHatIndex[target] == EntIndexToEntRef(entity) ) + { + return Plugin_Handled; + } + } + return Plugin_Continue; +} + + + +// ==================================================================================================== +// COMMANDS +// ==================================================================================================== +// sm_hats +// ==================================================================================================== +// Updated by pan0s +Action CmdHatMain(int client, int args) +{ + if( !g_bCvarAllow || !HatsValidClient(client) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return Plugin_Handled; + } + + if( g_iCvarMenu != 0 ) + { + int flags = GetUserFlagBits(client); + + if( !(flags & ADMFLAG_ROOT) && !(flags & g_iCvarMenu) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "No Access", client); + return Plugin_Handled; + } + } + + SetReadyUpPlugin(client, false); + + g_iMenuType[client] = 0; + + Menu menu = new Menu(HandleCmdHatMain); + menu.SetTitle("%T", "HAT_MAIN", client); + + char option [64]; + char optionName [10]; + bool bEnabled[4]; + bEnabled[0] = !g_bHatOff[client]; + bEnabled[1] = g_bHatView[client]; + bEnabled[2] = g_bHatViewTP[client]; + bEnabled[3] = g_bHatAll[client]; + + char options[][] = {"HAT_MENU", "HAT_WORE", "HAT_VIEWABLE", "HAT_VIEWABLE_TP", "HAT_VISIBILITY"}; + + Format(option, sizeof(option), "%T", options[0], client); + menu.AddItem("option0", option); + + for( int i=0; i < sizeof(bEnabled); i++ ) + { + Format(option, sizeof(option), "%T: %T", options[i+1], client, bEnabled[i] ? "HAT_ENABLED" : "HAT_DISABLED", client); + Format(optionName, sizeof(optionName),"option%d", i); + menu.AddItem(optionName, option); + } + + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); + + return Plugin_Handled; +} + +// Handles callbacks from a client using the director commands menu. +int HandleCmdHatMain(Handle menu, MenuAction action, int client, int itemNum) +{ + if( action == MenuAction_Select ) + { + switch (itemNum) + { + case 0: + { + CmdHat(client, 0); + return 0; + } + case 1: CmdHatOff(client, 0); + case 2: CmdHatShow(client, 0); + case 3: FakeClientCommand(client, "sm_hatshow tp"); + case 4: CmdHatsToggle(client, 0); + } + + CmdHatMain(client, 0); + } + else if( action == MenuAction_End ) + { + delete menu; + } + else if( action == MenuAction_Cancel ) + { + if( client == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + + return 0; +} + +// ==================================================================================================== +// sm_hat +// ==================================================================================================== +Action CmdHat(int client, int args) +{ + if( !g_bCvarAllow || !HatsValidClient(client) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return Plugin_Handled; + } + + if( g_iCvarMenu != 0 ) + { + int flags = GetUserFlagBits(client); + + if( !(flags & ADMFLAG_ROOT) && !(flags & g_iCvarMenu) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "No Access", client); + return Plugin_Handled; + } + } + + g_iMenuType[client] = 0; + g_iTarget[client] = 0; + + if( args == 1 ) + { + char sTemp[64]; + GetCmdArg(1, sTemp, sizeof(sTemp)); + + int len = strlen(sTemp); + if( len < 4 && IsCharNumeric(sTemp[0]) && (len == 1 || IsCharNumeric(sTemp[1])) && (len == 2 || IsCharNumeric(sTemp[2])) ) + { + int index = StringToInt(sTemp); + if( index < 0 || index >= (g_iCount + 1) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_No_Index", client, index, g_iCount); + } + else + { + RemoveHat(client); + + if( index == 0 ) + { + if( g_iCvarSave && !IsFakeClient(client) ) + { + SetClientCookie(client, g_hCookie_Hat, "-1"); + g_iType[client] = -1; + } + + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Off", client); + } + else if( CreateHat(client, index - 1) ) + { + ExternalView(client); + } + } + } + else if( strncmp(sTemp, "rand", 4, false) == 0 ) + { + RemoveHat(client); + + if( CreateHat(client, GetRandomInt(1, g_iCount) - 1) ) + { + ExternalView(client); + return Plugin_Handled; + } + } + else + { + ReplaceString(sTemp, sizeof(sTemp), " ", "_"); + + for( int i = 0; i < g_iCount; i++ ) + { + if( StrContains(g_sModels[i], sTemp) != -1 || StrContains(g_sNames[i], sTemp) != -1 ) + { + RemoveHat(client); + + if( CreateHat(client, i) ) + { + ExternalView(client); + } + return Plugin_Handled; + } + } + + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Not_Found", client, sTemp); + } + } + else + { + SetReadyUpPlugin(client, false); + + ShowMenu(client); + } + + return Plugin_Handled; +} + +int HatMenuHandler(Menu menu, MenuAction action, int client, int index) +{ + if( action == MenuAction_End && client != 0 ) + { + delete menu; + } + else if( action == MenuAction_Select ) + { + int target = g_iTarget[client]; + g_bHatOff[client] = false; + + if( target ) + { + target = GetClientOfUserId(target); + if( HatsValidClient(target) ) + { + char name[MAX_NAME_LENGTH]; + GetClientName(target, name, sizeof(name)); + + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Changed", client, name); + RemoveHat(target); + + if( index != 0 && CreateHat(target, index - 1) ) + { + ExternalView(target); + } + + ShowMenu(client); + } + else + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Invalid", client); + + ShowMenu(client); + } + + return 0; + } + else + { + RemoveHat(client); + + if( index == 0 ) + { + if( g_iCvarSave && !IsFakeClient(client) ) + { + SetClientCookie(client, g_hCookie_Hat, "-1"); + g_iType[client] = -1; + } + + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Off", client); + } + else if( CreateHat(client, index - 1) ) + { + ExternalView(client); + } + } + + int menupos = menu.Selection; + menu.DisplayAt(client, menupos, MENU_TIME_FOREVER); + } + else if( action == MenuAction_Cancel ) + { + if( index == MenuCancel_ExitBack ) + { + if( g_iMenuType[client] == 0 ) + { + CmdHatMain(client, 0); + } + else + { + CmdHatTarget(client, 0); + } + } + else if( index == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + + return 0; +} + +void ShowMenu(int client) +{ + SetReadyUpPlugin(client, false); + + if( g_bTranslation == false ) + { + g_hMenu.Display(client, MENU_TIME_FOREVER); + } + else + { + static char sMsg[128]; + Menu hTemp = new Menu(HatMenuHandler); + hTemp.SetTitle("%T", "Hat_Menu_Title", client); + FormatEx(sMsg, sizeof(sMsg), "%T", "HAT_DISABLED", client); + hTemp.AddItem("HAT_DISABLED", sMsg); + + for( int i = 0; i < g_iCount; i++ ) + { + FormatEx(sMsg, sizeof(sMsg), "%s", g_sModels[i]); + int lang = GetClientLanguage(client); + + if( IsTranslatedForLanguage(sMsg, lang) == true ) + { + Format(sMsg, sizeof(sMsg), "%T", sMsg, client); + hTemp.AddItem(g_sModels[i], sMsg); + } else { + FormatEx(sMsg, sizeof(sMsg), "Hat %d", i + 1); + if( IsTranslatedForLanguage(sMsg, lang) == true ) + { + Format(sMsg, sizeof(sMsg), "%T", sMsg, client); + hTemp.AddItem(g_sModels[i], sMsg); + } else { + hTemp.AddItem(g_sModels[i], g_sNames[i]); + } + } + } + + hTemp.ExitBackButton = true; + hTemp.Display(client, MENU_TIME_FOREVER); + + g_hMenus[client] = hTemp; + } +} + +// ==================================================================================================== +// sm_hatoff +// ==================================================================================================== +Action CmdHatOff(int client, int args) +{ + if( !g_bCvarAllow || g_bBlocked[client] ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return Plugin_Handled; + } + + g_bHatOff[client] = !g_bHatOff[client]; + + if( g_bHatOff[client] ) + RemoveHat(client); + + char sTemp[64]; + FormatEx(sTemp, sizeof(sTemp), "%T", g_bHatOff[client] ? "Hat_Off" : "Hat_On", client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Ability", client, sTemp); + + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatshow +// ==================================================================================================== +Action CmdHatShowOn(int client, int args) +{ + g_bHatView[client] = false; + CmdHatShow(client, args); + return Plugin_Handled; +} + +Action CmdHatShowOff(int client, int args) +{ + g_bHatView[client] = true; + CmdHatShow(client, args); + return Plugin_Handled; +} + +Action CmdHatShow(int client, int args) +{ + if( !g_bCvarAllow || g_bBlocked[client] ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return Plugin_Handled; + } + + int entity = g_iHatIndex[client]; + if( entity == 0 || (entity = EntRefToEntIndex(entity)) == INVALID_ENT_REFERENCE ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Missing", client); + return Plugin_Handled; + } + + /////////////////////////////////////////// + // Updated by pan0s + if( args == 1 ) + { + char sVar[3]; + + GetCmdArgString(sVar, sizeof(sVar)); + if( strcmp(sVar, "tp", false) == 0 ) + { + g_bHatViewTP[client] = !g_bHatViewTP[client]; + + if( g_bIsThirdPerson[client] ) + { + if( !g_bHatViewTP[client] ) + SetHatView(client, false); + else + SetHatView(client, true); + } + + IntToString(g_bHatViewTP[client], sVar, sizeof(sVar)); + SetClientCookie(client, g_hCookie_ThirdView, sVar); + + char sTemp[64]; + Format(sTemp, sizeof(sTemp), "%T", g_bHatViewTP[client] ? "Hat_On" : "Hat_Off", client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_ViewTP", client, sTemp); + + return Plugin_Handled; + } + } + /////////////////////////////////////////// + + g_bHatView[client] = !g_bHatView[client]; + + if( !g_bHatView[client] && (!g_bIsThirdPerson[client] || !g_bHatViewTP[client]) ) + SetHatView(client, false); + else if( g_bHatView[client] && (!g_bIsThirdPerson[client] || g_bHatViewTP[client]) ) + SetHatView(client, true); + + char sTemp[64]; + IntToString(g_bHatView[client], sTemp, sizeof(sTemp)); + SetClientCookie(client, g_hCookie_FirstView, sTemp); + + FormatEx(sTemp, sizeof(sTemp), "%T", g_bHatView[client] ? "Hat_On" : "Hat_Off", client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_View", client, sTemp); + + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatall +// ==================================================================================================== +Action CmdHatsToggle(int client, int args) +{ + if( !g_bCvarAllow ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return Plugin_Handled; + } + + g_bHatAll[client] = !g_bHatAll[client]; + + char sTemp[64]; + FormatEx(sTemp, sizeof(sTemp), "%T", g_bHatAll[client] ? "Hat_On" : "Hat_Off", client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Visibility_Set", client, sTemp); + + // Save hat visibility + SetClientCookie(client, g_hCookie_All, g_bHatAll[client] ? "1" : "0"); + + return Plugin_Handled; +} + + + +// ==================================================================================================== +// ADMIN COMMANDS +// ==================================================================================================== +// sm_hatrand / sm_ratrandom +// ==================================================================================================== +Action CmdHatRand(int client, int args) +{ + if( g_bCvarAllow ) + { + for( int i = 1; i <= MaxClients; i++ ) + { + RemoveHat(i); + } + + int last = g_iCvarRand; + g_iCvarRand = 1; + + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) ) + { + CreateHat(i, -1); + } + } + + g_iCvarRand = last; + } + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatclient +// ==================================================================================================== +Action CmdHatClient(int client, int args) +{ + if( args == 0 ) + { + ReplyToCommand(client, "Usage: sm_hatclient <#userid|name> [hat name or hat index: 0-128 (MAX_HATS)]."); + return Plugin_Handled; + } + + char sArg[32], target_name[MAX_TARGET_LENGTH]; + GetCmdArg(1, sArg, sizeof(sArg)); + + int target_list[MAXPLAYERS], target_count; + bool tn_is_ml; + + if( (target_count = ProcessTargetString( + sArg, + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_ALIVE, /* Only allow alive players */ + target_name, + sizeof(target_name), + tn_is_ml)) <= 0 ) + { + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + int index = -1; + if( args == 2 ) + { + GetCmdArg(2, sArg, sizeof(sArg)); + + if( strlen(sArg) > 3 ) + { + for( int i = 0; i < g_iCount; i++ ) + { + if( strcmp(g_sNames[i], sArg, false) == 0 ) + { + index = i; + break; + } + } + } else { + index = StringToInt(sArg); + } + } + else + { + index = GetRandomInt(0, g_iCount - 1); + } + + for( int i = 0; i < target_count; i++ ) + { + if( GetClientTeam(target_list[i]) == 2 ) + { + RemoveHat(target_list[i]); + CreateHat(target_list[i], index); + ReplyToCommand(client, "[Hat] Set '%N' to '%s'", target_list[i], g_sNames[index]); + } + } + + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatc / sm_hatoffc / sm_hatallc +// ==================================================================================================== +Action CmdHatTarget(int client, int args) +{ + if( g_bCvarAllow ) + { + g_iMenuType[client] = 1; + ShowPlayerList(client); + } + return Plugin_Handled; +} + +Action CmdHatOffTarget(int client, int args) +{ + if( g_bCvarAllow ) + { + g_iMenuType[client] = 2; + ShowPlayerList(client); + } + return Plugin_Handled; +} + +Action CmdHatAllTarget(int client, int args) +{ + if( g_bCvarAllow ) + { + g_iMenuType[client] = 3; + ShowPlayerList(client); + } + return Plugin_Handled; +} + +void ShowPlayerList(int client) +{ + if( client && IsClientInGame(client) ) + { + SetReadyUpPlugin(client, false); + + char sTempA[8], sTempB[MAX_NAME_LENGTH]; + Menu menu = new Menu(PlayerListMenu); + + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) ) + { + IntToString(GetClientUserId(i), sTempA, sizeof(sTempA)); + GetClientName(i, sTempB, sizeof(sTempB)); + menu.AddItem(sTempA, sTempB); + } + } + + switch( g_iMenuType[client] ) + { + case 1: menu.SetTitle("%T", "ADMIN_CHANGE_HAT", client); + case 2: menu.SetTitle("%T", "ADMIN_DISABLE_HAT", client); + case 3: menu.SetTitle("%T", "ADMIN_VISIBILITY", client); + } + + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); + } +} + +int PlayerListMenu(Menu menu, MenuAction action, int client, int index) +{ + if( action == MenuAction_End ) + { + delete menu; + } + else if( action == MenuAction_Cancel ) + { + if( index == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + else if( action == MenuAction_Select ) + { + char sTemp[64]; + menu.GetItem(index, sTemp, sizeof(sTemp)); + int target = StringToInt(sTemp); + target = GetClientOfUserId(target); + + switch( g_iMenuType[client] ) + { + case 1: + { + if( HatsValidClient(target) ) + { + g_iTarget[client] = GetClientUserId(target); + + ShowMenu(client); + } + } + case 2: + { + g_bBlocked[target] = !g_bBlocked[target]; + + if( g_bBlocked[target] == false ) + { + if( HatsValidClient(target) ) + { + RemoveHat(target); + CreateHat(target); + + char name[MAX_NAME_LENGTH]; + GetClientName(target, name, sizeof(name)); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Unblocked", client, name); + } + } + else + { + if( HatsValidClient(target) ) + { + char name[MAX_NAME_LENGTH]; + GetClientName(target, name, sizeof(name)); + GetClientAuthId(target, AuthId_Steam2, g_sSteamID[target], sizeof(g_sSteamID[])); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Blocked", client, name); + RemoveHat(target); + } + } + + ShowPlayerList(client); + } + case 3: + { + g_bHatAll[target] = !g_bHatAll[target]; + + if( HatsValidClient(target) ) + { + char name[MAX_NAME_LENGTH]; + GetClientName(target, name, sizeof(name)); + FormatEx(sTemp, sizeof(sTemp), "%T", g_bHatAll[target] ? "Hat_On" : "Hat_Off", client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_ViewSet", client, name, sTemp); + } + + ShowPlayerList(client); + } + } + } + + return 0; +} + +// ==================================================================================================== +// sm_hatadd +// ==================================================================================================== +Action CmdHatAdd(int client, int args) +{ + if( !g_bCvarAllow ) + return Plugin_Handled; + + if( args == 1 ) + { + if( g_iCount < MAX_HATS ) + { + char sTemp[64], sKey[4]; + GetCmdArg(1, sTemp, sizeof(sTemp)); + + if( FileExists(sTemp, true) ) + { + strcopy(g_sModels[g_iCount], sizeof(g_sModels[]), sTemp); + g_vAng[g_iCount] = view_as({ 0.0, 0.0, 0.0 }); + g_vPos[g_iCount] = view_as({ 0.0, 0.0, 0.0 }); + g_fSize[g_iCount] = 1.0; + + KeyValues hFile = OpenConfig(); + IntToString(g_iCount+1, sKey, sizeof(sKey)); + hFile.JumpToKey(sKey, true); + hFile.SetString("mod", sTemp); + SaveConfig(hFile); + delete hFile; + g_iCount++; + ReplyToCommand(client, "%TAdded hat '\05%s\x03' %d/%d", "HAT_SYSTEM", client, sTemp, g_iCount, MAX_HATS); + + if( g_bTranslation ) + { + ReplyToCommand(client, "%TYou must add the translation for this hat or the plugin will break.", "HAT_SYSTEM", client); + } + } + else + ReplyToCommand(client, "%TCould not find the model '\05%s'. Not adding to config.", "HAT_SYSTEM", client, sTemp); + } + else + { + ReplyToCommand(client, "%TReached maximum number of hats (%d)", "HAT_SYSTEM", client, MAX_HATS); + } + } + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatdel +// ==================================================================================================== +Action CmdHatDel(int client, int args) +{ + if( !g_bCvarAllow ) + return Plugin_Handled; + + if( args == 1 ) + { + char sTemp[64]; + int index; + bool bDeleted; + + GetCmdArg(1, sTemp, sizeof(sTemp)); + int len = strlen(sTemp); + if( len < 4 && IsCharNumeric(sTemp[0]) && (len == 1 || IsCharNumeric(sTemp[1])) && (len == 2 || IsCharNumeric(sTemp[2])) ) + { + index = StringToInt(sTemp); + if( index < 1 || index >= (g_iCount + 1) ) + { + ReplyToCommand(client, "%TCannot find the hat index %d, values between 1 and %d", "HAT_SYSTEM", client, index, g_iCount); + return Plugin_Handled; + } + index--; + strcopy(sTemp, sizeof(sTemp), g_sModels[index]); + } + else + { + index = 0; + } + + char sModel[64], sKey[4]; + KeyValues hFile = OpenConfig(); + + for( int i = index; i < MAX_HATS; i++ ) + { + IntToString(i+1, sKey, sizeof(sKey)); + if( hFile.JumpToKey(sKey) ) + { + if( bDeleted ) + { + IntToString(i, sKey, sizeof(sKey)); + hFile.SetSectionName(sKey); + + strcopy(g_sModels[i-1], sizeof(g_sModels[]), g_sModels[i]); + strcopy(g_sNames[i-1], sizeof(g_sNames[]), g_sNames[i]); + g_vAng[i-1] = g_vAng[i]; + g_vPos[i-1] = g_vPos[i]; + g_fSize[i-1] = g_fSize[i]; + } + else + { + hFile.GetString("mod", sModel, sizeof(sModel)); + if( StrContains(sModel, sTemp) != -1 ) + { + ReplyToCommand(client, "%TYou have deleted the hat '\x05%s\x03'", "HAT_SYSTEM", client, sModel); + hFile.DeleteThis(); + + g_iCount--; + bDeleted = true; + + if( g_bTranslation == false ) + { + g_hMenu.RemoveItem(i); + } + else + { + for( int x = 1; x <= MAXPLAYERS; x++ ) + { + if( g_hMenus[x] != null ) + { + g_hMenus[x].RemoveItem(i); + } + } + } + } + } + } + + hFile.Rewind(); + if( i == MAX_HATS - 1 ) + { + if( bDeleted ) + SaveConfig(hFile); + else + ReplyToCommand(client, "%TCould not delete hat, did not find model '\x05%s\x03'", "HAT_SYSTEM", client, sTemp); + } + } + delete hFile; + } + else + { + int index = g_iSelected[client]; + + TranslateHatName(client, index); + } + return Plugin_Handled; +} + +void TranslateHatName(int client, int index) +{ + if( g_bTranslation == false ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Wearing", client, g_sNames[index]); + } + else + { + static char sMsg[128]; + FormatEx(sMsg, sizeof(sMsg), "%s", g_sModels[index]); + int lang = GetClientLanguage(client); + + if( IsTranslatedForLanguage(sMsg, lang) == true ) + { + Format(sMsg, sizeof(sMsg), "%T", sMsg, client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Wearing", client, sMsg); + } else { + FormatEx(sMsg, sizeof(sMsg), "Hat %d", index + 1); + if( IsTranslatedForLanguage(sMsg, lang) == true ) + { + Format(sMsg, sizeof(sMsg), "%T", sMsg, client); + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Wearing", client, sMsg); + } else { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "Hat_Wearing", client, g_sNames[index]); + } + } + } +} + +// ==================================================================================================== +// sm_hatlist +// ==================================================================================================== +Action CmdHatList(int client, int args) +{ + for( int i = 0; i < g_iCount; i++ ) + ReplyToCommand(client, "%d) %s", i+1, g_sModels[i]); + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatload +// ==================================================================================================== +Action CmdHatLoad(int client, int args) +{ + if( g_bCvarAllow && HatsValidClient(client) ) + { + int selected = g_iSelected[client]; + PrintToChat(client, "%TLoaded hat '\x05%s\x03' on all players.", "HAT_SYSTEM", client, g_sModels[selected]); + + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) ) + { + RemoveHat(i); + CreateHat(i, selected); + } + } + } + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatsave +// ==================================================================================================== +Action CmdHatSave(int client, int args) +{ + if( g_bCvarAllow && HatsValidClient(client) ) + { + int entity = g_bCvarWall ? g_iHatWalls[client] : g_iHatIndex[client]; + if( IsValidEntRef(entity) ) + { + KeyValues hFile = OpenConfig(); + int index = g_iSelected[client]; + + char sTemp[4]; + IntToString(index+1, sTemp, sizeof(sTemp)); + if( hFile.JumpToKey(sTemp) ) + { + float vAng[3], vPos[3]; + float fSize; + + GetEntPropVector(entity, Prop_Send, "m_angRotation", vAng); + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vPos); + hFile.SetVector("ang", vAng); + hFile.SetVector("loc", vPos); + g_vAng[index] = vAng; + g_vPos[index] = vPos; + + if( g_bLeft4Dead2 ) + { + entity = g_iHatIndex[client]; + if( IsValidEntRef(entity) ) + { + fSize = GetEntPropFloat(entity, Prop_Send, "m_flModelScale"); + if( fSize == 1.0 ) + { + if( hFile.GetFloat("size", 999.9) != 999.9 ) + hFile.DeleteKey("size"); + } + else + hFile.SetFloat("size", fSize); + + g_fSize[index] = fSize; + } + } + + SaveConfig(hFile); + PrintToChat(client, "%TSaved '\x05%s\x03' hat origin and angles.", "HAT_SYSTEM", client, g_sModels[index]); + } + else + { + PrintToChat(client, "%T\x04Warning: \x03Could not save '\x05%s\x03' hat origin and angles.", "HAT_SYSTEM", client, g_sModels[index]); + } + delete hFile; + } + } + + return Plugin_Handled; +} + +// ==================================================================================================== +// sm_hatang +// ==================================================================================================== +Action CmdAng(int client, int args) +{ + if( g_bCvarAllow ) + ShowAngMenu(client); + return Plugin_Handled; +} + +void ShowAngMenu(int client) +{ + if( !HatsValidClient(client) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return; + } + + SetReadyUpPlugin(client, false); + + Menu menu = new Menu(AngMenuHandler); + + menu.AddItem("", "X + 10.0"); + menu.AddItem("", "Y + 10.0"); + menu.AddItem("", "Z + 10.0"); + menu.AddItem("", "Reset"); + menu.AddItem("", "X - 10.0"); + menu.AddItem("", "Y - 10.0"); + menu.AddItem("", "Z - 10.0"); + + menu.SetTitle("%T", "HAT_SET_ANGLE", client); + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +int AngMenuHandler(Menu menu, MenuAction action, int client, int index) +{ + if( action == MenuAction_End ) + { + delete menu; + } + else if( action == MenuAction_Cancel ) + { + if( index == MenuCancel_ExitBack ) + { + ShowAngMenu(client); + } + else if( index == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + else if( action == MenuAction_Select ) + { + if( HatsValidClient(client) ) + { + ShowAngMenu(client); + + float vAng[3]; + int entity; + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) ) + { + entity = g_bCvarWall ? g_iHatWalls[i] : g_iHatIndex[i]; + if( IsValidEntRef(entity) ) + { + GetEntPropVector(entity, Prop_Send, "m_angRotation", vAng); + + switch( index ) + { + case 0: vAng[0] += 10.0; + case 1: vAng[1] += 10.0; + case 2: vAng[2] += 10.0; + case 3: vAng = view_as({0.0,0.0,0.0}); + case 4: vAng[0] -= 10.0; + case 5: vAng[1] -= 10.0; + case 6: vAng[2] -= 10.0; + } + + TeleportEntity(entity, NULL_VECTOR, vAng, NULL_VECTOR); + } + } + } + + CPrintToChat(client, "%TNew hat angles: %f %f %f", "HAT_SYSTEM", client, vAng[0], vAng[1], vAng[2]); + } + } + + return 0; +} + +// ==================================================================================================== +// sm_hatpos +// ==================================================================================================== +Action CmdPos(int client, int args) +{ + if( g_bCvarAllow ) + ShowPosMenu(client); + return Plugin_Handled; +} + +void ShowPosMenu(int client) +{ + if( !HatsValidClient(client) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return; + } + + SetReadyUpPlugin(client, false); + + Menu menu = new Menu(PosMenuHandler); + + menu.AddItem("", "X + 0.5"); + menu.AddItem("", "Y + 0.5"); + menu.AddItem("", "Z + 0.5"); + menu.AddItem("", "Reset"); + menu.AddItem("", "X - 0.5"); + menu.AddItem("", "Y - 0.5"); + menu.AddItem("", "Z - 0.5"); + + menu.SetTitle("%T", "HAT_SET_POSITION", client); + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +int PosMenuHandler(Menu menu, MenuAction action, int client, int index) +{ + if( action == MenuAction_End ) + { + delete menu; + } + else if( action == MenuAction_Cancel ) + { + if( index == MenuCancel_ExitBack ) + { + ShowPosMenu(client); + } + else if( index == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + else if( action == MenuAction_Select ) + { + if( HatsValidClient(client) ) + { + ShowPosMenu(client); + + float vPos[3]; + int entity; + for( int i = 1; i <= MaxClients; i++ ) + { + if( HatsValidClient(i) ) + { + entity = g_bCvarWall ? g_iHatWalls[i] : g_iHatIndex[i]; + if( IsValidEntRef(entity) ) + { + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", vPos); + + switch( index ) + { + case 0: vPos[0] += 0.5; + case 1: vPos[1] += 0.5; + case 2: vPos[2] += 0.5; + case 3: vPos = view_as({0.0,0.0,0.0}); + case 4: vPos[0] -= 0.5; + case 5: vPos[1] -= 0.5; + case 6: vPos[2] -= 0.5; + } + + TeleportEntity(entity, vPos, NULL_VECTOR, NULL_VECTOR); + } + } + } + + CPrintToChat(client, "%TNew hat origin: %f %f %f", "HAT_SYSTEM", client, vPos[0], vPos[1], vPos[2]); + } + } + + return 0; +} + +// ==================================================================================================== +// sm_hatsize +// ==================================================================================================== +Action CmdHatSize(int client, int args) +{ + if( g_bCvarAllow ) + ShowSizeMenu(client); + return Plugin_Handled; +} + +void ShowSizeMenu(int client) +{ + if( !HatsValidClient(client) ) + { + CPrintToChat(client, "%T%T", "HAT_SYSTEM", client, "HAT_NOT_RIGHT_NOW", client); + return; + } + + if( !g_bLeft4Dead2 ) + { + CPrintToChat(client, "%TCannot set hat size in L4D1.", "HAT_SYSTEM", client); + return; + } + + SetReadyUpPlugin(client, false); + + Menu menu = new Menu(SizeMenuHandler); + + menu.AddItem("", "+ 0.1"); + menu.AddItem("", "- 0.1"); + menu.AddItem("", "+ 0.5"); + menu.AddItem("", "- 0.5"); + menu.AddItem("", "+ 1.0"); + menu.AddItem("", "- 1.0"); + menu.AddItem("", "Reset"); + + menu.SetTitle("%T", "HAT_SET_SIZE", client); + menu.ExitButton = true; + menu.Display(client, MENU_TIME_FOREVER); +} + +int SizeMenuHandler(Menu menu, MenuAction action, int client, int index) +{ + if( action == MenuAction_End ) + { + delete menu; + } + else if( action == MenuAction_Cancel ) + { + if( index == MenuCancel_ExitBack ) + { + ShowSizeMenu(client); + } + else if( index == MenuCancel_Exit ) + { + SetReadyUpPlugin(client, true); + } + } + else if( action == MenuAction_Select ) + { + if( HatsValidClient(client) ) + { + ShowSizeMenu(client); + + float fSize; + int entity; + for( int i = 1; i <= MaxClients; i++ ) + { + entity = g_iHatIndex[i]; + if( IsValidEntRef(entity) ) + { + fSize = GetEntPropFloat(entity, Prop_Send, "m_flModelScale"); + + switch( index ) + { + case 0: fSize += 0.1; + case 1: fSize -= 0.1; + case 2: fSize += 0.5; + case 3: fSize -= 0.5; + case 4: fSize += 1.0; + case 5: fSize -= 1.0; + case 6: fSize = 1.0; + } + + SetEntPropFloat(entity, Prop_Send, "m_flModelScale", fSize); + } + } + + CPrintToChat(client, "%TNew hat scale: %f", "HAT_SYSTEM", client, fSize); + } + } + + return 0; +} + + + +// ==================================================================================================== +// HAT STUFF +// =================================================================================================== +void RemoveHat(int client) +{ + // Hat entity + int entity = g_iHatIndex[client]; + g_iHatIndex[client] = 0; + + if( IsValidEntRef(entity) ) + RemoveEntity(entity); + + // Hidden entity + entity = g_iHatWalls[client]; + g_iHatWalls[client] = 0; + + if( IsValidEntRef(entity) ) + RemoveEntity(entity); +} + +bool CreateHat(int client, int index = -1) +{ + if( g_bBlocked[client] || g_bHatOff[client] || IsValidEntRef(g_iHatIndex[client]) == true || HatsValidClient(client) == false ) + return false; + + if( index == -1 ) // Random hat + { + if( g_iCvarRand == 0 ) return false; + if( g_iType[client] == -1 ) return false; + + if( g_iCvarMenu != 0 ) + { + if( IsFakeClient(client) ) + return false; + + int flags = GetUserFlagBits(client); + if( !(flags & ADMFLAG_ROOT) && !(flags & g_iCvarMenu) ) + return false; + } + + index = GetRandomInt(1, g_iCount); + g_iType[client] = index; + } + else if( index == -2 ) // Previous random hat + { + if( g_iCvarRand != 2 ) return false; + + index = g_iType[client]; + if( index == -1 ) return false; + + if( index == 0 ) + { + index = GetRandomInt(1, g_iCount); + g_iType[client] = index; + } + + index--; + } + else if( index == -3 ) // Saved hats + { + index = g_iType[client]; + if( index == -1 ) return false; + + if( index == 0 ) + { + if( IsFakeClient(client) ) + { + return false; + } + else + { + if( g_iCvarRand == 0 ) return false; + + index = GetRandomInt(1, g_iCount); + g_iType[client] = index; + } + } + + index--; + } + else // Specified hat + { + g_iType[client] = index + 1; + } + + if( g_iCvarSave && !IsFakeClient(client) ) + { + char sNum[4]; + IntToString(index + 1, sNum, sizeof(sNum)); + SetClientCookie(client, g_hCookie_Hat, sNum); + + /////////////////////////////////////////// + // Updated by pan0s + char s1On[2]; + char s3On[2]; + IntToString(g_bHatView[client], s1On, sizeof(s1On)); + SetClientCookie(client, g_hCookie_FirstView, s1On); + IntToString(g_bHatViewTP[client], s3On, sizeof(s3On)); + SetClientCookie(client, g_hCookie_ThirdView, s3On); + /////////////////////////////////////////// + } + + // Fix showing glow through walls, break glow inheritance by attaching hats to info_target. + // Method by "Marttt": https://forums.alliedmods.net/showpost.php?p=2737781&postcount=21 + int target; + + if( g_bCvarWall ) + { + target = CreateEntityByName("info_target"); + DispatchSpawn(target); + } + + int entity = CreateEntityByName("prop_dynamic_override"); + if( entity != -1 ) + { + SetEntityModel(entity, g_sModels[index]); + DispatchSpawn(entity); + if( g_bLeft4Dead2 ) + { + SetEntPropFloat(entity, Prop_Send, "m_flModelScale", g_fSize[index]); + } + + if( g_bCvarWall ) + { + SetVariantString("!activator"); + AcceptEntityInput(entity, "SetParent", target); + TeleportEntity(target, g_vPos[index], NULL_VECTOR, NULL_VECTOR); + + SetVariantString("!activator"); + AcceptEntityInput(target, "SetParent", client); + SetVariantString("eyes"); + AcceptEntityInput(target, "SetParentAttachment"); + TeleportEntity(target, g_vPos[index], NULL_VECTOR, NULL_VECTOR); + + g_iHatWalls[client] = EntIndexToEntRef(target); + } else { + SetVariantString("!activator"); + AcceptEntityInput(entity, "SetParent", client); + SetVariantString("eyes"); + AcceptEntityInput(entity, "SetParentAttachment"); + TeleportEntity(entity, g_vPos[index], NULL_VECTOR, NULL_VECTOR); + } + + // Lux + AcceptEntityInput(entity, "DisableCollision"); + SetEntProp(entity, Prop_Send, "m_noGhostCollision", 1, 1); + SetEntProp(entity, Prop_Data, "m_CollisionGroup", 0x0004); + SetEntPropVector(entity, Prop_Send, "m_vecMins", view_as({0.0, 0.0, 0.0})); + SetEntPropVector(entity, Prop_Send, "m_vecMaxs", view_as({0.0, 0.0, 0.0})); + // Lux + + TeleportEntity(g_bCvarWall ? target : entity, g_vPos[index], g_vAng[index], NULL_VECTOR); + SetEntProp(entity, Prop_Data, "m_iEFlags", 0); + + if( g_iCvarOpaq ) + { + SetEntityRenderMode(entity, RENDER_TRANSCOLOR); + SetEntityRenderColor(entity, 255, 255, 255, g_iCvarOpaq); + } + + g_iSelected[client] = index; + g_iHatIndex[client] = EntIndexToEntRef(entity); + + if( !g_bHatView[client] && (!g_bIsThirdPerson[client] || !g_bHatViewTP[client]) ) + { + g_bExternalState[client] = true; + SetHatView(client, false); + } + else if( g_bHatView[client] && (!g_bIsThirdPerson[client] || g_bHatViewTP[client]) ) + { + SetHatView(client, true); + } + + TranslateHatName(client, index); + + SpectatorHatHooks(); + return true; + } + + return false; +} + +void ExternalView(int client) +{ + if( g_fCvarChange && g_bLeft4Dead2 ) + { + EventHatView(client, true); + + g_bExternalChange[client] = true; + + delete g_hTimerView[client]; + g_hTimerView[client] = CreateTimer(g_fCvarChange + (g_fCvarChange >= 2.0 ? 0.4 : 0.2), TimerEventView, GetClientUserId(client)); + + // Survivor Thirdperson plugin sets 99999.3. + if( GetEntPropFloat(client, Prop_Send, "m_TimeForceExternalView") != 99999.3 ) + SetEntPropFloat(client, Prop_Send, "m_TimeForceExternalView", GetGameTime() + g_fCvarChange); + } +} + +Action TimerEventView(Handle timer, int client) +{ + client = GetClientOfUserId(client); + if( client ) + { + g_hTimerView[client] = null; + g_bExternalChange[client] = false; + + EventHatView(client, false); + } + + return Plugin_Continue; +} + +Action Hook_SetTransmit(int entity, int client) +{ + if( !g_bHatAll[client] || EntIndexToEntRef(entity) == g_iHatIndex[client] ) + return Plugin_Handled; + return Plugin_Continue; +} + +bool IsValidEntRef(int entity) +{ + if( entity && EntRefToEntIndex(entity) != INVALID_ENT_REFERENCE ) + return true; + return false; +} + + + +// ==================================================================================================== +// COLORS.INC REPLACEMENT +// ==================================================================================================== +/* +void CPrintToChat(int client, char[] message, any ...) +{ + static char buffer[256]; + VFormat(buffer, sizeof(buffer), message, 3); + + ReplaceString(buffer, sizeof(buffer), "{DEFAULT}", "\x01", false); + ReplaceString(buffer, sizeof(buffer), "{WHITE}", "\x01", false); + ReplaceString(buffer, sizeof(buffer), "{CYAN}", "\x03", false); + ReplaceString(buffer, sizeof(buffer), "{LIGHTGREEN}", "\x03", false); + ReplaceString(buffer, sizeof(buffer), "{ORANGE}", "\x04", false); + ReplaceString(buffer, sizeof(buffer), "{GREEN}", "\x04", false); // Actually orange in L4D2, but replicating colors.inc behaviour + ReplaceString(buffer, sizeof(buffer), "{OLIVE}", "\x05", false); + + PrintToChat(client, buffer); +} +*/ + + + +/************************************************************************** + * * + * New color inc * + * Author: Ernecio (updated by pan0s) * + * Version: 1.0.1 * + * * + **************************************************************************/ +enum +{ + SERVER_INDEX = 0, + NO_INDEX = -1, + NO_PLAYER = -2, + BLUE_INDEX = 2, + RED_INDEX = 3, +} + +stock const char CTag[][] = { "{DEFAULT}", "{ORANGE}", "{CYAN}", "{RED}", "{BLUE}", "{GREEN}" }; +stock const char CTagCode[][] = { "\x01", "\x04", "\x03", "\x03", "\x03", "\x05" }; +stock const bool CTagReqSayText2[] = { false, false, true, true, true, false }; +stock const int CProfile_TeamIndex[] = { NO_INDEX, NO_INDEX, SERVER_INDEX, RED_INDEX, BLUE_INDEX, NO_INDEX }; + +/** + * @note Prints a message to a specific client in the chat area. + * @note Supports color tags. + * + * @param client Client index. + * @param sMessage Message (formatting rules). + * @return No return + * + * On error/Errors: If the client is not connected an error will be thrown. + */ +stock void CPrintToChat(int client, const char[] sMessage, any ...) +{ + if( client <= 0 || client > MaxClients ) + ThrowError( "Invalid client index %d", client); + + if( !IsClientInGame(client) ) + ThrowError( "Client %d is not in game", client); + + static char sBuffer[250]; + static char sCMessage[250]; + SetGlobalTransTarget(client); + Format(sBuffer, sizeof(sBuffer), "\x01%s", sMessage); + VFormat( sCMessage, sizeof( sCMessage ), sBuffer, 3); + + int index = CFormat(sCMessage, sizeof(sCMessage)); + if( index == NO_INDEX ) + PrintToChat(client, sCMessage); + else + CSayText2(client, index, sCMessage); +} + +/** + * @note Prints a message to all clients in the chat area. + * @note Supports color tags. + * + * @param client Client index. + * @param sMessage Message (formatting rules) + * @return No return + */ +stock void CPrintToChatAll(const char[] sMessage, any ...) +{ + static char sBuffer[250]; + + for( int i = 1; i <= MaxClients; i++ ) + { + if( IsClientInGame(i) && !IsFakeClient(i) ) + { + SetGlobalTransTarget(i); + VFormat(sBuffer, sizeof(sBuffer), sMessage, 2); + CPrintToChat(i, sBuffer); + } + } +} + +/** + * @note Replaces color tags in a string with color codes + * + * @param sMessage String. + * @param maxlength Maximum length of the string buffer. + * @return Client index that can be used for SayText2 author index + * + * On error/Errors: If there is more then one team color is used an error will be thrown. + */ +stock int CFormat(char[] sMessage, int maxlength) +{ + int iRandomPlayer = NO_INDEX; + + for( int i = 0; i < sizeof(CTagCode); i++ ) // Para otras etiquetas de color se requiere un bucle. + { + if( StrContains( sMessage, CTag[i]) == -1 ) // Si no se encuentra la etiqueta, omitir. + continue; + else if( !CTagReqSayText2[i] ) + ReplaceString(sMessage, maxlength, CTag[i], CTagCode[i]); // Si la etiqueta no necesita Saytext2 simplemente reemplazará. + else // La etiqueta necesita Saytext2. + { + if( iRandomPlayer == NO_INDEX ) // Si no se especificó un cliente aleatorio para la etiqueta, reemplaca la etiqueta y busca un cliente para la etiqueta. + { + iRandomPlayer = CFindRandomPlayerByTeam(CProfile_TeamIndex[i]); // Busca un cliente válido para la etiqueta, equipo de infectados oh supervivientes. + if( iRandomPlayer == NO_PLAYER ) + ReplaceString(sMessage, maxlength, CTag[i], CTagCode[5]); // Si no se encuentra un cliente valido, reemplasa la etiqueta con una etiqueta de color verde. + else + ReplaceString(sMessage, maxlength, CTag[i], CTagCode[i]); // Si el cliente fue encontrado simplemente reemplasa. + } + else // Si en caso de usar dos colores de equipo infectado y equipo de superviviente juntos se mandará un mensaje de error. + ThrowError("Using two team colors in one message is not allowed"); // Si se ha usadó una combinación de colores no validad se registrara en la carpeta logs. + } + } + + return iRandomPlayer; +} + +/** + * @note Founds a random player with specified team + * + * @param color_team Client team. + * @return Client index or NO_PLAYER if no player found + */ +stock int CFindRandomPlayerByTeam(int color_team) +{ + if( color_team == SERVER_INDEX ) + return 0; + else + for( int i = 1; i <= MaxClients; i++ ) + if( IsClientInGame(i) && GetClientTeam(i) == color_team ) + return i; + + return NO_PLAYER; +} + +/** + * @note Sends a SayText2 usermessage to a client + * + * @param sMessage Client index + * @param maxlength Author index + * @param sMessage Message + * @return No return. + */ +stock void CSayText2(int client, int author, const char[] sMessage) +{ + Handle hBuffer = StartMessageOne("SayText2", client); + BfWriteByte(hBuffer, author); + BfWriteByte(hBuffer, true); + BfWriteString(hBuffer, sMessage); + EndMessage(); +} + + + +// ==================================================================================================== +// TRANSLATE CODE +// ==================================================================================================== +// If using this code, you must replace the "\" character with "/" in the new "*phrases.txt.new" file. +stock void TranslateHatnames() +{ + int maxIndex = 95; // Searches from "1" to maxIndex (including max) in the "hatnames" file. Matches to the data config. + + char sLang[4] = "zho/"; // Language folder to translate. Blank for "en" + char sText[256]; + char sModel[PLATFORM_MAX_PATH]; + char sTran[PLATFORM_MAX_PATH]; + char sData[PLATFORM_MAX_PATH]; + char sSave[PLATFORM_MAX_PATH]; + + BuildPath(Path_SM, sSave, sizeof sSave, "translations/%shatnames.phrases.txt.new", sLang); + BuildPath(Path_SM, sTran, sizeof sTran, "translations/%shatnames.phrases.txt", sLang); + BuildPath(Path_SM, sData, sizeof sData, "data/l4d_hats.cfg"); + + KeyValues hTran = new KeyValues("Phrases"); + KeyValues hData = new KeyValues("Models"); + KeyValues hSave = new KeyValues("Phrases"); + + hTran.ImportFromFile(sTran); + hData.ImportFromFile(sData); + + char sIndex[16]; + + for( int i = 1; i <= maxIndex; i++ ) + { + IntToString(i, sIndex, sizeof sIndex); + hData.JumpToKey(sIndex); + hData.GetString("mod", sModel, sizeof(sModel)); + ReplaceString(sModel, sizeof sModel, "/", "\\"); + + Format(sIndex, sizeof sIndex, "Hat %d", i); + hTran.JumpToKey(sIndex); + hTran.GetString(sLang, sText, sizeof(sText)); + + PrintToServer("%02d (%s) [%s] == [%s]", i, sIndex, sModel, sText); + + hSave.JumpToKey(sModel, true); + hSave.SetString(sLang, sText); + + hTran.Rewind(); + hData.Rewind(); + hSave.Rewind(); + } + + hSave.ExportToFile(sSave); + + delete hTran; + delete hData; + delete hSave; +} \ No newline at end of file diff --git a/addons/sourcemod/translations/chi/hatnames.phrases.txt b/addons/sourcemod/translations/chi/hatnames.phrases.txt new file mode 100644 index 000000000..3686c9f05 --- /dev/null +++ b/addons/sourcemod/translations/chi/hatnames.phrases.txt @@ -0,0 +1,383 @@ +"Phrases" +{ + "models/infected/gibs/gibs.mdl" + { + "chi" "一手遮天" + } + "models/infected/limbs/exploded_boomer_head.mdl" + { + "chi" "Boomer 头" + } + "models/infected/limbs/limb_male_head01.mdl" + { + "chi" "假面" + } + "models/props/cs_militia/circularsaw01.mdl" + { + "chi" "圆锯" + } + "models/props/de_nuke/emergency_lighta.mdl" + { + "chi" "红色应急灯" + } + "models/props_fairgrounds/alligator.mdl" + { + "chi" "鳄鱼" + } + "models/props_fairgrounds/mr_mustachio.mdl" + { + "chi" "胡子先生" + } + "models/props_fortifications/orange_cone001_clientside.mdl" + { + "chi" "路障" + } + "models/props_interiors/teddy_bear.mdl" + { + "chi" "玩具熊" + } + "models/props_interiors/toilet_b_breakable01_part13.mdl" + { + "chi" "便器" + } + "models/props_interiors/waterbottle.mdl" + { + "chi" "水瓶" + } + "models/props_urban/dock_pylon_cap001.mdl" + { + "chi" "码头塔" + } + "models/props_urban/life_ring001.mdl" + { + "chi" "救生圈" + } + "models/props_lighting/light_construction02.mdl" + { + "chi" "建筑灯" + } + "models/extras/info_speech.mdl" + { + "chi" "聊天气泡" + } + "models/infected/smoker_tongue_attach.mdl" + { + "chi" "舌头" + } + "models/props/de_inferno/ceiling_fan_blade.mdl" + { + "chi" "吊扇叶片" + } + "models/f18/f18_placeholder.mdl" + { + "chi" "F-18 喷气式飞机" + } + "models/deadbodies/dead_male_civilian_radio.mdl" + { + "chi" "对讲机" + } + "models/infected/cim_riot_faceplate.mdl" + { + "chi" "防爆罩" + } + "models/props_interiors/styrofoam_cups.mdl" + { + "chi" "泡沫塑料杯" + } + "models/props_shacks/bug_lamp01.mdl" + { + "chi" "灭蚊灯" + } + "models/props_unique/grill_campground.mdl" + { + "chi" "烧烤架" + } + "models/props_unique/luggagecart01.mdl" + { + "chi" "超市手推车" + } + "models/props_unique/spawn_apartment/lantern.mdl" + { + "chi" "手提灯" + } + "models/props_urban/exit_sign002.mdl" + { + "chi" "出口标记" + } + "models/props_urban/garden_hose001.mdl" + { + "chi" "软管" + } + "models/props_urban/plastic_flamingo001.mdl" + { + "chi" "粉红火烈鸟" + } + "models/props_waterfront/money_pile.mdl" + { + "chi" "钱堆" + } + "models/props_fairgrounds/elephant.mdl" + { + "chi" "大象" + } + "models/props_fairgrounds/giraffe.mdl" + { + "chi" "长颈鹿" + } + "models/props_fairgrounds/garbage_popcorn_box.mdl" + { + "chi" "爆米花" + } + "models/props_fairgrounds/gnome_closet.mdl" + { + "chi" "矮人架" + } + "models/props_fairgrounds/snake.mdl" + { + "chi" "蛇" + } + "models/props_interiors/toaster.mdl" + { + "chi" "面包机" + } + "models/props_unique/doll01.mdl" + { + "chi" "娃娃" + } + "models/props_junk/garbage_hubcap01a.mdl" + { + "chi" "轮胎盖" + } + "models/props_mall/mall_mannequin_rhand.mdl" + { + "chi" "模型手" + } + "models/props_lab/desklamp01.mdl" + { + "chi" "台灯" + } + "models/props_vehicles/helicopter_rescue.mdl" + { + "chi" "大直升机" + } + "models/hybridphysx/news_helicoptor_map1_intro_v1.mdl" + { + "chi" "新闻直升机" + } + "models/missiles/f18_agm65maverick.mdl" + { + "chi" "导弹" + } + "models/props_vehicles/boat_fishing02.mdl" + { + "chi" "渔船" + } + "models/props_urban/big_wheel001.mdl" + { + "chi" "儿童三轮车" + } + "models/props_interiors/mounteddeerhead01.mdl" + { + "chi" "鹿头" + } + "models/props_misc/pot-1.mdl" + { + "chi" "平底锅" + } + "models/props_c17/metalpot002a.mdl" + { + "chi" "煎锅" + } + "models/gibs/hgibs.mdl" + { + "chi" "骷髅头" + } + "models/props/cs_militia/caseofbeer01.mdl" + { + "chi" "啤酒箱" + } + "models/props/cs_office/chair_office.mdl" + { + "chi" "办公椅子" + } + "models/props_cemetery/crypts_top02.mdl" + { + "chi" "墓地十字架" + } + "models/props_fairgrounds/kiddyland_riderocket_small.mdl" + { + "chi" "儿童乐园火箭" + } + "models/props_interiors/toilet_b_breakable01.mdl" + { + "chi" "厕所" + } + "models/props_misc/lamp-1_gib1.mdl" + { + "chi" "灯罩" + } + "models/props_misc/saddle-1.mdl" + { + "chi" "马鞍" + } + "models/props_unique/wheelchair01.mdl" + { + "chi" "轮椅" + } + "models/props_urban/mega_phone001.mdl" + { + "chi" "大喇叭" + } + "models/props/de_train/de_train_floodlights_01.mdl" + { + "chi" "泛光灯" + } + "models/props_vehicles/tire001c_car.mdl" + { + "chi" "汽车轮胎" + } + "models/props/de_inferno/ceiling_fan.mdl" + { + "chi" "吊扇" + } + "models/props/de_prodigy/fan.mdl" + { + "chi" "涡轮扇" + } + "models/props_foliage/urban_pot_clay01.mdl" + { + "chi" "盆栽植物 1" + } + "models/props_foliage/urban_pot_clay02.mdl" + { + "chi" "盆栽植物 2" + } + "models/props_downtown/ironing_board_flat.mdl" + { + "chi" "熨衣板" + } + "models/props_collectables/coin.mdl" + { + "chi" "金币" + } + "models/infected/jockey.mdl" + { + "chi" "Jockey" + } + "models/items/l4d_gift.mdl" + { + "chi" "礼物" + } + "models/props_street/firehydrant.mdl" + { + "chi" "消防栓" + } + "models/props_junk/gnome.mdl" + { + "chi" "矮人" + } + "models/bunny/b_sailboat.mdl" + { + "chi" "帆船" + } + "models/props/de_nuke/light_red2.mdl" + { + "chi" "红色舱壁灯" + } + "models/props_buildables/mine.mdl" + { + "chi" "轮毂盖" + } + "models/props_cemetery/crypts_top01.mdl" + { + "chi" "墓地十字架" + } + "models/props_misc/tea_pot-1.mdl" + { + "chi" "泡泡茶壶" + } + "models/props_fairgrounds/pyrotechnics_launcher.mdl" + { + "chi" "烟火" + } + "models/props_interiors/coffee_maker.mdl" + { + "chi" "咖啡机" + } + "models/props_interiors/pot01a.mdl" + { + "chi" "水壶" + } + "models/props_interiors/tv.mdl" + { + "chi" "电视" + } + "models/props_interiors/lamp_floor_gib2.mdl" + { + "chi" "灯罩 1" + } + "models/props_interiors/lamp_table02_gib2.mdl" + { + "chi" "灯罩 2" + } + "models/props_lighting/semi_flush_001.mdl" + { + "chi" "灯罩 3" + } + "models/props_junk/petfoodbag01.mdl" + { + "chi" "狗粮" + } + "models/props_lighting/flashlight_dropped_01.mdl" + { + "chi" "手电筒" + } + "models/props_lighting/lightfixture03.mdl" + { + "chi" "球灯" + } + "models/props_mall/cash_register.mdl" + { + "chi" "收银机" + } + "models/props_placeable/tier1_guns_trophy.mdl" + { + "chi" "枪架 1" + } + "models/props_placeable/tier2_guns_trophy.mdl" + { + "chi" "枪架 2" + } + "models/weapons/arms/v_spitter_arms.mdl" + { + "chi" "Spitter 手" + } + "models/weapons/arms/v_jockey_arms.mdl" + { + "chi" "Jockey 爪子" + } + "models/v_models/weapons/v_claw_hulk.mdl" + { + "chi" "Tank 爪子" + } + "models/w_models/weapons/w_he_grenade.mdl" + { + "chi" "炮弹" + } + "models/props_junk/propanecanister001a.mdl" + { + "chi" "丙烷气瓶" + } + "models/w_models/weapons/w_eq_medkit.mdl" + { + "chi" "医疗包帽" + } + "models/weapons/melee/w_riotshield.mdl" + { + "chi" "防爆盾" + } + "models/props_skybox/boat_rescue_tug_sunshine.mdl" + { + "chi" "游艇" + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/chi/hats.phrases.txt b/addons/sourcemod/translations/chi/hats.phrases.txt new file mode 100644 index 000000000..d51c187f4 --- /dev/null +++ b/addons/sourcemod/translations/chi/hats.phrases.txt @@ -0,0 +1,143 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "chi" "您正在配戴{ORANGE}{1}{DEFAULT}。" + } + "Hat_Changed" + { + "#format" "{1:s}" + "chi" "您正在配戴{ORANGE}{1}{DEFAULT}。" + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "chi" "无法找到编号为{ORANGE}{1}{DEFAULT}的帽子。 (应在{ORANGE}1{DEFAULT}至{ORANGE}{2}{DEFAULT}内)" + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "chi" "无法找到帽子{ORANGE}{1}{DEFAULT}。" + } + "Hat_Invalid" + { + "chi" "无效的玩家。" + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "chi" "Unblocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "chi" "Blocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "chi" "您已{ORANGE}{1}{DEFAULT}第一人称可见状态。" + } + "Hat_Ability" + { + "#format" "{1:s}" + "chi" "您已{ORANGE}{1}{DEFAULT}帽子穿戴功能。" + } + "Hat_On" + { + "chi" "启用" + } + "Hat_Off" + { + "chi" "停用" + } + "Hat_Menu_Title" + { + "chi" "选择帽子:" + } + "Hat_Missing" + { + "chi" "无法配戴帽子。" + } + + "HAT_SYSTEM" + { + "chi" "{GREEN}[帽子]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "chi" "暂时无法使用。" + } + "ADMIN_DISABLE_HAT" + { + "chi" "选择玩家停用帽子:" + } + "ADMIN_CHANGE_HAT" + { + "chi" "选择玩家更换帽子:" + } + "ADMIN_VISIBILITY" + { + "chi" "Select player to set hat visibility:" + } + "HAT_SET_ANGLE" + { + "chi" "选择帽子角度:" + } + "HAT_SET_POSITION" + { + "chi" "选择帽子位置:" + } + "HAT_SET_SIZE" + { + "chi" "选择帽子大细:" + } + "HAT_MAIN" + { + "chi" "选择动作:" + } + "HAT_MENU" + { + "chi" "打开帽子选单" + } + "HAT_WORE" + { + "chi" "穿戴状态" + } + "HAT_VIEWABLE" + { + "chi" "第一人称可见状态" + } + "HAT_ENABLED" + { + "chi" "已启用" + } + "HAT_DISABLED" + { + "chi" "己停用" + } + "HAT_VIEWABLE_TP" + { + "chi" "第三人称可见状态" + } + "HAT_VISIBILITY" + { + "chi" "颢示其他玩家的帽子" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "chi" "您已{ORANGE}{1}{DEFAULT}第三人称可见状态" + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "chi" "{ORANGE}{2}{DEFAULT}玩家{ORANGE}{1}{DEFAULT}颢示其他玩家的帽子" + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "chi" "显示其他玩家的帽子:{ORANGE}{1}{DEFAULT}" + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/de/hats.phrases.txt b/addons/sourcemod/translations/de/hats.phrases.txt new file mode 100644 index 000000000..b57a00e46 --- /dev/null +++ b/addons/sourcemod/translations/de/hats.phrases.txt @@ -0,0 +1,143 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "de" "Du trägst nun den {ORANGE}[{1}] {DEFAULT}Hut." + } + "Hat_Changed" + { + "#format" "{1:s}" + "de" "Hut von {ORANGE}{1}{DEFAULT} geändert." + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "de" "Kann den Hut Index {ORANGE}{1}{DEFAULT} nicht finden, Werte zwischen {ORANGE}1{DEFAULT} und {ORANGE}{2}{DEFAULT}." + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "de" "Kann den Hut {ORANGE}{1}{DEFAULT} nicht finden." + } + "Hat_Invalid" + { + "de" "Ungültigen Spieler ausgewählt." + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "de" "Hüte für {ORANGE}{1}{DEFAULT} wurden erlaubt." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "de" "Hüte für {ORANGE}{1}{DEFAULT} wurden verboten." + } + "Hat_View" + { + "#format" "{1:s}" + "de" "{DEFAULT}Sichtbarkeit des eigenen Hutes {ORANGE}{1}{DEFAULT}." + } + "Hat_Ability" + { + "#format" "{1:s}" + "de" "{DEFAULT}Du hast die Möglichkeit einen Hut zu tragen {ORANGE}{1}{DEFAULT}geschaltet." + } + "Hat_On" + { + "de" "ein" + } + "Hat_Off" + { + "de" "aus" + } + "Hat_Menu_Title" + { + "de" "Wähle deinen Hut." + } + "Hat_Missing" + { + "de" "Du trägst keinen Hut!" + } + + "HAT_SYSTEM" + { + "de" "{GREEN}[Hut]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "de" "Du kannst diesen Befehl derzeit nicht verwenden." + } + "ADMIN_DISABLE_HAT" + { + "de" "Wähle Spieler aus wessen Hut du deaktiiveren möchtest:" + } + "ADMIN_CHANGE_HAT" + { + "de" "Wähle Spieler aus wessen Hut du ändern möchtest:" + } + "ADMIN_VISIBILITY" + { + "de" "Wähle Spieler aus wessen Hut-Sichtbarkeit du ändern möchtest:" + } + "HAT_SET_ANGLE" + { + "de" "Setze Winkel:" + } + "HAT_SET_POSITION" + { + "de" "Setze Position:" + } + "HAT_SET_SIZE" + { + "de" "Setze Größe:" + } + "HAT_MAIN" + { + "de" "Hut Einstellungen:" + } + "HAT_MENU" + { + "de" "Öffne das Hut Menü" + } + "HAT_WORE" + { + "de" "Hut Tragen" + } + "HAT_VIEWABLE" + { + "de" "Persönliche Hutansicht" + } + "HAT_ENABLED" + { + "de" "Ein" + } + "HAT_DISABLED" + { + "de" "Aus" + } + "HAT_VIEWABLE_TP" + { + "de" "Dritte Person Hutansicht" + } + "HAT_VISIBILITY" + { + "de" "Allgemeine Hut Sichtbarkeit" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "de" "Sichtbarkeit des dritte Person Hutes {ORANGE}{1}{DEFAULT}geschaltet." + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "de" "Allgemeine Hutsichtbarkeit für {ORANGE}{1}{DEFAULT} wurde {ORANGE}{2}{DEFAULT}geschaltet." + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "de" "Du hast die Sichtbarkeit von Hüten {ORANGE}{1}{DEFAULT} geschaltet." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/es/hatnames.phrases.txt b/addons/sourcemod/translations/es/hatnames.phrases.txt new file mode 100644 index 000000000..a9fcaffea --- /dev/null +++ b/addons/sourcemod/translations/es/hatnames.phrases.txt @@ -0,0 +1,383 @@ +"Phrases" +{ + "models/infected/gibs/gibs.mdl" + { + "es" "Mano Gib" + } + "models/infected/limbs/exploded_boomer_head.mdl" + { + "es" "Cabeza de Boomer" + } + "models/infected/limbs/limb_male_head01.mdl" + { + "es" "Cabeza Gib" + } + "models/props/cs_militia/circularsaw01.mdl" + { + "es" "Sierra Circular" + } + "models/props/de_nuke/emergency_lighta.mdl" + { + "es" "Luz de Emergencia Roja" + } + "models/props_fairgrounds/alligator.mdl" + { + "es" "Cocodrilo" + } + "models/props_fairgrounds/mr_mustachio.mdl" + { + "es" "Señor Bigote" + } + "models/props_fortifications/orange_cone001_clientside.mdl" + { + "es" "Cono de Tráfico" + } + "models/props_interiors/teddy_bear.mdl" + { + "es" "Osito de Peluche" + } + "models/props_interiors/toilet_b_breakable01_part13.mdl" + { + "es" "Asiento de Inodoro" + } + "models/props_interiors/waterbottle.mdl" + { + "es" "Botella de Agua" + } + "models/props_urban/dock_pylon_cap001.mdl" + { + "es" "Pilón de Muelle" + } + "models/props_urban/life_ring001.mdl" + { + "es" "Aro Salvavidas" + } + "models/props_lighting/light_construction02.mdl" + { + "es" "Luz de Construcción" + } + "models/extras/info_speech.mdl" + { + "es" "Cita de Discurso" + } + "models/infected/smoker_tongue_attach.mdl" + { + "es" "Lengua de Smoker" + } + "models/props/de_inferno/ceiling_fan_blade.mdl" + { + "es" "Aspas de Ventilador de Techo" + } + "models/f18/f18_placeholder.mdl" + { + "es" "Avión Jet F-18" + } + "models/deadbodies/dead_male_civilian_radio.mdl" + { + "es" "Radio Civil" + } + "models/infected/cim_riot_faceplate.mdl" + { + "es" "Placa de Cara de Riot" + } + "models/props_interiors/styrofoam_cups.mdl" + { + "es" "Vasos de Poliestireno" + } + "models/props_shacks/bug_lamp01.mdl" + { + "es" "Lámpara Mata Insectos" + } + "models/props_unique/grill_campground.mdl" + { + "es" "Asador" + } + "models/props_unique/luggagecart01.mdl" + { + "es" "Carrito de Equipaje" + } + "models/props_unique/spawn_apartment/lantern.mdl" + { + "es" "Lámpara" + } + "models/props_urban/exit_sign002.mdl" + { + "es" "Señal de Salida" + } + "models/props_urban/garden_hose001.mdl" + { + "es" "Manguera de Jardín" + } + "models/props_urban/plastic_flamingo001.mdl" + { + "es" "Flamenco Rosa" + } + "models/props_waterfront/money_pile.mdl" + { + "es" "Pila de Dinero" + } + "models/props_fairgrounds/elephant.mdl" + { + "es" "Elefante" + } + "models/props_fairgrounds/giraffe.mdl" + { + "es" "Jirafa" + } + "models/props_fairgrounds/garbage_popcorn_box.mdl" + { + "es" "Caja de Popcorn" + } + "models/props_fairgrounds/gnome_closet.mdl" + { + "es" "Armario de Gnome" + } + "models/props_fairgrounds/snake.mdl" + { + "es" "Serpiente" + } + "models/props_interiors/toaster.mdl" + { + "es" "Tostadora" + } + "models/props_unique/doll01.mdl" + { + "es" "Muñeca" + } + "models/props_junk/garbage_hubcap01a.mdl" + { + "es" "Tapa de Llanta" + } + "models/props_mall/mall_mannequin_rhand.mdl" + { + "es" "Mano de maniquí" + } + "models/props_lab/desklamp01.mdl" + { + "es" "Lámpara de Escritorio" + } + "models/props_vehicles/helicopter_rescue.mdl" + { + "es" "Helicóptero Grande" + } + "models/hybridphysx/news_helicoptor_map1_intro_v1.mdl" + { + "es" "Helicóptero de Noticias" + } + "models/missiles/f18_agm65maverick.mdl" + { + "es" "Missil" + } + "models/props_vehicles/boat_fishing02.mdl" + { + "es" "Barco de Pesca" + } + "models/props_urban/big_wheel001.mdl" + { + "es" "Triciclo de Niño" + } + "models/props_interiors/mounteddeerhead01.mdl" + { + "es" "Cabeza de Ciervo" + } + "models/props_misc/pot-1.mdl" + { + "es" "Cacerola" + } + "models/props_c17/metalpot002a.mdl" + { + "es" "Sartén" + } + "models/gibs/hgibs.mdl" + { + "es" "Cráneo" + } + "models/props/cs_militia/caseofbeer01.mdl" + { + "es" "Caja de Cerveza" + } + "models/props/cs_office/chair_office.mdl" + { + "es" "Silla de Oficina" + } + "models/props_cemetery/crypts_top02.mdl" + { + "es" "Cruz de Cementerio" + } + "models/props_fairgrounds/kiddyland_riderocket_small.mdl" + { + "es" "Cohete de Kiddyland" + } + "models/props_interiors/toilet_b_breakable01.mdl" + { + "es" "Inodoro" + } + "models/props_misc/lamp-1_gib1.mdl" + { + "es" "Pantalla de Lámpara" + } + "models/props_misc/saddle-1.mdl" + { + "es" "Montura" + } + "models/props_unique/wheelchair01.mdl" + { + "es" "Silla de Ruedas" + } + "models/props_urban/mega_phone001.mdl" + { + "es" "Megáfono" + } + "models/props/de_train/de_train_floodlights_01.mdl" + { + "es" "Focos de Luz" + } + "models/props_vehicles/tire001c_car.mdl" + { + "es" "Llanta de Carro" + } + "models/props/de_inferno/ceiling_fan.mdl" + { + "es" "Ventilador de Techo" + } + "models/props/de_prodigy/fan.mdl" + { + "es" "Ventilador" + } + "models/props_foliage/urban_pot_clay01.mdl" + { + "es" "Maceta 1" + } + "models/props_foliage/urban_pot_clay02.mdl" + { + "es" "Maceta 2" + } + "models/props_downtown/ironing_board_flat.mdl" + { + "es" "Tabla de Planchar" + } + "models/props_collectables/coin.mdl" + { + "es" "Moneda" + } + "models/infected/jockey.mdl" + { + "es" "Jockey" + } + "models/items/l4d_gift.mdl" + { + "es" "Regalo" + } + "models/props_street/firehydrant.mdl" + { + "es" "Boca de Incendios" + } + "models/props_junk/gnome.mdl" + { + "es" "Gnome" + } + "models/bunny/b_sailboat.mdl" + { + "es" "Barco de Vela" + } + "models/props/de_nuke/light_red2.mdl" + { + "es" "Luz de Bodega Roja" + } + "models/props_buildables/mine.mdl" + { + "es" "Tapa de Llanta" + } + "models/props_cemetery/crypts_top01.mdl" + { + "es" "Cruz de Cementerio" + } + "models/props_misc/tea_pot-1.mdl" + { + "es" "Tetera" + } + "models/props_fairgrounds/pyrotechnics_launcher.mdl" + { + "es" "Pirotecnia" + } + "models/props_interiors/coffee_maker.mdl" + { + "es" "Cafetera" + } + "models/props_interiors/pot01a.mdl" + { + "es" "Tetera" + } + "models/props_interiors/tv.mdl" + { + "es" "Televisión" + } + "models/props_interiors/lamp_floor_gib2.mdl" + { + "es" "Pantalla de Lámpara 1" + } + "models/props_interiors/lamp_table02_gib2.mdl" + { + "es" "Pantalla de Lámpara 2" + } + "models/props_lighting/semi_flush_001.mdl" + { + "es" "Pantalla de Lámpara 3" + } + "models/props_junk/petfoodbag01.mdl" + { + "es" "Comida de Perro" + } + "models/props_lighting/flashlight_dropped_01.mdl" + { + "es" "Linterna Dalek" + } + "models/props_lighting/lightfixture03.mdl" + { + "es" "Luz de Bola" + } + "models/props_mall/cash_register.mdl" + { + "es" "Caja Registradora" + } + "models/props_placeable/tier1_guns_trophy.mdl" + { + "es" "Trofeo de Arma 1" + } + "models/props_placeable/tier2_guns_trophy.mdl" + { + "es" "Trofeo de Arma 2" + } + "models/weapons/arms/v_spitter_arms.mdl" + { + "es" "Brazos de Spitter" + } + "models/weapons/arms/v_jockey_arms.mdl" + { + "es" "Garras de Jockey" + } + "models/v_models/weapons/v_claw_hulk.mdl" + { + "es" "Garras de Tank" + } + "models/w_models/weapons/w_he_grenade.mdl" + { + "es" "Granada" + } + "models/props_junk/propanecanister001a.mdl" + { + "es" "Cilindro de Propano" + } + "models/w_models/weapons/w_eq_medkit.mdl" + { + "es" "Botiquín" + } + "models/weapons/melee/w_riotshield.mdl" + { + "es" "Escudo Antidisturbios" + } + "models/props_skybox/boat_rescue_tug_sunshine.mdl" + { + "es" "Barco Remolcador" + } +} diff --git a/addons/sourcemod/translations/es/hats.phrases.txt b/addons/sourcemod/translations/es/hats.phrases.txt new file mode 100644 index 000000000..2cc5f682b --- /dev/null +++ b/addons/sourcemod/translations/es/hats.phrases.txt @@ -0,0 +1,143 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "es" "Ahora llevas el sombrero {ORANGE}[{1}] {DEFAULT}." + } + "Hat_Changed" + { + "#format" "{1:s}" + "es" "Cambio de sombrero en {ORANGE}{1}{DEFAULT}." + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "es" "No se encuentra el índice {ORANGE}{1}{DEFAULT}, valores entre {ORANGE}1{DEFAULT} y {ORANGE}{2}{DEFAULT}." + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "es" "No se encuentra el sombrero {ORANGE}{1}{DEFAULT}." + } + "Hat_Invalid" + { + "es" "Jugador no válido seleccionado." + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "es" "Sombreros desbloqueados para {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "es" "Sombreros bloqueados para {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "es" "{DEFAULT}La vista personal se ha convertido {ORANGE}{1}{DEFAULT}." + } + "Hat_Ability" + { + "#format" "{1:s}" + "es" "{DEFAULT}Se ha convertido en {ORANGE}{1}{DEFAULT} la capacidad de usar sombreros." + } + "Hat_On" + { + "es" "Activado" + } + "Hat_Off" + { + "es" "Desactivado" + } + "Hat_Menu_Title" + { + "es" "Seleccione su sombrero." + } + "Hat_Missing" + { + "es" "¡No llevas sombrero!" + } + + "HAT_SYSTEM" + { + "es" "{GREEN}[HAT]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "es" "No puedo usar este comando en este momento." + } + "ADMIN_DISABLE_HAT" + { + "es" "Selecciona jugador para desactivar:" + } + "ADMIN_CHANGE_HAT" + { + "es" "Selecciona jugador para cambiar:" + } + "ADMIN_VISIBILITY" + { + "es" "Selecciona jugador para configurar la visibilidad:" + } + "HAT_SET_ANGLE" + { + "es" "Establecer ángulo:" + } + "HAT_SET_POSITION" + { + "es" "Posición de ajuste:" + } + "HAT_SET_SIZE" + { + "es" "Establecer tamaño:" + } + "HAT_MAIN" + { + "es" "Configuración:" + } + "HAT_MENU" + { + "es" "Menú de sombreros" + } + "HAT_WORE" + { + "es" "Uso" + } + "HAT_VIEWABLE" + { + "es" "Vista personal" + } + "HAT_ENABLED" + { + "es" "Activado" + } + "HAT_DISABLED" + { + "es" "Desactivado" + } + "HAT_VIEWABLE_TP" + { + "es" "Tercera persona" + } + "HAT_VISIBILITY" + { + "es" "Visibilidad general" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "es" "La vista de sombrero en tercera persona se ha cambiado a {ORANGE}{1}{DEFAULT}." + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "es" "La visibilidad general para {ORANGE}{1}{DEFAULT} se ha convertido en {ORANGE}{2}{DEFAULT}." + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "es" "Has cambiado la visibilidad de los sombreros a {ORANGE}{1}{DEFAULT}." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/hatnames.phrases.txt b/addons/sourcemod/translations/hatnames.phrases.txt new file mode 100644 index 000000000..042954d91 --- /dev/null +++ b/addons/sourcemod/translations/hatnames.phrases.txt @@ -0,0 +1,383 @@ +"Phrases" +{ + "models/infected/gibs/gibs.mdl" + { + "en" "Hand Gib" + } + "models/infected/limbs/exploded_boomer_head.mdl" + { + "en" "Boomer Head" + } + "models/infected/limbs/limb_male_head01.mdl" + { + "en" "Head Gib" + } + "models/props/cs_militia/circularsaw01.mdl" + { + "en" "Circular Saw" + } + "models/props/de_nuke/emergency_lighta.mdl" + { + "en" "Red Emergency Light" + } + "models/props_fairgrounds/alligator.mdl" + { + "en" "Alligator" + } + "models/props_fairgrounds/mr_mustachio.mdl" + { + "en" "Mr Mustachio" + } + "models/props_fortifications/orange_cone001_clientside.mdl" + { + "en" "Traffic Cone" + } + "models/props_interiors/teddy_bear.mdl" + { + "en" "Teddy Bear" + } + "models/props_interiors/toilet_b_breakable01_part13.mdl" + { + "en" "Toilet Seat" + } + "models/props_interiors/waterbottle.mdl" + { + "en" "Water bottle" + } + "models/props_urban/dock_pylon_cap001.mdl" + { + "en" "Dock Pylon" + } + "models/props_urban/life_ring001.mdl" + { + "en" "Life Ring" + } + "models/props_lighting/light_construction02.mdl" + { + "en" "Construction Light" + } + "models/extras/info_speech.mdl" + { + "en" "Speech Quote" + } + "models/infected/smoker_tongue_attach.mdl" + { + "en" "Smoker Tongue" + } + "models/props/de_inferno/ceiling_fan_blade.mdl" + { + "en" "Ceiling Fan Blades" + } + "models/f18/f18_placeholder.mdl" + { + "en" "F-18 Jet Plane" + } + "models/deadbodies/dead_male_civilian_radio.mdl" + { + "en" "Civilian Radio" + } + "models/infected/cim_riot_faceplate.mdl" + { + "en" "Riot Faceplate" + } + "models/props_interiors/styrofoam_cups.mdl" + { + "en" "Styrofoam Cups" + } + "models/props_shacks/bug_lamp01.mdl" + { + "en" "Bug Lamp Zapper" + } + "models/props_unique/grill_campground.mdl" + { + "en" "BBQ Grill" + } + "models/props_unique/luggagecart01.mdl" + { + "en" "Luggage Cart" + } + "models/props_unique/spawn_apartment/lantern.mdl" + { + "en" "Lantern" + } + "models/props_urban/exit_sign002.mdl" + { + "en" "Exit Sign" + } + "models/props_urban/garden_hose001.mdl" + { + "en" "Garden Hose" + } + "models/props_urban/plastic_flamingo001.mdl" + { + "en" "Pink Flamingo" + } + "models/props_waterfront/money_pile.mdl" + { + "en" "Money Pile" + } + "models/props_fairgrounds/elephant.mdl" + { + "en" "Elephant" + } + "models/props_fairgrounds/giraffe.mdl" + { + "en" "Giraffe" + } + "models/props_fairgrounds/garbage_popcorn_box.mdl" + { + "en" "Popcorn Box" + } + "models/props_fairgrounds/gnome_closet.mdl" + { + "en" "Gnome Closet" + } + "models/props_fairgrounds/snake.mdl" + { + "en" "Snake" + } + "models/props_interiors/toaster.mdl" + { + "en" "Toaster" + } + "models/props_unique/doll01.mdl" + { + "en" "Doll" + } + "models/props_junk/garbage_hubcap01a.mdl" + { + "en" "Hub Cap" + } + "models/props_mall/mall_mannequin_rhand.mdl" + { + "en" "Mannequin Hand" + } + "models/props_lab/desklamp01.mdl" + { + "en" "Desk Lamp" + } + "models/props_vehicles/helicopter_rescue.mdl" + { + "en" "Big Helicopter" + } + "models/hybridphysx/news_helicoptor_map1_intro_v1.mdl" + { + "en" "News Helicopter" + } + "models/missiles/f18_agm65maverick.mdl" + { + "en" "Missile" + } + "models/props_vehicles/boat_fishing02.mdl" + { + "en" "Fishing Boat" + } + "models/props_urban/big_wheel001.mdl" + { + "en" "Childs Trike" + } + "models/props_interiors/mounteddeerhead01.mdl" + { + "en" "Deer Head" + } + "models/props_misc/pot-1.mdl" + { + "en" "Saucepan" + } + "models/props_c17/metalpot002a.mdl" + { + "en" "Frying Pan" + } + "models/gibs/hgibs.mdl" + { + "en" "Skull" + } + "models/props/cs_militia/caseofbeer01.mdl" + { + "en" "Case of Beer" + } + "models/props/cs_office/chair_office.mdl" + { + "en" "Office Chair" + } + "models/props_cemetery/crypts_top02.mdl" + { + "en" "Graveyard Cross" + } + "models/props_fairgrounds/kiddyland_riderocket_small.mdl" + { + "en" "Kiddyland Rocket" + } + "models/props_interiors/toilet_b_breakable01.mdl" + { + "en" "Toilet" + } + "models/props_misc/lamp-1_gib1.mdl" + { + "en" "Lamp Shade" + } + "models/props_misc/saddle-1.mdl" + { + "en" "Saddle" + } + "models/props_unique/wheelchair01.mdl" + { + "en" "Wheel Chair" + } + "models/props_urban/mega_phone001.mdl" + { + "en" "Mega Phone" + } + "models/props/de_train/de_train_floodlights_01.mdl" + { + "en" "Flood lights" + } + "models/props_vehicles/tire001c_car.mdl" + { + "en" "Car Tire" + } + "models/props/de_inferno/ceiling_fan.mdl" + { + "en" "Ceiling Fan" + } + "models/props/de_prodigy/fan.mdl" + { + "en" "Fan" + } + "models/props_foliage/urban_pot_clay01.mdl" + { + "en" "Pot Plant 1" + } + "models/props_foliage/urban_pot_clay02.mdl" + { + "en" "Pot Plant 2" + } + "models/props_downtown/ironing_board_flat.mdl" + { + "en" "Ironing Board" + } + "models/props_collectables/coin.mdl" + { + "en" "Coin" + } + "models/infected/jockey.mdl" + { + "en" "Jockey" + } + "models/items/l4d_gift.mdl" + { + "en" "Gift" + } + "models/props_street/firehydrant.mdl" + { + "en" "Fire Hydrant" + } + "models/props_junk/gnome.mdl" + { + "en" "Gnome" + } + "models/bunny/b_sailboat.mdl" + { + "en" "Sail Boat" + } + "models/props/de_nuke/light_red2.mdl" + { + "en" "Red Bulkhead Light" + } + "models/props_buildables/mine.mdl" + { + "en" "Wheel Hubcap" + } + "models/props_cemetery/crypts_top01.mdl" + { + "en" "Graveyard Cross" + } + "models/props_misc/tea_pot-1.mdl" + { + "en" "Tea Pot" + } + "models/props_fairgrounds/pyrotechnics_launcher.mdl" + { + "en" "Pyrotechnics" + } + "models/props_interiors/coffee_maker.mdl" + { + "en" "Coffee Maker" + } + "models/props_interiors/pot01a.mdl" + { + "en" "Kettle" + } + "models/props_interiors/tv.mdl" + { + "en" "TV" + } + "models/props_interiors/lamp_floor_gib2.mdl" + { + "en" "Lamp Shade 1" + } + "models/props_interiors/lamp_table02_gib2.mdl" + { + "en" "Lamp Shade 2" + } + "models/props_lighting/semi_flush_001.mdl" + { + "en" "Lamp Shade 3" + } + "models/props_junk/petfoodbag01.mdl" + { + "en" "Dog Food" + } + "models/props_lighting/flashlight_dropped_01.mdl" + { + "en" "Dalek Flash Light" + } + "models/props_lighting/lightfixture03.mdl" + { + "en" "Ball Light" + } + "models/props_mall/cash_register.mdl" + { + "en" "Cash Register" + } + "models/props_placeable/tier1_guns_trophy.mdl" + { + "en" "Gun Trophy 1" + } + "models/props_placeable/tier2_guns_trophy.mdl" + { + "en" "Gun Trophy 2" + } + "models/weapons/arms/v_spitter_arms.mdl" + { + "en" "Spitter Arms" + } + "models/weapons/arms/v_jockey_arms.mdl" + { + "en" "Jockey Claws" + } + "models/v_models/weapons/v_claw_hulk.mdl" + { + "en" "Tank Claws" + } + "models/w_models/weapons/w_he_grenade.mdl" + { + "en" "Grenade Shell" + } + "models/props_junk/propanecanister001a.mdl" + { + "en" "Propane Cylinder" + } + "models/w_models/weapons/w_eq_medkit.mdl" + { + "en" "Medkit Cap" + } + "models/weapons/melee/w_riotshield.mdl" + { + "en" "Riot Shield" + } + "models/props_skybox/boat_rescue_tug_sunshine.mdl" + { + "en" "Tug Boat" + } +} diff --git a/addons/sourcemod/translations/hats.phrases.txt b/addons/sourcemod/translations/hats.phrases.txt new file mode 100644 index 000000000..40c567f14 --- /dev/null +++ b/addons/sourcemod/translations/hats.phrases.txt @@ -0,0 +1,143 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "en" "You are now wearing the {ORANGE}[{1}] {DEFAULT}hat." + } + "Hat_Changed" + { + "#format" "{1:s}" + "en" "Changed hat on {ORANGE}{1}{DEFAULT}." + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "en" "Cannot find the hat index {ORANGE}{1}{DEFAULT}, values between {ORANGE}1{DEFAULT} and {ORANGE}{2}{DEFAULT}." + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "en" "Cannot find the hat {ORANGE}{1}{DEFAULT}." + } + "Hat_Invalid" + { + "en" "Invalid player selected." + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "en" "Unblocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "en" "Blocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "en" "Personal hat view has been turned {ORANGE}{1}{DEFAULT}." + } + "Hat_Ability" + { + "#format" "{1:s}" + "en" "You have turned {ORANGE}{1}{DEFAULT} the ability to wear hats." + } + "Hat_On" + { + "en" "on" + } + "Hat_Off" + { + "en" "off" + } + "Hat_Menu_Title" + { + "en" "Select your hat." + } + "Hat_Missing" + { + "en" "You're not wearing a hat!" + } + + "HAT_SYSTEM" + { + "en" "{GREEN}[HAT]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "en" "Can't use this command at the moment." + } + "ADMIN_DISABLE_HAT" + { + "en" "Select player to disable hats:" + } + "ADMIN_CHANGE_HAT" + { + "en" "Select player to change hat:" + } + "ADMIN_VISIBILITY" + { + "en" "Select player to set hat visibility:" + } + "HAT_SET_ANGLE" + { + "en" "Set Angle:" + } + "HAT_SET_POSITION" + { + "en" "Set Position:" + } + "HAT_SET_SIZE" + { + "en" "Set Size:" + } + "HAT_MAIN" + { + "en" "Hat Settings:" + } + "HAT_MENU" + { + "en" "Open Hats Menu" + } + "HAT_WORE" + { + "en" "Hat Wearing" + } + "HAT_VIEWABLE" + { + "en" "Personal Hat View" + } + "HAT_ENABLED" + { + "en" "On" + } + "HAT_DISABLED" + { + "en" "Off" + } + "HAT_VIEWABLE_TP" + { + "en" "Third Personal Hat View" + } + "HAT_VISIBILITY" + { + "en" "General Hat Visibility" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "en" "Third person hat view has been turned {ORANGE}{1}{DEFAULT}." + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "en" "General hat visibility for {ORANGE}{1}{DEFAULT} has been turned {ORANGE}{2}{DEFAULT}." + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "en" "You have turned the visibility of hats to {ORANGE}{1}{DEFAULT}." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/ru/hats.phrases.txt b/addons/sourcemod/translations/ru/hats.phrases.txt new file mode 100644 index 000000000..f2b6fcfc3 --- /dev/null +++ b/addons/sourcemod/translations/ru/hats.phrases.txt @@ -0,0 +1,142 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "ru" "Вы надели шляпу: {ORANGE}[{1}] {DEFAULT}" + } + "Hat_Changed" + { + "#format" "{1:s}" + "ru" "Шапка заменена у {ORANGE}{1}{DEFAULT}." + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "ru" "Не могу найти индекс шапки {ORANGE}{1}{DEFAULT}, значения между {ORANGE}1{DEFAULT} и {ORANGE}{2}{DEFAULT}." + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "ru" "Не могу найти шапку {ORANGE}{1}{DEFAULT}." + } + "Hat_Invalid" + { + "ru" "Выбран неверный игрок." + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "ru" "Разблокированы шапки для {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "ru" "Заблокированы шапки для {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "ru" "{DEFAULT}Возможность видеть свою шапку: {ORANGE}{1}." + } + "Hat_Ability" + { + "#format" "{1:s}" + "ru" "{DEFAULT}Возможность надевать шапки: {ORANGE}{1}." + } + "Hat_On" + { + "ru" "включена" + } + "Hat_Off" + { + "ru" "отключена" + } + "Hat_Menu_Title" + { + "ru" "Выберите свою шляпу." + } + "Hat_Missing" + { + "ru" "На вас не одета шляпа!" + } + "HAT_SYSTEM" + { + "ru" "{GREEN}[Шляпа]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "ru" "Нельзя использовать эту команду в данный момент." + } + "ADMIN_DISABLE_HAT" + { + "ru" "Выберите игрока для отключения шапки:" + } + "ADMIN_CHANGE_HAT" + { + "ru" "Выберите игрока для изменения шапки:" + } + "ADMIN_VISIBILITY" + { + "ru" "Выберите игрока для установки видимости шапки:" + } + "HAT_SET_ANGLE" + { + "ru" "Задать угол:" + } + "HAT_SET_POSITION" + { + "ru" "Задать позицию:" + } + "HAT_SET_SIZE" + { + "ru" "Задать размер:" + } + "HAT_MAIN" + { + "ru" "Настройки шапки:" + } + "HAT_MENU" + { + "ru" "Открыть меню шапок" + } + "HAT_WORE" + { + "ru" "Надевание шапки" + } + "HAT_VIEWABLE" + { + "ru" "Вид шапки от 1-го лица" + } + "HAT_ENABLED" + { + "ru" "Включено" + } + "HAT_DISABLED" + { + "ru" "Отключено" + } + "HAT_VIEWABLE_TP" + { + "ru" "Вид шапки от 3-го лица" + } + "HAT_VISIBILITY" + { + "ru" "Общая видимость шапки" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "ru" "Вид шапки от 3-го лица был {ORANGE}{1}{DEFAULT}." + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "ru" "Общий вид шапки для {ORANGE}{1}{DEFAULT} был {ORANGE}{2}{DEFAULT}." + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "ru" "Видимость шапки теперь {ORANGE}{1}{DEFAULT}." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/ua/hats.phrases.txt b/addons/sourcemod/translations/ua/hats.phrases.txt new file mode 100644 index 000000000..660ee2e84 --- /dev/null +++ b/addons/sourcemod/translations/ua/hats.phrases.txt @@ -0,0 +1,142 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "ua" "Ви одягнули капелюх: {ORANGE}[{1}] {DEFAULT}" + } + "Hat_Changed" + { + "#format" "{1:s}" + "ua" "Шапка замінена у {ORANGE}{1}{DEFAULT}." + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "ua" "Не можу знайти індекс шапки {ORANGE}{1}{DEFAULT}, значення між {ORANGE}1{DEFAULT} і {ORANGE}{2}{DEFAULT}." + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "ua" "Не можу знайти шапку {ORANGE}{1}{DEFAULT}." + } + "Hat_Invalid" + { + "ua" "Обрано невірного гравця." + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "ua" "Розблоковані шапки для {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "ua" "Заблоковані шапки для {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "ua" "{DEFAULT}Можливість бачити свою шапку: {ORANGE}{1}." + } + "Hat_Ability" + { + "#format" "{1:s}" + "ua" "{DEFAULT}Можливість вдягати шапки: {ORANGE}{1}." + } + "Hat_On" + { + "ua" "увімкнена" + } + "Hat_Off" + { + "ua" "відключена" + } + "Hat_Menu_Title" + { + "ua" "Оберіть свій капелюх." + } + "Hat_Missing" + { + "ua" "На вас не вдягнений капелюх!" + } + "HAT_SYSTEM" + { + "ua" "{GREEN}[Капелюх]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "ua" "Неможливо застосувати дану команду в цей чай." + } + "ADMIN_DISABLE_HAT" + { + "ua" "Оберіть гравця для відключення шапки:" + } + "ADMIN_CHANGE_HAT" + { + "ua" "Оберіть гравця для зміни шапки:" + } + "ADMIN_VISIBILITY" + { + "ua" "Оберіть гравця для встановлення бачення шапки:" + } + "HAT_SET_ANGLE" + { + "ua" "Встановити кут:" + } + "HAT_SET_POSITION" + { + "ua" "Встановити позицію:" + } + "HAT_SET_SIZE" + { + "ua" "Встановити розмір:" + } + "HAT_MAIN" + { + "ua" "Налаштування шапки:" + } + "HAT_MENU" + { + "ua" "Відкрити меню шапок" + } + "HAT_WORE" + { + "ua" "Вдягання шапки" + } + "HAT_VIEWABLE" + { + "ua" "Вигляд капелюха від 1-го лиця" + } + "HAT_ENABLED" + { + "ua" "Увімкнено" + } + "HAT_DISABLED" + { + "ua" "Відключено" + } + "HAT_VIEWABLE_TP" + { + "ua" "Вигляд капелюха від 3-го лиця" + } + "HAT_VISIBILITY" + { + "ua" "Загальне бачення капелюха" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "ua" "Вигляд капелюха від 3-го лиця {ORANGE}{1}{DEFAULT}." + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "ua" "Вигляд капелюха від 3-го лиця для {ORANGE}{1}{DEFAULT} було {ORANGE}{2}{DEFAULT}." + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "ua" "Бачення капелюха тепер {ORANGE}{1}{DEFAULT}." + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/zho/hatnames.phrases.txt b/addons/sourcemod/translations/zho/hatnames.phrases.txt new file mode 100644 index 000000000..89f652e93 --- /dev/null +++ b/addons/sourcemod/translations/zho/hatnames.phrases.txt @@ -0,0 +1,383 @@ +"Phrases" +{ + "models/infected/gibs/gibs.mdl" + { + "zho" "隻手遮天" + } + "models/infected/limbs/exploded_boomer_head.mdl" + { + "zho" "Boomer的頭" + } + "models/infected/limbs/limb_male_head01.mdl" + { + "zho" "假面" + } + "models/props/cs_militia/circularsaw01.mdl" + { + "zho" "圓鋸" + } + "models/props/de_nuke/emergency_lighta.mdl" + { + "zho" "紅色應急燈" + } + "models/props_fairgrounds/alligator.mdl" + { + "zho" "鱷魚布偶" + } + "models/props_fairgrounds/mr_mustachio.mdl" + { + "zho" "鬍子先生" + } + "models/props_fortifications/orange_cone001_clientside.mdl" + { + "zho" "交通錐" + } + "models/props_interiors/teddy_bear.mdl" + { + "zho" "玩具熊" + } + "models/props_interiors/toilet_b_breakable01_part13.mdl" + { + "zho" "馬桶座圈" + } + "models/props_interiors/waterbottle.mdl" + { + "zho" "礦泉水瓶" + } + "models/props_urban/dock_pylon_cap001.mdl" + { + "zho" "碼頭塔" + } + "models/props_urban/life_ring001.mdl" + { + "zho" "救生圈" + } + "models/props_lighting/light_construction02.mdl" + { + "zho" "施工燈" + } + "models/extras/info_speech.mdl" + { + "zho" "我無言了" + } + "models/infected/smoker_tongue_attach.mdl" + { + "zho" "Smoker的舌頭" + } + "models/props/de_inferno/ceiling_fan_blade.mdl" + { + "zho" "吊扇葉片" + } + "models/f18/f18_placeholder.mdl" + { + "zho" "F-18噴氣式飛機" + } + "models/deadbodies/dead_male_civilian_radio.mdl" + { + "zho" "對講機" + } + "models/infected/cim_riot_faceplate.mdl" + { + "zho" "防暴面板" + } + "models/props_interiors/styrofoam_cups.mdl" + { + "zho" "發泡膠杯" + } + "models/props_shacks/bug_lamp01.mdl" + { + "zho" "驅蟲燈" + } + "models/props_unique/grill_campground.mdl" + { + "zho" "燒烤架" + } + "models/props_unique/luggagecart01.mdl" + { + "zho" "購物車" + } + "models/props_unique/spawn_apartment/lantern.mdl" + { + "zho" "燈籠" + } + "models/props_urban/exit_sign002.mdl" + { + "zho" "出口指示牌" + } + "models/props_urban/garden_hose001.mdl" + { + "zho" "園藝軟管" + } + "models/props_urban/plastic_flamingo001.mdl" + { + "zho" "粉紅天鵝" + } + "models/props_waterfront/money_pile.mdl" + { + "zho" "錢堆" + } + "models/props_fairgrounds/elephant.mdl" + { + "zho" "大象布偶" + } + "models/props_fairgrounds/giraffe.mdl" + { + "zho" "長頸鹿布偶" + } + "models/props_fairgrounds/garbage_popcorn_box.mdl" + { + "zho" "爆米花盒" + } + "models/props_fairgrounds/gnome_closet.mdl" + { + "zho" "小矮人的櫃子" + } + "models/props_fairgrounds/snake.mdl" + { + "zho" "蛇布偶" + } + "models/props_interiors/toaster.mdl" + { + "zho" "烤麵包機" + } + "models/props_unique/doll01.mdl" + { + "zho" "玩具娃娃" + } + "models/props_junk/garbage_hubcap01a.mdl" + { + "zho" "輪轂蓋" + } + "models/props_mall/mall_mannequin_rhand.mdl" + { + "zho" "模型手" + } + "models/props_lab/desklamp01.mdl" + { + "zho" "檯燈" + } + "models/props_vehicles/helicopter_rescue.mdl" + { + "zho" "大直升機" + } + "models/hybridphysx/news_helicoptor_map1_intro_v1.mdl" + { + "zho" "新聞直升機" + } + "models/missiles/f18_agm65maverick.mdl" + { + "zho" "導彈" + } + "models/props_vehicles/boat_fishing02.mdl" + { + "zho" "漁船" + } + "models/props_urban/big_wheel001.mdl" + { + "zho" "兒童三輪車" + } + "models/props_interiors/mounteddeerhead01.mdl" + { + "zho" "鹿頭布偶" + } + "models/props_misc/pot-1.mdl" + { + "zho" "平底鍋" + } + "models/props_c17/metalpot002a.mdl" + { + "zho" "煎鍋" + } + "models/gibs/hgibs.mdl" + { + "zho" "顱骨" + } + "models/props/cs_militia/caseofbeer01.mdl" + { + "zho" "啤酒箱" + } + "models/props/cs_office/chair_office.mdl" + { + "zho" "辦公椅子" + } + "models/props_cemetery/crypts_top02.mdl" + { + "zho" "墓地十字架" + } + "models/props_fairgrounds/kiddyland_riderocket_small.mdl" + { + "zho" "兒童樂園火箭" + } + "models/props_interiors/toilet_b_breakable01.mdl" + { + "zho" "馬桶" + } + "models/props_misc/lamp-1_gib1.mdl" + { + "zho" "燈罩" + } + "models/props_misc/saddle-1.mdl" + { + "zho" "馬鞍" + } + "models/props_unique/wheelchair01.mdl" + { + "zho" "輪椅" + } + "models/props_urban/mega_phone001.mdl" + { + "zho" "擴音器" + } + "models/props/de_train/de_train_floodlights_01.mdl" + { + "zho" "投光燈" + } + "models/props_vehicles/tire001c_car.mdl" + { + "zho" "汽車輪胎" + } + "models/props/de_inferno/ceiling_fan.mdl" + { + "zho" "吊扇" + } + "models/props/de_prodigy/fan.mdl" + { + "zho" "風扇" + } + "models/props_foliage/urban_pot_clay01.mdl" + { + "zho" "盆栽植物1" + } + "models/props_foliage/urban_pot_clay02.mdl" + { + "zho" "盆栽植物2" + } + "models/props_downtown/ironing_board_flat.mdl" + { + "zho" "熨衣板" + } + "models/props_collectables/coin.mdl" + { + "zho" "金幣" + } + "models/infected/jockey.mdl" + { + "zho" "迷你Jockey" + } + "models/items/l4d_gift.mdl" + { + "zho" "禮物盒" + } + "models/props_street/firehydrant.mdl" + { + "zho" "消防栓" + } + "models/props_junk/gnome.mdl" + { + "zho" "小矮人" + } + "models/bunny/b_sailboat.mdl" + { + "zho" "帆船" + } + "models/props/de_nuke/light_red2.mdl" + { + "zho" "紅色隔板燈" + } + "models/props_buildables/mine.mdl" + { + "zho" "輪轂罩" + } + "models/props_cemetery/crypts_top01.mdl" + { + "zho" "墓地十字架" + } + "models/props_misc/tea_pot-1.mdl" + { + "zho" "茶壺" + } + "models/props_fairgrounds/pyrotechnics_launcher.mdl" + { + "zho" "煙火" + } + "models/props_interiors/coffee_maker.mdl" + { + "zho" "咖啡機" + } + "models/props_interiors/pot01a.mdl" + { + "zho" "水壺" + } + "models/props_interiors/tv.mdl" + { + "zho" "電視機" + } + "models/props_interiors/lamp_floor_gib2.mdl" + { + "zho" "燈罩1" + } + "models/props_interiors/lamp_table02_gib2.mdl" + { + "zho" "燈罩2" + } + "models/props_lighting/semi_flush_001.mdl" + { + "zho" "燈罩3" + } + "models/props_junk/petfoodbag01.mdl" + { + "zho" "狗糧" + } + "models/props_lighting/flashlight_dropped_01.mdl" + { + "zho" "手電筒" + } + "models/props_lighting/lightfixture03.mdl" + { + "zho" "球燈" + } + "models/props_mall/cash_register.mdl" + { + "zho" "收銀機" + } + "models/props_placeable/tier1_guns_trophy.mdl" + { + "zho" "槍架1" + } + "models/props_placeable/tier2_guns_trophy.mdl" + { + "zho" "槍架2" + } + "models/weapons/arms/v_spitter_arms.mdl" + { + "zho" "Spitter的手" + } + "models/weapons/arms/v_jockey_arms.mdl" + { + "zho" "Jockey的爪" + } + "models/v_models/weapons/v_claw_hulk.mdl" + { + "zho" "Tank的爪" + } + "models/w_models/weapons/w_he_grenade.mdl" + { + "zho" "炮彈" + } + "models/props_junk/propanecanister001a.mdl" + { + "zho" "丙烷氣瓶" + } + "models/w_models/weapons/w_eq_medkit.mdl" + { + "zho" "醫療包帽" + } + "models/weapons/melee/w_riotshield.mdl" + { + "zho" "防暴盾牌" + } + "models/props_skybox/boat_rescue_tug_sunshine.mdl" + { + "zho" "遊艇" + } +} \ No newline at end of file diff --git a/addons/sourcemod/translations/zho/hats.phrases.txt b/addons/sourcemod/translations/zho/hats.phrases.txt new file mode 100644 index 000000000..1e1633552 --- /dev/null +++ b/addons/sourcemod/translations/zho/hats.phrases.txt @@ -0,0 +1,143 @@ +"Phrases" +{ + "Hat_Wearing" + { + "#format" "{1:s}" + "zho" "您正在配戴{ORANGE}{1}{DEFAULT}。" + } + "Hat_Changed" + { + "#format" "{1:s}" + "zho" "您正在配戴{ORANGE}{1}{DEFAULT}。" + } + "Hat_No_Index" + { + "#format" "{1:d}{2:d}" + "zho" "無法找到編號為{ORANGE}{1}{DEFAULT}的帽子。 (應在{ORANGE}1{DEFAULT}至{ORANGE}{2}{DEFAULT}內)" + } + "Hat_Not_Found" + { + "#format" "{1:s}" + "zho" "無法找到帽子{ORANGE}{1}{DEFAULT}。" + } + "Hat_Invalid" + { + "zho" "無效的玩家。" + } + "Hat_Unblocked" + { + "#format" "{1:s}" + "zho" "Unblocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_Blocked" + { + "#format" "{1:s}" + "zho" "Blocked hats for {ORANGE}{1}{DEFAULT}." + } + "Hat_View" + { + "#format" "{1:s}" + "zho" "您已{ORANGE}{1}{DEFAULT}第一人稱可見狀態。" + } + "Hat_Ability" + { + "#format" "{1:s}" + "zho" "您已{ORANGE}{1}{DEFAULT}帽子穿戴功能。" + } + "Hat_On" + { + "zho" "啟用" + } + "Hat_Off" + { + "zho" "停用" + } + "Hat_Menu_Title" + { + "zho" "選擇帽子:" + } + "Hat_Missing" + { + "zho" "無法配戴帽子。" + } + + "HAT_SYSTEM" + { + "zho" "{GREEN}[帽子]{DEFAULT} " + } + "HAT_NOT_RIGHT_NOW" + { + "zho" "暫時無法使用。" + } + "ADMIN_DISABLE_HAT" + { + "zho" "選擇玩家停用帽子:" + } + "ADMIN_CHANGE_HAT" + { + "zho" "選擇玩家更換帽子:" + } + "ADMIN_VISIBILITY" + { + "zho" "Select player to set hat visibility:" + } + "HAT_SET_ANGLE" + { + "zho" "選擇帽子角度:" + } + "HAT_SET_POSITION" + { + "zho" "選擇帽子位置:" + } + "HAT_SET_SIZE" + { + "zho" "選擇帽子大細:" + } + "HAT_MAIN" + { + "zho" "選擇動作:" + } + "HAT_MENU" + { + "zho" "打開帽子選單" + } + "HAT_WORE" + { + "zho" "穿戴狀態" + } + "HAT_VIEWABLE" + { + "zho" "第一人稱可見狀態" + } + "HAT_ENABLED" + { + "zho" "已啟用" + } + "HAT_DISABLED" + { + "zho" "己停用" + } + "HAT_VIEWABLE_TP" + { + "zho" "第三人稱可見狀態" + } + "HAT_VISIBILITY" + { + "zho" "顥示其他玩家的帽子" + } + "Hat_ViewTP" + { + "#format" "{1:s}" + "zho" "您已{ORANGE}{1}{DEFAULT}第三人稱可見狀態" + } + "Hat_ViewSet" + { + "#format" "{1:s}{2:s}" + "zho" "{ORANGE}{2}{DEFAULT}玩家{ORANGE}{1}{DEFAULT}顥示其他玩家的帽子" + } + "Hat_Visibility_Set" + { + "#format" "{1:s}" + "zho" "顯示其他玩家的帽子:{ORANGE}{1}{DEFAULT}" + } +} \ No newline at end of file