diff --git a/plugins/l4d2_feedthetrolls.smx b/plugins/l4d2_feedthetrolls.smx index 257cc98..c90d9cb 100644 Binary files a/plugins/l4d2_feedthetrolls.smx and b/plugins/l4d2_feedthetrolls.smx differ diff --git a/scripting/include/feedthetrolls/base.inc b/scripting/include/feedthetrolls/base.inc index 5b63703..f50b510 100644 --- a/scripting/include/feedthetrolls/base.inc +++ b/scripting/include/feedthetrolls/base.inc @@ -4,7 +4,7 @@ //Allow MAX_TROLLS to be defined elsewhere #if defined MAX_TROLLS #else - #define MAX_TROLLS 45 + #define MAX_TROLLS 46 #endif enum trollModifier { @@ -278,7 +278,7 @@ int GetTroll(const char[] name, Troll troll) { troll = Trolls[i]; return i; } - PrintToServer("GetTroll: Troll was not found \"%s\"", name); + ThrowError("GetTroll: Troll was not found \"%s\"", name); return -1; } int GetTrollID(const char[] name) { @@ -311,6 +311,11 @@ void ToggleTroll(int client, const char[] name, int flags = 0) { troll.activeFlagClients[client] = flags; } +void SetTrollFlags(int client, const char[] name, int flags = -1) { + int index = GetTrollID(name); + Trolls[index].activeFlagClients[client] = flags; +} + void ApplyTroll(int victim, const char[] name, int activator, trollModifier modifier, int flags = 0, bool silent = false) { static Troll troll; int trollIndex = GetTroll(name, troll); @@ -413,6 +418,7 @@ bool IsTrollActive(int client, const char[] troll) { } static int i = 0; if(trollKV.GetValue(troll, i)) { + PrintToChatAll("index: %d. val: %d", i, Trolls[i].activeFlagClients[client] ); return Trolls[i].activeFlagClients[client] != -1; } ThrowError("Troll \"%s\" does not exist", troll); @@ -425,15 +431,11 @@ bool IsTrollActiveByRawID(int client, int id) { void EnableTroll(int client, const char[] troll, int flags = 0) { - if(!IsTrollActive(client, troll)) { - ToggleTroll(client, troll, flags); - } + SetTrollFlags(client, troll, flags); } void DisableTroll(int client, const char[] troll) { - if(IsTrollActive(client, troll)) { - ToggleTroll(client, troll); - } + SetTrollFlags(client, troll, -1); } public void SetCategory(const char[] newCat) { diff --git a/scripting/include/feedthetrolls/events.inc b/scripting/include/feedthetrolls/events.inc index 05f7f8a..5dc42a7 100644 --- a/scripting/include/feedthetrolls/events.inc +++ b/scripting/include/feedthetrolls/events.inc @@ -32,53 +32,63 @@ public void OnClientPutInServer(int client) { SDKHook(client, SDKHook_OnTakeDamage, Event_TakeDamage); SDKHook(client, SDKHook_OnTakeDamageAlive, NerfGun_OnTakeDamage); } -/* Projectile Magnet: - 1. Create watch timer on EntityCreateCallback - 2. Test velocity over time - 3. If velocity goes negative, its at apex: - - TraceRay to victim - - If collision && molotov (maybe acid/): - Get End Position - If Distance From End Position & Victim < 100 units or so - Allow - - If no collision, set angle to player, set velocity accordingly -*/ + +#define CAR_MODEL_COUNT 4 +static char CAR_MODELS[CAR_MODEL_COUNT][] = { + "props_vehicles\\car001a_phy.mdl", + "props_vehicles\\car001b_phy.mdl", + "props_vehicles\\car002a_physics.mdl", + "props_vehicles\\car002b_physics.mdl" +}; + 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 ) { + else if(StrEqual(classname, "prop_physics")) { + char model[64]; + GetEntPropString(entity, Prop_Data, "m_ModelName", model, sizeof(model)); + for(int i = 0; i < CAR_MODEL_COUNT; i++) { + if(StrEqual(CAR_MODELS[i], model)) { + HookSingleEntityOutput(entity, "OnHitByTank", OnCarHitByTank); + break; + } + } + } else if(StrEqual(classname, "prop_car_alarm")) { + HookSingleEntityOutput(entity, "OnHitByTank", OnCarHitByTank); + } else if(StrEqual(classname, "tank_rock") || StrContains(classname, "_projectile", true) > -1 ) { RequestFrame(EntityCreateCallback, entity); - } /*else if(g_iRockThrows > 0 && StrEqual(classname, "tank_rock")) { - --g_iRockThrows; - SDKHook(entity, SDKHook_SpawnPost, SpawnPost); - }*/ + } } } +void OnCarHitByTank(const char[] output, int caller, int activator, float delay) { + entLastHeight[activator] = -10000.0; + CreateTimer(0.1, Timer_WaitForApex, EntIndexToEntRef(activator), TIMER_REPEAT); +} + /*void SpawnPost(int entity) { RequestFrame(SetRockVelocity, EntIndexToEntRef(entity)) } void SetRockVelocity(int entity) { - entity = EntRefToEntIndex(entity); + entity = EntRefToEntIndex(entity); - if(!IsValidEntity(entity)) return; + if(!IsValidEntity(entity)) return; float vel[3]; - GetEntPropVector(entity, Prop_Send, "m_vecVelocity", vel); + GetEntPropVector(entity, Prop_Send, "m_vecVelocity", vel); ScaleVector(vel, 0.4); - // z_tank_throw_force - //SetEntPropVector(entity, Prop_Send, "m_vecVelocity", vel); - vel[2] += 150.0; - TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vel); + // z_tank_throw_force + //SetEntPropVector(entity, Prop_Send, "m_vecVelocity", vel); + vel[2] += 150.0; + TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vel); PrintToChatAll("set rock %d vel", entity); }*/ - void EntityCreateCallback(int entity) { if(!HasEntProp(entity, Prop_Send, "m_hOwnerEntity") || !IsValidEntity(entity)) return; static char class[16]; @@ -121,14 +131,26 @@ void EntityCreateCallback(int entity) { } SpawnItem("pipe_bomb", pos); } + return; } else if(Trolls[slipperyShoesIndex].IsActive(entOwner)) { if(Trolls[slipperyShoesIndex].activeFlagClients[entOwner] & 4) { L4D_StaggerPlayer(entOwner, entOwner, NULL_VECTOR); } + return; } } + entLastHeight[entity] = -10000.0; + CreateTimer(0.1, Timer_WaitForApex, EntIndexToEntRef(entity), TIMER_REPEAT); } +enum ProjectileMagnetType { + ProjType_Specials = 1, + ProjType_Survivors = 2, + ProjType_Cars = 4, +} + + + public void Event_PlayerSpawn(Event event, const char[] name, bool dontBroadcast) { int userid = event.GetInt("userid"); if(spIsActive) @@ -666,7 +688,7 @@ public Action OnPlayerRunCmd(int client, int& buttons, int& impulse, float vel[3 LookAtClient(client, pdata[client].shootAtTarget); } } - } + } // Inverted control code: if(Trolls[invertedTrollIndex].IsActive(client)) { @@ -732,10 +754,10 @@ public Action Event_TakeDamage(int victim, int& attacker, int& inflictor, float& } else if(Trolls[reverseFF].IsActive(attacker) && damagetype != DMG_BURN && attacker != victim && GetClientTeam(attacker) == GetClientTeam(victim)) { float returnDmg = damage; //default is 1:1 - if(Trolls[reverseFF].activeFlagClients[attacker] & 4) { - returnDmg /= 2.0; - } else if(Trolls[reverseFF].activeFlagClients[attacker] & 2) { + if(Trolls[reverseFF].activeFlagClients[attacker] & 2) { returnDmg *= 2.0; + } else if(Trolls[reverseFF].activeFlagClients[attacker] & 4) { + returnDmg /= 2.0; } else if(Trolls[reverseFF].activeFlagClients[attacker] & 8) { returnDmg = 0.0; } else if(Trolls[reverseFF].activeFlagClients[attacker] & 16) { @@ -916,8 +938,8 @@ stock int FindClosestVisibleClient(int source) { static float pos[3], ang[3]; GetClientEyePosition(source, pos); GetClientEyeAngles(source, ang); - Handle handle = TR_TraceRayFilterEx(pos, ang, MASK_VISIBLE, RayType_Infinite, TraceEntityFilterPlayer, source); - return TR_GetEntityIndex(handle); + TR_TraceRayFilter(pos, ang, MASK_VISIBLE, RayType_Infinite, TraceEntityFilterPlayer, source); + return TR_GetEntityIndex(); } public bool TraceEntityFilterPlayer(int entity, int mask, any data) { diff --git a/scripting/include/feedthetrolls/misc.inc b/scripting/include/feedthetrolls/misc.inc index fd15092..ffa503e 100644 --- a/scripting/include/feedthetrolls/misc.inc +++ b/scripting/include/feedthetrolls/misc.inc @@ -465,7 +465,7 @@ void ClearInventory(int client) { } } -void StopHealingBots() { +void StopHealingBots(bool dontKill = false) { healTargetPlayer = 0; for(int i = 1; i <= MaxClients; i++) { pdata[i].flags &= ~view_as(Flag_IsTargettingHealer); @@ -474,57 +474,14 @@ void StopHealingBots() { KickClient(i); } } + if(!dontKill && IsValidHandle(stopHealingTimer)) { + delete stopHealingTimer; + } + stopHealingTimer = null; if(hAbmAutoHard != null) hAbmAutoHard.IntValue = wasAbmAutoHard; if(hSbFixEnabled != null) hSbFixEnabled.BoolValue = wasSbFixEnabled; } -// Spawns a env_rock_launcher to throw at a random specified target name. -// Does not auto fire, need to call input LaunchRock -// Damage -1 will not override damage -// autoDeleteTime of <= 0.0 will persist forever. -stock int CreateRockLauncher(const float origin[3], const float ang[3], const char[] targetName, float damage = -1.0, float autoDeleteTime = 0.0) { - int launcher = CreateEntityByName("env_rock_launcher"); - if(launcher == -1) return -1; - // DispatchKeyValue(launcher, "targetname", "ftt_rock_launcher"); - DispatchKeyValue(launcher, "RockTargetName", targetName); - if(damage >= 0.0) { - DispatchKeyValueFloat(launcher, "RockDamageOverride", damage); - } - if(autoDeleteTime > 0.0) CreateTimer(autoDeleteTime, Timer_Delete, launcher); - DispatchSpawn(launcher); - TeleportEntity(launcher, origin, ang, NULL_VECTOR); - PrintToChatAll("Created rock launcher at %f %f %f at ang %f %f %f", origin[0], origin[1], origin[2], ang[0], ang[1], ang[2]); - // AcceptEntityInput(launcher, "Kill"); - return launcher; -} - -#define FTT_TARGET_NAME "ftt_target" -stock bool ThrowRockAtPosition(const float origin[3], float pos[3], float damage = -1.0) { - CreateTarget(pos, FTT_TARGET_NAME, 0.2); - GetVectorAngles(pos, pos); - int launcher = CreateRockLauncher(origin, pos, FTT_TARGET_NAME, damage, 0.0); - g_iRockThrows++; - AcceptEntityInput(launcher, "LaunchRock"); - AcceptEntityInput(launcher, "Kill"); - return true; -} -stock bool ThrowRockAtEntity(const float origin[3], int target, float damage = -1.0) { - float pos[3]; - GetEntPropVector(target, Prop_Send, "m_vecOrigin", pos); - return ThrowRockAtPosition(origin, pos, damage); -} - -int CreateTarget(const float origin[3], const char[] targetName, float duration = 0.0) { - int target = CreateEntityByName("info_target"); - DispatchKeyValue(target, "targetname", targetName); - - TeleportEntity(target, origin, NULL_VECTOR, NULL_VECTOR); - DispatchSpawn(target); - if(duration > 0.0) { - CreateTimer(duration, Timer_Delete, target); - } - return target; -} bool IsAnySurvivorInRange(const float origin[3], float range, int ignorePlayer = 0) { float pos[3]; @@ -538,4 +495,51 @@ bool IsAnySurvivorInRange(const float origin[3], float range, int ignorePlayer = } } return false; +} + +int GetRandomThrowableMagnetTarget(ProjectileMagnetType type, int owner = -1) { + static int throwMagnetIndex; + if(throwMagnetIndex == 0) throwMagnetIndex = GetTrollID("Projectile Magnet"); + ArrayList checkList = new ArrayList(); + for(int i = 1; i <= MaxClients; i++) { + if(IsClientConnected(i) && IsClientInGame(i) && IsPlayerAlive(i) && Trolls[throwMagnetIndex].IsActive(i)) { + if(type == ProjType_Survivors && owner != i) { + // If the projectile is not owned by player, check if troll flag not enabled + if(~Trolls[throwMagnetIndex].activeFlagClients[i] & view_as(ProjType_Survivors)) continue; + } else if(~Trolls[throwMagnetIndex].activeFlagClients[i] & view_as(type)) { + // Skip if client does not have flag + continue; + } + checkList.Push(i); + } + } + int target = -1; + if(checkList.Length > 0) { + target = checkList.Get(0, checkList.Length - 1); + } + delete checkList; + return target; +} + +stock bool CanSeePoint(const float origin[3], const float point[3]) { + TR_TraceRay(origin, point, MASK_ALL, RayType_EndPoint); + + return !TR_DidHit(); // Can see point if no collisions +} + +stock LookAtPoint(int entity, const float destination[3]){ + float angles[3], pos[3], result[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos); + MakeVectorFromPoints(destination, pos, result); + GetVectorAngles(result, angles); + if(angles[0] >= 270){ + angles[0] -= 270; + angles[0] = (90-angles[0]); + }else{ + if(angles[0] <= 90){ + angles[0] *= -1; + } + } + angles[1] -= 180; + TeleportEntity(entity, NULL_VECTOR, angles, NULL_VECTOR); } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/timers.inc b/scripting/include/feedthetrolls/timers.inc index d2d4d22..18f1744 100644 --- a/scripting/include/feedthetrolls/timers.inc +++ b/scripting/include/feedthetrolls/timers.inc @@ -223,6 +223,7 @@ public Action Timer_CheckForChargerOpportunity(Handle h, int userid) { if(activator) PrintToChat(activator, "Auto charge timed out after %d attempts", pdata[client].smartChargeAttempts); pdata[client].smartChargeAttempts = 0; pdata[client].smartChargeActivator = 0; + DisableTroll(client, "Smart Charge"); return Plugin_Stop; } return Plugin_Continue; @@ -237,7 +238,7 @@ public bool Filter_CheckChargerValid(int entity, int contentsMask, any data) { public Action Timer_UpdateHealTargetPos(Handle h) { int healTarget = GetClientOfUserId(healTargetPlayer); if(healTarget == 0) { - PrintToServer("[FTT] Lost heal target, stopping"); + PrintToServer("[FTT] Dep Bots: Lost heal target, stopping"); return Plugin_Stop; } GetAbsOrigin(healTarget, healTargetPos); @@ -290,7 +291,77 @@ Action Timer_SpawnHealBotsPost(Handle h) { } return Plugin_Handled; } -Action Timer_StopHealBots(Handle h) { - StopHealingBots(); +Action Timer_StopHealBots(Handle h, DataPack pack) { + pack.Reset(); + int activator = GetClientOfUserId(pack.ReadCell()); + int victim = GetClientOfUserId(pack.ReadCell()); + if(activator) { + PrintToChat(activator, "Dep bots has expired"); + } + if(victim) { + DisableTroll(victim, "Dep Bots"); + } + // TODO: stop right one + StopHealingBots(true); + return Plugin_Stop; +} + + +#define NO_ATTEMPT_MAX_DIST 1000.0 +#define NO_ATTEMPT_MAX_DIST_OPT NO_ATTEMPT_MAX_DIST * NO_ATTEMPT_MAX_DIST + + +Action Timer_WaitForApex(Handle h, int entref) { + if(!IsValidEntity(entref)) return Plugin_Stop; + int entity = EntRefToEntIndex(entref); + + static float pos[3]; + GetEntPropVector(entity, Prop_Send, "m_vecOrigin", pos); + + if(entLastHeight[entity] > pos[2]) { + char classname[32]; + GetEntityClassname(entity, classname, sizeof(classname)); + + int target; + if(StrEqual(classname, "tank_rock") || StrEqual(classname, "spitter_projectile")) + target = GetRandomThrowableMagnetTarget(ProjType_Specials); + else if(StrEqual(classname, "prop_physics") || StrEqual(classname, "prop_car_alarm")) + target = GetRandomThrowableMagnetTarget(ProjType_Cars) + else { + int entOwner = GetEntPropEnt(entity, Prop_Send, "m_hOwnerEntity"); + target = GetRandomThrowableMagnetTarget(ProjType_Survivors, entOwner); + } + + if(target > 0) { + float targetPos[3], vel[3]; + GetClientAbsOrigin(target, targetPos); + TR_TraceRay(pos, targetPos, MASK_SHOT, RayType_EndPoint); + if(TR_DidHit()) { + TR_GetEndPosition(pos); + if(GetVectorDistance(pos, targetPos, true) > NO_ATTEMPT_MAX_DIST_OPT) { + return Plugin_Stop; + } + } + + // SetEntityMoveType(entity, MOVETYPE_FLY); + SetEntityGravity(entity, 0.001); + float distance = GetVectorDistance(pos, targetPos); + SubtractVectors(targetPos, pos, vel); + ScaleVector(vel, 1000.0 / distance); + TeleportEntity(entity, NULL_VECTOR, NULL_VECTOR, vel); + + CreateTimer(3.0, Timer_ResetGravity, entref); + } + return Plugin_Stop; + } + entLastHeight[entity] = pos[2]; + return Plugin_Continue; +} + +Action Timer_ResetGravity(Handle h, int entref) { + if(IsValidEntity(entref)) { + int entity = EntRefToEntIndex(entref); + SetEntityGravity(entity, 800.0); + } return Plugin_Handled; } \ No newline at end of file diff --git a/scripting/include/feedthetrolls/trolls.inc b/scripting/include/feedthetrolls/trolls.inc index c2bc2a6..566a6ea 100644 --- a/scripting/include/feedthetrolls/trolls.inc +++ b/scripting/include/feedthetrolls/trolls.inc @@ -21,6 +21,12 @@ void SetupTrolls() { #if defined _behavior_included index = SetupTroll("Witch Magnet", "All witches when startled will target any player with this troll", TrollMod_Constant); #endif + index = SetupTroll("Projectile Magnet", "Makes all projectiles (biles, molotovs, pipes, tank rocks) go to player", TrollMod_Constant); + Trolls[index].AddCustomFlagPrompt("Target Sources", true); + // Tied to: ProjectileMagnetType + Trolls[index].AddFlag("Infected (rocks/goo)", true); + Trolls[index].AddFlag("Teammates (grenades)", false); + Trolls[index].AddFlag("Thrown Cars (wip)", false); /// CATEGORY: Infected SetCategory("Infected"); @@ -138,7 +144,6 @@ void SetupTrolls() { Trolls[index].AddFlag("0.5x Ratio", false); //4 Trolls[index].AddFlag("0.0x Ratio (None)", false); //8 Trolls[index].AddFlag("3x Ratio", false); //16 - Trolls[index].AddFlag("-2x Ratio", false); //16 index = SetupTroll("Dep Bots", "Makes bots heal a player. At any cost", TrollMod_Constant); Trolls[index].AddFlagPrompt(false); Trolls[index].AddFlag("Do not spawn extra", true); // 1 @@ -361,6 +366,11 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod if(!toActive) { StopHealingBots(); return true; + } else if(healTargetPlayer != 0) { + if(IsValidHandle(stopHealingTimer)) { + TriggerTimer(stopHealingTimer); + } + return true; } bool spawnExtra = flags & 2 > 0; @@ -398,7 +408,10 @@ bool ApplyAffect(int victim, const Troll troll, int activator, trollModifier mod } CreateTimer(2.0, Timer_UpdateHealTargetPos, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE); if(timeout > 0.0) { - CreateTimer(timeout, Timer_StopHealBots); + DataPack pack; + stopHealingTimer = CreateDataTimer(timeout, Timer_StopHealBots, pack); + pack.WriteCell(GetClientUserId(activator)); + pack.WriteCell(GetClientUserId(victim)) } if(spawnExtra && numBots > 0) { diff --git a/scripting/include/ftt.inc b/scripting/include/ftt.inc index 6bc8c91..c69c134 100644 --- a/scripting/include/ftt.inc +++ b/scripting/include/ftt.inc @@ -88,8 +88,10 @@ enum SpecialInternalFlags { int healTargetPlayer; float healTargetPos[3]; +Handle stopHealingTimer; + +float entLastHeight[2048]; -int g_iRockThrows; #define MODEL_CAR "models/props_vehicles/cara_95sedan.mdl"