32 Commits

Author SHA1 Message Date
Michael Fabian 'Xaymar' Dirks 87be085030 gamemode/player_class/class_seeker: Log killed players in the debug log 2018-08-07 00:17:00 +02:00
Michael Fabian 'Xaymar' Dirks 4169000b22 gamemode/player_class/class_hider: Add seeker bonus health on kill and log the kill
Fixes #7
2018-08-07 00:15:54 +02:00
Michael Fabian 'Xaymar' Dirks 2bace089e9 entities/weapons/weapon_ph_smg: Fix weapon refire time and damage
Fixes #8
2018-08-07 00:14:22 +02:00
Michael Fabian 'Xaymar' Dirks 6a6b8da999 gamemode/server: Fix prop rotation messages appearing without debug log
Also adds the intended angle and player initiating the action
2018-07-05 06:56:44 +02:00
Michael Fabian 'Xaymar' Dirks 45925f2029 gamemode/server/roundmanager: Switch to generated implementation
Like with states, allows reuse and prevents missing elements and uses the Lua engine to do the lookup instead of the Lua parser. Slightly faster, but may not be directly obvious in use.
2018-07-05 06:55:46 +02:00
Michael Fabian 'Xaymar' Dirks bf0673914b gamemode/server/states: Switch to generated implementation
This has the benefit that all base info is present as meta info and setting a member to nil simply uses the meta member instead.
2018-07-05 06:54:05 +02:00
Michael Fabian 'Xaymar' Dirks 8e92eeef70 proxy: Add proxy gamemode
This gamemode is a simple proxy to allow the servers to be listed as "Prop Hunt" (using 'gamemode prop_hunt') while in reality playing "Prop Hunt Extended". This is primarily intended for servers where the barebone Prop Hunt configuration is active.
2018-07-05 03:49:24 +02:00
Michael Fabian 'Xaymar' Dirks b4379e94e6 project: ReadMe and workshop.txt 2018-07-05 02:20:50 +02:00
Michael Fabian 'Xaymar' Dirks 3d91f969c9 Version 1.3.1 2018-03-04 22:09:10 +01:00
Michael Fabian 'Xaymar' Dirks 05cf316c58 Shared: Use correct ConVar for Config::Teams::SeekerPercentage() 2018-03-04 21:57:43 +01:00
Michael Fabian 'Xaymar' Dirks 3a78c97742 media: Add normal logo 2017-11-26 16:21:59 +01:00
Michael Fabian 'Xaymar' Dirks 6b2ff0b55f gamemode: Implement a fully featured Scoreboard
The original game mode had a Scoreboard and now Prop Hunt Extended also has one. As usual, it has separation by team (Spectator, Hider, Seeker) and has the game mode logo in the top left. This score board is kept very simple and is non-interactive.

Media: https://drive.google.com/uc?id=12uVh2-BSC3p_aPgRSWNRdz3M0zbBj2BZ
2017-11-26 14:44:26 +01:00
Michael Fabian 'Xaymar' Dirks f5dacddd8c gamemode: Remove dmultilinelabel.lua (See Panel:SetWrap) 2017-11-26 13:50:27 +01:00
Michael Fabian 'Xaymar' Dirks dfdd8f82a3 gamemode: Allow infinite rounds to be played 2017-11-26 13:49:59 +01:00
Michael Fabian 'Xaymar' Dirks 7f3ba60b40 gamemode: Fix Suicide and Team Killing increasing Frags 2017-11-26 13:49:20 +01:00
Michael Fabian 'Xaymar' Dirks 863f099f31 gamemode: Implement Scoreboard support
The game mode will now assign points to teams as well as increment frags and deaths. This paves the way for a proper scoreboard to be added.
2017-11-26 12:54:57 +01:00
Michael Fabian 'Xaymar' Dirks 73a587085c gamemode: Remove the NoCollide constraint on player death 2017-11-26 12:46:02 +01:00
Michael Fabian 'Xaymar' Dirks 20dc523045 media: Add transparent version of the wide logo 2017-11-26 12:41:38 +01:00
Michael Fabian 'Xaymar' Dirks 47b768ffbd content: Rename content directory to prophuntextended 2017-11-26 12:41:06 +01:00
Michael Fabian 'Xaymar' Dirks 7349a96603 gamemode: Implement Alive() function in player classes
This allows checking if the player is actually alive according to the current player class. Some classes are alive to the game, but not to the actual game mode, for example Spectators are alive, but not really.
2017-11-26 12:40:28 +01:00
Michael Fabian 'Xaymar' Dirks be456dc323 gamemode: Fix client load order causing missing files.
Garry's Mod registers lua files in the order they appear, and will immediately run cl_init.lua once it is registered. This occasionally caused includes to error.
2017-11-26 12:38:23 +01:00
Michael Fabian 'Xaymar' Dirks f83464c10b gamemode: Create a few extra fonts 2017-11-26 12:36:33 +01:00
Michael Fabian 'Xaymar' Dirks be017d3d55 gamemode: Fix Hiders getting stuck on themselves
It seems that the physics engine allows the parent to collide with childs, but childs not with the parent. This doesn't make any sense at all.
2017-11-26 09:30:15 +01:00
Michael Fabian 'Xaymar' Dirks 6fbd73b217 gamemode: Remove unused lua code
These are mostly now in sh_config.lua, so there's no need to keep them around.
2017-11-26 08:21:04 +01:00
Michael Fabian 'Xaymar' Dirks ceb9ed1513 data: Remove unused files 2017-11-26 08:11:17 +01:00
Michael Fabian 'Xaymar' Dirks 677ff7b529 gamemode: Only show the selection halo for Hiders
The selection halo was previously visible to everyone in approximate mode, which caused more issues than it should have.
2017-11-26 04:26:23 +01:00
Michael Fabian 'Xaymar' Dirks 624fc3968e gamemode: Fix wrong id for settings
This caused some of these to overwrite each other.
2017-11-25 13:53:10 +01:00
Michael Fabian 'Xaymar' Dirks eb3b7f3aef gamemode: Add new 'Selection Halo' feature
This feature allows Hiders to easily tell which prop they will turn into if they press Use. As always, it can be fully configured and even turned off. By default it is enabled and set to Approximate mode.

In Approximate mode, the client is responsible for guessing the best match to the Use key, but does not have any actual influence on what they will really turn into. This will in most cases be enough, as the internal Use test relies on the forward vector, which is checked by this mode. For those requiring more accuracy, there is the Accurate mode.

In Accurate mode, the server is responsible for exactly knowing the best match to the Use key and sending this to the user. This has a server performance impact that scales with the player base and the rate at which these updates are performed can be configured. This mode is affected by lag and packet loss, so only really useful for high performance LAN servers or testing.
2017-11-25 13:25:29 +01:00
Michael Fabian 'Xaymar' Dirks 785f55a017 gamemode: Add Player meta extension
The meta object is extended by some commonly used functionality that Garry's Mod does not expose directly. Some of these are Server only and will produce wrong results on the Client, also due to Garry's Mod not exposing things properly.
2017-11-25 13:14:57 +01:00
Michael Fabian 'Xaymar' Dirks 2691ca4e46 gamemode/compat: Remove old compatibility code 2017-11-25 09:21:59 +01:00
Michael Fabian 'Xaymar' Dirks 8413d8e08e gamemode: Add support for FindUseEntity hook 2017-11-25 09:18:59 +01:00
Michael Fabian 'Xaymar' Dirks 5a4791a097 gamemode/sh_player: Add proper license header 2017-11-25 09:16:20 +01:00
44 changed files with 1554 additions and 1176 deletions
+12
View File
@@ -0,0 +1,12 @@
[h1]Version: 1.3.1 (Hotfix 1)[/h1]
With this update Prop Hunt Extended now has the last core gameplay feature implemented: Scoreboards! You will no longer have to install any other addon just to have a scoreboard that shows who is in what team. But if you do install an external scoreboard, the gamemode now also properly tracks player kills and deaths, as well as team points.
Two other minor fixes were also implemented. You can now play an infinite amount of rounds (ph_round_limit 0) and Hiders should no longer get stuck on themselves.
[b]Changelog:[/b]
[list][*]Added support for Scoreboards by properly tracking player kills, deaths and team points.
[*]Implemented basic Scoreboard which shows players separated by teams plus their kills and deaths.
[*]Fixed Hiders occasionally getting stuck on their own prop.
[*]Added player class function Alive(), which can be used to tell apart alive and dead players (Note: Player:Alive() is different from this!).
[*]Hotfix: Fixed lua errors caused by indexing wrong ConVar in SeekerPercentage().
[/list]
Binary file not shown.
Binary file not shown.
+7
View File
@@ -0,0 +1,7 @@
{
"title" : "Prop Hunt Extended (Proxy Gamemode)",
"type" : "gamemode",
"tags" : [ ],
"ignore" : [
]
}
@@ -22,9 +22,4 @@
SOFTWARE.
--]]
local ELEMENT = {}
function ELEMENT:Paint()
end
vgui.Register("DMultilineLabel", ELEMENT, "Panel")
include("sh_init.lua")
@@ -0,0 +1,27 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
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.
--]]
AddCSLuaFile("sh_init.lua")
AddCSLuaFile("cl_init.lua")
include "sh_init.lua"
@@ -0,0 +1,37 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
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.
--]]
-- ------------------------------------------------------------------------- --
--! Gamemode Information
-- ------------------------------------------------------------------------- --
GM.Name = "Prop Hunt Extended"
GM.Author = "Michael Fabian 'Xaymar' Dirks"
GM.Email = "info@xaymar.com"
GM.Website = "http://xaymar.com/"
GM.TeamBased = true
GM.AllowAutoTeam = true
GM.SecondsBetweenTeamSwitches = 10
DeriveGamemode("prophuntextended")
+401
View File
@@ -0,0 +1,401 @@
"prop_hunt"
{
"base" "prophuntextended"
"title" "Prop Hunt Extended"
"maps" "^ph_"
"menusystem" "1"
"workshopid" "468149739"
"settings"
{
// Debug Settings
1
{
"name" "ph_debug"
"text" "Enable Debug Mode"
"type" "CheckBox"
"default" "0"
"help" "Prop Hunt: Enable Debug Mode"
}
2
{
"name" "ph_debug_log"
"text" "Enable Debug Logging"
"type" "CheckBox"
"default" "1"
"help" "Prop Hunt: Enable Debug Logging"
}
// Basic Settings
100
{
"name" "ph_gametype"
"text" "Game Type to use"
"type" "Numeric"
"default" "0"
"help" "Prop Hunt: Which Game Type should be played?"
}
101
{
"name" "mp_timelimit"
"text" "Map Time Limit"
"type" "Numeric"
"default" "0"
"help" "Map Time Limit (in Minutes)"
}
102
{
"name" "mp_friendlyfire"
"text" "Friendly fire"
"type" "Numeric"
"default" "0"
"help" "Allow friendly fire? (0 = No, 1 = Yes, 2 = Reflect)"
}
// Round Settings
200
{
"name" "ph_round_limit"
"text" "Maximum Rounds Played"
"type" "Numeric"
"default" "10"
"help" "Round Manager: Maximum Rounds to Play on a single Map"
}
201
{
"name" "ph_round_timelimit"
"text" "Time Limit per Round"
"type" "Numeric"
"default" "180"
"help" "Round Manager: Time Limit per Round (in Seconds)"
}
202
{
"name" "ph_round_blindtime"
"text" "Seeker Blind time per Round"
"type" "Numeric"
"default" "-30"
"help" "Round Manager: Blind Time for Seekers (in Seconds, positive takes away from ph_round_timelimit, negative adds extra time to ph_round_timelimit)"
}
// Team Settings
300
{
"name" "ph_teams_randomize"
"text" "Randomize Teams"
"type" "CheckBox"
"default" "1"
"help" "Teams: Randomize Teams instead of swapping each round"
}
301
{
"name" "ph_teams_weighted"
"text" "Randomize with weighted Score"
"type" "CheckBox"
"default" "1"
"help" "Teams: Use Weighted Randomization"
}
302
{
"name" "ph_teams_seekerpct"
"text" "Pct of Pl. becoming Seeker"
"type" "Numeric"
"default" "25"
"help" "Teams: Initial percentage of Seekers in Dead Hunt Game Type"
}
// Seeker Settings
400
{
"name" "ph_seeker_health"
"text" "Seeker Health"
"type" "Numeric"
"default" "100"
"help" "Seekers: Initial Health"
}
401
{
"name" "ph_seeker_health_max"
"text" "Seeker Max Health"
"type" "Numeric"
"default" "100"
"help" "Seekers: Maximum Health"
}
402
{
"name" "ph_seeker_health_bonus"
"text" "Seeker Health Kill-Bonus"
"type" "Numeric"
"default" "20"
"help" "Seekers: Health Bonus per Kill"
}
403
{
"name" "ph_seeker_health_penalty"
"text" "Seeker Health Penalty"
"type" "Numeric"
"default" "5"
"help" "Seekers: Health Penalty per wrong Shot"
}
404
{
"name" "ph_seeker_weapons"
"text" "Seeker Weapons"
"type" "Text"
"default" "weapon_crowbar,weapon_pistol,weapon_ph_smg,weapon_shotgun"
"help" "Seekers: Initial Weapons (Weapon,Weapon,...)"
}
405
{
"name" "ph_seeker_ammo"
"text" "Seeker Ammo"
"type" "Text"
"default" "Pistol:100,SMG1:300,SMG1_Grenade:1,Buckshot:64"
"help" "Seekers: Initial Ammo (Ammo:Amount,Ammo:Amount,...)"
}
406
{
"name" "ph_seeker_walk_speed"
"text" "Seeker Walk Speed"
"type" "Numeric"
"default" "250"
"help" "Seekers: Walk Speed"
}
407
{
"name" "ph_seeker_sprint"
"text" "Seeker Sprint Allowed"
"type" "CheckBox"
"default" "1"
"help" "Seekers: Allow Sprinting"
}
408
{
"name" "ph_seeker_sprint_speed"
"text" "Seeker Sprint Speed"
"type" "Numeric"
"default" "500"
"help" "Seekers: Sprint Speed"
}
409
{
"name" "ph_seeker_jump_power"
"text" "Seeker Jump Power"
"type" "Numeric"
"default" "200"
"help" "Seekers: Jump Power"
}
// Hider Settings
500
{
"name" "ph_hider_health"
"text" "Hider Health"
"type" "Numeric"
"default" "100"
"help" "Hiders: Initial Health"
}
501
{
"name" "ph_hider_health_max"
"text" "Hider Max Health"
"type" "Numeric"
"default" "100"
"help" "Hiders: Maximum Health"
}
502
{
"name" "ph_hider_health_scaling"
"text" "Hider Enable Health Scaling"
"type" "CheckBox"
"default" "1"
"help" "Hiders: Enable Health Scaling"
}
503
{
"name" "ph_hider_health_scaling_max"
"text" "Hider Scaled Max Health"
"type" "Numeric"
"default" "200"
"help" "Hiders: Maximum scaled Health"
}
504
{
"name" "ph_hider_allow_full_rotation"
"text" "Hider 3-Dimensional Rotation"
"type" "CheckBox"
"default" "0"
"help" "Hiders: Enable full 3D Rotation"
}
505
{
"name" "ph_hider_walk_speed"
"text" "Hider Walk Speed"
"type" "Numeric"
"default" "250"
"help" "Hiders: Walk Speed"
}
506
{
"name" "ph_hider_sprint"
"text" "Hider Sprint Allowed"
"type" "CheckBox"
"default" "0"
"help" "Hiders: Allow Sprinting"
}
507
{
"name" "ph_hider_sprint_speed"
"text" "Hider Sprint Speed"
"type" "Numeric"
"default" "500"
"help" "Hiders: Sprint Speed"
}
507
{
"name" "ph_hider_jump_power"
"text" "Hider Jump Power"
"type" "Numeric"
"default" "200"
"help" "Hiders: Jump Power"
}
// Whitelist & Blacklist
600
{
"name" "ph_list_class_whitelist"
"text" "Allowed Entity Classes"
"type" "Text"
"default" "ph_prop,prop_physics,prop_physics_multiplayer,prop_physics_respawnable"
"help" "Anti-Cheat: Whitelisted Hider Classes"
}
601
{
"name" "ph_list_abuse_blacklist"
"text" "Abuse Blacklist"
"type" "Text"
"default" "func_button,func_door,func_door_rotation,prop_door_rotation,func_tracktrain,func_tanktrain,func_breakable"
"help" "Anti-Cheat: Entity Abuse Blacklist"
}
602
{
"name" "ph_list_model_blacklist"
"text" "Model Blacklist"
"type" "Text"
"default" "models/props/cs_assault/dollar.mdl,models/props/cs_assault/money.mdl,models/props/cs_office/snowman_arm.mdl,models/props/cs_office/projector_remote.mdl"
"help" "Anti-Cheat: Model Abuse Blacklist"
}
// Taunts
700
{
"name" "ph_taunt_cooldown"
"text" "Cooldown for Taunts"
"type" "Numeric"
"default" "5"
"help" "Prop Hunt: Cooldown between Taunts"
}
701
{
"name" "ph_taunt_seekers"
"text" "Seeker Taunts"
"type" "Text"
"default" "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"
"help" "Prop Hunt: Seeker Taunts"
}
702
{
"name" "ph_taunt_hiders"
"text" "Hider Taunts"
"type" "Text"
"default" "ambient/alarms/apc_alarm_pass1.wav,ambient/alarms/manhack_alert_pass1.wav,ambient/alarms/razortrain_horn1.wav,ambient/alarms/scanner_alert_pass1.wav,ambient/alarms/train_horn2.wav,ambient/alarms/train_horn_distant1.wav,ambient/alarms/warningbell1.wav,ambient/energy/whiteflash.wav,ambient/intro/alyxremove.wav,ambient/intro/logosfx.wav,ambient/levels/launch/1stfiringwarning.wav,ambient/levels/launch/rockettakeoffblast.wav,ambient/misc/ambulance1.wav,ambient/misc/carhonk1.wav,ambient/misc/carhonk2.wav,ambient/misc/carhonk3.wav,ambient/outro/gunshipcrash.wav,ambient/3dmeagle.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,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"
"help" "Prop Hunt: Hider Taunts"
}
// Camera
800
{
"name" "ph_camera_allow_noclip"
"text" "Camera Noclip Allowed"
"type" "CheckBox"
"default" "0"
"help" "Camera: Allow clients to disable camera collision"
}
801
{
"name" "ph_camera_distance_max"
"text" "Camera Distance Max."
"type" "Numeric"
"default" "150"
"help" "Camera: Maximum allowed distance to player."
}
802
{
"name" "ph_camera_distance_min"
"text" "Camera Distance Min."
"type" "Numeric"
"default" "30"
"help" "Camera: Minimum allowed distance to player."
}
803
{
"name" "ph_camera_distance_right_range"
"text" "Camera Horizontal Offset Range"
"type" "Numeric"
"default" "20"
"help" "Camera: Horizontal allowed camera distance range."
}
804
{
"name" "ph_camera_distance_up_range"
"text" "Camera Vertical Offset Range"
"type" "Numeric"
"default" "20"
"help" "Camera: Vertical allowed camera distance range."
}
805
{
"name" "ph_camera_lag_min"
"text" "Camera Lag Min."
"type" "Numeric"
"default" "0"
"help" "Camera: Minimum Camera Lag."
}
806
{
"name" "ph_camera_lag_max"
"text" "Camera Lag Max."
"type" "Numeric"
"default" "0.95"
"help" "Camera: Maximum Camera Lag."
}
// Selection Halo
900
{
"name" "ph_selectionhalo_allow"
"text" "Selection Halo Allowed"
"type" "CheckBox"
"default" "1"
"help" "Selection Halo: Allow clients to enable halo around the currently looked at prop?"
}
901
{
"name" "ph_selectionhalo_approximate"
"text" "Selection Approximate Mode"
"type" "CheckBox"
"default" "1"
"help" "Selection Halo: Enable approximate selection halo, which only checks the forward vector on the client."
}
902
{
"name" "ph_selectionhalo_interval"
"text" "Selection Halo Allowed"
"type" "Numeric"
"default" "0.05"
"help" "Selection Halo: Interval for updates of the accuracte selection halo in seconds."
}
}
}
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
call env.win.bat
"%GARRYSMODPATH%\bin\gmad.exe" create -folder "%CD%\proxy" -out "%CD%\proxy.gma"
PAUSE
+4
View File
@@ -0,0 +1,4 @@
@ECHO OFF
call env.win.bat
"%GARRYSMODPATH%\bin\gmpublish.exe" update -id 1327985306 -icon "media\gamemode_workshop.jpg" -addon "proxy.gma" -changes ""
PAUSE
+20
View File
@@ -0,0 +1,20 @@
# Prop Hunt Extended
Hide n Seek with a twist - Hiders can turn into anything on the map! From bottles, to vending machines and all the way to huge cars - what you become depends on how you want to play. Do you want to blend in with your surroundings, or do you want to make a fool of the Seekers? And how will you deal with finding (and killing) all hiders as a Seeker?
The Gamemode comes with out of the box support for console configuration (ph_*), custom taunt support, prop rotation, an improved code base for modders and better integration and some other fixes. Support for [url=https://steamcommunity.com/sharedfiles/filedetails/?id=151583504]MapVote[/url] is also built in, so as long as you have a compatible addon installed you will get a map voting menu at the end of a match.
# How To Play
## Seekers
Your primary goal is to find hiders by any means possible. But watch out, you might lose health if you shoot an object that isn't actually a hider! Pick things up before shooting them, or simply remember the layout of the maps you are playing on. And remember: You lose if only a single hider is still alive at the end of the round.
## Hiders
Stay alive as long as possible and make Seekers look like a fool. Dance around their feet, hide in plain sight or be that huge car that nobody though you could become! What you do is entirely up to you.
## Spectators
You get to watch the whole thing go down live! Sit back, grab some popcorn or any other snack and relax.
# Support the Gamemode
Spreading the word that Prop Hunt Extended exists, work together with others on the gamemode or just play it on your favourite server. Every single thing helps! You can also join our [url=https://discord.gg/tbqhAxr]Discord[/url] and perhaps find additional people to play with that way.
# Need Support?
Please consider reading the [url=http://steamcommunity.com/workshop/filedetails/discussion/468149739/358415206095321277/]Frequently Asked Questions[/url] first. If nothing of that helped, either write a comment below or try asking your question in #gmod-prop-hunt-extended on [url=https://discord.gg/tbqhAxr]Discord[/url].
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@@ -1,8 +0,0 @@
"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"
}
@@ -1,8 +0,0 @@
"TableToKeyValues"
{
"TAUNT_DELAY" "0.1"
"ROUNDS_PER_MAP" "20"
"ROUND_TIME" "240"
"SWAP_TEAM_EVERY_ROUND" "1"
"GAME_TIME" "20"
}
@@ -1,26 +0,0 @@
"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"
}
@@ -1,20 +0,0 @@
"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"
}
@@ -1,131 +0,0 @@
"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"
}
@@ -1,52 +0,0 @@
"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"
}
@@ -1,6 +1,6 @@
-- SWEP Information
SWEP.Author = "Michael 'Xaymar' Dirks"
SWEP.Contact = "info@project-kube.de"
SWEP.Contact = "info@xaymar.com"
SWEP.PrintName = "PH Submachine Gun"
SWEP.Purpose = "More accurate SMG for Prop Hunt."
SWEP.Instructions = "Fire away! Alternative fire to fire a grenade."
@@ -30,8 +30,8 @@ SWEP.Primary.ClipSize = 45
SWEP.Primary.DefaultClip = 45
SWEP.Primary.Automatic = true
SWEP.Primary.Ammo = "SMG1"
SWEP.Primary.Damage = 5
SWEP.Primary.RefireTime = 0.1
SWEP.Primary.Damage = 4
SWEP.Primary.RefireTime = 0.075
-- Secondary Ammunition: SMG Grenades
SWEP.Secondary.ClipSize = 1
@@ -1,259 +0,0 @@
-- 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"
}
-- 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"
}
-- 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"
}
-- 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"
}
@@ -26,6 +26,7 @@
--! Includes
-- ------------------------------------------------------------------------- --
include("sh_init.lua")
include("derma/dscoreboard.lua")
GM.UI = {}
include("client/cl_ui_help.lua")
@@ -42,8 +43,21 @@ function GM:Initialize()
self.Data = {}
print("Prop Hunt CL: Creating Fonts...")
surface.CreateFont("Roboto16", {font="Roboto", extended=true, size=16, weight=500, antialias=true})
surface.CreateFont("Roboto24", {font="Roboto", extended=true, size=24, weight=500, antialias=true})
surface.CreateFont("Roboto32", {font="Roboto", extended=true, size=32, weight=500, antialias=true})
surface.CreateFont("Roboto40", {font="Roboto", extended=true, size=40, weight=500, antialias=true})
surface.CreateFont("Roboto48", {font="Roboto", extended=true, size=48, weight=500, antialias=true})
surface.CreateFont("Roboto16Bold", {font="Roboto Bold", extended=true, size=16, weight=500, antialias=true})
surface.CreateFont("Roboto24Bold", {font="Roboto Bold", extended=true, size=24, weight=500, antialias=true})
surface.CreateFont("Roboto32Bold", {font="Roboto Bold", extended=true, size=32, weight=500, antialias=true})
surface.CreateFont("Roboto40Bold", {font="Roboto Bold", extended=true, size=40, weight=500, antialias=true})
surface.CreateFont("Roboto48Bold", {font="Roboto Bold", extended=true, size=48, weight=500, antialias=true})
surface.CreateFont("RobotoBoldCondensed160", {font="Roboto Bold Condensed", extended=true, size=160, weight=800, antialias=true})
print("Prop Hunt CL: Initializing User Interface...")
self.UI.Scoreboard = vgui.Create("DScoreBoard")
print("Prop Hunt CL: Complete.")
print("-------------------------------------------------------------------------")
end
@@ -167,6 +181,14 @@ function GM:OnSpawnMenuClose()
net.SendToServer()
end
function GM:ScoreboardShow()
self.UI.Scoreboard:Show()
end
function GM:ScoreboardHide()
self.UI.Scoreboard:Hide()
end
function GM:ShowHelpUI()
self.UI.Help:Show()
end
@@ -230,26 +252,57 @@ function DrawNamePlates(bDrawingDepth, bDrawingSkybox)
end
hook.Add("PostDrawTranslucentRenderables", "PHDrawNamePlates", DrawNamePlates)
function DrawSelectionHalo(bDrawingDepth, bDrawingSkybox)
if (!GAMEMODE.Config.SelectionHalo:Enabled()) then return end
if ((LocalPlayer():Team() == GAMEMODE.Teams.Hiders)
&& (player_manager.GetPlayerClass(LocalPlayer()) == "Hider")) then
local ent = nil
if (GAMEMODE.Config.SelectionHalo:Approximate()) then
local trace = {
start = LocalPlayer():EyePos(),
endpos = LocalPlayer():EyePos() + LocalPlayer():EyeAngles():Forward() * 80,
mins = Vector(-16, -16, -16),
maxs = Vector( 16, 16, 16),
mask = MASK_SOLID + CONTENTS_DEBRIS + CONTENTS_PLAYERCLIP,
filter = function(ent)
-- Ensure that we don't actually hit ourselves by accident, or our "hands" model.
if (!IsValid(ent)
|| (ent == LocalPlayer())
|| (ent == LocalPlayer():GetHands())) then
return false
end
if table.HasValue(GAMEMODE.Config.Lists:ClassWhitelist(), ent:GetClass()) then return true end
return false
end,
output = {}
}
util.TraceLine(trace)
if !IsValid(trace.output.Entity) then util.TraceHull(trace) end
if IsValid(trace.output.Entity) then
if (!table.HasValue(GAMEMODE.Config.Lists:ModelBlacklist(), trace.output.Entity:GetModel())) then
ent = trace.output.Entity
end
end
else
ent = LocalPlayer():GetNWEntity("SelectionHalo")
end
if IsValid(ent) then
local color = HSVToColor(
GAMEMODE.Config.SelectionHalo:TintHue(),
GAMEMODE.Config.SelectionHalo:TintSaturation(),
GAMEMODE.Config.SelectionHalo:TintValue()
)
halo.Add({ ent }, color,
GAMEMODE.Config.SelectionHalo:BlurX(), GAMEMODE.Config.SelectionHalo:BlurY(), GAMEMODE.Config.SelectionHalo:Passes(),
GAMEMODE.Config.SelectionHalo:Additive(), GAMEMODE.Config.SelectionHalo:IgnoreZ())
end
end
end
hook.Add("PostDrawEffects", "PHDrawSelectionHalo", DrawSelectionHalo)
-- ------------------------------------------------------------------------- --
--! Old Code
-- ------------------------------------------------------------------------- --
--[[
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)
]]
@@ -41,9 +41,6 @@ function CompatTauntPackLoader()
Taunts[#Taunts+1] = t
elseif (ty == "table") then
Taunts[#Taunts+1] = t[1]
-- for j,snd in ipairs(t) do
-- HiderTaunts[#HiderTaunts+1] = snd
-- end
end
end
GAMEMODE.Config.Taunt.HidersCacheStatic = Taunts
@@ -54,9 +51,6 @@ function CompatTauntPackLoader()
Taunts[#Taunts+1] = t
elseif (ty == "table") then
Taunts[#Taunts+1] = t[1]
-- for j,snd in ipairs(t) do
-- SeekerTaunts[#SeekerTaunts+1] = snd
-- end
end
end
GAMEMODE.Config.Taunt.SeekersCacheStatic = Taunts
@@ -0,0 +1,340 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
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.
--]]
local DPlayerEntry = {}
function DPlayerEntry:Init()
self.Data = {}
self.Data.Margin = {
left = 0,
top = 0,
right = 0,
bottom = 0
}
self.Data.LastUpdate = CurTime() - 10
self.Data.Player = nil
self:SetMinimumSize(100, 40)
self.Container = vgui.Create("DPanel", self)
self.Container:SetBackgroundColor(Color(127,127,127,255))
self.Container:SetPaintBackground(true)
self.Container:Dock(FILL)
self.Container:DockMargin(0, 0, 0, 0)
self.Avatar = vgui.Create("AvatarImage", self.Container)
self.Avatar:Dock(LEFT)
self.Name = vgui.Create("DLabel", self.Container)
self.Name:SetText("Unknown")
self.Name:SetFont("Roboto24")
self.Name:SetContentAlignment(4)
self.Name:Dock(FILL)
self.Name:DockMargin(10, 5, 10, 5)
self.Deaths = vgui.Create("DLabel", self.Container)
self.Deaths:SetSize(80, 30)
self.Deaths:SetText("N/A")
self.Deaths:SetFont("Roboto24")
self.Deaths:SetContentAlignment(6)
self.Deaths:Dock(RIGHT)
self.Deaths:DockMargin(10, 5, 10, 5)
self.Kills = vgui.Create("DLabel", self.Container)
self.Kills:SetSize(80, 30)
self.Kills:SetText("N/A")
self.Kills:SetFont("Roboto24")
self.Kills:SetContentAlignment(6)
self.Kills:Dock(RIGHT)
self.Kills:DockMargin(10, 5, 10, 5)
end
function DPlayerEntry:PerformLayout(w, h)
self:SetMinimumSize(
100 + self.Data.Margin.left + self.Data.Margin.right,
40 + self.Data.Margin.top + self.Data.Margin.bottom
)
-- Docked Content
local containerX, containerY, containerW, containerH = self.Container:GetBounds()
self.Avatar:SetSize(containerH, containerH)
self.Kills:SetSize(80, containerH - 10)
self.Deaths:SetSize(80, containerH - 10)
end
function DPlayerEntry:Think()
if (CurTime() - self.Data.LastUpdate) < 0.25 then return end
if IsValid(self.Data.Player) then
self.Avatar:SetPlayer(self.Data.Player, 184)
self.Name:SetText(self.Data.Player:GetName())
self.Kills:SetText(self.Data.Player:Frags())
self.Deaths:SetText(self.Data.Player:Deaths())
end
end
function DPlayerEntry:SetMargin(left, top, right, bottom)
local uleft = left or 0
local utop = top or uleft
local uright = right or uleft
local ubottom = bottom or utop
self.Container:DockMargin(uleft, utop, uright, ubottom)
self:InvalidateLayout()
end
function DPlayerEntry:SetBackgroundColor(clr)
self.Container:SetBackgroundColor(clr)
end
function DPlayerEntry:SetForegroundColor(clr)
self.Name:SetColor(clr)
self.Kills:SetColor(clr)
self.Deaths:SetColor(clr)
end
function DPlayerEntry:SetPlayer(ply)
self.Data.Player = ply
end
function DPlayerEntry:ShowFlags(kills, deaths)
self.Kills:SetVisible(kills)
self.Deaths:SetVisible(deaths)
self.Container:InvalidateLayout()
end
vgui.Register("DPlayerEntry", DPlayerEntry, "Panel")
local DPlayerList = {}
function DPlayerList:Init()
self.Data = {}
self.Data.Time = CurTime() - 1
self.Data.Team = nil
self.Data.Players = {}
self.Data.ShowKills = true
self.Data.ShowDeaths = true
self.Container = vgui.Create("DPanel", self)
self.Container:SetPaintBackground(true)
self.Container:SetBackgroundColor(Color(51, 51, 51, 102))
self.Container:Dock(FILL)
self.Title = vgui.Create("DPanel", self.Container)
self.Title:SetSize(1, 40)
self.Title:Dock(TOP)
self.Title:DockMargin(0, 0, 0, 5)
self.Title.Name = vgui.Create("DLabel", self.Title)
self.Title.Name:SetText("Unknown")
self.Title.Name:SetFont("Roboto32Bold")
self.Title.Name:SetContentAlignment(5)
self.Title.Name:Dock(FILL)
self.Title.Name:DockMargin(10, 5, 10, 5)
self.Title.Deaths = vgui.Create("DLabel", self.Title)
self.Title.Deaths:SetText("Deaths")
self.Title.Deaths:SetFont("Roboto24")
self.Title.Deaths:SetContentAlignment(6)
self.Title.Deaths:SetSize(80, 40)
self.Title.Deaths:Dock(RIGHT)
self.Title.Deaths:DockMargin(10, 5, 10, 5)
self.Title.Kills = vgui.Create("DLabel", self.Title)
self.Title.Kills:SetText("Kills")
self.Title.Kills:SetFont("Roboto24")
self.Title.Kills:SetContentAlignment(6)
self.Title.Kills:SetSize(80, 40)
self.Title.Kills:Dock(RIGHT)
self.Title.Kills:DockMargin(10, 5, 10, 5)
self.Scroll = vgui.Create("DScrollPanel", self.Container)
self.Scroll:Dock(FILL)
self.Layout = vgui.Create("DListLayout", self.Scroll)
self.Layout:Dock(FILL)
end
function DPlayerList:PerformLayout(w, h)
--self.Container:SetSize(w, h)
end
function DPlayerList:Think()
if (self.Data.Team == -1) then return end
if ((CurTime() - self.Data.Time) < 0.1) then return end
self.Data.Time = CurTime()
-- Update Team Information
if (self.Data.Team != nil) then
self.Title:SetBackgroundColor(team.GetColor(self.Data.Team))
-- Name
self.Title.Name:SetText(team.GetName(self.Data.Team))
-- Text Color
local hue,sat,val = ColorToHSV(team.GetColor(self.Data.Team))
sat = sat * 0.1
val = val * 0.1
local col = HSVToColor(hue,sat,val)
self.Title.Name:SetColor(col)
self.Title.Kills:SetColor(col)
self.Title.Deaths:SetColor(col)
end
-- Update living players
local pls = team.GetPlayers(self.Data.Team)
for i,v in ipairs(pls) do
local alive = player_manager.RunClass(v, "Alive")
local gui = self.Data.Players[v]
if (gui == nil) then
gui = vgui.Create("DPlayerEntry")
gui:SetPlayer(v)
gui:SetMargin(0, 0, 0, 5)
gui:Stop()
gui:SetAlpha(0)
gui:AlphaTo(255, 1)
self.Data.Players[v] = gui
self.Layout:Add(gui)
end
if (alive) then
local hue,sat,val = ColorToHSV(team.GetColor(self.Data.Team))
local sat2,val2 = sat * 0.8, val * 0.8
gui:SetBackgroundColor(HSVToColor(hue, sat2, val2))
local sat3,val3 = sat * 0.1, val
gui:SetForegroundColor(HSVToColor(hue, sat3, val3))
else
local hue,sat,val = ColorToHSV(team.GetColor(self.Data.Team))
local sat2,val2 = sat * 0.8, val * 0.1
gui:SetBackgroundColor(HSVToColor(hue, sat2, val2))
local sat3,val3 = sat * 0.1, val
gui:SetForegroundColor(HSVToColor(hue, sat3, val3))
end
gui:ShowFlags(self.Data.ShowKills, self.Data.ShowDeaths)
end
-- Find dead players
for ply,gui in pairs(self.Data.Players) do
if (!table.HasValue(pls, ply)) then
self.Data.Players[ply] = nil
gui:Stop()
gui:AlphaTo(0, 1)
gui:SetTerm(1)
end
end
self.Layout:InvalidateLayout()
self.Scroll:InvalidateLayout()
end
function DPlayerList:SetTeam(team)
self.Data.Team = team
end
function DPlayerList:ShowFlags(kills, deaths)
self.Data.ShowKills = kills
self.Data.ShowDeaths = deaths
self.Title.Kills:SetVisible(self.Data.ShowKills)
self.Title.Deaths:SetVisible(self.Data.ShowDeaths)
self.Title:InvalidateLayout()
end
vgui.Register("DPlayerList", DPlayerList, "Panel")
local DScoreBoard = {}
function DScoreBoard:Init()
self.Container = vgui.Create("DPanel", self)
self.Container:SetPaintBackground(true)
self.Container:SetBackgroundColor(Color(0, 0, 0, 240))
self.Container:Dock(FILL)
self.Container:DockMargin(0, 35, 0, 0)
self.Logo = vgui.Create("DImage", self)
self.Logo:SetImage("prophuntextended/scoreboard_logo.png")
self.Logo:SetKeepAspect(true)
self.Logo:SetSize(580, 70)
self.Logo:SetPos(10, 0)
self.Frame = vgui.Create("DPanel", self.Container)
self.Frame:SetPaintBackground(false)
self.Frame:Dock(FILL)
self.Frame:DockMargin(5, 40, 5, 5)
self.Teams = {}
self.Teams.Seekers = vgui.Create("DPlayerList", self.Frame)
self.Teams.Seekers:SetTeam(GAMEMODE.Teams.Seekers)
self.Teams.Seekers:Dock(LEFT)
self.Teams.Seekers:DockMargin(5, 5, 5, 5)
self.Teams.Hiders = vgui.Create("DPlayerList", self.Frame)
self.Teams.Hiders:SetTeam(GAMEMODE.Teams.Hiders)
self.Teams.Hiders:Dock(RIGHT)
self.Teams.Hiders:DockMargin(5, 5, 5, 5)
self.Teams.Spectators = vgui.Create("DPlayerList", self.Container)
self.Teams.Spectators:SetSize(100, 100)
self.Teams.Spectators:SetTeam(GAMEMODE.Teams.Spectators)
self.Teams.Spectators:ShowFlags(false, false)
self.Teams.Spectators:Dock(BOTTOM)
self.Teams.Spectators:DockMargin(10, 0, 10, 10)
self:SetVisible(false)
self:SetAlpha(0)
end
function DScoreBoard:PerformLayout(w, h)
local frameW, frameH = self.Frame:GetSize()
local contW, contH = self.Container:GetSize()
-- Update Team Lists
self.Teams.Seekers:SetSize(frameW / 2 - 10, 0)
self.Teams.Hiders:SetSize(frameW / 2 - 10, 0)
self.Teams.Spectators:SetSize(100, contH / 5)
self.Frame:InvalidateLayout()
self.Container:InvalidateLayout()
end
function DScoreBoard:Show()
-- Size and Position
local w, h = ScrW() / 2, ScrH() / 4 * 3
if (w < 600) then w = 600 end
if (h < 400) then h = 400 end
self:SetSize(w, h)
self:Center()
-- Visibility
self:Stop()
self:AlphaTo(255, 0.1)
self:SetVisible(true)
-- Focus
self:SetPopupStayAtBack(true)
self:SetFocusTopLevel(true)
self:RequestFocus()
end
function DScoreBoard:Hide()
-- Visibility
self:Stop()
self:AlphaTo(0, 0.1, 0, function(animData, pnl)
pnl:SetVisible(false)
end)
end
vgui.Register("DScoreBoard", DScoreBoard, "EditablePanel")
@@ -28,15 +28,19 @@
-- Shared
AddCSLuaFile("sh_init.lua")
AddCSLuaFile("sh_config.lua")
AddCSLuaFile("meta/player.lua")
AddCSLuaFile("player_class/class_default.lua")
AddCSLuaFile("player_class/class_spectator.lua")
AddCSLuaFile("player_class/class_seeker.lua")
AddCSLuaFile("player_class/class_hider.lua")
-- Client-Only
AddCSLuaFile("cl_init.lua")
AddCSLuaFile("client/cl_ui_help.lua")
AddCSLuaFile("client/cl_ui_teamselection.lua")
AddCSLuaFile("derma/dscoreboard.lua")
-- Client Init
AddCSLuaFile("cl_init.lua") -- Immediately executed when downloaded, weird bug.
-- ------------------------------------------------------------------------- --
--! Code
@@ -72,8 +76,8 @@ function GM:Initialize()
self.Data = {}
self.Data.StartTime = CurTime()
print("Prop Hunt: Setting initial RoundManager State...")
self.RoundManager:SetState(StatePreMatch)
print("Prop Hunt: Initializing Round Manager")
self.RoundManager = roundManager(StatePreMatch)
print("Prop Hunt: Precaching...")
GAMEMODE.Config.Taunt:Seekers()
@@ -390,15 +394,23 @@ net.Receive("PlayerEnablePropRotation", function(len, ply)
if (ply:Team() != GAMEMODE.Teams.Hiders) then
return
end
print("Prop Hunt: Enabling Prop Rotation")
angle = net.ReadAngle()
ply:SetNWBool("PropRotation", true)
ply.Data.Prop:ApplyRotation(net.ReadAngle())
ply.Data.Prop:ApplyRotation(angle)
if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") enabled prop rotation with angle ["..tostring(angle).."].") end
end)
net.Receive("PlayerDisablePropRotation", function(len, ply)
if (ply:Team() != GAMEMODE.Teams.Hiders) then
return
end
print("Prop Hunt: Disabling Prop Rotation")
angle = net.ReadAngle()
ply:SetNWBool("PropRotation", false)
ply.Data.Prop:ApplyRotation(net.ReadAngle())
ply.Data.Prop:ApplyRotation(angle)
if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Player '"..ply:GetName().."' (SteamID: "..ply:SteamID()..") enabled prop rotation with angle ["..tostring(angle).."].") end
end)
@@ -0,0 +1,259 @@
--[[
The MIT License (MIT)
Copyright (c) 2017 Xaymar
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.
--]]
-- Finds the player meta table or terminates
local meta = FindMetaTable("Player")
if !meta then print("FAILED TO FIND PLAYER META") 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
-- Can the passed entity be used by the player?
function testflag(set, flag)
if (flag == 0) then return true end -- Mod(%) by 0 is nan.
return (set % (2 * flag)) >= flag
end
function meta:IsUseableEntity(ent, requiredCaps)
if ((ent != nil) && (ent:IsValid())) then
local caps = ent:ObjectCaps()
local capsmask = 16 + 32 + 64 + 128
if (testflag(caps, 16)
|| testflag(caps, 32)
|| testflag(caps, 64)
|| testflag(caps, 128)
) && testflag(caps, requiredCaps) then
return true
end
end
return false
end
function IntervalDistance(x, x0, x1)
-- swap so x0 < x1
if ( x0 > x1 ) then
local tmp = x0
x0 = x1
x1 = tmp
end
if ( x < x0 ) then
return x0-x
elseif ( x > x1 ) then
return x - x1
end
return 0
end
-- Find useable entity (Lua version of CBasePlayer:FindUseEntity)
function meta:FindUseEntity()
-- https://raw.githubusercontent.com/ValveSoftware/source-sdk-2013/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/shared/baseplayer_shared.cpp
local PLAYER_USE_RADIUS = 80
-- Vectors
local forward = self:EyeAngles():Forward()
local up = self:EyeAngles():Up()
local center = self:EyePos()
local trace = {
start = center,
endpos = center,
mins = Vector(-16, -16, -16),
maxs = Vector( 16, 16, 16),
mask = MASK_SOLID + CONTENTS_DEBRIS + CONTENTS_PLAYERCLIP,
filter = function(ent)
if (ent == self) then return false end
if (!ent:IsValid()) then return false end
if (ent:IsPlayer()) then return false end
if (ent == self:GetHands()) then return false end
return true
end,
output = {}
}
local foundEnt = nil
local nearestDist = 16777216
local nearestEnt = nil
local tangents_num = 8
local tangents = {}
tangents[1] = 0
tangents[2] = 1
tangents[3] = 0.57735026919
tangents[4] = 0.3639702342
tangents[5] = 0.267949192431
tangents[6] = 0.1763269807
tangents[7] = -0.1763269807
tangents[8] = -0.267949192431
for idx=1,tangents_num,1 do
if (idx == 1) then
trace.endpos = center + forward * 1024
util.TraceLine(trace)
else
local down = forward - (Vector(tangents[idx], tangents[idx], tangents[idx]) * up)
down:Normalize()
trace.endpos = center + down * 72
util.TraceHull(trace)
end
foundEnt = trace.output.Entity
local useable = self:IsUseableEntity(foundEnt, 0)
while ((foundEnt:IsValid()) && !useable && (foundEnt:GetMoveParent():IsValid())) do
foundEnt = foundEnt:GetMoveParent()
useable = self:IsUseableEntity(foundEnt, 0)
end
if (useable) then
local delta = trace.output.HitPos - trace.output.StartPos
local centerZ = foundEnt:WorldSpaceCenter().z
delta.z = IntervalDistance(trace.output.HitPos.z, centerZ + foundEnt:OBBMins().z, centerZ + foundEnt:OBBMaxs().z)
local dist = delta:Length()
if (dist < PLAYER_USE_RADIUS) then
if (foundEnt:IsNPC() && (foundEnt:Team() == self:Team())) then
foundEnt = self:DoubleCheckUseNPC(foundEnt, center, forward)
end
--if (dist < nearestDist) then -- Not identical to CBasePlayer
--nearestDist = dist
nearestEnt = foundEnt
--end
if (idx == 1) then return foundEnt end
end
end
end
-- check ground entity first
-- if you've got a useable ground entity, then shrink the cone of this search to 45 degrees
-- otherwise, search out in a 90 degree cone (hemisphere)
if (self:GetGroundEntity():IsValid() && self:IsUseableEntity(self:GetGroundEntity(), 256)) then
nearestEnt = self:GetGroundEntity()
end
if (nearestEnt) then
local point = self:NearestPoint(center)
nearestDist = util.DistanceToLine(point, center, forward)
end
local search = ents.FindInSphere(center, PLAYER_USE_RADIUS)
for k,v in ipairs(search) do
if (v) && (v:IsValid()) && (self:IsUseableEntity(v, 512)) then
local point = v:NearestPoint(center)
local dir = (point - center):GetNormalized()
local dot = dir:Dot(forward)
if (dot >= 0.8) then
local dist = util.DistanceToLine(point, center, forward)
if (dist < nearestDist) then
trace.endpos = point
util.TraceLine(trace)
if ((trace.output.Fraction == 1.0) || (trace.output.Entity == v)) then
nearestEnt = v
nearestDist = dist
end
end
end
end
end
if (!nearestEnt) then
trace.endpos = center + forward * PLAYER_USE_RADIUS
trace.mask = MASK_OPAQUE_AND_NPCS
util.TraceLine(trace)
if (trace.output.Entity
&& trace.output.Entity:IsValid()
&& self:IsUseableEntity(trace.output.Entity, 0)
&& trace.output.Entity:IsNPC()
&& (trace.output.Entity:Team() == self:Team())) then
nearestEnt = trace.output.Entity
end
end
if (foundEnt:IsNPC() && (foundEnt:Team() == self:Team())) then
foundEnt = self:DoubleCheckUseNPC(foundEnt, center, forward)
end
return nearestEnt
end
-- Double Check NPC
-- Perhaps a poorly-named function. This function traces against the supplied
-- NPC's hitboxes (instead of hull). If the trace hits a different NPC, the
-- new NPC is selected. Otherwise, the supplied NPC is determined to be the
-- one the citizen wants. This function allows the selection of a citizen over
-- another citizen's shoulder, which is impossible without tracing against
-- hitboxes instead of the hull (sjb)
function meta:DoubleCheckUseNPC(npc, src, dir)
local trace = {
start = src,
endpos = src + dir * 1024,
mask = MASK_SHOT,
result = {}
}
util.TraceLine(trace)
if ((trace.result.Entity != nil) && (trace.result.Entity:IsValid()) && (trace.result.Entity:IsNPC()) && (trace.result.Entity != npc)) then
-- Player is selecting a different NPC through some negative space
-- in the first NPC's hitboxes (between legs, over shoulder, etc).
return trace.result.Entity
end
return npc
end
@@ -44,7 +44,10 @@ CLASS.UseVMHands = true -- Uses viewmodel hands
--! Server-Side
-- ------------------------------------------------------------------------- --
-- Spawn
function CLASS:InitialSpawn() end
function CLASS:InitialSpawn()
self.Player.Data = {}
self.Player.Data.SelectionHaloTime = CurTime()
end
function CLASS:Spawn() end
function CLASS:Loadout() end
@@ -58,6 +61,14 @@ function CLASS:DamageEntity(ent, attacker, dmginfo) end -- Damage Dealt To Entit
function CLASS:Death(inflictor, attacker)
self.Player.Data.Alive = false
self.Player.Data.AliveTime = CurTime()
-- Score support
self.Player:AddDeaths(1)
if IsValid(attacker) && attacker:IsPlayer() then
if ((attacker == self.Player)
|| (attacker:Team() == self.Player:Team())) then
self.Player:AddFrags(-1)
end
end
end
function CLASS:SilentDeath()
self.Player.Data.Alive = false
@@ -122,6 +133,8 @@ function CLASS:ShowSpare2() end
-- ------------------------------------------------------------------------- --
function CLASS:PostThink() end
function CLASS:Tick(mv) end
function CLASS:FindUseEntity(defEnt) return defEnt end
function CLASS:Alive() return false end
-- ------------------------------------------------------------------------- --
--! Client-Side
@@ -66,6 +66,7 @@ function CLASS:Spawn()
self.Player.Data.Prop:SetOwner(self.Player)
self.Player.Data.Prop:Spawn()
self.Player:DeleteOnRemove(self.Player.Data.Prop)
self.Player.Data.PropContraint = constraint.NoCollide(self.Player, self.Player.Data.Prop, 0, 0)
-- Assign Hands (Auto Networked Sync!)
local oldhands = self.Player:GetHands()
@@ -74,11 +75,35 @@ function CLASS:Spawn()
end
-- Death
function CLASS:PostDeath(attacker, dmginfo)
function CLASS:DoDeath(attacker, dmginfo)
BaseClass.DoDeath(self, attacker, dmginfo)
if GAMEMODE.Config:DebugLog() then
if (IsValid(attacker) && attacker:IsPlayer() && attacker != self.Player) then
print("Prop Hunt: Hider '"..self.Player:GetName().."' (SteamID: "..self.Player:SteamID()..") killed by '"..attacker:GetName().."' (SteamID: "..attacker:SteamID()..").")
end
end
if (!IsValid(attacker)) then return end
if (!attacker:IsPlayer()) then return end
if (attacker:Team() == self.Player:Team()) then return end
if (attacker:Team() == GAMEMODE.Teams.Seekers) then
if GAMEMODE.Config:DebugLog() then
print("Prop Hunt: Seeker '"..attacker:GetName().."' (SteamID: "..attacker:SteamID()..") gained "..GAMEMODE.Config.Seeker:HealthBonus().." health for killing Hider '"..self.Player:GetName().."' (SteamID: "..self.Player:SteamID()..").")
end
local newhealth = attacker:Health() + GAMEMODE.Config.Seeker:HealthBonus()
if (newhealth > attacker:GetMaxHealth()) then newhealth = attacker:GetMaxHealth() end
attacker:SetHealth(newhealth)
end
end
function CLASS:PostDeath()
if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Hider '"..self.Player:GetName().."' (SteamID: "..self.Player:SteamID()..") died.") end
BaseClass.PostDeath(self, inflictor, attacker)
-- Delete Hands Model
if IsValid(self.Player.Data.PropConstraint) then
self.Player.Data.PropConstraint:Remove()
end
if IsValid(self.Player:GetHands()) then
self.Player:GetHands():Remove()
end
@@ -111,7 +136,9 @@ function CLASS:DeathThink()
end
-- Visible Stuff
function CLASS:SetModel() self.Player:SetModel("models/Gibs/Antlion_gib_small_3.mdl") end -- does "" even work?
function CLASS:SetModel()
self.Player:SetModel("models/Gibs/Antlion_gib_small_3.mdl")
end
-- Interaction
function CLASS:Use(ent)
@@ -179,6 +206,33 @@ end
-- ------------------------------------------------------------------------- --
--! Shared
-- ------------------------------------------------------------------------- --
if SERVER then
function CLASS:FindUseEntity(defEnt)
return self.Player:FindUseEntity()
end
function CLASS:Tick(mv)
if (self.Player.Data == nil) then return end
-- Selection Halo
if (GAMEMODE.Config.SelectionHalo:Allow()) && (!GAMEMODE.Config.SelectionHalo:Approximate()) then
if (self.Player.Data.SelectionHaloTime == nil) then
self.Player.Data.SelectionHaloTime = CurTime()
elseif ((CurTime() - self.Player.Data.SelectionHaloTime) > GAMEMODE.Config.SelectionHalo:Interval()) then
self.Player.Data.SelectionHaloTime = CurTime()
local ent = self.Player:FindUseEntity()
if (IsValid(ent)
&& table.HasValue(GAMEMODE.Config.Lists:ClassWhitelist(), ent:GetClass())
&& !table.HasValue(GAMEMODE.Config.Lists:ModelBlacklist(), ent:GetModel())) then
self.Player:SetNWEntity("SelectionHalo", ent)
else
self.Player:SetNWBool("SelectionHalo", false)
end
end
end
end
end
function CLASS:Alive() return true end
-- ------------------------------------------------------------------------- --
--! Client-Side
@@ -146,8 +146,13 @@ function CLASS:DamageEntity(ent, att, dmg)
end
-- Death
function CLASS:Death(inflictor, attacker)
BaseClass.Death(self, inflictor, attacker)
function CLASS:DoDeath(attacker, dmginfo)
BaseClass.DoDeath(self, attacker, dmginfo)
if GAMEMODE.Config:DebugLog() then
if (IsValid(attacker) && attacker:IsPlayer() && attacker != self.Player) then
print("Prop Hunt: Seeker '"..self.Player:GetName().."' (SteamID: "..self.Player:SteamID()..") killed by '"..attacker:GetName().."' (SteamID: "..attacker:SteamID()..").")
end
end
if SERVER then
self.Player:SetShouldServerRagdoll(true)
@@ -205,6 +210,11 @@ function CLASS:ShowSpare1()
if GAMEMODE.Config:DebugLog() then print("Prop Hunt: Seeker '"..self.Player:GetName().."' (SteamID: "..self.Player:SteamID()..") taunted with sound '"..tauntList[index].."'.") end
end
-- ------------------------------------------------------------------------- --
--! Shared
-- ------------------------------------------------------------------------- --
function CLASS:Alive() return true end
-- ------------------------------------------------------------------------- --
--! Client-Side
-- ------------------------------------------------------------------------- --
@@ -1,549 +0,0 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
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 = { }
GM.Config.ConVars = {}
-- ------------------------------------------------------------------------- --
--! Basic Settings
-- ------------------------------------------------------------------------- --
-- Debug Mode
GM.Config.ConVars.Debug = CreateConVar("ph_debug", 0, FCVAR_CHEAT + FCVAR_REPLICATED)
function GM.Config:Debug()
return self.ConVars.Debug:GetBool()
end
-- Game Mode (See sh_init.lua)
GM.Config.ConVars.GameMode = CreateConVar("ph_gamemode", GM.Modes.Original, FCVAR_REPLICATED)
function GM.Config:GameMode()
return self.ConVars.GameMode:GetInt()
end
-- Timelimit in minutes
GM.Config.ConVars.TimeLimit = GetConVar("mp_timelimit")
function GM.Config:TimeLimit()
return self.ConVars.TimeLimit:GetFloat()
end
-- Enable Sprinting?
GM.Config.ConVars.Sprinting = CreateConVar("ph_sprinting", 0, FCVAR_REPLICATED)
function GM.Config:Sprinting()
return self.ConVars.Sprinting:GetBool()
end
-- Taunt Cooldown (Seconds)
GM.Config.ConVars.TauntCoolDown = CreateConVar("ph_tauntcooldown", 5, FCVAR_REPLICATED)
function GM.Config:TauntCoolDown()
return self.ConVars.TauntCoolDown:GetFloat()
end
-- ------------------------------------------------------------------------- --
--! Round Settings
-- ------------------------------------------------------------------------- --
GM.Config.Round = {}
GM.Config.Round.ConVars = {}
-- How many rounds should the gamemode attempt to fit into the map timelimit, if there is any?
GM.Config.Round.ConVars.Amount = CreateConVar("ph_round_limit", 10, FCVAR_REPLICATED)
function GM.Config.Round:Amount()
return self.ConVars.Amount:GetInt()
end
-- Round Time Limit (Seconds, Default 3 minutes)
GM.Config.Round.ConVars.Time = CreateConVar("ph_round_timelimit", 180, FCVAR_REPLICATED)
function GM.Config.Round:Time()
return self.ConVars.Time:GetInt() - GAMEMODE.Config.Round:BlindTime()
end
-- For how many seconds are the Seekers blinded? (Seconds)
GM.Config.Round.ConVars.BlindTime = CreateConVar("ph_round_blindtime", 30, FCVAR_REPLICATED, "How long are hunters blinded? (positive values will be inside the round time limit, negative will add to the round time limit)")
function GM.Config.Round:BlindTime()
return self.ConVars.BlindTime:GetInt()
end
-- ------------------------------------------------------------------------- --
--! Seeker Settings
-- ------------------------------------------------------------------------- --
GM.Config.Seeker = {}
GM.Config.Seeker.ConVars = {}
GM.Config.Seeker.ConVars.Health = CreateConVar("ph_seeker_health", 100, FCVAR_REPLICATED)
function GM.Config.Seeker:Health()
return self.ConVars.Health:GetInt()
end
GM.Config.Seeker.ConVars.HealthMax = CreateConVar("ph_seeker_health_max", 100, FCVAR_REPLICATED)
function GM.Config.Seeker:HealthMax()
return self.ConVars.HealthMax:GetInt()
end
GM.Config.Seeker.ConVars.HealthBonus = CreateConVar("ph_seeker_health_bonus", 20, FCVAR_REPLICATED)
function GM.Config.Seeker:HealthBonus()
return self.ConVars.HealthBonus:GetInt()
end
GM.Config.Seeker.ConVars.HealthPenalty = CreateConVar("ph_seeker_health_penalty", 5, FCVAR_REPLICATED)
function GM.Config.Seeker:HealthPenalty()
return self.ConVars.HealthPenalty:GetInt()
end
GM.Config.Seeker.ConVars.Weapons = CreateConVar("ph_seeker_weapons", "weapon_crowbar,weapon_pistol,weapon_ph_smg,weapon_shotgun", FCVAR_REPLICATED)
function GM.Config.Seeker:Weapons()
return string.Split(self.ConVars.Weapons:GetString(), ",")
end
GM.Config.Seeker.ConVars.Ammo = CreateConVar("ph_seeker_ammo", "Pistol:100,SMG1:300,SMG1_Grenade:1,Buckshot:64", FCVAR_REPLICATED)
function GM.Config.Seeker:Ammo()
return string.Split(self.ConVars.Ammo:GetString(), ",")
end
-- ------------------------------------------------------------------------- --
--! Hider Settings
-- ------------------------------------------------------------------------- --
GM.Config.Hider = {}
GM.Config.Hider.ConVars = {}
GM.Config.Hider.ConVars.Health = CreateConVar("ph_hider_health", 100, FCVAR_REPLICATED)
function GM.Config.Hider:Health()
return self.ConVars.Health:GetInt()
end
GM.Config.Hider.ConVars.HealthMax = CreateConVar("ph_hider_health_max", 100, FCVAR_REPLICATED)
function GM.Config.Hider:HealthMax()
return self.ConVars.HealthMax:GetInt()
end
GM.Config.Hider.ConVars.HealthScaling = CreateConVar("ph_hider_health_scaling", 100, FCVAR_REPLICATED)
function GM.Config.Hider:HealthScaling()
return self.ConVars.HealthScaling:GetBool()
end
GM.Config.Hider.ConVars.HealthScalingMax = CreateConVar("ph_hider_health_scaling_max", 200, FCVAR_REPLICATED)
function GM.Config.Hider:HealthScalingMax()
return self.ConVars.HealthScalingMax:GetInt()
end
-- ------------------------------------------------------------------------- --
--! Whitelist & Blacklist
-- ------------------------------------------------------------------------- --
GM.Config.Lists = {}
GM.Config.Lists.ConVars = {}
GM.Config.Lists.ConCmds = {}
GM.Config.Lists.ConVars.ClassWhitelist = CreateConVar("ph_list_class_whitelist", "prop_physics,prop_physics_multiplayer,prop_physics_respawnable", FCVAR_REPLICATED)
function GM.Config.Lists:ClassWhitelist()
return string.Split(self.ConVars.ClassWhitelist:GetString(), ",")
end
-- Use Abuse Blacklist
GM.Config.Lists.ConVars.AbuseBlacklist = CreateConVar("ph_list_abuse_blacklist", "func_button,func_door,func_door_rotation,prop_door_rotation,func_tracktrain,func_tanktrain,func_breakable", FCVAR_REPLICATED)
function GM.Config.Lists:AbuseBlacklist()
return string.Split(self.ConVars.AbuseBlacklist:GetString(), ",")
end
-- Model Blacklist
GM.Config.Lists.ModelBlacklist = {}
GM.Config.Lists.ModelBlacklist["models/props/cs_assault/dollar.mdl"] = true
GM.Config.Lists.ModelBlacklist["models/props/cs_assault/money.mdl"] = true
GM.Config.Lists.ModelBlacklist["models/props/cs_office/snowman_arm.mdl"] = true
GM.Config.Lists.ModelBlacklist["models/props/cs_office/projector_remote.mdl"] = true
--GM.Config.Lists.ModelBlacklist["models/props_junk/garbage_plasticbottle001a.mdl"] = true
GM.Config.Lists.ConCmds.ModelBlacklistList = concommand.Add("ph_list_model_blacklist_list", function(ply, cmd, args, argStr)
print("Model Blacklist:")
for k,v in pairs(GAMEMODE.Config.Lists.ModelBlacklist) do
print(" "..k)
end
end, "List all blacklisted models.")
GM.Config.Lists.ConCmds.ModelBlacklistClear = concommand.Add("ph_list_model_blacklist_list", function(ply, cmd, args, argStr)
GM.Config.Lists.ModelBlacklist = {}
end, "Clear blacklisted models.")
GM.Config.Lists.ConCmds.ModelBlacklistAdd = concommand.Add("ph_list_model_blacklist_add", function(ply, cmd, args, argStr)
if (table.count(args) > 0) then
GAMEMODE.Config.Lists.ModelBlacklist[args[1]] = true
else
print("Missing model name")
end
end, "Add a new blacklisted model.")
GM.Config.Lists.ConCmds.ModelBlacklistRemove = concommand.Add("ph_list_model_blacklist_remove", function(ply, cmd, args, argStr)
if (table.count(args) > 0) then
GAMEMODE.Config.Lists.ModelBlacklist[args[1]] = nil
else
print("Missing model name")
end
end, "Removes a blacklisted model.")
-- ------------------------------------------------------------------------- --
--! 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.Config:Debug() then print("Prop Hunt.Taunts.Load: Loading taunt list from file '"..sTauntListFile.."'...") end
-- -- Safeguard against idiots.
-- if type(sTauntListFile) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Load: <sTauntListFile> is not a string.") end
-- return false
-- end
-- -- Given file must exist for us to continue.
-- if ! file.Exists(sTauntListFile, "GAME") then
-- if GAMEMODE.Config: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.Config: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.Config: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.Config: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.Config: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.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Save: Saving taunt list to file '"..sTauntListFile.."'...") end
-- -- Safeguard against idiots.
-- if type(sTauntListFile) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Save: <sTauntListFile> 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.Config: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.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Save: Failed to write to file.") end
-- return false
-- end
-- if GAMEMODE.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Add: Adding new taunt '"..sTauntName.."'...") end
-- -- Safeguard against idiots.
-- if type(sTauntName) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Add: <sTauntName> is not a string.") end
-- return false
-- end
-- if type(sSoundFile) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Add: <sSoundFile> is not a string.") end
-- return false
-- end
-- if (type(iTeamID) != "number" && iTeamID != nil) then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Add: <iTeamID> is not a number or nil.") end
-- return false
-- end
-- if (type(mPropFilter) != "string" && type(mPropFilter) != "table" && mPropFilter != nil) then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Add: <mPropFilter> 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.Config: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.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Add: Team "..iTeamID.."' can't have taunts.") end
-- return false
-- end
-- end
-- if GAMEMODE.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Remove: Removing taunt '"..sTauntName.."'...") end
-- -- Safeguard against idiots.
-- if type(sTauntName) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Remove: <sTauntName> is not a string.") end
-- return false
-- end
-- if (type(iTeamID) != "number" && iTeamID != nil) then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Taunts.Remove: <iTeamID> 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.Config: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.Config:Debug() then print("Prop Hunt.Taunts.Remove: Team "..iTeamID.."' can't have taunts.") end
-- return false
-- end
-- end
-- if GAMEMODE.Config: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.Config:Debug() then print("Prop Hunt.Announcers.Load: Loading announcer list from file '"..sAnnouncerListFile.."'...") end
-- -- Safeguard against idiots.
-- if type(sAnnouncerListFile) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Announcers.Load: <sAnnouncerListFile> is not a string.") end
-- return false
-- end
-- -- Given file must exist for us to continue.
-- if ! file.Exists(sAnnouncerListFile, "GAME") then
-- if GAMEMODE.Config: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.Config: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.Config: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.Config: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.Config: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.Config: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.Config: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.Config: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.Config:Debug() then print("Prop Hunt.Announcers.Save: Saving announcer list to file '"..sAnnouncerListFile.."'...") end
-- -- Safeguard against idiots.
-- if type(sAnnouncerListFile) != "string" then
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Announcers.Save: <sAnnouncerListFile> 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.Config: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.Config: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.Config:Debug() then print("Prop Hunt.Announcers.Save: Failed to write to file.") end
-- return false
-- end
-- if GAMEMODE.Config:Debug() then print("Prop Hunt.Announcers.Save: Complete.") end
-- return true
-- end
-- -- ToDo: Announcers.Add
-- -- ToDo: Announcers.Remove
-- -- ToDo: Announcers.Get(iType)
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,11 +22,27 @@
SOFTWARE.
--]]
GM.RoundManager = {}
GM.RoundManager.State = nil;
GM.RoundManager.NextState = nil;
local roundManagerDef = {}
roundManagerDef.__index = roundManagerDef
function GM.RoundManager:Tick(...)
function roundManagerDef:__construct()
self.State = nil
self.NextState = nil
end
function roundManagerDef:GetState()
return self.State
end
function roundManagerDef:GetNextState()
return self.NextState
end
function roundManagerDef:SetState(state)
self.NextState = state
end
function roundManagerDef:Tick(...)
if (self.State != nil) then
if (self.State.Tick != nil) then
self.State:Tick(...)
@@ -40,13 +56,19 @@ function GM.RoundManager:Tick(...)
if (self.State.OnLeave != nil) then
self.State:OnLeave(self.NextState)
end
-- Run Hook
hook.Run("RoundManagerLeaveState", self.State.Name)
end
-- Call OnEnter(OldState)
if (self.NextState != nil) then
if (self.NextState != nil) then
if (self.NextState.OnEnter != nil) then
self.NextState:OnEnter(self.State)
end
-- Run Hook
hook.Run("RoundManagerEnterState", self.NextState.Name)
end
-- Set State
@@ -54,10 +76,11 @@ function GM.RoundManager:Tick(...)
end
end
function GM.RoundManager:GetState()
return self.State
end
function GM.RoundManager:SetState(State)
self.NextState = State
function roundManager(initialState)
local obj = {}
obj.__index = obj
setmetatable(obj, roundManagerDef)
obj:__construct()
obj:SetState(initialState)
return obj
end
@@ -0,0 +1,53 @@
--[[
The MIT License (MIT)
Copyright (c) 2018 Xaymar
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.
--]]
local stateDef = {}
stateDef.__index = stateDef
function stateDef:__construct()
self.Name = ""
end
function stateDef:GetName()
return self.Name
end
function stateDef:OnEnter() end
function stateDef:Tick() end
function stateDef:OnLeave() end
function state(name)
local obj = {}
obj.__index = obj
setmetatable(obj, stateDef)
obj:__construct()
if (name != nil) then
obj.Name = name
else
obj.Name = "Unnamed"
end
return obj;
end
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,9 @@
SOFTWARE.
--]]
StateHide = {}
include "base.lua"
StateHide = state("Hide")
function StateHide:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StateHide: OnEnter") end
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,9 @@
SOFTWARE.
--]]
StatePostMatch = {}
include "base.lua"
StatePostMatch = state("PostMatch")
function StatePostMatch:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StatePostMatch: OnEnter") end
@@ -32,7 +34,7 @@ function StatePostMatch:OnEnter(OldState)
-- Check Change map conditions.
if ((GAMEMODE.Config:TimeLimit() > 0) && ((CurTime() - GAMEMODE.Data.StartTime) >= (GAMEMODE.Config:TimeLimit() * 60))) -- Over Time
|| (GAMEMODE:GetRound() >= GAMEMODE.Config.Round:Limit()) -- Over Round Limit
|| ((GAMEMODE.Config.Round:Limit() > 0) && (GAMEMODE:GetRound() >= GAMEMODE.Config.Round:Limit())) -- Over Round Limit
then
-- Advance to nothing
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,9 @@
SOFTWARE.
--]]
StatePostRound = {}
include "base.lua"
StatePostRound = state("PostRound")
function StatePostRound:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StatePostRound: OnEnter") end
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,8 +22,9 @@
SOFTWARE.
--]]
-- Precache Network Message
StatePreMatch = {}
include "base.lua"
StatePreMatch = state("PreMatch")
function StatePreMatch:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StatePreMatch: OnEnter") end
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,9 @@
SOFTWARE.
--]]
StatePreRound = {}
include "base.lua"
StatePreRound = state("PreRound")
function StatePreRound:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StatePreRound: OnEnter") end
@@ -1,7 +1,7 @@
--[[
The MIT License (MIT)
Copyright (c) 2015 Xaymar
Copyright (c) 2015-2018 Xaymar
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +22,10 @@
SOFTWARE.
--]]
include "base.lua"
StateSeek = {}
StateSeek.Name = "Seek"
function StateSeek:OnEnter(OldState)
if GAMEMODE.Config:DebugLog() then print("StateSeek: OnEnter") end
@@ -86,10 +89,19 @@ function StateSeek:OnLeave(NewState)
if GAMEMODE.Config:DebugLog() then print("StateSeek: OnLeave") end
if GAMEMODE:GetRoundWinner() == GAMEMODE.Teams.Seekers then
-- Run external Hooks
hook.Run("RoundVictorySeeker")
-- Assign Team Points
team.SetScore(GAMEMODE.Teams.Seekers, team.GetScore(GAMEMODE.Teams.Seekers) + 1)
elseif GAMEMODE:GetRoundWinner() == GAMEMODE.Teams.Hiders then
-- Run external Hooks
hook.Run("RoundVictoryHider")
-- Assign Team Points
team.SetScore(GAMEMODE.Teams.Hiders, team.GetScore(GAMEMODE.Teams.Hiders) + 1)
else
-- Run external Hooks
hook.Run("RoundVictoryDraw")
end
end
@@ -115,7 +115,7 @@ end
-- The Dead Hunt: Percent of players to assign to seeker.
GM.Config.Teams.ConVars.SeekerPercentage = CreateConVarIfNotExists("ph_teams_seekerpct", "25", FCVAR_REPLICATED, "Teams: Initial percentage of Seekers in Dead Hunt Game Type")
function GM.Config.Teams:SeekerPercentage()
return self.ConVars.Teams:GetFloat() / 100
return self.ConVars.SeekerPercentage:GetFloat() / 100
end
-- ------------------------------------------------------------------------- --
@@ -462,3 +462,79 @@ if CLIENT then
return self.ConVars.TintTeam:GetBool()
end
end
-- ------------------------------------------------------------------------- --
--! Selection Halo
-- ------------------------------------------------------------------------- --
GM.Config.SelectionHalo = {}
GM.Config.SelectionHalo.ConVars = {}
-- Allow
GM.Config.SelectionHalo.ConVars.Allow = CreateConVarIfNotExists("ph_selectionhalo_allow", "1", FCVAR_REPLICATED, "Selection Halo: Allow clients to enable halo around the currently looked at prop?")
function GM.Config.SelectionHalo:Allow()
return self.ConVars.Allow:GetBool()
end
-- Approximate
GM.Config.SelectionHalo.ConVars.Approximate = CreateConVarIfNotExists("ph_selectionhalo_approximate", "1", FCVAR_REPLICATED, "Selection Halo: Enable approximate selection halo, which only checks the forward vector on the client.")
function GM.Config.SelectionHalo:Approximate()
return self.ConVars.Approximate:GetBool()
end
if SERVER then
-- Update Interval
GM.Config.SelectionHalo.ConVars.Interval = CreateConVarIfNotExists("ph_selectionhalo_interval", "0.05", FCVAR_ARCHIVE, "Selection Halo: Interval for updates of the accuracte selection halo in seconds.")
function GM.Config.SelectionHalo:Interval()
return self.ConVars.Interval:GetFloat()
end
end
if CLIENT then
-- Enabled
GM.Config.SelectionHalo.ConVars.Enabled = CreateConVarIfNotExists("ph_selectionhalo_enabled", "1", FCVAR_ARCHIVE, "Selection Halo: Enable halo around prop you might become.")
function GM.Config.SelectionHalo:Enabled()
if (self:Allow()) then
return self.ConVars.Enabled:GetBool()
else
return false
end
end
-- Settings
GM.Config.SelectionHalo.ConVars.Passes = CreateConVarIfNotExists("ph_selectionhalo_passes", "1", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Passes")
function GM.Config.SelectionHalo:Passes()
return self.ConVars.Passes:GetInt()
end
GM.Config.SelectionHalo.ConVars.Additive = CreateConVarIfNotExists("ph_selectionhalo_additive", "1", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Additive")
function GM.Config.SelectionHalo:Additive()
return self.ConVars.Additive:GetBool()
end
GM.Config.SelectionHalo.ConVars.IgnoreZ = CreateConVarIfNotExists("ph_selectionhalo_ignorez", "0", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Ignore Z")
function GM.Config.SelectionHalo:IgnoreZ()
return self.ConVars.IgnoreZ:GetBool()
end
-- Blur
GM.Config.SelectionHalo.ConVars.BlurX = CreateConVarIfNotExists("ph_selectionhalo_blur_x", "2", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Blur X")
function GM.Config.SelectionHalo:BlurX()
return self.ConVars.BlurX:GetFloat()
end
GM.Config.SelectionHalo.ConVars.BlurY = CreateConVarIfNotExists("ph_selectionhalo_blur_y", "2", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Blur Y")
function GM.Config.SelectionHalo:BlurY()
return self.ConVars.BlurY:GetFloat()
end
-- Tint Color
GM.Config.SelectionHalo.ConVars.TintHue = CreateConVarIfNotExists("ph_selectionhalo_tint_hue", "0", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Tint Hue")
function GM.Config.SelectionHalo:TintHue()
return self.ConVars.TintHue:GetFloat()
end
GM.Config.SelectionHalo.ConVars.TintSaturation = CreateConVarIfNotExists("ph_selectionhalo_tint_saturation", "0", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Tint Saturation")
function GM.Config.SelectionHalo:TintSaturation()
return self.ConVars.TintSaturation:GetFloat()
end
GM.Config.SelectionHalo.ConVars.TintValue = CreateConVarIfNotExists("ph_selectionhalo_tint_value", "1", FCVAR_ARCHIVE + FCVAR_CLIENTDLL, "Selection Halo: Tint Value")
function GM.Config.SelectionHalo:TintValue()
return self.ConVars.TintValue:GetFloat()
end
end
@@ -104,6 +104,10 @@ function GM:PlayerTick(ply, mv)
return player_manager.RunClass(ply, "Tick", mv)
end
function GM:FindUseEntity(ply, defaultEnt)
return player_manager.RunClass(ply, "FindUseEntity", defaultEnt)
end
-- ------------------------------------------------------------------------- --
--! Gamemode Functionality
-- ------------------------------------------------------------------------- --
@@ -126,6 +130,10 @@ end
-- ------------------------------------------------------------------------- --
--! Includes
-- ------------------------------------------------------------------------- --
-- Meta
include "meta/player.lua"
-- Configuration
include "sh_config.lua"
-- Player Classes
@@ -1,49 +0,0 @@
-- 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
@@ -346,7 +346,7 @@
"default" "20"
"help" "Camera: Horizontal allowed camera distance range."
}
803
804
{
"name" "ph_camera_distance_up_range"
"text" "Camera Vertical Offset Range"
@@ -354,7 +354,7 @@
"default" "20"
"help" "Camera: Vertical allowed camera distance range."
}
803
805
{
"name" "ph_camera_lag_min"
"text" "Camera Lag Min."
@@ -362,7 +362,7 @@
"default" "0"
"help" "Camera: Minimum Camera Lag."
}
803
806
{
"name" "ph_camera_lag_max"
"text" "Camera Lag Max."
@@ -371,5 +371,31 @@
"help" "Camera: Maximum Camera Lag."
}
// Selection Halo
900
{
"name" "ph_selectionhalo_allow"
"text" "Selection Halo Allowed"
"type" "CheckBox"
"default" "1"
"help" "Selection Halo: Allow clients to enable halo around the currently looked at prop?"
}
901
{
"name" "ph_selectionhalo_approximate"
"text" "Selection Approximate Mode"
"type" "CheckBox"
"default" "1"
"help" "Selection Halo: Enable approximate selection halo, which only checks the forward vector on the client."
}
902
{
"name" "ph_selectionhalo_interval"
"text" "Selection Halo Allowed"
"type" "Numeric"
"default" "0.05"
"help" "Selection Halo: Interval for updates of the accuracte selection halo in seconds."
}
}
}
+31
View File
@@ -0,0 +1,31 @@
Hide n Seek with a twist - Hiders can turn into anything on the map! From bottles, to vending machines and all the way to huge cars - what you become depends on how you want to play. Do you want to blend in with your surroundings, or do you want to make a fool of the Seekers? And how will you deal with finding (and killing) all hiders as a Seeker?
The Gamemode comes with out of the box support for console configuration (ph_*), custom taunt support, prop rotation, an improved code base for modders and better integration and some other fixes. Support for [url=https://steamcommunity.com/sharedfiles/filedetails/?id=151583504]MapVote[/url] is also built in, so as long as you have a compatible addon installed you will get a map voting menu at the end of a match.
[h1]How To Play[/h1]
[b]Seekers[/b]
Your primary goal is to find hiders by any means possible. But watch out, you might lose health if you shoot an object that isn't actually a hider! Pick things up before shooting them, or simply remember the layout of the maps you are playing on. And remember: You lose if only a single hider is still alive at the end of the round.
[b]Hiders[/b]
Stay alive as long as possible and make Seekers look like a fool. Dance around their feet, hide in plain sight or be that huge car that nobody though you could become! What you do is entirely up to you.
[b]Spectators[/b]
You get to watch the whole thing go down live! Sit back, grab some popcorn or any other snack and relax.
[h1]Controls[/h1]
[list][*]F2 (ShowTeam): Select A Team or Change Current Team.
[*]F3 (ShowSpare1): Play a Taunt.
[*]C (ShowContextMenu): Toggle between first and third person camera.
[*]Q (ShowSpawnMenu): Hold to rotate your object (Hider only).
[*]Ctrl (Duck, Crouch): Interact with objects instead of turning into them, allowing you to pick things up (Hiders only, Seekers always interact with things).
[*]R with the Prop Hunt SMG (Reload): On full magazine, switch between auto and burst fire.
[/list]
[h1]Support the Gamemode[/h1]
Spreading the word that Prop Hunt Extended exists, work together with others on the gamemode or just play it on your favourite server. Every single thing helps! You can also join our [url=https://discord.gg/tbqhAxr]Discord[/url] and perhaps find additional people to play with that way.
[h1]Need support?[/h1]
Please consider reading the [url=http://steamcommunity.com/workshop/filedetails/discussion/468149739/358415206095321277/]Frequently Asked Questions[/url] first. If nothing of that helped, either write a comment below or try asking your question in #gmod-prop-hunt-extended on [url=https://discord.gg/tbqhAxr]Discord[/url].
[h1]Want to join development?[/h1]
The entire source code is available under the MIT License [url=https://github.com/Xaymar/PropHuntExtended]on GitHub[/url].