mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 05:43:46 +03:00
Upload
This commit is contained in:
245
gamemodes/ixhl2rp/plugins/housing/cl_plugin.lua
Normal file
245
gamemodes/ixhl2rp/plugins/housing/cl_plugin.lua
Normal file
@@ -0,0 +1,245 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
PLUGIN.apartments = PLUGIN.apartments or {}
|
||||
|
||||
-- A function to get an apartment name
|
||||
function PLUGIN:SetApartmentNames(tApartments)
|
||||
for _, ent in pairs(ents.GetAll()) do
|
||||
if !ent:GetClass():find("door") then continue end
|
||||
ent.doorName = nil
|
||||
ent.doorType = nil
|
||||
|
||||
for _, tInfo in pairs(tApartments) do
|
||||
for _, doorIndex in pairs(tInfo.doors) do
|
||||
if doorIndex == ent:EntIndex() then
|
||||
ent.doorName = tInfo.name
|
||||
ent.doorType = tInfo.type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:GetApartmentName(appID)
|
||||
netstream.Start("RequestApartmentNames", appID)
|
||||
end
|
||||
|
||||
function PLUGIN:GetApartmentNameDatafile(appID)
|
||||
netstream.Start("RequestApartmentNamesDatafile", appID)
|
||||
end
|
||||
|
||||
function PLUGIN:InitPostEntity()
|
||||
netstream.Start("RequestApartmentNames")
|
||||
end
|
||||
|
||||
function PLUGIN:ConvertStringRequestToComboselect(requestPanel, comboValue, comboPopulate, callback)
|
||||
for i = 1, 2 do
|
||||
requestPanel:GetChildren()[5]:GetChildren()[i]:Remove()
|
||||
end
|
||||
|
||||
local comboBox = requestPanel:Add("DComboBox")
|
||||
comboBox:SetValue(comboValue or "")
|
||||
comboBox:SetFont("MenuFontNoClamp")
|
||||
comboBox:Dock(TOP)
|
||||
comboBox:SetTall(SScaleMin(30 / 3))
|
||||
comboBox:DockMargin(0, SScaleMin(20 / 3), 0, 0)
|
||||
comboBox.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
|
||||
surface.DrawOutlinedRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
comboPopulate(comboBox)
|
||||
|
||||
requestPanel:GetChildren()[6]:GetChildren()[1].DoClick = function()
|
||||
callback(comboBox)
|
||||
requestPanel:Close()
|
||||
end
|
||||
|
||||
requestPanel.Think = function(this)
|
||||
if IsValid(comboBox.Menu) and comboBox.Menu:IsVisible() then return end
|
||||
this:MoveToFront()
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to register the apartments ESP
|
||||
do
|
||||
local priColor = Color(191, 91, 81, 255)
|
||||
local shopColor = Color(193, 136, 79, 255)
|
||||
local normColor = Color(185, 122, 87, 255)
|
||||
local center = TEXT_ALIGN_CENTER
|
||||
local function apartmentsESP(client, entity, x, y, factor)
|
||||
if !entity.doorName or !entity.doorType then return end
|
||||
|
||||
local type = entity.doorType
|
||||
local espcol = (type == "priority" and priColor or type == "shop" and shopColor or normColor)
|
||||
ix.util.DrawText("APP: "..entity.doorName.." | "..string.upper(entity.doorType), x, y - math.max(10, 32 * factor), espcol, center, center, nil, math.max(255 * factor, 80))
|
||||
end
|
||||
|
||||
ix.observer:RegisterESPType("func_door", apartmentsESP, "apartments")
|
||||
ix.observer:RegisterESPType("func_door_rotating", apartmentsESP, "apartments")
|
||||
ix.observer:RegisterESPType("prop_door_rotating", apartmentsESP, "apartments")
|
||||
ix.observer:RegisterESPType("prop_dynamic", apartmentsESP, "apartments")
|
||||
end
|
||||
|
||||
-- Clientside hook to Sync App names, types and doors (for ESP)
|
||||
netstream.Hook("SyncApartments", function(tApartments, bNoSetNames, bDatafile)
|
||||
for appID, tApartment in pairs(tApartments) do
|
||||
if PLUGIN.apartments[appID] then continue end
|
||||
|
||||
PLUGIN.apartments[appID] = tApartment
|
||||
end
|
||||
|
||||
if !bNoSetNames then
|
||||
PLUGIN:SetApartmentNames(tApartments)
|
||||
end
|
||||
|
||||
if !bDatafile then return end
|
||||
if IsValid(ix.gui.datafileHousingInfo) and ix.gui.datafileHousingInfo.CreateHousingRow then
|
||||
ix.gui.datafileHousingInfo:CreateHousingRow(tApartments)
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("SyncApartmentsToDatapad", function(tApartments)
|
||||
PLUGIN.apartments = tApartments
|
||||
if !IsValid(ix.gui.apartments) then return end
|
||||
if !ix.gui.apartments.CreateApartments then return end
|
||||
|
||||
if !tApartments or tApartments and !istable(tApartments) then return end
|
||||
if #tApartments == 0 then
|
||||
ix.gui.apartments.curCollect = ix.gui.apartments.curCollect - 5
|
||||
return
|
||||
end
|
||||
|
||||
ix.gui.apartments:ClearContent()
|
||||
|
||||
local normalHousing = {}
|
||||
local shops = {}
|
||||
|
||||
for _, tApartment in pairs(tApartments) do
|
||||
local appID = tApartment.appID or 1
|
||||
|
||||
if tApartment.type == "shop" then
|
||||
shops[appID] = tApartment
|
||||
continue
|
||||
end
|
||||
|
||||
normalHousing[appID] = tApartment
|
||||
end
|
||||
|
||||
ix.gui.apartments:CreateApartments(normalHousing, function(panel, combineutilities)
|
||||
panel:Dock(TOP)
|
||||
panel:SetTall(ix.gui.apartments:GetParent():GetTall() * 0.5 - ix.gui.apartments.apartmentsFrame:GetTall())
|
||||
panel:DockMargin(0, 0, 0, SScaleMin(10 / 3))
|
||||
|
||||
ix.gui.apartments.shopsFrame = ix.gui.apartments:Add("EditablePanel")
|
||||
combineutilities:CreateTitle(ix.gui.apartments.shopsFrame, ix.gui.apartments, "shops")
|
||||
|
||||
local decrementApartments, incrementApartments = ix.gui.apartments.shopsFrame:Add("DButton"), ix.gui.apartments.shopsFrame:Add("DButton")
|
||||
combineutilities:CreateTitleFrameRightTextButton(incrementApartments, ix.gui.apartments.shopsFrame, 87, "next page", RIGHT)
|
||||
incrementApartments:SetZPos(2)
|
||||
combineutilities:CreateTitleFrameRightTextButton(decrementApartments, ix.gui.apartments.shopsFrame, 87, "previous page", RIGHT)
|
||||
decrementApartments:SetZPos(3)
|
||||
incrementApartments:DockMargin(SScaleMin(20 / 3), 0, 0 - SScaleMin(3 / 3), SScaleMin(6 / 3))
|
||||
|
||||
ix.gui.apartments.nextClick = CurTime()
|
||||
|
||||
decrementApartments.DoClick = function()
|
||||
if ix.gui.apartments.nextClick > CurTime() then return end
|
||||
|
||||
ix.gui.apartments.curCollect = math.max(ix.gui.apartments.curCollect - 5, 5)
|
||||
ix.gui.apartments:GetApartments(ix.gui.apartments.curCollect)
|
||||
|
||||
ix.gui.apartments.nextClick = CurTime() + 1
|
||||
end
|
||||
|
||||
incrementApartments.DoClick = function()
|
||||
if ix.gui.apartments.nextClick > CurTime() then return end
|
||||
|
||||
ix.gui.apartments.curCollect = ix.gui.apartments.curCollect + 5
|
||||
ix.gui.apartments:GetApartments(ix.gui.apartments.curCollect)
|
||||
|
||||
ix.gui.apartments.nextClick = CurTime() + 1
|
||||
end
|
||||
|
||||
ix.gui.apartments.content2 = ix.gui.apartments:Add("DScrollPanel")
|
||||
ix.gui.apartments.content2:Dock(FILL)
|
||||
|
||||
ix.gui.apartments:CreateApartments(shops, false, true)
|
||||
end)
|
||||
end)
|
||||
|
||||
netstream.Hook("UpdateIndividualApartment", function(appID, tApartment)
|
||||
if IsValid(ix.gui.apartments) and ix.gui.apartments.ViewSingleApartment then
|
||||
ix.gui.apartments:ViewSingleApartment(appID, tApartment)
|
||||
end
|
||||
end)
|
||||
|
||||
local doors = ix.plugin.list["doors"]
|
||||
if !doors then return end
|
||||
|
||||
do
|
||||
doors.DrawDoorInfo = function(self, door, width, position, angles, scale, clientPosition)
|
||||
local alpha = math.max((1 - clientPosition:DistToSqr(door:GetPos()) / 65536) * 255, 0)
|
||||
|
||||
if (alpha < 1) then
|
||||
return
|
||||
end
|
||||
|
||||
local info = hook.Run("GetDoorInfo", door) or self:GetDefaultDoorInfo(door)
|
||||
|
||||
if (!istable(info) or table.IsEmpty(info)) then
|
||||
return
|
||||
end
|
||||
|
||||
-- title + background
|
||||
surface.SetFont("WN3D2DLargeText")
|
||||
if string.len(info.name) > 23 then
|
||||
surface.SetFont("WN3D2DMediumText")
|
||||
end
|
||||
local nameWidth, nameHeight = surface.GetTextSize(info.name)
|
||||
|
||||
surface.SetTextColor(ColorAlpha(color_white, alpha))
|
||||
surface.SetTextPos(-nameWidth * 0.5, -nameHeight * 0.5)
|
||||
surface.DrawText(info.name)
|
||||
|
||||
-- description
|
||||
local lines = ix.util.WrapText(info.description, width, "ix3D2DSmallFont")
|
||||
local y = nameHeight * 0.5 + 4
|
||||
|
||||
for i = 1, #lines do
|
||||
local line = lines[i]
|
||||
if line:find(L("dIsOwnable")) or line:find(L("dIsNotOwnable")) then return end
|
||||
local textWidth, textHeight = surface.GetTextSize(line)
|
||||
|
||||
surface.SetTextPos(-textWidth * 0.5, y)
|
||||
surface.DrawText(line)
|
||||
|
||||
y = y + textHeight
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:CreateExtraCharacterTabInfo(character, informationSubframe, CreatePart)
|
||||
local genericData = character:GetGenericdata()
|
||||
local genericHousing = genericData.housing
|
||||
if genericHousing then
|
||||
netstream.Start("RequestApartmentNames", tonumber(genericData.housing))
|
||||
end
|
||||
|
||||
local housing = ix.plugin.list["housing"]
|
||||
local apartments = housing.apartments or {}
|
||||
|
||||
-- Apartment
|
||||
local apartmentPanel = informationSubframe:Add("Panel")
|
||||
CreatePart(apartmentPanel, "Apartment:", (genericHousing and apartments[tonumber(genericData.housing)] and apartments[tonumber(genericData.housing)].name) or "N/A", "cid")
|
||||
end
|
||||
218
gamemodes/ixhl2rp/plugins/housing/derma/cl_showallapartments.lua
Normal file
218
gamemodes/ixhl2rp/plugins/housing/derma/cl_showallapartments.lua
Normal file
@@ -0,0 +1,218 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PANEL = {}
|
||||
|
||||
local padding = SScaleMin(10 / 3)
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
function PANEL:Init()
|
||||
ix.gui.apartmentseditor = self
|
||||
self:SetSize(ScrW(), ScrH())
|
||||
self.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(63, 58, 115, 220))
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
|
||||
Derma_DrawBackgroundBlur( this, 1 )
|
||||
end
|
||||
|
||||
self.content = self:Add("EditablePanel")
|
||||
self.content:SetSize(SScaleMin(700 / 3), SScaleMin(600 / 3))
|
||||
self.content:Center()
|
||||
self.content:MakePopup()
|
||||
self.content:DockPadding(padding, 0, padding, padding)
|
||||
self.content.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(0, 0, 0, 130)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
Schema:AllowMessage(self.content)
|
||||
|
||||
self:CreateTopBar()
|
||||
|
||||
self.combineutilities = ix.plugin.list["combineutilities"]
|
||||
if !self.combineutilities then return end
|
||||
|
||||
self.topHalf = self.content:Add("Panel")
|
||||
self.topHalf:Dock(TOP)
|
||||
|
||||
local titleFrame = self.topHalf:Add("EditablePanel")
|
||||
self.combineutilities:CreateTitle(titleFrame, self.topHalf, "apartments")
|
||||
self.topHalf:SetTall(self.content:GetTall() * 0.5 - titleFrame:GetTall())
|
||||
|
||||
local titleSubframe = titleFrame:Add("EditablePanel")
|
||||
titleSubframe:SetSize(SScaleMin(300 / 3), 0)
|
||||
titleSubframe:Dock(RIGHT)
|
||||
titleSubframe.Paint = nil
|
||||
|
||||
local addApartment = titleSubframe:Add("DButton")
|
||||
self.combineutilities:CreateTitleFrameRightTextButton(addApartment, titleSubframe, 87, "add housing", RIGHT)
|
||||
addApartment.DoClick = function()
|
||||
local window = Derma_StringRequest("Select Apartment Type", "Which type do you want this to be?", false, false, false, false, false, true)
|
||||
local types = {"shop", "priority", "normal"}
|
||||
|
||||
PLUGIN:ConvertStringRequestToComboselect(window, "Choose Type", function(comboBox)
|
||||
for _, type in pairs(types) do
|
||||
comboBox:AddChoice( type, type )
|
||||
end
|
||||
end, function(comboBox)
|
||||
local _, type = comboBox:GetSelected()
|
||||
if !type then window:Close() return end
|
||||
|
||||
Derma_StringRequest("Set Name", "Sets the name of the new apartment.", "", function(name)
|
||||
netstream.Start("CreateApartmentApartmentsUI", type, name)
|
||||
surface.PlaySound("willardnetworks/datapad/navigate.wav")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
self.apartmentPanel = self.topHalf:Add("DScrollPanel")
|
||||
self.apartmentPanel:Dock(FILL)
|
||||
self.apartmentPanel:DockMargin(0, 0, 0, padding)
|
||||
|
||||
self.bottomHalf = self.content:Add("Panel")
|
||||
self.bottomHalf:Dock(FILL)
|
||||
|
||||
local titleFrame1 = self.bottomHalf:Add("EditablePanel")
|
||||
self.combineutilities:CreateTitle(titleFrame1, self.bottomHalf, "shops")
|
||||
|
||||
self.shopPanel = self.bottomHalf:Add("DScrollPanel")
|
||||
self.shopPanel:Dock(FILL)
|
||||
|
||||
self:RequestContent()
|
||||
end
|
||||
|
||||
function PANEL:RequestContent()
|
||||
netstream.Start("RequestApartmentNamesApartmentsPanel")
|
||||
end
|
||||
|
||||
function PANEL:CreateContent(appTable)
|
||||
self.apartmentPanel:Clear()
|
||||
self.shopPanel:Clear()
|
||||
|
||||
local appCounter = 0
|
||||
local shopCounter = 0
|
||||
|
||||
for appID, tApartment in SortedPairsByMemberValue(appTable, "name") do
|
||||
if tApartment.type == "shop" then
|
||||
shopCounter = shopCounter + 1
|
||||
|
||||
self:CreateRow(self.shopPanel, appID, tApartment, shopCounter)
|
||||
continue
|
||||
end
|
||||
|
||||
appCounter = appCounter + 1
|
||||
|
||||
self:CreateRow(self.apartmentPanel, appID, tApartment, appCounter)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateRow(parent, appID, tApartment, counter)
|
||||
local appRow = parent:Add("EditablePanel")
|
||||
self.combineutilities:CreateRow(appRow, "name | rent", tApartment.name.." | "..tApartment.rent or "", nil, (counter % 2 == 0 and true or false))
|
||||
|
||||
self:CreateBottomOrTopTextOrButton(appRow:GetChildren()[1], "OPTIONS:", true)
|
||||
local editName = self:CreateBottomOrTopTextOrButton(appRow.bottom, "EDIT NAME", false, true)
|
||||
editName.DoClick = function()
|
||||
Derma_StringRequest("Change Name", "Changes the name of the apartment.", tApartment.name, function(text)
|
||||
netstream.Start("ChangeApartmentName", appID, text)
|
||||
surface.PlaySound("willardnetworks/datapad/navigate.wav")
|
||||
end)
|
||||
end
|
||||
|
||||
local editRent = self:CreateBottomOrTopTextOrButton(appRow.bottom, "EDIT RENT", false, true)
|
||||
editRent.DoClick = function()
|
||||
Derma_StringRequest("Change Rent", "Changes the required rent of the apartment.", tApartment.rent, function(text)
|
||||
netstream.Start("ApartmentUpdateRent", appID, tonumber(text))
|
||||
surface.PlaySound("willardnetworks/datapad/navigate.wav")
|
||||
end)
|
||||
|
||||
surface.PlaySound("willardnetworks/datapad/navigate.wav")
|
||||
end
|
||||
|
||||
local remove = self:CreateBottomOrTopTextOrButton(appRow.bottom, "REMOVE", false, true)
|
||||
remove.DoClick = function()
|
||||
netstream.Start("RemoveApartmentHousing", tApartment.name)
|
||||
end
|
||||
end
|
||||
|
||||
function PANEL:CreateBottomOrTopTextOrButton(parent, text, bTop, bButton)
|
||||
local labelText = parent:Add(bButton and "DButton" or "DLabel")
|
||||
labelText:SetTextColor(bTop and Color(154, 169, 175, 255) or Color(41, 243, 229, 255))
|
||||
labelText:SetFont("MenuFontNoClamp")
|
||||
labelText:SetText(text)
|
||||
labelText:Dock(RIGHT)
|
||||
labelText:DockMargin(0, 0, SScaleMin(20 / 3), 0)
|
||||
labelText:SizeToContents()
|
||||
labelText.Paint = nil
|
||||
|
||||
return labelText
|
||||
end
|
||||
|
||||
function PANEL:CreateTopBar()
|
||||
self.topbar = self.content:Add("Panel")
|
||||
self.topbar:SetSize(self:GetWide(), SScaleMin(50 / 3))
|
||||
self.topbar:Dock(TOP)
|
||||
self.topbar:DockMargin(0, 0, 0, padding)
|
||||
self.topbar.Paint = function( this, w, h )
|
||||
surface.SetDrawColor(0, 0, 0, 130)
|
||||
surface.DrawRect(0, 0, w, h)
|
||||
end
|
||||
|
||||
self.titleText = self.topbar:Add("DLabel")
|
||||
self.titleText:SetFont("CharCreationBoldTitleNoClamp")
|
||||
self.titleText:Dock(LEFT)
|
||||
self.titleText:SetText("Apartments Information")
|
||||
self.titleText:DockMargin(SScaleMin(10 / 3), 0, 0, 0)
|
||||
self.titleText:SetContentAlignment(4)
|
||||
self.titleText:SizeToContents()
|
||||
|
||||
local exit = self.topbar:Add("DImageButton")
|
||||
exit:SetImage("willardnetworks/tabmenu/navicons/exit.png")
|
||||
exit:SetSize(SScaleMin(20 / 3), SScaleMin(20 / 3))
|
||||
exit:DockMargin(0, SScaleMin(15 / 3), SScaleMin(10 / 3), SScaleMin(15 / 3))
|
||||
exit:Dock(RIGHT)
|
||||
exit.DoClick = function()
|
||||
self:Remove()
|
||||
surface.PlaySound("helix/ui/press.wav")
|
||||
end
|
||||
|
||||
local divider = self.topbar:Add("Panel")
|
||||
self:CreateDivider(divider)
|
||||
end
|
||||
|
||||
function PANEL:CreateDivider(parent)
|
||||
parent:SetSize(1, self.topbar:GetTall())
|
||||
parent:Dock(RIGHT)
|
||||
parent:DockMargin(SScaleMin(5 / 3), padding, padding + SScaleMin(5 / 3), padding)
|
||||
parent.Paint = function(this, w, h)
|
||||
surface.SetDrawColor(Color(111, 111, 136, (255 / 100 * 30)))
|
||||
surface.DrawLine(0, 0, 0, h)
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("ixHousingShowAllApartments", function()
|
||||
vgui.Create("ixHousingShowAllApartments")
|
||||
end)
|
||||
|
||||
netstream.Hook("UpdateApartmentList", function()
|
||||
if IsValid(ix.gui.apartmentseditor) then
|
||||
ix.gui.apartmentseditor:RequestContent()
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("SyncApartmentsApartmentsPanel", function(nameAndType)
|
||||
if IsValid(ix.gui.apartmentseditor) then
|
||||
ix.gui.apartmentseditor:CreateContent(nameAndType)
|
||||
end
|
||||
end)
|
||||
|
||||
vgui.Register("ixHousingShowAllApartments", PANEL, "EditablePanel")
|
||||
309
gamemodes/ixhl2rp/plugins/housing/items/base/sh_housingkey.lua
Normal file
309
gamemodes/ixhl2rp/plugins/housing/items/base/sh_housingkey.lua
Normal file
@@ -0,0 +1,309 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
ITEM.name = "Housing Key"
|
||||
ITEM.model = Model("models/willardnetworks/props/vortkey.mdl")
|
||||
ITEM.description = ""
|
||||
ITEM.category = "Housing"
|
||||
ITEM.KeepOnDeath = true
|
||||
ITEM.iconCam = {
|
||||
pos = Vector(-509.64, -427.61, 310.24),
|
||||
ang = Angle(25.06, 400.15, 0),
|
||||
fov = 0.68
|
||||
}
|
||||
|
||||
if (CLIENT) then
|
||||
function ITEM:GetDescription()
|
||||
if self:GetData("cid") then
|
||||
return "A key assigned to CID: "..self:GetData("cid").." to open the doors of an assigned "..(self.shop and "shop" or "apartment").."."
|
||||
end
|
||||
|
||||
return "A key to open the doors of an assigned "..(self.shop and "shop" or "apartment").."."
|
||||
end
|
||||
|
||||
function ITEM:PopulateTooltip(tooltip)
|
||||
if (self:GetData("apartment")) then
|
||||
local appID = self:GetData("apartment")
|
||||
PLUGIN:GetApartmentName(appID)
|
||||
|
||||
if PLUGIN.apartments[appID] then
|
||||
local panel = tooltip:AddRowAfter("name", "apartment")
|
||||
panel:SetBackgroundColor(derma.GetColor("Warning", tooltip))
|
||||
panel:SetText("Assigned "..(self.shop and "Shop" or "Apartment")..": " .. (appID and PLUGIN.apartments[appID].name or appID))
|
||||
panel:SizeToContents()
|
||||
end
|
||||
end
|
||||
|
||||
local id = tooltip:AddRowAfter("apartment", "id")
|
||||
id:SetBackgroundColor(Color(125, 96, 90, 255))
|
||||
id:SetText("Key ID: " .. self:GetID())
|
||||
id:SizeToContents()
|
||||
end
|
||||
end
|
||||
|
||||
function ITEM:SetCID(cid)
|
||||
local client = self:GetOwner()
|
||||
|
||||
PLUGIN:SetKeyCID(client, self, cid)
|
||||
end
|
||||
|
||||
function ITEM:AssignToApartment(apartment)
|
||||
if !apartment then return false end
|
||||
local client = self:GetOwner()
|
||||
|
||||
PLUGIN:AddKeyToApartment(client, self, apartment)
|
||||
end
|
||||
|
||||
function ITEM:RemoveApartment(appID)
|
||||
local client = self:GetOwner()
|
||||
appID = appID or self:GetData("apartment")
|
||||
|
||||
PLUGIN:AddKeyToApartment(client, self, appID, true)
|
||||
end
|
||||
|
||||
function ITEM:UnlockLockApartment(bUnlock)
|
||||
local client = self:GetOwner()
|
||||
local targetEnt = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetEnt) and targetEnt:IsDoor()) then
|
||||
PLUGIN:UnlockLockApartment(self, bUnlock, targetEnt)
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
|
||||
ITEM.functions.Unlock = {
|
||||
name = "Unlock Door",
|
||||
icon = "icon16/lock_open.png",
|
||||
OnRun = function(itemTable)
|
||||
itemTable:UnlockLockApartment(true)
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
local client = itemTable:GetOwner()
|
||||
if !client then return false end
|
||||
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local targetEnt = client:GetEyeTrace().Entity
|
||||
if (!IsValid(targetEnt)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if !targetEnt:IsDoor() then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!itemTable:GetData("apartment", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.Lock = {
|
||||
name = "Lock Door",
|
||||
icon = "icon16/lock.png",
|
||||
OnRun = function(itemTable)
|
||||
itemTable:UnlockLockApartment()
|
||||
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
local client = itemTable.GetOwner and itemTable:GetOwner()
|
||||
if !client then return false end
|
||||
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local targetEnt = client:GetEyeTrace().Entity
|
||||
if (!IsValid(targetEnt)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if !targetEnt:IsDoor() then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!itemTable:GetData("apartment", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
local function ShouldHaveAccess(client)
|
||||
if (SERVER) then
|
||||
local combineutilities = ix.plugin.list["combineutilities"]
|
||||
if (combineutilities and combineutilities:HasAccessToDatafile(client)) then return true end
|
||||
else
|
||||
local faction = ix.faction.Get(client:Team())
|
||||
if (faction.alwaysDatafile or client:GetCharacter():HasFlags("U")) then
|
||||
return true
|
||||
elseif (client:HasActiveCombineSuit() or faction.allowDatafile) then
|
||||
if (client:GetCharacter():GetInventory():HasItem("pda") or client:GetActiveWeapon():GetClass() == "weapon_datapad") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
ITEM.functions.Assign = {
|
||||
name = "Assign to Housing",
|
||||
icon = "icon16/building_go.png",
|
||||
OnClick = function(itemTable)
|
||||
local window = Derma_StringRequest("Assign Key To "..(itemTable.shop and "Shop" or "Apartment"), "Which "..(itemTable.shop and "shop" or "apartment").." do you want to assign this to?", false, function()
|
||||
netstream.Start("ixApartmentsAssignCIDToKey", itemTable:GetID())
|
||||
end, false, false, false, true)
|
||||
|
||||
PLUGIN:ConvertStringRequestToComboselect(window, "Choose "..(itemTable.shop and "Shop" or "Apartment"), function(comboBox)
|
||||
for appID, tApartment in pairs(PLUGIN.apartments) do
|
||||
if !tApartment.type then continue end
|
||||
if itemTable.shop and tApartment.type != "shop" then continue end
|
||||
if !itemTable.shop and tApartment.type == "shop" then continue end
|
||||
|
||||
comboBox:AddChoice( tApartment.name.." | "..string.upper(tApartment.type), appID )
|
||||
end
|
||||
end, function(comboBox)
|
||||
local _, data = comboBox:GetSelected()
|
||||
if !data then window:Close() return end
|
||||
netstream.Start("ixApartmentsAssignKeyToApartment", itemTable:GetID(), data)
|
||||
end)
|
||||
end,
|
||||
OnRun = function(itemTable)
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local client = itemTable.player
|
||||
if !client then return false end
|
||||
if ShouldHaveAccess(client) then return true end
|
||||
|
||||
if !CAMI.PlayerHasAccess(itemTable:GetOwner(), "Helix - Manage Apartments Key", nil) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!itemTable:GetData("cid", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (itemTable:GetData("apartment", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.AssignCID = {
|
||||
name = "Assign CID",
|
||||
icon = "icon16/vcard_add.png",
|
||||
OnClick = function(itemTable)
|
||||
Derma_StringRequest("Set "..(itemTable.shop and "Shop" or "Apartment").." Key CID", "What CID do you want to assign this to?", nil, function(text)
|
||||
netstream.Start("ixApartmentsAssignCIDToKey", itemTable:GetID(), text)
|
||||
end)
|
||||
end,
|
||||
OnRun = function(itemTable)
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local client = itemTable.player
|
||||
if !client then return false end
|
||||
|
||||
if ShouldHaveAccess(client) then return true end
|
||||
|
||||
if !CAMI.PlayerHasAccess(itemTable:GetOwner(), "Helix - Manage Apartments Key", nil) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (itemTable:GetData("cid", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.RemoveTenant = {
|
||||
name = "Remove From Housing",
|
||||
icon = "icon16/building_delete.png",
|
||||
OnClick = function(itemTable)
|
||||
netstream.Start("ixApartmentsAssignKeyToApartment", itemTable:GetID(), false, true)
|
||||
end,
|
||||
OnRun = function(itemTable)
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local client = itemTable.player
|
||||
if !client then return false end
|
||||
if ShouldHaveAccess(client) then return true end
|
||||
|
||||
if !CAMI.PlayerHasAccess(itemTable:GetOwner(), "Helix - Manage Apartments Key", nil) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!itemTable:GetData("apartment", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
ITEM.functions.RemoveCID = {
|
||||
name = "Remove CID from key",
|
||||
icon = "icon16/vcard_delete.png",
|
||||
OnClick = function(itemTable)
|
||||
netstream.Start("ixApartmentsAssignCIDToKey", itemTable:GetID(), false)
|
||||
end,
|
||||
OnRun = function(itemTable)
|
||||
return false
|
||||
end,
|
||||
OnCanRun = function(itemTable)
|
||||
if (IsValid(itemTable.entity)) then
|
||||
return false
|
||||
end
|
||||
|
||||
local client = itemTable.player
|
||||
if !client then return false end
|
||||
if ShouldHaveAccess(client) then return true end
|
||||
|
||||
if !CAMI.PlayerHasAccess(itemTable:GetOwner(), "Helix - Manage Apartments Key", nil) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (!itemTable:GetData("cid", false)) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
ITEM.name = "Apartment Key"
|
||||
@@ -0,0 +1,17 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
ITEM.name = "Shop Key"
|
||||
ITEM.shop = true
|
||||
ITEM.iconCam = {
|
||||
pos = Vector(-509.64, -427.61, 310.24),
|
||||
ang = Angle(25.06, 400.15, 0),
|
||||
fov = 0.68
|
||||
}
|
||||
232
gamemodes/ixhl2rp/plugins/housing/sh_commands.lua
Normal file
232
gamemodes/ixhl2rp/plugins/housing/sh_commands.lua
Normal file
@@ -0,0 +1,232 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
ix.command.Add("ShowAllApartments", {
|
||||
description = "Shows all apartments with some options.",
|
||||
privilege = "Manage Apartments",
|
||||
OnRun = function(self, client)
|
||||
netstream.Start(client, "ixHousingShowAllApartments")
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("CreateApartment", {
|
||||
description = "Creates an apartment group.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, apartmentName)
|
||||
PLUGIN:CreateApartment(client, apartmentName, false, "normal")
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("CreatePriorityApartment", {
|
||||
description = "Creates an apartment group for loyalists.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, apartmentName)
|
||||
PLUGIN:CreateApartment(client, apartmentName, false, "priority")
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("CreateShop", {
|
||||
description = "Creates an apartment group for shops.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, apartmentName)
|
||||
PLUGIN:CreateApartment(client, apartmentName, false, "shop")
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveApartment", {
|
||||
description = "Removes an apartment group.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, apartmentName)
|
||||
PLUGIN:RemoveApartment(client, apartmentName)
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("SetApartmentDoor", {
|
||||
description = "Adds the door you are looking at to an apartment group and creates a non-priority group if it doesn't exist.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.text,
|
||||
OnRun = function(self, client, apartmentName)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
PLUGIN:SetApartmentDoor(client, targetDoor, apartmentName)
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("RemoveApartmentDoor", {
|
||||
description = "Removes the door you are looking at from an apartment group.",
|
||||
privilege = "Manage Apartments",
|
||||
OnRun = function(self, client)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
PLUGIN:RemoveApartmentDoor(client, targetDoor)
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("GetDoorApartment", {
|
||||
description = "Gets the apartment group the door you are looking at is assigned to.",
|
||||
privilege = "Manage Apartments",
|
||||
OnRun = function(self, client)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
PLUGIN:GetDoorApartment(client, targetDoor)
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("SetApartmentRent", {
|
||||
description = "Sets the rent of an apartment group by the door you are looking at.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.number,
|
||||
OnRun = function(self, client, rent)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
PLUGIN:SetApartmentRent(client, false, targetDoor, rent)
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("AssignApartment", {
|
||||
description = "Assigns a character the first found apartment with 0-2 tenants until found (types: normal, priority).",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = {ix.type.character, ix.type.string},
|
||||
OnRun = function(self, client, character, appType)
|
||||
if appType then
|
||||
if string.lower(appType) == "shop" then
|
||||
client:NotifyLocalized("This command does not assign people to shops..")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if (character) then
|
||||
local appID = PLUGIN:GetWhichApartmentToAssign(appType)
|
||||
if (appID) then
|
||||
for oldAppID, v in pairs(PLUGIN.apartments) do
|
||||
if v.type == "shop" then continue end
|
||||
|
||||
if v.tenants[character:GetCid()] then
|
||||
client:NotifyLocalized("This character's CID is already assigned to the apartment: "..v.name.."!")
|
||||
PLUGIN:AddKeyToApartment(client, character:GetCid(), oldAppID, true)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if PLUGIN.apartments[appID].tenants[character:GetCid()] then
|
||||
PLUGIN:AddKeyToApartment(client, character:GetCid(), appID, true)
|
||||
else
|
||||
PLUGIN:AddKeyToApartment(client, character:GetCid(), appID)
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Could not find a suitable apartment!")
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Could not find the character!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("SetApartmentTenant", {
|
||||
description = "Sets/Removes the tenant of an apartment group by the door you are looking at.",
|
||||
privilege = "Manage Apartments",
|
||||
arguments = ix.type.character,
|
||||
OnRun = function(self, client, character)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
if character then
|
||||
local appID = PLUGIN:CheckIfDoorAlreadyAdded(client, targetDoor, true)
|
||||
if !appID then
|
||||
client:NotifyLocalized("This door is not allocated to any apartment.")
|
||||
return
|
||||
end
|
||||
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
client:NotifyLocalized("This command cannot add tenants to a shop.")
|
||||
return
|
||||
end
|
||||
|
||||
local bFound = false
|
||||
local inventory = character:GetInventory()
|
||||
|
||||
for _, v in pairs(inventory:GetItems()) do
|
||||
if v.uniqueID != "apartmentkey" then continue end
|
||||
if v.shop then continue end
|
||||
|
||||
if !v:GetData("apartment", false) then
|
||||
if v:GetData("cid", false) and v:GetData("cid", false) == character:GetCid() then
|
||||
bFound = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if v:GetData("apartment", false) and v:GetData("apartment", false) == appID then
|
||||
bFound = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if bFound then
|
||||
local otherAccess = PLUGIN:GetKeyAlreadyHasOtherAccess(bFound)
|
||||
|
||||
if otherAccess then
|
||||
if bFound:GetData("apartment") then
|
||||
bFound:RemoveApartment()
|
||||
return
|
||||
else
|
||||
bFound:RemoveApartment(otherAccess)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
bFound:AssignToApartment(appID)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if PLUGIN.apartments[appID].tenants[character:GetCid()] then
|
||||
PLUGIN:AddKeyToApartment(client, character:GetCid(), appID, true)
|
||||
else
|
||||
PLUGIN:AddKeyToApartment(client, character:GetCid(), appID)
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Could not find the character!")
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("You are not looking at a valid door!")
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
ix.command.Add("ToggleApartmentNoAssign", {
|
||||
description = "Toggles apartment capability of automatic assignment via terminal by the door you are looking at.",
|
||||
privilege = "Manage Apartments",
|
||||
OnRun = function(self, client)
|
||||
local targetDoor = client:GetEyeTrace().Entity
|
||||
if (IsValid(targetDoor) and targetDoor:IsDoor()) then
|
||||
local appID = PLUGIN:CheckIfDoorAlreadyAdded(client, targetDoor, true)
|
||||
PLUGIN:ToggleApartmentNoAssign(client, appID)
|
||||
end
|
||||
end
|
||||
})
|
||||
128
gamemodes/ixhl2rp/plugins/housing/sh_plugin.lua
Normal file
128
gamemodes/ixhl2rp/plugins/housing/sh_plugin.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
PLUGIN.name = "Housing"
|
||||
PLUGIN.author = "Fruity"
|
||||
PLUGIN.description = "A way for citizens to have housing assigned to them automatically via the citizen terminal."
|
||||
|
||||
ix.util.Include("cl_plugin.lua")
|
||||
ix.util.Include("sh_commands.lua")
|
||||
ix.util.Include("sv_plugin.lua")
|
||||
ix.util.Include("sv_util.lua")
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Apartments Key",
|
||||
MinAccess = "superadmin"
|
||||
})
|
||||
|
||||
CAMI.RegisterPrivilege({
|
||||
Name = "Helix - Manage Apartments",
|
||||
MinAccess = "admin"
|
||||
})
|
||||
|
||||
ix.config.Add("housingCheckInteractionAndRentTimer", 20, "The amount of time in minutes to check if rent is due and if people have interacted with their apartment.", PLUGIN.UpdateInteractionTimer, {
|
||||
data = {min = 1, max = 120},
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("costForAnApartment", 35, "The amount of credits needed to request an apartment.", nil, {
|
||||
data = {min = 1, max = 100},
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("tenantDoorInteractionCheckWeeks", 2, "The amount of weeks citizens can go without using a door before being removed as tenant.", nil, {
|
||||
data = {min = 1, max = 10},
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("housingRentDueInWeeks", 1, "The amount of weeks before rent is due for apartment rent sessions.", nil, {
|
||||
data = {min = 1, max = 10},
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("shouldLockDoorsAfterRestart", true, "Determines whether doors assigned to an apartment should be locked after restart.", nil, {
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("shouldCreateNonExistingApartment", true, "Determines whether apartments should be automatically created if they do not exist when using the /SetApartmentDoor command.", nil, {
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("housingTesterMode", false, "Enables tester mode, so that rent is due every 10 seconds instead (for new apartments).", function(_, newValue)
|
||||
if (CLIENT) then return end
|
||||
|
||||
if ix.config.Get("housingTesterMode", false) then
|
||||
return timer.Adjust("ixHousingCheckInteractionAndRent", 10)
|
||||
else
|
||||
return timer.Adjust("ixHousingCheckInteractionAndRent", ix.config.Get("housingCheckInteractionAndRentTimer", 20) * 60)
|
||||
end
|
||||
end, {
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("housingFirstDouble", false, "Prefer assigning people into apartments with 1 tenant before they get assigned an empty apartment.", nil, {
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("housingFirstFull", false, "Prefer to assign people into the fullest apartment first, then nearly full, etc. Empty apartments will only be filled if there is no room anywhere else.", nil, {
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("housingMaxTenants", 3, "How many peole should be assigned into an apartment.", nil, {
|
||||
data = {min = 2, max = 10},
|
||||
category = "Housing"
|
||||
})
|
||||
|
||||
ix.config.Add("priorityHousingTierNeeded", "TIER 4 (BLUE)", "Determines the tier needed for priority housing to be available.", nil, {
|
||||
type = ix.type.array,
|
||||
category = "Housing",
|
||||
populate = function()
|
||||
local entries = {}
|
||||
|
||||
for _, v in SortedPairs({"TIER 4 (BLUE)", "TIER 5 (GREEN)", "TIER 6 (WHITE)", "TIER 7 (COMMENDED)", "CCA MEMBER"}) do
|
||||
local name = v
|
||||
local name2 = v:utf8sub(1, 1):utf8upper() .. v:utf8sub(2)
|
||||
|
||||
if (name) then
|
||||
name = name
|
||||
else
|
||||
name = name2
|
||||
end
|
||||
|
||||
entries[v] = name
|
||||
end
|
||||
|
||||
return entries
|
||||
end
|
||||
})
|
||||
|
||||
function PLUGIN:GetNumbersFromText(txt)
|
||||
local str = ""
|
||||
string.gsub(txt,"%d+",function(e)
|
||||
str = str .. e
|
||||
end)
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
function PLUGIN:GetRemainingRent(appTable)
|
||||
local rent = tonumber(appTable.rent)
|
||||
if !rent then return 0 end
|
||||
if !appTable.payments then return 0 end
|
||||
|
||||
for _, tPaymentInfo in pairs(appTable.payments) do
|
||||
rent = tonumber(rent) - tonumber(tPaymentInfo.amount)
|
||||
end
|
||||
|
||||
return math.max(0, tonumber(rent))
|
||||
end
|
||||
988
gamemodes/ixhl2rp/plugins/housing/sv_plugin.lua
Normal file
988
gamemodes/ixhl2rp/plugins/housing/sv_plugin.lua
Normal file
@@ -0,0 +1,988 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
PLUGIN.apartments = PLUGIN.apartments or {}
|
||||
|
||||
-- Database formalia
|
||||
function PLUGIN:DatabaseConnected()
|
||||
local query = mysql:Create("ix_apartments_"..game.GetMap())
|
||||
query:Create("app_id", "INT(11) UNSIGNED NOT NULL AUTO_INCREMENT")
|
||||
query:Create("app_name", "TEXT")
|
||||
query:Create("app_doors", "TEXT")
|
||||
query:Create("app_employees", "TEXT")
|
||||
query:Create("app_permits", "TEXT")
|
||||
query:Create("app_tenants", "TEXT")
|
||||
query:Create("app_lastactions", "TEXT")
|
||||
query:Create("app_rent", "TEXT")
|
||||
query:Create("app_payments", "TEXT")
|
||||
query:Create("app_type", "TEXT")
|
||||
query:Create("app_noassign", "TEXT")
|
||||
query:Create("app_rentdue", "TEXT")
|
||||
query:PrimaryKey("app_id")
|
||||
query:Callback(function()
|
||||
local appsQuery = mysql:Select("ix_apartments_"..game.GetMap())
|
||||
appsQuery:Callback(function(appsResult)
|
||||
if (!istable(appsResult) or #appsResult == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
for _, v in pairs(appsResult) do
|
||||
local doorsCreationID = util.JSONToTable(v["app_doors"])
|
||||
local doors = {}
|
||||
for _, v1 in pairs(doorsCreationID) do
|
||||
local ent = ents.GetMapCreatedEntity(v1)
|
||||
if (!ent) then continue end
|
||||
doors[#doors + 1] = ent:EntIndex()
|
||||
|
||||
if (!ix.config.Get("shouldLockDoorsAfterRestart", true) or self.lockedApps) then continue end
|
||||
ent:Fire("Lock")
|
||||
local partner = ent:GetDoorPartner()
|
||||
if (IsValid(partner)) then
|
||||
partner:Fire("lock")
|
||||
end
|
||||
end
|
||||
|
||||
self.apartments[tonumber(v["app_id"])] = {
|
||||
name = v["app_name"],
|
||||
doors = doors,
|
||||
employees = util.JSONToTable(v["app_employees"]),
|
||||
permits = util.JSONToTable(v["app_permits"]),
|
||||
tenants = util.JSONToTable(v["app_tenants"]),
|
||||
lastActions = util.JSONToTable(v["app_lastactions"]),
|
||||
rent = v["app_rent"],
|
||||
payments = util.JSONToTable(v["app_payments"]),
|
||||
type = v["app_type"],
|
||||
noAssign = v["app_noassign"],
|
||||
rentDue = v["app_rentdue"]
|
||||
}
|
||||
|
||||
local employeeList = {}
|
||||
for cid, tEmployee in pairs(self.apartments[tonumber(v["app_id"])].employees) do
|
||||
employeeList[Schema:ZeroNumber(cid, 5)] = tEmployee
|
||||
end
|
||||
|
||||
self.apartments[tonumber(v["app_id"])].employees = employeeList
|
||||
|
||||
local tenantList = {}
|
||||
for cid, tTenant in pairs(self.apartments[tonumber(v["app_id"])].tenants) do
|
||||
tenantList[Schema:ZeroNumber(cid, 5)] = tTenant
|
||||
end
|
||||
|
||||
self.apartments[tonumber(v["app_id"])].tenants = tenantList
|
||||
|
||||
local lastActionList = {}
|
||||
for cid, osTime in pairs(self.apartments[tonumber(v["app_id"])].lastActions) do
|
||||
lastActionList[Schema:ZeroNumber(cid, 5)] = osTime
|
||||
end
|
||||
|
||||
self.apartments[tonumber(v["app_id"])].lastActions = lastActionList
|
||||
|
||||
local paymentList = {}
|
||||
for cid, tPaymentInfo in pairs(self.apartments[tonumber(v["app_id"])].payments) do
|
||||
paymentList[Schema:ZeroNumber(cid, 5)] = tPaymentInfo
|
||||
end
|
||||
|
||||
self.apartments[tonumber(v["app_id"])].payments = paymentList
|
||||
end
|
||||
|
||||
self.lockedApps = true
|
||||
end)
|
||||
|
||||
appsQuery:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
-- A function to create an apartment
|
||||
function PLUGIN:CreateApartment(client, appName, bNoNotify, sType, callback)
|
||||
if self:CheckIfApartmentExists(client, appName) then
|
||||
client:NotifyLocalized("An apartment with this name already exists..")
|
||||
return false
|
||||
end
|
||||
local permitsToAssign = {}
|
||||
for k, _ in pairs(ix.permits.get()) do
|
||||
permitsToAssign[k] = false
|
||||
end
|
||||
local queryAdd = mysql:Insert("ix_apartments_"..game.GetMap())
|
||||
queryAdd:Insert("app_name", appName)
|
||||
queryAdd:Insert("app_doors", util.TableToJSON({}))
|
||||
queryAdd:Insert("app_employees", util.TableToJSON({}))
|
||||
queryAdd:Insert("app_permits", util.TableToJSON(permitsToAssign))
|
||||
queryAdd:Insert("app_tenants", util.TableToJSON({}))
|
||||
queryAdd:Insert("app_lastactions", util.TableToJSON({}))
|
||||
queryAdd:Insert("app_rent", 0)
|
||||
queryAdd:Insert("app_payments", util.TableToJSON({}))
|
||||
queryAdd:Insert("app_type", sType)
|
||||
queryAdd:Insert("app_noassign", string.lower(sType) == "shop" and true or false)
|
||||
queryAdd:Insert("app_rentdue", os.time() + (!ix.config.Get("housingTesterMode", false) and 3600 * 24 * 7 * ix.config.Get("housingRentDueInWeeks", 1) or 10))
|
||||
queryAdd:Callback(function(result, _, insertID)
|
||||
self.apartments[insertID] = {
|
||||
name = appName,
|
||||
doors = {},
|
||||
employees = {},
|
||||
permits = permitsToAssign,
|
||||
tenants = {},
|
||||
lastActions = {},
|
||||
rent = 0,
|
||||
payments = {},
|
||||
type = sType,
|
||||
noAssign = string.lower(sType) == "shop" and true or false,
|
||||
rentDue = os.time() + (!ix.config.Get("housingTesterMode", false) and 3600 * 24 * 7 * ix.config.Get("housingRentDueInWeeks", 1) or 10)
|
||||
}
|
||||
if (callback) then
|
||||
callback()
|
||||
end
|
||||
|
||||
self:SyncApartments(client)
|
||||
end)
|
||||
queryAdd:Execute()
|
||||
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("You have successfully added a "..sType.." apartment with the name: "..appName)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- A function to remove an apartment
|
||||
function PLUGIN:RemoveApartment(client, appName)
|
||||
local appID = self:CheckIfApartmentExists(client, appName)
|
||||
if appID then
|
||||
if self.apartments[appID] then
|
||||
for _, doorEntID in pairs(self.apartments[appID].doors) do
|
||||
local entDoor = Entity(doorEntID)
|
||||
if (IsValid(entDoor) and entDoor:IsDoor()) then
|
||||
entDoor:SetNetVar("visible", false)
|
||||
entDoor:SetNetVar("name", nil)
|
||||
|
||||
local doorPlugin = ix.plugin.list["doors"]
|
||||
if doorPlugin then
|
||||
doorPlugin:SaveDoorData()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.apartments[appID] = nil
|
||||
|
||||
local queryDelete = mysql:Delete("ix_apartments_"..game.GetMap())
|
||||
queryDelete:Where("app_id", appID)
|
||||
queryDelete:Execute()
|
||||
|
||||
client:NotifyLocalized("Successfully removed the apartment: "..appName)
|
||||
self:SyncApartments(client)
|
||||
else
|
||||
client:NotifyLocalized("This apartment does not exist..")
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to add a door to an apartment
|
||||
function PLUGIN:SetApartmentDoor(client, entDoor, appName)
|
||||
local existingAppID = self:CheckIfApartmentExists(client, appName)
|
||||
|
||||
if !existingAppID then
|
||||
if ix.config.Get("shouldCreateNonExistingApartment", true) then
|
||||
client:NotifyLocalized("This apartment did not exist. Created a non-priority apartment with the name "..appName)
|
||||
self:CreateApartment(client, appName, true, "normal", function()
|
||||
PLUGIN:SetApartmentDoor(client, entDoor, appName)
|
||||
end)
|
||||
return
|
||||
else
|
||||
client:NotifyLocalized("This apartment does not exist.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if self:CheckIfDoorAlreadyAdded(client, entDoor) then
|
||||
return false
|
||||
end
|
||||
|
||||
self.apartments[existingAppID].doors[#self.apartments[existingAppID].doors + 1] = entDoor:EntIndex()
|
||||
|
||||
local doors = {}
|
||||
for _, v in pairs(self.apartments[existingAppID].doors) do
|
||||
if (IsValid(Entity(v))) then
|
||||
local id = Entity(v):MapCreationID()
|
||||
if (id == -1) then continue end
|
||||
doors[#doors + 1] = id
|
||||
end
|
||||
end
|
||||
|
||||
if (IsValid(entDoor) and entDoor:IsDoor()) then
|
||||
entDoor:SetNetVar("visible", true)
|
||||
|
||||
if (appName:find("%S")) then
|
||||
entDoor:SetNetVar("name", appName)
|
||||
end
|
||||
|
||||
local doorPlugin = ix.plugin.list["doors"]
|
||||
if doorPlugin then
|
||||
doorPlugin:SaveDoorData()
|
||||
end
|
||||
end
|
||||
|
||||
local addDoor = mysql:Update("ix_apartments_"..game.GetMap())
|
||||
addDoor:Where("app_id", existingAppID)
|
||||
addDoor:Update("app_doors", util.TableToJSON(doors))
|
||||
addDoor:Execute()
|
||||
|
||||
client:NotifyLocalized("Successfully added this door to "..appName)
|
||||
self:SyncApartments(client)
|
||||
end
|
||||
|
||||
-- A function to remove an door from an apartment
|
||||
function PLUGIN:RemoveApartmentDoor(client, entDoor)
|
||||
for appKey, tInfo in pairs(self.apartments) do
|
||||
for doorKey, doorIndex in pairs(tInfo.doors) do
|
||||
if doorIndex == entDoor:EntIndex() then
|
||||
table.remove(self.apartments[appKey].doors, doorKey)
|
||||
|
||||
local doors = {}
|
||||
for _, v in pairs(self.apartments[appKey].doors) do
|
||||
if (IsValid(Entity(v))) then
|
||||
local id = Entity(v):MapCreationID()
|
||||
if (id == -1) then continue end
|
||||
doors[#doors + 1] = id
|
||||
end
|
||||
end
|
||||
|
||||
if (IsValid(entDoor) and entDoor:IsDoor()) then
|
||||
entDoor:SetNetVar("visible", false)
|
||||
entDoor:SetNetVar("name", nil)
|
||||
|
||||
local doorPlugin = ix.plugin.list["doors"]
|
||||
if doorPlugin then
|
||||
doorPlugin:SaveDoorData()
|
||||
end
|
||||
end
|
||||
|
||||
local removeDoor = mysql:Update("ix_apartments_"..game.GetMap())
|
||||
removeDoor:Where("app_id", appKey)
|
||||
removeDoor:Update("app_doors", util.TableToJSON(doors))
|
||||
removeDoor:Execute()
|
||||
|
||||
client:NotifyLocalized("Successfully removed this door from "..tInfo.name)
|
||||
self:SyncApartments(client)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
client:NotifyLocalized("This door could not be found assigned to an apartment..")
|
||||
return false
|
||||
end
|
||||
|
||||
-- A function to set the rent of an apartment
|
||||
function PLUGIN:SetApartmentRent(client, appID, entDoor, newRent)
|
||||
if (appID) then
|
||||
self.apartments[appID].rent = newRent
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
for _, shopTerminal in pairs(ents.FindByClass("ix_shopterminal")) do
|
||||
shopTerminal:UpdateScreen()
|
||||
end
|
||||
end
|
||||
self:UpdateApartment(appID)
|
||||
return true
|
||||
end
|
||||
|
||||
if (entDoor) then
|
||||
local fetchedID = self:CheckIfDoorAlreadyAdded(client, entDoor, true)
|
||||
if (fetchedID) then
|
||||
self.apartments[fetchedID].rent = newRent
|
||||
self:UpdateApartment(fetchedID)
|
||||
client:NotifyLocalized("Set the rent of "..self.apartments[fetchedID].name.." to "..tostring(newRent).."!")
|
||||
else
|
||||
client:NotifyLocalized("This door is not added to any apartment!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to remove a tenant from an apartment
|
||||
function PLUGIN:RemoveTenant(client, item, appID, bNoNotify)
|
||||
local itemCID = (!isnumber(tonumber(item)) and item:GetData("cid") or item)
|
||||
|
||||
if !self.apartments[appID] then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Could not find the apartment.")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if !self.apartments[appID].tenants[itemCID] then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Could not find an apartment with this CID as tenant.")
|
||||
|
||||
if !isnumber(tonumber(item)) then
|
||||
client:NotifyLocalized("Removed the item assigned apartment.")
|
||||
item:SetData("apartment", nil)
|
||||
end
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
self:LookUpCardItemIDByCID(tostring(itemCID), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
local charID
|
||||
if !result then return end
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(card)
|
||||
if !card then return end
|
||||
charID = card:GetData("owner")
|
||||
end)
|
||||
else
|
||||
charID = ix.item.instances[idCardID]:GetData("owner")
|
||||
end
|
||||
|
||||
local permitsToApply = {}
|
||||
for k, v in pairs(self.apartments[appID].permits) do
|
||||
if v then
|
||||
permitsToApply[k] = !v
|
||||
end
|
||||
end
|
||||
|
||||
local character = ix.char.loaded[charID]
|
||||
if character then
|
||||
for k, v in pairs(permitsToApply) do
|
||||
character:SetPermit(k, v)
|
||||
end
|
||||
else
|
||||
local query = mysql:Select("ix_characters_data")
|
||||
query:Where("key", "genericdata")
|
||||
query:Where("id", tostring(charID))
|
||||
query:Select("data")
|
||||
query:Callback(function(genResult)
|
||||
if (!istable(genResult) or #genResult == 0) then
|
||||
return
|
||||
end
|
||||
local genericData = util.JSONToTable(genResult[1]["data"])
|
||||
|
||||
for k, v in pairs(permitsToApply) do
|
||||
genericData.permits[k] = v
|
||||
end
|
||||
local removePermits = mysql:Update("ix_characters_data")
|
||||
removePermits:Where("id", tostring(charID))
|
||||
removePermits:Where("key", "genericdata")
|
||||
removePermits:Update("data", util.TableToJSON(genericData))
|
||||
removePermits:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end)
|
||||
self.apartments[appID].tenants[itemCID] = nil
|
||||
self.apartments[appID].lastActions[itemCID] = nil
|
||||
if !isnumber(tonumber(item)) then
|
||||
item:SetData("apartment", nil)
|
||||
end
|
||||
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Removed the tenant "..itemCID.." from the apartment.")
|
||||
end
|
||||
|
||||
if self.apartments[appID].type == "shop" and table.IsEmpty(self.apartments[appID].tenants) then
|
||||
for _, shopTerminal in pairs(ents.FindByClass("ix_shopterminal")) do
|
||||
shopTerminal:UpdateScreen()
|
||||
end
|
||||
end
|
||||
|
||||
self:UpdateApartment(appID)
|
||||
|
||||
if !isnumber(tonumber(item)) then return end
|
||||
|
||||
PLUGIN:LookUpCardItemIDByCID(tostring(itemCID), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(cid)
|
||||
if !cid then return end
|
||||
cid:LoadOwnerGenericData(PLUGIN.RemoveTenantHousing, false, appID)
|
||||
end)
|
||||
else
|
||||
ix.item.instances[idCardID]:LoadOwnerGenericData(PLUGIN.RemoveTenantHousing, false, appID)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:RemoveEmployee(client, item, appID, bNoNotify)
|
||||
local itemCID = (!isnumber(tonumber(item)) and item:GetData("cid") or item)
|
||||
if !self.apartments[appID] then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Could not find the apartment.")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
if !self.apartments[appID].employees[itemCID] then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Could not find an apartment with this CID as employee.")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
self:LookUpCardItemIDByCID(tostring(itemCID), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
local charID
|
||||
if !result then return end
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(card)
|
||||
if !card then return end
|
||||
charID = card:GetData("owner")
|
||||
end)
|
||||
else
|
||||
charID = ix.item.instances[idCardID]:GetData("owner")
|
||||
end
|
||||
|
||||
local permitsToApply = {}
|
||||
for k, v in pairs(self.apartments[appID].permits) do
|
||||
if v then
|
||||
permitsToApply[k] = !v
|
||||
end
|
||||
end
|
||||
|
||||
local character = ix.char.loaded[charID]
|
||||
if character then
|
||||
for k, v in pairs(permitsToApply) do
|
||||
character:SetPermit(k, v)
|
||||
end
|
||||
else
|
||||
local query = mysql:Select("ix_characters_data")
|
||||
query:Where("key", "genericdata")
|
||||
query:Where("id", tostring(charID))
|
||||
query:Select("data")
|
||||
query:Callback(function(gResult)
|
||||
if (!istable(gResult) or #gResult == 0) then
|
||||
return
|
||||
end
|
||||
local genericData = util.JSONToTable(gResult[1]["data"])
|
||||
|
||||
for k, v in pairs(permitsToApply) do
|
||||
genericData.permits[k] = v
|
||||
end
|
||||
local removePermits = mysql:Update("ix_characters_data")
|
||||
removePermits:Where("id", tostring(charID))
|
||||
removePermits:Where("key", "genericdata")
|
||||
removePermits:Update("data", util.TableToJSON(genericData))
|
||||
removePermits:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end)
|
||||
|
||||
self.apartments[appID].employees[itemCID] = nil
|
||||
self.apartments[appID].lastActions[itemCID] = nil
|
||||
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("Removed the employee "..itemCID.." from the apartment.")
|
||||
end
|
||||
|
||||
self:UpdateApartment(appID)
|
||||
|
||||
if !isnumber(tonumber(item)) then return end
|
||||
end
|
||||
|
||||
-- A function to add a or remove a key to an apartment
|
||||
function PLUGIN:AddKeyToApartment(client, item, apartment, bRemove, bNoNotify, bEmployee)
|
||||
local itemCID = (!isnumber(tonumber(item)) and item:GetData("cid") or item)
|
||||
local itemID = (!isnumber(tonumber(item)) and item:GetID() or true)
|
||||
|
||||
if !self.apartments[apartment] then return end
|
||||
if !itemCID then return end
|
||||
|
||||
if bRemove and !bEmployee then
|
||||
self:RemoveTenant(client, item, apartment)
|
||||
return
|
||||
elseif bRemove and bEmployee then
|
||||
self:RemoveEmployee(client, item, apartment)
|
||||
return
|
||||
end
|
||||
|
||||
local assignedToShop = false
|
||||
local assignedToApp = false
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if tApartment.tenants[itemCID] or tApartment.employees[itemCID] then
|
||||
if tApartment.type == "shop" then
|
||||
assignedToShop = appID
|
||||
else
|
||||
assignedToApp = appID
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (self.apartments[apartment].type == "shop" and assignedToShop) then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("The tenant "..itemCID.." is already assigned to the shop "..self.apartments[assignedToShop].name)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
if (self.apartments[apartment].type != "shop" and assignedToApp) then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("The tenant "..itemCID.." is already assigned to the apartment "..self.apartments[assignedToApp].name)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
if !bEmployee then
|
||||
self.apartments[apartment].tenants[itemCID] = {key = itemID, autoPay = true}
|
||||
self.apartments[apartment].lastActions[itemCID] = os.time()
|
||||
else
|
||||
self.apartments[apartment].employees[itemCID] = {key = itemID}
|
||||
self.apartments[apartment].lastActions[itemCID] = os.time()
|
||||
end
|
||||
|
||||
if !isnumber(tonumber(item)) and !bEmployee then
|
||||
item:SetData("apartment", apartment)
|
||||
end
|
||||
|
||||
if !bNoNotify and !bEmployee then
|
||||
client:NotifyLocalized("Added the tenant "..itemCID.." to the "..(self.apartments[apartment].type == "shop" and "shop " or "apartment ")..self.apartments[apartment].name)
|
||||
elseif !bNoNotify and bEmployee then
|
||||
client:NotifyLocalized("Added the employee "..itemCID.." to the "..(self.apartments[apartment].type == "shop" and "shop " or "apartment ")..self.apartments[apartment].name)
|
||||
end
|
||||
|
||||
self:UpdateApartment(apartment)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function PLUGIN:CheckIfCIDExistsLoaded(cid)
|
||||
local target = false
|
||||
|
||||
-- Check if CID exists
|
||||
local cidCheck = tonumber(cid) != nil and string.utf8len( tostring(cid) ) <= 5
|
||||
-- Check for cached characters
|
||||
for _, v in pairs(ix.char.loaded) do
|
||||
local targetCid = v.GetCid and v:GetCid()
|
||||
local name = v.GetName and v:GetName()
|
||||
if (cidCheck and targetCid) then
|
||||
if string.match( targetCid, cid ) then
|
||||
target = v
|
||||
break
|
||||
end
|
||||
elseif name and string.find(name, cid) then
|
||||
target = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
function PLUGIN:CheckIfCIDExistsDatabase(cid)
|
||||
local target = false
|
||||
cid = tostring(cid)
|
||||
|
||||
if (tonumber(cid) != nil and string.utf8len( cid ) <= 5) then
|
||||
local query = mysql:Select("ix_characters")
|
||||
query:Select("name")
|
||||
query:Where("cid", cid)
|
||||
query:Where("schema", Schema and Schema.folder or "helix")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
target = result
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
return target
|
||||
end
|
||||
|
||||
-- A function to set the CID of a key
|
||||
function PLUGIN:SetKeyCID(client, item, cid)
|
||||
if !cid then
|
||||
if item:GetData("apartment") then
|
||||
client:NotifyLocalized("You need to remove the assigned apartment first.")
|
||||
return false
|
||||
end
|
||||
|
||||
client:NotifyLocalized("CID removed from key.")
|
||||
item:SetData("cid", nil)
|
||||
return
|
||||
end
|
||||
|
||||
if !self:CheckIfCIDExistsLoaded(cid) then
|
||||
if !self:CheckIfCIDExistsDatabase(cid) then
|
||||
client:NotifyLocalized("This CID doesn't exist!")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
item:SetData("cid", cid)
|
||||
client:NotifyLocalized("Assigned the CID: "..cid.." to the key.")
|
||||
end
|
||||
|
||||
-- A function for the key item to unlock/lock an apartment
|
||||
function PLUGIN:UnlockLockApartment(keyItem, bUnlock, entDoor)
|
||||
local appID = keyItem:GetData("apartment", false)
|
||||
local client = keyItem:GetOwner()
|
||||
if !appID then return false end
|
||||
|
||||
if self:GetKeyHasAccess(keyItem, entDoor) then
|
||||
if (IsValid(client) and client:GetPos():Distance(entDoor:GetPos()) > 96) then
|
||||
client:NotifyLocalized("You are not close enough to the door!")
|
||||
return
|
||||
end
|
||||
|
||||
if (entDoor:IsDoor()) then
|
||||
local partner = entDoor:GetDoorPartner()
|
||||
if entDoor.locked then return end
|
||||
|
||||
if (!bUnlock) then
|
||||
if (IsValid(partner)) then
|
||||
partner:Fire("lock")
|
||||
end
|
||||
|
||||
entDoor:Fire("lock")
|
||||
client:EmitSound("doors/door_latch3.wav")
|
||||
|
||||
hook.Run("PlayerLockedDoor", client, entDoor, partner)
|
||||
else
|
||||
if (IsValid(partner)) then
|
||||
partner:Fire("unlock")
|
||||
end
|
||||
|
||||
entDoor:Fire("unlock")
|
||||
client:EmitSound("doors/door_latch1.wav")
|
||||
|
||||
hook.Run("PlayerUnlockedDoor", client, entDoor, partner)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:GetPriorityApartmentAccess(genericData)
|
||||
local loyaltyStatus = genericData.loyaltyStatus
|
||||
local tier = tonumber(self:GetNumbersFromText(loyaltyStatus))
|
||||
local tierNeeded = tonumber(self:GetNumbersFromText(ix.config.Get("priorityHousingTierNeeded", "TIER 4 (BLUE)")))
|
||||
|
||||
if genericData.loyaltyStatus == "CCA MEMBER" then
|
||||
return true
|
||||
end
|
||||
|
||||
if (!tier or !isnumber(tier)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (isnumber(tier) and tier < tierNeeded) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- A function to assign an apartment
|
||||
function PLUGIN.AssignApartment(idCard, genericData, client, foundApartment)
|
||||
if idCard:GetCredits() < ix.config.Get("costForAnApartment", 35) then
|
||||
netstream.Start(client, "SendHousingErrorMessage", "NOT ENOUGH CREDITS")
|
||||
return
|
||||
end
|
||||
|
||||
if !foundApartment then
|
||||
netstream.Start(client, "SendHousingErrorMessage", "NO HOUSING FOUND")
|
||||
return
|
||||
end
|
||||
|
||||
if foundApartment == "priority" then
|
||||
if PLUGIN:GetPriorityApartmentAccess(genericData) then
|
||||
foundApartment = PLUGIN:GetWhichApartmentToAssign(foundApartment)
|
||||
if !foundApartment then
|
||||
netstream.Start(client, "SendHousingErrorMessage", "NO HOUSING FOUND")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cid = idCard:GetData("cid", false)
|
||||
if !cid then return false end
|
||||
|
||||
for appID, v in pairs(PLUGIN.apartments) do
|
||||
if v.type == "shop" then continue end
|
||||
if v.tenants[cid] then
|
||||
PLUGIN:RemoveTenant(client, cid, appID, true)
|
||||
end
|
||||
end
|
||||
|
||||
genericData.housing = foundApartment
|
||||
local combineutilities = ix.plugin.list["combineutilities"]
|
||||
if combineutilities and combineutilities.UpdateGenericData then
|
||||
combineutilities:UpdateGenericData(genericData)
|
||||
end
|
||||
|
||||
local character = ix.char.loaded[genericData.id]
|
||||
if character then
|
||||
if !character:GetPurchasedItems()["apartmentkey"] then
|
||||
character:SetPurchasedItems("apartmentkey", 1)
|
||||
else
|
||||
client:NotifyLocalized("There is already a key waiting at the pickup dispenser for this cid. It has been updated.")
|
||||
end
|
||||
end
|
||||
|
||||
idCard:TakeCredits(ix.config.Get("costForAnApartment", 35), "Housing", "Apartment bought")
|
||||
ix.city.main:AddCredits(ix.config.Get("costForAnApartment", 35))
|
||||
PLUGIN:AddKeyToApartment(client, cid, foundApartment, false, true)
|
||||
PLUGIN:SyncToOneApartments(client, false, foundApartment, genericData)
|
||||
end
|
||||
|
||||
-- A function to update the assigned itemID of a CID tenant
|
||||
function PLUGIN:UpdateItemIDTenant(cid, itemID, type)
|
||||
for appID, v in pairs(self.apartments) do
|
||||
if type:find("shop") and v.type != "shop" then continue end
|
||||
if type:find("apartment") and v.type == "shop" then continue end
|
||||
if !v.tenants[cid] then continue end
|
||||
if v.tenants[cid].key == itemID then break end
|
||||
|
||||
self.apartments[appID].tenants[cid].key = itemID
|
||||
self:UpdateApartment(appID)
|
||||
break
|
||||
end
|
||||
end
|
||||
function PLUGIN:UpdateItemIDEmployee(cid, itemID, type)
|
||||
for appID, v in pairs(self.apartments) do
|
||||
if type:find("shop") and v.type != "shop" then continue end
|
||||
if type:find("apartment") and v.type == "shop" then continue end
|
||||
if !v.employees[cid] then continue end
|
||||
if v.employees[cid].key == itemID then break end
|
||||
self.apartments[appID].employees[cid].key = itemID
|
||||
self:UpdateApartment(appID)
|
||||
break
|
||||
end
|
||||
end
|
||||
-- A function to handle the apartment assignment request
|
||||
function PLUGIN:HandleAssignment(client, cid, type)
|
||||
cid = self:HasCIDInInventory(client, cid)
|
||||
if !cid then return false end
|
||||
|
||||
local foundApartment = type
|
||||
if type != "priority" then
|
||||
foundApartment = self:GetWhichApartmentToAssign(type)
|
||||
end
|
||||
|
||||
cid:LoadOwnerGenericData(self.AssignApartment, false, client, foundApartment)
|
||||
end
|
||||
|
||||
function PLUGIN:CheckTenantInteraction()
|
||||
local interactionWeeks = ix.config.Get("tenantDoorInteractionCheck", 2)
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
for cid, time in pairs(tApartment.lastActions) do
|
||||
if (os.time() < (tonumber(time) + 3600 * 24 * 7 * interactionWeeks)) then continue end
|
||||
self:RemoveTenant(false, cid, appID, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:LookUpCardItemIDByCID(cid, callback)
|
||||
local query = mysql:Select("ix_characters")
|
||||
query:Select("idcard")
|
||||
query:WhereIn("cid", cid)
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
if (callback) then
|
||||
callback(result)
|
||||
end
|
||||
end)
|
||||
|
||||
query:Execute()
|
||||
end
|
||||
|
||||
function PLUGIN:PayRentOnCheck(idCard, individualRent, cid, appID)
|
||||
local credits = idCard and idCard.GetCredits and idCard:GetCredits()
|
||||
local madePayment = false
|
||||
|
||||
if (!idCard or (idCard and !credits)) then
|
||||
return false
|
||||
end
|
||||
|
||||
if (idCard and credits) and (credits >= individualRent) then
|
||||
idCard:TakeCredits(individualRent, "Housing", "Rent")
|
||||
ix.city.main:AddCredits(individualRent)
|
||||
if self.apartments[appID].payments[cid] then
|
||||
self.apartments[appID].payments[cid] = {amount = individualRent + self.apartments[appID].payments[cid].amount, date = os.time()}
|
||||
else
|
||||
self.apartments[appID].payments[cid] = {amount = individualRent, date = os.time()}
|
||||
end
|
||||
|
||||
madePayment = cid
|
||||
else
|
||||
self:RemoveTenant(false, cid, appID, true)
|
||||
end
|
||||
|
||||
return madePayment
|
||||
end
|
||||
|
||||
function PLUGIN:CheckRentPayments()
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if os.time() < tonumber(tApartment.rentDue) then continue end
|
||||
local remainingRent = self:GetRemainingRent(tApartment)
|
||||
if !remainingRent then return end
|
||||
if remainingRent <= 0 then self:ResetRent(appID) continue end
|
||||
|
||||
local tenantCount = table.Count(tApartment.tenants)
|
||||
local individualRent = math.ceil( tApartment.rent / tenantCount )
|
||||
|
||||
for tenantCID, _ in pairs(tApartment.tenants) do
|
||||
local madePayment = false
|
||||
if tApartment.tenants[tenantCID].autoPay then
|
||||
self:LookUpCardItemIDByCID(tenantCID, function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !idCardID then
|
||||
ix.log.AddRaw("[HOUSING] "..tenantCID.." was removed from apartment "..appID..". Reason: false/nil idCardID.")
|
||||
self:RemoveTenant(false, tenantCID, appID, true)
|
||||
return
|
||||
end
|
||||
|
||||
if !ix.item.instances[tonumber(idCardID)] then
|
||||
ix.item.LoadItemByID(tonumber(idCardID), false, function(item)
|
||||
if !item then
|
||||
ix.log.AddRaw("[HOUSING] "..tenantCID.." ID Card Item was not found upon processing offline rent payment for housing ID: "..appID..".")
|
||||
return
|
||||
end
|
||||
madePayment = self:PayRentOnCheck(ix.item.instances[tonumber(idCardID)], individualRent, tenantCID, appID)
|
||||
end)
|
||||
else
|
||||
madePayment = self:PayRentOnCheck(ix.item.instances[tonumber(idCardID)], individualRent, tenantCID, appID)
|
||||
end
|
||||
|
||||
timer.Simple(10, function()
|
||||
if !madePayment then
|
||||
ix.log.AddRaw("[HOUSING] "..tenantCID.." did not make an automatic payment, checking non-auto-payments before removing from housing ID: "..appID..".")
|
||||
self:CheckNoAutoPayments(tApartment, appID, tenantCID)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
timer.Simple(6, function()
|
||||
self:ResetRent(appID)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN.RemovePermitsGeneric(idCard, genericData)
|
||||
genericData.permits = {}
|
||||
|
||||
local combineutilities = ix.plugin.list["combineutilities"]
|
||||
if combineutilities and combineutilities.UpdateGenericData then
|
||||
combineutilities:UpdateGenericData(genericData)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:RemovePermits(tenantCID)
|
||||
tenantCID = Schema:ZeroNumber(tenantCID, 5)
|
||||
|
||||
PLUGIN:LookUpCardItemIDByCID(tostring(tenantCID), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
item:LoadOwnerGenericData(PLUGIN.RemovePermitsGeneric, false)
|
||||
end)
|
||||
else
|
||||
ix.item.instances[idCardID]:LoadOwnerGenericData(PLUGIN.RemovePermitsGeneric, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function PLUGIN:CheckNoAutoPayments(tApartment, appID, tenantCID)
|
||||
local tenantCount = table.Count(tApartment.tenants)
|
||||
local individualRent = math.ceil( tApartment.rent / tenantCount )
|
||||
|
||||
if !tApartment.payments[tenantCID] then
|
||||
ix.log.AddRaw("[HOUSING] "..tenantCID.." did not make any manual payments at all, removing from housing ID: "..appID..".. dev check: "..tonumber(isnumber(tenantCID)))
|
||||
self:RemoveTenant(false, tenantCID, appID, true)
|
||||
|
||||
if tApartment.type == "shop" then
|
||||
self:RemovePermits(tenantCID)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
for paymentCID, tPayments in pairs(tApartment.payments) do
|
||||
if tPayments.paidAmount and individualRent then
|
||||
if tPayments.paidAmount >= individualRent then continue end
|
||||
end
|
||||
|
||||
if !tApartment.tenants[paymentCID] then continue end
|
||||
|
||||
if tApartment.type == "shop" then
|
||||
self:RemovePermits(paymentCID)
|
||||
end
|
||||
|
||||
ix.log.AddRaw("[HOUSING] "..paymentCID.." did not manually pay above the individual rent, removing from housing ID: "..appID..".. dev check: "..isnumber(paymentCID))
|
||||
self:RemoveTenant(false, paymentCID, appID, true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:ResetRent(appID)
|
||||
self.apartments[appID].payments = {}
|
||||
self.apartments[appID].rentDue = self.apartments[appID].rentDue + (!ix.config.Get("housingTesterMode", false) and 3600 * 24 * 7 * ix.config.Get("housingRentDueInWeeks", 1) or 10)
|
||||
self:UpdateApartment(appID)
|
||||
end
|
||||
|
||||
function PLUGIN:PreCharacterDeleted(client, character)
|
||||
local cid = character:GetCid()
|
||||
if !cid then return end
|
||||
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if !tApartment.tenants[cid] then continue end
|
||||
|
||||
self:RemoveTenant(client, cid, appID, true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterBanned(character)
|
||||
local cid = character:GetCid()
|
||||
if !cid then return end
|
||||
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if !tApartment.tenants[cid] then continue end
|
||||
|
||||
self:RemoveTenant(false, cid, appID, true)
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:OnCharacterBannedByID(charID)
|
||||
if (!ix.plugin.list.cid) then return end
|
||||
if !ix.plugin.list.cid.GenerateCID then return end
|
||||
local cid = ix.plugin.list.cid:GenerateCID(charID)
|
||||
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if !tApartment.tenants[cid] then continue end
|
||||
|
||||
self:RemoveTenant(false, cid, appID, true)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
timer.Create("ixHousingCheckInteractionAndRent", ix.config.Get("housingCheckInteractionAndRentTimer", 20) * 60, 0, function()
|
||||
PLUGIN:CheckTenantInteraction()
|
||||
PLUGIN:CheckRentPayments()
|
||||
end)
|
||||
|
||||
function PLUGIN.UpdateInteractionTimer(oldVal, newVal)
|
||||
timer.Adjust("ixHousingCheckInteractionAndRent", newVal * 60)
|
||||
end
|
||||
end
|
||||
892
gamemodes/ixhl2rp/plugins/housing/sv_util.lua
Normal file
892
gamemodes/ixhl2rp/plugins/housing/sv_util.lua
Normal file
@@ -0,0 +1,892 @@
|
||||
--[[
|
||||
| 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/
|
||||
--]]
|
||||
|
||||
|
||||
local PLUGIN = PLUGIN
|
||||
|
||||
-- Util Functions:
|
||||
-- A function to sync apartment name and type with the client (for ESP)
|
||||
function PLUGIN:SyncApartments(client)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Observer Extra ESP", nil) then return end
|
||||
|
||||
local nameAndType = self:AppGetNameAndType()
|
||||
|
||||
for _, v in pairs(player.GetAll()) do
|
||||
if !CAMI.PlayerHasAccess(v, "Helix - Observer Extra ESP", nil) then continue end
|
||||
netstream.Start(v, "SyncApartments", nameAndType)
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to sync the apartment doors, name and type with the client (for ESP) on initial spawn
|
||||
function PLUGIN:SyncToOneApartments(client, appID, foundApartment, genericData, bDatafile)
|
||||
if (appID) then
|
||||
local nameTable = {}
|
||||
appID = tonumber(appID)
|
||||
if !self.apartments[appID] then return end
|
||||
|
||||
nameTable[appID] = {
|
||||
name = self.apartments[appID].name
|
||||
}
|
||||
|
||||
netstream.Start(client, "SyncApartments", nameTable, true, bDatafile)
|
||||
return
|
||||
end
|
||||
|
||||
if foundApartment and genericData then
|
||||
local names = self:GetAppNames()
|
||||
netstream.Start(client, "TerminalUpdateHousing", foundApartment, names, genericData)
|
||||
end
|
||||
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Observer Extra ESP", nil) then return end
|
||||
|
||||
local nameAndType = self:AppGetNameAndType()
|
||||
netstream.Start(client, "SyncApartments", nameAndType)
|
||||
end
|
||||
|
||||
function PLUGIN:SyncApartmentsToDatapad(client, curCollect)
|
||||
if !self:HasAccessToDatafile(client) then return end
|
||||
|
||||
local toSend = {}
|
||||
local curApartments = {}
|
||||
local curShops = {}
|
||||
|
||||
for appID, v in pairs(self.apartments) do
|
||||
v.appID = appID
|
||||
|
||||
if v.type == "normal" then
|
||||
curApartments[#curApartments + 1] = v
|
||||
continue
|
||||
end
|
||||
|
||||
curShops[#curShops + 1] = v
|
||||
end
|
||||
|
||||
for i = curCollect - 4, curCollect do
|
||||
if curApartments[i] then
|
||||
toSend[#toSend + 1] = curApartments[i]
|
||||
end
|
||||
|
||||
if curShops[i] then
|
||||
toSend[#toSend + 1] = curShops[i]
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Start(client, "SyncApartmentsToDatapad", toSend)
|
||||
end
|
||||
|
||||
-- A function to get doors, type and names from the apartments table
|
||||
function PLUGIN:AppGetNameAndType()
|
||||
local namesAndType = {}
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
namesAndType[appID] = {
|
||||
name = tApartment.name,
|
||||
doors = tApartment.doors,
|
||||
type = tApartment.type
|
||||
}
|
||||
end
|
||||
|
||||
return namesAndType
|
||||
end
|
||||
|
||||
-- A function to get names from the apartments table
|
||||
function PLUGIN:GetAppNames()
|
||||
local names = {}
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
names[appID] = {
|
||||
name = tApartment.name
|
||||
}
|
||||
end
|
||||
|
||||
return names
|
||||
end
|
||||
|
||||
-- A function to update an apartment in the database
|
||||
function PLUGIN:UpdateApartment(appID)
|
||||
local queryUpdate = mysql:Update("ix_apartments_"..game.GetMap())
|
||||
queryUpdate:Where("app_id", appID)
|
||||
queryUpdate:Update("app_name", self.apartments[appID].name)
|
||||
local doors = {}
|
||||
for _, v in pairs(self.apartments[appID].doors) do
|
||||
if (IsValid(Entity(v))) then
|
||||
local id = Entity(v):MapCreationID()
|
||||
if (id == -1) then continue end
|
||||
doors[#doors + 1] = id
|
||||
end
|
||||
end
|
||||
queryUpdate:Update("app_doors", util.TableToJSON(doors))
|
||||
queryUpdate:Update("app_employees", util.TableToJSON(self.apartments[appID].employees))
|
||||
queryUpdate:Update("app_permits", util.TableToJSON(self.apartments[appID].permits))
|
||||
queryUpdate:Update("app_tenants", util.TableToJSON(self.apartments[appID].tenants))
|
||||
queryUpdate:Update("app_lastactions", util.TableToJSON(self.apartments[appID].lastActions))
|
||||
queryUpdate:Update("app_rent", self.apartments[appID].rent)
|
||||
queryUpdate:Update("app_payments", util.TableToJSON(self.apartments[appID].payments))
|
||||
queryUpdate:Update("app_type", self.apartments[appID].type)
|
||||
queryUpdate:Update("app_noassign", self.apartments[appID].noAssign)
|
||||
queryUpdate:Update("app_rentdue", self.apartments[appID].rentDue)
|
||||
queryUpdate:Execute()
|
||||
end
|
||||
|
||||
local function HandlePermitUpdateForEachChar(newPermit, permits, char, charID, isEmployee)
|
||||
if char then
|
||||
local genericData = char:GetGenericdata()
|
||||
local charPermits = ix.permits.get()
|
||||
if isEmployee then
|
||||
for k, _ in pairs(charPermits) do
|
||||
charPermits[k] = false
|
||||
end
|
||||
for k, v in pairs(genericData.permits) do
|
||||
charPermits[k] = v
|
||||
end
|
||||
for k, v in pairs(permits) do
|
||||
if charPermits[k] != v then return end
|
||||
end
|
||||
end
|
||||
|
||||
char:SetPermit(newPermit, !permits[newPermit])
|
||||
else
|
||||
local query = mysql:Select("ix_characters_data")
|
||||
query:Where("key", "genericdata")
|
||||
query:Where("id", tostring(charID))
|
||||
query:Select("data")
|
||||
query:Callback(function(result)
|
||||
if (!istable(result) or #result == 0) then
|
||||
return
|
||||
end
|
||||
local genericData = util.JSONToTable(result[1]["data"])
|
||||
|
||||
local charPermits = ix.permits.get()
|
||||
if isEmployee then
|
||||
for k, _ in pairs(charPermits) do
|
||||
charPermits[k] = false
|
||||
end
|
||||
for k, v in pairs(genericData.permits) do
|
||||
charPermits[k] = v
|
||||
end
|
||||
for k, v in pairs(permits) do
|
||||
if charPermits[k] != v then return end
|
||||
end
|
||||
end
|
||||
if !permits[newPermit] then
|
||||
genericData.permits[newPermit] = !permits[newPermit]
|
||||
else
|
||||
genericData.permits[newPermit] = nil
|
||||
end
|
||||
local addPermit = mysql:Update("ix_characters_data")
|
||||
addPermit:Where("id", tostring(charID))
|
||||
addPermit:Where("key", "genericdata")
|
||||
addPermit:Update("data", util.TableToJSON(genericData))
|
||||
addPermit:Execute()
|
||||
end)
|
||||
query:Execute()
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN:HandleShopPermitUpdate(appID, newPermit)
|
||||
local apartment = self.apartments[appID]
|
||||
if apartment.type != "shop" then return end
|
||||
local permits = {}
|
||||
for permit, bValue in pairs(apartment.permits) do
|
||||
permits[permit] = bValue
|
||||
end
|
||||
permits[newPermit] = !permits[newPermit]
|
||||
for cid, employee in pairs(apartment.employees) do
|
||||
local char
|
||||
local charID
|
||||
self:LookUpCardItemIDByCID(tostring(cid), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then return end
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
char = ix.char.loaded[item:GetData("owner")]
|
||||
charID = item:GetData("owner")
|
||||
|
||||
HandlePermitUpdateForEachChar(newPermit, permits, char, charID, true)
|
||||
end)
|
||||
else
|
||||
char = ix.char.loaded[ix.item.instances[idCardID]:GetData("owner")]
|
||||
charID = ix.item.instances[idCardID]:GetData("owner")
|
||||
|
||||
HandlePermitUpdateForEachChar(newPermit, permits, char, charID, true)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
for cid, tenant in pairs(apartment.tenants) do
|
||||
local char
|
||||
local charID
|
||||
self:LookUpCardItemIDByCID(tostring(cid), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then return end
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
char = ix.char.loaded[item:GetData("owner")]
|
||||
charID = item:GetData("owner")
|
||||
|
||||
HandlePermitUpdateForEachChar(newPermit, permits, char, charID, false)
|
||||
end)
|
||||
else
|
||||
char = ix.char.loaded[ix.item.instances[idCardID]:GetData("owner")]
|
||||
charID = ix.item.instances[idCardID]:GetData("owner")
|
||||
|
||||
HandlePermitUpdateForEachChar(newPermit, permits, char, charID, false)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to check if a door is already added to an apartment
|
||||
function PLUGIN:CheckIfDoorAlreadyAdded(client, entDoor, bNoNotify)
|
||||
for key, tInfo in pairs(self.apartments) do
|
||||
for _, doorIndex in pairs(tInfo.doors) do
|
||||
if doorIndex == entDoor:EntIndex() then
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("This door is already added to an apartment! - "..tInfo.name)
|
||||
end
|
||||
|
||||
return key, tInfo.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- A function to check if an apartment exists before adding a door to it
|
||||
function PLUGIN:CheckIfApartmentExists(client, appName)
|
||||
for key, tInfo in pairs(self.apartments) do
|
||||
if string.lower(tInfo.name) == string.lower(appName) then
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- A function check what apartment a CID belongs to
|
||||
function PLUGIN:GetApartmentByCID(cid, type)
|
||||
for key, tInfo in pairs(self.apartments) do
|
||||
if (type and type:find("shop")) then
|
||||
if tInfo.type != "shop" then continue end
|
||||
end
|
||||
|
||||
if (type and type:find("apartment")) then
|
||||
if tInfo.type == "shop" then continue end
|
||||
end
|
||||
|
||||
if tInfo.tenants[cid] or tInfo.employees[cid] then
|
||||
return key
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- A function to find the apartment to assign
|
||||
function PLUGIN:GetWhichApartmentToAssign(appType)
|
||||
local appTenants = {}
|
||||
local maxTenants = ix.config.Get("housingMaxTenants")
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if tApartment.noAssign == true then continue end
|
||||
if table.IsEmpty(tApartment.doors) then continue end
|
||||
if table.Count(tApartment.tenants) >= maxTenants then continue end
|
||||
if tApartment.type != appType then continue end
|
||||
|
||||
appTenants[appID] = table.Count(tApartment.tenants)
|
||||
end
|
||||
|
||||
local fillOrder = {}
|
||||
for i = 0, maxTenants - 1 do
|
||||
fillOrder[#fillOrder + 1] = i
|
||||
end
|
||||
if (maxTenants >= 2) then
|
||||
if (ix.config.Get("housingFirstFull")) then
|
||||
fillOrder = table.Reverse(fillOrder)
|
||||
elseif (ix.config.Get("housingFirstDouble")) then
|
||||
fillOrder[1] = 1
|
||||
fillOrder[2] = 0
|
||||
end
|
||||
end
|
||||
|
||||
for _, i in ipairs(fillOrder) do
|
||||
for appID2, tenantCount in pairs(appTenants) do
|
||||
if tenantCount == i then
|
||||
return appID2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PLUGIN:InventoryItemAdded(oldInv, inventory, item)
|
||||
if (oldInv != nil) then return end
|
||||
|
||||
if (item.uniqueID == "apartmentkey" or item.uniqueID == "shopkey") then
|
||||
PLUGIN:UpdateItemIDTenant(item:GetData("cid"), item:GetID(), item.uniqueID)
|
||||
PLUGIN:UpdateItemIDEmployee(item:GetData("cid"), item:GetID(), item.uniqueID)
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to get what apartment the door is assigned to
|
||||
function PLUGIN:GetDoorApartment(client, entDoor)
|
||||
if !self:CheckIfDoorAlreadyAdded(client, entDoor) then
|
||||
client:NotifyLocalized("Could not find an apartment that this door is assigned to..")
|
||||
end
|
||||
end
|
||||
|
||||
-- A function to check if a key has access
|
||||
function PLUGIN:GetKeyHasAccess(item, entDoor, bNoNotify)
|
||||
local client = item:GetOwner()
|
||||
local _, appName = self:CheckIfDoorAlreadyAdded(client, entDoor, true)
|
||||
local itemCID = item:GetData("cid") or false
|
||||
local itemID = item:GetID()
|
||||
|
||||
for appID, tApartment in pairs(self.apartments) do
|
||||
if !tApartment.tenants[itemCID] and !tApartment.employees[itemCID] then continue end
|
||||
for _, doorIndex in pairs(tApartment.doors) do
|
||||
if doorIndex != entDoor:EntIndex() then continue end
|
||||
|
||||
if tApartment.tenants[itemCID] and tApartment.tenants[itemCID].key == itemID then return appID, itemCID
|
||||
elseif tApartment.employees[itemCID] and tApartment.employees[itemCID].key == itemID then return appID, itemCID end
|
||||
end
|
||||
end
|
||||
|
||||
if !bNoNotify then
|
||||
client:NotifyLocalized("You don't have access to this housing!")
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- A function get if a key already has access to another apartment
|
||||
function PLUGIN:GetKeyAlreadyHasOtherAccess(item)
|
||||
local itemCID = item:GetData("cid") or false
|
||||
|
||||
for key, tApartment in pairs(self.apartments) do
|
||||
local tenant = tApartment.tenants[itemCID]
|
||||
if (tenant and tenant != false) then return key end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function PLUGIN:HasCIDInInventory(client, cid)
|
||||
local bFound = false
|
||||
for _, v in pairs(client:GetCharacter():GetInventory():GetItems()) do
|
||||
if v.uniqueID != "id_card" then continue end
|
||||
if v:GetData("cid", false) and tonumber(v:GetData("cid", false)) == tonumber(cid) then
|
||||
bFound = v
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return bFound
|
||||
end
|
||||
|
||||
function PLUGIN:RequestAssignedApartmentInfo(client, cid, bShop)
|
||||
if !self:HasCIDInInventory(client, cid) then return end
|
||||
|
||||
local assignedApp
|
||||
if !bShop then assignedApp = self:GetApartmentByCID(cid, "apartment") end
|
||||
if bShop then assignedApp = self:GetApartmentByCID(cid, "shop") end
|
||||
if !assignedApp then
|
||||
netstream.Start(client, "ReplyApartmentInfo", false, false)
|
||||
return
|
||||
end
|
||||
|
||||
netstream.Start(client, "ReplyApartmentInfo", self.apartments[assignedApp], assignedApp, bShop)
|
||||
end
|
||||
|
||||
function PLUGIN:ToggleApartmentNoAssign(client, appID)
|
||||
if !appID then client:NotifyLocalized("Invalid apartment") return end
|
||||
|
||||
if self.apartments[appID].noAssign then
|
||||
self.apartments[appID].noAssign = false
|
||||
client:NotifyLocalized("Toggled this apartment from apartment assignment to false.")
|
||||
else
|
||||
self.apartments[appID].noAssign = true
|
||||
client:NotifyLocalized("Toggled this apartment from apartment assignment to true.")
|
||||
end
|
||||
|
||||
self:UpdateApartment(appID)
|
||||
end
|
||||
|
||||
function PLUGIN:AddDoorInteraction(client, appID, cid)
|
||||
self.apartments[appID].lastActions[cid] = os.time()
|
||||
self:UpdateApartment(appID)
|
||||
end
|
||||
|
||||
function PLUGIN:PayRent(client, amount, appID, cid)
|
||||
local cidItem = self:HasCIDInInventory(client, cid)
|
||||
if !cidItem then return false end
|
||||
|
||||
local cidData = cidItem:GetData("cid")
|
||||
if !cidData then return false end
|
||||
|
||||
if cidItem:GetCredits() < amount then
|
||||
netstream.Start(client, "SendHousingErrorMessage", "NOT ENOUGH CREDITS")
|
||||
return
|
||||
end
|
||||
|
||||
cidData = Schema:ZeroNumber(cidData, 5)
|
||||
|
||||
if self.apartments[appID].payments[cidData] then
|
||||
self.apartments[appID].payments[cidData] = {amount = amount + self.apartments[appID].payments[cidData].amount, date = os.time()}
|
||||
else
|
||||
self.apartments[appID].payments[cidData] = {amount = amount, date = os.time()}
|
||||
end
|
||||
|
||||
cidItem:TakeCredits(amount, "Housing", "Rent")
|
||||
ix.city.main:AddCredits(amount)
|
||||
|
||||
self:UpdateApartment(appID)
|
||||
|
||||
netstream.Start(client, "UpdateHousingPaymentPanel", self.apartments[appID], appID, amount)
|
||||
end
|
||||
|
||||
function PLUGIN:SetTenantAutoPayment(client, appID, cid, bEnabled)
|
||||
local cidItem = self:HasCIDInInventory(client, cid)
|
||||
if !cidItem then return false end
|
||||
|
||||
local cidData = cidItem:GetData("cid")
|
||||
if !cidData then return false end
|
||||
|
||||
cidData = Schema:ZeroNumber(cidData, 5)
|
||||
|
||||
self.apartments[appID].tenants[cidData].autoPay = bEnabled
|
||||
self:UpdateApartment(appID)
|
||||
end
|
||||
|
||||
function PLUGIN:HasAccessToDatafile(client)
|
||||
local combineutilities = ix.plugin.list["combineutilities"]
|
||||
if !combineutilities then return end
|
||||
if (!combineutilities:HasAccessToDatafile(client)) then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Hooks
|
||||
|
||||
netstream.Hook("SetTenantAutoPayment", function(client, appID, cid, bEnabled)
|
||||
PLUGIN:SetTenantAutoPayment(client, appID, cid, bEnabled)
|
||||
end)
|
||||
|
||||
hook.Add("CanPlayerAccessDoor", "CheckForKeyItem", function(client, entDoor)
|
||||
for _, item in ipairs(client:GetCharacter():GetInventory():GetItemsByUniqueID("apartmentkey")) do
|
||||
local appID, cid = PLUGIN:GetKeyHasAccess(item, entDoor, true)
|
||||
if (appID and cid) then
|
||||
PLUGIN:AddDoorInteraction(client, appID, cid)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
for _, item in ipairs(client:GetCharacter():GetInventory():GetItemsByUniqueID("shopkey")) do
|
||||
local appID, cid = PLUGIN:GetKeyHasAccess(item, entDoor, true)
|
||||
if (appID and cid) then
|
||||
PLUGIN:AddDoorInteraction(client, appID, cid)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
||||
|
||||
hook.Add("CAMI.PlayerUsergroupChanged", "SyncApartmentsToOne", function(client)
|
||||
PLUGIN:SyncToOneApartments(client)
|
||||
end)
|
||||
|
||||
netstream.Hook("RequestApartmentNames", function(client, appID)
|
||||
PLUGIN:SyncToOneApartments(client, appID)
|
||||
end)
|
||||
|
||||
function PLUGIN:RefreshApartmentsPanel(client)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Manage Apartments", nil) then return end
|
||||
|
||||
local nameAndType = PLUGIN:AppGetNameAndType()
|
||||
|
||||
for appID, tApartment in pairs(PLUGIN.apartments) do
|
||||
nameAndType[appID].rent = tApartment.rent
|
||||
end
|
||||
|
||||
netstream.Start(client, "SyncApartmentsApartmentsPanel", nameAndType)
|
||||
end
|
||||
|
||||
netstream.Hook("RequestApartmentNamesApartmentsPanel", function(client)
|
||||
PLUGIN:RefreshApartmentsPanel(client)
|
||||
end)
|
||||
|
||||
netstream.Hook("CreateApartmentApartmentsUI", function(client, type, name)
|
||||
PLUGIN:CreateApartment(client, name, false, type)
|
||||
PLUGIN:RefreshApartmentsPanel(client)
|
||||
end)
|
||||
|
||||
netstream.Hook("RequestApartmentNamesDatafile", function(client, appID)
|
||||
PLUGIN:SyncToOneApartments(client, appID, false, false, true)
|
||||
end)
|
||||
|
||||
netstream.Hook("ixApartmentsRequest", function(client, cid, type)
|
||||
PLUGIN:HandleAssignment(client, cid, type)
|
||||
end)
|
||||
|
||||
netstream.Hook("RequestAssignedApartmentInfo", function(client, cid, bShop)
|
||||
PLUGIN:RequestAssignedApartmentInfo(client, cid, bShop)
|
||||
end)
|
||||
|
||||
netstream.Hook("ixApartmentsAssignKeyToApartment", function(client, keyItemID, appID, bRemove)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Manage Apartments Key", nil) then return end
|
||||
if !ix.item.instances[keyItemID] then return end
|
||||
|
||||
if !bRemove then
|
||||
ix.item.instances[keyItemID]:AssignToApartment(appID)
|
||||
else
|
||||
ix.item.instances[keyItemID]:RemoveApartment()
|
||||
end
|
||||
end)
|
||||
|
||||
netstream.Hook("ixApartmentsAssignCIDToKey", function(client, keyItemID, cid)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Manage Apartments Key", nil) then return end
|
||||
if !ix.item.instances[keyItemID] then return end
|
||||
|
||||
ix.item.instances[keyItemID]:SetCID(cid)
|
||||
end)
|
||||
|
||||
netstream.Hook("PayRent", function(client, amount, appID, cid)
|
||||
PLUGIN:PayRent(client, amount, appID, cid)
|
||||
end)
|
||||
|
||||
netstream.Hook("RequestApartmentNamesDatapad", function(client, curCollect)
|
||||
PLUGIN:SyncApartmentsToDatapad(client, curCollect)
|
||||
end)
|
||||
|
||||
netstream.Hook("ChangeApartmentName", function(client, appID, newName)
|
||||
if !CAMI.PlayerHasAccess(client, "Helix - Manage Apartments", nil) then return end
|
||||
if !PLUGIN.apartments[appID] then return end
|
||||
PLUGIN.apartments[appID].name = newName
|
||||
PLUGIN:UpdateApartment(appID)
|
||||
|
||||
for _, doorEntID in pairs(PLUGIN.apartments[appID].doors) do
|
||||
local entDoor = Entity(doorEntID)
|
||||
if (IsValid(entDoor) and entDoor:IsDoor()) then
|
||||
entDoor:SetNetVar("visible", true)
|
||||
entDoor:SetNetVar("name", newName)
|
||||
|
||||
local doorPlugin = ix.plugin.list["doors"]
|
||||
if doorPlugin then
|
||||
doorPlugin:SaveDoorData()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Start(client, "UpdateApartmentList")
|
||||
end)
|
||||
|
||||
netstream.Hook("RemoveApartmentHousing", function(client, name)
|
||||
PLUGIN:RemoveApartment(client, name)
|
||||
netstream.Start(client, "UpdateApartmentList")
|
||||
end)
|
||||
|
||||
netstream.Hook("RemoveTenant", function(client, tenantCID, appID)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
if !PLUGIN.apartments[appID] then return end
|
||||
if !PLUGIN.apartments[appID].tenants[tenantCID] then
|
||||
for newAppID, tApartment in pairs(PLUGIN.apartments) do
|
||||
if !tApartment.tenants[tenantCID] then continue end
|
||||
|
||||
PLUGIN:RemoveTenant(client, tenantCID, newAppID, true)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
PLUGIN:RemoveTenant(client, tenantCID, appID, true)
|
||||
end)
|
||||
|
||||
netstream.Hook("RemoveEmployee", function(client, employeeCID, appID)
|
||||
if !PLUGIN:HasAccessToDatafile(client) and !PLUGIN.apartments[appID].tenants[client:GetCharacter():GetGenericdata().cid] then return end
|
||||
if !PLUGIN.apartments[appID] then return end
|
||||
if !PLUGIN.apartments[appID].employees[employeeCID] then
|
||||
for newAppID, tApartment in pairs(PLUGIN.apartments) do
|
||||
if !tApartment.employees[employeeCID] then continue end
|
||||
|
||||
PLUGIN:RemoveEmployee(client, employeeCID, newAppID, true)
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
PLUGIN:RemoveEmployee(client, employeeCID, appID, true)
|
||||
end)
|
||||
|
||||
function PLUGIN.RenewKey(idCard, genericData, client, appID, bEmployee)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
|
||||
if !idCard then return end
|
||||
local cid = idCard:GetData("cid")
|
||||
if !cid or istable(cid) then client:NotifyLocalized("CID IS TABLE") return end
|
||||
|
||||
if !PLUGIN.apartments[appID] then return end
|
||||
if !bEmployee then
|
||||
if !PLUGIN.apartments[appID].tenants[cid] then return end
|
||||
else
|
||||
if !PLUGIN.apartments[appID].employees[cid] then return end
|
||||
end
|
||||
local character = ix.char.loaded[genericData.id]
|
||||
if character then
|
||||
local appType = PLUGIN.apartments[appID].type
|
||||
if !character:GetPurchasedItems()[appType == "shop" and "shopkey" or "apartmentkey"] then
|
||||
character:SetPurchasedItems(appType == "shop" and "shopkey" or "apartmentkey", 1)
|
||||
else
|
||||
client:NotifyLocalized("There is already a key waiting at the pickup dispenser for this cid. It has been updated.")
|
||||
end
|
||||
if !bEmployee then
|
||||
PLUGIN.apartments[appID].tenants[cid].key = true
|
||||
else
|
||||
PLUGIN.apartments[appID].employees[cid].key = true
|
||||
end
|
||||
PLUGIN:UpdateApartment(appID)
|
||||
client:NotifyLocalized("Success. Their current key is disabled and they can now pickup a new key from the pickup dispenser.")
|
||||
netstream.Start(client, "UpdateIndividualApartment", appID, PLUGIN.apartments[appID])
|
||||
return
|
||||
end
|
||||
|
||||
client:NotifyLocalized("Fail. The character was not online, so no new key was assigned.")
|
||||
end
|
||||
|
||||
netstream.Hook("RenewKey", function(client, tenantCID, appID, bEmployee)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
if !PLUGIN.apartments[appID] then return end
|
||||
tenantCID = Schema:ZeroNumber(tenantCID, 5)
|
||||
|
||||
if !PLUGIN.apartments[appID].tenants[tenantCID] and !PLUGIN.apartments[appID].employees[tenantCID] then return end
|
||||
|
||||
PLUGIN:LookUpCardItemIDByCID(tostring(tenantCID), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then client:NotifyLocalized("This CID doesn't exist!") return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
item:LoadOwnerGenericData(PLUGIN.RenewKey, false, client, appID, bEmployee)
|
||||
end)
|
||||
else
|
||||
ix.item.instances[idCardID]:LoadOwnerGenericData(PLUGIN.RenewKey, false, client, appID, bEmployee)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
function PLUGIN.AddEmployeeManually(idCard, genericData, client, appID)
|
||||
local cid = idCard:GetData("cid")
|
||||
if !cid or istable(cid) then client:NotifyLocalized("CID IS TABLE") return end
|
||||
if PLUGIN.apartments[appID].type != "shop" then return client:NotifyLocalized("This is not a shop!") end
|
||||
cid = Schema:ZeroNumber(idCard:GetData("cid"), 5)
|
||||
for newAppID, tApartment in pairs(PLUGIN.apartments) do
|
||||
if tApartment.type != "shop" then continue end
|
||||
if tApartment.employees[cid] then return client:NotifyLocalized(tostring(cid) .. " is already being an employee in " .. PLUGIN.apartments[newAppID].name) end
|
||||
if tApartment.tenants[cid] then return client:NotifyLocalized(tostring(cid) .. " is already being a tenant in " .. PLUGIN.apartments[newAppID].name) end
|
||||
end
|
||||
local character = ix.char.loaded[genericData.id]
|
||||
if character then
|
||||
for k, v in pairs(PLUGIN.apartments[appID].permits) do
|
||||
character:SetPermit(k, v)
|
||||
end
|
||||
if PLUGIN:AddKeyToApartment(client, cid, appID, nil, nil, true) then
|
||||
local appType = PLUGIN.apartments[appID].type
|
||||
local item = (appType != "shop" and "apartmentkey" or "shopkey")
|
||||
if !character:GetPurchasedItems()[item] then
|
||||
character:SetPurchasedItems(item, 1)
|
||||
else
|
||||
client:NotifyLocalized("There is already a"..(appType != "shop" and "n apartment" or " shop").." key waiting at the pickup dispenser for this cid. It has been updated.")
|
||||
end
|
||||
netstream.Start(client, "UpdateIndividualApartment", appID, PLUGIN.apartments[appID])
|
||||
end
|
||||
netstream.Start(client, "AddEmployeeToList", PLUGIN.apartments[appID], cid, appID)
|
||||
else
|
||||
client:NotifyLocalized("Fail. The character was not online, so no employee was assigned.")
|
||||
end
|
||||
end
|
||||
|
||||
function PLUGIN.AddTenantManually(idCard, genericData, client, appID)
|
||||
local cid = idCard:GetData("cid")
|
||||
if !cid or istable(cid) then client:NotifyLocalized("CID IS TABLE") return end
|
||||
cid = Schema:ZeroNumber(idCard:GetData("cid"), 5)
|
||||
|
||||
if PLUGIN.apartments[appID].employees[cid] then return client:NotifyLocalized("You can't assign person who's already is an employee as a tenant.") end
|
||||
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
if table.IsEmpty(genericData.permits) then
|
||||
client:NotifyLocalized("This CID does not have a business license, but the CID was added anyway.")
|
||||
end
|
||||
end
|
||||
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
genericData.shop = appID
|
||||
else
|
||||
genericData.housing = appID
|
||||
end
|
||||
|
||||
local combineutilities = ix.plugin.list["combineutilities"]
|
||||
if combineutilities and combineutilities.UpdateGenericData then
|
||||
combineutilities:UpdateGenericData(genericData)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local character = ix.char.loaded[genericData.id]
|
||||
if character then
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
for k, v in pairs(PLUGIN.apartments[appID].permits) do
|
||||
character:SetPermit(k, v)
|
||||
end
|
||||
end
|
||||
if PLUGIN:AddKeyToApartment(client, cid, appID) then
|
||||
local appType = PLUGIN.apartments[appID].type
|
||||
local item = (appType != "shop" and "apartmentkey" or "shopkey")
|
||||
|
||||
if !character:GetPurchasedItems()[item] then
|
||||
character:SetPurchasedItems(item, 1)
|
||||
else
|
||||
client:NotifyLocalized("There is already a"..(appType != "shop" and "n apartment" or " shop").." key waiting at the pickup dispenser for this cid. It has been updated.")
|
||||
end
|
||||
|
||||
netstream.Start(client, "UpdateIndividualApartment", appID, PLUGIN.apartments[appID])
|
||||
end
|
||||
|
||||
if PLUGIN.apartments[appID].type == "shop" then
|
||||
for _, shopTerminal in pairs(ents.FindByClass("ix_shopterminal")) do
|
||||
shopTerminal:UpdateScreen()
|
||||
end
|
||||
end
|
||||
else
|
||||
client:NotifyLocalized("Fail. The character was not online, so no tenant was assigned.")
|
||||
end
|
||||
end
|
||||
|
||||
netstream.Hook("AddTenant", function(client, appID, cid)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
if !appID or !cid then return end
|
||||
|
||||
cid = Schema:ZeroNumber(cid, 5)
|
||||
|
||||
if !PLUGIN:CheckIfCIDExistsLoaded(cid) then
|
||||
if !PLUGIN:CheckIfCIDExistsDatabase(cid) then
|
||||
client:NotifyLocalized("This CID doesn't exist!")
|
||||
return
|
||||
end
|
||||
|
||||
client:NotifyLocalized("This CID doesn't exist!")
|
||||
return
|
||||
end
|
||||
|
||||
PLUGIN:LookUpCardItemIDByCID(tostring(cid), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then client:NotifyLocalized("This CID doesn't exist!") return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
item:LoadOwnerGenericData(PLUGIN.AddTenantManually, false, client, appID)
|
||||
end)
|
||||
else
|
||||
ix.item.instances[idCardID]:LoadOwnerGenericData(PLUGIN.AddTenantManually, false, client, appID)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
netstream.Hook("AddEmployee", function(client, appID, cid)
|
||||
if !PLUGIN:HasAccessToDatafile(client) and !PLUGIN.apartments[appID].tenants[client:GetCharacter():GetGenericdata().cid] then return end
|
||||
if !appID or !cid then return end
|
||||
if PLUGIN.apartments[appID].type != "shop" then return client:NotifyLocalized("This is not a shop!") end
|
||||
|
||||
|
||||
cid = Schema:ZeroNumber(cid, 5)
|
||||
|
||||
if !PLUGIN:CheckIfCIDExistsLoaded(cid) then
|
||||
if !PLUGIN:CheckIfCIDExistsDatabase(cid) then
|
||||
client:NotifyLocalized("This CID doesn't exist!")
|
||||
return
|
||||
end
|
||||
|
||||
client:NotifyLocalized("This CID doesn't exist!")
|
||||
return
|
||||
end
|
||||
|
||||
PLUGIN:LookUpCardItemIDByCID(tostring(cid), function(result)
|
||||
local idCardID = result[1].idcard or false
|
||||
if !result then client:NotifyLocalized("This CID doesn't exist!") return end
|
||||
|
||||
if !ix.item.instances[idCardID] then
|
||||
ix.item.LoadItemByID(idCardID, false, function(item)
|
||||
if !item then return end
|
||||
item:LoadOwnerGenericData(PLUGIN.AddEmployeeManually, false, client, appID)
|
||||
end)
|
||||
else
|
||||
ix.item.instances[idCardID]:LoadOwnerGenericData(PLUGIN.AddEmployeeManually, false, client, appID)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
netstream.Hook("ApartmentUpdateRent", function(client, appID, newRent)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
|
||||
PLUGIN:SetApartmentRent(client, appID, false, newRent)
|
||||
netstream.Start(client, "UpdateIndividualApartment", appID, PLUGIN.apartments[appID])
|
||||
end)
|
||||
|
||||
netstream.Hook("ApartmentExtendRentDueDateByDays", function(client, appID, days)
|
||||
if !PLUGIN:HasAccessToDatafile(client) then return end
|
||||
PLUGIN.apartments[appID].rentDue = PLUGIN.apartments[appID].rentDue + 3600 * 24 * tonumber(days)
|
||||
PLUGIN:UpdateApartment(appID)
|
||||
|
||||
netstream.Start(client, "UpdateIndividualApartment", appID, PLUGIN.apartments[appID])
|
||||
end)
|
||||
|
||||
netstream.Hook("BuyShopViaTerminal", function(client, ent, permits)
|
||||
if !IsValid(ent) then return end
|
||||
|
||||
if ent:IsPurchasable() then
|
||||
local shopID = ent:GetShopID()
|
||||
local character = client:GetCharacter()
|
||||
local idCard = character:GetInventory():HasItem("id_card")
|
||||
|
||||
if (client:EyePos():DistToSqr(ent:GetPos()) > 62500) then return end
|
||||
if !shopID then return client:NotifyLocalized("Can't find any shop with this ID.") end
|
||||
if !idCard then return client:NotifyLocalized("You don't have CID in your inventory!") end
|
||||
if !permits or #table.GetKeys(permits) > 3 then return end
|
||||
if character:GetGenericdata().socialCredits < ent:GetShopSocialCreditReq() then
|
||||
return client:NotifyLocalized("You don't have enough social credits!")
|
||||
end
|
||||
|
||||
if !idCard:HasCredits(ent:GetShopCost()) then
|
||||
return client:NotifyLocalized("You don't have enough credits!")
|
||||
end
|
||||
|
||||
idCard:TakeCredits(ent:GetShopCost(), "Shop Terminal", "Shop bought.")
|
||||
local idCardNumber = idCard:GetData("cid", "00000")
|
||||
|
||||
for i = 1, #PLUGIN.apartments do
|
||||
local apartment = PLUGIN.apartments[i]
|
||||
if apartment.type == "shop" and apartment.tenants[idCardNumber] then
|
||||
return client:NotifyLocalized("You already own a shop!")
|
||||
end
|
||||
end
|
||||
|
||||
for k, _ in pairs(PLUGIN.apartments[shopID].permits) do
|
||||
if permits[k] then
|
||||
PLUGIN.apartments[shopID].permits[k] = true
|
||||
else
|
||||
PLUGIN.apartments[shopID].permits[k] = false
|
||||
end
|
||||
end
|
||||
|
||||
idCard:LoadOwnerGenericData(PLUGIN.AddTenantManually, false, client, shopID)
|
||||
end
|
||||
end)
|
||||
Reference in New Issue
Block a user