From d1974f550408fc7cf7be48ad0c4e60614eeb35fa Mon Sep 17 00:00:00 2001 From: Jackz Date: Tue, 7 Jun 2022 21:11:44 -0500 Subject: [PATCH] Split h&s to multiple files --- .gitignore | 1 + plugins/l4d2_hideandseek.smx | Bin 34387 -> 38954 bytes scripting/include/hideandseek/hscmds.inc | 344 ++++++++ scripting/include/hideandseek/hscore.inc | 167 ++++ scripting/include/hideandseek/hsents.inc | 267 ++++++ scripting/include/hideandseek/hsgame.inc | 299 +++++++ scripting/l4d2_hideandseek.sp | 1007 +++------------------- 7 files changed, 1192 insertions(+), 893 deletions(-) create mode 100644 scripting/include/hideandseek/hscmds.inc create mode 100644 scripting/include/hideandseek/hscore.inc create mode 100644 scripting/include/hideandseek/hsents.inc create mode 100644 scripting/include/hideandseek/hsgame.inc diff --git a/.gitignore b/.gitignore index f54db8a..f30ce97 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ !scripting !scripting/include !scripting/include/feedthetrolls +!scripting/include/hideandseek !gamedata/ template.config.js .* diff --git a/plugins/l4d2_hideandseek.smx b/plugins/l4d2_hideandseek.smx index 2dfdba86d94894f9aaee08529ed0297d707824a9..29face1d788a7a8712744a4b83baafcc628bfd5c 100644 GIT binary patch literal 38954 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%0DQwB5pIh2D0NGOOCTWKj3V*=XD)_>O^NW zjdi*%0CF|Rma$IP?VXm`pU)ePTxXL$@71nxcIxxQZ#0QC<9@HRho5)*BMuq-PKlStD`Q<6y2OVb_F8;*06t~9-L9mwp1eZF-E>sBWB z2DsY+Gx14!V|25#Wg^Qo{rRf1Z(>EfKY#3y^vX?t{?s8o5BkHT+f9G&NV?MW=jTbJ z>Ce9;k)}UKlFBswnX>!FUtQVO!v zALVl^KcAvhDalA zwu2mYNF%{M2D#lKjR^Xbn~yrA@nv>^Y;#B>mwp4X!6A)Y+6i)sLk^i3AlvSc!zBL+ za%*~mG(DdJ$=&XdCMK{y$cRIln7~&+PIgE$Mi~bv>Q2(@&&&f7+kATF>xrXCI~gV= zk>=%^2XZ%vwj-ShR^n_G>P@hV95NjAYm64U;er_81$n6Mxd0+^ zp|0#Axf^5_$n1r(E;g8W2*@@Ng_XL}-0p`St_9g$sVmLX$54e^G8`{+e{M1p!BAA` zN<$t5vI9hPk;oj@bNpDP!gVcDJ$k8XMo&cNj9_9MD{>#wIt1hHj>4(LEVpPHtfNwdQ4SOwmH;FXj;;$Ud=TYwS&inp%XK!N9=L#J-nlxqZ=aBpe zXnd~YL2h^*t=g(Ghd)9+a}Ly6GLo#kdHSim@-@_8t7|VgJ0W`#>|B?OKYgx4W~{cF zYca^2En1l836>g=TVH~->35=V^&q<((rB3#Y>_2BFOr)qZFmtcY`@cW*MN*Tq^W$X zvu(3i`P*DFUirHml9e}QJ_Rx#ttt4iQ|5Ed*3Vviwm0DtyycX+ z!`VLCEA!7T887qi4(XM7Agt7ZA8S>eSLQfp3u>>-qg*mx=5Y?mGR>7UogJ$aCb2_$ zJ---a$CGI4Q%)p*sk6a#B39|_a$ONOB<=y3foaqJnq$J<@9cl=J*W@lx)n+S^qTpi zvop5$cJwEaa@icxyzeaY6PEe379vwc_81!sWY;qo`(Bae%Fhnco`{9E?M5>E31k6U zQ_?HRVCLsRs95?Fxb;cBhNki-f*jXv$#y%FFXMJ!KZVBHCd*3SJLrVH4e9-kW00?O zHt=SPjCpW%5~(@wkQvq*2!7uoz2Ugcy|U9jy&Gi5TUzPpUHPQ5T{t_$RQ?T+YldKM ze(a3V0G*|ev;fV!{cUG=ao%poPn|8sy)t*RO7CbLBIlA(6MqA_>pi^l?>qMAzqnxL zU9AZ8B!i#ZWxlJe$-OG=2XeLDyPTcT@iF%#K3IzF`_29(T=d7nG za|XzEyZ^b@%tB{VbnnUvkZq91MK3y6E_Aj_=aptO?{xM^Cn=}iA;YIEw!s{S^vc|n zL>jgGF~~i3e^9OjF#Wj$cIQ>Fb5No?qz zvBg&D5SPrfPd^ppcDvQPFaGp1Kz3U)9A6_b9b~H|z0oWH8F5JS^tsOV@!r!*T{8ZW TZl$xayw`QBlgR%A+90o{Nb?|~ literal 34387 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 z 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 diff --git a/scripting/include/hideandseek/hscmds.inc b/scripting/include/hideandseek/hscmds.inc new file mode 100644 index 0000000..bc42d77 --- /dev/null +++ b/scripting/include/hideandseek/hscmds.inc @@ -0,0 +1,344 @@ +public Action Command_HideAndSeek(int client, int args) { + if(args > 0) { + char subcmd[16]; + GetCmdArg(1, subcmd, sizeof(subcmd)); + if(StrEqual(subcmd, "r") || StrEqual(subcmd, "reload", false)) { + GetCurrentMap(currentMap, sizeof(currentMap)); + char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + if(ReloadMapDB()) { + if(!LoadConfigForMap(currentMap)) { + ReplyToCommand(client, "Warn: Map has no config file"); + } + Cleanup(); + if(arg[0] == 'f') { + CreateTimer(0.1, Timer_RoundStart); + } + SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); + ReplyToCommand(client, "Reloaded map from config"); + } else { + ReplyToCommand(client, "Error occurred while reloading map file"); + } + } else if(StrEqual(subcmd, "set", false)) { + char set[16]; + if(args == 1) { + ReplyToCommand(client, "Current Map Set: \"%s\" (Specify with /hs set )", currentSet); + if(validSets.Length == 0) ReplyToCommand(client, "Available Sets: (no map config found)"); + else { + ReplyToCommand(client, "Available Sets: "); + for(int i = 0; i < validSets.Length; i++) { + validSets.GetString(i, set, sizeof(set)); + ReplyToCommand(client, "%d. %s", i + 1, set); + } + } + } else { + GetCmdArg(2, currentSet, sizeof(currentSet)); + for(int i = 0; i < validSets.Length; i++) { + validSets.GetString(i, set, sizeof(set)); + if(StrEqual(set, currentSet)) { + if(!LoadConfigForMap(currentMap)) { + ReplyToCommand(client, "Warn: Map has no config file"); + } + Cleanup(); + SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); + ReplyToCommand(client, "Set the current set to \"%s\"", currentSet); + return Plugin_Handled; + } + } + ReplyToCommand(client, "Warning: Set was not found, if this is an error use /hs r to load."); + } + } else if(StrEqual(subcmd, "toggle")) { + char type[32]; + GetCmdArg(2, type, sizeof(type)); + bool doAll = StrEqual(type, "all"); + bool isUnknown = true; + + if(doAll || StrEqual(type, "blockers", false)) { + if(isNavBlockersEnabled) { + EntFire("hsblocker", "Disable"); + ReplyToCommand(client, "Disabled all custom gamemode blockers"); + } else { + EntFire("hsblocker", "Enable"); + ReplyToCommand(client, "Enabled all custom gamemode blockers"); + } + isNavBlockersEnabled = !isNavBlockersEnabled; + isUnknown = false; + } + if(doAll || StrEqual(type, "props", false)) { + if(isPropsEnabled) { + EntFire("hsprop", "Disable"); + EntFire("hsprop", "DisableCollision"); + ReplyToCommand(client, "Disabled all custom gamemode props"); + } else { + EntFire("hsprop", "Enable"); + EntFire("hsprop", "EnableCollision"); + ReplyToCommand(client, "Enabled all custom gamemode props"); + } + isPropsEnabled = !isPropsEnabled; + isUnknown = false; + } + if(doAll || StrEqual(type, "portals", false)) { + if(isPortalsEnabled) { + EntFire("hsportal", "Disable"); + ReplyToCommand(client, "Disabled all custom gamemode portals"); + } else { + EntFire("hsportal", "Enable"); + ReplyToCommand(client, "Enabled all custom gamemode portals"); + } + isPortalsEnabled = !isPortalsEnabled; + isUnknown = false; + } + if(isUnknown) ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); + } else if(StrEqual(subcmd, "clear", false)) { + static char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + if(StrEqual(arg, "all")) { + Cleanup(); + ReplyToCommand(client, "Cleaned up everything."); + } else if(StrEqual(arg, "props")) { + EntFire("hsprop", "kill"); + ReplyToCommand(client, "Removed all custom gamemode props"); + } else if(StrEqual(arg, "blockers")) { + EntFire("hsblocker", "kill"); + ReplyToCommand(client, "Removed all custom gamemode blockers"); + } else if(StrEqual(arg, "portals")) { + EntFire("hsportal", "kill"); + ReplyToCommand(client, "Removed all custom gamemode portals"); + } else ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); + } else if(StrEqual(subcmd, "settime")) { + int prev = GetMapTime(); + static char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + int time = StringToInt(arg); + mapConfig.mapTime = time; + SetMapTime(time); + ReplyToCommand(client, "Map's time is temporarily set to %d seconds (was %d)", time, prev); + } else if(StrEqual(subcmd, "settick")) { + static char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + int tick = -StringToInt(arg); + SetTick(tick); + ReplyToCommand(client, "Set tick time to %d", tick); + } else if(StrEqual(subcmd, "map")) { + static char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + if(StrEqual(arg, "list")) { + ReplyToCommand(client, "See the console for available maps"); + char map[64]; + for(int i = 0; i < validMaps.Length; i++) { + validMaps.GetString(i, map, sizeof(map)); + PrintToConsole(client, "%d. %s", i + 1, map); + } + } else if(StrEqual(arg, "random")) { + bool foundMap; + char map[64]; + do { + int mapIndex = GetURandomInt() % validMaps.Length; + validMaps.GetString(mapIndex, map, sizeof(map)); + if(!StrEqual(currentMap, map, false)) { + foundMap = true; + } + } while(!foundMap); + PrintToChatAll("[H&S] Switching map to %s", map); + ChangeMap(map); + } else if(StrEqual(arg, "next", false)) { + if(args == 1) { + ReplyToCommand(client, "Specify the map to change on the next round: 'next '"); + } else { + char arg2[64]; + GetCmdArg(3, arg2, sizeof(arg2)); + if(IsMapValid(arg2)) { + strcopy(nextRoundMap, sizeof(nextRoundMap), arg2); + PrintToChatAll("[H&S] Switching map next round to %s", arg2); + ForceChangeLevel(arg, "SetMapSelect"); + } else { + ReplyToCommand(client, "Map is not valid"); + } + } + } else if(StrEqual(arg, "force", false)) { + if(args == 1) { + ReplyToCommand(client, "Specify the map to change to: 'force '"); + } else { + char arg2[64]; + GetCmdArg(3, arg2, sizeof(arg2)); + if(IsMapValid(arg2)) { + PrintToChatAll("[H&S] Switching map to %s", arg2); + ChangeMap(arg2); + } else { + ReplyToCommand(client, "Map is not valid"); + } + } + } else { + ReplyToCommand(client, "Syntax: 'map /next >"); + } + return Plugin_Handled; + } else if(StrEqual(subcmd, "pos", false)) { + float pos[3]; + GetAbsOrigin(client, pos); + ReplyToCommand(client, "\"origin\" \"%f %f %f\"", pos[0], pos[1], pos[2]); + GetClientEyeAngles(client, pos); + ReplyToCommand(client, "\"rotation\" \"%f %f %f\"", pos[0], pos[1], pos[2]); + } else if(StrEqual(subcmd, "prop", false)) { + float pos[3]; + GetAbsOrigin(client, pos); + ReplyToCommand(client, "\"MYPROP\""); + ReplyToCommand(client, "{"); + ReplyToCommand(client, "\t\"origin\" \"%f %f %f\"", pos[0], pos[1], pos[2]); + GetClientAbsAngles(client, pos); + ReplyToCommand(client, "\t\"rotation\" \"%f %f %f\"", pos[0], pos[1], pos[2]); + ReplyToCommand(client, "\t\"type\" \"prop_dynamic\""); + ReplyToCommand(client, "\t\"model\" \"props_junk/dumpster_2.mdl\""); + ReplyToCommand(client, "}"); + } else if(StrEqual(subcmd, "setspawn", false)) { + GetClientAbsOrigin(client, mapConfig.spawnpoint); + ReplyToCommand(client, "Set map's temporarily spawnpoint to your location."); + } else if(StrEqual(subcmd, "stuck")) { + TeleportEntity(client, mapConfig.spawnpoint, NULL_VECTOR, NULL_VECTOR); + } else if(StrEqual(subcmd, "bots")) { + if(args == 2) { + char arg[16]; + GetCmdArg(2, arg, sizeof(arg)); + if(StrEqual(arg, "toggle")) { + bool newValue = !IsBotsEnabled(); + SetBotsEnabled(newValue); + if(newValue) ReplyToCommand(client, "Bots are now enabled"); + else ReplyToCommand(client, "Bots are now disabled"); + return Plugin_Handled; + } else if(StrEqual(arg, "on") || StrEqual(arg, "true")) { + SetBotsEnabled(true); + ReplyToCommand(client, "Bots are now enabled"); + return Plugin_Handled; + } else if(StrEqual(arg, "off") || StrEqual(arg, "false")) { + SetBotsEnabled(false); + ReplyToCommand(client, "Bots are now disabled"); + return Plugin_Handled; + } + } + if(IsBotsEnabled()) ReplyToCommand(client, "Bots are enabled"); + else ReplyToCommand(client, "Bots are disabled"); + } else if(StrEqual(subcmd, "peek")) { + SetPeekCamTarget(client); + SetPeekCamActive(client, !IsPeekCamActive(client)); + } else if(StrEqual(subcmd, "seeker")) { + if(args == 2) { + char arg1[32]; + GetCmdArg(2, arg1, sizeof(arg1)); + char target_name[MAX_TARGET_LENGTH]; + int target_list[1], target_count; + bool tn_is_ml; + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + 1, + 0, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0 + || target_list[0] == 0){ + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + SetSlasher(target_list[0]); + ReplyToCommand(client, "Set the current seeker to %N", target_list[0]); + } else { + ReplyToCommand(client, "The current seeker is: %N", GetSlasher()); + } + } else if(StrEqual(subcmd, "debug")) { + ReplyToCommand(client, "- Game Info -"); + int addSlasher = GetSlasher(); + ReplyToCommand(client, "Current seeker: %N(%d) (addon says %N(%d))", currentSeeker, currentSeeker, addSlasher, addSlasher); + ReplyToCommand(client, "State: %d | Tick: %d", view_as(GetState()), GetTick()); + + ReplyToCommand(client, "- Map Info -"); + ReplyToCommand(client, "Map: %s (set %s)", currentMap, currentSet); + if(mapConfig.hasSpawnpoint) + ReplyToCommand(client, "Has Spawnpoint: yes (%f %f %f)", mapConfig.spawnpoint[0], mapConfig.spawnpoint[1], mapConfig.spawnpoint[2]); + else + ReplyToCommand(client, "Has Spawnpoint: no (possibly map spawn %f %f %f)", mapConfig.spawnpoint[0], mapConfig.spawnpoint[1], mapConfig.spawnpoint[2]); + ReplyToCommand(client, "Climbing: %b", mapConfig.canClimb); + ReplyToCommand(client, "Buttons Auto-press: %b", mapConfig.pressButtons); + ReplyToCommand(client, "Map Time Override: %d", mapConfig.mapTime); + } + return Plugin_Handled; + } + ReplyToCommand(client, " - Hide & Seek Commands -"); + ReplyToCommand(client, "toggle : Toggles all specified"); + ReplyToCommand(client, "set [new set]: Change the prop set or view current"); + ReplyToCommand(client, "clear : Clear all specified"); + ReplyToCommand(client, "settime [seconds]: Sets the time override for the map"); + ReplyToCommand(client, "settick [tick]: Sets the current tick timer value"); + ReplyToCommand(client, "setspawn: Sets the temporary spawnpoint for the map"); + ReplyToCommand(client, "stuck: Teleports you to spawn to unstuck yourself"); + ReplyToCommand(client, "bots [toggle, [value]]: View if bots are enabled, or turn them on"); + ReplyToCommand(client, "seeker [new seeker]: Get the active seeker, or set a new one."); + ReplyToCommand(client, "peek - debug cmd"); + return Plugin_Handled; +} + +public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { + if(isEnabled) { + if(!StrEqual(command, "say")) { //Is team message + if(currentSeeker <= 0 || currentSeeker == client) { + return Plugin_Continue; + } + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) + PrintToChat(i, "[Hiders] %N: %s", client, sArgs); + } + return Plugin_Handled; + } + } + return Plugin_Continue; +} + + +public Action Command_Join(int client, int args) { + static float tpLoc[3]; + GetSpawnPosition(tpLoc); + if(args == 1) { + static char arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + char target_name[MAX_TARGET_LENGTH]; + int target_list[MAXPLAYERS], target_count; + bool tn_is_ml; + if ((target_count = ProcessTargetString( + arg1, + client, + target_list, + MAXPLAYERS, + 0, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + /* This function replies to the admin with a failure message */ + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + for (int i = 0; i < target_count; i++) { + int target = target_list[i]; + if(GetClientTeam(target) != 2) { + ChangeClientTeam(target, 2); + L4D_RespawnPlayer(target); + TeleportEntity(target, tpLoc, NULL_VECTOR, NULL_VECTOR); + isPendingPlay[client] = false; + CheatCommand(target, "give", "knife"); + } + } + ReplyToCommand(client, "Joined %s", target_name); + } else { + if(currentSeeker == client) { + ReplyToCommand(client, "You are already in-game as a seeker."); + return Plugin_Handled; + } + isPendingPlay[client] = false; + ChangeClientTeam(client, 2); + L4D_RespawnPlayer(client); + TeleportEntity(client, tpLoc, NULL_VECTOR, NULL_VECTOR); + CheatCommand(client, "give", "knife"); + } + return Plugin_Handled; +} diff --git a/scripting/include/hideandseek/hscore.inc b/scripting/include/hideandseek/hscore.inc new file mode 100644 index 0000000..5d51238 --- /dev/null +++ b/scripting/include/hideandseek/hscore.inc @@ -0,0 +1,167 @@ +#include +#include +#include + +static KeyValues kv; +StringMap mapConfigs; + +bool ReloadMapDB() { + if(kv != null) { + delete kv; + } + kv = new KeyValues("hideandseek"); + + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, sizeof(sPath), "data/hideandseek.cfg"); + + + if(!FileExists(sPath) || !kv.ImportFromFile(sPath)) { + delete kv; + return false; + } + + validMaps.Clear(); + + char map[64]; + kv.GotoFirstSubKey(true); + do { + kv.GetSectionName(map, sizeof(map)); + validMaps.PushString(map); + } while(kv.GotoNextKey(true)); + kv.GoBack(); + return true; +} + + + +bool LoadConfigForMap(const char[] map) { + kv.Rewind(); + if (kv.JumpToKey(map)) { + PrintToServer("[H&S] Loading config data for set %s on %s", currentSet, map); + MapConfig config; + config.entities = new ArrayList(sizeof(EntityConfig)); + config.inputs = new ArrayList(ByteCountToCells(64)); + validSets.Clear(); + if(kv.JumpToKey("ents")) { + kv.GotoFirstSubKey(); + char entSet[16]; + do { + EntityConfig entCfg; + kv.GetVector("origin", entCfg.origin, NULL_VECTOR); + kv.GetVector("rotation", entCfg.rotation, NULL_VECTOR); + kv.GetString("type", entCfg.type, sizeof(entCfg.type), "env_physics_blocker"); + kv.GetString("model", entCfg.model, sizeof(entCfg.model), ""); + if(entCfg.model[0] != '\0') + Format(entCfg.model, sizeof(entCfg.model), "models/%s", entCfg.model); + kv.GetVector("scale", entCfg.scale, DEFAULT_SCALE); + kv.GetVector("offset", entCfg.offset, NULL_VECTOR); + kv.GetString("set", entSet, sizeof(entSet), "default"); + if(validSets.FindString(entSet) == -1) { + validSets.PushString(entSet); + } + char debug_str[64]; + if(StrEqual(entSet, "default") || StrEqual(currentSet, entSet, false)) { + + config.entities.PushArray(entCfg); + } else { + kv.GetSectionName(debug_str, sizeof(debug_str)); + PrintToServer("Skipping %s", debug_str); + } + } while (kv.GotoNextKey()); + // JumpToKey and GotoFirstSubKey both traverse, i guess, go back + kv.GoBack(); + kv.GoBack(); + } + if(kv.JumpToKey("inputs")) { + kv.GotoFirstSubKey(false); + static char buffer[64]; + do { + kv.GetSectionName(buffer, sizeof(buffer)); + config.inputs.PushString(buffer); + + kv.GetString(NULL_STRING, buffer, sizeof(buffer)); + config.inputs.PushString(buffer); + } while (kv.GotoNextKey(false)); + kv.GoBack(); + kv.GoBack(); + } + int mapTime; + + config.hasSpawnpoint = false; + config.canClimb = true; + config.pressButtons = true; + if(!StrEqual(currentSet, "default") && kv.JumpToKey("sets")) { + char set[16]; + kv.GotoFirstSubKey(true); + do { + kv.GetSectionName(set, sizeof(set)); + if(validSets.FindString(set) == -1) { + validSets.PushString(set); + } + if(StrEqual(currentSet, set, false)) { + kv.GetVector("spawnpoint", config.spawnpoint); + if(config.spawnpoint[0] != 0.0 && config.spawnpoint[1] != 0.0 && config.spawnpoint[2] != 0.0) { + PrintToServer("[H&S] Using provided custom spawnpoint for set %s at %0.1f, %0.1f, %0.1f", currentSet, config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); + config.hasSpawnpoint = true; + } + char buf[8]; + kv.GetString("climbing", buf, sizeof(buf)); + config.canClimb = !StrEqual(buf, "off"); + kv.GetString("buttons", buf, sizeof(buf)); + config.pressButtons = !StrEqual(buf, "no"); + mapTime = kv.GetNum("maptime", 0); + break; + } + + } while(kv.GotoNextKey(true)); + kv.GoBack(); + kv.GoBack(); + } + + if(!config.hasSpawnpoint) { + kv.GetVector("spawnpoint", config.spawnpoint); + if(config.spawnpoint[0] != 0.0 && config.spawnpoint[1] != 0.0 && config.spawnpoint[2] != 0.0) { + PrintToServer("[H&S] Using provided custom spawnpoint at %0.1f, %0.1f, %0.1f", config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); + config.hasSpawnpoint = true; + } else if (GetSpawnPosition(config.spawnpoint, false)) { + PrintToServer("[H&S] Using map spawnpoint at %0.1f, %0.1f, %0.1f", config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); + config.hasSpawnpoint = true; + } else { + PrintToServer("[H&S] Could not find any spawnpoints, using default spawn"); + config.hasSpawnpoint = false; + } + } + + // Use default maptime if exists + if(mapTime == 0) + mapTime = kv.GetNum("maptime", 0); + if(mapTime > 0) { + config.mapTime = mapTime; + PrintToServer("[H&S] Map time overwritten to %d seconds", mapTime); + } + + char buf[8]; + if(config.canClimb) { + kv.GetString("climbing", buf, sizeof(buf)); + config.canClimb = !StrEqual(buf, "off"); + } + if(config.pressButtons) { + kv.GetString("buttons", buf, sizeof(buf)); + config.pressButtons = !StrEqual(buf, "no"); + } + + + + mapConfigs.SetArray(map, config, sizeof(MapConfig)); + // Discard entInputs if unused + if(config.inputs.Length == 0) { + delete config.inputs; + } + mapConfig = config; + return true; + } else { + mapConfig.hasSpawnpoint = false; + PrintToServer("[H&S] No map config exists for %s", map); + return false; + } +} \ No newline at end of file diff --git a/scripting/include/hideandseek/hsents.inc b/scripting/include/hideandseek/hsents.inc new file mode 100644 index 0000000..b7ecbd4 --- /dev/null +++ b/scripting/include/hideandseek/hsents.inc @@ -0,0 +1,267 @@ +stock int CreateEnvBlockerBox(const float pos[3], bool enabled = true) { + int entity = CreateEntityByName("env_physics_blocker"); + if(entity == -1) return -1; + DispatchKeyValue(entity, "targetname", "hsblocker"); + DispatchKeyValue(entity, "initialstate", "1"); + DispatchKeyValue(entity, "BlockType", "0"); + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + if(DispatchSpawn(entity)) { + if(enabled) + AcceptEntityInput(entity, "Enable"); + return entity; + } + return -1; +} + +stock int CreateEnvBlockerScaled(const char[] entClass, const float pos[3], const float scale[3] = { 5.0, 5.0, 5.0 }, bool enabled = true) { + int entity = CreateEntityByName(entClass); + DispatchKeyValue(entity, "targetname", "hsblocker"); + DispatchKeyValue(entity, "initialstate", "1"); + DispatchKeyValue(entity, "BlockType", "0"); + static float mins[3]; + mins = scale; + NegateVector(mins); + DispatchKeyValueVector(entity, "boxmins", mins); + DispatchKeyValueVector(entity, "boxmaxs", scale); + DispatchKeyValueVector(entity, "mins", mins); + DispatchKeyValueVector(entity, "maxs", scale); + + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + if(DispatchSpawn(entity)) { + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn blocker scaled %.1f %.1f %.1f scale [%.0f %.0f %.0f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); + #endif + SetEntPropVector(entity, Prop_Send, "m_vecMaxs", scale); + SetEntPropVector(entity, Prop_Send, "m_vecMins", mins); + if(enabled) + AcceptEntityInput(entity, "Enable"); + #if defined DEBUG_BLOCKERS + Effect_DrawBeamBoxRotatableToAll(pos, mins, scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 0, 255}, 0); + #endif + return entity; + } + return -1; +} + +enum PortalType { + Portal_Relative, + Portal_Teleport +} +PortalType entityPortalType[2048]; +float entityPortalOffsets[2048][3]; + +stock int CreatePortal(PortalType type, const char model[64], const float pos[3], const float offset[3] = { 40.0, 40.0, 0.0 }, const float scale[3] = { 5.0, 5.0, 5.0 }) { + int entity = CreateEntityByName("trigger_multiple"); + if(entity == -1) return -1; + DispatchKeyValue(entity, "spawnflags", "513"); + DispatchKeyValue(entity, "solid", "6"); + DispatchKeyValue(entity, "targetname", "hsportal"); + DispatchKeyValue(entity, "wait", "0"); + if(DispatchSpawn(entity)) { + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + static float mins[3]; + mins = scale; + NegateVector(mins); + SetEntPropVector(entity, Prop_Send, "m_vecMaxs", scale); + SetEntPropVector(entity, Prop_Send, "m_vecMins", mins); + SetEntProp(entity, Prop_Send, "m_nSolidType", 2); + + HookSingleEntityOutput(entity, "OnStartTouch", OnPortalTouch, false); + #if defined DEBUG_BLOCKERS + Effect_DrawBeamBoxRotatableToAll(pos, mins, scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 255, 255}, 0); + #endif + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn portal %d - pos %.1f %.1f %.1f - scale %.1f %.1f %.1f", entity, pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); + #endif + AcceptEntityInput(entity, "Enable"); + + entityPortalOffsets[entity] = NULL_VECTOR; + + // Convert relative offset to one based off full scale: + entityPortalType[entity] = type; + if(type == Portal_Relative) { + if(offset[0] != 0.0) entityPortalOffsets[entity][0] = (scale[0] * 2) + offset[0]; + if(offset[1] != 0.0) entityPortalOffsets[entity][1] = (scale[1] * 2) + offset[1]; + if(offset[2] != 0.0) entityPortalOffsets[entity][2] = (scale[2] * 2) + offset[2]; + } else { + entityPortalOffsets[entity] = offset; + } + + return entity; + } + return -1; +} + +void OnPortalTouch(const char[] output, int caller, int activator, float delay) { + if(entityPortalType[caller] == Portal_Relative) { + float pos[3]; + GetClientAbsOrigin(activator, pos); + float ang[3]; + GetClientAbsAngles(activator, ang); + if(ang[0] < 0) pos[0] -= entityPortalOffsets[caller][0]; + else pos[0] += entityPortalOffsets[caller][0]; + if(ang[1] < 0) pos[1] -= entityPortalOffsets[caller][1]; + else pos[1] += entityPortalOffsets[caller][1]; + if(ang[2] < 0) pos[2] -= entityPortalOffsets[caller][2]; + else pos[2] += entityPortalOffsets[caller][2]; + TeleportEntity(activator, pos, NULL_VECTOR, NULL_VECTOR); + } else { + TeleportEntity(activator, entityPortalOffsets[caller], NULL_VECTOR, NULL_VECTOR); + } +} + +stock int StartPropCreate(const char[] entClass, const char[] model, const float pos[3], const float ang[3]) { + int entity = CreateEntityByName(entClass); + if(entity == -1) return -1; + DispatchKeyValue(entity, "model", model); + DispatchKeyValue(entity, "solid", "6"); + DispatchKeyValue(entity, "targetname", "hsprop"); + DispatchKeyValue(entity, "disableshadows", "1"); + TeleportEntity(entity, pos, ang, NULL_VECTOR); + return entity; +} + +stock int CreateProp(const char[] entClass, const char[] model, const float pos[3], const float ang[3]) { + int entity = StartPropCreate(entClass, model, pos, ang); + if(DispatchSpawn(entity)) { + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn prop %.1f %.1f %.1f model %s", pos[0], pos[1], pos[2], model[7]); + #endif + return entity; + } + return -1; +} + +stock int CreateDummy(const char[] model, const float pos[3], const float ang[3]) { + int entity = StartPropCreate("commentary_dummy", model, pos, ang); + if(entity == -1) return -1; + DispatchKeyValue(entity, "LookAtPlayers", "Yes"); + DispatchKeyValue(entity, "StartingWeapons", "weapon_rifle_ak47"); + DispatchKeyValue(entity, "StartingAnim", "hit_by_tankpunch"); //idle_calm_rifle + DispatchKeyValueFloat(entity, "LookAtPlayers", 40.0); + DispatchSpawn(entity); + SetVariantString("idle_unabletoreachtarget_01a"); + AcceptEntityInput(entity, "SetAnimation"); + return entity; +} + +// Taken from silver's https://forums.alliedmods.net/showthread.php?p=1658873 +stock int CreateDynamicLight(float vOrigin[3], float vAngles[3], int color, float brightness, int style = 0) { + int entity = CreateEntityByName("light_dynamic"); + if( entity == -1) + return -1; + + DispatchKeyValue(entity, "_light", "0 0 0 255"); + DispatchKeyValue(entity, "brightness", "1"); + DispatchKeyValueFloat(entity, "spotlight_radius", 32.0); + DispatchKeyValueFloat(entity, "distance", brightness); + DispatchKeyValue(entity, "targetname", "hslamp"); + DispatchKeyValueFloat(entity, "style", float(style)); + SetEntProp(entity, Prop_Send, "m_clrRender", color); + if(DispatchSpawn(entity)) { + TeleportEntity(entity, vOrigin, vAngles, NULL_VECTOR); + AcceptEntityInput(entity, "TurnOn"); + #if defined DEBUG_LOG_MAPSTART + PrintToServer("spawn dynamic light %.1f %.1f %.1f", vOrigin[0], vOrigin[1], vOrigin[2]); + #endif + return entity; + } + return -1; +} + + +stock void CheatCommand(int client, const char[] command, const char[] argument1) { + int userFlags = GetUserFlagBits(client); + SetUserFlagBits(client, ADMFLAG_ROOT); + int flags = GetCommandFlags(command); + SetCommandFlags(command, flags & ~FCVAR_CHEAT); + FakeClientCommand(client, "%s %s", command, argument1); + SetCommandFlags(command, flags); + SetUserFlagBits(client, userFlags); +} + +stock void EntFire(const char[] name, const char[] input) { + static char targetname[64]; + static char cmd[32]; + #if defined DEBUG_LOG_MAPSTART + PrintToServer("[H&S] EntFire: %s \"%s\"", name, input); + #endif + int len = SplitString(input, " ", cmd, sizeof(cmd)); + if(len > -1) SetVariantString(input[len]); + for(int i = MaxClients + 1; i <= 4096; i++) { + if(IsValidEntity(i) && (IsValidEdict(i) || EntIndexToEntRef(i) != -1)) { + GetEntPropString(i, Prop_Data, "m_iName", targetname, sizeof(targetname)); + if(StrEqual(targetname, name, false)) { + if(len > -1) AcceptEntityInput(i, cmd); + else AcceptEntityInput(i, input); + } /*else { + GetEntityClassname(targetname, sizeof(targetname)); + if(StrEqual(targetname, name, false)) { + if(len > -1) AcceptEntityInput(i, cmd); + else AcceptEntityInput(i, input); + } + }*/ + } + } +} + +void SetupEntities(bool blockers = true, bool props = true, bool portals = true) { + if(mapConfig.entities != null) { + PrintToServer("[H&S] Deploying %d custom entities (Set: %s) (blockers:%b props:%b portals:%b)", mapConfig.entities.Length, currentSet, blockers, props, portals); + for(int i = 0; i < mapConfig.entities.Length; i++) { + EntityConfig config; + mapConfig.entities.GetArray(i, config); + + if(config.model[0] != '\0') PrecacheModel(config.model); + + if(StrEqual(config.type, "env_physics_blocker")) { + if(blockers && CreateEnvBlockerScaled(config.type, config.origin, config.scale, isNavBlockersEnabled) == -1) { + PrintToServer("[H&S:WARN] Failed to spawn blocker [type=%s] at (%.1f,%.1f, %.1f)", config.type, config.origin[0], config.origin[1], config.origin[2]); + } + } else if(StrEqual(config.type, "_relportal")) { + if(portals && CreatePortal(Portal_Relative, config.model, config.origin, config.offset, config.scale) == -1) { + PrintToServer("[H&S:WARN] Failed to spawn rel portal at (%.1f,%.1f, %.1f)", config.origin[0], config.origin[1], config.origin[2]); + } + } else if(StrEqual(config.type, "_portal")) { + if(portals && CreatePortal(Portal_Teleport, config.model, config.origin, config.offset, config.scale) == -1) { + PrintToServer("[H&S:WARN] Failed to spawn portal at (%.1f,%.1f, %.1f)", config.origin[0], config.origin[1], config.origin[2]); + } + } else if(StrEqual(config.type, "_lantern")) { + int parent = CreateProp("prop_dynamic", config.model, config.origin, config.rotation); + if(parent == -1) { + PrintToServer("[H&S:WARN] Failed to spawn prop [type=%s] [model=%s] at (%.1f,%.1f, %.1f)", config.type, config.model, config.origin[0], config.origin[1], config.origin[2]); + } else { + float pos[3]; + pos = config.origin; + pos[2] += 15.0; + int child = CreateDynamicLight(pos, config.rotation, GetColorInt(255, 255, 242), 80.0, 11); + if(child == -1) { + PrintToServer("[H&S] Failed to spawn light source for _lantern"); + } else { + SetParent(child, parent); + TeleportEntity(parent, config.origin, NULL_VECTOR, NULL_VECTOR); + } + } + } else if(StrEqual(config.type, "_dummy")) { + if(CreateDummy(config.model, config.origin, config.rotation) == -1) { + PrintToServer("[H&S:WARN] Failed to spawn dummy [model=%s] at (%.1f,%.1f, %.1f)", config.model, config.origin[0], config.origin[1], config.origin[2]); + } + }else if(props) { + if(CreateProp(config.type, config.model, config.origin, config.rotation) == -1) { + PrintToServer("[H&S:WARN] Failed to spawn prop [type=%s] [model=%s] at (%.1f,%.1f, %.1f)", config.type, config.model, config.origin[0], config.origin[1], config.origin[2]); + } + } + } + + static char key[64]; + static char value[64]; + if(mapConfig.inputs != null) { + for(int i = 0; i < mapConfig.inputs.Length - 1; i += 2) { + mapConfig.inputs.GetString(i, key, sizeof(key)); + mapConfig.inputs.GetString(i + 1, value, sizeof(value)); + EntFire(key, value); + } + } + } +} + diff --git a/scripting/include/hideandseek/hsgame.inc b/scripting/include/hideandseek/hsgame.inc new file mode 100644 index 0000000..0f8f906 --- /dev/null +++ b/scripting/include/hideandseek/hsgame.inc @@ -0,0 +1,299 @@ +static int mapChangeMsgTicks = 5; + + +int GetColorInt(int r, int g, int b) { + int color = r; + color += 256 * g; + color += 65536 * b; + return color; +} + +void Cleanup() { + EntFire("hsprop", "kill"); + EntFire("hsblocker", "kill"); + EntFire("hsportal", "kill"); + if(seekerCam != INVALID_ENT_REFERENCE && IsValidEntity(seekerCam)) { + AcceptEntityInput(seekerCam, "Disable"); + AcceptEntityInput(seekerCam, "Kill"); + seekerCam = INVALID_ENT_REFERENCE; + } +} + +GameState GetState() { + if(!isEnabled) return State_Unknown; + static char buffer[4]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentStage", buffer, sizeof(buffer)); + int stage = -1; + if(StringToIntEx(buffer, stage) > 0) { + return view_as(stage); + } else { + return State_Unknown; + } +} + +int GetSlasher() { + if(!isEnabled) return -1; + static char buffer[8]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentSlasher && \"GetPlayerUserId\" in g_ModeScript.MutationState.CurrentSlasher ? g_ModeScript.MutationState.CurrentSlasher.GetPlayerUserId() : -1", buffer, sizeof(buffer)); + int uid = StringToInt(buffer); + if(uid > 0) { + return GetClientOfUserId(uid); + } else { + PrintToServer("[H&S] Could not find real slasher, falling back to manual check"); + return FindSlasher(); + } +} + +int FindSlasher() { + char buf[16]; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i)) { + int entity = GetPlayerWeaponSlot(i, 1); + if(entity > -1 && GetEntityClassname(entity, buf, sizeof(buf)) && StrEqual(buf, "melee")) { + GetEntPropString(entity, Prop_Data, "m_strMapSetScriptName", buf, sizeof(buf)); + if(StrEqual(buf, "fireaxe")) { + return i; + } + } + } + } + return -1; +} + +void SetSlasher(int client) { + GameState state = GetState(); + char buf[128]; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && i != client) { + for(int s = 0; s < 6; s++) { + int ent = GetPlayerWeaponSlot(i, s); + if(ent > 0) AcceptEntityInput(ent, "Kill"); + } + if(state == State_Hunting) + CheatCommand(i, "give", "pistol_magnum"); + else + CheatCommand(i, "give", "knife"); + } + } + Format(buf, sizeof(buf), "g_ModeScript.MutationState.CurrentSlasher = GetPlayerFromUserID(%d); g_ModeScript.GiveSeekerItem(GetPlayerFromUserID(%d))", GetClientUserId(client), GetClientUserId(client)); + L4D2_ExecVScriptCode(buf); + currentSeeker = client; + // CheatCommand(client, "give", "fireaxe"); + CheatCommand(client, "give", "adrenaline"); +} + +int GetTick() { + if(!isEnabled) return -1; + static char buffer[4]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.StateTick", buffer, sizeof(buffer)); + int value = -1; + if(StringToIntEx(buffer, value) > 0) { + return value; + } else { + return -1; + } +} + +void SetTick(int tick) { + static char buf[64]; + Format(buf, sizeof(buf), "g_ModeScript.MutationState.StateTick = %d", tick); + L4D2_ExecVScriptCode(buf); +} + + +int GetMapTime() { + static char mapTime[16]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.MapTime", mapTime, sizeof(mapTime)); + return StringToInt(mapTime); +} + +void SetMapTime(int seconds) { + static char buf[64]; + Format(buf, sizeof(buf), "g_ModeScript.MutationState.MapTime = %d", seconds); + L4D2_ExecVScriptCode(buf); +} + +Action Timer_ChangeMap(Handle h) { + PrintToChatAll("Changing map to %s in %d seconds", nextRoundMap, mapChangeMsgTicks); + if(mapChangeMsgTicks-- == 0) { + ForceChangeLevel(nextRoundMap, "H&SMapSelect"); + nextRoundMap[0] = '\0'; + return Plugin_Stop; + } + return Plugin_Continue; +} + +void ChangeMap(const char map[64], int time = 5) { + strcopy(nextRoundMap, sizeof(nextRoundMap), map); + mapChangeMsgTicks = time; + CreateTimer(1.0, Timer_ChangeMap, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); +} + +bool GetSpawnPosition(float pos[3], bool includePlayers = true) { + if(includePlayers) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { + GetClientAbsOrigin(i, pos); + return true; + } + } + } + int entity = INVALID_ENT_REFERENCE; + while ((entity = FindEntityByClassname(entity, "info_player_start")) != INVALID_ENT_REFERENCE) { + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos); + return true; + } + return false; +} + +bool SetState(GameState state) { + if(!isEnabled) return false; + static char buffer[64]; + Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.CurrentStage = %d", view_as(state)); + return L4D2_ExecVScriptCode(buffer); +} + +bool IsGameSoloOrPlayersLoading() { + int connecting, ingame; + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i)) { + if(IsClientInGame(i)) + ingame++; + else + connecting++; + } + } + return connecting > 0 || ingame == 1; +} + +//cm_NoSurvivorBots +bool SetBotsEnabled(bool value) { + static char buffer[64]; + if(value) + Format(buffer, sizeof(buffer), "g_ModeScript.MutationOptions.cm_NoSurvivorBots = true"); + else + Format(buffer, sizeof(buffer), "g_ModeScript.MutationOptions.cm_NoSurvivorBots = false"); + return L4D2_ExecVScriptCode(buffer); +} + +bool IsBotsEnabled() { + static char result[8]; + L4D2_GetVScriptOutput("g_ModeScript.MutationState.cm_NoSurvivorBots", result, sizeof(result)); + return StrEqual(result, "true", false); +} + +stock void GetHorizontalPositionFromClient(int client, float units, float finalPosition[3]) { + float pos[3], ang[3]; + GetClientEyeAngles(client, ang); + GetClientAbsOrigin(client, pos); + + float theta = DegToRad(ang[1]); + pos[0] += units * Cosine(theta); + pos[1] += units * Sine(theta); + finalPosition = pos; +} + +void SetParent(int child, int parent) { + SetVariantString("!activator"); + AcceptEntityInput(child, "SetParent", parent); +} + +void SetPeekCamTarget(int target, int src = 0) { + if(seekerCam == INVALID_ENT_REFERENCE || !IsValidEntity(seekerCam)) { + seekerCam = CreateEntityByName("point_viewcontrol_survivor"); + DispatchKeyValue(seekerCam, "targetname", "hscam"); + DispatchSpawn(seekerCam); + for(int i = 0; i <= MaxClients; i++) { + isViewingCam[i] = false; + } + PrintToServer("created new peek cam %d", seekerCam); + } + + AcceptEntityInput(seekerCam, "ClearParent"); + + float pos[3], endPos[3], ang[3]; + GetClientEyePosition(target, pos); + GetClientEyeAngles(target, ang); + if(src) { + pos[2] += 20.0; + TeleportEntity(seekerCam, pos, ang, NULL_VECTOR); + // SetParent(seekerCam, src); + } else { + /*GetHorizontalPositionFromClient(target, -20.0, endPos); + endPos[2] += 80.0; + TeleportEntity(seekerCam, endPos, ang, NULL_VECTOR); + SetParent(seekerCam, target);*/ + + TR_TraceRayFilter(pos, ang, CONTENTS_PLAYERCLIP | MASK_SOLID | MASK_VISIBLE, RayType_Infinite, Filter_IgnoreAll); + if(TR_DidHit()) { + TR_GetEndPosition(endPos); + } + endPos[2] += 50.0; + + ang[0] = 0.0; + float deltaA = endPos[0] - pos[0]; + float deltaB = endPos[1] - pos[1]; + float deltaC = endPos[2] - pos[2]; + ang[0] = RadToDeg(ArcTangent(deltaC / GetVectorDistance(endPos, pos, false) )); + ang[1] = RadToDeg(ArcTangent2(deltaA, deltaB)); + + // pos[2] += 50.0; + // GetAnglesLookAt(seekerCam, target, ang); + TeleportEntity(seekerCam, endPos, ang, NULL_VECTOR); + // SetParent(seekerCam, target);*/ + } +} + +stock void GetAnglesLookAt(int iClient, int iTarget, float fFinalPos[3]) { + static float fTargetPos[3]; + static float fTargetAngles[3]; + static float fClientPos[3]; + + GetEntPropVector(iClient, Prop_Send, "m_vecOrigin", fClientPos); + GetClientEyePosition(iTarget, fTargetPos); + GetClientEyeAngles(iTarget, fTargetAngles); + + float fVecFinal[3]; + AddInFrontOf(fTargetPos, fTargetAngles, 7.0, fVecFinal); + MakeVectorFromPoints(fClientPos, fVecFinal, fFinalPos); + + GetVectorAngles(fFinalPos, fFinalPos); + + // TeleportEntity(iClient, NULL_VECTOR, fFinalPos, NULL_VECTOR); +} +stock void AddInFrontOf(float fVecOrigin[3], float fVecAngle[3], float fUnits, float fOutPut[3]) +{ + float fVecView[3]; GetViewVector(fVecAngle, fVecView); + + fOutPut[0] = fVecView[0] * fUnits + fVecOrigin[0]; + fOutPut[1] = fVecView[1] * fUnits + fVecOrigin[1]; + fOutPut[2] = fVecView[2] * fUnits + fVecOrigin[2]; +} +stock void GetViewVector(float fVecAngle[3], float fOutPut[3]) +{ + fOutPut[0] = Cosine(fVecAngle[1] / (180 / FLOAT_PI)); + fOutPut[1] = Sine(fVecAngle[1] / (180 / FLOAT_PI)); + fOutPut[2] = -Sine(fVecAngle[0] / (180 / FLOAT_PI)); +} + + +bool Filter_IgnoreAll(int entity, int mask) { + return false; +} + +bool IsPeekCamActive(int client) { + return isViewingCam[client]; +} + +// int GetClientsInRange(const float origin[3], ClientRangeType rangeType, int[] clients, int size) +void SetPeekCamActive(int client, bool active) { + if(seekerCam != INVALID_ENT_REFERENCE) { + if(active) + AcceptEntityInput(seekerCam, "Enable", client); + else + AcceptEntityInput(seekerCam, "Disable", client); + } else { + PrintToServer("warn: SetPeekCamActive(%d, %b) when seekerCam invalid", client, active); + } + isViewingCam[client] = active; +} \ No newline at end of file diff --git a/scripting/l4d2_hideandseek.sp b/scripting/l4d2_hideandseek.sp index 2d77327..c64aab3 100644 --- a/scripting/l4d2_hideandseek.sp +++ b/scripting/l4d2_hideandseek.sp @@ -16,7 +16,6 @@ #include int g_iLaserIndex; #endif -//#include public Plugin myinfo = { @@ -54,32 +53,26 @@ enum GameState { State_Hunting } -static char gamemode[32], currentMap[64]; -static bool isEnabled, lateLoaded; +char gamemode[32], currentMap[64]; +bool isEnabled, lateLoaded; -static bool isPendingPlay[MAXPLAYERS+1]; -static bool isNavBlockersEnabled = true, isPropsEnabled = true, isPortalsEnabled = true; -static bool isNearbyPlaying[MAXPLAYERS+1]; -static bool wasThirdPersonVomitted[MAXPLAYERS+1]; -static bool gameOver; -static int currentSeeker; -static int currentPlayers = 0; +bool isPendingPlay[MAXPLAYERS+1]; +bool isNavBlockersEnabled = true, isPropsEnabled = true, isPortalsEnabled = true; +bool isNearbyPlaying[MAXPLAYERS+1]; +bool wasThirdPersonVomitted[MAXPLAYERS+1]; +bool gameOver; +int currentSeeker; +int currentPlayers = 0; -static const float DEFAULT_SCALE[3] = { 5.0, 5.0, 5.0 }; +float DEFAULT_SCALE[3] = { 5.0, 5.0, 5.0 }; -static ArrayList validMaps; -static ArrayList validSets; +char currentSet[16] = "default"; -static KeyValues kv; -static StringMap mapConfigs; -static char currentSet[16] = "default"; +Handle suspenseTimer, thirdPersonTimer; -static Handle suspenseTimer, thirdPersonTimer; - -static char nextRoundMap[64]; -static int mapChangeMsgTicks = 5; - -// TODO: Disable weapon drop +char nextRoundMap[64]; +int seekerCam = INVALID_ENT_REFERENCE; +bool isViewingCam[MAXPLAYERS+1]; enum struct EntityConfig { float origin[3]; @@ -96,9 +89,15 @@ enum struct MapConfig { float spawnpoint[3]; bool hasSpawnpoint; int mapTime; + bool canClimb; + bool pressButtons; } -static MapConfig mapConfig; +MapConfig mapConfig; +ArrayList validMaps; +ArrayList validSets; + +#include public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { lateLoaded = late; @@ -124,451 +123,13 @@ public void OnPluginStart() { hGamemode.AddChangeHook(Event_GamemodeChange); Event_GamemodeChange(hGamemode, gamemode, gamemode); - if(lateLoaded) { - int seeker = GetSlasher(); - if(seeker > -1) { - currentSeeker = seeker; - PrintToServer("[H&S] Late load, found seeker %N", currentSeeker); - } - if(IsGameSoloOrPlayersLoading()) { - Handle timer = CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); - TriggerTimer(timer); - PrintToServer("[H&S] Late load, player(s) are connecting, or solo. Waiting..."); - SetState(State_Startup); - } - } - RegConsoleCmd("sm_joingame", Command_Join, "Joins or joins someone else"); RegAdminCmd("sm_hs", Command_HideAndSeek, ADMFLAG_CHEATS, "The main command. see /hs help"); } -bool ReloadMapDB() { - if(kv != null) { - delete kv; - } - kv = new KeyValues("hideandseek"); - - char sPath[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, sPath, sizeof(sPath), "data/hideandseek.cfg"); - - - if(!FileExists(sPath) || !kv.ImportFromFile(sPath)) { - delete kv; - return false; - } - - validMaps.Clear(); - - char map[64]; - kv.GotoFirstSubKey(true); - do { - kv.GetSectionName(map, sizeof(map)); - validMaps.PushString(map); - } while(kv.GotoNextKey(true)); - kv.GoBack(); - return true; -} - -public Action Command_HideAndSeek(int client, int args) { - if(args > 0) { - char subcmd[16]; - GetCmdArg(1, subcmd, sizeof(subcmd)); - if(StrEqual(subcmd, "r") || StrEqual(subcmd, "reload", false)) { - GetCurrentMap(currentMap, sizeof(currentMap)); - char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - if(ReloadMapDB()) { - if(!LoadConfigForMap(currentMap)) { - ReplyToCommand(client, "Warn: Map has no config file"); - } - Cleanup(); - if(arg[0] == 'f') { - CreateTimer(0.1, Timer_RoundStart); - } - SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); - ReplyToCommand(client, "Reloaded map from config"); - } else { - ReplyToCommand(client, "Error occurred while reloading map file"); - } - } else if(StrEqual(subcmd, "set", false)) { - if(args == 1) { - ReplyToCommand(client, "Current Map Set: \"%s\" (Specify with /hs set )", currentSet); - if(validSets.Length == 0) ReplyToCommand(client, "Available Sets: (no map config found)"); - else { - ReplyToCommand(client, "Available Sets: "); - char set[16]; - for(int i = 0; i < validSets.Length; i++) { - validSets.GetString(i, set, sizeof(set)); - ReplyToCommand(client, "%d. %s", i + 1, set); - } - } - } else { - GetCmdArg(2, currentSet, sizeof(currentSet)); - if(!LoadConfigForMap(currentMap)) { - ReplyToCommand(client, "Warn: Map has no config file"); - } - Cleanup(); - SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); - ReplyToCommand(client, "Set the current set to \"%s\"", currentSet); - } - } else if(StrEqual(subcmd, "toggle")) { - char type[32]; - GetCmdArg(2, type, sizeof(type)); - bool doAll = StrEqual(type, "all"); - bool isUnknown = true; - - if(doAll || StrEqual(type, "blockers", false)) { - if(isNavBlockersEnabled) { - EntFire("hsblocker", "Disable"); - ReplyToCommand(client, "Disabled all custom gamemode blockers"); - } else { - EntFire("hsblocker", "Enable"); - ReplyToCommand(client, "Enabled all custom gamemode blockers"); - } - isNavBlockersEnabled = !isNavBlockersEnabled; - isUnknown = false; - } - if(doAll || StrEqual(type, "props", false)) { - if(isPropsEnabled) { - EntFire("hsprop", "Disable"); - EntFire("hsprop", "DisableCollision"); - ReplyToCommand(client, "Disabled all custom gamemode props"); - } else { - EntFire("hsprop", "Enable"); - EntFire("hsprop", "EnableCollision"); - ReplyToCommand(client, "Enabled all custom gamemode props"); - } - isPropsEnabled = !isPropsEnabled; - isUnknown = false; - } - if(doAll || StrEqual(type, "portals", false)) { - if(isPortalsEnabled) { - EntFire("hsportal", "Disable"); - ReplyToCommand(client, "Disabled all custom gamemode portals"); - } else { - EntFire("hsportal", "Enable"); - ReplyToCommand(client, "Enabled all custom gamemode portals"); - } - isPortalsEnabled = !isPortalsEnabled; - isUnknown = false; - } - if(isUnknown) ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); - } else if(StrEqual(subcmd, "clear", false)) { - static char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - bool doAll = StrEqual(arg, "all"); - bool isUnknown = true; - - if(doAll || StrEqual(arg, "props")) { - EntFire("hsprop", "kill"); - ReplyToCommand(client, "Removed all custom gamemode props"); - isUnknown = false; - } - if(doAll || StrEqual(arg, "blockers")) { - EntFire("hsblocker", "kill"); - ReplyToCommand(client, "Removed all custom gamemode blockers"); - isUnknown = false; - } - if(doAll || StrEqual(arg, "portals")) { - EntFire("hsportal", "kill"); - ReplyToCommand(client, "Removed all custom gamemode portals"); - isUnknown = false; - } - if(isUnknown) ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); - } else if(StrEqual(subcmd, "settime")) { - int prev = GetMapTime(); - static char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - int time = StringToInt(arg); - mapConfig.mapTime = time; - SetMapTime(time); - ReplyToCommand(client, "Map's time is temporarily set to %d seconds (was %d)", time, prev); - } else if(StrEqual(subcmd, "settick")) { - static char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - int tick = -StringToInt(arg); - SetTick(tick); - ReplyToCommand(client, "Set tick time to %d", tick); - } else if(StrEqual(subcmd, "map")) { - static char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - if(StrEqual(arg, "list")) { - ReplyToCommand(client, "See the console for available maps"); - char map[64]; - for(int i = 0; i < validMaps.Length; i++) { - validMaps.GetString(i, map, sizeof(map)); - PrintToConsole(client, "%d. %s", i + 1, map); - } - } else if(StrEqual(arg, "random")) { - bool foundMap; - char map[64]; - do { - int mapIndex = GetURandomInt() % validMaps.Length; - validMaps.GetString(mapIndex, map, sizeof(map)); - if(!StrEqual(currentMap, map, false)) { - foundMap = true; - } - } while(!foundMap); - PrintToChatAll("[H&S] Switching map to %s", map); - ChangeMap(map); - } else if(StrEqual(arg, "next", false)) { - if(args == 1) { - ReplyToCommand(client, "Specify the map to change on the next round: 'next '"); - } else { - char arg2[64]; - GetCmdArg(2, arg2, sizeof(arg2)); - if(IsMapValid(arg2)) { - strcopy(nextRoundMap, sizeof(nextRoundMap), arg2); - PrintToChatAll("[H&S] Switching map next round to %s", arg2); - ForceChangeLevel(arg, "SetMapSelect"); - } else { - ReplyToCommand(client, "Map is not valid"); - } - } - } else if(StrEqual(arg, "force", false)) { - if(args == 1) { - ReplyToCommand(client, "Specify the map to change to: 'force '"); - } else { - char arg2[64]; - GetCmdArg(2, arg2, sizeof(arg2)); - if(IsMapValid(arg2)) { - PrintToChatAll("[H&S] Switching map to %s", arg2); - ChangeMap(arg2); - } else { - ReplyToCommand(client, "Map is not valid"); - } - } - } else { - ReplyToCommand(client, "Syntax: 'map /next >"); - } - return Plugin_Handled; - } else if(StrEqual(subcmd, "pos", false)) { - float pos[3]; - GetAbsOrigin(client, pos); - ReplyToCommand(client, "\"origin\" \"%f %f %f\"", pos[0], pos[1], pos[2]); - GetClientEyeAngles(client, pos); - ReplyToCommand(client, "\"rotation\" \"%f %f %f\"", pos[0], pos[1], pos[2]); - } else if(StrEqual(subcmd, "prop", false)) { - float pos[3]; - GetAbsOrigin(client, pos); - ReplyToCommand(client, "\"MYPROP\""); - ReplyToCommand(client, "{"); - ReplyToCommand(client, "\t\"origin\" \"%f %f %f\"", pos[0], pos[1], pos[2]); - GetClientAbsAngles(client, pos); - ReplyToCommand(client, "\t\"rotation\" \"%f %f %f\"", pos[0], pos[1], pos[2]); - ReplyToCommand(client, "\t\"type\" \"prop_dynamic\""); - ReplyToCommand(client, "\t\"model\" \"props_junk/dumpster_2.mdl\""); - ReplyToCommand(client, "}"); - } else if(StrEqual(subcmd, "setspawn", false)) { - GetClientAbsOrigin(client, mapConfig.spawnpoint); - ReplyToCommand(client, "Set map's temporarily spawnpoint to your location."); - } else if(StrEqual(subcmd, "stuck")) { - TeleportEntity(client, mapConfig.spawnpoint, NULL_VECTOR, NULL_VECTOR); - } else if(StrEqual(subcmd, "bots")) { - if(args == 2) { - char arg[16]; - GetCmdArg(2, arg, sizeof(arg)); - if(StrEqual(arg, "toggle")) { - bool newValue = !IsBotsEnabled(); - SetBotsEnabled(newValue); - if(newValue) ReplyToCommand(client, "Bots are now enabled"); - else ReplyToCommand(client, "Bots are now disabled"); - return Plugin_Handled; - } else if(StrEqual(arg, "on") || StrEqual(arg, "true")) { - SetBotsEnabled(true); - ReplyToCommand(client, "Bots are now enabled"); - return Plugin_Handled; - } else if(StrEqual(arg, "off") || StrEqual(arg, "false")) { - SetBotsEnabled(false); - ReplyToCommand(client, "Bots are now disabled"); - return Plugin_Handled; - } - } - if(IsBotsEnabled()) ReplyToCommand(client, "Bots are enabled"); - else ReplyToCommand(client, "Bots are disabled"); - } - return Plugin_Handled; - } - ReplyToCommand(client, " - Hide & Seek Commands -"); - ReplyToCommand(client, "toggle : Toggles all specified"); - ReplyToCommand(client, "set [new set]: Change the prop set or view current"); - ReplyToCommand(client, "clear : Clear all specified"); - ReplyToCommand(client, "settime [seconds]: Sets the time override for the map"); - ReplyToCommand(client, "settick [tick]: Sets the current tick timer value"); - ReplyToCommand(client, "setspawn: Sets the temporary spawnpoint for the map"); - ReplyToCommand(client, "stuck: Teleports you to spawn to unstuck yourself"); - ReplyToCommand(client, "bots [toggle, [value]]: View if bots are enabled, or turn them on"); - return Plugin_Handled; -} - - -public Action OnClientSayCommand(int client, const char[] command, const char[] sArgs) { - if(isEnabled) { - if(!StrEqual(command, "say")) { //Is team message - if(currentSeeker <= 0 || currentSeeker == client) { - return Plugin_Continue; - } - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) - PrintToChat(i, "[Hiders] %N: %s", client, sArgs); - } - return Plugin_Handled; - } - } - return Plugin_Continue; -} - -bool LoadConfigForMap(const char[] map) { - kv.Rewind(); - if (kv.JumpToKey(map)) { - MapConfig config; - config.entities = new ArrayList(sizeof(EntityConfig)); - config.inputs = new ArrayList(ByteCountToCells(64)); - validSets.Clear(); - if(kv.JumpToKey("ents")) { - kv.GotoFirstSubKey(); - char entSet[16]; - do { - EntityConfig entCfg; - kv.GetVector("origin", entCfg.origin, NULL_VECTOR); - kv.GetVector("rotation", entCfg.rotation, NULL_VECTOR); - kv.GetString("type", entCfg.type, sizeof(entCfg.type), "env_physics_blocker"); - kv.GetString("model", entCfg.model, sizeof(entCfg.model), ""); - if(entCfg.model[0] != '\0') - Format(entCfg.model, sizeof(entCfg.model), "models/%s", entCfg.model); - kv.GetVector("scale", entCfg.scale, DEFAULT_SCALE); - kv.GetVector("offset", entCfg.offset, NULL_VECTOR); - kv.GetString("set", entSet, sizeof(entSet), "default"); - if(validSets.FindString(entSet) == -1) { - validSets.PushString(entSet); - } - if(StrEqual(currentSet, entSet, false)) { - config.entities.PushArray(entCfg); - } - } while (kv.GotoNextKey()); - // JumpToKey and GotoFirstSubKey both traverse, i guess, go back - kv.GoBack(); - kv.GoBack(); - } - if(kv.JumpToKey("inputs")) { - // Use 'false' to propery grab - // "key" "value" in a section - kv.GotoFirstSubKey(false); - static char buffer[64]; - do { - kv.GetSectionName(buffer, sizeof(buffer)); - config.inputs.PushString(buffer); - - kv.GetString(NULL_STRING, buffer, sizeof(buffer)); - config.inputs.PushString(buffer); - } while (kv.GotoNextKey(false)); - kv.GoBack(); - } - int mapTime; - - config.hasSpawnpoint = false; - if(kv.JumpToKey("sets")) { - char set[16]; - kv.GotoFirstSubKey(true); - do { - kv.GetSectionName(set, sizeof(set)); - if(validSets.FindString(set) == -1) { - validSets.PushString(set); - } - if(StrEqual(currentSet, set, false)) { - kv.GetVector("spawnpoint", config.spawnpoint); - if(config.spawnpoint[0] != 0.0 && config.spawnpoint[1] != 0.0 && config.spawnpoint[2] != 0.0) { - PrintToServer("[H&S] Using provided custom spawnpoint for set %s at %0.1f, %0.1f, %0.1f", currentSet, config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); - config.hasSpawnpoint = true; - } - mapTime = kv.GetNum("maptime", 0); - } - } while(kv.GotoNextKey(true)); - kv.GoBack(); - } - - if(!config.hasSpawnpoint) { - kv.GetVector("spawnpoint", config.spawnpoint); - if(config.spawnpoint[0] != 0.0 && config.spawnpoint[1] != 0.0 && config.spawnpoint[2] != 0.0) { - PrintToServer("[H&S] Using provided custom spawnpoint at %0.1f, %0.1f, %0.1f", config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); - config.hasSpawnpoint = true; - } else if (GetSpawnPosition(config.spawnpoint, false)) { - PrintToServer("[H&S] Using map spawnpoint at %0.1f, %0.1f, %0.1f", config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]); - config.hasSpawnpoint = true; - } else { - PrintToServer("[H&S] Could not find any spawnpoints, using default spawn"); - config.hasSpawnpoint = false; - } - } - - // Use default maptime if exists - if(mapTime == 0) - mapTime = kv.GetNum("maptime", 0); - if(mapTime > 0) { - config.mapTime = mapTime; - PrintToServer("[H&S] Map time overwritten to %d seconds", mapTime); - } - mapConfigs.SetArray(map, config, sizeof(MapConfig)); - // Discard entInputs if unused - if(config.inputs.Length == 0) { - delete config.inputs; - } - mapConfig = config; - return true; - } else { - mapConfig.hasSpawnpoint = false; - PrintToServer("[H&S] No map config exists for %s", map); - return false; - } -} - -public Action Command_Join(int client, int args) { - static float tpLoc[3]; - GetSpawnPosition(tpLoc); - if(args == 1) { - static char arg1[32]; - GetCmdArg(1, arg1, sizeof(arg1)); - char target_name[MAX_TARGET_LENGTH]; - int target_list[MAXPLAYERS], target_count; - bool tn_is_ml; - if ((target_count = ProcessTargetString( - arg1, - client, - target_list, - MAXPLAYERS, - 0, - target_name, - sizeof(target_name), - tn_is_ml)) <= 0) - { - /* This function replies to the admin with a failure message */ - ReplyToTargetError(client, target_count); - return Plugin_Handled; - } - for (int i = 0; i < target_count; i++) { - int target = target_list[i]; - if(GetClientTeam(target) != 2) { - ChangeClientTeam(target, 2); - L4D_RespawnPlayer(target); - TeleportEntity(target, tpLoc, NULL_VECTOR, NULL_VECTOR); - isPendingPlay[client] = false; - CheatCommand(target, "give", "knife"); - } - } - ReplyToCommand(client, "Joined %s", target_name); - } else { - if(currentSeeker == client) { - ReplyToCommand(client, "You are already in-game as a seeker."); - return Plugin_Handled; - } - isPendingPlay[client] = false; - ChangeClientTeam(client, 2); - L4D_RespawnPlayer(client); - TeleportEntity(client, tpLoc, NULL_VECTOR, NULL_VECTOR); - CheatCommand(client, "give", "knife"); - } - return Plugin_Handled; +public void OnPluginEnd() { + Cleanup(); } public void OnClientConnected(int client) { @@ -598,8 +159,10 @@ public void OnClientDisconnect(int client) { public void OnMapStart() { if(!isEnabled) return; + seekerCam = INVALID_ENT_REFERENCE; currentSeeker = 0; + char map[64]; GetCurrentMap(map, sizeof(map)); @@ -610,6 +173,12 @@ public void OnMapStart() { LoadConfigForMap(map); } strcopy(currentSet, sizeof(currentSet), "default"); + if(IsGameSoloOrPlayersLoading()) { + Handle timer = CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); + TriggerTimer(timer); + PrintToServer("[H&S] Player(s) are connecting, or solo. Waiting..."); + SetState(State_Startup); + } } #if defined DEBUG_BLOCKERS @@ -623,6 +192,18 @@ public void OnMapStart() { if(lateLoaded) { lateLoaded = false; SetupEntities(); + int seeker = GetSlasher(); + if(seeker > -1) { + currentSeeker = seeker; + PrintToServer("[H&S] Late load, found seeker %N", currentSeeker); + } + SetPeekCamTarget(currentSeeker); + if(IsGameSoloOrPlayersLoading()) { + Handle timer = CreateTimer(10.0, Timer_KeepWaiting, _, TIMER_REPEAT); + TriggerTimer(timer); + PrintToServer("[H&S] Late load, player(s) are connecting, or solo. Waiting..."); + SetState(State_Startup); + } } } @@ -645,7 +226,8 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ #endif if(isEnabled) { HookEvent("round_end", Event_RoundEnd); - HookEvent("round_start_post_nav", Event_RoundStart); + HookEvent("round_start", Event_RoundStart); + HookEvent("player_spawn", Event_PlayerSpawn); HookEvent("item_pickup", Event_ItemPickup); HookEvent("player_death", Event_PlayerDeath); SetupEntities(); @@ -667,20 +249,43 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ UnhookEvent("round_start", Event_RoundStart); UnhookEvent("item_pickup", Event_ItemPickup); UnhookEvent("player_death", Event_PlayerDeath); + UnhookEvent("player_spawn", Event_PlayerSpawn); Cleanup(); delete suspenseTimer; delete thirdPersonTimer; } } +const float DEATH_CAM_MIN_DIST = 150.0; +public Action Timer_StopPeekCam(Handle h) { + PrintToServer("clearing cam"); + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i)) { + SetPeekCamActive(i, false); + } + } +} public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); + int attacker = GetClientOfUserId(event.GetInt("attacker")); + if(!gameOver && client && GetClientTeam(client) == 2) { + SetPeekCamTarget(attacker > 0 ? attacker : client, client); + int alive = 0; + float pos[3], checkPos[3]; + GetClientAbsOrigin(attacker, pos); + SetPeekCamActive(attacker, true); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - alive++; + if(attacker > 0 && attacker != client) { + GetClientAbsOrigin(i, checkPos); + if(GetVectorDistance(checkPos, pos) > DEATH_CAM_MIN_DIST) { + SetPeekCamActive(i, true); + } + alive++; + } } } if(client == currentSeeker) { @@ -694,14 +299,17 @@ public void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast if(client != currentSeeker) { PrintToChatAll("Seeker %N won!", currentSeeker); } else { + SetPeekCamTarget(client); PrintToChatAll("Hiders win! The last survivor was %N!", client); } gameOver = true; + return; } else if(alive > 2 && client != currentSeeker) { PrintToChatAll("%d hiders remain", alive - 1); } } + CreateTimer(2.0, Timer_StopPeekCam); } } @@ -728,19 +336,27 @@ public void Event_ItemPickup(Event event, const char[] name, bool dontBroadcast) if(StrEqual(item, "fireaxe")) { gameOver = false; currentSeeker = GetSlasher(); - if(currentSeeker != client) { + if(currentSeeker <= 0) { + PrintToServer("[H&S] ERROR: GetSlasher() returned invalid seeker"); + currentSeeker = client; + } else if(currentSeeker != client) { PrintToChatAll("[H&S] Seeker does not equal axe-receiver. Possible seeker: %N", client); } - if(currentSeeker == -1) { - PrintToServer("[H&S] ERROR: GetSlasher() returned -1"); - currentSeeker = client; - } + SetPeekCamTarget(currentSeeker); PrintToChatAll("%N is the seeker", currentSeeker); } } } } +public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client > 0 && mapConfig.hasSpawnpoint) { + TeleportEntity(client, mapConfig.spawnpoint, NULL_VECTOR, NULL_VECTOR); + } + isViewingCam[client] = false; +} + public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { if(mapConfig.hasSpawnpoint) { PrintToServer("[H&S] Using provided spawnpoint: %.1f %.1f %.1f", mapConfig.spawnpoint[0], mapConfig.spawnpoint[1], mapConfig.spawnpoint[2]); @@ -753,7 +369,6 @@ public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) EntFire("relay_intro_start", "Kill"); SetupEntities(); CreateTimer(15.0, Timer_RoundStart); - } public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { @@ -763,23 +378,18 @@ public void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { return; } currentSeeker = 0; - static float tpLoc[3]; - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - GetClientAbsOrigin(i, tpLoc); - break; - } - } - for(int i = 1; i <= MaxClients; i++) { isNearbyPlaying[i] = false; - if(isPendingPlay[i]) { - if(IsClientConnected(i) && IsClientInGame(i)) { + if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 1) { + SetPeekCamActive(i, false); + if(isPendingPlay[i]) { ChangeClientTeam(i, 2); L4D_RespawnPlayer(i); - TeleportEntity(i, tpLoc, NULL_VECTOR, NULL_VECTOR); + if(mapConfig.hasSpawnpoint) { + TeleportEntity(i, mapConfig.spawnpoint, NULL_VECTOR, NULL_VECTOR); + } + isPendingPlay[i] = false; } - isPendingPlay[i] = false; } } } @@ -812,20 +422,28 @@ public void QueryClientConVarCallback(QueryCookie cookie, int client, ConVarQuer } } +GameState prevState; public Action Timer_Music(Handle h) { static float seekerLoc[3]; static float playerLoc[3]; + bool changedToHunting; if(currentSeeker > 0) { GetClientAbsOrigin(currentSeeker, seekerLoc); GameState state = GetState(); if(state == State_Hunting) { + if(prevState == State_Hiding) { + changedToHunting = true; + SetPeekCamTarget(currentSeeker); + } EmitSoundToClient(currentSeeker, SOUND_SUSPENSE_1, currentSeeker, SNDCHAN_AUTO, SNDLEVEL_NORMAL, SND_CHANGEPITCH, 0.2, 90, currentSeeker, seekerLoc, seekerLoc, true); } + prevState = state; } int playerCount; for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && i != currentSeeker) { - + if(changedToHunting) + SetPeekCamActive(i, true); playerCount++; GetClientAbsOrigin(i, playerLoc); float dist = GetVectorDistance(seekerLoc, playerLoc, true); @@ -844,15 +462,20 @@ public Action Timer_Music(Handle h) { } } } + if(changedToHunting) + CreateTimer(2.2, Timer_StopPeekCam); return Plugin_Continue; } public Action Timer_RoundStart(Handle h) { + PrintToServer("[H&S] Running round entity tweaks"); CreateTimer(0.1, Timer_CheckWeapons); CreateTimer(10.0, Timer_CheckWeapons); int entity = INVALID_ENT_REFERENCE; - while ((entity = FindEntityByClassname(entity, "func_button")) != INVALID_ENT_REFERENCE) { - AcceptEntityInput(entity, "Press"); + if(mapConfig.pressButtons) { + while ((entity = FindEntityByClassname(entity, "func_button")) != INVALID_ENT_REFERENCE) { + AcceptEntityInput(entity, "Press"); + } } entity = INVALID_ENT_REFERENCE; while ((entity = FindEntityByClassname(entity, "func_brush")) != INVALID_ENT_REFERENCE) { @@ -868,10 +491,11 @@ public Action Timer_RoundStart(Handle h) { if(GetURandomFloat() > 0.5) AcceptEntityInput(entity, "Toggle"); } - while ((entity = FindEntityByClassname(entity, "func_simpleladder")) != INVALID_ENT_REFERENCE) { - SetEntProp(entity, Prop_Send, "m_iTeamNum", 0); - } - PrintToServer("[H&S] Pressing buttons"); + if(mapConfig.canClimb) { + while ((entity = FindEntityByClassname(entity, "func_simpleladder")) != INVALID_ENT_REFERENCE) { + SetEntProp(entity, Prop_Send, "m_iTeamNum", 0); + } + } if(mapConfig.mapTime > 0) { SetMapTime(mapConfig.mapTime); } @@ -893,406 +517,3 @@ public Action Timer_CheckWeapons(Handle h) { return Plugin_Continue; } -stock int CreateEnvBlockerBox(const float pos[3], bool enabled = true) { - int entity = CreateEntityByName("env_physics_blocker"); - if(entity == -1) return -1; - DispatchKeyValue(entity, "targetname", "hsblocker"); - DispatchKeyValue(entity, "initialstate", "1"); - DispatchKeyValue(entity, "BlockType", "0"); - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - if(DispatchSpawn(entity)) { - if(enabled) - AcceptEntityInput(entity, "Enable"); - return entity; - } - return -1; -} - -stock int CreateEnvBlockerScaled(const char[] entClass, const float pos[3], const float scale[3] = { 5.0, 5.0, 5.0 }, bool enabled = true) { - int entity = CreateEntityByName(entClass); - DispatchKeyValue(entity, "targetname", "hsblocker"); - DispatchKeyValue(entity, "initialstate", "1"); - DispatchKeyValue(entity, "BlockType", "0"); - static float mins[3]; - mins = scale; - NegateVector(mins); - DispatchKeyValueVector(entity, "boxmins", mins); - DispatchKeyValueVector(entity, "boxmaxs", scale); - DispatchKeyValueVector(entity, "mins", mins); - DispatchKeyValueVector(entity, "maxs", scale); - - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - if(DispatchSpawn(entity)) { - #if defined DEBUG_LOG_MAPSTART - PrintToServer("spawn blocker scaled %.1f %.1f %.1f scale [%.0f %.0f %.0f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); - #endif - SetEntPropVector(entity, Prop_Send, "m_vecMaxs", scale); - SetEntPropVector(entity, Prop_Send, "m_vecMins", mins); - if(enabled) - AcceptEntityInput(entity, "Enable"); - #if defined DEBUG_BLOCKERS - Effect_DrawBeamBoxRotatableToAll(pos, mins, scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 0, 255}, 0); - #endif - return entity; - } - return -1; -} -enum PortalType { - Portal_Relative, - Portal_Teleport -} -PortalType entityPortalType[2048]; -float entityPortalOffsets[2048][3]; -stock int CreatePortal(PortalType type, const char model[64], const float pos[3], const float offset[3] = { 40.0, 40.0, 0.0 }, const float scale[3] = { 5.0, 5.0, 5.0 }) { - int entity = CreateEntityByName("trigger_multiple"); - if(entity == -1) return -1; - DispatchKeyValue(entity, "spawnflags", "513"); - DispatchKeyValue(entity, "solid", "6"); - DispatchKeyValue(entity, "targetname", "hsportal"); - DispatchKeyValue(entity, "wait", "0"); - if(DispatchSpawn(entity)) { - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - static float mins[3]; - mins = scale; - NegateVector(mins); - SetEntPropVector(entity, Prop_Send, "m_vecMaxs", scale); - SetEntPropVector(entity, Prop_Send, "m_vecMins", mins); - SetEntProp(entity, Prop_Send, "m_nSolidType", 2); - - HookSingleEntityOutput(entity, "OnStartTouch", OnPortalTouch, false); - #if defined DEBUG_BLOCKERS - Effect_DrawBeamBoxRotatableToAll(pos, mins, scale, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {255, 0, 255, 255}, 0); - #endif - #if defined DEBUG_LOG_MAPSTART - PrintToServer("spawn portal %d - pos %.1f %.1f %.1f - scale %.1f %.1f %.1f", entity, pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); - #endif - AcceptEntityInput(entity, "Enable"); - - entityPortalOffsets[entity] = NULL_VECTOR; - - // Convert relative offset to one based off full scale: - entityPortalType[entity] = type; - if(type == Portal_Relative) { - if(offset[0] != 0.0) entityPortalOffsets[entity][0] = (scale[0] * 2) + offset[0]; - if(offset[1] != 0.0) entityPortalOffsets[entity][1] = (scale[1] * 2) + offset[1]; - if(offset[2] != 0.0) entityPortalOffsets[entity][2] = (scale[2] * 2) + offset[2]; - } else { - entityPortalOffsets[entity] = offset; - } - - return entity; - } - return -1; -} - -void OnPortalTouch(const char[] output, int caller, int activator, float delay) { - if(entityPortalType[caller] == Portal_Relative) { - float pos[3]; - GetClientAbsOrigin(activator, pos); - float ang[3]; - GetClientAbsAngles(activator, ang); - if(ang[0] < 0) pos[0] -= entityPortalOffsets[caller][0]; - else pos[0] += entityPortalOffsets[caller][0]; - if(ang[1] < 0) pos[1] -= entityPortalOffsets[caller][1]; - else pos[1] += entityPortalOffsets[caller][1]; - if(ang[2] < 0) pos[2] -= entityPortalOffsets[caller][2]; - else pos[2] += entityPortalOffsets[caller][2]; - TeleportEntity(activator, pos, NULL_VECTOR, NULL_VECTOR); - } else { - TeleportEntity(activator, entityPortalOffsets[caller], NULL_VECTOR, NULL_VECTOR); - } -} - -stock int CreatePropDynamic(const char[] model, const float pos[3], const float ang[3]) { - return CreateProp("prop_dynamic", model, pos, ang); -} - -stock int CreatePropPhysics(const char[] model, const float pos[3], const float ang[3]) { - return CreateProp("prop_physics", model, pos, ang); -} - -stock int CreateProp(const char[] entClass, const char[] model, const float pos[3], const float ang[3]) { - int entity = CreateEntityByName(entClass); - if(entity == -1) return -1; - DispatchKeyValue(entity, "model", model); - DispatchKeyValue(entity, "solid", "6"); - DispatchKeyValue(entity, "targetname", "hsprop"); - DispatchKeyValue(entity, "disableshadows", "1"); - TeleportEntity(entity, pos, ang, NULL_VECTOR); - if(DispatchSpawn(entity)) { - #if defined DEBUG_LOG_MAPSTART - PrintToServer("spawn prop %.1f %.1f %.1f model %s", pos[0], pos[1], pos[2], model[7]); - #endif - return entity; - } - return -1; -} - -// Taken from silver's https://forums.alliedmods.net/showthread.php?p=1658873 -stock int CreateDynamicLight(float vOrigin[3], float vAngles[3], int color, float brightness, int style = 0) { - int entity = CreateEntityByName("light_dynamic"); - if( entity == -1) - return -1; - - DispatchKeyValue(entity, "_light", "0 0 0 255"); - DispatchKeyValue(entity, "brightness", "1"); - DispatchKeyValueFloat(entity, "spotlight_radius", 32.0); - DispatchKeyValueFloat(entity, "distance", brightness); - DispatchKeyValue(entity, "targetname", "hslamp"); - DispatchKeyValueFloat(entity, "style", float(style)); - SetEntProp(entity, Prop_Send, "m_clrRender", color); - if(DispatchSpawn(entity)) { - TeleportEntity(entity, vOrigin, vAngles, NULL_VECTOR); - AcceptEntityInput(entity, "TurnOn"); - #if defined DEBUG_LOG_MAPSTART - PrintToServer("spawn dynamic light %.1f %.1f %.1f", vOrigin[0], vOrigin[1], vOrigin[2]); - #endif - return entity; - } - return -1; -} - - -stock void CheatCommand(int client, const char[] command, const char[] argument1) { - int userFlags = GetUserFlagBits(client); - SetUserFlagBits(client, ADMFLAG_ROOT); - int flags = GetCommandFlags(command); - SetCommandFlags(command, flags & ~FCVAR_CHEAT); - FakeClientCommand(client, "%s %s", command, argument1); - SetCommandFlags(command, flags); - SetUserFlagBits(client, userFlags); -} - -stock void EntFire(const char[] name, const char[] input) { - static char targetname[64]; - static char cmd[32]; - #if defined DEBUG_LOG_MAPSTART - PrintToServer("[H&S] EntFire: %s \"%s\"", name, input); - #endif - int len = SplitString(input, " ", cmd, sizeof(cmd)); - if(len > -1) SetVariantString(input[len]); - for(int i = MAXPLAYERS + 1; i <= 4096; i++) { - if(IsValidEntity(i) && (IsValidEdict(i) || EntIndexToEntRef(i) != -1)) { - GetEntPropString(i, Prop_Data, "m_iName", targetname, sizeof(targetname)); - if(StrEqual(targetname, name, false)) { - if(len > -1) AcceptEntityInput(i, cmd); - else AcceptEntityInput(i, input); - } /*else { - GetEntityClassname(targetname, sizeof(targetname)); - if(StrEqual(targetname, name, false)) { - if(len > -1) AcceptEntityInput(i, cmd); - else AcceptEntityInput(i, input); - } - }*/ - } - } -} - -void SetupEntities(bool blockers = true, bool props = true, bool portals = true) { - if(mapConfig.entities != null) { - PrintToServer("[H&S] Deploying %d custom entities (Set: %s) (blockers:%b props:%b portals:%b)", mapConfig.entities.Length, currentSet, blockers, props, portals); - for(int i = 0; i < mapConfig.entities.Length; i++) { - EntityConfig config; - mapConfig.entities.GetArray(i, config); - - if(config.model[0] != '\0') PrecacheModel(config.model); - - if(StrEqual(config.type, "env_physics_blocker")) { - if(blockers && CreateEnvBlockerScaled(config.type, config.origin, config.scale, isNavBlockersEnabled) == -1) { - PrintToServer("[H&S:WARN] Failed to spawn blocker [type=%s] at (%.1f,%.1f, %.1f)", config.type, config.origin[0], config.origin[1], config.origin[2]); - } - } else if(StrEqual(config.type, "_relportal")) { - if(portals && CreatePortal(Portal_Relative, config.model, config.origin, config.offset, config.scale) == -1) { - PrintToServer("[H&S:WARN] Failed to spawn rel portal at (%.1f,%.1f, %.1f)", config.origin[0], config.origin[1], config.origin[2]); - } - } else if(StrEqual(config.type, "_portal")) { - if(portals && CreatePortal(Portal_Teleport, config.model, config.origin, config.offset, config.scale) == -1) { - PrintToServer("[H&S:WARN] Failed to spawn portal at (%.1f,%.1f, %.1f)", config.origin[0], config.origin[1], config.origin[2]); - } - } else if(StrEqual(config.type, "_lantern")) { - int parent = CreateProp("prop_dynamic", config.model, config.origin, config.rotation); - if(parent == -1) { - PrintToServer("[H&S:WARN] Failed to spawn prop [type=%s] [model=%s] at (%.1f,%.1f, %.1f)", config.type, config.model, config.origin[0], config.origin[1], config.origin[2]); - } else { - float pos[3]; - pos = config.origin; - pos[2] += 15.0; - int child = CreateDynamicLight(pos, config.rotation, GetColorInt(255, 255, 242), 80.0, 11); - if(child == -1) { - PrintToServer("[H&S] Failed to spawn light source for _lantern"); - } else { - SetParent(child, parent); - TeleportEntity(parent, config.origin, NULL_VECTOR, NULL_VECTOR); - } - } - } else if(props) { - if(CreateProp(config.type, config.model, config.origin, config.rotation) == -1) { - PrintToServer("[H&S:WARN] Failed to spawn prop [type=%s] [model=%s] at (%.1f,%.1f, %.1f)", config.type, config.model, config.origin[0], config.origin[1], config.origin[2]); - } - } - } - - static char key[64]; - static char value[64]; - if(mapConfig.inputs != null) { - for(int i = 0; i < mapConfig.inputs.Length - 1; i += 2) { - mapConfig.inputs.GetString(i, key, sizeof(key)); - mapConfig.inputs.GetString(i + 1, value, sizeof(value)); - EntFire(key, value); - } - } - } -} - -int GetColorInt(int r, int g, int b) { - int color = r; - color += 256 * g; - color += 65536 * b; - return color; -} - -void Cleanup() { - EntFire("hsprop", "kill"); - EntFire("hsblocker", "kill"); - EntFire("hsportal", "kill"); -} - -GameState GetState() { - if(!isEnabled) return State_Unknown; - static char buffer[4]; - L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentStage", buffer, sizeof(buffer)); - int stage = -1; - if(StringToIntEx(buffer, stage) > 0) { - return view_as(stage); - } else { - return State_Unknown; - } -} - -int GetSlasher() { - if(!isEnabled) return -1; - static char buffer[8]; - L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentSlasher && \"GetPlayerUserId\" in g_ModeScript.MutationState.CurrentSlasher ? g_ModeScript.MutationState.CurrentSlasher.GetPlayerUserId() : -1", buffer, sizeof(buffer)); - int uid = StringToInt(buffer); - if(uid > 0) { - return GetClientOfUserId(uid); - } else { - return -1; - } -} - -int GetTick() { - if(!isEnabled) return -1; - static char buffer[4]; - L4D2_GetVScriptOutput("g_ModeScript.MutationState.StateTick", buffer, sizeof(buffer)); - int value = -1; - if(StringToIntEx(buffer, value) > 0) { - return value; - } else { - return -1; - } -} - -void SetTick(int tick) { - static char buf[64]; - Format(buf, sizeof(buf), "g_ModeScript.MutationState.StateTick = %d", tick); - L4D2_ExecVScriptCode(buf); -} - - -int GetMapTime() { - static char mapTime[16]; - L4D2_GetVScriptOutput("g_ModeScript.MutationState.MapTime", mapTime, sizeof(mapTime)); - return StringToInt(mapTime); -} - -void SetMapTime(int seconds) { - static char buf[64]; - Format(buf, sizeof(buf), "g_ModeScript.MutationState.MapTime = %d", seconds); - L4D2_ExecVScriptCode(buf); -} - -Action Timer_ChangeMap(Handle h) { - PrintToChatAll("Changing map to %s in %d seconds", nextRoundMap, mapChangeMsgTicks); - if(mapChangeMsgTicks-- == 0) { - ForceChangeLevel(nextRoundMap, "H&SMapSelect"); - nextRoundMap[0] = '\0'; - return Plugin_Stop; - } - return Plugin_Continue; -} - -void ChangeMap(const char map[64], int time = 5) { - strcopy(nextRoundMap, sizeof(nextRoundMap), map); - mapChangeMsgTicks = time; - CreateTimer(1.0, Timer_ChangeMap, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); -} - -bool GetSpawnPosition(float pos[3], bool includePlayers = true) { - if(includePlayers) { - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) { - GetClientAbsOrigin(i, pos); - return true; - } - } - } - int entity = INVALID_ENT_REFERENCE; - while ((entity = FindEntityByClassname(entity, "info_player_start")) != INVALID_ENT_REFERENCE) { - GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos); - return true; - } - return false; -} - -bool SetState(GameState state) { - if(!isEnabled) return false; - static char buffer[64]; - Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.CurrentStage = %d", view_as(state)); - return L4D2_ExecVScriptCode(buffer); -} - -bool IsGameSoloOrPlayersLoading() { - int connecting, ingame; - for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i)) { - if(IsClientInGame(i)) - ingame++; - else - connecting++; - } - } - return connecting > 0 || ingame == 1; -} - -//cm_NoSurvivorBots -bool SetBotsEnabled(bool value) { - static char buffer[64]; - if(value) - Format(buffer, sizeof(buffer), "g_ModeScript.MutationOptions.cm_NoSurvivorBots = true"); - else - Format(buffer, sizeof(buffer), "g_ModeScript.MutationOptions.cm_NoSurvivorBots = false"); - return L4D2_ExecVScriptCode(buffer); -} - -bool IsBotsEnabled() { - static char result[8]; - L4D2_GetVScriptOutput("g_ModeScript.MutationState.cm_NoSurvivorBots", result, sizeof(result)); - return StrEqual(result, "true", false); -} - -stock void GetHorizontalPositionFromClient(int client, float units, float finalPosition[3]) { - float pos[3], ang[3]; - GetClientEyeAngles(client, ang); - GetClientAbsOrigin(client, pos); - - float theta = DegToRad(ang[1]); - pos[0] += units * Cosine(theta); - pos[1] += units * Sine(theta); - finalPosition = pos; -} - -void SetParent(int child, int parent) { - SetVariantString("!activator"); - AcceptEntityInput(child, "SetParent", parent); -} \ No newline at end of file