precache.PrecacheParticle("utaunt_marigoldritual_red_orbit_holder"); -- Required TAG_ITEMNAME = "hp_itemname"; -- Optional TAG_ITEMPOS = "hp_itempos"; -- ... TAG_COSMETICS = "hp_cosmetics"; TAG_WEAPONS = "hp_weapons"; -- ... TAG_LOADOUT_START = "hp_loadout_" TAG_LOADOUT_PRIMARY = "hp_loadout_primary"; TAG_LOADOUT_SECONDARY = "hp_loadout_secondary"; TAG_LOADOUT_MELEE = "hp_loadout_melee"; -- ... TAG_IGNORECLASS = "hp_ignoreclass"; -- Round a number math.round = function(num, decimals) if (not decimals or decimals <= 0) then return math.floor(num + 0.5); else local mod = 10 ^ decimals; return math.floor((num * mod) + 0.5) / mod; end end ------------- -- CEntity ------------- -- Grab the CEntity table local _ = Entity("info_target", false, false); local CEntity = getmetatable(_); _:Remove(); function IsValidPlayer(ent) return IsValid(ent) and ent:IsPlayer(); end function IsValidRealPlayer(ent) return IsValid(ent) and ent:IsRealPlayer(); end function IsValidAlivePlayer(ent) return IsValid(ent) and ent:IsPlayer() and ent:IsAlive(); end function IsValidAliveRealPlayer(ent) return IsValid(ent) and ent:IsRealPlayer() and ent:IsAlive(); end -- Get player eye position CEntity.GetEyePos = function(self) if (not IsValidPlayer(self)) then return; end local eyepos = self:GetAbsOrigin(); eyepos.z = eyepos.z + self["m_vecViewOffset[2]"]; return eyepos; end -- Get player's wearables CEntity.GetWearables = function(self) if (not IsValidPlayer(self)) then return; end local wearables = ents.FindAllByClass("tf_wearable"); local my_wearables = {}; for index, wearable in pairs(wearables) do if (self == wearable.m_hOwnerEntity) then table.insert(my_wearables, wearable); end end return my_wearables; end -- Get player's wearables CEntity.GetWearableNames = function(self) if (not IsValidPlayer(self)) then return; end local wearables = ents.FindAllByClass("tf_wearable"); local my_wearables = {}; for index, wearable in pairs(wearables) do if (self == wearable.m_hOwnerEntity) then table.insert(my_wearables, wearable:GetItemName()); end end return my_wearables; end CEntity.GiveLoadout = function(self, item, hatname, hatattributes, cosmetics, weapons) if (not IsValidAlivePlayer(self)) then return; end -- Hat local hat = self:GiveItem(hatname); -- Hat attributes if (hatattributes) then for attr, val in pairs(hatattributes) do self:SetAttributeValue(attr, val); self:AcceptInput("$addplayerattribute", attr.."|"..val); end end -- Other cosmetics if (cosmetics) then -- Remove old ones for index, cosmetic in pairs(self:GetWearables()) do if (cosmetic ~= hat) then cosmetic:Remove(); end end -- Add new ones for index, cosmetic in pairs(cosmetics) do if (cosmetic ~= item) then self:GiveItem(cosmetic); end end end if (weapons) then for slot, wep in pairs(weapons) do self:GiveItem(wep[1], wep[2]); end end self._item = item; self._itemname = hatname; self._hatattributes = hatattributes; self._cosmetics = cosmetics; self._weapons = weapons; end -- Called when the wave spawns a bot, after the bot is initialized with key values function OnWaveSpawnBot(bot, wave, tags) bot.tags = tags; end function OnPlayerKeyReleased(player, key) if (key == IN_RELOAD and IsValid(player._touchingpickup)) then local entity = player._touchingpickup; player:GiveLoadout(entity._item, entity._itemname, entity._hatattributes, entity._cosmetics, entity._weapons); player:PlaySoundToSelf("player/recharged.wav"); player:Print(PRINT_TARGET_CENTER, ""); entity:Remove() end end function OnPickupStartTouch(entity, other, hitPos, hitNormal) if (IsValidAliveRealPlayer(other)) then if (entity._ignoreclass or entity._class == other.m_iClass) then other:Print(PRINT_TARGET_CENTER, "Press to pick up!"); other._touchingpickup = entity; end end end function OnPickupEndTouch(entity, other, hitPos, hitNormal) if (IsValidAliveRealPlayer(other)) then if (entity._ignoreclass or entity._class == other.m_iClass) then other:Print(PRINT_TARGET_CENTER, ""); other._touchingpickup = nil; end end end function OnPickupCreated(entity, classname) entity:AddCallback(ON_START_TOUCH, OnPickupStartTouch); entity:AddCallback(ON_END_TOUCH, OnPickupEndTouch); end function TagStarts(tag, str) return (string.find(tag, str) == 1 and #tag > #str + 1); end function OnBotDeath(bot) local itemname = nil; local itempos = nil; local cosmetics = nil; local ignoreclass = false; local hatattributes = nil; local weapons = nil; -- Parse tags for index, tag in pairs(bot.tags) do if (TagStarts(tag, TAG_ITEMNAME)) then itemname = string.sub(tag, #TAG_ITEMNAME + 2); elseif (TagStarts(tag, TAG_ITEMPOS)) then local line = string.sub(tag, #TAG_ITEMPOS + 2); local list = {}; for token in string.gmatch(line, "[^%s]+") do pcall(table.insert, list, tonumber(token)); end if (#list == 3) then itempos = Vector(table.unpack(list)); else itempos = Vector(0, 0, 0); print("Hat Pickup ERROR -- INVALID ITEMPOS VALUE; CANNOT ASSIGN TO VECTOR: ".. line); end elseif (tag == TAG_COSMETICS) then cosmetics = bot:GetWearableNames(); elseif (tag == TAG_IGNORECLASS) then ignoreclass = true; elseif (tag == TAG_WEAPONS) then weapons = {}; for i=0,2 do local wep = bot:GetPlayerItemBySlot(i) weapons[i] = {wep:GetItemName(), wep:GetAllAttributeValues()}; end end end -- Don't bother if we don't find hp_itemname tag if (not itemname) then return; end local item = bot:GetPlayerItemByName(itemname); if (not IsValid(item)) then print("Hat Pickup ERROR -- INVALID ITEMNAME VALUE: ".. itemname); return; end -- We go through this again because loadout depends on what weapons is set to and order of tags is not guaranteed for index, tag in pairs(bot.tags) do if (TagStarts(tag, TAG_LOADOUT_START)) then -- What weapon slot? local slot = 0; if (tag == TAG_LOADOUT_SECONDARY) then slot = 1; elseif (tag == TAG_LOADOUT_MELEE) then slot = 2; end -- Make sure weapons exists if hp_weapons tag wasn't present if (not weapons) then weapons = {}; end if (weapons[slot]) then weapons[slot] = nil; else local wep = bot:GetPlayerItemBySlot(slot) weapons[slot] = {wep:GetItemName(), wep:GetAllAttributeValues()}; end end end -- Get ground position local groundpos = nil; if (not util.IsLagCompensationActive()) then util.StartLagCompensation(bot) local traceresult = util.Trace({ start = bot:GetAbsOrigin(), distance = 8192, angles = Vector(90, 0, 0), -- Down mask = MASK_PLAYERSOLID_BRUSHONLY, collisiongroup = COLLISION_GROUP_PLAYER_MOVEMENT, }); util.FinishLagCompensation(bot) if (traceresult.HitPos) then groundpos = traceresult.HitPos; end end if (not groundpos) then groundpos = bot:GetAbsOrigin(); end -- Pickup entity local pickup = ents.CreateWithKeys("item_bonuspack", { TeamNum = 1 }, false, false); pickup:SetAbsOrigin(groundpos); pickup:Activate(); pickup:DispatchSpawn(); pickup:HideToAll(); pickup:PlaySound("misc/halloween/merasmus_appear.wav"); -- Get model name from item name local modelname = item.m_ModelName; if (not modelname) then modelname = "models/player/items/Soldier/Soldier_bill.mdl"; end -- pills here! -- Share info with pickup for OnPickupTouch pickup._itemname = itemname; pickup._class = bot.m_iClass; pickup._item = item; pickup._cosmetics = cosmetics; pickup._weapons = weapons; pickup._ignoreclass = ignoreclass; pickup._hatattributes = item:GetAllAttributeValues(); -- Hat model display local prop = ents.CreateWithKeys("prop_dynamic", { solid = 0, skin = 1, model = modelname, modelscale = 1.5, rendermode = 1, DisableBoneFollowers = true, disablereceiveshadows = true, disableshadows = true, }, true, true); -- Particle effect local particle = ents.CreateWithKeys("info_particle_system", { effect_name="utaunt_marigoldritual_blue_orbit_holder", start_active=1, }, true, true) particle.m_vecOrigin = pickup:GetAbsOrigin(); particle["Start"](particle); -- Prop think local think_timer = nil; local yaw = 0; local time_elapsed = 0; think_timer = timer.Create(0.015, function() if (IsValid(pickup)) then prop:SetAbsOrigin(pickup:GetAbsOrigin() + itempos); prop:SetAbsAngles(Vector(0, yaw, 0)); yaw = yaw + 4; -- Kill me time_elapsed = time_elapsed + 0.015; if ((time_elapsed >= 8 and time_elapsed < 8.5) or (time_elapsed >= 9 and time_elapsed < 9.5) or (time_elapsed >= 10 and time_elapsed < 10.4) or (time_elapsed >= 10.8 and time_elapsed < 11.2) or (time_elapsed >= 11.4 and time_elapsed < 11.6) or (time_elapsed >= 11.8 and time_elapsed < 12) or (time_elapsed >= 12.2 and time_elapsed < 12.3) or (time_elapsed >= 12.4 and time_elapsed < 12.5)) then prop:AddOutput("renderamt 100"); else prop:AddOutput("renderamt 255"); end else pcall(timer.Stop, think_timer); think_timer = nil; prop:Remove(); particle:Remove(); end end, 0); end function OnPlayerDeath(player) player._item = nil; player._itemname = nil; player._hatattributes = nil; player._cosmetics = nil; player._weapons = nil; end function OnPlayerInventoryApplication(eventTable) local player = ents.GetPlayerByUserId(eventTable.userid); if (not IsValidRealPlayer(player)) then return; end if (player._itemname) then player:GiveLoadout(player._item, player._itemname, player._hatattributes, player._cosmetics, player._weapons); end end function OnPlayerConnected(player) if (player:IsBot()) then player:AddCallback(ON_DEATH, OnBotDeath); else player:AddCallback(ON_KEY_RELEASED, OnPlayerKeyReleased); player:AddCallback(ON_DEATH, OnPlayerDeath); end end ents.AddCreateCallback("item_bonuspack", OnPickupCreated); AddEventCallback("post_inventory_application", OnPlayerInventoryApplication);