diff --git a/.gitignore b/.gitignore index 6fd0a37..be839f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1,41 @@ -# Compiled Lua sources -luac.out - -# luarocks build files -*.src.rock -*.zip -*.tar.gz - -# Object files -*.o -*.os -*.ko -*.obj -*.elf - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo -*.def -*.exp - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + diff --git a/Description.txt b/Description.txt new file mode 100644 index 0000000..685ac05 --- /dev/null +++ b/Description.txt @@ -0,0 +1,14 @@ +Based on the original "Prop Hunt" game mode, Xaymar's Custom Prop Hunt brings you some much needed changes to freshen up the gamemode. + +New features: +- Easy configuration - configuration is now stored inside data\prop_hunt\ as a key-values table! +- Default Taunts are no longer infringing on copyrighted material - taunt your life away as a streamer or YouTuber! +- Taunt Pack support - add new taunts by just downloading addons, no file modification needed! +- Prop Rotation - Hit F4 and dance- err I ment rotate! + +Fixes: +- Camera can now no longer glitch through the world. +- Camera root position now closely follows the prop size. +- Prop sizes are now set correctly - previously you could make your way behind things with props way too big. +- Props can no longer heal themselves by becoming prop, they'll always keep the same percentage of health. +- Picking a prop is now more accurate, picking from where your camera is looking. \ No newline at end of file diff --git a/Gamemode/addon.json b/Gamemode/addon.json new file mode 100644 index 0000000..229a1a2 --- /dev/null +++ b/Gamemode/addon.json @@ -0,0 +1,7 @@ +{ + "title" : "Xaymar's Custom Prop Hunt ", + "type" : "gamemode", + "tags" : [ "fun" ], + "ignore" : [ + ] +} \ No newline at end of file diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/banned_props.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/banned_props.txt new file mode 100644 index 0000000..934fd59 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/banned_props.txt @@ -0,0 +1,8 @@ +"TableToKeyValues" +{ + "1" "models/props/cs_assault/dollar.mdl" + "2" "models/props/cs_assault/money.mdl" + "3" "models/props/cs_office/snowman_arm.mdl" + "4" "models/props_junk/garbage_plasticbottle001a.mdl" + "5" "models/props/cs_office/projector_remote.mdl" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/config.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/config.txt new file mode 100644 index 0000000..c439f2f --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/config.txt @@ -0,0 +1,8 @@ +"TableToKeyValues" +{ + "TAUNT_DELAY" "0.1" + "ROUNDS_PER_MAP" "20" + "ROUND_TIME" "240" + "SWAP_TEAM_EVERY_ROUND" "1" + "GAME_TIME" "20" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_loss.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_loss.txt new file mode 100644 index 0000000..fbb371b --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_loss.txt @@ -0,0 +1,26 @@ +"TableToKeyValues" +{ + "1" "bot/aw_hell.wav" + "2" "bot/aww_man.wav" + "3" "bot/anyone_see_anything.wav" + "4" "bot/anyone_see_them.wav" + "5" "bot/come_out_and_fight_like_a_man.wav" + "6" "bot/come_out_wherever_you_are.wav" + "7" "bot/he_got_away.wav" + "8" "bot/he_got_away2.wav" + "9" "bot/i_dont_know_where_he_went.wav" + "10" "bot/i_got_nothing.wav" + "11" "bot/nothing_happening_over_here.wav" + "12" "bot/nothing_here.wav" + "13" "bot/nothing_moving_over_here.wav" + "14" "bot/thats_not_good.wav" + "15" "bot/theres_too_many.wav" + "16" "bot/theres_too_many_of_them.wav" + "17" "bot/theyre_all_over_the_place2.wav" + "18" "bot/theyre_everywhere2.wav" + "19" "bot/too_many2.wav" + "20" "bot/what_happened.wav" + "21" "bot/what_have_you_done.wav" + "22" "bot/where_are_they.wav" + "23" "bot/where_are_you_hiding.wav" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_hunter.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_hunter.txt new file mode 100644 index 0000000..c29b6ff --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_hunter.txt @@ -0,0 +1,20 @@ +"TableToKeyValues" +{ + "1" "bot/a_bunch_of_them.wav" + "2" "bot/come_out_and_fight_like_a_man.wav" + "3" "bot/come_out_wherever_you_are.wav" + "4" "bot/come_to_papa.wav" + "5" "bot/dont_worry_hell_get_it.wav" + "6" "bot/hang_on_i_heard_something.wav" + "7" "bot/hang_on_im_coming.wav" + "8" "bot/i_dont_think_so.wav" + "9" "bot/i_have_the_hostages.wav" + "10" "bot/i_see_our_target.wav" + "11" "bot/im_waiting_here.wav" + "12" "bot/keeping_an_eye_on_the_hostages.wav" + "13" "bot/nnno_sir.wav" + "14" "bot/spotted_the_delivery_boy.wav" + "15" "bot/target_acquired.wav" + "16" "bot/target_spotted.wav" + "17" "bot/you_heard_the_man_lets_go.wav" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_prop.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_prop.txt new file mode 100644 index 0000000..889e742 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_taunt_prop.txt @@ -0,0 +1,131 @@ +"TableToKeyValues" +{ + "1" "ambient/alarms/apc_alarm_pass1.wav" + "2" "ambient/alarms/manhack_alert_pass1.wav" + "3" "ambient/alarms/razortrain_horn1.wav" + "4" "ambient/alarms/scanner_alert_pass1.wav" + "5" "ambient/alarms/train_horn2.wav" + "6" "ambient/alarms/train_horn_distant1.wav" + "7" "ambient/alarms/warningbell1.wav" + "8" "ambient/energy/whiteflash.wav" + "9" "ambient/intro/alyxremove.wav" + "10" "ambient/intro/logosfx.wav" + "11" "ambient/levels/launch/1stfiringwarning.wav" + "12" "ambient/levels/launch/rockettakeoffblast.wav" + "13" "ambient/misc/ambulance1.wav" + "14" "ambient/misc/carhonk1.wav" + "15" "ambient/misc/carhonk2.wav" + "16" "ambient/misc/carhonk3.wav" + "17" "ambient/outro/gunshipcrash.wav" + "18" "ambient/3dmeagle.wav" + "19" "beams/beamstart5.wav" + "20" "buttons/bell1.wav" + "21" "buttons/weapon_cant_buy.wav" + "22" "common/bass.wav" + "23" "common/bugreporter_failed.wav" + "24" "common/warning.wav" + "25" "doors/door_squeek1.wav" + "26" "friends/friend_join.wav" + "27" "friends/friend_online.wav" + "28" "friends/message.wav" + "29" "hostage/hunuse/comeback.wav" + "30" "hostage/hunuse/dontleaveme.wav" + "31" "hostage/hunuse/yeahillstay.wav" + "32" "items/gift_drop.wav" + "33" "music/radio1.mp3" + "34" "phx/eggcrack.wav" + "35" "plats/elevbell1.wav" + "36" "player/headshot1.wav" + "37" "player/headshot2.wav" + "38" "player/sprayer.wav" + "39" "radio/enemydown.wav" + "40" "radio/go.wav" + "41" "radio/locknload.wav" + "42" "radio/negative.wav" + "43" "radio/rounddraw.wav" + "44" "radio/takepoint.wav" + "45" "resource/warning.wav" + "46" "ui/achievement_earned.wav" + "47" "ui/freeze_cam.wav" + "48" "vehicles/junker/radar_ping_friendly1.wav" + "49" "weapons/c4/c4_beep1.wav" + "50" "weapons/c4/c4_click.wav" + "51" "weapons/awp/awp1.wav" + "52" "vo/canals/female01/gunboat_giveemhell.wav" + "53" "vo/canals/female01/gunboat_justintime.wav" + "54" "vo/canals/female01/stn6_incoming.wav" + "55" "vo/canals/male01/gunboat_giveemhell.wav" + "56" "vo/canals/male01/gunboat_justintime.wav" + "57" "vo/canals/male01/stn6_incoming.wav" + "58" "vo/canals/al_radio_stn6.wav" + "59" "vo/canals/arrest_getgoing.wav" + "60" "vo/canals/arrest_helpme.wav" + "61" "vo/canals/arrest_lookingforyou.wav" + "62" "vo/canals/boxcar_lethimhelp.wav" + "63" "vo/canals/matt_closecall.wav" + "64" "vo/canals/premassacre.wav" + "65" "vo/ravenholm/aimforhead.wav" + "66" "vo/ravenholm/bucket_patience.wav" + "67" "vo/ravenholm/madlaugh01.wav" + "68" "vo/ravenholm/madlaugh02.wav" + "69" "vo/ravenholm/madlaugh03.wav" + "70" "vo/ravenholm/madlaugh04.wav" + "71" "weapons/strider_buster/ol12_stickybombcreator.wav" + "72" "weapons/c4/c4_explode1.wav" + "73" "weapons/357/357_fire2.wav" + "74" "weapons/357/357_fire3.wav" + "75" "weapons/scout/scout_fire-1.wav" + "76" "weapons/smokegrenade/sg_explode.wav" + "77" "weapons/grenade_launcher1.wav" + "78" "weapons/explode3.wav" + "79" "weapons/underwater_explode3.wav" + "80" "items/nvg_on.wav" + "81" "hostage/huse/letsdoit.wav" + "82" "hostage/huse/illfollow.wav" + "83" "hostage/huse/getouttahere.wav" + "84" "doors/door_screen_move1.wav" + "85" "doors/heavy_metal_stop1.wav" + "86" "doors/default_move.wav" + "87" "common/stuck2.wav" + "88" "ambient/water_splash1.wav" + "89" "ambient/water_splash2.wav" + "90" "ambient/water_splash3.wav" + "91" "ambient/weather/thunder1.wav" + "92" "ambient/weather/thunder2.wav" + "93" "ambient/weather/thunder3.wav" + "94" "ambient/weather/thunder4.wav" + "95" "ambient/weather/thunder5.wav" + "96" "ambient/weather/thunder6.wav" + "97" "ambient/outro/thunder7.wav" + "98" "ambient/voices/crying_loop1.wav" + "99" "ambient/voices/playground_memory.wav" + "100" "ambient/voices/f_scream1.wav" + "101" "ambient/voices/m_scream1.wav" + "102" "ambient/voices/cough1.wav" + "103" "ambient/voices/cough2.wav" + "104" "ambient/voices/cough3.wav" + "105" "ambient/voices/cough4.wav" + "106" "ambient/overhead/plane1.wav" + "107" "ambient/overhead/plane2.wav" + "108" "ambient/overhead/plane3.wav" + "109" "ambient/overhead/hel1.wav" + "110" "ambient/overhead/hel2.wav" + "111" "ambient/misc/truck_backup1.wav" + "112" "ambient/misc/truck_drive1.wav" + "113" "ambient/misc/truck_drive2.wav" + "114" "ambient/machines/pneumatic_drill_1.wav" + "115" "ambient/machines/pneumatic_drill_2.wav" + "116" "ambient/machines/pneumatic_drill_3.wav" + "117" "ambient/machines/pneumatic_drill_4.wav" + "118" "ambient/machines/station_train_squeel.wav" + "119" "ambient/machines/ticktock.wav" + "120" "ambient/creatures/teddy.wav" + "121" "ambient/creatures/town_child_scream1.wav" + "122" "ambient/creatures/town_moan1.wav" + "123" "ambient/creatures/town_muffled_cry1.wav" + "124" "ambient/creatures/town_scared_breathing1.wav" + "125" "ambient/creatures/town_scared_breathing2.wav" + "126" "ambient/creatures/town_scared_sob1.wav" + "127" "ambient/creatures/town_scared_sob2.wav" + "128" "ambient/creatures/town_zombie_call1.wav" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_victory.txt b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_victory.txt new file mode 100644 index 0000000..7e950e1 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/data/prop_hunt/sounds_victory.txt @@ -0,0 +1,52 @@ +"TableToKeyValues" +{ + "1" "bot/and_thats_how_its_done.wav" + "2" "bot/come_to_papa.wav" + "3" "bot/do_not_mess_with_me.wav" + "4" "bot/dropped_him.wav" + "5" "bot/enemy_down.wav" + "6" "bot/enemy_down2.wav" + "7" "bot/good_job_team.wav" + "8" "bot/got_him.wav" + "9" "bot/hes_broken.wav" + "10" "bot/hes_dead.wav" + "11" "bot/hes_done.wav" + "12" "bot/hes_down.wav" + "13" "bot/its_a_party.wav" + "14" "bot/i_am_dangerous.wav" + "15" "bot/i_am_on_fire.wav" + "16" "bot/i_got_more_where_that_came_from.wav" + "17" "bot/i_wasnt_worried_for_a_minute.wav" + "18" "bot/killed_him.wav" + "19" "bot/look_out_brag.wav" + "20" "bot/made_him_cry.wav" + "21" "bot/oh_yea.wav" + "22" "bot/oh_yea2.wav" + "23" "bot/owned.wav" + "24" "bot/ruined_his_day.wav" + "25" "bot/tag_them_and_bag_them.wav" + "26" "bot/thats_the_way_this_is_done.wav" + "27" "bot/that_was_a_close_one.wav" + "28" "bot/that_was_it.wav" + "29" "bot/that_was_the_last_guy.wav" + "30" "bot/that_was_the_last_one.wav" + "31" "bot/they_never_knew_what_hit_them.wav" + "32" "bot/they_will_not_escape.wav" + "33" "bot/they_wont_get_away.wav" + "34" "bot/they_wont_get_away2.wav" + "35" "bot/this_is_my_house.wav" + "36" "bot/took_him_down.wav" + "37" "bot/took_him_out.wav" + "38" "bot/took_him_out2.wav" + "39" "bot/wasted_him.wav" + "40" "bot/way_to_be_team.wav" + "41" "bot/well_done.wav" + "42" "bot/we_owned_them.wav" + "43" "bot/whew_that_was_close.wav" + "44" "bot/whoo.wav" + "45" "bot/whoo2.wav" + "46" "bot/whos_the_man.wav" + "47" "bot/who_wants_some_more.wav" + "48" "bot/yesss.wav" + "49" "bot/yesss2.wav" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/cl_init.lua b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/cl_init.lua new file mode 100644 index 0000000..83f6547 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/cl_init.lua @@ -0,0 +1,7 @@ +-- Include needed files +include("shared.lua") + +-- Draw entity model. +function ENT:Draw() + self:DrawModel() +end \ No newline at end of file diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/init.lua b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/init.lua new file mode 100644 index 0000000..70cc2d1 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/init.lua @@ -0,0 +1,50 @@ +--Thanks to Blasteh for gmod update the fix and nifnat for spectate fix! + +-- Send required files to client +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("shared.lua") + + +-- Include needed files +include("shared.lua") + +-- Called when we take damge +function ENT:OnTakeDamage(dmg) + local pl = self.Owner + local attacker = dmg:GetAttacker() + local inflictor = dmg:GetInflictor() + + -- Health + if pl && pl:IsValid() && pl:Alive() && pl:IsPlayer() && attacker:IsPlayer() && dmg:GetDamage() > 0 then + pl:SetHealth(pl:Health() - dmg:GetDamage()) + + if pl:Health() <= 0 then + -- Figure out real attacker. + if inflictor && inflictor == attacker && inflictor:IsPlayer() then + inflictor = inflictor:GetActiveWeapon() + if !inflictor || inflictor == NULL then inflictor = attacker end + end + + -- Kill player and remove the prop + pl:KillSilent() + pl:RemoveProp() -- Needs to be executed before net.Broadcast + + -- Send kill message. + net.Start( "PlayerKilledByPlayer" ) + net.WriteEntity( pl ) + net.WriteString( inflictor:GetClass() ) + net.WriteEntity( attacker ) + net.Broadcast() + + -- Broadcast same message to all players in console. + MsgAll(attacker:Name() .. " found and killed " .. pl:Name() .. "\n") + + -- Increment frags and deaths. + attacker:AddFrags(1) + pl:AddDeaths(1) + + -- Update attacker health + attacker:SetHealth(math.Clamp(attacker:Health() + GetConVar("HUNTER_KILL_BONUS"):GetInt(), 1, 100)) + end + end +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/shared.lua b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/shared.lua new file mode 100644 index 0000000..37734d3 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/entities/entities/ph_prop/shared.lua @@ -0,0 +1,46 @@ +-- Entity information +ENT.Type = "anim" +ENT.Base = "base_anim" + +-- Initialize +function ENT:Initialize() + self:SetModel("models/player/Kleiner.mdl") + self:DrawShadow(true); + + -- Physical properties + self:SetSolid(SOLID_OBB) + + -- Initialize Networked Variables + self:SetApplyNewAngles(false) + self:SetHealth(100) + if SERVER then self:SetMaxHealth(100) end + if CLIENT then self.Owner.ph_prop = self.Entity end +end + +-- Set up shared Data. +function ENT:SetupDataTables() + self:NetworkVar( "Bool", 0, "ApplyNewAngles" ) +end + +-- Update position +function ENT:Think() + -- Calculate hull size. + local hullOBBMin = self:OBBMins() + local hullOBBMax = self:OBBMaxs() + local hullOBB = hullOBBMax - hullOBBMin + + -- Shared update + local pos = -Vector(0, 0, hullOBBMin.z) + self:SetPos(self.Owner:GetPos() + pos) + self:SetVelocity(self:GetOwner():GetVelocity()) + + -- Server only updates + if SERVER then + -- Prevent confusion by updating angles only on the server. + if self:GetApplyNewAngles() then + self:SetAngles(Angle(0, self:GetOwner():GetAngles().yaw, 0)) + end + + self:NextThink( CurTime() ) + end +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/entities/weapons/weapon_ph_smg.lua b/Gamemode/gamemodes/xaymars_prop_hunt/entities/weapons/weapon_ph_smg.lua new file mode 100644 index 0000000..5ff146b --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/entities/weapons/weapon_ph_smg.lua @@ -0,0 +1,210 @@ +-- SWEP Information +SWEP.Author = "Michael 'Xaymar' Dirks" +SWEP.Contact = "info@project-kube.de" +SWEP.Purpose = "More accurate SMG for Prop Hunt." +SWEP.Instructions = "Fire away! Alternative fire to fire a grenade." +SWEP.Category = "Prop Hunt Weapons" + +-- Weapon Information +SWEP.Weight = 5 +SWEP.AutoSwitchTo = false +SWEP.AutoSwitchFrom = false + +SWEP.Slot = 2 +SWEP.SlotPos = 1 +SWEP.DrawAmmo = true +SWEP.DrawCrosshair = true + +-- Weapon is spawnable for everyone, not just administrators. +SWEP.Spawnable = true +SWEP.AdminSpawnable = true + +-- Model +SWEP.ViewModel = "models/weapons/c_smg1.mdl" +SWEP.WorldModel = "models/weapons/w_smg1.mdl" +SWEP.UseHands = true + +-- Primary Ammunition: SMG +SWEP.Primary.ClipSize = 45 +SWEP.Primary.DefaultClip = 45 +SWEP.Primary.Automatic = true +SWEP.Primary.Ammo = "SMG1" +SWEP.Primary.Damage = 5 + +-- Secondary Ammunition: SMG Grenades +SWEP.Secondary.ClipSize = 1 +SWEP.Secondary.DefaultClip = 0 +SWEP.Secondary.Automatic = false +SWEP.Secondary.Ammo = "SMG1_Grenade" +SWEP.Secondary.Damage = 100 + +-- Recoil +SWEP.Recoil = {} +SWEP.Recoil.SingleFire = 1.0 +SWEP.Recoil.BurstFire = 4.0 +SWEP.Recoil.SecondaryFire = 8.0 + +-- Accuracy +SWEP.Accuracy = {} +SWEP.Accuracy.Primary = {} +SWEP.Accuracy.Primary.Min = 0.975 +SWEP.Accuracy.Primary.Max = 1.00 +SWEP.Accuracy.Primary.Reduction = 0.005 +SWEP.Accuracy.Primary.Recovery = 0.0025 +SWEP.Accuracy.Primary.Delay = 0.1 +SWEP.Accuracy.Secondary = {} +SWEP.Accuracy.Secondary.Min = 1.00 +SWEP.Accuracy.Secondary.Max = 1.00 +SWEP.Accuracy.Secondary.Reduction = 0.00 +SWEP.Accuracy.Secondary.Recovery = 0.00 +SWEP.Accuracy.Secondary.Delay = 0.00 + +-- Sounds +SWEP.Sound = {} +SWEP.Sound.SwitchSingle = "weapons/smg1/switch_single.wav" +SWEP.Sound.SwitchBurst = "weapons/smg1/switch_burst.wav" +SWEP.Sound.SingleFire = "weapons/smg1/smg1_fire1.wav" +SWEP.Sound.BurstFire = "weapons/smg1/smg1_fireburst1.wav" +SWEP.Sound.SecondaryFire = "weapons/ar2/ar2_altfire.wav" +SWEP.Sound.Reload = "weapons/smg1/smg1_reload.wav" +SWEP.Sound.NoPrimaryAmmo = "weapons/pistol/pistol_empty.wav" +SWEP.Sound.NoSecondaryAmmo = "weapons/pistol/pistol_empty.wav" + +-- Initialization +function SWEP:Initialize() + -- Set holding type to smg. + self:SetHoldType("smg"); + + -- Precache sounds for lagless experience. + for i,v in ipairs(self.Sound) do + util.PrecacheSound(v) + end + + -- Initialize default values. + self.BurstFire = false + self.PrimaryAccuracy = self.Accuracy.Primary.Max + self.LastPrimaryAttack = CurTime() + self.SecondaryAccuracy = self.Accuracy.Secondary.Max + self.LastSecondaryAttack = CurTime() +end + +-- Primary Attack +function SWEP:CanPrimaryAttack() + -- Check if there is ammo in the clip. + if (self:Clip1() <= 0) then + -- If not, check if there's ammo available. + if (self:Ammo1() > 0) then + -- If yes, reload and wait for weapon to be ready again. + self:EmitSound(self.Sound.NoPrimaryAmmo) + self:Reload() + return false + end + + -- If no, emit empty sound for primary fire. + self:EmitSound(self.Sound.NoPrimaryAmmo) + self:SetNextPrimaryFire(CurTime() + 0.1) + return false + end + + -- Otherwise, return true. + return true +end + +function SWEP:PrimaryAttack() + -- Can't fire without Ammo + if (!self:CanPrimaryAttack()) then return end + + if (self.BurstFire == false) then + -- Single Mode: fire and take one bullet from the clip. + self:ShootBullet(self.Primary.Damage, bullet_count, 1.0 - self.PrimaryAccuracy) + self:TakePrimaryAmmo(1) + self:EmitSound(self.Sound.SingleFire) + self:SetNextPrimaryFire( CurTime() + 0.1 ) + + -- Apply recoil + self.Owner:ViewPunch( Angle(-1, 0, 0) * self.Recoil.SingleFire * (1 + (1 - self.PrimaryAccuracy)) ) + + -- Decrease accuracy + self.PrimaryAccuracy = math.Clamp(self.PrimaryAccuracy - self.Accuracy.Primary.Reduction, self.Accuracy.Primary.Min, self.Accuracy.Primary.Max) + else + -- Burst Mode: fire and take up to three bullets from the clip + local bulletCount = math.Clamp(self:Clip1(), 1, 3) + self:ShootBullet(self.Primary.Damage, bullet_count, 1.0 - self.PrimaryAccuracy) + self:TakePrimaryAmmo(bulletCount) + self:EmitSound(self.Sound.BurstFire) + self:SetNextPrimaryFire( CurTime() + 0.5 * (bulletCount / 3.0) ) + + -- Apply recoil + self.Owner:ViewPunch(Angle(-1, 0, 0) * self.Recoil.BurstFire * (bulletCount / 3.0) * (1 + (1 - self.PrimaryAccuracy))) + + -- Decrease accuracy + self.PrimaryAccuracy = math.Clamp(self.PrimaryAccuracy - self.Accuracy.Primary.Reduction * bulletCount, self.Accuracy.Primary.Min, self.Accuracy.Primary.Max) + end + + -- Set Animation and attack time. + self:SendWeaponAnim(ACT_VM_PRIMARYATTACK) + self.LastPrimaryAttack = CurTime() + return +end + + +-- Secondary Attack +function SWEP:CanSecondaryAttack() + if (self:Clip2() == 0) then + if (self:Ammo2() == 0) then + self:EmitSound(self.Sound.NoSecondaryAmmo) + self:SetNextSecondaryFire( CurTime() + 1.0 ) + return false + else + self:SetClip2( 1 ) + self.Owner:SetAmmo( self.Owner:GetAmmoCount( self:GetSecondaryAmmoType() ) - 1, self:GetSecondaryAmmoType() ) + end + end + + return true +end + +function SWEP:SecondaryAttack() + -- Can't fire without Ammo + if (!self:CanSecondaryAttack()) then return end + + -- Emit a sound. + self:EmitSound(self.Sound.SecondaryFire) + self:SetNextSecondaryFire( CurTime() + 1.0 ) + self:TakeSecondaryAmmo(1) + + -- Create grenade + if SERVER then + local grenade = ents.Create("grenade_ar2") + if (!IsValid(grenade)) then return end + grenade:SetPos( self.Owner:GetShootPos() + self.Owner:GetAimVector() * 30 ) + grenade:SetVelocity( self.Owner:GetAimVector() * 1000 ) + grenade:SetAngles( self.Owner:GetAngles() ) + grenade:SetOwner( self.Owner ) + grenade:Spawn() + grenade:SetPhysicsAttacker( self.Owner, 60 ) + end + + -- Set Animation and attack time. + self:SendWeaponAnim(ACT_VM_SECONDARYATTACK) + self.LastSecondaryAttack = CurTime() + return +end + +-- Reload: Combination of reloading and switching fire type. +function SWEP:Reload() + if self:DefaultReload(ACT_VM_RELOAD) then self:EmitSound(self.Sound.Reload) end + return +end + +-- Think: Restore accuracy over time. +function SWEP:Think() + local ThinkTime = CurTime() + + if (ThinkTime >= (self.LastPrimaryAttack + self.Accuracy.Primary.Delay)) then + self.PrimaryAccuracy = math.Clamp(self.PrimaryAccuracy + self.Accuracy.Primary.Recovery, self.Accuracy.Primary.Min, self.Accuracy.Primary.Max) + end + if (ThinkTime >= (self.LastSecondaryAttack + self.Accuracy.Secondary.Delay)) then + self.SecondaryAccuracy = math.Clamp(self.SecondaryAccuracy + self.Accuracy.Secondary.Recovery, self.Accuracy.Secondary.Min, self.Accuracy.Secondary.Max) + end +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_init.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_init.lua new file mode 100644 index 0000000..c16c637 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_init.lua @@ -0,0 +1,178 @@ +-- Include the needed files +include("sh_init.lua") +--include("cl_hints.lua") + +-- Draw round timeleft and hunter release timeleft +function HUDPaint() + if GetGlobalBool("InRound", false) then + local blindlock_time_left = (GetConVar("HUNTER_BLINDLOCK_TIME"):GetInt() - (CurTime() - GetGlobalFloat("RoundStartTime", 0))) + 1 + + if blindlock_time_left < 1 && blindlock_time_left > -6 then + blindlock_time_left_msg = "Hunters have been released!" + elseif blindlock_time_left > 0 then + blindlock_time_left_msg = "Hunters will be unblinded and released in "..string.ToMinutesSeconds(blindlock_time_left) + else + blindlock_time_left_msg = nil + end + + if LocalPlayer():Team() == TEAM_HUNTERS && blindlock_time_left > 1 then + draw.RoundedBox(0, 0, 0, surface.ScreenWidth(), surface.ScreenHeight(), Color(20, 20, 20, 255)) + end + + if blindlock_time_left_msg then + surface.SetFont("MyFont") + local tw, th = surface.GetTextSize(blindlock_time_left_msg) + + draw.RoundedBox(8, 20, 20, tw + 20, 26, Color(0, 0, 0, 75)) + draw.DrawText(blindlock_time_left_msg, "MyFont", 31, 26, Color(255, 255, 0, 255), TEXT_ALIGN_LEFT) + end + end +end +hook.Add("HUDPaint", "PH_HUDPaint", HUDPaint) + +-- Called immediately after starting the gamemode +function Initialize() + hullz = 80 + --surface.CreateFont("Arial", 14, 1200, true, false, "ph_arial") + surface.CreateFont( "MyFont", + { + font = "Arial", + size = 14, + weight = 1200, + antialias = true, + underline = false + }) +end +hook.Add("Initialize", "PH_Initialize", Initialize) + + +-- Resets the player hull +function ResetHull(um) + if LocalPlayer() && LocalPlayer():IsValid() then + LocalPlayer():ResetHull() + LocalPlayer():SetViewOffset(Vector(0, 0, 64)) + LocalPlayer():SetViewOffsetDucked(Vector(0, 0, 28)) + hullz = 80 + end +end +usermessage.Hook("ResetHull", ResetHull) + + +-- Sets the local blind variable to be used in CalcView +function SetBlind(um) + blind = um:ReadBool() +end +usermessage.Hook("SetBlind", SetBlind) + + +function GM:ShouldDrawLocalPlayer() + return false +end + +function GM:CalcView( ply, pos, ang, fov ) + local view = { + origin = pos, + angles = ang, + fov = fov + } + + if ply:Team() == TEAM_PROPS && ply:Alive() then + -- Fix lua errors by doing this instead. + if LocalPlayer().ThirdPersonDistance == nil then + LocalPlayer().ThirdPersonDistance = 100 + end + + -- Slowly return ThirdPersonDistance to 100 + local traceDist = math.Clamp(LocalPlayer().ThirdPersonDistance * 0.98 + 100 * 0.02, 0, 100) + + -- Trace a line to find the correct position for the camera. + local tracePos = pos + local trace = { + -- Start somewhat outside the prop. + start = tracePos - (ang:Forward() * 5), + endpos = tracePos - (ang:Forward() * (traceDist + 10)), + filter = function(ent) + if (ent == LocalPlayer() || ent == LocalPlayer().ph_prop || ent == LocalPlayer().ThirdPersonFilter || ent == ThirdPersonFilter) then + return false + elseif (ent:GetClass() == "ph_prop" || ent:GetClass() == "worldspawn") then + return false + end + return true + end + } + local traceResult = util.TraceLine(trace); + + -- Readjust trace hit distance to force camera outside of the hit position. + if traceResult.Hit then + traceDist = math.max(traceResult.HitPos:Distance(tracePos) - 10, 0) + end + LocalPlayer().ThirdPersonDistance = traceDist + + -- Correct view position + view.origin = tracePos - (ang:Forward() * traceDist) +--[[ else + local wep = ply:GetActiveWeapon() + if wep && wep != NULL then + local func = wep.GetViewModelPosition + if func then + view.vm_origin, view.vm_angles = func(wep, origin*1, angles*1) -- Note: *1 to copy the object so the child function can't edit it. + end + + local func = wep.CalcView + if func then + view.origin, view.angles, view.fov = func(wep, pl, origin*1, angles*1, fov) -- Note: *1 to copy the object so the child function can't edit it. + end + end--]] + end + + return view; +end + +-- Render halos and player names. +function DrawPlayerNames(bDrawingDepth, bDrawingSkybox) + for i,v in ipairs(player.GetAll()) do + if v:Alive() && v != LocalPlayer() then + local pos = v:GetPos() + v:GetViewOffset() + Vector(0, 0, 5) + local ang = Angle(0, LocalPlayer():EyeAngles().y - 90, 90 - LocalPlayer():EyeAngles().x) + local healthPrc = v:Health() / v:GetMaxHealth() + local healthCol = HSVToColor(120 * healthPrc, 1.0, 1.0) + + if v:Team() == TEAM_HUNTERS || LocalPlayer():Team() == TEAM_PROPS then + cam.Start3D2D(pos, ang, 0.15) + draw.DrawText(v:GetName(), "Trebuchet24", 0, -draw.GetFontHeight("Trebuchet24"), healthCol, TEXT_ALIGN_CENTER) + cam.End3D2D() + end + end + end +end +hook.Add("PostDrawTranslucentRenderables", "PH_DrawPlayerNames", DrawPlayerNames) + +function DrawPlayerHalos(bDrawingDepth, bDrawingSkybox) + for i,v in ipairs(player.GetAll()) do + if v:Alive() then + local pos = v:GetPos() + Vector(0, 0, 1) * (v:OBBMaxs().z - v:OBBMins().z + 5) + local ang = Angle(0, LocalPlayer():EyeAngles().y - 90, 90 - LocalPlayer():EyeAngles().x) + local healthPrc = v:Health() / v:GetMaxHealth() + local healthCol = HSVToColor(120 * healthPrc, 1.0, 1.0) + + if v:Team() == TEAM_HUNTERS || LocalPlayer():Team() == TEAM_PROPS then + local ent = v + if v.ph_prop && v.ph_prop:IsValid() then ent = v.ph_prop end + + halo.Add({ent}, healthCol, 2, 2, 1) + end + end + end +end +--hook.Add("PostDrawEffects", "PH_DrawPlayerHalos", DrawPlayerHalos) + +-- UMSG: Update player Hull and health. +function UMSGSetHull(um) + local hullOBBMin = um:ReadVector() + local hullOBBMax = um:ReadVector() + local new_health = um:ReadShort() + + LocalPlayer():NewHull(hullOBBMin, hullOBBMax) + LocalPlayer():SetHealth(new_health) +end +usermessage.Hook("SetHull", UMSGSetHull) diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_worldtips.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_worldtips.lua new file mode 100644 index 0000000..c4ed6e0 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/cl_worldtips.lua @@ -0,0 +1,93 @@ + + +surface.CreateFont( "GModWorldtip", +{ + font = "Helvetica", + size = 20, + weight = 700 +}) + +local cl_drawworldtooltips = CreateConVar( "cl_drawworldtooltips", "1", { FCVAR_ARCHIVE } ) +local WorldTip = nil + +local TipColor = Color( 250, 250, 200, 255 ) + +-- +-- Adds a hint to the queue +-- +function AddWorldTip( unused1, text, unused2, pos, ent ) + + WorldTip = {} + + WorldTip.dietime = SysTime() + 0.05 + WorldTip.text = text + WorldTip.pos = pos + WorldTip.ent = ent + +end + + +local function DrawWorldTip( tip ) + + if ( IsValid( tip.ent ) ) then + tip.pos = tip.ent:GetPos() + end + + local pos = tip.pos:ToScreen() + + local black = Color( 0, 0, 0, 255 ) + local tipcol = Color( TipColor.r, TipColor.g, TipColor.b, 255 ) + + local x = 0 + local y = 0 + local padding = 10 + local offset = 50 + + surface.SetFont( "GModWorldtip" ) + local w, h = surface.GetTextSize( tip.text ) + + x = pos.x - w + y = pos.y - h + + x = x - offset + y = y - offset + + draw.RoundedBox( 8, x-padding-2, y-padding-2, w+padding*2+4, h+padding*2+4, black ) + + + local verts = {} + verts[1] = { x=x+w/1.5-2, y=y+h+2 } + verts[2] = { x=x+w+2, y=y+h/2-1 } + verts[3] = { x=pos.x-offset/2+2, y=pos.y-offset/2+2 } + + draw.NoTexture() + surface.SetDrawColor( 0, 0, 0, tipcol.a ) + surface.DrawPoly( verts ) + + + draw.RoundedBox( 8, x-padding, y-padding, w+padding*2, h+padding*2, tipcol ) + + local verts = {} + verts[1] = { x=x+w/1.5, y=y+h } + verts[2] = { x=x+w, y=y+h/2 } + verts[3] = { x=pos.x-offset/2, y=pos.y-offset/2 } + + draw.NoTexture() + surface.SetDrawColor( tipcol.r, tipcol.g, tipcol.b, tipcol.a ) + surface.DrawPoly( verts ) + + + draw.DrawText( tip.text, "GModWorldtip", x + w/2, y, black, TEXT_ALIGN_CENTER ) + +end + + +function GM:PaintWorldTips() + + if ( !cl_drawworldtooltips:GetBool() ) then return end + + if ( WorldTip && WorldTip.dietime > SysTime() ) then + DrawWorldTip( WorldTip ) + end + +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/compat/compat_tauntpackloader.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/compat/compat_tauntpackloader.lua new file mode 100644 index 0000000..1049ace --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/compat/compat_tauntpackloader.lua @@ -0,0 +1,49 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +-- This file adds compatability to the older Taunt Pack loader format, which +-- directly modifies the gamemode tables (something that shouldn't be done). + +function CompatTauntPackLoader() + -- Prepare an empty table for the taunts. + GAMEMODE.Prop_Taunts = {} + GAMEMODE.Hunter_Taunts = {} + + -- Run the old hook name. + hook.Run("ph_AddTaunts", nil) + + -- Insert the taunts into the new structure. + for k,v in ipairs(GAMEMODE.Hunter_Taunts) do + -- ToDo: string.GetFileFromFilename is broken! + --pcall(GAMEMODE.Config.Taunts.Add("TauntPackLoader."..string.GetFileFromFilename(v), v, TEAM_SEEKERES, nil)) + end + for k,v in ipairs(GAMEMODE.Prop_Taunts) do + --pcall(GAMEMODE.Config.Taunts.Add("TauntPackLoader."..string.GetFileFromFilename(v), v, TEAM_HIDERS, nil)) + end + + -- Clean up after ourselves + GAMEMODE.Prop_Taunts = nil + GAMEMODE.Hunter_Taunts = nil +end +hook.Add("OnPropHuntInitialized", "CompatTauntPackLoader", CompatTauntPackLoader) \ No newline at end of file diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/init.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/init.lua new file mode 100644 index 0000000..ab3621a --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/init.lua @@ -0,0 +1,401 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +--! Client Files +AddCSLuaFile("cl_init.lua") +AddCSLuaFile("sh_init.lua") + +--! Include shared lua. +include "sh_init.lua" + +--! Include server lua. +include "server/config.lua" +include "server/console/variables.lua" +include "compat/compat_tauntpackloader.lua" + +-- ------------------------------------------------------------------------- -- +--! Gamemode +-- ------------------------------------------------------------------------- -- + +-- GameStates +GM_STATES_SETUP = 0 +GM_STATES_HIDE = 1 +GM_STATES_SEEK = 2 +GM_STATES_SHAMING = 3 -- Do your best, hiders! + +--! Initialize +function GM:Initialize() + self.State = GM_STATES_SETUP + self.StatePrev = self.State + + --! Run any hooked functions that addons have registered for Prop Hunt. + hook.Run("OnPropHuntInitialized") +end + +--! Check Victory and Loss conditions +function GM:Think() + if (self.State == GM_STATES_SETUP) then + --! GameState: Setup + -- Here we do all the stuff that would be ridiculous to do while playing. + if (self.StatePrev != self.State) then + game.CleanUpMap() + + -- Assign correct teams depending on Sub-Gamemode. + if (self.Config.Modes.SwapTeams == 1) then + for i,pl in ipairs(player.GetAll()) do + if (pl:Team() == TEAM_HIDERS) then + pl:SetTeam(TEAM_SEEKERS) + elseif (pl:Team() == TEAM_SEEKERS) then + pl:SetTeam(TEAM_HIDERS) + end + end + elseif (self.Config.Modes.RandomizeTeams == 1) then + local plys = player.GetAll() + --local plyCount = + + elseif (self.Config.Modes.TheDeadHunt == 1) then + + end + + -- Respawn players + for i,pl in ipairs(player.GetAll()) do + pl:Respawn() + end + end + + -- Advanced to next GameState if we have enough players + self.StatePrev = self.State + self.State = GM_STATES_HIDE + elseif (self.State == GM_STATES_HIDE) then + + elseif (self.State == GM_STATES_SEEK) then + + elseif (self.State == GM_STATES_SHAMING) then + + else + -- Invalid State, we have to reset + self.StatePrev = self.State + self.State = GM_STATES_SETUP + end +end + +-- ------------------------------------------------------------------------- -- +--! LEGACY CODE - TO BE REPLACED SOON +-- ------------------------------------------------------------------------- -- +function GM:AllowPlayerPickup(player, entity) + return true +end + +function AnnounceVictory(players, force) + for i,pl in ipairs(players) do + if pl:Alive() || force || pl:Team() == TEAM_SPECTATOR then + announcer = table.Random(VICTORY_SOUNDS); + print("Prop Hunt: '"..pl:GetName().."' announcing victory with '"..announcer.."'.") + pl:EmitSound(announcer, 200, 100, 0.5, CHAN_VOICE2) + end + end +end + +function AnnounceLoss(players, force) + for i,pl in ipairs(players) do + if pl:Alive() || force || pl:Team() == TEAM_SPECTATOR then + announcer = table.Random(LOSS_SOUNDS); + print("Prop Hunt: '"..pl:GetName().."' announcing loss with '"..announcer.."'.") + pl:EmitSound(announcer, 100, 100, 0.5, CHAN_VOICE2) + end + end +end + +-- If there is a mapfile send it to the client (sometimes servers want to change settings for certain maps) +if file.Exists("maps/"..game.GetMap()..".lua", "LUA") then + AddCSLuaFile("maps/"..game.GetMap()..".lua") +end + + +-- Server only constants +EXPLOITABLE_DOORS = { + "func_door", + "prop_door_rotating", + "func_door_rotating" +} +USABLE_PROP_ENTITIES = { + "prop_physics", + "prop_physics_multiplayer" +} + +-- Send the required resources to the client +for _, announcer in pairs(LOSS_SOUNDS) do resource.AddFile("sound/"..announcer) end +for _, announcer in pairs(VICTORY_SOUNDS) do resource.AddFile("source/"..announcer) end +for _, taunt in pairs(HUNTER_TAUNTS) do resource.AddFile("sound/"..taunt) end +for _, taunt in pairs(PROP_TAUNTS) do resource.AddFile("sound/"..taunt) end + +-- Called alot +function GM:CheckPlayerDeathRoundEnd() + if !GAMEMODE.RoundBased || !GAMEMODE:InRound() then + return + end + + local Teams = GAMEMODE:GetTeamAliveCounts() + + if table.Count(Teams) == 0 then + GAMEMODE:RoundEndWithResult(1001, "Draw, everyone loses!") + AnnounceLoss(player.GetAll(), true) + return + end + + if table.Count(Teams) == 1 then + -- Play victory and loss sounds. + if Teams[0] == TEAM_HUNTERS then + AnnounceVictory(team.GetPlayers(TEAM_HUNTERS)) + AnnounceLoss(team.GetPlayers(TEAM_PROPS)) + elseif Teams[0] == TEAM_PROPS then + AnnounceLoss(team.GetPlayers(TEAM_HUNTERS)) + AnnounceVictory(team.GetPlayers(TEAM_PROPS)) + end + + local TeamID = table.GetFirstKey(Teams) + GAMEMODE:RoundEndWithResult(TeamID, team.GetName(TeamID).." win!") + return + end +end + + +-- Called when an entity takes damage +function EntityTakeDamage(ent, dmginfo) + local att = dmginfo:GetAttacker() + if GAMEMODE:InRound() && ent && ent:GetClass() != "ph_prop" && !ent:IsPlayer() && att && att:IsPlayer() && att:Team() == TEAM_HUNTERS && att:Alive() then + att:SetHealth(att:Health() - GetConVar("HUNTER_FIRE_PENALTY"):GetInt()) + if att:Health() <= 0 then + MsgAll(att:Name() .. " felt guilty for hurting so many innocent props and committed suicide\n") + att:Kill() + end + end +end +hook.Add("EntityTakeDamage", "PH_EntityTakeDamage", EntityTakeDamage) + + +-- Called when player tries to pickup a weapon +function GM:PlayerCanPickupWeapon(pl, ent) + if pl:Team() != TEAM_HUNTERS then + return false + end + + return true +end + + +-- Called when player needs a model +function GM:PlayerSetModel(pl) + local player_model = pl:GetModel() + + if pl:Team() == TEAM_HUNTERS then + player_model = "models/player/combine_super_soldier.mdl" + elseif pl:Team() == TEAM_PROPS then + player_model = "models/Gibs/Antlion_gib_small_3.mdl" + end + + util.PrecacheModel(player_model) + pl:SetModel(player_model) +end + + +-- Called when a player tries to use an object +function GM:PlayerUse(pl, ent) + if !pl:Alive() || pl:Team() == TEAM_SPECTATOR then return false end + +-- if pl:Team() == TEAM_PROPS && pl:IsOnGround() && !pl:Crouching() && table.HasValue(USABLE_PROP_ENTITIES, ent:GetClass()) && ent:GetModel() then + if pl:Team() == TEAM_PROPS && table.HasValue(USABLE_PROP_ENTITIES, ent:GetClass()) && ent:GetModel() then + if table.HasValue(BANNED_PROP_MODELS, ent:GetModel()) then + pl:ChatPrint("That prop has been banned by the server.") + elseif ent:GetPhysicsObject():IsValid() then -- && pl.ph_prop:GetModel() != ent:GetModel() then + local ent_health = math.Clamp(ent:GetPhysicsObject():GetVolume() / 250, 1, 200) + + -- Prevent props from gaining health by changing. + local per = math.min(pl:Health() / pl:GetMaxHealth(), pl.tempHealthPc || 1.0) + if (pl.tempHealthPc == nil) || (per <= pl.tempHealthPc) then pl.tempHealthPc = per end + local new_health = math.Clamp(per * ent_health, 1, 200) + + pl:SetJumpPower(math.Clamp(ent:GetPhysicsObject():GetVolume() / 333, 200, 600)); + + -- Set Prop Data + pl.ph_prop:SetHealth(new_health) + pl.ph_prop:SetMaxHealth(ent_health) + pl.ph_prop:SetModel(ent:GetModel()) + pl.ph_prop:SetSkin(ent:GetSkin()) + + -- Update Hull and Health + pl:NewHull(ent:OBBMins(), ent:OBBMaxs()) + pl:SetHealth(new_health) + pl:SetMaxHealth(ent_health) + umsg.Start("SetHull", pl) + umsg.Vector(ent:OBBMins()) + umsg.Vector(ent:OBBMaxs()) + umsg.Short(new_health) + umsg.End() + end + end + + -- Prevent the door exploit + if table.HasValue(EXPLOITABLE_DOORS, ent:GetClass()) && pl.last_door_time && pl.last_door_time + 1 > CurTime() then + return false + end + + pl.last_door_time = CurTime() + return true +end + + +-- Called when player presses [F3]. Plays a taunt for their team +function GM:ShowSpare1(pl) + if GAMEMODE:InRound() && pl:Alive() && (pl:Team() == TEAM_HUNTERS || pl:Team() == TEAM_PROPS) && pl.last_taunt_time + TAUNT_DELAY <= CurTime() && #PROP_TAUNTS > 1 && #HUNTER_TAUNTS > 1 then +-- repeat + if pl:Team() == TEAM_HUNTERS then + rand_taunt = table.Random(HUNTER_TAUNTS) + else + rand_taunt = table.Random(PROP_TAUNTS) + end +-- until rand_taunt != pl.last_taunt + + pl.last_taunt_time = CurTime() + pl.last_taunt = rand_taunt + + print("Prop Hunt: '"..pl:GetName().."' taunting with '"..rand_taunt.."'.") + local vol = 1.0 + if pl:Team() == TEAM_HUNTERS then vol = vol * 0.5 end + pl:EmitSound(rand_taunt, 100, 100, vol, CHAN_VOICE2) + end +end + +-- Allow player to rotate the prop. (Either F4 or ducking) +function GM:ShowSpare2(pl) + if pl:Alive() && (pl:Team() == TEAM_PROPS) then + pl.ph_prop:SetApplyNewAngles(!pl.ph_prop:GetApplyNewAngles()) +-- pl.ph_prop:SetNewAngles(pl:GetAngles()) + end +end + +-- Called when the gamemode is initialized +function Initialize() + game.ConsoleCommand("mp_flashlight 0\n") +end +hook.Add("Initialize", "PH_Initialize", Initialize) + + +-- Called when a player leaves +function PlayerDisconnected(pl) + pl:RemoveProp() +end +hook.Add("PlayerDisconnected", "PH_PlayerDisconnected", PlayerDisconnected) + + +-- Called when the players spawns +function PlayerSpawn(pl) + pl:Blind(false) + pl:RemoveProp() + pl:SetColor( Color(255, 255, 255, 255)) + pl:SetRenderMode( RENDERMODE_TRANSALPHA ) + pl:UnLock() + pl:ResetHull() + pl:SetViewOffset(Vector(0, 0, 64)) + pl:SetViewOffsetDucked(Vector(0, 0, 28)) + pl.last_taunt_time = 0 + pl:SetupHands() + + umsg.Start("ResetHull", pl) + umsg.End() +-- umsg.Start("ThirdPerson", pl) +-- umsg.Bool(pl:Team() == TEAM_PROPS) +-- umsg.Entity(nil) +-- umsg.End() + + pl:SetCollisionGroup(COLLISION_GROUP_PASSABLE_DOOR) +end +hook.Add("PlayerSpawn", "PH_PlayerSpawn", PlayerSpawn) + +function GM:PlayerSetHandsModel( ply, ent ) + local simplemodel = player_manager.TranslateToPlayerModelName( ply:GetModel() ) + local info = player_manager.TranslatePlayerHands( simplemodel ) + if ( info ) then + ent:SetModel( info.model ) + ent:SetSkin( info.skin ) + ent:SetBodyGroups( info.body ) + end +end + + +-- Removes all weapons on a map +function RemoveWeaponsAndItems() + for _, wep in pairs(ents.FindByClass("weapon_*")) do + wep:Remove() + end + + for _, item in pairs(ents.FindByClass("item_*")) do + item:Remove() + end +end +hook.Add("InitPostEntity", "PH_RemoveWeaponsAndItems", RemoveWeaponsAndItems) + + +-- Called when round ends +function RoundEnd() + for _, pl in pairs(team.GetPlayers(TEAM_HUNTERS)) do + pl:Blind(false) + pl:UnLock() + end +end +hook.Add("RoundEnd", "PH_RoundEnd", RoundEnd) + + +-- This is called when the round time ends (props win) +function GM:RoundTimerEnd() + if !GAMEMODE:InRound() then + return + end + + GAMEMODE:RoundEndWithResult(TEAM_PROPS, "Props win!") +end + + +-- Called before start of round +function GM:OnPreRoundStart(num) + game.CleanUpMap() + + if GetGlobalInt("RoundNumber") != 1 && (SWAP_TEAMS_EVERY_ROUND == 1 || ((team.GetScore(TEAM_PROPS) + team.GetScore(TEAM_HUNTERS)) > 0 || SWAP_TEAMS_POINTS_ZERO==1)) then + for _, pl in pairs(player.GetAll()) do + if pl:Team() == TEAM_PROPS || pl:Team() == TEAM_HUNTERS then + if pl:Team() == TEAM_PROPS then + pl:SetTeam(TEAM_HUNTERS) + else + pl:SetTeam(TEAM_PROPS) + end + + pl:ChatPrint("Teams have been swapped!") + end + end + end + + UTIL_StripAllPlayers() + UTIL_SpawnAllPlayers() + UTIL_FreezeAllPlayers() +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_hider.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_hider.lua new file mode 100644 index 0000000..b1f1996 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_hider.lua @@ -0,0 +1,52 @@ +-- Create new class +DEFINE_BASECLASS( "player_default" ) +local CLASS = {} + +-- Some settings for the class +CLASS.DisplayName = "Hider" +CLASS.WalkSpeed = 250 +CLASS.CrouchedWalkSpeed = 0.2 +CLASS.RunSpeed = 250 +CLASS.DuckSpeed = 0.2 +CLASS.DrawTeamRing = false + +-- Called by spawn and sets loadout +function CLASS:Loadout(pl) + -- Props don't get anything +end + +-- Called when player spawns with this class +function CLASS:OnSpawn(pl) + pl:SetColor( Color(255, 255, 255, 0)) + + pl.ph_prop = ents.Create("ph_prop") + pl.ph_prop:SetPos(pl:GetPos()) + pl.ph_prop:SetAngles(pl:GetAngles()) + pl.ph_prop:Spawn() + pl.ph_prop:SetOwner(pl) + + pl:SetJumpPower(200); + + -- Update Hull and Health + pl:SetHealth(100) + pl:SetMaxHealth(100) + pl.ph_prop:SetHealth(100) + pl.ph_prop:SetMaxHealth(100) + + local hullMin = Vector(-20, -20, -10) + local hullMax = Vector( 20, 20, 60) + pl:NewHull(hullMin, hullMax) + umsg.Start("SetHull", pl) + umsg.Vector(hullMin) + umsg.Vector(hullMax) + umsg.Short(100) + umsg.End() +end + +-- Called when a player dies with this class +function CLASS:OnDeath(pl, attacker, dmginfo) + pl:RemoveProp() +end + +-- Register +player_manager.RegisterClass("Hider", CLASS) diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_seeker.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_seeker.lua new file mode 100644 index 0000000..14decc6 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/player_class/class_seeker.lua @@ -0,0 +1,120 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +--! This file defines the Seeker player class. +-- A seeker is someone who is looking for the hiders, using weapons or other +-- means of detecting idiots. Also someone who looks like a diaper baby. +-- Weapons and Ammo are granted upon spawn and have to be used sparingly or +-- they'll be stuck with the crowbar. Bad seeker, bad. +-- Gain health upon killing a hider, lose health when attacking non-hiders. +-- Death upon health reaching 0. + +DEFINE_BASECLASS( "player_default" ) +local SEEKER = {} +SEEKER.DisplayName = "Seeker" +SEEKER.WalkSpeed = 230 -- How fast to move when not running +SEEKER.RunSpeed = 460 -- How fast to move when running +SEEKER.CrouchedWalkSpeed = 0.2 -- Multiply move speed by this when crouching +SEEKER.DuckSpeed = 0.2 -- How fast to go from not ducking, to ducking +SEEKER.UnDuckSpeed = 0.2 -- How fast to go from ducking, to not ducking +SEEKER.JumpPower = 200 -- How powerful our jump should be +SEEKER.CanUseFlashlight = true -- Can we use the flashlight +SEEKER.MaxHealth = 100 -- Max health we can have +SEEKER.StartHealth = 100 -- How much health we start with +SEEKER.StartArmor = 0 -- How much armour we start with +SEEKER.DropWeaponOnDie = false -- Do we drop our weapon when we die +SEEKER.TeammateNoCollide = true -- Do we collide with teammates or run straight through them +SEEKER.AvoidPlayers = true -- Automatically swerves around other players +SEEKER.UseVMHands = true -- Uses viewmodel hands + +-- Called by spawn and sets loadout +function SEEKER:Loadout(pl) + -- Give the weapons the admin told us to. + local weapons = string.Split(GM.Config.Seeker.Weapons, ",") + for i,weapon in ipairs(weapons) do + pl:Give(weapon) + end + + -- Give the ammo the admin told us to. + local ammos = string.Split(GM.Config.Seeker.Ammo, ",") + for i,ammo in ipairs(ammos) do + local typeCount = string.Split(ammo, ":") + pl:GiveAmmo(typeCount[1], typeCount[0], true) + end + + -- Default weapon stuff + local cl_defaultweapon = pl:GetInfo("cl_defaultweapon") + if pl:HasWeapon(cl_defaultweapon) then + pl:SelectWeapon(cl_defaultweapon) + end +end + +-- Called when player spawns with this class +function SEEKER:OnSpawn(pl) + local unlock_time = math.Clamp(GetConVar("ph_rounds_blindtime"):GetInt() - (CurTime() - GetGlobalFloat("RoundStartTime", 0)), 0, GetConVar("ph_rounds_blindtime"):GetInt()) - 1 + -- !TODO! ph_rounds_blindtime + + --function MyLockFunc() + --function MyUnlockFunc() + + local unblindfunc = function() + if pl:IsValid() == false then return end + --MyUnblindFunc(pl.Blind(false)) + pl:Blind(false) + end + local lockfunc = function() + if pl:IsValid() == false then return end + --MyLockFunc(pl.Lock()) + pl.Lock(pl) + end + local unlockfunc = function() + if pl:IsValid() == false then return end + --MyUnlockFunc(pl.UnLock()) + pl.UnLock(pl) + end + + if unlock_time > 2 then + pl:Blind(true) + + timer.Simple(unlock_time, unblindfunc) + + timer.Simple(1, lockfunc) + timer.Simple(unlock_time, unlockfunc) + end + + pl:SetupHands() +end + +function SEEKER:GetHandsModel() + return { model = "models/weapons/c_arms_combine.mdl", skin = 1, body = "0100000" } +end + +-- Called when a player dies with this SEEKER +function SEEKER:OnDeath(pl, attacker, dmginfo) + pl:CreateRagdoll() + pl:UnLock() +end + +-- Register +player_manager.RegisterClass("Seeker", SEEKER) diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/config.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/config.lua new file mode 100644 index 0000000..73dded6 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/config.lua @@ -0,0 +1,433 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +--! Initialize configuration table. +GM.Config = { } + +--! Basic Settings +-- Timelimit in minutes +GM.Config.TimeLimit = math.max(GetConVarNumber("mp_timelimit"),0) +-- You have to wait x seconds to taunt again. +GM.Config.TauntWaitTime = 5 +-- Is sprinting enabled? +GM.Config.Sprinting = 0 --false + +--! Round Settings +GM.Config.Rounds = {} +-- How many rounds should we attempt to have? +GM.Config.Rounds.Amount = 10 +-- Rounds last x seconds. +GM.Config.Rounds.Time = 300 +-- Time for HIders to hide. +GM.Config.Rounds.BlindTime = 30 + +--! Game Modes +GM.Config.Modes = {} +-- Sub-mode: Simple Team Swap +GM.Config.Modes.SwapTeams = 1--true +-- Sub-mode: Randomize Teams +GM.Config.Modes.RandomizeTeams = 0--false +-- Sub-mode: The Dead Hunt (Dead Prop becomes Hunter) +GM.Config.Modes.TheDeadHunt = 0--false + +--! Seeker Settings +GM.Config.Seeker = {} +GM.Config.Seeker.HealthStart = 100 +GM.Config.Seeker.HealthMax = 100 +GM.Config.Seeker.HealthBonus = 10 +GM.Config.Seeker.HealthPenalty = 1 +GM.Config.Seeker.Weapons = "weapon_crowbar,weapon_pistol,weapon_ph_smg,weapon_shotgun" +GM.Config.Seeker.Ammo = "Pistol:100,SMG1:300,SMG1_Grenade:1,Buckshot:64" + +--! Hider Settings +GM.Config.Hider = {} +GM.Config.Hider.HealthStart = 100 +GM.Config.Hider.HealthMax = 100 +GM.Config.Hider.HealthScaling = 1--true +GM.Config.Hider.HealthScalingMax = 200 + +--! Taunts +GM.Config.Taunts = { + Seeker = { }, + Hider = { }, +} + +-- Taunts.Clear() +--@desc: Clears the current taunt list. +GM.Config.Taunts.Clear = function() + this.Seeker = {} + this.Hider = {} +end + +-- Taunts.Load(sTauntListFile) +--@desc: Loads the taunt list from disk. +--@param: +-- sTauntListFile - A string containing the path to the taunt list to load. +--@return: true or false depending on success. +GM.Config.Taunts.Load = function(sTauntListFile) + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: Loading taunt list from file '"..sTauntListFile.."'...") end + + -- Safeguard against idiots. + if type(sTauntListFile) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: is not a string.") end + return false + end + + -- Given file must exist for us to continue. + if ! file.Exists(sTauntListFile, "GAME") then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: File not found.") end + return false + end + + -- Read the file and check if it's empty. + sTauntListData = file.Read(sTauntListFile, "GAME") + if sTauntListData == "" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: File is empty.") end + return false + end + + -- Convert JSON to a table for us to use. + sTauntList = util.JSONToTable(sTauntListData) + + -- Is it nil? Then it's not valid JSON + if sTauntList == nil then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: File contains invalid JSON.") end + return false + end + + -- Finally, append the taunt lists. + if sTauntList.Seeker != nil then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: Adding Seeker taunts...") end + for k,v in pairs(sTauntList.Seeker) do + GAMEMODE.Taunts.Seeker[k] = v + end + end + if sTauntList.Hider != nil then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: Adding Hider taunts...") end + for k,v in pairs(sTauntList.Hider) do + GAMEMODE.Taunts.Hider[k] = v + end + end + + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Load: Complete.") end + return true +end + +-- Taunts.Save(sTauntListFile) +--@desc: Saves the current taunt list to disk. +--@param: +-- sTauntListFile - A string containing the path to the file to save to. +--@return: true or false depending on success. +GM.Config.Taunts.Save = function(sTauntListFile) + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: Saving taunt list to file '"..sTauntListFile.."'...") end + + -- Safeguard against idiots. + if type(sTauntListFile) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: is not a string.") end + return false + end + + -- File must not be nil, otherwise we can't write to it. + if sTauntListFile == nil then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: No file given.") end + return false + end + + -- Convert our taunt table to JSON. + sTauntListData = util.TableToJSON(GAMEMODE.Taunts); + if sTauntListData == nil then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: Corrupted GAMEMODE table.") end + return false + end + + -- Write out JSON out to file + if ! file.Write(sTauntListFile, sTauntListData) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: Failed to write to file.") end + return false + end + + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Save: Complete.") end + return true +end + +-- Taunts.Add(sTauntName, sSoundFile, iTeamID, mPropFilter) +--@desc: Registers a new taunt with the given name, file, team and filter. +--@param: +-- sTauntName - The unique name of the taunt. +-- sSoundFile - A sound file to play when this taunt is selected. +-- iTeamID - The team that should receive the taunt or nil for all teams. +-- mPropFilter - A string or a table containing strings for props that should be able to use this taunt. +--@return: true or false depending on success. +GM.Config.Taunts.Add = function(sTauntName, sSoundFile, iTeamID, mPropFilter) + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: Adding new taunt '"..sTauntName.."'...") end + + -- Safeguard against idiots. + if type(sTauntName) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: is not a string.") end + return false + end + if type(sSoundFile) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: is not a string.") end + return false + end + if (type(iTeamID) != "number" && iTeamID != nil) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: is not a number or nil.") end + return false + end + if (type(mPropFilter) != "string" && type(mPropFilter) != "table" && mPropFilter != nil) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: is not a string, table or nil.") end + return false + end + + -- Check if the sound file actually exists + if !file.Exists("sound/"..sSoundFile, "GAME") then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: File '"..sSoundFile.."' does not exist.") end + return false + end + + -- Make sure that our prop filter is a table listing the props it's supposed to work for. + if (mPropFilter == nil) then + mPropFilter = { } + elseif type(mPropFilter) == "string" then + mPropFilter = { mPropFilter } + end + + -- Prepare Taunt table + Taunt = { + File = sSoundFile, + Filter = mPropFilter + } + + -- If iTeamID is nil, both teams will receive the taunt. + if iTeamID == nil then + GAMEMODE.Taunts.Seeker[sTauntName] = Taunt + GAMEMODE.Taunts.Hider[sTauntName] = Taunt + else + -- Make sure that the team is valid. + if ! team.Valid(iTeamID) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: Team "..iTeamID.."' does not exist.") end + return false + end + + if (iTeamID == TEAM_SEEKERS) then + GAMEMODE.Taunts.Seeker[sTauntName] = Taunt + elseif (iTeamID == TEAM_HIDERS) then + GAMEMODE.Taunts.Hider[sTauntName] = Taunt + else + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: Team "..iTeamID.."' can't have taunts.") end + return false + end + end + + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Add: Complete.") end + return true +end + +-- Taunts.Remove(sTauntName, iTeamID) +--@desc: Removes a registered taunt with the given name and team. +--@param: +-- sTauntName - The unique name of the taunt. +-- iTeamID - The team that the taunt should be removed from or nil for all teams. +--@return: true or false depending on success. +GM.Config.Taunts.Remove = function(sTauntName, iTeamID) + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: Removing taunt '"..sTauntName.."'...") end + + -- Safeguard against idiots. + if type(sTauntName) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: is not a string.") end + return false + end + if (type(iTeamID) != "number" && iTeamID != nil) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: is not a number or nil.") end + return false + end + + -- if iTeamID is nil, both teams will have the taunt removed. + if (iTeamID == nil) then + GAMEMODE.Taunts.Seeker[sTauntName] = nil + GAMEMODE.Taunts.Hider[sTauntName] = nil + else + -- Make sure we have a valid Team. + if ! team.Valid(iTeamID) then + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: Team "..iTeamID.."' does not exist.") end + return false + end + + if iTeamID == TEAM_SEEKERS then + GAMEMODE.Taunts.Seeker[sTauntName] = nil + elseif iTeamID == TEAM_HIDERS then + GAMEMODE.Taunts.Hider[sTauntName] = nil + else + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: Team "..iTeamID.."' can't have taunts.") end + return false + end + end + + if GAMEMODE.Debug then print("Prop Hunt.Taunts.Remove: Complete.") end + return true +end + +-- ToDo: Taunts.Get(iTeamID, sPropName) + +--! Announcers (Round Start, Unblind, Win, Loss) +GM.Config.Announcers = { + Start = { }, + Unblind = { }, + Win = { }, + Loss = { } +} + +-- Announcers.Clear() +--@desc: Clears the current announcer list. +GM.Config.Announcers.Clear = function() + Announcers.Start = { } + Announcers.Unblind = { } + Announcers.Win = { } + Announcers.Loss = { } +end + +-- Announcers.Load(sAnnouncerListFile) +--@desc: Tries to load the given announcer list. +--@param: +-- sAnnouncerListFile - A string containing the path to the announcer list to load. +--@return: true or false depending on success. +GM.Config.Announcers.Load = function(sAnnouncerListFile) + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Loading announcer list from file '"..sAnnouncerListFile.."'...") end + + -- Safeguard against idiots. + if type(sAnnouncerListFile) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: is not a string.") end + return false + end + + -- Given file must exist for us to continue. + if ! file.Exists(sAnnouncerListFile, "GAME") then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: File not found.") end + return false + end + + -- Read the file and check if it's empty. + sAnnouncerListData = file.Read(sAnnouncerListFile, "GAME") + if sAnnouncerListData == "" then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: File is empty.") end + return false + end + + -- Convert JSON to a table for us to use. + sAnnouncerList = util.JSONToTable(sAnnouncerListData) + + -- Is it nil? Then it's not valid JSON + if sAnnouncerList == nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: File contains invalid JSON.") end + return false + end + + -- Finally, insert the announcer lists. + if sAnnouncerList.Start != nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Adding Start announcers...") end + for k,v in pairs(sAnnouncerList.Start) do + GAMEMODE.Announcers.Start[k] = v + end + end + if sAnnouncerList.Unblind != nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Adding Unblind announcers...") end + for k,v in pairs(sAnnouncerList.Unblind) do + GAMEMODE.Announcers.Unblind[k] = v + end + end + if sAnnouncerList.Win != nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Adding Win announcers...") end + for k,v in pairs(sAnnouncerList.Win) do + GAMEMODE.Announcers.Win[k] = v + end + end + if sAnnouncerList.Loss != nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Adding Loss announcers...") end + for k,v in pairs(sAnnouncerList.Loss) do + GAMEMODE.Announcers.Loss[k] = v + end + end + + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Load: Complete.") end + return true +end + +-- Announcers.Save(sAnnouncerListFile) +--@desc: Saves the current taunt list to disk. +--@param: +-- sAnnouncerListFile - A string containing the path to the file to save to. +--@return: true or false depending on success. +GM.Config.Announcers.Save = function(sAnnouncerListFile) + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: Saving announcer list to file '"..sAnnouncerListFile.."'...") end + + -- Safeguard against idiots. + if type(sAnnouncerListFile) != "string" then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: is not a string.") end + return false + end + + -- File must not be nil, otherwise we can't write to it. + if sAnnouncerListFile == nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: No file given.") end + return false + end + + -- Convert our taunt table to JSON. + sAnnouncerListData = util.TableToJSON(GAMEMODE.Announcers); + if sAnnouncerListData == nil then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: Corrupted GAMEMODE table.") end + return false + end + + -- Write out JSON out to file + if ! file.Write(sAnnouncerListFile, sAnnouncerListData) then + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: Failed to write to file.") end + return false + end + + + if GAMEMODE.Debug then print("Prop Hunt.Announcers.Save: Complete.") end + return true +end + +-- ToDo: Announcers.Add +-- ToDo: Announcers.Remove +-- ToDo: Announcers.Get(iType) + +--! Whitelists and Blacklists +-- Hiders: Entity Whitelist (exact match) +GM.Config.EntityWhitelist = { + "prop_physics", + "prop_physics_multiplayer" +} + +-- Hiders: Prop Blacklist (exact match) +GM.Config.PropBlacklist = {} + +-- Both: Entity Use-abuse Blacklist (exact match) +GM.Config.EntityAbuseBlacklist = { + "func_door", + "func_door_rotating", + "prop_door_rotating" +} diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/console/variables.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/console/variables.lua new file mode 100644 index 0000000..1db30c9 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/server/console/variables.lua @@ -0,0 +1,45 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +CreateConVar("ph_tauntwaittime", GM.Config.TauntWaitTime) +CreateConVar("ph_sprinting", GM.Config.Sprinting) +CreateConVar("ph_rounds", GM.Config.Rounds.Amount) +CreateConVar("ph_rounds_time", GM.Config.Rounds.Time) +CreateConVar("ph_rounds_blindtime", GM.Config.Rounds.BlindTime) + +CreateConVar("ph_modes_swap", GM.Config.Modes.SwapTeams) +CreateConVar("ph_modes_randomize", GM.Config.Modes.RandomizeTeams) +CreateConVar("ph_modes_thedeadhunt", GM.Config.Modes.TheDeadHunt) + +CreateConVar("ph_seeker_health", GM.Config.Seeker.HealthStart) +CreateConVar("ph_seeker_health_max", GM.Config.Seeker.HealthMax) +CreateConVar("ph_seeker_health_killbonus", GM.Config.Seeker.HealthBonus) +CreateConVar("ph_seeker_health_penalty", GM.Config.Seeker.HealthPenalty) +CreateConVar("ph_seeker_weapons", GM.Config.Seeker.Weapons) +CreateConVar("ph_seeker_ammo", GM.Config.Seeker.Ammo) + +CreateConVar("ph_hider_health", GM.Config.Hider.HealthStart) +CreateConVar("ph_hider_health_max", GM.Config.Hider.HealthMax) +CreateConVar("ph_hider_health_scale", GM.Config.Hider.HealthScaling) +CreateConVar("ph_hider_health_scaled_max", GM.Config.Hider.HealthScalingMax) \ No newline at end of file diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_config.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_config.lua new file mode 100644 index 0000000..dd91bc2 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_config.lua @@ -0,0 +1,366 @@ +if !file.IsDir("prop_hunt", "DATA") then file.CreateDir("prop_hunt") end + +-- These are Models that the Prop team can not become. +-- Usually you'd put invisible or impossible to hit models in here. +BANNED_PROP_MODELS = { + "models/props/cs_assault/dollar.mdl", + "models/props/cs_assault/money.mdl", + "models/props/cs_office/snowman_arm.mdl", + "models/props_junk/garbage_plasticbottle001a.mdl", + "models/props/cs_office/projector_remote.mdl" +} +if (! file.Exists("prop_hunt/banned_props.txt", "DATA")) then + file.Write("prop_hunt/banned_props.txt", util.TableToKeyValues(BANNED_PROP_MODELS)) +end +local fileContent = file.Read("prop_hunt/banned_props.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then BANNED_PROP_MODELS = fileTable end +end + +-- Sounds played by members of the losing team at the end of the round. +LOSS_SOUNDS = { + "bot/aw_hell.wav", + "bot/aww_man.wav", + "bot/anyone_see_anything.wav", + "bot/anyone_see_them.wav", + "bot/come_out_and_fight_like_a_man.wav", + "bot/come_out_wherever_you_are.wav", + "bot/he_got_away.wav", + "bot/he_got_away2.wav", + "bot/i_dont_know_where_he_went.wav", + "bot/i_got_nothing.wav", + "bot/nothing_happening_over_here.wav", + "bot/nothing_here.wav", + "bot/nothing_moving_over_here.wav", + "bot/thats_not_good.wav", + "bot/theres_too_many.wav", + "bot/theres_too_many_of_them.wav", + "bot/theyre_all_over_the_place2.wav", + "bot/theyre_everywhere2.wav", + "bot/too_many2.wav", + "bot/what_happened.wav", + "bot/what_have_you_done.wav", + "bot/where_are_they.wav", + "bot/where_are_you_hiding.wav" +} +if (! file.Exists("prop_hunt/sounds_loss.txt", "DATA")) then + file.Write("prop_hunt/sounds_loss.txt", util.TableToKeyValues(LOSS_SOUNDS)) +end +local fileContent = file.Read("prop_hunt/sounds_loss.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then LOSS_SOUNDS = fileTable end +end + +-- Sounds played by members of the winning team at the end of the round. +VICTORY_SOUNDS = { + "bot/and_thats_how_its_done.wav", + "bot/come_to_papa.wav", + "bot/do_not_mess_with_me.wav", + "bot/dropped_him.wav", + "bot/enemy_down.wav", + "bot/enemy_down2.wav", + "bot/good_job_team.wav", + "bot/got_him.wav", + "bot/hes_broken.wav", + "bot/hes_dead.wav", + "bot/hes_done.wav", + "bot/hes_down.wav", + "bot/its_a_party.wav", + "bot/i_am_dangerous.wav", + "bot/i_am_on_fire.wav", + "bot/i_got_more_where_that_came_from.wav", + "bot/i_wasnt_worried_for_a_minute.wav", + "bot/killed_him.wav", + "bot/look_out_brag.wav", + "bot/made_him_cry.wav", + "bot/oh_yea.wav", + "bot/oh_yea2.wav", + "bot/owned.wav", + "bot/ruined_his_day.wav", + "bot/tag_them_and_bag_them.wav", + "bot/thats_the_way_this_is_done.wav", + "bot/that_was_a_close_one.wav", + "bot/that_was_it.wav", + "bot/that_was_the_last_guy.wav", + "bot/that_was_the_last_one.wav", + "bot/they_never_knew_what_hit_them.wav", + "bot/they_will_not_escape.wav", + "bot/they_wont_get_away.wav", + "bot/they_wont_get_away2.wav", + "bot/this_is_my_house.wav", + "bot/took_him_down.wav", + "bot/took_him_out.wav", + "bot/took_him_out2.wav", + "bot/wasted_him.wav", + "bot/way_to_be_team.wav", + "bot/well_done.wav", + "bot/we_owned_them.wav", + "bot/whew_that_was_close.wav", + "bot/whoo.wav", + "bot/whoo2.wav", + "bot/whos_the_man.wav", + "bot/who_wants_some_more.wav", + "bot/yesss.wav", + "bot/yesss2.wav" +} +if (! file.Exists("prop_hunt/sounds_victory.txt", "DATA")) then + file.Write("prop_hunt/sounds_victory.txt", util.TableToKeyValues(VICTORY_SOUNDS)) +end +local fileContent = file.Read("prop_hunt/sounds_victory.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then VICTORY_SOUNDS = fileTable end +end + +-- Taunts played when Hunters hit their Spare1 binding. +HUNTER_TAUNTS = { + "bot/a_bunch_of_them.wav", + "bot/come_out_and_fight_like_a_man.wav", + "bot/come_out_wherever_you_are.wav", + "bot/come_to_papa.wav", + "bot/dont_worry_hell_get_it.wav", + "bot/hang_on_i_heard_something.wav", + "bot/hang_on_im_coming.wav", + "bot/i_dont_think_so.wav", + "bot/i_have_the_hostages.wav", + "bot/i_see_our_target.wav", + "bot/im_waiting_here.wav", + "bot/keeping_an_eye_on_the_hostages.wav", + "bot/nnno_sir.wav", + "bot/spotted_the_delivery_boy.wav", + "bot/target_acquired.wav", + "bot/target_spotted.wav", + "bot/you_heard_the_man_lets_go.wav" +} +if (! file.Exists("prop_hunt/sounds_taunt_hunter.txt", "DATA")) then + file.Write("prop_hunt/sounds_taunt_hunter.txt", util.TableToKeyValues(HUNTER_TAUNTS)) +end +local fileContent = file.Read("prop_hunt/sounds_taunt_hunter.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then HUNTER_TAUNTS = fileTable end +end + +-- Taunts played when Props hit their Spare1 binding. +PROP_TAUNTS = { +-- "ambient/alarms/apc_alarm_loop1.wav", + "ambient/alarms/apc_alarm_pass1.wav", +-- "ambient/alarms/citadel_alert_loop2.wav", +-- "ambient/alarms/city_firebell_loop1.wav", +-- "ambient/alarms/city_siren_loop2.wav", +-- "ambient/alarms/combine_bank_alarm_loop1.wav", +-- "ambient/alarms/combine_bank_alarm_loop4.wav", +-- "ambient/alarms/klaxon1.wav", + "ambient/alarms/manhack_alert_pass1.wav", + "ambient/alarms/razortrain_horn1.wav", + "ambient/alarms/scanner_alert_pass1.wav", +-- "ambient/alarms/siren.wav", +-- "ambient/alarms/train_crossing_bell_loop1.wav", + "ambient/alarms/train_horn2.wav", + "ambient/alarms/train_horn_distant1.wav", + "ambient/alarms/warningbell1.wav", +-- "ambient/chatter/cb_radio_chatter_1.wav", +-- "ambient/chatter/cb_radio_chatter_2.wav", +-- "ambient/chatter/cb_radio_chatter_3.wav", + "ambient/energy/whiteflash.wav", + "ambient/intro/alyxremove.wav", + "ambient/intro/logosfx.wav", +-- "ambient/levels/labs/teleport_alarm_loop1.wav", + "ambient/levels/launch/1stfiringwarning.wav", + "ambient/levels/launch/rockettakeoffblast.wav", +-- "ambient/levels/outland/basealarmloop.wav", +-- "ambient/machines/60hzhum.wav", + "ambient/misc/ambulance1.wav", + "ambient/misc/carhonk1.wav", + "ambient/misc/carhonk2.wav", + "ambient/misc/carhonk3.wav", +-- "ambient/music/bongo.wav", +-- "ambient/music/country_rock_am_radio_loop.wav", +-- "ambient/music/cubanmusic1.wav", +-- "ambient/music/dustmusic1.wav", +-- "ambient/music/dustmusic2.wav", +-- "ambient/music/dustmusic3.wav", +-- "ambient/music/flamenco.wav", +-- "ambient/music/latin.wav", +-- "ambient/music/mirame_radio_thru_wall.wav", +-- "ambient/music/piano1.wav", +-- "ambient/music/piano2.wav", + "ambient/outro/gunshipcrash.wav", + "ambient/3dmeagle.wav", +-- "ambient/guit1.wav", +-- "ambient/opera.wav", +-- "ambient/sheep.wav", + "beams/beamstart5.wav", + "buttons/bell1.wav", + "buttons/weapon_cant_buy.wav", + "common/bass.wav", + "common/bugreporter_failed.wav", + "common/warning.wav", + "doors/door_squeek1.wav", + "friends/friend_join.wav", + "friends/friend_online.wav", + "friends/message.wav", + "hostage/hunuse/comeback.wav", + "hostage/hunuse/dontleaveme.wav", + "hostage/hunuse/yeahillstay.wav", + "items/gift_drop.wav", + "music/radio1.mp3", + "phx/eggcrack.wav", + "plats/elevbell1.wav", + "player/headshot1.wav", + "player/headshot2.wav", + "player/sprayer.wav", + "radio/enemydown.wav", + "radio/go.wav", + "radio/locknload.wav", + "radio/negative.wav", + "radio/rounddraw.wav", + "radio/takepoint.wav", + "resource/warning.wav", +-- "test/temp/soundscape_test/tv_music.wav", + "ui/achievement_earned.wav", + "ui/freeze_cam.wav", + "vehicles/junker/radar_ping_friendly1.wav", + "weapons/c4/c4_beep1.wav", + "weapons/c4/c4_click.wav", + "weapons/awp/awp1.wav", + "vo/canals/female01/gunboat_giveemhell.wav", + "vo/canals/female01/gunboat_justintime.wav", + "vo/canals/female01/stn6_incoming.wav", + "vo/canals/male01/gunboat_giveemhell.wav", + "vo/canals/male01/gunboat_justintime.wav", + "vo/canals/male01/stn6_incoming.wav", + "vo/canals/al_radio_stn6.wav", + "vo/canals/arrest_getgoing.wav", + "vo/canals/arrest_helpme.wav", + "vo/canals/arrest_lookingforyou.wav", + "vo/canals/boxcar_lethimhelp.wav", + "vo/canals/matt_closecall.wav", + "vo/canals/premassacre.wav", + "vo/ravenholm/aimforhead.wav", + "vo/ravenholm/bucket_patience.wav", + "vo/ravenholm/madlaugh01.wav", + "vo/ravenholm/madlaugh02.wav", + "vo/ravenholm/madlaugh03.wav", + "vo/ravenholm/madlaugh04.wav", + "weapons/strider_buster/ol12_stickybombcreator.wav", + "weapons/c4/c4_explode1.wav", + "weapons/357/357_fire2.wav", + "weapons/357/357_fire3.wav", + "weapons/scout/scout_fire-1.wav", + "weapons/smokegrenade/sg_explode.wav", + "weapons/grenade_launcher1.wav", + "weapons/explode3.wav", + "weapons/underwater_explode3.wav", + "items/nvg_on.wav", + "hostage/huse/letsdoit.wav", + "hostage/huse/illfollow.wav", + "hostage/huse/getouttahere.wav", + "doors/door_screen_move1.wav", + "doors/heavy_metal_stop1.wav", + "doors/default_move.wav", + "common/stuck2.wav", + "ambient/water_splash1.wav", + "ambient/water_splash2.wav", + "ambient/water_splash3.wav", + "ambient/weather/thunder1.wav", + "ambient/weather/thunder2.wav", + "ambient/weather/thunder3.wav", + "ambient/weather/thunder4.wav", + "ambient/weather/thunder5.wav", + "ambient/weather/thunder6.wav", + "ambient/outro/thunder7.wav", + "ambient/voices/crying_loop1.wav", + "ambient/voices/playground_memory.wav", + "ambient/voices/f_scream1.wav", + "ambient/voices/m_scream1.wav", + "ambient/voices/cough1.wav", + "ambient/voices/cough2.wav", + "ambient/voices/cough3.wav", + "ambient/voices/cough4.wav", + "ambient/overhead/plane1.wav", + "ambient/overhead/plane2.wav", + "ambient/overhead/plane3.wav", + "ambient/overhead/hel1.wav", + "ambient/overhead/hel2.wav", + "ambient/misc/truck_backup1.wav", + "ambient/misc/truck_drive1.wav", + "ambient/misc/truck_drive2.wav", + "ambient/machines/pneumatic_drill_1.wav", + "ambient/machines/pneumatic_drill_2.wav", + "ambient/machines/pneumatic_drill_3.wav", + "ambient/machines/pneumatic_drill_4.wav", + "ambient/machines/station_train_squeel.wav", + "ambient/machines/ticktock.wav", + "ambient/creatures/teddy.wav", + "ambient/creatures/town_child_scream1.wav", + "ambient/creatures/town_moan1.wav", + "ambient/creatures/town_muffled_cry1.wav", + "ambient/creatures/town_scared_breathing1.wav", + "ambient/creatures/town_scared_breathing2.wav", + "ambient/creatures/town_scared_sob1.wav", + "ambient/creatures/town_scared_sob2.wav", + "ambient/creatures/town_zombie_call1.wav" +} +if (! file.Exists("prop_hunt/sounds_taunt_prop.txt", "DATA")) then + file.Write("prop_hunt/sounds_taunt_prop.txt", util.TableToKeyValues(PROP_TAUNTS)) +end +local fileContent = file.Read("prop_hunt/sounds_taunt_prop.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then PROP_TAUNTS = fileTable end +end + +-- Maximum time (in minutes) for this fretta gamemode (Default: 30) +GAME_TIME = math.max(GetConVarNumber("mp_timelimit"),1) + +-- Number of seconds hunters are blinded/locked at the beginning of the map (Default: 30) +CreateConVar("HUNTER_BLINDLOCK_TIME", "30", FCVAR_REPLICATED) + +--Create the convars here +-- Health points removed from hunters when they shoot (Default: 5) +CreateConVar( "HUNTER_FIRE_PENALTY", "5", FCVAR_REPLICATED) + +-- How much health to give back to the Hunter after killing a prop (Default: 20) +CreateConVar( "HUNTER_KILL_BONUS", "20", FCVAR_REPLICATED) + +--Whether or not we include grenade launcher ammo (default: 1) +CreateConVar( "WEAPONS_ALLOW_GRENADE", "1", FCVAR_REPLICATED) + +-- Seconds a player has to wait before they can taunt again (Default: 5) +TAUNT_DELAY = 2 + +-- Rounds played on a map (Default: 10) +ROUNDS_PER_MAP = 60 + +-- Time (in seconds) for each round (Default: 300) +ROUND_TIME = 300 + +-- Determains if players should be team swapped every round [0 = No, 1 = Yes] (Default: 1) +SWAP_TEAMS_EVERY_ROUND = 1 + +-- Update above values with values from configuration. +if (! file.Exists("prop_hunt/config.txt", "DATA")) then + file.Write("prop_hunt/config.txt", util.TableToKeyValues({ +-- GAME_TIME = 30, + TAUNT_DELAY = 2, + ROUNDS_PER_MAP = 60, + ROUND_TIME = 300, + SWAP_TEAM_EVERY_ROUND = 1 + })) +end +local fileContent = file.Read("prop_hunt/config.txt", "DATA"); +if fileContent then + local fileTable = util.KeyValuesToTable(fileContent) + if fileTable then +-- if fileTable.GAME_TIME then GAME_TIME = fileTable.GAME_TIME end + if fileTable.TAUNT_DELAY then TAUNT_DELAY = fileTable.TAUNT_DELAY end + if fileTable.ROUNDS_PER_MAP then ROUNDS_PER_MAP = fileTable.ROUNDS_PER_MAP end + if fileTable.ROUND_TIME then ROUND_TIME = fileTable.ROUND_TIME end + if fileTable.SWAP_TEAM_EVERY_ROUND then ROUND_TIME = fileTable.SWAP_TEAM_EVERY_ROUND end + end +end + +GAME_TIME = math.max(GetConVarNumber("mp_timelimit"),1) diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_init.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_init.lua new file mode 100644 index 0000000..62864a9 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_init.lua @@ -0,0 +1,93 @@ +-- Gamemode Information +GM.Name = "Prop Hunt" +GM.Author = "Michael 'Xaymar' Dirks (Based on Kow@lskis Version, Original by AMT)" +GM.Email = "michael.fabian.dirks@gmail.com" +GM.Website = "http://xaymar.com/" +GM.Debug = (cvars.Number("developer") != 0) + +-- ToDo: Remove fretta as a base gamemode (it's outdated and doesn't help us much). +DeriveGamemode("fretta13") + +-- Shared Lua Files +if SERVER then + AddCSLuaFile("sh_config.lua") + AddCSLuaFile("sh_player.lua") +end + +-- Player Classes +if SERVER then + AddCSLuaFile("player_class/class_seeker.lua") + AddCSLuaFile("player_class/class_hider.lua") +end +include "player_class/class_seeker.lua" +include "player_class/class_hider.lua" + +-- Set up Teams +TEAM_SPECTATOR = 0 +TEAM_SEEKERS = 1 +TEAM_HIDERS = 2 +TEAM_HUNTERS = TEAM_SEEKERS -- Deprecated +TEAM_PROPS = TEAM_HIDERS -- Deprecated + +function GM:CreateTeams() + -- Seekers: "Hunters" + team.SetUp(TEAM_SEEKERS, "Seekers", Color(153, 204, 255, 255)) + team.SetSpawnPoint(TEAM_SEEKERS, { + "info_player_deathmatch", + "info_player_axis", + "info_player_combine", + "info_player_counterterrorist" + }) + team.SetClass(TEAM_SEEKERS, { "Seeker" }) + + -- Hiders: "Props" + team.SetUp(TEAM_HIDERS, "Hiders", Color(255, 204, 153, 255)) + team.SetSpawnPoint(TEAM_HIDERS, { + "info_player_deathmatch", + "info_player_allies", + "info_player_terrorist" + }) + team.SetClass(TEAM_HIDERS, { "Hider" }) +end + + + + + +-- Gamemode Player Code & Data +include("sh_player.lua") + +-- Gamemode Configuration Code & Data +include("sh_config.lua") + +-- Include the configuration for this map +if file.Exists("maps/"..game.GetMap()..".lua", "LUA") || file.Exists("maps/"..game.GetMap()..".lua", "LUA") then + include("maps/"..game.GetMap()..".lua") +end + +-- Help info +GM.Help = [[Prop Hunt is a twist on the classic backyard game Hide and Seek. + +As a Prop you have ]]..GetConVar("HUNTER_BLINDLOCK_TIME"):GetInt()..[[ seconds to replicate an existing prop on the map and then find a good hiding spot. Press [E] to replicate the prop you are looking at. Your health is scaled based on the size of the prop you replicate. + +As a Hunter you will be blindfolded for the first ]]..GetConVar("HUNTER_BLINDLOCK_TIME"):GetInt()..[[ seconds of the round while the Props hide. When your blindfold is taken off, you will need to find props controlled by players and kill them. Damaging non-player props will lower your health significantly. However, killing a Prop will increase your health by ]]..GetConVar("HUNTER_KILL_BONUS"):GetInt()..[[ points. + +Both teams can press [F3] to play a taunt sound. Props can press F4 to enable rotation.]] + + +-- Fretta configuration +GM.AddFragsToTeamScore = true +GM.CanOnlySpectateOwnTeam = true +GM.Data = {} +GM.EnableFreezeCam = true +GM.GameLength = GAME_TIME +GM.NoAutomaticSpawning = true +GM.NoNonPlayerPlayerDamage = true +GM.NoPlayerPlayerDamage = true +GM.RoundBased = true +GM.RoundLimit = ROUNDS_PER_MAP +GM.RoundLength = ROUND_TIME +GM.RoundPreStartTime = 0 +GM.SelectModel = false +GM.SuicideString = "couldn't take the pressure and committed suicide." +GM.TeamBased = true diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_player.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_player.lua new file mode 100644 index 0000000..a49f8d5 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_player.lua @@ -0,0 +1,49 @@ +-- Finds the player meta table or terminates +local meta = FindMetaTable("Player") +if !meta then return end + +-- Blinds the player by setting view out into the void +function meta:Blind(bool) + if !self:IsValid() then return end + + if SERVER then + umsg.Start("SetBlind", self) + if bool then + umsg.Bool(true) + else + umsg.Bool(false) + end + umsg.End() + elseif CLIENT then + blind = bool + end +end + +-- Blinds the player by setting view out into the void +function meta:RemoveProp() + if CLIENT || !self:IsValid() then return end + + if self.ph_prop && self.ph_prop:IsValid() then + self.ph_prop:Remove() + self.ph_prop = nil + end +end + +-- Sets a new Hull for a player. +function meta:NewHull(hullOBBMin, hullOBBMax) + if !self:IsValid() then return end + if hullOBBMax == nil then return end + if hullOBBMin == nil then return end + + local hullOBB = hullOBBMax - hullOBBMin + local hullOBBXY = math.max(hullOBB.x, hullOBB.y) + + local xyMul = 0.5 + local hullMin = Vector(-hullOBBXY * xyMul, -hullOBBXY * xyMul, 0) + local hullMax = Vector( hullOBBXY * xyMul, hullOBBXY * xyMul, hullOBB.z) + + self:SetHull(hullMin, hullMax) + self:SetHullDuck(hullMin, hullMax) + self:SetViewOffset(Vector(0, 0, hullOBB.z)) + self:SetViewOffsetDucked(Vector(0, 0, hullOBB.z / 2.0)) +end diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_roundmanager.lua b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_roundmanager.lua new file mode 100644 index 0000000..cca9642 --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/gamemode/sh_roundmanager.lua @@ -0,0 +1,39 @@ +--[[ + The MIT License (MIT) + + Copyright (c) 2015 Project Kube + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +--]] + +--! Our custom RoundManager +-- We need to keep track of three states in a round: Hide, Seek and PostRound. + +--! Round States +ROUNDSTATE_HIDE = 1 +ROUNDSTATE_SEEK = 2 +ROUNDSTATE_POSTROUND = 3 + +function RoundManagerInit() + +end + +function RoundManagerAdvance() + +end \ No newline at end of file diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/icon24.png b/Gamemode/gamemodes/xaymars_prop_hunt/icon24.png new file mode 100644 index 0000000..7d942bd Binary files /dev/null and b/Gamemode/gamemodes/xaymars_prop_hunt/icon24.png differ diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/logo.png b/Gamemode/gamemodes/xaymars_prop_hunt/logo.png new file mode 100644 index 0000000..02be7d6 Binary files /dev/null and b/Gamemode/gamemodes/xaymars_prop_hunt/logo.png differ diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/prophunt.npprj b/Gamemode/gamemodes/xaymars_prop_hunt/prophunt.npprj new file mode 100644 index 0000000..6e4e41e --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/prophunt.npprj @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gamemode/gamemodes/xaymars_prop_hunt/xaymars_prop_hunt.txt b/Gamemode/gamemodes/xaymars_prop_hunt/xaymars_prop_hunt.txt new file mode 100644 index 0000000..90f354b --- /dev/null +++ b/Gamemode/gamemodes/xaymars_prop_hunt/xaymars_prop_hunt.txt @@ -0,0 +1,196 @@ +"xaymars_prop_hunt" +{ + "base" "base" + "title" "Prop Hunt" + "maps" "^ph_|^cs_|^de_|^ttt_" + + "fretta_maps" + { + "1" "ph_" + "2" "cs_" + "3" "de_" + "4" "ttt_" + } + "selectable" "1" + + "menusystem" "1" + "workshopid" "468149739" + + "settings" + { + // Game Settings + 1 + { + "name" "mp_timelimit" + "text" "G: Time Limit" + "help" "Time Limit in Minutes for how long should we stay on one map. (0 to disable)" + "type" "Numeric" + "default" "20" + } + + 2 + { + "name" "ph_sprinting" + "text" "G: Enable Sprinting" + "help" "Should sprinting be possible?" + "type" "CheckBox" + "default" "0" + } + + 3 + { + "name" "ph_tauntwaittime" + "text" "G: Taunt Cooldown" + "help" "How much time must pass before another taunt may be played." + "type" "Numeric" + "default" "5" + } + + // Round Settings + 4 + { + "name" "ph_rounds" + "text" "R: Rounds per Map" + "help" "How many rounds are played per map." + "type" "Numeric" + "default" "10" + } + + 5 + { + "name" "ph_rounds_time" + "text" "R: Round Duration (Seconds)" + "help" "How long is each round going to last?" + "type" "Numeric" + "default" "300" + } + + 6 + { + "name" "ph_rounds_blindtime" + "text" "R: Hiding Time (Seconds)" + "help" "How long are Seekers blinded in seconds?" + "type" "Numeric" + "default" "30" + } + + 7 + { + "name" "ph_rounds_teams_swap" + "text" "R: Swap Teams every round?" + "help" "Should teams be swapped every round? (Can't be used with Randomize)" + "type" "CheckBox" + "default" "1" + } + + 8 + { + "name" "ph_rounds_teams_randomize" + "text" "R: Randomize Teams every round?" + "help" "Should teams be randomized every round? (Can't be used with Swap)" + "type" "CheckBox" + "default" "0" + } + + // Seeker Settings + 9 + { + "name" "ph_seeker_health" + "text" "S: Health" + "type" "Numeric" + "default" "100" + } + + 10 + { + "name" "ph_seeker_health_max" + "text" "S: Max Health" + "type" "Numeric" + "default" "100" + } + + 11 + { + "name" "ph_seeker_health_killbonus" + "text" "S: Health Kill-Bonus" + "help" "Health gained on kill." + "type" "Numeric" + "default" "10" + } + + 11 + { + "name" "ph_seeker_health_penalty" + "text" "S: Health Penalty" + "help" "Health lost on wrong shot." + "type" "Numeric" + "default" "1" + } + + 12 + { + "name" "ph_seeker_ammo_pistol" + "text" "S: Ammo for Pistol" + "type" "Numeric" + "default" "40" + } + + 13 + { + "name" "ph_seeker_ammo_smg" + "text" "S: Ammo for SMG" + "type" "Numeric" + "default" "300" + } + + 14 + { + "name" "ph_seeker_ammo_smggrenade" + "text" "S: Ammo for SMG Grenade" + "type" "Numeric" + "default" "1" + } + + 15 + { + "name" "ph_seeker_ammo_shotgun" + "text" "S: Ammo for Shotgun" + "type" "Numeric" + "default" "60" + } + + // Hider Settings + 16 + { + "name" "ph_hider_health" + "text" "H: Health" + "type" "Numeric" + "default" "100" + } + + 17 + { + "name" "ph_hider_health_max" + "text" "H: Max Health" + "type" "Numeric" + "default" "100" + } + + 18 + { + "name" "ph_hider_health_scale" + "text" "H: Enable Health Scaling" + "help" "Larger & heavier objects have more health, smaller less." + "type" "CheckBox" + "default" "1" + } + + 19 + { + "name" "ph_hider_health_scaled_max" + "text" "H: Scaled Max Health" + "type" "Numeric" + "default" "200" + } + } +} diff --git a/LICENSE b/LICENSE index 2144741..836263d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,22 @@ -The MIT License (MIT) - -Copyright (c) 2016 Michael Fabian Dirks - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +The MIT License (MIT) + +Copyright (c) 2016 Michael Fabian Dirks + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Logo.fw.png b/Logo.fw.png new file mode 100644 index 0000000..2c31d6c Binary files /dev/null and b/Logo.fw.png differ diff --git a/Logo.jpg b/Logo.jpg new file mode 100644 index 0000000..6577311 Binary files /dev/null and b/Logo.jpg differ diff --git a/Tools.bat b/Tools.bat new file mode 100644 index 0000000..50cb207 --- /dev/null +++ b/Tools.bat @@ -0,0 +1,7 @@ +@ECHO OFF +:: Retrieve Garry's Mod path from Regristry +:: FOR /F "tokens=2* delims= " %%A IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 4000" /v InstallLocation') DO SET "GARRYSMODPATH=%%B" +SET "GARRYSMODPATH=D:\Program Files (x86)\Steam\steamapps\common\GarrysMod" + +SET "PATH=%CD%;%GARRYSMODPATH%\bin;%PATH%" +CMD diff --git a/Workshop Package.bat b/Workshop Package.bat new file mode 100644 index 0000000..f3474d9 --- /dev/null +++ b/Workshop Package.bat @@ -0,0 +1,9 @@ +@ECHO OFF +:: Fallback +SET "GARRYSMODPATH=D:\Program Files (x86)\Steam\steamapps\common\GarrysMod" + +:: Retrieve Garry's Mod path from Regristry +FOR /F "tokens=2* delims= " %%A IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 4000" /v InstallLocation') DO SET "GARRYSMODPATH=%%B" + +"%GARRYSMODPATH%\bin\gmad.exe" create -folder "%CD%/Gamemode" -out "%CD%/Pack.gma" +PAUSE \ No newline at end of file diff --git a/Workshop Update.bat b/Workshop Update.bat new file mode 100644 index 0000000..bbdd358 --- /dev/null +++ b/Workshop Update.bat @@ -0,0 +1,10 @@ +@ECHO OFF +:: Fallback +SET "GARRYSMODPATH=D:\Program Files (x86)\Steam\steamapps\common\GarrysMod" + +:: Retrieve Garry's Mod path from Regristry +FOR /F "tokens=2* delims= " %%A IN ('REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 4000" /v InstallLocation') DO SET "GARRYSMODPATH=%%B" + +SET /P CHANGES=Changes: +"%GARRYSMODPATH%\bin\gmpublish.exe" update -id 468149739 -icon "Logo.jpg" -addon "Pack.gma" -changes "%CHANGES% +PAUSE \ No newline at end of file