This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

View 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