diff --git a/addon.txt b/addon.txt new file mode 100644 index 0000000..e69de29 diff --git a/lua/autorun/pointshop.lua b/lua/autorun/pointshop.lua new file mode 100644 index 0000000..eb6a413 --- /dev/null +++ b/lua/autorun/pointshop.lua @@ -0,0 +1,25 @@ +if SERVER then + AddCSLuaFile() + AddCSLuaFile('vgui/DPointShopMenu.lua') + AddCSLuaFile('vgui/DPointShopItem.lua') + AddCSLuaFile('sh_pointshop.lua') + AddCSLuaFile('sh_config.lua') + AddCSLuaFile('cl_player_extension.lua') + AddCSLuaFile('cl_pointshop.lua') + + include('sh_pointshop.lua') + include('sh_config.lua') + include('sv_player_extension.lua') + include('sv_pointshop.lua') +end + +if CLIENT then + include('vgui/DPointShopMenu.lua') + include('vgui/DPointShopItem.lua') + include('sh_pointshop.lua') + include('sh_config.lua') + include('cl_player_extension.lua') + include('cl_pointshop.lua') +end + +PS:LoadItems() \ No newline at end of file diff --git a/lua/cl_player_extension.lua b/lua/cl_player_extension.lua new file mode 100644 index 0000000..99788b3 --- /dev/null +++ b/lua/cl_player_extension.lua @@ -0,0 +1,81 @@ +local Player = FindMetaTable('Player') + +-- items + +function Player:PS_GetItems() + return self.PS_Items or {} +end + +function Player:PS_HasItem(item_id) + return self.PS_Items[item_id] and true or false +end + +function Player:PS_HasItemEquipped(item_id) + if not self:PS_HasItem(item_id) then return false end + + return self.PS_Items[item_id].Equipped or false +end + +function Player:PS_BuyItem(item_id) + if self:PS_HasItem(item_id) then return false end + if not self:PS_HasPoints(PS.Items[item_id].Price) then return false end + + net.Start('PS_BuyItem') + net.WriteString(item_id) + net.SendToServer() +end + +function Player:PS_SellItem(item_id) + if not self:PS_HasItem(item_id) then return false end + + net.Start('PS_SellItem') + net.WriteString(item_id) + net.SendToServer() +end + +function Player:PS_EquipItem(item_id) + if not self:PS_HasItem(item_id) then return false end + + net.Start('PS_EquipItem') + net.WriteString(item_id) + net.SendToServer() +end + +function Player:PS_HolsterItem(item_id) + if not self:PS_HasItem(item_id) then return false end + + net.Start('PS_HolsterItem') + net.WriteString(item_id) + net.SendToServer() +end + +-- points + +function Player:PS_GetPoints() + return self.PS_Points or 0 +end + +function Player:PS_HasPoints(points) + return self:PS_GetPoints() >= points +end + +-- clientside models + +function Player:PS_AddClientsideModel(item_id) + if not PS.Items[item_id] then return false end + + local ITEM = PS.Items[item_id] + + local mdl = ClientsideModel(ITEM.Model, RENDERGROUP_OPAQUE) + mdl:SetNoDraw(true) + + if not PS.ClientsideModels[self] then PS.ClientsideModels[self] = {} end + PS.ClientsideModels[self][item_id] = mdl +end + +function Player:PS_RemoveClientsideModel(item_id) + if not PS.Items[item_id] then return false end + if not PS.ClientsideModels[self][item_id] then return false end + + PS.ClientsideModels[self][item_id] = nil +end diff --git a/lua/cl_pointshop.lua b/lua/cl_pointshop.lua new file mode 100644 index 0000000..b072d18 --- /dev/null +++ b/lua/cl_pointshop.lua @@ -0,0 +1,119 @@ +PS.ShopMenu = nil +PS.ClientsideModels = {} + +-- menu stuff + +function PS:ToggleMenu() + if not PS.ShopMenu then + PS.ShopMenu = vgui.Create('DPointShopMenu') + PS.ShopMenu:SetVisible(false) + end + + if PS.ShopMenu:IsVisible() then + PS.ShopMenu:Hide() + gui.EnableScreenClicker(false) + else + PS.ShopMenu:Show() + gui.EnableScreenClicker(true) + end +end + +-- modification stuff + +function PS:ShowColorChooser(item, modifications) + -- TODO: Do this + local chooser = vgui.Create('DPointShopColorChooser') + chooser:SetColor(modifications.color) + + chooser.OnChoose = function(color) + self:SendModifications(item.ID, {color = color}) + end +end + +function PS:SendModifications(item_id, modifications) + net.Start('PS_ModifyItem') + net.WriteString(item_id) + net.WriteTable(modifications) + net.SendToServer() +end + +-- net hooks + +net.Receive('PS_ToggleMenu', function(length) + PS:ToggleMenu() +end) + +net.Receive('PS_Items', function(length) + local ply = net.ReadEntity() + local items = net.ReadTable() + ply.PS_Items = PS:ValidateItems(items) +end) + +net.Receive('PS_Points', function(length) + local ply = net.ReadEntity() + local points = net.ReadInt(32) + ply.PS_Points = PS:ValidatePoints(points) +end) + +net.Receive('PS_AddClientsideModel', function(length) + local ply = net.ReadEntity() + local item_id = net.ReadString() + + ply:PS_AddClientsideModel(item_id) +end) + +net.Receive('PS_RemoveClientsideModel', function(length) + local ply = net.ReadEntity() + local item_id = net.ReadString() + + ply:PS_RemoveClientsideModel(item_id) +end) + +net.Receive('PS_SendClientsideModels', function(length) + for ply, items in pairs(net.ReadTable()) do + for _, item_id in pairs(items) do + if PS.Items[item_id] then + ply:PS_AddClientsideModel(item_id) + end + end + end +end) + +-- hooks + +hook.Add('PostPlayerDraw', 'PS_PostPlayerDraw', function(ply) + if not ply:Alive() then return end + if ply == LocalPlayer() and GetViewEntity():GetClass() == 'player' then return end + if not PS.ClientsideModels[ply] then return end + + for item_id, model in pairs(PS.ClientsideModels[ply]) do + if not PS.Items[item_id] then PS.ClientsideModel[ply][item_id] = nil continue end + + local ITEM = PS.Items[item_id] + + if not ITEM.Attachment and not ITEM.Bone then PS.ClientsideModel[ply][item_id] = nil continue end + + local pos = Vector() + local ang = Angle() + + if ITEM.Attachment then + local attach = ply:GetAttachment(ply:LookupAttachment(ITEM.Attachment)) + pos = attach.Pos + ang = attach.Ang + else + pos, ang = ply:GetBonePosition(ply:LookupBone(ITEM.Bone)) + end + + model, pos, ang = ITEM:ModifyClientsideModel(ply, model, pos, ang) + + model:SetPos(pos) + model:SetAngles(ang) + + model:SetRenderOrigin(pos) + model:SetRenderAngles(ang) + model:SetupBones() + model:DrawModel() + model:SetRenderOrigin() + model:SetRenderAngles() + end +end) \ No newline at end of file diff --git a/lua/items/accessories/__category.lua b/lua/items/accessories/__category.lua new file mode 100644 index 0000000..977fd10 --- /dev/null +++ b/lua/items/accessories/__category.lua @@ -0,0 +1,3 @@ +CATEGORY.Name = 'Accessories' +CATEGORY.Icon = 'add' +CATEGORY.AllowedEquiped = 1 \ No newline at end of file diff --git a/lua/items/accessories/backpack.lua b/lua/items/accessories/backpack.lua new file mode 100644 index 0000000..dcec7c8 --- /dev/null +++ b/lua/items/accessories/backpack.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'Backpack' +ITEM.Price = 100 +ITEM.Model = 'models/props_c17/SuitCase_Passenger_Physics.mdl' +ITEM.Bone = 'ValveBiped.Bip01_Spine2' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.8, 0) + pos = pos + (ang:Right() * 5) + (ang:Up() * 6) + (ang:Forward() * 2) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/__category.lua b/lua/items/headshatsmasks/__category.lua new file mode 100644 index 0000000..e42d6fc --- /dev/null +++ b/lua/items/headshatsmasks/__category.lua @@ -0,0 +1,3 @@ +CATEGORY.Name = 'Hats, Heads and Masks' +CATEGORY.Icon = 'emoticon_smile' +CATEGORY.AllowedEquiped = 1 \ No newline at end of file diff --git a/lua/items/headshatsmasks/afro.lua b/lua/items/headshatsmasks/afro.lua new file mode 100644 index 0000000..0837447 --- /dev/null +++ b/lua/items/headshatsmasks/afro.lua @@ -0,0 +1,21 @@ +ITEM.Name = 'Afro' +ITEM.Price = 200 +ITEM.Model = 'models/dav0r/hoverball.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(1.6, 0) + model:SetMaterial('models/weapons/v_stunbaton/w_shaft01a') + pos = pos + (ang:Forward() * -7) + (ang:Up() * 8) + ang:RotateAroundAxis(ang:Right(), 90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/bombhead.lua b/lua/items/headshatsmasks/bombhead.lua new file mode 100644 index 0000000..624121b --- /dev/null +++ b/lua/items/headshatsmasks/bombhead.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Bomb Head' +ITEM.Price = 100 +ITEM.Model = 'models/Combine_Helicopter/helicopter_bomb01.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.5, 0) + pos = pos + (ang:Forward() * -2) + ang:RotateAroundAxis(ang:Right(), 90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/buckethat.lua b/lua/items/headshatsmasks/buckethat.lua new file mode 100644 index 0000000..7d223e6 --- /dev/null +++ b/lua/items/headshatsmasks/buckethat.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Bucket Hat' +ITEM.Price = 100 +ITEM.Model = 'models/props_junk/MetalBucket01a.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.7, 0) + pos = pos + (ang:Forward() * -5) + (ang:Up() * 5) + ang:RotateAroundAxis(ang:Right(), 200) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/clockmask.lua b/lua/items/headshatsmasks/clockmask.lua new file mode 100644 index 0000000..2e926b5 --- /dev/null +++ b/lua/items/headshatsmasks/clockmask.lua @@ -0,0 +1,18 @@ +ITEM.Name = 'Clock Mask' +ITEM.Price = 50 +ITEM.Model = 'models/props_c17/clock01.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + ang:RotateAroundAxis(ang:Right(), -90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/conehat.lua b/lua/items/headshatsmasks/conehat.lua new file mode 100644 index 0000000..81ec575 --- /dev/null +++ b/lua/items/headshatsmasks/conehat.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Cone Hat' +ITEM.Price = 100 +ITEM.Model = 'models/props_junk/TrafficCone001a.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.8, 0) + pos = pos + (ang:Forward() * -7) + (ang:Up() * 11) + ang:RotateAroundAxis(ang:Right(), 20) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/headcrabhat.lua b/lua/items/headshatsmasks/headcrabhat.lua new file mode 100644 index 0000000..55ca176 --- /dev/null +++ b/lua/items/headshatsmasks/headcrabhat.lua @@ -0,0 +1,21 @@ +ITEM.Name = 'Headcrab Hat' +ITEM.Price = 100 +ITEM.Model = 'models/headcrabclassic.mdl' +ITEM.Attachment = 'eyes' +ITEM.AdminOnly = true + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.7, 0) + pos = pos + (ang:Forward() * 2) + ang:RotateAroundAxis(ang:Right(), 20) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/lampshadehat.lua b/lua/items/headshatsmasks/lampshadehat.lua new file mode 100644 index 0000000..b60e5f7 --- /dev/null +++ b/lua/items/headshatsmasks/lampshadehat.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Lampshade Hat' +ITEM.Price = 100 +ITEM.Model = 'models/props_c17/lampShade001a.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.7, 0) + pos = pos + (ang:Forward() * -3.5) + (ang:Up() * 4) + ang:RotateAroundAxis(ang:Right(), 10) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/melonhead.lua b/lua/items/headshatsmasks/melonhead.lua new file mode 100644 index 0000000..50bb999 --- /dev/null +++ b/lua/items/headshatsmasks/melonhead.lua @@ -0,0 +1,18 @@ +ITEM.Name = 'Melon Head' +ITEM.Price = 100 +ITEM.Model = 'models/props_junk/watermelon01.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + pos = pos + (ang:Forward() * -2) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/monitorhead.lua b/lua/items/headshatsmasks/monitorhead.lua new file mode 100644 index 0000000..668449e --- /dev/null +++ b/lua/items/headshatsmasks/monitorhead.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'Monitor Head' +ITEM.Price = 100 +ITEM.Model = 'models/props_lab/monitor02.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.75, 0) + pos = pos + (ang:Forward() * -5) + (ang:Up() * -5) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/noentrymask.lua b/lua/items/headshatsmasks/noentrymask.lua new file mode 100644 index 0000000..cf83dc6 --- /dev/null +++ b/lua/items/headshatsmasks/noentrymask.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'No Entry Mask' +ITEM.Price = 50 +ITEM.Model = 'models/props_c17/streetsign004f.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.7, 0) + pos = pos + (ang:Forward() * 3) + ang:RotateAroundAxis(ang:Up(), -90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/panhat.lua b/lua/items/headshatsmasks/panhat.lua new file mode 100644 index 0000000..b3db0c1 --- /dev/null +++ b/lua/items/headshatsmasks/panhat.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'Pan Hat' +ITEM.Price = 100 +ITEM.Model = 'models/props_interiors/pot02a.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + pos = pos + (ang:Forward() * -3) + (ang:Up() * 2) + (ang:Right() * 5.5) + ang:RotateAroundAxis(ang:Right(), 180) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/skullhead.lua b/lua/items/headshatsmasks/skullhead.lua new file mode 100644 index 0000000..b06a843 --- /dev/null +++ b/lua/items/headshatsmasks/skullhead.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Skull Head' +ITEM.Price = 150 +ITEM.Model = 'models/Gibs/HGIBS.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(1.6, 0) + pos = pos + (ang:Forward() * -2.5) + ang:RotateAroundAxis(ang:Right(), -15) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/snowmanhead.lua b/lua/items/headshatsmasks/snowmanhead.lua new file mode 100644 index 0000000..76fad65 --- /dev/null +++ b/lua/items/headshatsmasks/snowmanhead.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'Snowman Head' +ITEM.Price = 200 +ITEM.Model = 'models/props/cs_office/Snowman_face.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + pos = pos + (ang:Forward() * -2.2) + ang:RotateAroundAxis(ang:Up(), -90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/spotlighthead.lua b/lua/items/headshatsmasks/spotlighthead.lua new file mode 100644 index 0000000..a877167 --- /dev/null +++ b/lua/items/headshatsmasks/spotlighthead.lua @@ -0,0 +1,12 @@ +ITEM.Name = 'Spotlight Head' +ITEM.Price = 100 +ITEM.Model = 'models/props_wasteland/light_spotlight01_lamp.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end diff --git a/lua/items/headshatsmasks/turtlehat.lua b/lua/items/headshatsmasks/turtlehat.lua new file mode 100644 index 0000000..0698522 --- /dev/null +++ b/lua/items/headshatsmasks/turtlehat.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'Turtle Hat' +ITEM.Price = 100 +ITEM.Model = 'models/props/de_tides/Vending_turtle.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + pos = pos + (ang:Forward() * -3) + ang:RotateAroundAxis(ang:Up(), -90) + + return model, pos, ang +end diff --git a/lua/items/headshatsmasks/tvhead.lua b/lua/items/headshatsmasks/tvhead.lua new file mode 100644 index 0000000..71ac840 --- /dev/null +++ b/lua/items/headshatsmasks/tvhead.lua @@ -0,0 +1,19 @@ +ITEM.Name = 'TV Head' +ITEM.Price = 100 +ITEM.Model = 'models/props_c17/tv_monitor01.mdl' +ITEM.Attachment = 'eyes' + +function ITEM:OnEquip(ply, modifications) + ply:PS_AddClientsideModel(self.ID) +end + +function ITEM:OnHolster(ply) + ply:PS_RemoveClientsideModel(self.ID) +end + +function ITEM:ModifyClientsideModel(ply, model, pos, ang) + model:SetModelScale(0.8, 0) + pos = pos + (ang:Right() * -2) + (ang:Forward() * -3) + (ang:Up() * 0.5) + + return model, pos, ang +end diff --git a/lua/items/playermodels/__category.lua b/lua/items/playermodels/__category.lua new file mode 100644 index 0000000..4f1243b --- /dev/null +++ b/lua/items/playermodels/__category.lua @@ -0,0 +1,2 @@ +CATEGORY.Name = 'Player Models' +CATEGORY.Icon = 'user' diff --git a/lua/items/playermodels/kleiner.lua b/lua/items/playermodels/kleiner.lua new file mode 100644 index 0000000..04334c9 --- /dev/null +++ b/lua/items/playermodels/kleiner.lua @@ -0,0 +1,17 @@ +ITEM.Name = 'Kleiner' +ITEM.Price = 250 +ITEM.Model = 'models/player/kleiner.mdl' + +function ITEM:OnEquip(ply, modifications) + if not ply._OldModel then + ply._OldModel = ply:GetModel() + end + + timer.Simple(1, function() ply:SetModel(self.Model) end) +end + +function ITEM:OnHolster(ply) + if ply._OldModel then + ply:SetModel(ply._OldModel) + end +end \ No newline at end of file diff --git a/lua/items/trails/__category.lua b/lua/items/trails/__category.lua new file mode 100644 index 0000000..c5abac2 --- /dev/null +++ b/lua/items/trails/__category.lua @@ -0,0 +1,2 @@ +CATEGORY.Name = 'Trails' +CATEGORY.Icon = 'rainbow' diff --git a/lua/items/trails/electric.lua b/lua/items/trails/electric.lua new file mode 100644 index 0000000..47be948 --- /dev/null +++ b/lua/items/trails/electric.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Electric Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/electric.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.ElectricTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.ElectricTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.ElectricTrail) + self:OnEquip(ply, modifications) +end \ No newline at end of file diff --git a/lua/items/trails/laser.lua b/lua/items/trails/laser.lua new file mode 100644 index 0000000..7fe604c --- /dev/null +++ b/lua/items/trails/laser.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Laser Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/laser.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.LaserTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.LaserTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.LaserTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/items/trails/loltrail.lua b/lua/items/trails/loltrail.lua new file mode 100644 index 0000000..3910b28 --- /dev/null +++ b/lua/items/trails/loltrail.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'LOL Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/lol.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.LolTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.LolTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.LolTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/items/trails/lovetrail.lua b/lua/items/trails/lovetrail.lua new file mode 100644 index 0000000..c65694c --- /dev/null +++ b/lua/items/trails/lovetrail.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Love Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/love.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.LoveTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.LoveTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.LoveTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/items/trails/plasmatrail.lua b/lua/items/trails/plasmatrail.lua new file mode 100644 index 0000000..546d86e --- /dev/null +++ b/lua/items/trails/plasmatrail.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Plasma Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/plasma.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.PlasmaTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.PlasmaTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.PlasmaTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/items/trails/smoke.lua b/lua/items/trails/smoke.lua new file mode 100644 index 0000000..6e4e55e --- /dev/null +++ b/lua/items/trails/smoke.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Smoke Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/smoke.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.SmokeTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.SmokeTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.SmokeTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/items/trails/tube.lua b/lua/items/trails/tube.lua new file mode 100644 index 0000000..3f1377a --- /dev/null +++ b/lua/items/trails/tube.lua @@ -0,0 +1,20 @@ +ITEM.Name = 'Tube Trail' +ITEM.Price = 150 +ITEM.Material = 'trails/tube.vmt' + +function ITEM:OnEquip(ply, modifications) + ply.TubeTrail = util.SpriteTrail(ply, 0, modifications.color, false, 15, 1, 4, 0.125, self.Material) +end + +function ITEM:OnHolster(ply) + SafeRemoveEntity(ply.TubeTrail) +end + +function ITEM:Modify(modifications) + PS:ShowColorChooser(self, modifications) +end + +function ITEM:OnModify(ply, modifications) + SafeRemoveEntity(ply.TubeTrail) + self:OnEquip(ply, modifications) +end diff --git a/lua/sh_config.lua b/lua/sh_config.lua new file mode 100644 index 0000000..d06d4bf --- /dev/null +++ b/lua/sh_config.lua @@ -0,0 +1,7 @@ +PS.Config = {} + +PS.Config.ShopKey = "F3" + +PS.Config.CalculateSellPrice = function(original) + return math.Round(original * 0.75) +end \ No newline at end of file diff --git a/lua/sh_pointshop.lua b/lua/sh_pointshop.lua new file mode 100644 index 0000000..f022c7d --- /dev/null +++ b/lua/sh_pointshop.lua @@ -0,0 +1,79 @@ +PS = {} +PS.__index = PS + +PS.Items = {} +PS.Categories = {} +PS.ClientsideModels = {} + +-- validation + +function PS:ValidateItems(items) + if type(items) ~= 'table' then return {} end + + -- Remove any items that no longer exist + for item_id, item in pairs(items) do + if not self.Items[item_id] then + items[item_id] = nil + end + end + + return items +end + +function PS:ValidatePoints(points) + if type(points) != 'number' then return 0 end + + return points >= 0 and points or 0 +end + +function PS:LoadItems() + local _, dirs = file.Find('items/*', 'LUA') + + for _, category in pairs(dirs) do + local f, _ = file.Find('items/' .. category .. '/__category.lua', 'LUA') + + if #f > 0 then + CATEGORY = {} + + CATEGORY.Name = '' + CATEGORY.Icon = '' + CATEGORY.AllowedEquiped = -1 + + if SERVER then AddCSLuaFile('items/' .. category .. '/__category.lua') end + include('items/' .. category .. '/__category.lua') + + if not PS.Categories[category] then + PS.Categories[category] = CATEGORY + end + + local files, _ = file.Find('items/' .. category .. '/*.lua', 'LUA') + + for _, name in pairs(files) do + if name ~= '__category.lua' then + if SERVER then AddCSLuaFile('items/' .. category .. '/' .. name) end + + ITEM = {} + + ITEM.__index = ITEM + ITEM.ID = string.gsub(name, '.lua', '') + ITEM.Category = CATEGORY.Name + ITEM.Price = 0 + + ITEM.OnBuy = function() end + ITEM.OnSell = function() end + ITEM.OnEquip = function() end + ITEM.OnHolster = function() end + ITEM.ModifyClientsideModel = function(s, ply, model, pos, ang) + return model, pos, ang + end + + include('items/' .. category .. '/' .. name) + + self.Items[ITEM.ID] = ITEM + + ITEM = nil + end + end + end + end +end \ No newline at end of file diff --git a/lua/sv_player_extension.lua b/lua/sv_player_extension.lua new file mode 100644 index 0000000..34b3477 --- /dev/null +++ b/lua/sv_player_extension.lua @@ -0,0 +1,245 @@ +local Player = FindMetaTable('Player') + +function Player:PS_PlayerSpawn() + if not self:Alive() then return end + if self.IsSpec and self:IsSpec() then return end + + timer.Simple(1, function() + for item_id, item in pairs(self.PS_Items) do + local ITEM = PS.Items[item_id] + if item.Equipped then + ITEM:OnEquip(self, item.Modifiers) + end + end + end) +end + +function Player:PS_PlayerDeath(rar) + for item_id, item in pairs(self.PS_Items) do + if item.Equipped then + local ITEM = PS.Items[item_id] + ITEM:OnHolster(self, item.Modifiers) + end + end +end + +function Player:PS_Initialize() + self.PS_Items = {} + self.PS_Points = 0 + + self.PS_Items = PS:ValidateItems(util.JSONToTable(self:GetPData('PS_Items', '[]'))) + self.PS_Points = PS:ValidatePoints(tonumber(self:GetPData('PS_Points'))) + + -- Send stuff + timer.Simple(1, function() + self:PS_SendItems() + self:PS_SendPoints() + self:PS_SendClientsideModels() + end) +end + +function Player:PS_Save() + self:SetPData('PS_Items', util.TableToJSON(self.PS_Items)) + self:SetPData('PS_Points', self.PS_Points) +end + +-- points + +function Player:PS_GivePoints(points) + self.PS_Points = self.PS_Points + points + self:PS_SendPoints() +end + +function Player:PS_TakePoints(points) + self.PS_Points = self.PS_Points - points >= 0 and self.PS_Points - points or 0 + self:PS_SendPoints() +end + +function Player:PS_SetPoints(points) + self.PS_Points = points + self:PS_SendPoints() +end + +function Player:PS_HasPoints(points) + return self.PS_Points >= points +end + +-- give/take items + +function Player:PS_GiveItem(item_id) + if not PS.Items[item_id] then return false end + + self.PS_Items[item_id] = { Modifiers = {}, Equipped = false } + + self:PS_SendItems() + + return true +end + +function Player:PS_TakeItem(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + + self.PS_Items[item_id] = nil + + self:PS_SendItems() + + return true +end + +-- buy/sell items + +function Player:PS_BuyItem(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasPoints(PS.Items[item_id].Price) then return false end + + if not self:IsAdmin() and PS.Items[item_id].AdminOnly then + self:PS_Notify('This item is Admin only!') + return false + end + + local ITEM = PS.Items[item_id] + + self:PS_TakePoints(ITEM.Price) + + self:PS_Notify('Bought ', ITEM.Name, ' for ', ITEM.Price, ' points.') + + ITEM:OnBuy(self) + + return self:PS_GiveItem(item_id) +end + +function Player:PS_SellItem(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + + local ITEM = PS.Items[item_id] + local points = PS.Config.CalculateSellPrice(ITEM.Price) + + self:PS_GivePoints(points) + + ITEM:OnHolster(self) + ITEM:OnSell(self) + + self:PS_Notify('Sold ', ITEM.Name, ' for ', points, ' points.') + + return self:PS_TakeItem(item_id) +end + +function Player:PS_HasItem(item_id) + return self.PS_Items[item_id] or false +end + +-- equip/hoster items + +function Player:PS_EquipItem(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + if not self:Alive() then return false end + + self.PS_Items[item_id].Equipped = true + + local ITEM = PS.Items[item_id] + ITEM:OnEquip(self, self.PS_Items[item_id].Modifiers) + + self:PS_SendItems() +end + +function Player:PS_HolsterItem(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + if not self:Alive() then return false end + + self.PS_Items[item_id].Equipped = false + + local ITEM = PS.Items[item_id] + ITEM:OnHolster(self) + + self:PS_SendItems() +end + +-- modify items + +function Player:PS_ModifyItem(item_id, modifications) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + + local ITEM = PS.Items[item_id] + + for key, value in pairs(modifications) do + self.PS_Items[item_id].Modifiers[key] = value + end + + ITEM:OnModify(self, self.PS_Items[item_id].Modifiers) + + self:PS_SendItems() +end + +-- clientside Models + +function Player:PS_AddClientsideModel(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + + net.Start('PS_AddClientsideModel') + net.WriteEntity(self) + net.WriteString(item_id) + net.Broadcast() + + if not PS.ClientsideModels[self] then PS.ClientsideModels[self] = {} end + + PS.ClientsideModels[self][item_id] = item_id +end + +function Player:PS_RemoveClientsideModel(item_id) + if not PS.Items[item_id] then return false end + if not self:PS_HasItem(item_id) then return false end + if not PS.ClientsideModels[self] or not PS.ClientsideModels[self][item_id] then return false end + + net.Start('PS_RemoveClientsideModel') + net.WriteEntity(self) + net.WriteString(item_id) + net.Broadcast() + + PS.ClientsideModels[self][item_id] = nil +end + +-- menu stuff + +function Player:PS_ToggleMenu(show) + net.Start('PS_ToggleMenu') + net.Send(self) +end + +-- send stuff + +function Player:PS_SendPoints() + self:PS_Save() + + net.Start('PS_Points') + net.WriteEntity(self) + net.WriteInt(self.PS_Points, 32) + net.Broadcast() +end + +function Player:PS_SendItems() + self:PS_Save() + + net.Start('PS_Items') + net.WriteEntity(self) + net.WriteTable(self.PS_Items) + net.Broadcast() +end + +function Player:PS_SendClientsideModels() + net.Start('PS_SendClientsideModels') + net.WriteTable(PS.ClientsideModels) + net.Send(self) +end + +-- notifications + +function Player:PS_Notify(...) + local str = table.concat({...}, ' ') + self:SendLua('notification.AddLegacy("' .. str .. '", NOTIFY_GENERIC, 5)') +end diff --git a/lua/sv_pointshop.lua b/lua/sv_pointshop.lua new file mode 100644 index 0000000..ccd1217 --- /dev/null +++ b/lua/sv_pointshop.lua @@ -0,0 +1,84 @@ +-- net hooks + +net.Receive('PS_BuyItem', function(length, ply) + ply:PS_BuyItem(net.ReadString()) +end) + +net.Receive('PS_SellItem', function(length, ply) + ply:PS_SellItem(net.ReadString()) +end) + +net.Receive('PS_EquipItem', function(length, ply) + ply:PS_EquipItem(net.ReadString()) +end) + +net.Receive('PS_HolsterItem', function(length, ply) + ply:PS_HolsterItem(net.ReadString()) +end) + +net.Receive('PS_ModifyItem', function(length, ply) + ply:PS_ModifyItem(net.ReadString(), net.ReadTable()) +end) + +net.Receive('PS_GivePoints', function(length, ply) + local other = net.ReadEntity() + local points = net.ReadInt(32) + + if ply:IsAdmin() and other and points and IsValid(other) and other:IsPlayer() then + other:PS_GivePoints(points) + end +end) + +net.Receive('PS_TakePoints', function(length, ply) + local other = net.ReadEntity() + local points = net.ReadInt(32) + + if ply:IsAdmin() and other and points and IsValid(other) and other:IsPlayer() then + other:PS_TakePoints(points) + end +end) + +net.Receive('PS_SetPoints', function(length, ply) + local other = net.ReadEntity() + local points = net.ReadInt(32) + + if ply:IsAdmin() and other and points and IsValid(other) and other:IsPlayer() then + other:PS_SetPoints(points) + end +end) + +-- hooks + +local KeyToHook = { + F1 = "ShowHelp", + F2 = "ShowTeam", + F3 = "ShowSpare1", + F4 = "ShowSpare2", + None = "ThisHookDoesNotExist" +} + +hook.Add(KeyToHook[PS.Config.ShopKey], "PS_ShopKey", function(ply) + ply:PS_ToggleMenu() +end) + +hook.Add('PlayerSpawn', 'PS_PlayerSpawn', function(ply) ply:PS_PlayerSpawn() end) +hook.Add('PlayerDeath', 'PS_PlayerDeath', function(ply) ply:PS_PlayerDeath() end) +hook.Add('PlayerInitialSpawn', 'PS_PlayerInitialSpawn', function(ply) ply:PS_Initialize() end) +hook.Add('PlayerDisconnected', 'PS_PlayerDisconnected', function(ply) ply:PS_Save() PS.ClientsideModels[ply] = nil end) + +-- ugly networked strings + +util.AddNetworkString('PS_Items') +util.AddNetworkString('PS_Points') +util.AddNetworkString('PS_BuyItem') +util.AddNetworkString('PS_SellItem') +util.AddNetworkString('PS_EquipItem') +util.AddNetworkString('PS_HolsterItem') +util.AddNetworkString('PS_ModifyItem') +util.AddNetworkString('PS_GivePoints') +util.AddNetworkString('PS_TakePoints') +util.AddNetworkString('PS_SetPoints') +util.AddNetworkString('PS_AddClientsideModel') +util.AddNetworkString('PS_RemoveClientsideModel') +util.AddNetworkString('PS_SendClientsideModels') +util.AddNetworkString('PS_ToggleMenu') diff --git a/lua/vgui/DPointShopColorChooser.lua b/lua/vgui/DPointShopColorChooser.lua new file mode 100644 index 0000000..412d5af --- /dev/null +++ b/lua/vgui/DPointShopColorChooser.lua @@ -0,0 +1,34 @@ +local PANEL = {} + +function PANEL:Init() + self:SetTitle("PointShop Color Chooser") + self:SetSize(300, 300) + + self.colorpicker = vgui.Create('DColorMixer', self) + --colorpicker:DockMargin(0, 0, 0, 60) + self.colorpicker:Dock(FILL) + + local done = vgui.Create('DButton', self) + done:DockMargin(0, 5, 0, 0) + done:Dock(BOTTOM) + + done:SetText('Done') + + done.DoClick = function() + self.OnChoose(self.colorpicker:GetColor()) + self:Close() + end + + self:Center() + self:Show() +end + +function PANEL:OnChoose(color) + -- nothing, gets over-ridden +end + +function PANEL:SetColor(color) + self.colorpicker:SetColor(color or Color(255, 255, 255, 255)) +end + +vgui.Register('DPointShopColorChooser', PANEL, 'DFrame') diff --git a/lua/vgui/DPointShopItem.lua b/lua/vgui/DPointShopItem.lua new file mode 100644 index 0000000..c0e952e --- /dev/null +++ b/lua/vgui/DPointShopItem.lua @@ -0,0 +1,191 @@ +local PANEL = {} + +local adminicon = Material("icon16/shield.png") +local equippedicon = Material("icon16/eye.png") + +local canbuycolor = Color(0, 100, 0, 125) +local cantbuycolor = Color(100, 0, 0, 125) +local ownedcolor = Color(0, 0, 200, 125) + +function PANEL:Init() + self.Info = "" + self.InfoHeight = 14 +end + +function PANEL:DoClick() + if not LocalPlayer():PS_HasItem(self.Data.ID) and not LocalPlayer():PS_HasPoints(self.Data.Price) then + notification.AddLegacy("You don't have enough points for this!", NOTIFY_GENERIC, 5) + end + + local menu = DermaMenu(self) + + if LocalPlayer():PS_HasItem(self.Data.ID) then + menu:AddOption('Sell', function() + LocalPlayer():PS_SellItem(self.Data.ID) + end) + elseif LocalPlayer():PS_HasPoints(self.Data.Price) then + if not self.Data.AdminOnly or (self.Data.AdminOnly and LocalPlayer():IsAdmin()) then + menu:AddOption('Buy', function() + LocalPlayer():PS_BuyItem(self.Data.ID) + end) + end + end + + if LocalPlayer():PS_HasItem(self.Data.ID) then + menu:AddSpacer() + + if LocalPlayer():PS_HasItemEquipped(self.Data.ID) then + menu:AddOption('Holster', function() + LocalPlayer():PS_HolsterItem(self.Data.ID) + end) + else + menu:AddOption('Equip', function() + LocalPlayer():PS_EquipItem(self.Data.ID) + end) + end + + if LocalPlayer():PS_HasItemEquipped(self.Data.ID) and self.Data.Modify then + menu:AddSpacer() + + menu:AddOption('Modify...', function() + PS.Items[self.Data.ID]:Modify(LocalPlayer().PS_Items[self.Data.ID].Modifiers) + end) + end + end + + menu:Open() +end + +function PANEL:SetData(data) + self.Data = data + self.Info = data.Name + + if data.Model then + local DModelPanel = vgui.Create('DModelPanel', self) + DModelPanel:SetModel(data.Model) + + DModelPanel:Dock(FILL) + + if data.Skin then + DModelPanel:SetSkin(data.Skin) + end + + local PrevMins, PrevMaxs = DModelPanel.Entity:GetRenderBounds() + DModelPanel:SetCamPos(PrevMins:Distance(PrevMaxs) * Vector(0.5, 0.5, 0.5)) + DModelPanel:SetLookAt((PrevMaxs + PrevMins) / 2) + + function DModelPanel:LayoutEntity(ent) + if self:GetParent().Hovered then + ent:SetAngles(Angle(0, ent:GetAngles().y + 2, 0)) + end + end + + function DModelPanel:DoClick() + self:GetParent():DoClick() + end + + function DModelPanel:OnCursorEntered() + self:GetParent():OnCursorEntered() + end + + function DModelPanel:OnCursorExited() + self:GetParent():OnCursorExited() + end + + local oldPaint = DModelPanel.Paint + + function DModelPanel:Paint() + local x, y = self:LocalToScreen( 0, 0 ) + local w, h = self:GetSize() + + local sl, st, sr, sb = x, y, x + w, y + h + + local p = self + while p:GetParent() do + p = p:GetParent() + local pl, pt = p:LocalToScreen( 0, 0 ) + local pr, pb = pl + p:GetWide(), pt + p:GetTall() + sl = sl < pl and pl or sl + st = st < pt and pt or st + sr = sr > pr and pr or sr + sb = sb > pb and pb or sb + end + + render.SetScissorRect( sl, st, sr, sb, true ) + oldPaint(self) + render.SetScissorRect( 0, 0, 0, 0, false ) + end + else + local DImageButton = vgui.Create('DImageButton', self) + DImageButton:SetMaterial(data.Material) + + DImageButton:Dock(FILL) + + function DImageButton:DoClick() + self:GetParent():DoClick() + end + + function DImageButton:OnCursorEntered() + self:GetParent():OnCursorEntered() + end + + function DImageButton:OnCursorExited() + self:GetParent():OnCursorExited() + end + end + + if self.Description then + self:SetTooltip(data.Description) + end +end + +function PANEL:PaintOver() + if self.Data.AdminOnly then + surface.SetMaterial(adminicon) + surface.SetDrawColor(Color(255, 255, 255, 255)) + surface.DrawTexturedRect(5, 5, 16, 16) + end + + if LocalPlayer():PS_HasItemEquipped(self.Data.ID) then + surface.SetMaterial(equippedicon) + surface.SetDrawColor(Color(255, 255, 255, 255)) + surface.DrawTexturedRect(self:GetWide() - 5 - 16, 5, 16, 16) + end + + if LocalPlayer():PS_HasPoints(self.Data.Price) then + self.BarColor = canbuycolor + else + self.BarColor = cantbuycolor + end + + if LocalPlayer():PS_HasItem(self.Data.ID) then + self.BarColor = ownedcolor + end + + surface.SetDrawColor(self.BarColor) + surface.DrawRect(0, self:GetTall() - self.InfoHeight, self:GetWide(), self.InfoHeight) + + draw.SimpleText(self.Info, "DefaultSmall", self:GetWide() / 2, self:GetTall() - (self.InfoHeight / 2), Color(255, 255, 255, 255), TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER) + + if LocalPlayer().PS_Items[self.Data.ID] and LocalPlayer().PS_Items[self.Data.ID].Modifiers and LocalPlayer().PS_Items[self.Data.ID].Modifiers.color then + surface.SetDrawColor(LocalPlayer().PS_Items[self.Data.ID].Modifiers.color) + surface.DrawRect(self:GetWide() - 5 - 16, self:GetTall() - self.InfoHeight - 5 - 16, 16, 16) + end +end + +function PANEL:OnCursorEntered() + self.Hovered = true + + if LocalPlayer():PS_HasItem(self.Data.ID) then + self.Info = '+' .. PS.Config.CalculateSellPrice(self.Data.Price) + else + self.Info = '-' .. self.Data.Price + end +end + +function PANEL:OnCursorExited() + self.Hovered = false + self.Info = self.Data.Name +end + +vgui.Register('DPointShopItem', PANEL, 'DPanel') diff --git a/lua/vgui/DPointShopMenu.lua b/lua/vgui/DPointShopMenu.lua new file mode 100644 index 0000000..a03a2ca --- /dev/null +++ b/lua/vgui/DPointShopMenu.lua @@ -0,0 +1,174 @@ +surface.CreateFont('PS_Heading', { font = 'coolvetica', size = 64 }) +surface.CreateFont('PS_Heading2', { font = 'coolvetica', size = 24 }) +surface.CreateFont('PS_Heading3', { font = 'coolvetica', size = 19 }) + +local PANEL = {} + +function PANEL:Init() + self:SetSize(1024, 768) + self:SetPos((ScrW() / 2) - (self:GetWide() / 2), (ScrH() / 2) - (self:GetTall() / 2)) + + -- close button + local closeButton = vgui.Create('DButton', self) + closeButton:SetFont('marlett') + closeButton:SetText('r') + closeButton:SetColor(Color(255, 255, 255)) + closeButton:SetSize(15, 15) + closeButton:SetDrawBackground(false) + closeButton:SetPos(self:GetWide() - 25, 10) + closeButton.DoClick = function() + PS:ToggleMenu() + end + + local tabs = vgui.Create('DPropertySheet', self) + + tabs:DockMargin(10, 80, 10, 10) + tabs:Dock(FILL) + + tabs:SetSize(self:GetWide() - 60, self:GetTall() - 150) + tabs:SetPos((self:GetWide() / 2) - (tabs:GetWide() / 2), 120) + + -- items + for _, CATEGORY in pairs(PS.Categories) do + local ShopCategoryTab = vgui.Create('DPanel') + + local DScrollPanel = vgui.Create('DScrollPanel', ShopCategoryTab) + DScrollPanel:Dock(FILL) + + local ShopCategoryTabLayout = vgui.Create('DIconLayout', DScrollPanel) + ShopCategoryTabLayout:Dock(FILL) + ShopCategoryTabLayout:SetBorder(10) + ShopCategoryTabLayout:SetSpaceX(10) + ShopCategoryTabLayout:SetSpaceY(10) + + DScrollPanel:AddItem(ShopCategoryTabLayout) + + for _, ITEM in pairs(PS.Items) do + + if ITEM.Category == CATEGORY.Name then + local model = vgui.Create('DPointShopItem') + model:SetData(ITEM) + model:SetSize(126, 126) + + ShopCategoryTabLayout:Add(model) + end + end + + tabs:AddSheet(CATEGORY.Name, ShopCategoryTab, 'icon16/' .. CATEGORY.Icon .. '.png', false, false, '') + end + + if not LocalPlayer():IsAdmin() then return end + + -- admin tab + local AdminTab = vgui.Create('DPanel') + + local ClientsList = vgui.Create('DListView', AdminTab) + ClientsList:DockMargin(10, 10, 10, 10) + ClientsList:Dock(FILL) + + ClientsList:SetMultiSelect(false) + ClientsList:AddColumn('ID'):SetFixedWidth(100) + ClientsList:AddColumn('Name') + ClientsList:AddColumn('Points'):SetFixedWidth(60) + ClientsList:AddColumn('Items'):SetFixedWidth(60) + + ClientsList.OnClickLine = function(parent, line, selected) + local ply = player.GetByUniqueID(line:GetValue(1)) + + if not ply then MsgN('[PS] Invalid player?') return end + + local menu = DermaMenu() + + menu:AddOption('Set Points...', function() + Derma_StringRequest( + "Set Points for " .. ply:GetName(), + "Set points to...", + "", + function(str) + net.Start('PS_SetPoints') + net.WriteEntity(ply) + net.WriteInt(tonumber(str), 32) + net.SendToServer() + end + ) + end) + + menu:AddOption('Give Points...', function() + Derma_StringRequest( + "Give Points to " .. ply:GetName(), + "Give points...", + "", + function(str) + net.Start('PS_GivePoints') + net.WriteEntity(ply) + net.WriteInt(tonumber(str), 32) + net.SendToServer() + end + ) + end) + + menu:AddOption('Take Points...', function() + Derma_StringRequest( + "Take Points from " .. ply:GetName(), + "Take points...", + "", + function(str) + net.Start('PS_TakePoints') + net.WriteEntity(ply) + net.WriteInt(tonumber(str), 32) + net.SendToServer() + end + ) + end) + + menu:Open() + end + + self.ClientsList = ClientsList + + tabs:AddSheet('Admin', AdminTab, 'icon16/shield.png', false, false, '') +end + +function PANEL:Think() + if self.ClientsList then + local lines = self.ClientsList:GetLines() + + for _, ply in pairs(player.GetAll()) do + local found = false + + for _, line in pairs(lines) do + if line.Player == ply then + found = true + end + end + + if not found then + self.ClientsList:AddLine(ply:UniqueID(), ply:GetName(), ply:PS_GetPoints(), table.Count(ply:PS_GetItems())).Player = ply + end + end + + for i, line in pairs(lines) do + if IsValid(line.Player) then + local ply = line.Player + + line:SetValue(2, ply:GetName()) + line:SetValue(3, ply:PS_GetPoints()) + line:SetValue(4, table.Count(ply:PS_GetItems())) + else + self.ClientsList:RemoveLine(i) + end + end + end +end + +function PANEL:Paint() + Derma_DrawBackgroundBlur(self) + + draw.RoundedBox(10, 0, 0, self:GetWide(), self:GetTall(), Color(0, 0, 0, 150)) + + draw.SimpleText('PointShop', 'PS_Heading', 20, 10, color_white) + draw.SimpleText('by _Undefined', 'PS_Heading2', 275, 50, color_white) + draw.SimpleText('You have ' .. LocalPlayer():PS_GetPoints() .. ' points', 'PS_Heading3', self:GetWide() - 10, 60, color_white, TEXT_ALIGN_RIGHT, TEXT_ALIGN_BOTTOM) +end + +vgui.Register('DPointShopMenu', PANEL)