zombiesurvival-evolved/gamemodes/zombiesurvival/gamemode/sv_concommands.lua

628 lines
21 KiB
Lua
Raw Normal View History

function GM:ConCommandErrorMessage(pl, message)
pl:CenterNotify(COLOR_RED, message)
pl:SendLua("surface.PlaySound(\"buttons/button10.wav\")")
end
concommand.Add("zs_pointsshopbuy", function(sender, command, arguments)
if not (sender:IsValid() and sender:IsConnected() and sender:IsValidLivingHuman()) or #arguments == 0 then return end
local usescrap = arguments[2]
local midwave = GAMEMODE:GetWave() < GAMEMODE:GetNumberOfWaves() / 2 or GAMEMODE:GetWave() == GAMEMODE:GetNumberOfWaves() / 2 and GAMEMODE:GetWaveActive() and CurTime() < GAMEMODE:GetWaveEnd() - (GAMEMODE:GetWaveEnd() - GAMEMODE:GetWaveStart()) / 2
if sender:IsSkillActive(SKILL_D_LATEBUYER) and not usescrap and midwave then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "late_buyer_warning"))
return
end
if usescrap and not sender:NearRemantler() or not usescrap and not sender:NearArsenalCrate() then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, usescrap and "need_to_be_near_remantler" or "need_to_be_near_arsenal_crate"))
return
end
if not (usescrap or gamemode.Call("PlayerCanPurchase", sender)) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "cant_purchase_right_now"))
return
end
local id = arguments[1]
id = tonumber(id) or id
local itemtab = FindItem(id)
if not itemtab or not itemtab.PointShop then return end
local itemcat = itemtab.Category
if usescrap and not (itemcat == ITEMCAT_TRINKETS or itemcat == ITEMCAT_AMMO) and not itemtab.CanMakeFromScrap then return end
local points = usescrap and sender:GetAmmoCount("scrap") or sender:GetPoints()
local cost = itemtab.Price
if GAMEMODE:IsClassicMode() and itemtab.NoClassicMode then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientFormat(sender, "cant_use_x_in_classic", itemtab.Name))
return
end
if GAMEMODE.ZombieEscape and itemtab.NoZombieEscape then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientFormat(sender, "cant_use_x_in_zombie_escape", itemtab.Name))
return
end
if itemtab.SkillRequirement and not sender:IsSkillActive(itemtab.SkillRequirement) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientFormat(sender, "x_requires_a_skill_you_dont_have", itemtab.Name))
return
end
if itemtab.Tier and GAMEMODE.LockItemTiers and not GAMEMODE.ObjectiveMap and not GAMEMODE.ZombieEscape and not GAMEMODE:IsClassicMode() and GAMEMODE:GetNumberOfWaves() == GAMEMODE.NumberOfWaves and GAMEMODE:GetWave() + (GAMEMODE:GetWaveActive() and 0 or 1) < itemtab.Tier then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientFormat(sender, "tier_x_items_unlock_at_wave_y", itemtab.Tier, itemtab.Tier))
return
end
if not GAMEMODE:HasItemStocks(id) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "out_of_stock"))
return
end
cost = usescrap and math.ceil(GAMEMODE:PointsToScrap(cost)) or math.floor(cost * (sender.ArsenalDiscount or 1))
if points < cost then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, usescrap and "need_to_have_enough_scrap" or "dont_have_enough_points"))
return
end
if itemtab.Callback then
itemtab.Callback(sender)
elseif itemtab.SWEP then
if string.sub(itemtab.SWEP, 1, 6) ~= "weapon" then
if GAMEMODE:GetInventoryItemType(itemtab.SWEP) == INVCAT_TRINKETS and sender:HasInventoryItem(itemtab.SWEP) then
local wep = ents.Create("prop_invitem")
if wep:IsValid() then
wep:SetPos(sender:GetShootPos())
wep:SetAngles(sender:GetAngles())
wep:SetInventoryItemType(itemtab.SWEP)
wep:Spawn()
end
else
sender:AddInventoryItem(itemtab.SWEP)
end
elseif sender:HasWeapon(itemtab.SWEP) then
local stored = weapons.Get(itemtab.SWEP)
if stored and stored.AmmoIfHas then
sender:GiveAmmo(stored.Primary.DefaultClip, stored.Primary.Ammo)
else
local wep = ents.Create("prop_weapon")
if wep:IsValid() then
wep:SetPos(sender:GetShootPos())
wep:SetAngles(sender:GetAngles())
wep:SetWeaponType(itemtab.SWEP)
wep:SetShouldRemoveAmmo(true)
wep:Spawn()
end
end
else
local wep = sender:Give(itemtab.SWEP)
if wep and wep:IsValid() and wep.EmptyWhenPurchased and wep:GetOwner():IsValid() then
if wep.Primary then
local primary = wep:ValidPrimaryAmmo()
if primary then
sender:RemoveAmmo(math.max(0, wep.Primary.DefaultClip - wep.Primary.ClipSize), primary)
end
end
if wep.Secondary then
local secondary = wep:ValidSecondaryAmmo()
if secondary then
sender:RemoveAmmo(math.max(0, wep.Secondary.DefaultClip - wep.Secondary.ClipSize), secondary)
end
end
end
end
GAMEMODE.StatTracking:IncreaseElementKV(STATTRACK_TYPE_WEAPON, itemtab.SWEP, "Purchases", 1)
else
return
end
if usescrap then
sender:RemoveAmmo(cost, "scrap")
sender:SendLua("surface.PlaySound(\"buttons/lever"..math.random(5)..".wav\")")
else
sender:TakePoints(cost)
sender:SendLua("surface.PlaySound(\"ambient/levels/labs/coinslot1.wav\")")
end
sender:PrintTranslatedMessage(HUD_PRINTTALK, usescrap and "created_x_for_y_scrap" or "purchased_x_for_y_points", itemtab.Name, cost)
GAMEMODE:AddItemStocks(id, -1)
if usescrap then
local nearest = sender:NearestRemantler()
if nearest then
local owner = nearest.GetObjectOwner and nearest:GetObjectOwner() or nearest:GetOwner()
if owner:IsValid() and owner ~= sender then
local scrapcom = math.ceil(cost / 8)
nearest:SetScraps(nearest:GetScraps() + scrapcom)
nearest:GetObjectOwner():CenterNotify(COLOR_GREEN, translate.Format("remantle_used", scrapcom))
end
end
else
local nearest = sender:NearestArsenalCrateOwnedByOther()
if nearest then
local owner = nearest.GetObjectOwner and nearest:GetObjectOwner() or nearest:GetOwner()
if owner:IsValid() then
local commission = cost * GAMEMODE.ArsenalCrateCommission
if commission > 0 then
owner:AddPoints(commission, nil, nil, true)
net.Start("zs_commission")
net.WriteEntity(nearest)
net.WriteEntity(sender)
net.WriteFloat(commission)
net.Send(owner)
end
end
end
end
end)
concommand.Add("zs_dismantle", function(sender, command, arguments)
if not (sender:IsValid() and sender:IsConnected() and sender:IsValidLivingHuman()) then return end
local invitem, itypecat, potinv
if #arguments > 0 then
invitem = arguments[1]
end
if invitem and not sender:HasInventoryItem(invitem) then return end
local active = sender:GetActiveWeapon()
local contents, wtbl = active:GetClass()
if not invitem then
wtbl = weapons.Get(contents)
if wtbl.NoDismantle or not (wtbl.AllowQualityWeapons or wtbl.PermitDismantle) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "cannot_dismantle"))
return
end
if wtbl.AmmoIfHas and sender:GetAmmoCount(wtbl.Primary.Ammo) == 0 and active:Clip1() == 0 then
sender:SendLua("surface.PlaySound(\"buttons/button10.wav\")")
return
end
potinv = GAMEMODE.Breakdowns[contents]
else
itypecat = GAMEMODE:GetInventoryItemType(invitem)
if itypecat ~= INVCAT_TRINKETS or GAMEMODE.ZSInventoryItemData[invitem].PermitDismantle then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "cannot_dismantle"))
return
end
potinv = GAMEMODE.Breakdowns[invitem]
end
local scrap = GAMEMODE:GetDismantleScrap(wtbl or GAMEMODE.ZSInventoryItemData[invitem], invitem)
net.Start("zs_ammopickup")
net.WriteUInt(scrap, 16)
net.WriteString("scrap")
net.Send(sender)
sender:GiveAmmo(scrap, "scrap")
if invitem then
sender:TakeInventoryItem(invitem)
else
sender:GetActiveWeapon():EmptyAll(true)
if wtbl and wtbl.AmmoIfHas then
sender:RemoveAmmo(1, wtbl.Primary.Ammo)
end
sender:StripWeapon(contents)
sender:UpdateAltSelectedWeapon()
end
GAMEMODE.StatTracking:IncreaseElementKV(STATTRACK_TYPE_WEAPON, invitem or contents, "Disassembles", 1)
if potinv and potinv.Result then
sender:AddInventoryItem(potinv.Result)
net.Start("zs_invitem")
net.WriteString(potinv.Result)
net.Send(sender)
end
end)
concommand.Add("zs_upgrade", function(sender, command, arguments)
if not (sender:IsValid() and sender:IsConnected() and sender:IsValidLivingHuman()) then return end
if not sender:NearRemantler() then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "need_to_be_near_remantler"))
return
end
local nearest = sender:NearestRemantler()
local contents = sender:GetActiveWeapon():GetClass()
local contentstbl = weapons.Get(contents)
local contentsqua = contentstbl.QualityTier
local desiredqua = contentsqua and contentsqua + 1 or 1
local branch = contentstbl.Branch
if not contentsqua and #arguments > 0 then
branch = tonumber(arguments[1])
end
if not (nearest and nearest:IsValid() and contents) then return end
local wtbl = weapons.Get(contents)
local scrapcost = GAMEMODE:GetUpgradeScrap(wtbl, desiredqua)
if wtbl.AmmoIfHas and sender:GetAmmoCount(wtbl.Primary.Ammo) == 0 then
sender:SendLua("surface.PlaySound(\"buttons/button10.wav\")")
return
end
if sender:GetAmmoCount("scrap") < scrapcost then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "need_to_have_enough_scrap"))
return
end
local upgclass = GAMEMODE:GetWeaponClassOfQuality(not contentsqua and contents or contentstbl.BaseQuality, desiredqua, branch)
local classtbl = weapons.Get(upgclass)
if not classtbl then return end
if sender:HasWeapon(upgclass) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "remantle_cannot"))
return
end
local upgname = classtbl.PrintName
sender:CenterNotify(COLOR_CYAN, translate.ClientGet(sender, "remantle_success"), color_white, " "..upgname)
sender:SendLua("surface.PlaySound(\"buttons/lever"..math.random(5)..".wav\")")
sender:RemoveAmmo(scrapcost, "scrap")
local wep = sender:GiveEmptyWeapon(upgclass)
if wep and wep:IsValid() then
sender:GetActiveWeapon():EmptyAll(true)
sender:SelectWeapon(upgclass)
sender:StripWeapon(contents)
sender:UpdateAltSelectedWeapon()
if wtbl.AmmoIfHas then
sender:RemoveAmmo(1, wtbl.Primary.Ammo)
end
if wep.AmmoIfHas then
sender:GiveAmmo(1, wep.Primary.Ammo)
end
net.Start("zs_remantleconf")
net.Send(sender)
GAMEMODE.StatTracking:IncreaseElementKV(STATTRACK_TYPE_WEAPON, upgclass, "Upgrades", 1)
end
local owner = nearest.GetObjectOwner and nearest:GetObjectOwner() or nearest:GetOwner()
if owner:IsValid() and owner ~= sender then
local scrapcom = math.ceil(scrapcost * 0.08)
nearest:SetScraps(nearest:GetScraps() + scrapcom)
nearest:GetObjectOwner():CenterNotify(COLOR_GREEN, translate.Format("remantle_used", scrapcom))
end
end)
concommand.Add("worthrandom", function(sender, command, arguments)
if sender:IsValid() and sender:IsConnected() and gamemode.Call("PlayerCanCheckout", sender) then
gamemode.Call("GiveRandomEquipment", sender)
end
end)
concommand.Add("worthcheckout", function(sender, command, arguments)
if not (sender:IsValid() and sender:IsConnected()) or #arguments == 0 then return end
if not gamemode.Call("PlayerCanCheckout", sender) then
sender:CenterNotify(COLOR_RED, translate.ClientGet(sender, "cant_use_worth_anymore"))
return
end
local cost = 0
local hasalready = {}
for _, id in pairs(arguments) do
id = tonumber(id) or id
local tab = FindStartingItem(id)
if tab and not hasalready[id] and (not tab.SkillRequirement or sender:IsSkillActive(tab.SkillRequirement)) then
cost = cost + tab.Price
hasalready[id] = true
end
end
if cost > GAMEMODE.StartingWorth + (sender.ExtraStartingWorth or 0) then return end
hasalready = {}
for _, id in pairs(arguments) do
id = tonumber(id) or id
local tab = FindStartingItem(id)
if tab and not hasalready[id] then
if tab.SkillRequirement and not sender:IsSkillActive(tab.SkillRequirement) then
sender:PrintMessage(HUD_PRINTTALK, translate.ClientFormat(sender, "x_requires_a_skill_you_dont_have", tab.Name))
elseif tab.NoClassicMode and GAMEMODE:IsClassicMode() then
sender:PrintMessage(HUD_PRINTTALK, translate.ClientFormat(sender, "cant_use_x_in_classic_mode", tab.Name))
elseif tab.Callback then
tab.Callback(sender)
hasalready[id] = true
elseif tab.SWEP then
GAMEMODE.StatTracking:IncreaseElementKV(STATTRACK_TYPE_WEAPON, tab.SWEP, "Checkouts", 1)
sender:StripWeapon(tab.SWEP) -- "Fixes" players giving each other empty weapons to make it so they get no ammo from the Worth menu purchase.
if GAMEMODE.ZSInventoryItemData[tab.SWEP] then
sender:AddInventoryItem(tab.SWEP)
else
sender:Give(tab.SWEP)
end
hasalready[id] = true
end
end
end
if table.Count(hasalready) > 0 then
GAMEMODE.CheckedOut[sender:UniqueID()] = true
end
gamemode.Call("RemoveDuplicateAmmo", sender)
end)
concommand.Add("zsdropweapon", function(sender, command, arguments)
local currentwep = sender:GetActiveWeapon()
if GAMEMODE.ZombieEscape then
local hwep, zwep = sender:GetWeapon("weapon_elite"), sender:GetWeapon("weapon_knife")
if hwep and hwep:IsValid() then
sender:DropWeapon(hwep)
elseif zwep and zwep:IsValid() then
sender:DropWeapon(zwep)
end
return
end
if not (sender:IsValid() and sender:Alive() and sender:Team() == TEAM_HUMAN) or CurTime() < (sender.NextWeaponDrop or 0) or GAMEMODE.ZombieEscape then return end
sender.NextWeaponDrop = CurTime() + 0.15
local invitem
if #arguments > 0 then
invitem = arguments[1]
end
if invitem and not sender:HasInventoryItem(invitem) then return end
if invitem or (currentwep and currentwep:IsValid()) then
local ent = invitem and sender:DropInventoryItemByType(invitem) or sender:DropWeaponByType(currentwep:GetClass())
if ent and ent:IsValid() then
local shootpos = sender:GetShootPos()
local aimvec = sender:GetAimVector()
ent:SetPos(util.TraceHull({start = shootpos, endpos = shootpos + aimvec * 32, mask = MASK_SOLID, filter = sender, mins = Vector(-2, -2, -2), maxs = Vector(2, 2, 2)}).HitPos)
ent:SetAngles(sender:GetAngles())
end
end
end)
concommand.Add("zsemptyclip", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not (sender:IsValid() and sender:Alive() and sender:Team() == TEAM_HUMAN) then return end
sender.NextEmptyClip = sender.NextEmptyClip or 0
if sender.NextEmptyClip <= CurTime() then
sender.NextEmptyClip = CurTime() + 0.1
local wep = sender:GetActiveWeapon()
if wep:IsValid() and (not wep.NoMagazine and not wep.AmmoIfHas or wep.AllowEmpty) then
local primary = wep:ValidPrimaryAmmo()
if primary and 0 < wep:Clip1() then
sender:GiveAmmo(wep:Clip1(), primary, true)
wep:SetClip1(0)
end
local secondary = wep:ValidSecondaryAmmo()
if secondary and 0 < wep:Clip2() then
sender:GiveAmmo(wep:Clip2(), secondary, true)
wep:SetClip2(0)
end
end
end
end)
function GM:TryGetLockOnTrace(sender, arguments)
local ent
local dent = Entity(tonumbersafe(arguments[2] or 0) or 0)
if GAMEMODE:ValidMenuLockOnTarget(sender, dent) then
ent = dent
end
if not ent then
ent = sender:MeleeTrace(48, 2, nil, nil, true).Entity
end
return ent
end
concommand.Add("zsgiveammo", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not sender:IsValid() or not sender:Alive() or sender:Team() ~= TEAM_HUMAN then return end
local ammotype = arguments[1]
if not ammotype or #ammotype == 0 or not GAMEMODE.AmmoCache[ammotype] then return end
local count = sender:GetAmmoCount(ammotype)
if count <= 0 then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "no_spare_ammo_to_give"))
return
end
local ent = GAMEMODE:TryGetLockOnTrace(sender, arguments)
if ent and ent:IsValidLivingHuman() then
local desiredgive = math.min(count, GAMEMODE.AmmoCache[ammotype])
if desiredgive >= 1 then
sender:RemoveAmmo(desiredgive, ammotype)
ent:GiveAmmo(desiredgive, ammotype)
if CurTime() >= (sender.NextGiveAmmoSound or 0) then
sender.NextGiveAmmoSound = CurTime() + 1
sender:PlayGiveAmmoSound()
end
sender:RestartGesture(ACT_GMOD_GESTURE_ITEM_GIVE)
net.Start("zs_ammogive")
net.WriteUInt(desiredgive, 16)
net.WriteString(ammotype)
net.WriteEntity(ent)
net.Send(sender)
net.Start("zs_ammogiven")
net.WriteUInt(desiredgive, 16)
net.WriteString(ammotype)
net.WriteEntity(sender)
net.Send(ent)
return
end
else
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "no_person_in_range"))
end
end)
concommand.Add("zsgiveweapon", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not (sender:IsValid() and sender:Alive() and sender:Team() == TEAM_HUMAN) then return end
local invitem
if #arguments > 0 then
invitem = arguments[2]
end
if invitem and not sender:HasInventoryItem(invitem) then return end
local currentwep = sender:GetActiveWeapon()
if not invitem and not IsValid(currentwep) then return end
local ent = GAMEMODE:TryGetLockOnTrace(sender, arguments)
if ent and ent:IsValidLivingHuman() then
if not invitem then
if ent:HasWeapon(currentwep:GetClass()) then
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "person_has_weapon"))
else
sender:GiveWeaponByType(currentwep, ent, false)
end
else
sender:GiveInventoryItemByType(invitem, ent)
end
else
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "no_person_in_range"))
end
end)
concommand.Add("zsgiveweaponclip", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not (sender:IsValid() and sender:Alive() and sender:Team() == TEAM_HUMAN) then return end
local currentwep = sender:GetActiveWeapon()
if currentwep and currentwep:IsValid() then
local ent = GAMEMODE:TryGetLockOnTrace(sender, arguments)
if ent and ent:IsValidLivingHuman() then
if not ent:HasWeapon(currentwep:GetClass()) then
sender:GiveWeaponByType(currentwep, ent, true)
else
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "person_has_weapon"))
end
else
GAMEMODE:ConCommandErrorMessage(sender, translate.ClientGet(sender, "no_person_in_range"))
end
end
end)
concommand.Add("zsdropammo", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not sender:IsValid() or not sender:Alive() or sender:Team() ~= TEAM_HUMAN or CurTime() < (sender.NextDropClip or 0) then return end
sender.NextDropClip = CurTime() + 0.2
local wep = sender:GetActiveWeapon()
if not wep:IsValid() then return end
local ammotype = arguments[1] or wep:GetPrimaryAmmoTypeString()
if GAMEMODE.AmmoNames[ammotype] and GAMEMODE.AmmoCache[ammotype] then
local ent = sender:DropAmmoByType(ammotype, GAMEMODE.AmmoCache[ammotype] * 2)
if ent and ent:IsValid() then
ent:SetPos(sender:EyePos() + sender:GetAimVector() * 8)
ent:SetAngles(sender:GetAngles())
local phys = ent:GetPhysicsObject()
if phys:IsValid() then
phys:Wake()
phys:SetVelocityInstantaneous(sender:GetVelocity() * 0.85)
end
end
end
end)
concommand.Add("zs_resupplyammotype", function(sender, command, arguments)
if GAMEMODE.ZombieEscape then return end
if not (sender:IsValid() and sender:Alive() and sender:Team() == TEAM_HUMAN) then return end
local ammotype = arguments[1]
if not ammotype or #ammotype == 0 or not (ammotype == "default" or GAMEMODE.AmmoResupply[ammotype]) then return end
sender.ResupplyChoice = ammotype ~= "default" and ammotype or nil
end)
concommand.Add("zs_shitmap_check", function(sender, command, arguments)
if not sender:IsAdmin() then return end
local teleporters = ents.FindByClass("trigger_teleport")
local buttons = ents.FindByClass("func_button")
local doors = ents.FindByClass("func_door_rotating")
table.Add(doors, ents.FindByClass("func_movelinear"))
sender:PrintMessage(HUD_PRINTCONSOLE, "Teleports: "..#teleporters.." Buttons: "..#buttons.." Doors: "..#doors)
end)
concommand.Add("zs_shitmap_toteleport", function(sender, command, arguments)
if not sender:IsSuperAdmin() then return end
local ent = ents.FindByClass("trigger_teleport")[tonumber(arguments[1])]
if ent then
sender:SetPos(ent:WorldSpaceCenter())
end
end)
concommand.Add("zs_shitmap_teleport_on", function(sender, command, arguments)
if not sender:IsSuperAdmin() then return end
for _, ent in pairs(ents.FindByClass("trigger_teleport")) do
ent:Fire("enable", "", 0)
end
end)
concommand.Add("zs_shitmap_teleport_off", function(sender, command, arguments)
if not sender:IsSuperAdmin() then return end
for _, ent in pairs(ents.FindByClass("trigger_teleport")) do
ent:Fire("enable", "", 0)
end
end)
concommand.Add("zs_shitmap_tobutton", function(sender, command, arguments)
if not sender:IsSuperAdmin() then return end
local ent = ents.FindByClass("func_button")[tonumber(arguments[1])]
if ent then
sender:SetPos(ent:WorldSpaceCenter())
end
end)
concommand.Add("zs_shitmap_tomover", function(sender, command, arguments)
if not sender:IsSuperAdmin() then return end
local entities = ents.FindByClass("func_door_rotating")
table.Add(entities, ents.FindByClass("func_movelinear"))
local ent = entities[tonumber(arguments[1])]
if ent then
sender:SetPos(ent:WorldSpaceCenter())
end
end)