Work on prophunt

This commit is contained in:
Jackz 2022-08-24 11:00:07 -05:00
parent f60250042a
commit 613802344b
No known key found for this signature in database
GPG key ID: E0BBD94CF657F603
7 changed files with 1243 additions and 0 deletions

BIN
plugins/l4d2_prophunt.smx Normal file

Binary file not shown.

View file

@ -0,0 +1,333 @@
#define GAMEMODE_PROP_NAME "phprop"
#define GAMEMODE_BLOCKER_NAME "phblocker"
public Action Command_PropHunt(int client, int args) {
if(!isEnabled) ReplyToCommand(client, "Warn: %s is not active", GAMEMODE_NAME);
if(args > 0) {
char subcmd[32];
GetCmdArg(1, subcmd, sizeof(subcmd));
if(StrEqual(subcmd, "r") || StrEqual(subcmd, "reload", false)) {
GetCurrentMap(g_currentMap, sizeof(g_currentMap));
char arg[4];
GetCmdArg(2, arg, sizeof(arg));
if(ReloadMapDB()) {
if(!LoadConfigForMap(g_currentMap)) {
ReplyToCommand(client, "Warn: Map has no config file");
}
Game.Cleanup(true);
if(arg[0] == 'f') {
InitGamemode();
}
SetupEntities(Game.Blockers, Game.Props, Game.Portals);
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 /gw set <set>)", g_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, g_currentSet, sizeof(g_currentSet));
for(int i = 0; i < validSets.Length; i++) {
validSets.GetString(i, set, sizeof(set));
if(StrEqual(set, g_currentSet)) {
if(!LoadConfigForMap(g_currentMap)) {
ReplyToCommand(client, "Warn: No config entry for %s", g_currentMap);
}
Game.Cleanup();
SetupEntities(Game.Blockers, Game.Props, Game.Portals);
PrintToChatAll("[PropHunt] Map set has been changed to \"%s\"", g_currentSet);
return Plugin_Handled;
}
}
ReplyToCommand(client, "Warning: Set was not found, use /gw r to force 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(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");
}
Game.Blockers = !Game.Blockers;
isUnknown = false;
}
if(doAll || StrEqual(type, "props", false)) {
if(Game.Props) {
EntFire(GAMEMODE_PROP_NAME, "Disable");
EntFire(GAMEMODE_PROP_NAME, "DisableCollision");
ReplyToCommand(client, "Disabled all custom gamemode props");
} else {
EntFire(GAMEMODE_PROP_NAME, "Enable");
EntFire(GAMEMODE_PROP_NAME, "EnableCollision");
ReplyToCommand(client, "Enabled all custom gamemode props");
}
Game.Props = !Game.Props;
isUnknown = false;
}
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));
if(StrEqual(arg, "all")) {
Game.Cleanup();
ReplyToCommand(client, "Cleaned up everything.");
} else if(StrEqual(arg, "props")) {
EntFire(GAMEMODE_PROP_NAME, "kill");
ReplyToCommand(client, "Removed all custom gamemode props");
} 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', or 'all'");
} else if(StrEqual(subcmd, "settime")) {
int prev = Game.MapTime;
static char arg[16];
GetCmdArg(2, arg, sizeof(arg));
int time = StringToInt(arg);
mapConfig.mapTime = time;
Game.MapTime = 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);
Game.Tick = tick;
ReplyToCommand(client, "Set tick time to %d", tick);
} else if(StrContains(subcmd, "map") >= 0) {
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(g_currentMap, map, false)) {
foundMap = true;
}
} while(!foundMap);
PrintToChatAll("%s Switching map to %s", GAMEMODE_PREFIX, map);
ChangeMap(map);
} else if(StrEqual(arg, "next", false)) {
if(args == 1) {
ReplyToCommand(client, "Specify the map to change on the next round: 'next <map>'");
} else {
char arg2[64];
GetCmdArg(3, arg2, sizeof(arg2));
if(IsMapValid(arg2)) {
strcopy(nextRoundMap, sizeof(nextRoundMap), arg2);
PrintToChatAll("%s Switching map next round to %s", GAMEMODE_PREFIX, 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 <map>'");
} 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 <list/random/force <mapname>/next <mapname>>");
}
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, "peekfix")) {
if(!PeekCam.Exists()) {
PeekCam.Target = client;
}
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i)) {
PeekCam.SetViewing(client, true);
PeekCam.SetViewing(client, false);
}
}
PeekCam.Destroy();
ReplyToCommand(client, "Killing active camera");
} 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;
}
Game.ForceSetSeeker(target_list[0]);
ReplyToCommand(client, "Set the current seeker to %N", target_list[0]);
} else {
ReplyToCommand(client, "The current seeker is: %N", Game.Seeker);
}
} else if(StrEqual(subcmd, "debug")) {
ReplyToCommand(client, "- Game Info -");
ReplyToCommand(client, "State: %d | Tick: %d", view_as<int>(Game.State), Game.Tick);
ReplyToCommand(client, "- Map Info -");
ReplyToCommand(client, "Map: %s (set %s)", g_currentMap, g_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, "Map Time: %d", mapConfig.mapTime);
ReplyToCommand(client, "Flow Bounds: (%f, %f)", movePoints.MinFlow, movePoints.MaxFlow);
} else if(StrEqual(subcmd, "test")) {
} else {
ReplyToCommand(client, "Unknown option. Leave blank for help");
}
return Plugin_Handled;
}
ReplyToCommand(client, " === [ %s Commands ] ===", GAMEMODE_NAME);
if(GetUserAdmin(client) != INVALID_ADMIN_ID) {
ReplyToCommand(client, "- Dev Commands -");
ReplyToCommand(client, "r/reload [force]: Reloads map config from file");
ReplyToCommand(client, "toggle <blockers/props/all>: Toggles all specified entities");
ReplyToCommand(client, "clear <props/blockers/all>: 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, "- Admin Commands -");
ReplyToCommand(client, "set [new set]: Change the prop set or view current");
ReplyToCommand(client, "setspawn: Sets the temporary spawnpoint for the map");
ReplyToCommand(client, "peekfix - Clear peek camera from all players");
ReplyToCommand(client, "seeker [new seeker]: Get the active seeker, or set a new one.");
ReplyToCommand(client, "- User Commands -");
}
ReplyToCommand(client, "stuck: Teleports you to spawn to unstuck yourself");
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) {
if(!isEnabled) return Plugin_Continue;
static float tpLoc[3];
FindSpawnPosition(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);
Game.SetupPlayer(client);
if(!ArePlayersJoining()) {
InitGamemode();
}
}
return Plugin_Handled;
}

View file

@ -0,0 +1,171 @@
#define FOLDER_PERMS ( FPERM_U_READ | FPERM_U_WRITE | FPERM_U_EXEC | FPERM_G_EXEC | FPERM_G_WRITE | FPERM_G_READ | FPERM_O_EXEC )
#include <prophunt/phgame>
#include <prophunt/phcmds>
#include <prophunt/phents>
#include <prophunt/phtimers>
static KeyValues kv;
StringMap mapConfigs;
bool ReloadMapDB() {
if(kv != null) {
delete kv;
}
kv = new KeyValues("prophunt");
char sPath[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sPath, sizeof(sPath), "data/prophunt");
CreateDirectory(sPath, FOLDER_PERMS);
Format(sPath, sizeof(sPath), "%s/config.cfg", sPath);
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;
}
static float DEFAULT_SCALE[3] = { 5.0, 5.0, 5.0 };
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();
static char buffer[64];
buffer[0] = '\0';
if(StrEqual(g_currentSet, "default") && kv.GetString("defaultset", buffer, sizeof(buffer)) && buffer[0] != '\0') {
strcopy(g_currentSet, sizeof(g_currentSet), buffer);
}
PrintToServer("[PropHunt] Loading config data for set %s on %s", g_currentSet, map);
if(kv.JumpToKey("ents")) {
kv.GotoFirstSubKey();
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", buffer, sizeof(buffer), "default");
if(validSets.FindString(buffer) == -1) {
validSets.PushString(buffer);
}
if(StrEqual(buffer, "default") || StrEqual(g_currentSet, buffer, false)) {
config.entities.PushArray(entCfg);
} else {
kv.GetSectionName(buffer, sizeof(buffer));
PrintToServer("Skipping %s", buffer);
}
} while (kv.GotoNextKey());
// JumpToKey and GotoFirstSubKey both traverse, i guess, go back
kv.GoBack();
kv.GoBack();
}
if(kv.JumpToKey("inputs")) {
kv.GotoFirstSubKey(false);
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(g_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(g_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("[PropHunt] Using provided custom spawnpoint for set %s at %0.1f, %0.1f, %0.1f", g_currentSet, config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]);
config.hasSpawnpoint = true;
}
mapTime = kv.GetNum("maptime", 0);
if(kv.JumpToKey("inputs")) {
kv.GotoFirstSubKey(false);
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();
}
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("[PropHunt] 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 (FindSpawnPosition(config.spawnpoint, false)) {
PrintToServer("[PropHunt] Using map spawnpoint at %0.1f, %0.1f, %0.1f", config.spawnpoint[0], config.spawnpoint[1], config.spawnpoint[2]);
config.hasSpawnpoint = true;
} else {
PrintToServer("[PropHunt] 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("[PropHunt] 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("[PropHunt] %s has no config entry", map);
return false;
}
}

View file

@ -0,0 +1,128 @@
#define ENT_PROP_NAME "gwprop"
#define ENT_BLOCKER_NAME "gwblocker"
#define ENT_PORTAL_NAME "gwportal"
#define ENT_ENV_NAME "gwenv"
#include <gamemodes/ents>
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("EntFire: %s \"%s\"", name, input);
#endif
int len = SplitString(input, " ", cmd, sizeof(cmd));
if(len > -1) SetVariantString(input[len]);
int hammerId = name[0] == '!' ? StringToInt(name[1]) : 0;
for(int i = MaxClients + 1; i <= 4096; i++) {
if(IsValidEntity(i) && (IsValidEdict(i) || EntIndexToEntRef(i) != -1)) {
if(hammerId > 0) {
if(hammerId == Entity_GetHammerId(i)) {
if(len > -1) AcceptEntityInput(i, cmd);
else AcceptEntityInput(i, input);
}
} else {
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(i, 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 defined DEBUG_BLOCKERS
if(mapConfig.hasSpawnpoint) {
PrecacheModel("survivors/survivor_teenangst.mdl", true);
int dummy = CreateDummy("models/survivors/survivor_teenangst.mdl", "idle", mapConfig.spawnpoint, NULL_VECTOR);
SetEntProp(dummy, Prop_Data, "m_nSolidType", 0);
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);
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) {
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) {
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) {
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) {
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) {
Game.Warn("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, "hitby_tankpunch", config.origin, config.rotation) == -1) {
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(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) {
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]);
}
}
}
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);
}
}
}
}

View file

@ -0,0 +1,390 @@
static int mapChangeMsgTicks = 5;
int GetColorInt(int r, int g, int b) {
int color = r;
color += 256 * g;
color += 65536 * b;
return color;
}
Action Timer_ChangeMap(Handle h) {
PrintToChatAll("Changing map to %s in %d seconds", nextRoundMap, mapChangeMsgTicks);
if(mapChangeMsgTicks-- == 0) {
ForceChangeLevel(nextRoundMap, "GuessWhoMapSelect");
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 FindSpawnPosition(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;
}
static char buffer[128];
bool isSeeker[MAXPLAYERS+1];
methodmap PropHuntGame < BaseGame {
property int Seekers {
public get() {
int count = 0;
for(int i = 1; i <= MaxClients; i++) {
if(isSeeker[i]) count++;
}
return count;
}
}
public bool IsSeeker(int client) {
return isSeeker[client];
}
public void SetSeeker(int client, bool value) {
isSeeker[client] = value;
}
public void ClearSeekers() {
for(int i = 1; i <= MaxClients; i++) {
isSeeker[i] = false;
}
}
property int Tick {
public get() {
if(!isEnabled) return -1;
L4D2_GetVScriptOutput("g_ModeScript.MutationState.Tick", buffer, sizeof(buffer));
int value = -1;
if(StringToIntEx(buffer, value) > 0) {
return value;
} else {
return -1;
}
}
public set(int tick) {
Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.Tick = %d", tick);
L4D2_ExecVScriptCode(buffer);
}
}
property GameState State {
public get() {
if(!isEnabled) return State_Unknown;
L4D2_GetVScriptOutput("g_ModeScript.MutationState.State", buffer, sizeof(buffer));
int stage = 0;
if(StringToIntEx(buffer, stage) > 0) {
return view_as<GameState>(stage);
} else {
return State_Unknown;
}
}
public set(GameState state) {
if(isEnabled) {
Format(buffer, sizeof(buffer), "g_ModeScript.MutationState.State = %d", view_as<int>(state));
L4D2_ExecVScriptCode(buffer);
}
}
}
property int MapTime {
public get() {
L4D2_GetVScriptOutput("g_ModeScript.MutationState.MaxTime", buffer, sizeof(buffer));
return StringToInt(buffer);
}
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);
}
}
}
public void Start() {
}
public void End(GameState state) {
this.State = state;
CreateTimer(5.0, Timer_ResetAll);
}
public void Cleanup(bool noClearInv = false) {
DeleteCustomEnts();
}
public int _FindSeeker() {
if(!isEnabled) return -1;
L4D2_GetVScriptOutput("g_ModeScript.MutationState.CurrentSeeker && \"GetPlayerUserId\" in g_ModeScript.MutationState.CurrentSeeker ? g_ModeScript.MutationState.CurrentSeeker.GetPlayerUserId() : -1", buffer, sizeof(buffer));
int uid = StringToInt(buffer);
if(uid > 0) {
return GetClientOfUserId(uid);
} else {
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);
if(entity > -1 && GetEntityClassname(entity, buffer, sizeof(buffer)) && StrEqual(buffer, "melee")) {
GetEntPropString(entity, Prop_Data, "m_strMapSetScriptName", buffer, sizeof(buffer));
if(StrEqual(buffer, "smg")) {
return i;
}
}
}
}
}
Game.Debug("All attempts to find a seeker failed");
return -1;
}
public void ForceSetSeeker(int client, bool ignoreBalance = false) {
ignoreSeekerBalance = true;
this.Seeker = client;
}
public bool TeleportToSpawn(int client) {
if(mapConfig.hasSpawnpoint) {
TeleportEntity(client, mapConfig.spawnpoint, NULL_VECTOR, NULL_VECTOR);
return true;
} else {
float pos[3];
if(FindSpawnPosition(pos)) {
return false;
}
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
}
return false;
}
public void TeleportAllToStart() {
if(mapConfig.hasSpawnpoint) {
PrintToServer("[GuessWho] Teleporting all players to provided spawnpoint (%f %f %f)", mapConfig.spawnpoint[0], mapConfig.spawnpoint[1], mapConfig.spawnpoint[2]);
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i)) {
this.TeleportToSpawn(i);
}
}
} else {
PrintToServer("[GuessWho] Warn: No spawnpoint found (provided or map spawn)");
}
}
// Ignores seeker
property int AlivePlayers {
public get() {
int amount = 0;
for(int i = 1; i <= MaxClients; i++) {
if(!this.IsSeeker(i) && IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i) && !IsFakeClient(i)) {
amount++;
}
}
return amount;
}
}
public void SetupInventory(int client) {
ClearInventory(client);
if(this.IsSeeker(client)) {
CheatCommand(client, "give", "smg");
}
}
public void SetupPlayer(int client) {
this.SetupInventory(client);
SDKHook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
}
public void UnsetupPlayer(int client) {
SDKUnhook(client, SDKHook_OnTakeDamageAlive, OnTakeDamageAlive);
}
}
stock bool ArePlayersJoining() {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && !IsClientInGame(i)) {
return true;
}
}
return 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;
}
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(const float fVecOrigin[3], const 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(const 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));
}
stock void LookAtClient(int iClient, int iTarget) {
static float fTargetPos[3];
static float fTargetAngles[3];
static float fClientPos[3];
static float fFinalPos[3];
GetClientEyePosition(iClient, 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 LookAtPoint(int client, const float targetPos[3]) {
static float targetAngles[3];
static float clientPos[3];
static float fFinalPos[3];
GetClientEyePosition(client, clientPos);
GetClientEyeAngles(client, targetAngles);
float fVecFinal[3];
AddInFrontOf(targetPos, targetAngles, 7.0, fVecFinal);
MakeVectorFromPoints(clientPos, fVecFinal, fFinalPos);
GetVectorAngles(fFinalPos, fFinalPos);
TeleportEntity(client, NULL_VECTOR, fFinalPos, NULL_VECTOR);
}
void SetPlayerBlind(int target, int amount) {
int targets[1];
targets[0] = target;
int duration = 1536;
int holdtime = 1536;
int flags = (amount == 0) ? (0x0001 | 0x0010) : (0x0002 | 0x0008);
int color[4] = { 0, 0, 0, 0 };
color[3] = amount;
Handle message = StartMessageEx(g_FadeUserMsgId, targets, 1);
BfWrite bf = UserMessageToBfWrite(message);
bf.WriteShort(duration);
bf.WriteShort(holdtime);
bf.WriteShort(flags);
bf.WriteByte(color[0]);
bf.WriteByte(color[1]);
bf.WriteByte(color[2]);
bf.WriteByte(color[3]);
EndMessage();
}
#define HIDER_DISTANCE_MAX_SIZE 10
#define MAX_AUTO_VOCALIZATIONS 9
static char AUTO_VOCALIZATIONS[MAX_AUTO_VOCALIZATIONS][] = {
"PlayerLaugh",
"PlayerSpotPill",
"Playerlookout",
"EatPills",
"ReviveMeInterrupted",
"PlayerIncapacitated",
"PlayerNiceShot",
"ResponseSoftDispleasureSwear",
"PlayerAreaClear"
};
enum struct HiderDistQueue {
int index;
float list[HIDER_DISTANCE_MAX_SIZE];
int lastVocalize;
void AddPos(const float pos[3]) {
this.list[this.index] = GetVectorDistance(seekerPos, pos);
if(++this.index == HIDER_DISTANCE_MAX_SIZE) {
this.index = 0;
}
}
void Clear() {
for(int i = 0; i < HIDER_DISTANCE_MAX_SIZE; i++) {
this.list[i] = 0.0;
}
}
float GetAverage() {
float sum = 0.0;
for(int i = 0; i < HIDER_DISTANCE_MAX_SIZE; i++) {
sum += this.list[i];
}
return sum / float(HIDER_DISTANCE_MAX_SIZE);
}
void Check(int i) {
if(this.GetAverage() > HIDER_MIN_AVG_DISTANCE_AUTO_VOCALIZE) {
int time = GetTime();
if(time - this.lastVocalize > HIDER_AUTO_VOCALIZE_GRACE_TIME) {
this.lastVocalize = time;
int index = GetRandomInt(0, MAX_AUTO_VOCALIZATIONS - 1);
PerformScene(i, AUTO_VOCALIZATIONS[index]);
}
}
}
}
HiderDistQueue distQueue[MAXPLAYERS+1];

View file

@ -0,0 +1,70 @@
Action Timer_RecordPoints(Handle h, int i) {
if(GetEntityFlags(i) & FL_ONGROUND && IsPlayerAlive(i)) {
LocationMeta meta;
GetClientAbsOrigin(i, meta.pos);
GetClientEyeAngles(i, meta.ang);
if(meta.pos[0] != vecLastLocation[i][0] || meta.pos[1] != vecLastLocation[i][1] || meta.pos[2] != vecLastLocation[i][2]) {
if(movePoints.AddPoint(meta)) {
recordTimer = null;
return Plugin_Stop;
}
Effect_DrawBeamBoxRotatableToClient(i, meta.pos, DEBUG_POINT_VIEW_MIN, DEBUG_POINT_VIEW_MAX, NULL_VECTOR, g_iLaserIndex, 0, 0, 0, 150.0, 0.1, 0.1, 0, 0.0, {0, 0, 255, 64}, 0);
vecLastLocation[i] = meta.pos;
}
}
PrintHintText(i, "Points: %d / %d", movePoints.Length, MAX_VALID_LOCATIONS);
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];
static char classname[16];
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) == 2 && IsPlayerAlive(i)) {
GetClientAbsOrigin(i, pos);
distQueue[i].AddPos(pos);
distQueue[i].Check(i);
int activeWeapon = GetEntPropEnt(i, Prop_Send, "m_hActiveWeapon");
if(IsValidEntity(activeWeapon)) {
GetEntityClassname(activeWeapon, classname, sizeof(classname));
if(i == currentSeeker) {
if(StrEqual(classname, "weapon_melee")) continue;
Game.SetupInventory(i);
} else if(StrEqual(classname, "weapon_gnome")) continue;
}
Game.SetupInventory(i);
}
}
Game.CleanupGnomes(true);
return Plugin_Continue;
}
Action Timer_ResetAll(Handle h) {
for(int i = 1; i <= MaxClients; i++) {
if(IsClientConnected(i) && IsClientInGame(i) && GetClientTeam(i) == 2) {
ForcePlayerSuicide(i);
}
}
return Plugin_Handled;
}

151
scripting/l4d2_prophunt.sp Normal file
View file

@ -0,0 +1,151 @@
#pragma semicolon 1
#pragma newdecls required
//#define DEBUG
#define PLUGIN_VERSION "1.0"
#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <gamemodes/base>
#include <gamemodes/ents>
enum GameState {
State_Unknown = 0,
State_Hiding,
State_Active,
State_PropsWin,
State_SeekerWin,
}
#define MAX_VALID_MODELS 2
static char VALID_MODELS[MAX_VALID_MODELS][] = {
"models/props_crates/static_crate_40.mdl",
"models/props_junk/gnome.mdl"
};
static float EMPTY_ANG[3];
#define TRANSPARENT "255 255 255 0"
#define WHITE "255 255 255 255"
enum struct PropData {
int prop;
bool rotationLock;
}
PropData propData[MAXPLAYERS+1];
public Plugin myinfo =
{
name = "Prophunt",
author = "jackzmc",
description = "",
version = PLUGIN_VERSION,
url = "https://github.com/Jackzmc/sourcemod-plugins"
};
public void OnPluginStart() {
EngineVersion g_Game = GetEngineVersion();
if(g_Game != Engine_Left4Dead2) {
SetFailState("This plugin is for L4D2 only.");
}
RegConsoleCmd("sm_game", Command_Test);
}
public void OnMapStart() {
for(int i = 0; i < MAX_VALID_MODELS; i++) {
PrecacheModel(VALID_MODELS[i]);
}
}
void ResetPlayerData(int client) {
if(propData[client].prop > 0) {
AcceptEntityInput(propData[client].prop, "Kill");
propData[client].prop = 0;
}
propData[client].rotationLock = false;
}
public void OnMapEnd() {
for(int i = 1; i <= MaxClients; i++) {
ResetPlayerData(i);
if(IsClientConnected(i) && IsClientInGame(i)) {
DispatchKeyValue(i, "rendercolor", WHITE);
}
}
}
public void OnClientDisconnect(int client) {
ResetPlayerData(client);
}
public Action Command_Test(int client, int args) {
int prop = CreatePropInternal(VALID_MODELS[0]);
if(prop <= 0) {
ReplyToCommand(client, "Failed to spawn prop");
return Plugin_Handled;
}
float pos[3];
propData[client].prop = prop;
DispatchKeyValue(client, "rendercolor", TRANSPARENT);
// SetParent(prop, client);
// SetParentAttachment(prop, "eyes", true);
// TeleportEntity(prop, pos, EMPTY_ANG, NULL_VECTOR);
// SetParentAttachment(prop, "eyes", true);
SDKHook(client, SDKHook_SetTransmit, OnPlayerTransmit);
ReplyToCommand(client, "Game!");
return Plugin_Handled;
}
Action OnPlayerTransmit(int entity, int client) {
return entity == client ? Plugin_Continue : Plugin_Stop;
}
int CreatePropInternal(const char[] model) {
int entity = CreateEntityByName("prop_dynamic");
DispatchKeyValue(entity, "model", model);
DispatchKeyValue(entity, "disableshadows", "1");
DispatchKeyValue(entity, "targetname", "phprop");
DispatchSpawn(entity);
SetEntProp(entity, Prop_Send, "m_nSolidType", 6);
SetEntProp(entity, Prop_Send, "m_CollisionGroup", 1);
SetEntProp(entity, Prop_Send, "movetype", MOVETYPE_NONE);
return entity;
}
public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3], float angles[3], int& weapon, int& subtype, int& cmdnum, int& tickcount, int& seed, int mouse[2]) {
if(propData[client].prop > 0) {
static float pos[3], ang[3];
GetClientAbsOrigin(client, pos);
TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR);
if(propData[client].rotationLock)
TeleportEntity(propData[client].prop, NULL_VECTOR, angles, NULL_VECTOR);
else {
ang[0] = 0.0;
ang[1] = angles[1];
ang[2] = 0.0;
TeleportEntity(propData[client].prop, pos, ang, NULL_VECTOR);
}
}
return Plugin_Continue;
}
Action OnTakeDamageAlive(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) {
/*if(attacker == currentSeeker) {
damage = 100.0;
ClearInventory(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);
}
return Plugin_Changed;
} else if(attacker > 0 && attacker <= MaxClients) {
damage = 0.0;
return Plugin_Changed;
} else {
return Plugin_Continue;
}*/
return Plugin_Continue;
}