local ghost_Character_Attributes = { ["not solid to players"] = 1, ["SET BONUS: special dsp"] = 38, ["SET BONUS: move speed set bonus"] = 500, ["damage force reduction"] = 0, ["airblast vulnerability multiplier"] = 0, ["dmg penalty vs players"] = 0, ["max health additive bonus"] = 874, ["voice pitch scale"] = 1.6, ["ignored by enemy sentries"] = 1, ["ignored by bots"] = 1, ["dmg taken increased"] = 0, ["health regen"] = 1000, ["special item description 3"] = "GHOST", ["air dash count"] = 100, ["increased jump height"] = 1.3, ["increased air control"] = 3, } --Works almost identically to royal's time constraint prime insult system, just without the nested table to --handle multiple classes local PLAYER_RELEASE_MESSAGE = { "{FFFF00}Administrative Override{reset} {FFFFFF} : REMOTE SESSION ON {red} %s {FFFFFF} TERMINATED", "{FFFF00}Administrative Override{reset} {FFFFFF} : CONNECTION CLOSING ON {red} %s", "{FFFF00}Administrative Override{reset} {FFFFFF} : quit {red} %s", } local PLAYER_DIE_MESSAGE = { "{FFFF00}Administrative Override{reset} {FFFFFF} : CONNECTION TIMEOUT ON {red} %s", "{FFFF00}Administrative Override{reset} {FFFFFF} : {red} %s {FFFFFF} UNRESPONSIVE", "{FFFF00}Administrative Override{reset} {FFFFFF} : UNREACHABLE HOST: {red} %s", } local PLAYER_POSSESS_MESSAGE = { "{FFFF00}Administrative Override{reset} {FFFFFF} : ASSUMING DIRECT CONTROL OF {red} %s", "{FFFF00}Administrative Override{reset} {FFFFFF} : telnet -e MOUSE3 {red} %s {FFFFFF} 23", "{FFFF00}Administrative Override{reset} {FFFFFF} : REMOTE SESSION OPENING ON: {red} %s", } --thank you royal local function removeCallbacks(player, callbacks) if not IsValid(player) then return end for _, callbackId in pairs(callbacks) do player:RemoveCallback(callbackId) end end --temp value, we'll put bots in wherever our spawn is later on local BOT_HELL_VECTOR = Vector(0,0,0) --in case anyone else uses this script, they can set bot hell vector to wherever the spawn is on their map function setBotHellVectorX(changedVectorX) BOT_HELL_VECTOR = Vector(changedVectorX, BOT_HELL_VECTOR[2], BOT_HELL_VECTOR[3]) end function setBotHellVectorY(changedVectorY) BOT_HELL_VECTOR = Vector(BOT_HELL_VECTOR[1], changedVectorY, BOT_HELL_VECTOR[3]) end function setBotHellVectorZ(changedVectorZ) BOT_HELL_VECTOR = Vector(BOT_HELL_VECTOR[1], BOT_HELL_VECTOR[2], changedVectorZ) end --This function is handled by a spawn template over in the popfile to stop an infinite loop from occuring, it is a ghetto ass solution --to the problem of class limits when the script's main function changes your class. function EvaluateSpawn(_, activator) --If you're not a spy and you aren't tagged with a bot matching tag, turn into a spy and apply addcond 66 --this is tripped when you pick a non-spy class between waves if activator.m_iClass ~= 8 and activator:GetAttributeValue("special item description 3") == nil then activator:SwitchClassInPlace(8) activator:AddCond(TF_COND_STEALTHED_USER_BUFF_FADING) --If you aren't a spy, but you do have a tag, do nothing. If this is tripped, it means that you're possessing something --so no class changes or cond applications are needed elseif activator.m_iClass ~= 8 and activator:GetAttributeValue("special item description 3") ~= nil then return --If you're a spy with a tag, just apply the cond. If this is tripped, it means you picked spy from the menu or you tripped clause 1 --and the script respawned you as a spy else activator:AddCond(TF_COND_STEALTHED_USER_BUFF_FADING) end end AddEventCallback("player_spawn", function(eventTable) local activator = ents.GetPlayerByUserId(eventTable.userid) if activator:IsRealPlayer() == false then return end local callbacks = {} --print(activator) timer.Simple(0.01, function() callbacks.spawned = activator:AddCallback(ON_SPAWN, function() --print("Purged due to being dead, but we didn't catch it before") removeCallbacks(activator, callbacks) end) end) local logicLoop logicLoop = timer.Create(0.2, function() if not activator:IsAlive() then --print("Imma gonna die now") timer.Stop(logicLoop) return end if activator:GetAttributeValue("special item description 3") ~= "GHOST" then activator:Print(PRINT_TARGET_RIGHT, "PRESS SPECIAL ATTACK (MOUSE3) TO EXIT") if activator.m_nButtons >= 33554432 then local bot local botNameReference = activator:GetAttributeValue("special item description 3") activator:RemoveCond(34) for _, player in pairs(ents.GetAllPlayers()) do --Look for a bot who has been 66'd, and has the same name as our reference if player.m_szNetname == botNameReference and player:InCond(TF_COND_STEALTHED_USER_BUFF_FADING) and player:IsRealPlayer() == false then bot = player --print("We got a bot") end --print(player.m_szNetname) --print(player:InCond(TF_COND_STEALTHED_USER_BUFF_FADING)) end --this shouldn't be nil unless some mega fuckup happens, but it never hurts to check --if a mega fuckup did not happen, move the bot over to where you are, undrug it, and set its health --to yours if bot ~= nil then bot:SnapEyeAngles(Vector(activator["m_angEyeAngles[0]"],activator["m_angEyeAngles[1]"], 0)) bot:SetAbsOrigin(activator:GetAbsOrigin()) bot.m_iHealth = activator.m_iHealth bot:RemoveCond(TF_COND_STEALTHED_USER_BUFF_FADING) bot:RemoveCond(TF_COND_STUNNED) activator:AcceptInput("$displaytextchat", string.format(PLAYER_RELEASE_MESSAGE[math.random(#PLAYER_RELEASE_MESSAGE)], bot.m_szNetname)) end --Turn you into a spy, thanos snap items and hand you the possession tool again activator:SwitchClassInPlace(8) activator:AddCond(TF_COND_STEALTHED_USER_BUFF_FADING) activator:WeaponStripSlot(0) activator:WeaponStripSlot(1) activator:GiveItem("Possession Tool") --Clear our old target's character attributes local robotAttributesTable = activator:GetAllAttributeValues(false) for name, value in pairs(robotAttributesTable) do activator:SetAttributeValue(name, nil) end --Set our ghost character attributes for name, value in pairs(ghost_Character_Attributes) do activator:SetAttributeValue(name, value) end end else activator:Print(PRINT_TARGET_RIGHT, "MELEE FRIENDLY ROBOTS TO POSSESS THEM") end --print("I am not dead") --release control of the bot if you are holding special attack, and you are controlling a bot to begin with end, 0) callbacks.owie = activator:AddCallback(ON_DAMAGE_RECEIVED_PRE, function(_, damageInfo) -- just in case if not activator:IsAlive() then --print("Purged due to being dead") removeCallbacks(activator, callbacks) return end if activator:InCond(5) or activator:InCond(14) then return end local damage = damageInfo.Damage curHealth = activator.m_iHealth if (damageInfo.DamageType & DMG_CRITICAL > 0) then damage = damageInfo.Damage * 3.1 --print("That was a crit") end --print(curHealth .. " health compared to " .. damage .. " damage") --If the incoming damage is lethal, find the closest clone, swap places and angles with them, then make them die in our place. If we can't find any, just die if curHealth <= damage and (damageInfo.Attacker.m_iTeamNum ~= activator.m_iTeamNum or damageInfo.Attacker == activator) then --print("we're about to die") damageInfo.Damage = 0 damageInfo.DamageType = DMG_GENERIC local botNameReference = activator:GetAttributeValue("special item description 3") --print(botNameReference) --if we are already a ghost, do not respawn us if botNameReference ~= "GHOST" then local bot activator:RemoveCond(34) for _, player in pairs(ents.GetAllPlayers()) do --Look for a bot who has been 66'd, and has the same name as our reference if player.m_szNetname == botNameReference and player:InCond(TF_COND_STEALTHED_USER_BUFF_FADING) and player:IsRealPlayer() == false then bot = player --print("We got a bot") end --print(player.m_szNetname) --print(player:InCond(TF_COND_STEALTHED_USER_BUFF_FADING)) end if bot ~= nil then bot:SnapEyeAngles(Vector(activator["m_angEyeAngles[0]"],activator["m_angEyeAngles[1]"], 0)) bot:SetAbsOrigin(activator:GetAbsOrigin()) damageInfo.Damage = bot.m_iHealth timer.Simple(0.05, function() bot:TakeDamage(damageInfo) activator:AcceptInput("$displaytextchat", string.format(PLAYER_DIE_MESSAGE[math.random(#PLAYER_DIE_MESSAGE)], botNameReference)) --print("die bot") if bot:IsAlive() then bot:Suicide() end end) end --Turn you into a spy, thanos snap items and hand you the possession tool again activator:SwitchClassInPlace(8) activator:AddCond(TF_COND_STEALTHED_USER_BUFF_FADING) activator:WeaponStripSlot(0) activator:WeaponStripSlot(1) activator:GiveItem("Possession Tool") --Clear our old target's character attributes local robotAttributesTable = activator:GetAllAttributeValues(false) for name, value in pairs(robotAttributesTable) do activator:SetAttributeValue(name, nil) end --Set our ghost character attributes for name, value in pairs(ghost_Character_Attributes) do activator:SetAttributeValue(name, value) end end --take your nonexistent damage local setHealthDmgInfo = { Attacker = damageInfo.Attacker, Inflictor = damageInfo.Inflictor, Weapon = damageInfo.Weapon, Damage = 0, CritType = 0, DamageType = damageInfo.DamageType, DamageCustom = damageInfo.DamageCustom, DamagePosition = damageInfo.DamagePosition, DamageForce = nil, ReportedPosition = damageInfo.ReportedPosition, } activator:TakeDamage(setHealthDmgInfo) return true end end) callbacks.dead = activator:AddCallback(ON_DEATH, function() --print("Purged due to being dead, but later") removeCallbacks(activator, callbacks) end) end) function checkIfMeleeHitAllyPossess(param, activator, caller) if not util.IsLagCompensationActive() then util.StartLagCompensation(activator) end --ripped from red_sniper_laser.lua local function getEyeAngles(player) local pitch = player["m_angEyeAngles[0]"] local yaw = player["m_angEyeAngles[1]"] return Vector(pitch, yaw, 0) end --ripped from my med hunter script, hybrid of red sniper laser check and some original work on my end local TraceLineOfSight = { start = activator, -- Start position vector. Can also be set to entity, in this case the trace will start from entity eyes position distance = 100, -- Used if endpos is nil angles = getEyeAngles(activator), -- Used if endpos is nil mask = MASK_SOLID, -- Solid type mask, see MASK_* globals collisiongroup = COLLISION_GROUP_PLAYER, -- Pretend the trace to be fired by an entity belonging to this group. See COLLISION_GROUP_* globals } local lineOfSightTraceTable = util.Trace(TraceLineOfSight) local hitEntity = lineOfSightTraceTable.Entity --print(lineOfSightTraceTable.Entity) --If the trace hit a valid bot that is on your team, and they aren't in addcond 66 (currently being controlled by someone else), begin the possession logic. if IsValid(hitEntity) and hitEntity:IsBot() and hitEntity.m_iTeamNum == activator.m_iTeamNum and hitEntity:InCond(TF_COND_STEALTHED_USER_BUFF_FADING) == false then --Smash a rock over the bot's skull then hide them from the authorities hitEntity:AddCond(TF_COND_MVM_BOT_STUN_RADIOWAVE, 50000, activator) hitEntity:AddCond(TF_COND_STEALTHED_USER_BUFF_FADING, 50000) activator:AddCond(57, 0.5, activator) --Snap player's view to what the bot was looking at activator:SnapEyeAngles(Vector(hitEntity["m_angEyeAngles[0]"],hitEntity["m_angEyeAngles[1]"], 0)) --copypasted wholesale from the scout mech primary, spawns a set of particles, a lightning strike on the bot and a teleport particle on the player local teleParticle = ents.CreateWithKeys("info_particle_system", { effect_name = "teleportedin_red", start_active = 1, flag_as_weather = 0, }, true, true) teleParticle:SetAbsOrigin(activator:GetAbsOrigin()) teleParticle:Start() local teleParticle2 = ents.CreateWithKeys("info_particle_system", { effect_name = "wrenchmotron_teleport_beam", start_active = 1, flag_as_weather = 0, }, true, true) teleParticle2:SetAbsOrigin(activator:GetAbsOrigin()) teleParticle2:Start() timer.Simple(1, function() teleParticle:Remove() teleParticle2:Remove() end) --Kidnap bot, snap player to bot's old location activator:SetAbsOrigin(hitEntity:GetAbsOrigin()) hitEntity:SetAbsOrigin(BOT_HELL_VECTOR) --Clear our ghost character attributes for name, value in pairs(ghost_Character_Attributes) do activator:SetAttributeValue(name, nil) end --Make us their class, order of operations here is important because a few sanity check attributes are applied --whenever the player is respawned this way, meaning the previous check will not clean them off. activator:SwitchClassInPlace(hitEntity.m_iClass) --Give us our target's character attributes local robotAttributesTable = hitEntity:GetAllAttributeValues(false) for name, value in pairs(robotAttributesTable) do activator:SetAttributeValue(name, value) end --Give us the bot's weapons, it is VERY important that player's melee is overwritten given that it is the transformation tool for i = 0, 8 do local firearm = hitEntity:GetPlayerItemBySlot(i) --if we exist, give the player a copy of us, then xerox our attributes onto the copy if firearm ~= nil then activator:GiveItem(firearm:GetItemName()) for name, value in pairs(firearm:GetAllAttributeValues()) do activator:GetPlayerItemBySlot(i):SetAttributeValue(name, value); end end end if hitEntity:InCond(34) == true then activator:AddCond(34) end activator.m_flRageMeter = 100 --not a good solution for minigiants, but its all I've got unless there's a GetScale method if hitEntity.m_bIsMiniBoss == 0 and hitEntity:GetAttributeValueByClass("add_maxhealth_nonbuffed", 0) > 0 then --I fucking hate this, but the stock valve gauntlet templates go up to 1.5, so I guess I have to suffer if hitEntity.m_iClass == 6 then activator:SetAttributeValue("model scale", 1.3) else activator:SetAttributeValue("model scale", 1.5) end end if hitEntity.m_bIsMiniBoss == 1 then activator:SetAttributeValue("is miniboss", 1) activator:SetAttributeValue("model scale", 1.75) --If I were in a different language this would be a switch statement, but I'm not, so yeeowch. if hitEntity.m_iClass == 1 then activator:SetCustomModelWithClassAnimations("models/bots/scout_boss/bot_scout_boss.mdl") --note that this is not a valve model elseif hitEntity.m_iClass == 2 then activator:SetCustomModelWithClassAnimations("models/bots/sniper_boss/bot_sniper_boss.mdl") elseif hitEntity.m_iClass == 3 then activator:SetCustomModelWithClassAnimations("models/bots/soldier_boss/bot_soldier_boss.mdl") elseif hitEntity.m_iClass == 4 then activator:SetCustomModelWithClassAnimations("models/bots/demo_boss/bot_demo_boss.mdl") elseif hitEntity.m_iClass == 6 then activator:SetCustomModelWithClassAnimations("models/bots/heavy_boss/bot_heavy_boss.mdl") elseif hitEntity.m_iClass == 7 then activator:SetCustomModelWithClassAnimations("models/bots/pyro_boss/bot_pyro_boss.mdl") end end activator:SetAttributeValue("special item description 3", hitEntity.m_szNetname) activator:AcceptInput("$displaytextchat", string.format(PLAYER_POSSESS_MESSAGE[math.random(#PLAYER_POSSESS_MESSAGE)], hitEntity.m_szNetname)) print(activator:GetAttributeValue("special item description 3")) activator.m_iHealth = hitEntity.m_iHealth end end