diff --git a/data/prophunt/maps.cfg b/data/prophunt/maps.cfg new file mode 100644 index 0000000..1324fa5 --- /dev/null +++ b/data/prophunt/maps.cfg @@ -0,0 +1,28 @@ +"prophunt" +{ + "c1m1_hotel" + { + "props" + { + "SOFA_1" + { + "model" "props_urban/hotel_chair001.mdl" + "origin" "1532.456787 4609 1184" + "rotation" "0 323.5 0" + } + "SOFA_2" + { + "origin" "1649 4529 1184" + "rotation" "0 170 0" + "model" "props_urban/hotel_chair001.mdl" + } + } + } + "c8m1_apartments" + { + "props" + { + + } + } +} \ No newline at end of file diff --git a/data/prophunt/props.cfg b/data/prophunt/props.cfg new file mode 100644 index 0000000..c524e38 --- /dev/null +++ b/data/prophunt/props.cfg @@ -0,0 +1,23 @@ +"props" +{ + "props/cs_office/Fire_Extinguisher.mdl" "5" + "props_urban/ashtray_stand001.mdl" "5" + "props_furniture/hotel_chair.mdl" "10" + "props_urban/hotel_chair001.mdl" "20" + "props_interiors/tv.mdl" "10" + "props_downtown/ironing_board.mdl" "15" + "props_urban/hotel_lamp001.mdl" "10" + "props_windows/hotel_window_glass001.mdl" "30" + "props/cs_office/shelves_metal1.mdl" "50" + "props/cs_office/shelves_metal.mdl" "50" + "props_downtown/dresser.mdl" "50" + "props_interiors_Hotel_Cart.mdl" "40" + "props_downtown/side_table.mdl" "10" + "props_interiors/ac_wallunit.mdl" "30" + "props_downtown/bed_motel01.mdl" "50" + "props_interiors/coffee_table_oval.mdl" "20" + "props_downtown/mini_fridge.mdl" "10" + "props_interiors/fridge_mini.mdl" "10" + "props/cs_office/vending_machine.mdl" "50" + "props_interiors/toilet.mdl" "30" +} \ No newline at end of file diff --git a/plugins/l4d2_prophunt.smx b/plugins/l4d2_prophunt.smx index 2265ef1..0546ac9 100644 Binary files a/plugins/l4d2_prophunt.smx and b/plugins/l4d2_prophunt.smx differ diff --git a/scripting/include/prophunt/phcmds.inc b/scripting/include/prophunt/phcmds.inc index a26d0b0..3fd5e32 100644 --- a/scripting/include/prophunt/phcmds.inc +++ b/scripting/include/prophunt/phcmds.inc @@ -12,6 +12,7 @@ public Action Command_PropHunt(int client, int args) { GetCurrentMap(g_currentMap, sizeof(g_currentMap)); char arg[4]; GetCmdArg(2, arg, sizeof(arg)); + ReloadPropDB(); if(ReloadMapDB()) { if(!LoadConfigForMap(g_currentMap)) { ReplyToCommand(client, "Warn: Map has no config file"); @@ -20,7 +21,7 @@ public Action Command_PropHunt(int client, int args) { if(arg[0] == 'f') { InitGamemode(); } - SetupEntities(Game.Blockers, Game.Props, Game.Portals); + SetupEntities(Game.Blockers, Game.Props); ReplyToCommand(client, "Reloaded map from config"); } else { ReplyToCommand(client, "Error occurred while reloading map file"); @@ -46,7 +47,7 @@ public Action Command_PropHunt(int client, int args) { ReplyToCommand(client, "Warn: No config entry for %s", g_currentMap); } Game.Cleanup(); - SetupEntities(Game.Blockers, Game.Props, Game.Portals); + SetupEntities(Game.Blockers, Game.Props); PrintToChatAll("[PropHunt] Map set has been changed to \"%s\"", g_currentSet); return Plugin_Handled; } diff --git a/scripting/include/prophunt/phcore.inc b/scripting/include/prophunt/phcore.inc index c63b043..3302f47 100644 --- a/scripting/include/prophunt/phcore.inc +++ b/scripting/include/prophunt/phcore.inc @@ -6,42 +6,73 @@ #include -static KeyValues kv; +static KeyValues mapsKv; StringMap mapConfigs; bool ReloadMapDB() { - if(kv != null) { - delete kv; + if(mapsKv != null) { + delete mapsKv; } - kv = new KeyValues("prophunt"); + mapsKv = 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); + Format(sPath, sizeof(sPath), "%s/maps.cfg", sPath); - if(!FileExists(sPath) || !kv.ImportFromFile(sPath)) { - delete kv; + if(!FileExists(sPath) || !mapsKv.ImportFromFile(sPath)) { + delete mapsKv; return false; } validMaps.Clear(); char map[64]; - kv.GotoFirstSubKey(true); + mapsKv.GotoFirstSubKey(true); do { - kv.GetSectionName(map, sizeof(map)); + mapsKv.GetSectionName(map, sizeof(map)); validMaps.PushString(map); - } while(kv.GotoNextKey(true)); - kv.GoBack(); + } while(mapsKv.GotoNextKey(true)); + mapsKv.GoBack(); + + PrintToServer("[PropHunt] Loaded %d map configs", validMaps.Length); + return true; +} + +bool ReloadPropDB() { + if(propHealths != null) + delete propHealths; + propHealths = new StringMap(); + KeyValues propKv = new KeyValues("props"); + + char sPath[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, sPath, sizeof(sPath), "data/prophunt"); + CreateDirectory(sPath, FOLDER_PERMS); + Format(sPath, sizeof(sPath), "%s/props.cfg", sPath); + + if(!FileExists(sPath) || !propKv.ImportFromFile(sPath)) { + delete propKv; + return false; + } + + char model[64]; + propKv.GotoFirstSubKey(false); + do { + propKv.GetSectionName(model, sizeof(model)); + propHealths.SetValue(model, propKv.GetNum(NULL_STRING)); + } while(propKv.GotoNextKey(false)); + + PrintToServer("[PropHunt] Loaded %d models", propHealths.Size); + + delete propKv; return true; } static float DEFAULT_SCALE[3] = { 5.0, 5.0, 5.0 }; bool LoadConfigForMap(const char[] map) { - kv.Rewind(); - if (kv.JumpToKey(map)) { + mapsKv.Rewind(); + if (mapsKv.JumpToKey(map)) { MapConfig config; config.entities = new ArrayList(sizeof(EntityConfig)); config.inputs = new ArrayList(ByteCountToCells(64)); @@ -49,24 +80,24 @@ bool LoadConfigForMap(const char[] map) { static char buffer[64]; buffer[0] = '\0'; - if(StrEqual(g_currentSet, "default") && kv.GetString("defaultset", buffer, sizeof(buffer)) && buffer[0] != '\0') { + if(StrEqual(g_currentSet, "default") && mapsKv.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(); + if(mapsKv.JumpToKey("props")) { + mapsKv.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), ""); + mapsKv.GetVector("origin", entCfg.origin, NULL_VECTOR); + mapsKv.GetVector("rotation", entCfg.rotation, NULL_VECTOR); + mapsKv.GetString("type", entCfg.type, sizeof(entCfg.type), "prop_dynamic"); + mapsKv.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"); + mapsKv.GetVector("scale", entCfg.scale, DEFAULT_SCALE); + mapsKv.GetVector("offset", entCfg.offset, NULL_VECTOR); + mapsKv.GetString("set", buffer, sizeof(buffer), "default"); if(validSets.FindString(buffer) == -1) { validSets.PushString(buffer); } @@ -74,68 +105,68 @@ bool LoadConfigForMap(const char[] map) { config.entities.PushArray(entCfg); } else { - kv.GetSectionName(buffer, sizeof(buffer)); + mapsKv.GetSectionName(buffer, sizeof(buffer)); PrintToServer("Skipping %s", buffer); } - } while (kv.GotoNextKey()); + } while (mapsKv.GotoNextKey()); // JumpToKey and GotoFirstSubKey both traverse, i guess, go back - kv.GoBack(); - kv.GoBack(); + mapsKv.GoBack(); + mapsKv.GoBack(); } - if(kv.JumpToKey("inputs")) { - kv.GotoFirstSubKey(false); + if(mapsKv.JumpToKey("inputs")) { + mapsKv.GotoFirstSubKey(false); do { - kv.GetSectionName(buffer, sizeof(buffer)); + mapsKv.GetSectionName(buffer, sizeof(buffer)); config.inputs.PushString(buffer); - kv.GetString(NULL_STRING, buffer, sizeof(buffer)); + mapsKv.GetString(NULL_STRING, buffer, sizeof(buffer)); config.inputs.PushString(buffer); - } while (kv.GotoNextKey(false)); - kv.GoBack(); - kv.GoBack(); + } while (mapsKv.GotoNextKey(false)); + mapsKv.GoBack(); + mapsKv.GoBack(); } int mapTime; config.hasSpawnpoint = false; config.canClimb = true; config.pressButtons = true; - if(!StrEqual(g_currentSet, "default") && kv.JumpToKey("sets")) { + if(!StrEqual(g_currentSet, "default") && mapsKv.JumpToKey("sets")) { char set[16]; - kv.GotoFirstSubKey(true); + mapsKv.GotoFirstSubKey(true); do { - kv.GetSectionName(set, sizeof(set)); + mapsKv.GetSectionName(set, sizeof(set)); if(validSets.FindString(set) == -1) { validSets.PushString(set); } if(StrEqual(g_currentSet, set, false)) { - kv.GetVector("spawnpoint", config.spawnpoint); + mapsKv.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); + mapTime = mapsKv.GetNum("maptime", 0); + if(mapsKv.JumpToKey("inputs")) { + mapsKv.GotoFirstSubKey(false); do { - kv.GetSectionName(buffer, sizeof(buffer)); + mapsKv.GetSectionName(buffer, sizeof(buffer)); config.inputs.PushString(buffer); - kv.GetString(NULL_STRING, buffer, sizeof(buffer)); + mapsKv.GetString(NULL_STRING, buffer, sizeof(buffer)); config.inputs.PushString(buffer); - } while (kv.GotoNextKey(false)); - kv.GoBack(); - kv.GoBack(); + } while (mapsKv.GotoNextKey(false)); + mapsKv.GoBack(); + mapsKv.GoBack(); } break; } - } while(kv.GotoNextKey(true)); - kv.GoBack(); - kv.GoBack(); + } while(mapsKv.GotoNextKey(true)); + mapsKv.GoBack(); + mapsKv.GoBack(); } if(!config.hasSpawnpoint) { - kv.GetVector("spawnpoint", config.spawnpoint); + mapsKv.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; @@ -150,7 +181,7 @@ bool LoadConfigForMap(const char[] map) { // Use default maptime if exists if(mapTime == 0) - mapTime = kv.GetNum("maptime", 0); + mapTime = mapsKv.GetNum("maptime", 0); if(mapTime > 0) { config.mapTime = mapTime; PrintToServer("[PropHunt] Map time overwritten to %d seconds", mapTime); diff --git a/scripting/include/prophunt/phents.inc b/scripting/include/prophunt/phents.inc index e46bcee..05b7b51 100644 --- a/scripting/include/prophunt/phents.inc +++ b/scripting/include/prophunt/phents.inc @@ -52,7 +52,7 @@ stock void EntFire(const char[] name, const char[] input) { -void SetupEntities(bool blockers = true, bool props = true, bool portals = true) { +void SetupEntities(bool blockers = true, bool props = true) { #if defined DEBUG_BLOCKERS if(mapConfig.hasSpawnpoint) { PrecacheModel("survivors/survivor_teenangst.mdl", true); @@ -64,7 +64,7 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) 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); + PrintToServer("[PropHunt] Deploying %d custom entities (Set: %s) (blockers:%b props:%b)", mapConfig.entities.Length, g_currentSet, blockers, props); for(int i = 0; i < mapConfig.entities.Length; i++) { EntityConfig config; mapConfig.entities.GetArray(i, config); @@ -75,34 +75,6 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) 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]); @@ -125,9 +97,3 @@ void SetupEntities(bool blockers = true, bool props = true, bool portals = true) } } } - - -stock int Entity_GetHammerId(int entity) -{ - return GetEntProp(entity, Prop_Data, "m_iHammerID"); -} \ No newline at end of file diff --git a/scripting/include/prophunt/phgame.inc b/scripting/include/prophunt/phgame.inc index d58f3b1..4c2da18 100644 --- a/scripting/include/prophunt/phgame.inc +++ b/scripting/include/prophunt/phgame.inc @@ -197,16 +197,44 @@ methodmap PropHuntGame < BaseGame { } } - public void SetupProp(int client) { - PrintToChatAll("SetupProp"); - int propIndex = GetURandomInt() % MAX_VALID_MODELS; + public void SetupProp(int client, int prop) { + if(!IsValidEntity(prop)) { + ThrowError("Invalid prop (id %d)", prop); + return; + } + if(propData[client].prop > 0 && IsValidEntity(propData[client].prop)) { + AcceptEntityInput(propData[client].prop, "Kill"); + } + static char model[64]; + GetEntPropString(prop, Prop_Data, "m_ModelName", model, sizeof(model)); + PrintToConsole(client, "Setup prop model: %s", model); - propData[client].prop = CreatePropInternal(VALID_MODELS[propIndex]); + int health = 1; + if(!propHealths.GetValue(model[7], health)) { + PrintToServer("[PropHunt] Missing health for model: %s", model[7]); + } + float mins[3]; + GetEntPropVector(prop, Prop_Data, "m_vecMins", mins); + propData[client].verticalOffset = -mins[2]; + SetEntProp(client, Prop_Send, "m_iHealth", health); + propData[client].prop = prop; DispatchKeyValue(client, "rendercolor", TRANSPARENT); SDKHook(client, SDKHook_SetTransmit, OnPlayerTransmit); } - public void SetupSeeker(int client) { + public void SetupRandomProp(int client) { + int propIndex = GetURandomInt() % MAX_VALID_MODELS; + int prop = CreatePropInternal(VALID_MODELS[propIndex]); + this.SetupProp(client, prop); + } + + public void SetupPropTeam(int client) { + DispatchKeyValue(client, "rendercolor", TRANSPARENT); + SDKHook(client, SDKHook_SetTransmit, OnPlayerTransmit); + this.SetupRandomProp(client); + } + + public void SetupSeekerTeam(int client) { CheatCommand(client, "give", "smg"); } diff --git a/scripting/include/prophunt/phtimers.inc b/scripting/include/prophunt/phtimers.inc index ff1e165..e489886 100644 --- a/scripting/include/prophunt/phtimers.inc +++ b/scripting/include/prophunt/phtimers.inc @@ -23,7 +23,7 @@ Action Timer_StartGame(Handle h) { for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i)) { if(Game.IsSeeker(i)) { - Game.SetupSeeker(i); + Game.SetupSeekerTeam(i); } SetPlayerBlind(i, 0); SetEntPropFloat(i, Prop_Send, "m_flLaggedMovementValue", 1.0); diff --git a/scripting/l4d2_prophunt.sp b/scripting/l4d2_prophunt.sp index 3147d3a..4c7964c 100644 --- a/scripting/l4d2_prophunt.sp +++ b/scripting/l4d2_prophunt.sp @@ -11,6 +11,7 @@ #include #include #include +#include enum GameState { @@ -27,11 +28,14 @@ char VALID_MODELS[MAX_VALID_MODELS][] = { "models/props_junk/gnome.mdl" }; +#define SOUND_SWITCH_MODEL "buttons/button22.wav" + #define TRANSPARENT "255 255 255 0" #define WHITE "255 255 255 255" enum struct PropData { int prop; + float verticalOffset; bool rotationLock; } @@ -46,8 +50,10 @@ bool isStarting, firstCheckDone; Handle timesUpTimer; Handle waitTimer; +StringMap propHealths; PropHuntGame Game; + #include #include @@ -65,6 +71,10 @@ public void OnPluginStart() { if(g_Game != Engine_Left4Dead2) { SetFailState("This plugin is for L4D2 only."); } + validMaps = new ArrayList(ByteCountToCells(64)); + validSets = new ArrayList(ByteCountToCells(16)); + mapConfigs = new StringMap(); + Game.Init("PropHunt"); g_FadeUserMsgId = GetUserMessageId("Fade"); @@ -106,7 +116,7 @@ public void OnClientPutInServer(int client) { public void OnClientDisconnect(int client) { - if(!isEnabled) return; + if(!isEnabled || IsFakeClient(client)) return; ResetPlayerData(client); if(Game.IsSeeker(client)) { if(Game.SeekersAlive == 0) { @@ -139,21 +149,25 @@ public void Event_GamemodeChange(ConVar cvar, const char[] oldValue, const char[ } public void OnMapStart() { + if(!isEnabled) return; for(int i = 0; i < MAX_VALID_MODELS; i++) { PrecacheModel(VALID_MODELS[i]); } + PrecacheSound(SOUND_SWITCH_MODEL); isStarting = false; - if(!isEnabled) return; char map[128]; GetCurrentMap(map, sizeof(map)); if(!StrEqual(g_currentMap, map)) { firstCheckDone = false; strcopy(g_currentSet, sizeof(g_currentSet), "default"); + ReloadPropDB(); ReloadMapDB(); strcopy(g_currentMap, sizeof(g_currentMap), map); } + g_iLaserIndex = PrecacheModel("materials/sprites/laserbeam.vmt", true); + if(lateLoaded) { for(int i = 1; i <= MaxClients; i++) { @@ -163,6 +177,7 @@ public void OnMapStart() { } InitGamemode(); } + Game.State = State_Unknown; } @@ -214,8 +229,7 @@ void InitGamemode() { Game.State = State_Hiding; for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && IsClientInGame(i) && !Game.IsSeeker(i)) { - PrintToChatAll("setting up non seeker %N", i); - Game.SetupProp(i); + Game.SetupPropTeam(i); } } CreateTimer(float(BLIND_TIME), Timer_StartGame); @@ -224,6 +238,15 @@ void InitGamemode() { void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { int client = GetClientOfUserId(event.GetInt("userid")); int attacker = GetClientOfUserId(event.GetInt("attacker")); + if(!Game.IsSeeker(client)) { + if(attacker > 0) + PrintToChatAll("%N was killed by %N", client, attacker); + else + PrintToChatAll("%N died", client); + } + + ResetPlayerData(client); + if(client > 0 && Game.State == State_Active) { if(Game.SeekersAlive == 0) { Game.Broadcast("All seekers have perished. Hiders win!"); @@ -236,7 +259,7 @@ void Event_PlayerDeath(Event event, const char[] name, bool dontBroadcast) { } void ResetPlayerData(int client) { - if(propData[client].prop > 0) { + if(propData[client].prop > 0 && IsValidEntity(propData[client].prop)) { AcceptEntityInput(propData[client].prop, "Kill"); propData[client].prop = 0; } @@ -275,7 +298,7 @@ Action OnPlayerTransmit(int entity, int client) { } int CreatePropInternal(const char[] model) { - int entity = CreateEntityByName("prop_dynamic"); + int entity = CreateEntityByName("prop_physics_override"); DispatchKeyValue(entity, "model", model); DispatchKeyValue(entity, "disableshadows", "1"); DispatchKeyValue(entity, "targetname", "phprop"); @@ -286,18 +309,74 @@ int CreatePropInternal(const char[] model) { return entity; } +stock int CloneProp(int prop) { + char model[64]; + GetEntPropString(prop, Prop_Data, "m_ModelName", model, sizeof(model)); + return CreatePropInternal(model); +} + +stock void GlowNearbyProps(int client, float range, bool optimizeRange = false) { + int entity = INVALID_ENT_REFERENCE; + static float pos[3], clientPos[3]; + GetClientAbsOrigin(client, clientPos); + static char model[64]; + while ((entity = FindEntityByClassname(entity, "prop_dynamic")) != -1) { + GetEntPropVector(entity, Prop_Data, "m_vecOrigin", pos); + float distance = GetVectorDistance(clientPos, pos, optimizeRange); + if (distance < range) { + GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model)); + GlowEntity(entity, client, 2.0); + } + } + while ((entity = FindEntityByClassname(entity, "prop_physics")) != -1) { + GetEntPropVector(entity, Prop_Data, "m_vecOrigin", pos); + float distance = GetVectorDistance(clientPos, pos, optimizeRange); + if (distance < range) { + GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model)); + GlowEntity(entity, client, 2.0); + } + } +} + +static int COLOR_PROPFINDER[4] = { 255, 255, 255, 128 }; + +stock void GlowEntity(int entity, int client, float lifetime = 5.0) { + static float pos[3], mins[3], maxs[3], ang[3]; + GetEntPropVector(entity, Prop_Data, "m_vecOrigin", pos); + GetEntPropVector(entity, Prop_Data, "m_vecMins", mins); + GetEntPropVector(entity, Prop_Data, "m_vecMaxs", maxs); + GetEntPropVector(entity, Prop_Data, "m_angRotation", ang); + + Effect_DrawBeamBoxRotatableToClient(client, pos, mins, maxs, ang, g_iLaserIndex, 0, 0, 1, lifetime, 1.0, 1.0, 100, 0.1, COLOR_PROPFINDER, 0.0); +} + 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(!isEnabled) return Plugin_Continue; + if(!isEnabled || !IsPlayerAlive(client) || GetClientTeam(client) < 2 || Game.IsSeeker(client)) return Plugin_Continue; + // TODO: Check team int oldButtons = GetEntProp(client, Prop_Data, "m_nOldButtons"); if(buttons & IN_RELOAD && !(oldButtons & IN_RELOAD)) { propData[client].rotationLock = !propData[client].rotationLock; PrintHintText(client, "Rotation lock now %s", propData[client].rotationLock ? "enabled" : "disabled"); return Plugin_Continue; + } else if(buttons & IN_ATTACK && !(oldButtons & IN_ATTACK)) { + GlowNearbyProps(client, 200.0); + int lookAtProp = GetLookingProp(client, 100.0); + if(lookAtProp > 0) { + int prop = CloneProp(lookAtProp); + if(prop > 0) { + EmitSoundToClient(client, SOUND_SWITCH_MODEL); + Game.SetupProp(client, prop); + PrintHintText(client, "Changed prop"); + SetEntPropFloat(client, Prop_Send, "m_flNextAttack", GetGameTime() + 1.0); + return Plugin_Handled; + } + } } - if(propData[client].prop > 0) { + if(propData[client].prop > 0 && IsValidEntity(propData[client].prop)) { static float pos[3], ang[3]; GetClientAbsOrigin(client, pos); TeleportEntity(client, pos, NULL_VECTOR, NULL_VECTOR); + pos[2] += propData[client].verticalOffset; if(propData[client].rotationLock) TeleportEntity(propData[client].prop, pos, NULL_VECTOR, NULL_VECTOR); else { @@ -310,16 +389,42 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 return Plugin_Continue; } +int GetLookingProp(int client, float distance = 0.0, bool optimizeDist = false) { + static float pos[3], ang[3]; + GetClientEyePosition(client, pos); + GetClientEyeAngles(client, ang); + TR_TraceRayFilter(pos, ang, MASK_SHOT, RayType_Infinite, Filter_FindProp, propData[client].prop); + if(TR_DidHit()) { + if(distance > 0) { + TR_GetEndPosition(ang); + if(GetVectorDistance(pos, ang, optimizeDist) > distance) { + return -1; + } + } + return TR_GetEntityIndex(); + } + return -1; +} + +bool Filter_FindProp(int entity, int mask, int data) { + if(entity <= MaxClients || data == entity) return false; + static char classname[32]; + GetEntityClassname(entity, classname, sizeof(classname)); + return StrEqual(classname, "prop_dynamic") || StrEqual(classname, "prop_physics"); +} + Action OnTakeDamageAlive(int victim, int& attacker, int& inflictor, float& damage, int& damagetype) { - if(Game.IsSeeker(attacker)) { + if(attacker > MaxClients || Game.IsSeeker(attacker)) { if(victim <= MaxClients && victim > 0) { - damage = 10.0; + damage = 5.0; return Plugin_Changed; } else { SDKHooks_TakeDamage(attacker, 0, 0, 10.0, DMG_DIRECT); damage = 0.0; return Plugin_Handled; } + } else if(attacker == victim || attacker == 0) { + return Plugin_Continue; } else { damage = 0.0; return Plugin_Handled;