Files
wnsrc/gamemodes/helix/plugins/vendor/sh_plugin.lua
lifestorm 6a58f406b1 Upload
2024-08-04 23:54:45 +03:00

826 lines
20 KiB
Lua

--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- luacheck: globals VENDOR_BUY VENDOR_SELL VENDOR_BOTH VENDOR_WELCOME VENDOR_LEAVE VENDOR_NOTRADE VENDOR_PRICE
-- luacheck: globals VENDOR_STOCK VENDOR_MODE VENDOR_MAXSTOCK VENDOR_SELLANDBUY VENDOR_SELLONLY VENDOR_BUYONLY VENDOR_TEXT
local PLUGIN = PLUGIN
PLUGIN.name = "Vendors"
PLUGIN.author = "Chessnut"
PLUGIN.description = "Adds NPC vendors that can sell things."
CAMI.RegisterPrivilege({
Name = "Helix - Manage Vendors",
MinAccess = "admin"
})
VENDOR_BUY = 1
VENDOR_SELL = 2
VENDOR_BOTH = 3
-- Keys for vendor messages.
VENDOR_WELCOME = 1
VENDOR_LEAVE = 2
VENDOR_NOTRADE = 3
-- Keys for item information.
VENDOR_PRICE = 1
VENDOR_STOCK = 2
VENDOR_MODE = 3
VENDOR_MAXSTOCK = 4
-- Sell and buy the item.
VENDOR_SELLANDBUY = 1
-- Only sell the item to the player.
VENDOR_SELLONLY = 2
-- Only buy the item from the player.
VENDOR_BUYONLY = 3
if (SERVER) then
util.AddNetworkString("ixVendorOpen")
util.AddNetworkString("ixVendorClose")
util.AddNetworkString("ixVendorTrade")
util.AddNetworkString("ixVendorEdit")
util.AddNetworkString("ixVendorEditFinish")
util.AddNetworkString("ixVendorEditor")
util.AddNetworkString("ixVendorMoney")
util.AddNetworkString("ixVendorStock")
util.AddNetworkString("ixVendorAddItem")
function PLUGIN:RegisterSaveEnts()
ix.saveEnts:RegisterEntity("ix_vendor", true, true, true, {
OnSave = function(entity, data) --OnSave
data.name = entity:GetDisplayName()
data.description = entity:GetDescription()
data.model = entity:GetModel()
data.items = entity.items
data.money = entity.money
data.maxStock = entity.maxStock
data.stashList = entity.stashList
data.motion = nil
data.bubble = entity:GetNoBubble()
data.factions = entity.factions
data.classes = entity.classes
data.scale = entity.scale
data.useCredits = entity:GetUseCredits()
end,
OnRestore = function(entity, data) --OnRestore
entity:SetModel(data.model)
entity:SetSolid(SOLID_BBOX)
entity:PhysicsInit(SOLID_BBOX)
local physObj = entity:GetPhysicsObject()
if (IsValid(physObj)) then
physObj:EnableMotion(false)
physObj:Sleep()
end
entity:SetDisplayName(data.name)
entity:SetDescription(data.description)
entity:SetNoBubble(data.bubble)
entity:SetUseCredits(data.useCredits or false)
local items = {}
for uniqueID, itemData in pairs(data.items) do
items[tostring(uniqueID)] = itemData
end
entity.items = items
entity.factions = data.factions or {}
entity.classes = data.classes or {}
entity.money = data.money
entity.scale = data.scale or 1
end,
})
end
function PLUGIN:SaveData()
local data = {}
for _, entity in ipairs(ents.FindByClass("ix_vendor")) do
local bodygroups = {}
for _, v in ipairs(entity:GetBodyGroups() or {}) do
bodygroups[v.id] = entity:GetBodygroup(v.id)
end
data[#data + 1] = {
name = entity:GetDisplayName(),
description = entity:GetDescription(),
pos = entity:GetPos(),
angles = entity:GetAngles(),
model = entity:GetModel(),
skin = entity:GetSkin(),
bodygroups = bodygroups,
bubble = entity:GetNoBubble(),
items = entity.items,
factions = entity.factions,
classes = entity.classes,
money = entity.money,
scale = entity.scale,
useCredits = entity:GetUseCredits()
}
end
self:SetData(data)
end
function PLUGIN:LoadData()
if (ix.saveEnts and !ix.config.Get("SaveEntsOldLoadingEnabled")) then return end
for _, v in ipairs(self:GetData() or {}) do
local entity = ents.Create("ix_vendor")
entity:SetPos(v.pos)
entity:SetAngles(v.angles)
entity:Spawn()
entity:SetModel(v.model)
entity:SetSkin(v.skin or 0)
entity:SetSolid(SOLID_BBOX)
entity:PhysicsInit(SOLID_BBOX)
local physObj = entity:GetPhysicsObject()
if (IsValid(physObj)) then
physObj:EnableMotion(false)
physObj:Sleep()
end
entity:SetNoBubble(v.bubble)
entity:SetDisplayName(v.name)
entity:SetDescription(v.description)
entity:SetUseCredits(v.useCredits or false)
for id, bodygroup in pairs(v.bodygroups or {}) do
entity:SetBodygroup(id, bodygroup)
end
local items = {}
for uniqueID, data in pairs(v.items) do
items[tostring(uniqueID)] = data
end
entity.items = items
entity.factions = v.factions or {}
entity.classes = v.classes or {}
entity.money = v.money
entity.scale = v.scale or 1
end
end
function PLUGIN:CanVendorSellItem(client, vendor, itemID)
local tradeData = vendor.items[itemID]
local char = client:GetCharacter()
if (!tradeData or !char) then
return false
end
if (!char:HasMoney(tradeData[1] or 0)) then
return false
end
return true
end
ix.log.AddType("vendorUse", function(client, ...)
local arg = {...}
return string.format("%s used the '%s' vendor.", client:Name(), arg[1])
end)
net.Receive("ixVendorClose", function(length, client)
local entity = client.ixVendor
if (IsValid(entity)) then
for k, v in ipairs(entity.receivers) do
if (v == client) then
table.remove(entity.receivers, k)
break
end
end
client.ixVendor = nil
end
end)
local function UpdateEditReceivers(receivers, key, value)
net.Start("ixVendorEdit")
net.WriteString(key)
net.WriteType(value)
net.Send(receivers)
end
net.Receive("ixVendorEdit", function(length, client)
if (!CAMI.PlayerHasAccess(client, "Helix - Manage Vendors", nil)) then
return
end
local entity = client.ixVendor
if (!IsValid(entity)) then
return
end
local key = net.ReadString()
local data = net.ReadType()
local feedback = true
if (key == "name") then
entity:SetDisplayName(data)
elseif (key == "description") then
entity:SetDescription(data)
elseif (key == "bubble") then
entity:SetNoBubble(data)
elseif (key == "mode") then
local uniqueID = data[1]
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_MODE] = data[2]
UpdateEditReceivers(entity.receivers, key, data)
elseif (key == "price") then
local uniqueID = data[1]
data[2] = tonumber(data[2])
if (data[2]) then
data[2] = math.Round(data[2])
end
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_PRICE] = data[2]
UpdateEditReceivers(entity.receivers, key, data)
data = uniqueID
elseif (key == "stockDisable") then
local uniqueID = data[1]
entity.items[data] = entity.items[uniqueID] or {}
entity.items[data][VENDOR_MAXSTOCK] = nil
UpdateEditReceivers(entity.receivers, key, data)
elseif (key == "stockMax") then
local uniqueID = data[1]
data[2] = math.max(math.Round(tonumber(data[2]) or 1), 1)
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]
entity.items[uniqueID][VENDOR_STOCK] = math.Clamp(entity.items[uniqueID][VENDOR_STOCK] or data[2], 1, data[2])
data[3] = entity.items[uniqueID][VENDOR_STOCK]
UpdateEditReceivers(entity.receivers, key, data)
data = uniqueID
elseif (key == "stock") then
local uniqueID = data[1]
entity.items[uniqueID] = entity.items[uniqueID] or {}
if (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then
data[2] = math.max(math.Round(tonumber(data[2]) or 0), 0)
entity.items[uniqueID][VENDOR_MAXSTOCK] = data[2]
end
data[2] = math.Clamp(math.Round(tonumber(data[2]) or 0), 0, entity.items[uniqueID][VENDOR_MAXSTOCK])
entity.items[uniqueID][VENDOR_STOCK] = data[2]
UpdateEditReceivers(entity.receivers, key, data)
data = uniqueID
elseif (key == "faction") then
local faction = ix.faction.teams[data]
if (faction) then
entity.factions[data] = !entity.factions[data]
if (!entity.factions[data]) then
entity.factions[data] = nil
end
end
local uniqueID = data
data = {uniqueID, entity.factions[uniqueID]}
elseif (key == "class") then
local class
for _, v in ipairs(ix.class.list) do
if (v.uniqueID == data) then
class = v
break
end
end
if (class) then
entity.classes[data] = !entity.classes[data]
if (!entity.classes[data]) then
entity.classes[data] = nil
end
end
local uniqueID = data
data = {uniqueID, entity.classes[uniqueID]}
elseif (key == "model") then
entity:SetModel(data)
entity:SetSolid(SOLID_BBOX)
entity:PhysicsInit(SOLID_BBOX)
entity:SetAnim()
elseif (key == "useMoney") then
if (entity.money) then
entity:SetMoney()
else
entity:SetMoney(0)
end
elseif (key == "money") then
data = math.Round(math.abs(tonumber(data) or 0))
entity:SetMoney(data)
feedback = false
elseif (key == "scale") then
data = tonumber(data) or 0.5
entity.scale = data
UpdateEditReceivers(entity.receivers, key, data)
elseif (key == "useCredits") then
entity:SetUseCredits(data)
end
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(entity)
end
PLUGIN:SaveData()
if (feedback) then
local receivers = {}
for _, v in ipairs(entity.receivers) do
if (CAMI.PlayerHasAccess(v, "Helix - Manage Vendors", nil)) then
receivers[#receivers + 1] = v
end
end
net.Start("ixVendorEditFinish")
net.WriteString(key)
net.WriteType(data)
net.Send(receivers)
end
end)
net.Receive("ixVendorTrade", function(length, client)
if ((client.ixVendorTry or 0) < CurTime()) then
client.ixVendorTry = CurTime() + 0.33
else
return
end
local entity = client.ixVendor
if (!IsValid(entity) or client:GetPos():Distance(entity:GetPos()) > 192) then
return
end
local uniqueID = net.ReadString()
local isSellingToVendor = net.ReadBool()
if (entity.items[uniqueID] and
hook.Run("CanPlayerTradeWithVendor", client, entity, uniqueID, isSellingToVendor) != false) then
local price = entity:GetPrice(uniqueID, isSellingToVendor)
if (isSellingToVendor) then
local found = false
local name
if (!entity:HasMoney(price)) then
return client:NotifyLocalized("vendorNoMoney")
end
local stock, max = entity:GetStock(uniqueID)
if (stock and stock >= max) then
return client:NotifyLocalized("vendorMaxStock")
end
local invOkay = true
for _, v in pairs(client:GetCharacter():GetInventory():GetItems()) do
if (v.uniqueID == uniqueID and v:GetID() != 0 and ix.item.instances[v:GetID()] and v:GetData("equip", false) == false) then
invOkay = v:Remove()
found = true
name = L(v.name, client)
break
end
end
if (!found) then
return
end
if (!invOkay) then
client:GetCharacter():GetInventory():Sync(client, true)
return client:NotifyLocalized("tellAdmin", "trd!iid")
end
if (entity:GetUseCredits()) then
local card = client:GetCharacter():GetInventory():HasItem("id_card")
card:LoadOwnerGenericData(PLUGIN.GiveTakeCredits, nil, client, entity, price, uniqueID, false)
else
client:GetCharacter():GiveMoney(price)
end
client:NotifyLocalized("businessSell", name, !entity:GetUseCredits() and ix.currency.Get(price) or (price .. " Credits"))
entity:TakeMoney(price)
entity:AddStock(uniqueID)
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(entity)
end
PLUGIN:SaveData()
hook.Run("CharacterVendorTraded", client, entity, uniqueID, isSellingToVendor)
else
local stock = entity:GetStock(uniqueID)
if (stock and stock < 1) then
return client:NotifyLocalized("vendorNoStock")
end
local name = L(ix.item.list[uniqueID].name, client)
if (entity:GetUseCredits()) then
local card = client:GetCharacter():GetInventory():HasItem("id_card")
card:LoadOwnerGenericData(PLUGIN.GiveTakeCredits, nil, client, entity, price, uniqueID, true)
else
if (!client:GetCharacter():HasMoney(price)) then
return client:NotifyLocalized("canNotAfford")
end
client:GetCharacter():TakeMoney(price)
client:NotifyLocalized("businessPurchase", name, !entity:GetUseCredits()
and ix.currency.Get(price) or L("vendorNCredits", price))
entity:GiveMoney(price)
if (!client:GetCharacter():GetInventory():Add(uniqueID)) then
ix.item.Spawn(uniqueID, client)
else
net.Start("ixVendorAddItem")
net.WriteString(uniqueID)
net.Send(client)
end
entity:TakeStock(uniqueID)
end
if (ix.saveEnts) then
ix.saveEnts:SaveEntity(entity)
end
PLUGIN:SaveData()
hook.Run("CharacterVendorTraded", client, entity, uniqueID, isSellingToVendor)
end
else
client:NotifyLocalized("vendorNoTrade")
end
end)
function PLUGIN.GiveTakeCredits(idCard, _, client, entity, price, itemID, isBuying)
if (isBuying) then
if (idCard:HasCredits(price)) then
local item = ix.item.Get(itemID)
local name = item and item.name or ""
idCard:TakeCredits(price, entity:GetDisplayName(), "\"" .. name .. "\" vendor purchase.")
client:NotifyLocalized("businessPurchase", name, price.." Credits")
entity:GiveMoney(price)
if (!client:GetCharacter():GetInventory():Add(itemID)) then
ix.item.Spawn(itemID, client)
else
net.Start("ixVendorAddItem")
net.WriteString(itemID)
net.Send(client)
end
entity:TakeStock(itemID)
else
return client:NotifyLocalized("canNotAfford")
end
else
idCard:GiveCredits(price, entity:GetDisplayName(),
"\"" .. ix.item.Get(itemID).name .. " " .. entity:GetDisplayName() .. "\" vendor sale.")
end
end
else
VENDOR_TEXT = {}
VENDOR_TEXT[VENDOR_SELLANDBUY] = "vendorBoth"
VENDOR_TEXT[VENDOR_BUYONLY] = "vendorBuy"
VENDOR_TEXT[VENDOR_SELLONLY] = "vendorSell"
net.Receive("ixVendorOpen", function()
local entity = net.ReadEntity()
if (!IsValid(entity)) then
return
end
entity.money = net.ReadUInt(16)
entity.items = net.ReadTable()
entity.scale = net.ReadFloat()
local usingCredits = net.ReadBool()
local credits = false
if (usingCredits) then
credits = net.ReadFloat()
end
ix.gui.vendor = vgui.Create("ixVendor")
ix.gui.vendor:SetReadOnly(false)
ix.gui.vendor:Populate(credits)
ix.gui.vendor:Setup(entity)
end)
net.Receive("ixVendorEditor", function()
local entity = net.ReadEntity()
if (!IsValid(entity) or !CAMI.PlayerHasAccess(LocalPlayer(), "Helix - Manage Vendors", nil)) then
return
end
entity.money = net.ReadUInt(16)
entity.items = net.ReadTable()
entity.scale = net.ReadFloat()
entity.messages = net.ReadTable()
entity.factions = net.ReadTable()
entity.classes = net.ReadTable()
ix.gui.vendor = vgui.Create("ixVendor")
ix.gui.vendor:SetReadOnly(true)
ix.gui.vendor:Populate()
ix.gui.vendor:Setup(entity)
ix.gui.vendorEditor = vgui.Create("ixVendorEditor")
end)
net.Receive("ixVendorEdit", function()
local panel = ix.gui.vendor
if (!IsValid(panel)) then
return
end
local entity = panel.entity
if (!IsValid(entity)) then
return
end
local key = net.ReadString()
local data = net.ReadType()
if (key == "mode") then
entity.items[data[1]] = entity.items[data[1]] or {}
entity.items[data[1]][VENDOR_MODE] = data[2]
if (!data[2]) then
panel:removeItem(data[1])
elseif (data[2] == VENDOR_SELLANDBUY) then
panel:addItem(data[1])
else
panel:addItem(data[1], data[2] == VENDOR_SELLONLY and "selling" or "buying")
panel:removeItem(data[1], data[2] == VENDOR_SELLONLY and "buying" or "selling")
end
elseif (key == "price") then
local uniqueID = data[1]
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_PRICE] = tonumber(data[2])
elseif (key == "stockDisable") then
if (entity.items[data]) then
entity.items[data][VENDOR_MAXSTOCK] = nil
end
elseif (key == "stockMax") then
local uniqueID = data[1]
local value = data[2]
local current = data[3]
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_MAXSTOCK] = value
entity.items[uniqueID][VENDOR_STOCK] = current
elseif (key == "stock") then
local uniqueID = data[1]
local value = data[2]
entity.items[uniqueID] = entity.items[uniqueID] or {}
if (!entity.items[uniqueID][VENDOR_MAXSTOCK]) then
entity.items[uniqueID][VENDOR_MAXSTOCK] = value
end
entity.items[uniqueID][VENDOR_STOCK] = value
elseif (key == "scale") then
entity.scale = data
end
end)
net.Receive("ixVendorEditFinish", function()
local panel = ix.gui.vendor
local editor = ix.gui.vendorEditor
if (!IsValid(panel) or !IsValid(editor)) then
return
end
local entity = panel.entity
if (!IsValid(entity)) then
return
end
local key = net.ReadString()
local data = net.ReadType()
if (key == "name") then
editor.name:SetText(data)
elseif (key == "description") then
editor.description:SetText(data)
elseif (key == "bubble") then
editor.bubble.noSend = true
editor.bubble:SetValue(data and 1 or 0)
elseif (key == "mode") then
if (data[2] == nil) then
editor.lines[data[1]]:SetValue(3, L"none")
else
editor.lines[data[1]]:SetValue(3, L(VENDOR_TEXT[data[2]]))
end
elseif (key == "price") then
editor.lines[data]:SetValue(4, entity:GetPrice(data))
elseif (key == "stockDisable") then
editor.lines[data]:SetValue(5, "-")
elseif (key == "stockMax" or key == "stock") then
local current, max = entity:GetStock(data)
editor.lines[data]:SetValue(5, current.."/"..max)
elseif (key == "faction") then
local uniqueID = data[1]
local state = data[2]
local editPanel = ix.gui.editorFaction
entity.factions[uniqueID] = state
if (IsValid(editPanel) and IsValid(editPanel.factions[uniqueID])) then
editPanel.factions[uniqueID]:SetChecked(state == true)
end
elseif (key == "class") then
local uniqueID = data[1]
local state = data[2]
local editPanel = ix.gui.editorFaction
entity.classes[uniqueID] = state
if (IsValid(editPanel) and IsValid(editPanel.classes[uniqueID])) then
editPanel.classes[uniqueID]:SetChecked(state == true)
end
elseif (key == "model") then
editor.model:SetText(entity:GetModel())
elseif (key == "scale") then
editor.sellScale.noSend = true
editor.sellScale:SetValue(data)
end
surface.PlaySound("buttons/button14.wav")
end)
net.Receive("ixVendorMoney", function()
local panel = ix.gui.vendor
if (!IsValid(panel)) then
return
end
local entity = panel.entity
if (!IsValid(entity)) then
return
end
local value = net.ReadUInt(16)
value = value != -1 and value or nil
entity.money = value
local editor = ix.gui.vendorEditor
if (IsValid(editor)) then
local useMoney = tonumber(value) != nil
editor.money:SetDisabled(!useMoney)
editor.money:SetEnabled(useMoney)
editor.money:SetText(useMoney and value or "")
end
end)
net.Receive("ixVendorStock", function()
local panel = ix.gui.vendor
if (!IsValid(panel)) then
return
end
local entity = panel.entity
if (!IsValid(entity)) then
return
end
local uniqueID = net.ReadString()
local amount = net.ReadUInt(16)
entity.items[uniqueID] = entity.items[uniqueID] or {}
entity.items[uniqueID][VENDOR_STOCK] = amount
local editor = ix.gui.vendorEditor
if (IsValid(editor)) then
local _, max = entity:GetStock(uniqueID)
editor.lines[uniqueID]:SetValue(4, amount .. "/" .. max)
end
end)
net.Receive("ixVendorAddItem", function()
local uniqueID = net.ReadString()
if (IsValid(ix.gui.vendor)) then
ix.gui.vendor:addItem(uniqueID, "buying")
end
end)
end
properties.Add("vendor_edit", {
MenuLabel = "Edit Vendor",
Order = 999,
MenuIcon = "icon16/user_edit.png",
Filter = function(self, entity, client)
if (!IsValid(entity)) then return false end
if (entity:GetClass() != "ix_vendor") then return false end
if (!gamemode.Call( "CanProperty", client, "vendor_edit", entity)) then return false end
return CAMI.PlayerHasAccess(client, "Helix - Manage Vendors", nil)
end,
Action = function(self, entity)
self:MsgStart()
net.WriteEntity(entity)
self:MsgEnd()
end,
Receive = function(self, length, client)
local entity = net.ReadEntity()
if (!IsValid(entity)) then return end
if (!self:Filter(entity, client)) then return end
entity.receivers[#entity.receivers + 1] = client
local itemsTable = {}
for k, v in pairs(entity.items) do
if (!table.IsEmpty(v)) then
itemsTable[k] = v
end
end
client.ixVendor = entity
net.Start("ixVendorEditor")
net.WriteEntity(entity)
net.WriteUInt(entity.money or 0, 16)
net.WriteTable(itemsTable)
net.WriteFloat(entity.scale or 1)
net.WriteTable(entity.messages)
net.WriteTable(entity.factions)
net.WriteTable(entity.classes)
net.Send(client)
end
})