diff --git a/data/guesswho/config.cfg b/data/guesswho/config.cfg index 1ed9ce1..a42efa4 100644 --- a/data/guesswho/config.cfg +++ b/data/guesswho/config.cfg @@ -62,4 +62,26 @@ } } } + "c11m4_terminal" + { + "spawnpoint" "3081.824951 4571.367187 152.031250" + "ents" + { + "ENTRANCE_BLOCKER" + { + "origin" "2700.844482 1865.502807 177.313140" + "scale" "200 100 200" + } + "fire" + { + "origin" "2700.844482 1865.502807 179.313140" + "type" "env_fire" + "scale" "1.0 256 8" + } + } + "inputs" + { + "checkpoint_entrance" "Kill" + } + } } diff --git a/data/hideandseek.cfg b/data/hideandseek.cfg index 2a72212..addd74b 100644 --- a/data/hideandseek.cfg +++ b/data/hideandseek.cfg @@ -1492,6 +1492,82 @@ "door_exitr" "Lock" } } + "c11m4_terminal" + { + "spawnpoint" "-482.813659 3566.486572 296.031250" + "sets" + { + "terminal" + { + "spawnpoint" "1811.834716 1570.712402 264.607604" + } + } + "ents" + { + "BAG_HOLE_A" + { + "origin" "382.787200 2708.387207 40.031250" + "rotation" "0.000000 -174.077285 0.000000" + "type" "prop_dynamic" + "model" "props_unique/airport/luggage_pile1.mdl" + } + "BAG_HOLE_B" + { + "origin" "402.307434 2415.856201 40.031250" + "rotation" "0.000000 -174.077285 0.000000" + "type" "prop_dynamic" + "model" "props_unique/airport/luggage_pile1.mdl" + } + "BAG_HOLE_C" + { + "origin" "401.137786 2110.304199 40.031250" + "rotation" "0.000000 -174.077285 0.000000" + "type" "prop_dynamic" + "model" "props_unique/airport/luggage_pile1.mdl" + } + "DETECTOR_NO_DETECT" + { + "origin" "1023.898742 1750.977294 16.031250" + "scale" "20 650 300" + "set" "terminal" + } + "DETECTOR_PROP" + { + "origin" "1030.929931 1755.157714 37.031250" + "rotation" "90 0 0" + "type" "prop_dynamic" + "model" "props_interiors\chairs_airport.mdl" + "set" "terminal" + } + "lol" + { + "origin" "2350.819335 1851.536376 152.235565" + "rotation" "10.137485 -178.424728 15.000000" + "type" "prop_dynamic" + "model" "survivors/survivor_teenangst.mdl" + "set" "terminal" + } + } + "inputs" + { + "van_blocker" "Kill" + "van_start_relay" "Trigger" + "!3098419" "Kill" + "breakwall01" "Break" + "func_breakable" "Kill" + "breakwall01_illusionary" "Kill" + "!6802112" "Open" + "!6765323" "Kill" + "!6765365" "Kill" + "securityalarmlight1" "TurnOn" + "securityspotlight1" "LightOn" + "securityrotator1" "StopSound" + "securityalarmbase1" "Skin 1" + "securityalarmsprite1" "ShowSprite" + "alarm_safety_relay" "Kill" + "env_soundscape" "Kill" + } + } "c12m1_hilltop" { "sets" diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index 2869b8e..f1fc13d 100644 Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ diff --git a/plugins/l4d2_guesswho.smx b/plugins/l4d2_guesswho.smx index 10a9f2e..e6c0bbf 100644 Binary files a/plugins/l4d2_guesswho.smx and b/plugins/l4d2_guesswho.smx differ diff --git a/plugins/l4d2_hideandseek.smx b/plugins/l4d2_hideandseek.smx index 3998320..8626063 100644 Binary files a/plugins/l4d2_hideandseek.smx and b/plugins/l4d2_hideandseek.smx differ diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 2c37550..6689036 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -31,7 +31,68 @@ public void OnClientPutInServer(int client) { if(IsTrollActive(client, "Voice Mute")) BaseComm_SetClientMute(client, true); SDKHook(client, SDKHook_OnTakeDamage, Event_TakeDamage); + SDKHook(client, SDKHook_OnTakeDamageAlive, NerfGun_OnTakeDamage); } +public void OnEntityCreated(int entity, const char[] classname) { + if(entity >= MaxClients) { + if(StrEqual(classname, "infected", false)) + SDKHook(entity, SDKHook_OnTakeDamageAlive, NerfGun_OnTakeDamage); + else if(StrContains(classname, "_projectile", true) > -1 ) { + RequestFrame(EntityCreateCallback, entity); + } + } +} + +void EntityCreateCallback(int entity) { + if(!HasEntProp(entity, Prop_Send, "m_hOwnerEntity") || !IsValidEntity(entity)) return; + static char class[16]; + + static int badThrowID; + if(badThrowID == 0) badThrowID = GetTrollID("Bad Throw"); + + GetEntityClassname(entity, class, sizeof(class)); + int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); + if(entOwner > 0 && entOwner <= MaxClients) { + if(Trolls[badThrowID].IsActive(entOwner)) { + static float pos[3]; + GetClientEyePosition(entOwner, pos); + if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_1) && StrContains(class, "vomitjar", true) > -1) { + AcceptEntityInput(entity, "Kill"); + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { + L4D_CTerrorPlayer_OnVomitedUpon(entOwner, entOwner); + EmitSoundToAll("weapons/ceda_jar/ceda_jar_explode.wav", entOwner); + FindClosestClient(entOwner, false, pos); + } + SpawnItem("vomitjar", pos); + } else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_2) && StrContains(class, "molotov", true) > -1) { + // Burn them if no one near :) + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { + GetClientAbsOrigin(entOwner, pos); + if(IsAnyPlayerNear(entOwner, 500.0)) { + AcceptEntityInput(entity, "Kill"); + EmitSoundToAll("weapons/molotov/molotov_detonate_1.wav", entOwner); + } else { // or delete if there is + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + } + } else { + SpawnItem("molotov", pos); + AcceptEntityInput(entity, "Kill"); + } + } else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_3) && StrContains(class, "pipe_bomb", true) > -1) { + if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + ExplodeProjectile(entity); + } + SpawnItem("pipe_bomb", pos); + } + } else if(Trolls[slipperyShoesIndex].IsActive(entOwner)) { + if(Trolls[slipperyShoesIndex].activeFlagClients[entOwner] & 4) { + L4D_StaggerPlayer(entOwner, entOwner, NULL_VECTOR); + } + } + } +} + public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int userid = event.GetInt("userid"); if(spIsActive) @@ -593,17 +654,22 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 return Plugin_Continue; } +public Action NerfGun_OnTakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) { + static int nerfGunIndex; + if(nerfGunIndex == 0) nerfGunIndex = GetTrollID("Nerf Gun"); + if(attacker > 0 && attacker <= MaxClients && GetClientTeam(attacker) == 2 && Trolls[nerfGunIndex].IsActive(attacker)) { + damage = 0.0; + return Plugin_Changed; + } + return Plugin_Continue; +} + public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) { //Stop FF from marked: - static int reverseFF, nerfGunIndex; + static int reverseFF; if(reverseFF == 0) reverseFF = GetTrollID("Reverse FF"); - if(nerfGunIndex == 0) nerfGunIndex = GetTrollID("Nerf Gun"); if(attacker > 0 && attacker <= MaxClients) { if(GetClientTeam(attacker) == 4 && IsFakeClient(attacker)) return Plugin_Continue; - else if(Trolls[nerfGunIndex].IsActive(attacker)) { - damage = 0.0; - return Plugin_Changed; - } } if(attacker > 0 && victim <= MaxClients && attacker <= MaxClients && IsClientInGame(attacker) && IsPlayerAlive(attacker)) { @@ -787,62 +853,6 @@ public void Event_WitchVictimSet(Event event, const char[] name, bool dontBroadc } } -public void OnEntityCreated(int entity, const char[] classname) { - if(IsValidEntity(entity) && StrContains(classname, "_projectile", true) > -1 ) { - RequestFrame(EntityCreateCallback, entity); - } -} - -void EntityCreateCallback(int entity) { - if(!HasEntProp(entity, Prop_Send, "m_hOwnerEntity") || !IsValidEntity(entity)) return; - static char class[16]; - - static int badThrowID; - if(badThrowID == 0) badThrowID = GetTrollID("Bad Throw"); - - GetEntityClassname(entity, class, sizeof(class)); - int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); - if(entOwner > 0 && entOwner <= MaxClients) { - if(Trolls[badThrowID].IsActive(entOwner)) { - static float pos[3]; - GetClientEyePosition(entOwner, pos); - if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_1) && StrContains(class, "vomitjar", true) > -1) { - AcceptEntityInput(entity, "Kill"); - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { - L4D_CTerrorPlayer_OnVomitedUpon(entOwner, entOwner); - EmitSoundToAll("weapons/ceda_jar/ceda_jar_explode.wav", entOwner); - FindClosestClient(entOwner, false, pos); - } - SpawnItem("vomitjar", pos); - } else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_2) && StrContains(class, "molotov", true) > -1) { - // Burn them if no one near :) - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { - GetClientAbsOrigin(entOwner, pos); - if(IsAnyPlayerNear(entOwner, 500.0)) { - AcceptEntityInput(entity, "Kill"); - EmitSoundToAll("weapons/molotov/molotov_detonate_1.wav", entOwner); - } else { // or delete if there is - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - } - } else { - SpawnItem("molotov", pos); - AcceptEntityInput(entity, "Kill"); - } - } else if(Trolls[badThrowID].IsFlagActive(entOwner, Flag_3) && StrContains(class, "pipe_bomb", true) > -1) { - if(hBadThrowHitSelf.FloatValue > 0.0 && GetRandomFloat() <= hBadThrowHitSelf.FloatValue) { - TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); - ExplodeProjectile(entity); - } - SpawnItem("pipe_bomb", pos); - } - } else if(Trolls[slipperyShoesIndex].IsActive(entOwner)) { - if(Trolls[slipperyShoesIndex].activeFlagClients[entOwner] & 4) { - L4D_StaggerPlayer(entOwner, entOwner, NULL_VECTOR); - } - } - } -} - public Action L4D2_MeleeGetDamageForVictim(int client, int weapon, int victim, float &damage) { static int dullMeleeID; if(!dullMeleeID) dullMeleeID = GetTrollID("Dull Melee"); diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index 865dd09..c6991fb 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -297,7 +297,7 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod SetEntProp(primaryWpn, Prop_Send, "m_iClip1", GetRandomInt(0, maxCap)); } } - } else if(~modifier & TrollMod_Constant) { + } else if(~modifier & TrollMod_Constant) { PrintToServer("[FTT] Warn: Possibly invalid troll, no apply action defined for \"%s\"", troll.name); #if defined DEBUG ReplyToCommand(activator, "[FTT/Debug] If nothing occurs, this troll possibly was not implemented correctly. "); diff --git a/scripting/include/gamemodes/base.inc b/scripting/include/gamemodes/base.inc index ab4e294..04d6403 100644 --- a/scripting/include/gamemodes/base.inc +++ b/scripting/include/gamemodes/base.inc @@ -12,7 +12,7 @@ char g_currentMap[64]; char nextRoundMap[64]; // Legacy: -bool isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled +bool isNavBlockersEnabled = true, isPropsEnabled = true, isPortalsEnabled = true static int _debugFlags = BaseDebug_Server; @@ -23,9 +23,9 @@ enum { BaseDebug_ChatAll = 4 } -static bool ents_NavBlockers; -static bool ents_Props; -static bool ents_Portals; +static bool ents_NavBlockers = true; +static bool ents_Props = true; +static bool ents_Portals = true; int g_iLaserIndex; @@ -50,23 +50,23 @@ methodmap BaseGame { } public void PrintToServer(const char[] format, any ...) { - VFormat(buffer, sizeof(buffer), format, 2); + VFormat(buffer, sizeof(buffer), format, 3); PrintToServer("[%s] %s", GAMEMODE_NAME, buffer); } public void Warn(const char[] format, any ...) { - VFormat(buffer, sizeof(buffer), format, 2); + VFormat(buffer, sizeof(buffer), format, 3); PrintToServer("[%s::WARN] %s", GAMEMODE_NAME, buffer); } public void Broadcast(const char[] format, any ...) { - VFormat(buffer, sizeof(buffer), format, 2); + VFormat(buffer, sizeof(buffer), format, 3); PrintToChatAll("[%s] %s", GAMEMODE_NAME, buffer); } public void Debug(const char[] format, any ...) { if(_debugFlags == BaseDebug_None) return; - VFormat(buffer, sizeof(buffer), format, 2); + VFormat(buffer, sizeof(buffer), format, 3); if(_debugFlags & BaseDebug_Server) PrintToServer("[%s/debug] %s", GAMEMODE_NAME, buffer); if(_debugFlags & BaseDebug_ChatAll) @@ -77,7 +77,7 @@ methodmap BaseGame { public void DebugConsole(const char[] format, any ...) { if(_debugFlags == BaseDebug_None) return; - VFormat(buffer, sizeof(buffer), format, 2); + VFormat(buffer, sizeof(buffer), format, 3); PrintToConsoleAll("[%s/debug] %s", GAMEMODE_NAME, buffer); } @@ -226,12 +226,12 @@ methodmap PeekCamera { } public void Destroy() { - if(seekerCam == INVALID_ENT_REFERENCE || !IsValidEntity(seekerCam)) { - seekerTarget = 0; + if(seekerCam != INVALID_ENT_REFERENCE && IsValidEntity(seekerCam)) { AcceptEntityInput(seekerCam, "Disable"); AcceptEntityInput(seekerCam, "Kill"); seekerCam = INVALID_ENT_REFERENCE } + seekerTarget = 0; } } diff --git a/scripting/include/gamemodes/cvars.inc b/scripting/include/gamemodes/cvars.inc index d9d91dd..af0d24f 100644 --- a/scripting/include/gamemodes/cvars.inc +++ b/scripting/include/gamemodes/cvars.inc @@ -30,12 +30,13 @@ methodmap GameConVarStorage < StringMap { } -methodmap GameConVar < ConVar { +methodmap GameConVar __nullable__ < ConVar { public GameConVar(const char[] name) { return view_as(FindConVar(name)); } public void RecordInt(int value, GameConVarStorage storage) { + if(this == null) return; if(storage != null) { this.GetName(buffer, sizeof(buffer)); storage.SetValue(buffer, float(value)); @@ -44,6 +45,7 @@ methodmap GameConVar < ConVar { } public void RecordFloat(float value, GameConVarStorage storage) { + if(this == null) return; if(storage != null) { this.GetName(buffer, sizeof(buffer)); storage.SetValue(buffer, value); @@ -52,6 +54,7 @@ methodmap GameConVar < ConVar { } public void RecordString(const char[] value, GameConVarStorage storage) { + if(this == null) return; if(storage != null) { char prevValue[32]; this.GetName(buffer, sizeof(buffer)); diff --git a/scripting/include/gamemodes/ents.inc b/scripting/include/gamemodes/ents.inc new file mode 100644 index 0000000..4179296 --- /dev/null +++ b/scripting/include/gamemodes/ents.inc @@ -0,0 +1,205 @@ +#if !defined ENT_PROP_NAME + #define ENT_PROP_NAME "cprop" +#endif +#if !defined ENT_BLOCKER_NAME + #define ENT_BLOCKER_NAME "cblocker" +#endif +#if !defined ENT_PORTAL_NAME + #define ENT_PORTAL_NAME "cportal" +#endif +#if !defined ENT_ENV_NAME + #define ENT_ENV_NAME "cenv" +#endif + +void DeleteCustomEnts() { + EntFire(ENT_PROP_NAME, "Kill"); + EntFire(ENT_BLOCKER_NAME, "Kill"); + EntFire(ENT_PORTAL_NAME, "Kill"); + EntFire(ENT_ENV_NAME, "Kill"); +} + +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", ENT_BLOCKER_NAME); + 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; + } else { + #if defined DEBUG_LOG_MAPSTART + PrintToServer("FAILED: spawn blocker scaled %.1f %.1f %.1f scale [%.0f %.0f %.0f]", pos[0], pos[1], pos[2], scale[0], scale[1], scale[2]); + #endif + } + return -1; +} + +enum PortalType { + Portal_Relative, + Portal_Teleport +} +enum struct EntData { + PortalType portalType; + float portalOffsets[3]; +} +EntData entData[2048]; + +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", ENT_PORTAL_NAME); + 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"); + + entData[entity].portalOffsets = NULL_VECTOR; + + // Convert relative offset to one based off full scale: + entData[entity].portalType = type; + if(type == Portal_Relative) { + if(offset[0] != 0.0) entData[entity].portalOffsets[0] = (scale[0] * 2) + offset[0]; + if(offset[1] != 0.0) entData[entity].portalOffsets[1] = (scale[1] * 2) + offset[1]; + if(offset[2] != 0.0) entData[entity].portalOffsets[2] = (scale[2] * 2) + offset[2]; + } else { + entData[entity].portalOffsets = offset; + } + + return entity; + } + return -1; +} + +void OnPortalTouch(const char[] output, int caller, int activator, float delay) { + if(entData[caller].portalType == Portal_Relative) { + float pos[3]; + GetClientAbsOrigin(activator, pos); + float ang[3]; + GetClientAbsAngles(activator, ang); + if(ang[0] < 0) pos[0] -= entData[caller].portalOffsets[0]; + else pos[0] += entData[caller].portalOffsets[0]; + if(ang[1] < 0) pos[1] -= entData[caller].portalOffsets[1]; + else pos[1] += entData[caller].portalOffsets[1]; + if(ang[2] < 0) pos[2] -= entData[caller].portalOffsets[2]; + else pos[2] += entData[caller].portalOffsets[2]; + TeleportEntity(activator, pos, NULL_VECTOR, NULL_VECTOR); + } else { + TeleportEntity(activator, entData[caller].portalOffsets, 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", ENT_PROP_NAME); + 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 char[] anim, const float pos[3], const float ang[3] = NULL_VECTOR) { + int entity = StartPropCreate("commentary_dummy", model, pos, ang); + if(entity == -1) return -1; + DispatchKeyValue(entity, "targetname", ENT_PROP_NAME); + DispatchKeyValue(entity, "LookAtPlayers", "Yes"); + DispatchKeyValue(entity, "StartingWeapons", "weapon_rifle_ak47"); + DispatchKeyValue(entity, "StartingAnim", anim); //idle_calm_rifle + DispatchKeyValueFloat(entity, "LookAtPlayers", 40.0); + DispatchSpawn(entity); + return entity; +} + +stock int CreateFire(const float pos[3], float damage = 10.0, float size = 256.0, float attack = 4.0) { + int entity = CreateEntityByName("env_fire"); + if(entity == -1) return -1; + DispatchKeyValue(entity, "spawnflags", "13"); + DispatchKeyValue(entity, "targetname", ENT_ENV_NAME); + DispatchKeyValueFloat(entity, "firesize", size); + DispatchKeyValueFloat(entity, "fireattack", attack); + DispatchKeyValueFloat(entity, "damagescale", damage); + TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR); + DispatchSpawn(entity); + AcceptEntityInput(entity, "Enable"); + AcceptEntityInput(entity, "StartFire"); + #if defined DEBUG_LOG_MAPSTART + + PrintToServer("spawn env_fire at %.1f %.1f %.1f", pos[0], pos[1], pos[2]); + #endif + 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", ENT_ENV_NAME); + 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; +} + + +int GetHammerId(int entity) { + return HasEntProp(entity, Prop_Data, "m_iHammerID") ? GetEntProp(entity, Prop_Data, "m_iHammerID") : -1; +} \ No newline at end of file diff --git a/scripting/include/guesswho/gwcmds.inc b/scripting/include/guesswho/gwcmds.inc index 1daa926..e90acdb 100644 --- a/scripting/include/guesswho/gwcmds.inc +++ b/scripting/include/guesswho/gwcmds.inc @@ -66,7 +66,7 @@ public Action Command_GuessWho(int client, int args) { if(arg[0] == 'f') { InitGamemode(); } - SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); + SetupEntities(Game.Blockers, Game.Props, Game.Portals); ReplyToCommand(client, "Reloaded map from config"); } else { ReplyToCommand(client, "Error occurred while reloading map file"); @@ -101,7 +101,7 @@ public Action Command_GuessWho(int client, int args) { ReplyToCommand(client, "Warn: %s/%s has 0 saved movement locations", g_currentMap, g_currentSet); } Game.Cleanup(); - SetupEntities(isNavBlockersEnabled, isPropsEnabled, isPortalsEnabled); + SetupEntities(Game.Blockers, Game.Props, Game.Portals); PrintToChatAll("[GuessWho] Map set has been changed to \"%s\"", g_currentSet); return Plugin_Handled; } @@ -115,18 +115,18 @@ public Action Command_GuessWho(int client, int args) { bool isUnknown = true; if(doAll || StrEqual(type, "blockers", false)) { - if(isNavBlockersEnabled) { + if(Game.Blockers) { EntFire(GAMEMODE_BLOCKER_NAME, "Disable"); ReplyToCommand(client, "Disabled all custom gamemode blockers"); } else { EntFire(GAMEMODE_BLOCKER_NAME, "Enable"); ReplyToCommand(client, "Enabled all custom gamemode blockers"); } - isNavBlockersEnabled = !isNavBlockersEnabled; + Game.Blockers = !Game.Blockers; isUnknown = false; } if(doAll || StrEqual(type, "props", false)) { - if(isPropsEnabled) { + if(Game.Props) { EntFire(GAMEMODE_PROP_NAME, "Disable"); EntFire(GAMEMODE_PROP_NAME, "DisableCollision"); ReplyToCommand(client, "Disabled all custom gamemode props"); @@ -135,10 +135,10 @@ public Action Command_GuessWho(int client, int args) { EntFire(GAMEMODE_PROP_NAME, "EnableCollision"); ReplyToCommand(client, "Enabled all custom gamemode props"); } - isPropsEnabled = !isPropsEnabled; + Game.Props = !Game.Props; isUnknown = false; } - if(isUnknown) ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); + if(isUnknown) ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', or 'all'"); } else if(StrEqual(subcmd, "clear", false)) { static char arg[16]; GetCmdArg(2, arg, sizeof(arg)); @@ -151,7 +151,7 @@ public Action Command_GuessWho(int client, int args) { } else if(StrEqual(arg, "blockers")) { EntFire(GAMEMODE_BLOCKER_NAME, "kill"); ReplyToCommand(client, "Removed all custom gamemode blockers"); - } else ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', 'portals', or 'all'"); + } else ReplyToCommand(client, "Specify the type to affect: 'blockers', 'props', or 'all'"); } else if(StrEqual(subcmd, "settime")) { int prev = Game.MapTime; static char arg[16]; @@ -292,7 +292,7 @@ public Action Command_GuessWho(int client, int args) { else ReplyToCommand(client, "Has Spawnpoint: no (possibly map spawn %f %f %f)", mapConfig.spawnpoint[0], mapConfig.spawnpoint[1], mapConfig.spawnpoint[2]); ReplyToCommand(client, "Map Time: %d", mapConfig.mapTime); - ReplyToCommand(client, "Flow Bounds: (%f, %f)", flowMin, flowMax); + ReplyToCommand(client, "Flow Bounds: (%f, %f)", movePoints.MinFlow, movePoints.MaxFlow); } else { ReplyToCommand(client, "Unknown option. Leave blank for help"); } @@ -383,6 +383,9 @@ public Action Command_Join(int client, int args) { L4D_RespawnPlayer(client); TeleportEntity(client, tpLoc, NULL_VECTOR, NULL_VECTOR); CheatCommand(client, "give", "gnome"); + if(!ArePlayersJoining()) { + InitGamemode(); + } } return Plugin_Handled; } diff --git a/scripting/include/guesswho/gwents.inc b/scripting/include/guesswho/gwents.inc index 22ddf8c..2bd6dfa 100644 --- a/scripting/include/guesswho/gwents.inc +++ b/scripting/include/guesswho/gwents.inc @@ -1,174 +1,8 @@ -#define GAMEMODE_PROP_NAME "gwprop" -#define GAMEMODE_BLOCKER_NAME "gwblocker" -stock int CreateEnvBlockerBox(const float pos[3], bool enabled = true) { - int entity = CreateEntityByName("env_physics_blocker"); - if(entity == -1) return -1; - DispatchKeyValue(entity, "targetname", GAMEMODE_BLOCKER_NAME); - 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", GAMEMODE_BLOCKER_NAME); - 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", "gwportal"); - 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", GAMEMODE_PROP_NAME); - 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 char[] anim, const float pos[3], const float ang[3] = NULL_VECTOR) { - 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", anim); //idle_calm_rifle - DispatchKeyValueFloat(entity, "LookAtPlayers", 40.0); - DispatchSpawn(entity); - 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", "gwlamp"); - 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; -} - +#define ENT_PROP_NAME "gwprop" +#define ENT_BLOCKER_NAME "gwblocker" +#define ENT_PORTAL_NAME "gwportal" +#define ENT_ENV_NAME "gwenv" +#include stock void CheatCommand(int client, const char[] command, const char[] argument1) { int userFlags = GetUserFlagBits(client); @@ -180,11 +14,12 @@ stock void CheatCommand(int client, const char[] command, const char[] argument1 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("[GuessWho] EntFire: %s \"%s\"", name, input); + PrintToServer("EntFire: %s \"%s\"", name, input); #endif int len = SplitString(input, " ", cmd, sizeof(cmd)); if(len > -1) SetVariantString(input[len]); @@ -202,6 +37,7 @@ stock void EntFire(const char[] name, const char[] input) { if(StrEqual(targetname, name, false)) { if(len > -1) AcceptEntityInput(i, cmd); else AcceptEntityInput(i, input); + } else { GetEntityClassname(i, targetname, sizeof(targetname)); if(StrEqual(targetname, name, false)) { @@ -214,10 +50,6 @@ stock void EntFire(const char[] name, const char[] input) { } } -int GetHammerId(int entity) { - return HasEntProp(entity, Prop_Data, "m_iHammerID") ? GetEntProp(entity, Prop_Data, "m_iHammerID") : -1; -} - void SetupEntities(bool blockers = true, bool props = true, bool portals = true) { @@ -229,6 +61,7 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) SetEntProp(dummy, Prop_Send, "m_CollisionGroup", 0); SetEntProp(dummy, Prop_Send, "movetype", MOVETYPE_NONE); } + EntFire("info_changelevel", "Kill"); #endif if(mapConfig.entities != null) { PrintToServer("[GuessWho] Deploying %d custom entities (Set: %s) (blockers:%b props:%b portals:%b)", mapConfig.entities.Length, g_currentSet, blockers, props, portals); @@ -240,27 +73,27 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) 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]); + Game.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]); + Game.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]); + Game.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]); + Game.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("[GuessWho] Failed to spawn light source for _lantern"); + Game.Warn("Failed to spawn light source for _lantern"); } else { SetParent(child, parent); TeleportEntity(parent, config.origin, NULL_VECTOR, NULL_VECTOR); @@ -268,11 +101,15 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) } } else if(StrEqual(config.type, "_dummy")) { if(CreateDummy(config.model, "hitby_tankpunch", 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]); + Game.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) { + } else if(StrEqual(config.type, "env_fire")) { + if(props && CreateFire(config.origin, config.scale[0], config.scale[1], config.scale[2]) == -1) { + Game.Warn("Failed to spawn env_fire at (%.1f,%.1f, %.1f)", 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]); + Game.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]); } } } diff --git a/scripting/include/guesswho/gwgame.inc b/scripting/include/guesswho/gwgame.inc index b666f97..355dc1d 100644 --- a/scripting/include/guesswho/gwgame.inc +++ b/scripting/include/guesswho/gwgame.inc @@ -116,6 +116,11 @@ methodmap GuessWhoGame < BaseGame { public set(int seconds) { Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.MaxTime = %d", seconds); L4D2_ExecVScriptCode(buffer); + if(timesUpTimer != null) { + float remaining = float(seconds) - float(this.Tick); + delete timesUpTimer; + timesUpTimer = CreateTimer(remaining, Timer_TimesUp, _, TIMER_FLAG_NO_MAPCHANGE); + } } } @@ -124,6 +129,10 @@ methodmap GuessWhoGame < BaseGame { } public void End(GameState state) { + if(recordTimer != null) { + PrintToChatAll("Active recording stopped due to end of game. %d ready to save. \"/guesswho points save\" to save", movePoints.Length); + delete recordTimer; + } if(acquireLocationsTimer != null) delete acquireLocationsTimer; if(timesUpTimer != null) delete timesUpTimer; if(hiderCheckTimer != null) delete hiderCheckTimer; @@ -135,7 +144,6 @@ methodmap GuessWhoGame < BaseGame { if(IsClientConnected(i) && IsClientInGame(i) && IsFakeClient(i)) { if(IsFakeClient(i)) { ClearInventory(i); - PrintToServer("PlayerDeath: Seeker kill %d", i); KickClient(i); } else { L4D2_SetEntityGlow(i, L4D2Glow_Constant, 0, 20, PLAYER_GLOW_COLOR, false); @@ -147,22 +155,23 @@ methodmap GuessWhoGame < BaseGame { CreateTimer(5.0, Timer_ResetAll); } - public void Cleanup(bool entsOnly = false) { - EntFire("gwprop", "kill"); - EntFire("gwblocker", "kill"); - EntFire("gwportal", "kill"); - // PeekCam.Destroy(); - if(entsOnly) return; + public void Cleanup(bool noClearInv = false) { + DeleteCustomEnts(); + PeekCam.Destroy(); + if(recordTimer != null) { + delete recordTimer; + } + for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { - if(isEnabled) + if(!noClearInv && isEnabled) ClearInventory(i); SDKUnhook(i, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive); SDKUnhook(i, SDKHook_WeaponDrop, OnWeaponDrop); } - } - if(recordTimer != null) { - delete recordTimer; + if(moveTimers[i] != null) { + delete moveTimers[i]; + } } } @@ -174,7 +183,7 @@ methodmap GuessWhoGame < BaseGame { if(uid > 0) { return GetClientOfUserId(uid); } else { - PrintToServer("[GuessWho] Could not find real seeker, falling back to manual check"); + Game.Debug("Mutation has no seeker, manually attempting to find seeker"); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { int entity = GetPlayerWeaponSlot(i, 1); @@ -187,6 +196,7 @@ methodmap GuessWhoGame < BaseGame { } } } + Game.Debug("All attempts to find a seeker failed"); return -1; } @@ -227,7 +237,11 @@ methodmap GuessWhoGame < BaseGame { if(movePoints != null) { delete movePoints; } - movePoints = points; + if(points == null) { + movePoints = new MovePoints(); + } else { + movePoints = points; + } } // Ignores seeker @@ -406,7 +420,7 @@ enum struct HiderDistQueue { int time = GetTime(); if(time - this.lastVocalize > HIDER_AUTO_VOCALIZE_GRACE_TIME) { this.lastVocalize = time; - int index = GetRandomInt(0, MAX_AUTO_VOCALIZATIONS); + int index = GetRandomInt(0, MAX_AUTO_VOCALIZATIONS - 1); PerformScene(i, AUTO_VOCALIZATIONS[index]); } } diff --git a/scripting/include/guesswho/gwpoints.inc b/scripting/include/guesswho/gwpoints.inc index fb7454a..637d2df 100644 --- a/scripting/include/guesswho/gwpoints.inc +++ b/scripting/include/guesswho/gwpoints.inc @@ -78,13 +78,13 @@ methodmap MovePoints < ArrayList { min -= FLOW_BOUND_BUFFER; max += FLOW_BOUND_BUFFER; - movePoints.SetBounds(flowMin, flowMax); + movePoints.SetBounds(min, max); - PrintToServer("[GuessWho] Loaded %d locations (bounds (%.0f, %.0f)) for %s/%s", movePoints.Length, min, max, map, set); + PrintToServer("[GuessWho] Loaded %d locations with bounds [%.0f, %.0f] for %s/%s", points.Length, min, max, map, set); delete file; return points; } - PrintToServer("[GuessWho] OpenFile(r+) returned null for %s", buffer); + PrintToServer("[GuessWho] No point data for %s/%s", map, set); return null; } @@ -101,6 +101,7 @@ methodmap MovePoints < ArrayList { } public void GetRandomPoint(LocationMeta meta) { + if(this.Length == 0) return; meta.runto = GetURandomFloat() < BOT_MOVE_RUN_CHANCE; meta.attempts = 0; this.GetArray(GetURandomInt() % this.Length, meta); diff --git a/scripting/include/guesswho/gwtimers.inc b/scripting/include/guesswho/gwtimers.inc index c665e58..1763176 100644 --- a/scripting/include/guesswho/gwtimers.inc +++ b/scripting/include/guesswho/gwtimers.inc @@ -17,11 +17,25 @@ Action Timer_RecordPoints(Handle h, int i) { return Plugin_Continue; } +bool firstCheckDone = false; Action Timer_WaitForPlayers(Handle h) { if(!isEnabled) return Plugin_Stop; + if(!ArePlayersJoining()) { + Game.Debug("No players pending, ready to go"); + if(!firstCheckDone) { + // Wait one more iteration + firstCheckDone = true; + } else { + firstCheckDone = false; + InitGamemode(); + return Plugin_Stop; + } + } + Game.Debug("Waiting for players"); return Plugin_Continue; } + Action Timer_CheckHiders(Handle h) { static float pos[3]; for(int i = 1; i <= MaxClients; i++) { diff --git a/scripting/include/hideandseek/hsents.inc b/scripting/include/hideandseek/hsents.inc index 5445eea..20ce382 100644 --- a/scripting/include/hideandseek/hsents.inc +++ b/scripting/include/hideandseek/hsents.inc @@ -1,172 +1,8 @@ -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 char[] anim, const float pos[3], const float ang[3] = NULL_VECTOR) { - 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", anim); //idle_calm_rifle - DispatchKeyValueFloat(entity, "LookAtPlayers", 40.0); - DispatchSpawn(entity); - 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; -} - +#define ENT_PROP_NAME "hsprop" +#define ENT_BLOCKER_NAME "hsblocker" +#define ENT_PORTAL_NAME "hsportal" +#define ENT_ENV_NAME "hsenv" +#include stock void CheatCommand(int client, const char[] command, const char[] argument1) { int userFlags = GetUserFlagBits(client); @@ -182,7 +18,7 @@ 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); + PrintToServer("EntFire: %s \"%s\"", name, input); #endif int len = SplitString(input, " ", cmd, sizeof(cmd)); if(len > -1) SetVariantString(input[len]); @@ -220,11 +56,6 @@ stock void EntFire(const char[] name, const char[] input) { } } -int GetHammerId(int entity) { - return HasEntProp(entity, Prop_Data, "m_iHammerID") ? GetEntProp(entity, Prop_Data, "m_iHammerID") : -1; -} - - void SetupEntities(bool blockers = true, bool props = true, bool portals = true) { #if defined DEBUG_BLOCKERS diff --git a/scripting/l4d2_guesswho.sp b/scripting/l4d2_guesswho.sp index a2b6a75..731ff8a 100644 --- a/scripting/l4d2_guesswho.sp +++ b/scripting/l4d2_guesswho.sp @@ -5,6 +5,7 @@ #define DEBUG_SHOW_POINTS #define DEBUG_BOT_MOVE #define DEBUG_BLOCKERS +// #define DEBUG_LOG_MAPSTART // #define DEBUG_MOVE_ATTEMPTS // #define DEBUG_SEEKER_PATH_CREATION 1 @@ -22,7 +23,7 @@ #define BOT_MOVE_RUN_CHANCE 0.15 #define BOT_MOVE_NOT_REACHED_DISTANCE 60.0 // The distance that determines if a bot reached a point #define BOT_MOVE_NOT_REACHED_ATTEMPT_RUNJUMP 6 // The minimum amount of attempts where bot will run or jump to dest -#define BOT_MOVE_NOT_REACHED_ATTEMPT_RETRY 9 // The minimum amount of attempts where bot gives up and picks new +#define BOT_MOVE_NOT_REACHED_ATTEMPT_RETRY 10 // The minimum amount of attempts where bot gives up and picks new #define DOOR_TOGGLE_INTERVAL 5.0 // Interval that loops throuh all doors to randomly toggle #define DOOR_TOGGLE_CHANCE 0.01 // Chance that every Timer_DoorToggles triggers a door to toggle state #define HIDER_SWAP_COOLDOWN 30.0 // Amount of seconds until they can swap @@ -157,11 +158,19 @@ public void OnPluginStart() { validSets = new ArrayList(ByteCountToCells(16)); mapConfigs = new StringMap(); - movePoints = new MovePoints(); - g_FadeUserMsgId = GetUserMessageId("Fade"); cvarStorage = new GameConVarStorage(); + + cvar_survivorLimit = new GameConVar("cvar_survivorLimit"); + cvar_separationMinRange = new GameConVar("sb_separation_danger_min_range"); + cvar_separationMaxRange = new GameConVar("sb_separation_danger_max_range"); + cvar_abmAutoHard = new GameConVar("abm_autohard"); + cvar_sbFixEnabled = new GameConVar("sb_fix_enabled"); + cvar_sbPushScale = new GameConVar("sb_pushscale"); + cvar_battlestationGiveUp = new GameConVar("sb_battlestation_give_up_range_from_human"); + cvar_sbMaxBattlestationRange = new GameConVar("sb_max_battlestation_range_from_human"); + cvar_enforceProximityRange = new GameConVar("enforce_proximity_range"); ConVar hGamemode = FindConVar("mp_gamemode"); hGamemode.AddChangeHook(Event_GamemodeChange); @@ -190,6 +199,7 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ SetCvars(cvarStorage); Game.Broadcast("Gamemode is starting"); HookEvent("round_start", Event_RoundStart); + HookEvent("round_end", Event_RoundEnd); HookEvent("player_death", Event_PlayerDeath); HookEvent("player_bot_replace", Event_PlayerToBot); HookEvent("player_ledge_grab", Event_LedgeGrab); @@ -198,6 +208,7 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ cvarStorage.Restore(); delete cvarStorage; UnhookEvent("round_start", Event_RoundStart); + UnhookEvent("round_end", Event_RoundEnd); UnhookEvent("player_death", Event_PlayerDeath); UnhookEvent("player_bot_replace", Event_PlayerToBot); UnhookEvent("player_ledge_grab", Event_LedgeGrab); @@ -261,7 +272,12 @@ void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { } void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { - CreateTimer(5.0, Timer_WaitForPlayers, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); + CreateTimer(2.5, Timer_WaitForPlayers, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); +} + +void Event_RoundEnd(Event event, const char[] name, bool dontBroadcast) { + // Skip the check, everyone's loaded in + firstCheckDone = true; } public void OnMapStart() { @@ -297,7 +313,7 @@ public void OnMapStart() { int seeker = Game.Seeker; if(seeker > -1) { currentSeeker = seeker; - Game.Debug("-Late load- Seeker: %N",currentSeeker); + Game.Debug("-Late load- Seeker: %N", currentSeeker); } for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { @@ -317,7 +333,6 @@ public void OnMapStart() { } public void ThinkPost(int entity) { static int iTeamNum[MAXPLAYERS+1]; - GetEntDataArray(entity, g_iTeamNum, iTeamNum, sizeof(iTeamNum)); for(int i = 1 ; i<= MaxClients; i++) { @@ -329,14 +344,6 @@ public void ThinkPost(int entity) { SetEntDataArray(entity, g_iTeamNum, iTeamNum, sizeof(iTeamNum)); } -public void OnMapEnd() { - for(int i = 1; i <= MaxClients; i++) { - if(moveTimers[i] != null) { - delete moveTimers[i]; - } - } -} - public void OnClientPutInServer(int client) { if(isEnabled && !IsFakeClient(client)) { ChangeClientTeam(client, 1); @@ -387,7 +394,7 @@ void InitGamemode() { Game.DebugConsole("InitGamemode(): activating"); ArrayList validPlayerIds = new ArrayList(); for(int i = 1; i <= MaxClients; i++) { - if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) { + if(IsClientConnected(i) && IsClientInGame(i)) { ChangeClientTeam(i, 2); activeBotLocations[i].attempts = 0; if(IsFakeClient(i)) { @@ -403,6 +410,7 @@ void InitGamemode() { if(!hasBeenSeeker[i] || ignoreSeekerBalance) validPlayerIds.Push(GetClientUserId(i)); } + Game.TeleportToSpawn(i); } } if(validPlayerIds.Length == 0) { @@ -507,15 +515,15 @@ Action Timer_WaitForStart(Handle h) { for(int i = 1; i <= MaxClients; i++) { if(i != currentSeeker && IsClientConnected(i) && IsClientInGame(i)) { TeleportEntity(i, seekerPos, NULL_VECTOR, NULL_VECTOR); - if(IsFakeClient(i)) { + if(IsFakeClient(i) && movePoints.Length > 0) { moveTimers[i] = CreateTimer(GetRandomFloat(BOT_MOVE_RANDOM_MIN_TIME, BOT_MOVE_RANDOM_MAX_TIME), Timer_BotMove, GetClientUserId(i), TIMER_REPEAT); - movePoints.GetArray(GetURandomInt() % movePoints.Length, activeBotLocations[i]); + movePoints.GetRandomPoint(activeBotLocations[i]); TeleportEntity(i, activeBotLocations[i].pos, activeBotLocations[i].ang, NULL_VECTOR); } } } - Game.Broadcast("Seeker will start in %.0f seconds", SEED_TIME); + Game.Broadcast("The Seeker (%N) will start in %.0f seconds", Game.Seeker, SEED_TIME); Game.State = State_Starting; Game.Tick = 0; Game.MapTime = RoundFloat(SEED_TIME); @@ -554,7 +562,7 @@ Action OnTakeDamageAlive(int victim, int& attacker, int& inflictor, float& damag if(attacker == currentSeeker) { damage = 100.0; ClearInventory(victim); - if(IsFakeClient(victim)) { + if(attacker > 0 && attacker <= MaxClients && IsFakeClient(victim)) { PrintToChat(attacker, "That was a bot! -%.0f health", cvar_seekerFailDamageAmount.FloatValue); SDKHooks_TakeDamage(attacker, 0, 0, cvar_seekerFailDamageAmount.FloatValue, DMG_DIRECT); }