567 lines
22 KiB
Lua
567 lines
22 KiB
Lua
local propertyLocks = {}
|
|
local characterPurchaseLocks = {}
|
|
local playerLocation = {}
|
|
local temporaryInvites = {}
|
|
local directInvites = {}
|
|
local pendingDoorbells = {}
|
|
local configuredPropertyKeys = {}
|
|
|
|
for _, building in ipairs(DevXHousing.Buildings) do
|
|
for _, unit in ipairs(building.units) do
|
|
configuredPropertyKeys[unit.key] = true
|
|
end
|
|
end
|
|
|
|
local function findBuilding(key)
|
|
for _, building in ipairs(DevXHousing.Buildings) do
|
|
if building.key == key then return building end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function findUnit(key)
|
|
for _, building in ipairs(DevXHousing.Buildings) do
|
|
for _, unit in ipairs(building.units) do
|
|
if unit.key == key then return unit, building end
|
|
end
|
|
end
|
|
return nil, nil
|
|
end
|
|
|
|
local function unitRow(propertyKey)
|
|
return MySQL.single.await([[
|
|
SELECT p.id, p.property_key AS propertyKey, p.price,
|
|
po.character_id AS ownerCharacterId,
|
|
CONCAT(c.first_name, ' ', c.last_name) AS ownerName
|
|
FROM devx_properties p
|
|
LEFT JOIN devx_property_owners po ON po.property_id = p.id
|
|
LEFT JOIN devx_characters c ON c.id = po.character_id AND c.deleted_at IS NULL
|
|
WHERE p.property_key = ?
|
|
LIMIT 1
|
|
]], { propertyKey })
|
|
end
|
|
|
|
local function ownedUnitRow(characterId)
|
|
return MySQL.single.await([[
|
|
SELECT p.id, p.property_key AS propertyKey, p.label, p.price,
|
|
po.character_id AS ownerCharacterId,
|
|
CONCAT(c.first_name, ' ', c.last_name) AS ownerName
|
|
FROM devx_property_owners po
|
|
INNER JOIN devx_properties p ON p.id = po.property_id
|
|
LEFT JOIN devx_characters c ON c.id = po.character_id AND c.deleted_at IS NULL
|
|
WHERE po.character_id = ? AND p.property_type = 'apartment'
|
|
ORDER BY po.purchased_at ASC
|
|
LIMIT 1
|
|
]], { characterId })
|
|
end
|
|
|
|
local function floorBucket(building, floor)
|
|
return DevXHousing.FloorBucketBase + (building.index * 100) + tonumber(floor or 0)
|
|
end
|
|
|
|
local function unitBucket(propertyId)
|
|
return DevXHousing.UnitBucketBase + tonumber(propertyId)
|
|
end
|
|
|
|
local function setLocation(source, location, bucket)
|
|
playerLocation[source] = location
|
|
SetPlayerRoutingBucket(source, bucket or 0)
|
|
if bucket and bucket > 0 then
|
|
SetRoutingBucketPopulationEnabled(bucket, false)
|
|
SetRoutingBucketEntityLockdownMode(bucket, 'relaxed')
|
|
end
|
|
end
|
|
|
|
local function resetPlayer(source)
|
|
playerLocation[source] = nil
|
|
temporaryInvites[source] = nil
|
|
directInvites[source] = nil
|
|
pendingDoorbells[source] = nil
|
|
SetPlayerRoutingBucket(source, 0)
|
|
end
|
|
|
|
local function syncProperties()
|
|
for _, building in ipairs(DevXHousing.Buildings) do
|
|
for _, unit in ipairs(building.units) do
|
|
local interior = DevXHousing.Interiors[unit.style]
|
|
MySQL.query.await([[
|
|
INSERT INTO devx_properties
|
|
(property_key, label, price, property_type, entry_data, interior_data)
|
|
VALUES (?, ?, ?, 'apartment', ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
label = VALUES(label),
|
|
price = VALUES(price),
|
|
entry_data = VALUES(entry_data),
|
|
interior_data = VALUES(interior_data)
|
|
]], {
|
|
unit.key,
|
|
unit.label,
|
|
unit.price,
|
|
json.encode({
|
|
buildingKey = building.key,
|
|
floor = unit.floor,
|
|
unitNumber = unit.unitNumber,
|
|
exterior = building.exterior
|
|
}),
|
|
json.encode(interior)
|
|
})
|
|
end
|
|
end
|
|
-- Soft-deleted or otherwise missing characters must not keep a door sign.
|
|
-- This also repairs ownership rows left behind by older test versions.
|
|
MySQL.query.await([[
|
|
DELETE po FROM devx_property_owners po
|
|
LEFT JOIN devx_characters c ON c.id = po.character_id
|
|
WHERE c.id IS NULL OR c.deleted_at IS NOT NULL
|
|
]])
|
|
|
|
print('[devx_housing] Apartmentdaten v0.4.3 synchronisiert; verwaiste Eigentümer bereinigt.')
|
|
end
|
|
|
|
MySQL.ready(syncProperties)
|
|
|
|
DevXRegisterCallback('housing:list', function(source)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
|
|
|
|
local rows = MySQL.query.await([[
|
|
SELECT p.id, p.property_key AS propertyKey, p.label, p.price,
|
|
po.character_id AS ownerCharacterId,
|
|
CONCAT(c.first_name, ' ', c.last_name) AS ownerName
|
|
FROM devx_properties p
|
|
LEFT JOIN devx_property_owners po ON po.property_id = p.id
|
|
LEFT JOIN devx_characters c ON c.id = po.character_id AND c.deleted_at IS NULL
|
|
WHERE p.property_type = 'apartment'
|
|
ORDER BY p.id
|
|
]]) or {}
|
|
|
|
local filtered = {}
|
|
for _, row in ipairs(rows) do
|
|
if configuredPropertyKeys[row.propertyKey] then
|
|
-- An ownership row without a valid active character is never exposed
|
|
-- to the hallway UI as an occupied apartment.
|
|
if row.ownerCharacterId and (not row.ownerName or row.ownerName == '') then
|
|
MySQL.query.await('DELETE FROM devx_property_owners WHERE property_id = ?', { row.id })
|
|
row.ownerCharacterId = nil
|
|
row.ownerName = nil
|
|
end
|
|
filtered[#filtered + 1] = row
|
|
end
|
|
end
|
|
|
|
local owned = ownedUnitRow(player.characterId)
|
|
if owned and not configuredPropertyKeys[owned.propertyKey] then owned = nil end
|
|
return {
|
|
ok = true,
|
|
properties = filtered,
|
|
characterId = player.characterId,
|
|
ownedPropertyKey = owned and owned.propertyKey or nil,
|
|
ownedPropertyLabel = owned and owned.label or nil
|
|
}
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:buy', function(source, payload)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
local unit = findUnit(payload.propertyKey)
|
|
if not player or not unit then return { ok = false, error = 'Ungültige Wohnung.' } end
|
|
|
|
local existing = ownedUnitRow(player.characterId)
|
|
if existing then
|
|
return {
|
|
ok = false,
|
|
error = ('Du besitzt bereits %s. Pro Charakter ist derzeit nur ein Apartment erlaubt.'):format(existing.label)
|
|
}
|
|
end
|
|
|
|
if characterPurchaseLocks[player.characterId] then
|
|
return { ok = false, error = 'Ein anderer Wohnungskauf wird bereits verarbeitet.' }
|
|
end
|
|
if propertyLocks[unit.key] then return { ok = false, error = 'Die Wohnung wird gerade bearbeitet.' } end
|
|
characterPurchaseLocks[player.characterId] = true
|
|
propertyLocks[unit.key] = true
|
|
|
|
local ok, response = pcall(function()
|
|
local row = unitRow(unit.key)
|
|
if not row then return { ok = false, error = 'Wohnung fehlt in der Datenbank.' } end
|
|
if row.ownerCharacterId then return { ok = false, error = 'Die Wohnung ist bereits verkauft.' } end
|
|
|
|
local charged, chargeResult = exports.devx_banking:ChargeCharacter(
|
|
player.characterId,
|
|
tonumber(row.price),
|
|
('Kauf: %s'):format(unit.label),
|
|
'Immobilienverwaltung DevX-City'
|
|
)
|
|
if not charged then return { ok = false, error = chargeResult } end
|
|
|
|
local ownerId = MySQL.insert.await([[
|
|
INSERT INTO devx_property_owners (property_id, character_id, purchase_price)
|
|
VALUES (?, ?, ?)
|
|
]], { row.id, player.characterId, row.price })
|
|
|
|
if not ownerId then
|
|
exports.devx_banking:CreditCharacter(
|
|
player.characterId,
|
|
tonumber(row.price),
|
|
('Rückbuchung: %s'):format(unit.label),
|
|
'Immobilienverwaltung DevX-City'
|
|
)
|
|
return { ok = false, error = 'Kauf wurde abgebrochen und zurückgebucht.' }
|
|
end
|
|
|
|
local overview = MySQL.single.await('SELECT balance FROM devx_bank_accounts WHERE id = ?', { player.accountId })
|
|
if overview then exports.devx_core:SetBankCache(source, tonumber(overview.balance)) end
|
|
return { ok = true, propertyKey = unit.key }
|
|
end)
|
|
|
|
propertyLocks[unit.key] = nil
|
|
characterPurchaseLocks[player.characterId] = nil
|
|
if not ok then
|
|
print(('[devx_housing] Kauf fehlgeschlagen: %s'):format(response))
|
|
return { ok = false, error = 'Interner Immobilienfehler.' }
|
|
end
|
|
return response
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:previewStart', function(source, payload)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
local building = findBuilding(payload.buildingKey)
|
|
if not player or not building then return { ok = false, error = 'Ungültiges Gebäude.' } end
|
|
local owned = ownedUnitRow(player.characterId)
|
|
if owned then
|
|
return { ok = false, error = ('Du besitzt bereits %s.'):format(owned.label or 'ein Apartment') }
|
|
end
|
|
|
|
local bucket = DevXHousing.PreviewBucketBase + tonumber(source)
|
|
setLocation(source, { type = 'preview', buildingKey = building.key }, bucket)
|
|
return { ok = true }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:previewEnd', function(source)
|
|
local location = playerLocation[source]
|
|
if location and location.type == 'preview' then resetPlayer(source) end
|
|
return { ok = true }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:enterLobby', function(source, payload)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
local building = findBuilding(payload.buildingKey)
|
|
if not player or not building then return { ok = false, error = 'Ungültiges Gebäude.' } end
|
|
|
|
setLocation(source, { type = 'lobby', buildingKey = building.key, floor = 0 }, floorBucket(building, 0))
|
|
return { ok = true, position = DevXHousing.FloorShell.spawn, building = building.key }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:goFloor', function(source, payload)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
local building = findBuilding(payload.buildingKey)
|
|
local floor = tonumber(payload.floor)
|
|
if not player or not building or not floor or floor < 1 or floor > building.floors then
|
|
return { ok = false, error = 'Ungültige Etage.' }
|
|
end
|
|
|
|
setLocation(source, { type = 'floor', buildingKey = building.key, floor = floor }, floorBucket(building, floor))
|
|
return { ok = true, position = DevXHousing.FloorShell.spawn, floor = floor }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:leaveBuilding', function(source)
|
|
local location = playerLocation[source]
|
|
if not location then return { ok = false, error = 'Du bist in keinem Gebäude.' } end
|
|
local building = findBuilding(location.buildingKey)
|
|
if not building then return { ok = false, error = 'Gebäude nicht gefunden.' } end
|
|
|
|
resetPlayer(source)
|
|
return { ok = true, position = building.exterior }
|
|
end)
|
|
|
|
local function hasUnitAccess(source, row)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
if not player or not row then return false end
|
|
if tonumber(row.ownerCharacterId) == tonumber(player.characterId) then return true end
|
|
|
|
local invite = temporaryInvites[source]
|
|
return invite
|
|
and invite.propertyKey == row.propertyKey
|
|
and invite.expiresAt >= os.time()
|
|
end
|
|
|
|
local function enterUnitFor(source, propertyKey)
|
|
local unit, building = findUnit(propertyKey)
|
|
local row = unit and unitRow(propertyKey) or nil
|
|
if not unit or not building or not row or not row.ownerCharacterId then
|
|
return { ok = false, error = 'Diese Wohnung ist nicht bewohnt.' }
|
|
end
|
|
if not hasUnitAccess(source, row) then
|
|
return { ok = false, error = 'Du hast keine Einladung für diese Wohnung.' }
|
|
end
|
|
|
|
local interior = DevXHousing.Interiors[unit.style]
|
|
setLocation(source, {
|
|
type = 'unit',
|
|
propertyKey = unit.key,
|
|
propertyId = tonumber(row.id),
|
|
buildingKey = building.key,
|
|
floor = unit.floor,
|
|
ownerCharacterId = tonumber(row.ownerCharacterId)
|
|
}, unitBucket(row.id))
|
|
|
|
return {
|
|
ok = true,
|
|
interior = interior,
|
|
propertyKey = unit.key,
|
|
label = unit.label,
|
|
ownerName = row.ownerName,
|
|
floor = unit.floor,
|
|
style = unit.style,
|
|
interactions = interior.interactions or {}
|
|
}
|
|
end
|
|
|
|
DevXRegisterCallback('housing:enterUnit', function(source, payload)
|
|
return enterUnitFor(source, payload.propertyKey)
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:enterOwned', function(source, payload)
|
|
local player = exports.devx_core:GetPlayer(source)
|
|
if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end
|
|
|
|
local owned = ownedUnitRow(player.characterId)
|
|
if not owned then return { ok = false, error = 'Du besitzt noch kein Apartment.' } end
|
|
|
|
local unit, building = findUnit(owned.propertyKey)
|
|
if not unit or not building then return { ok = false, error = 'Dein Apartment ist nicht mehr konfiguriert.' } end
|
|
if payload and payload.buildingKey and payload.buildingKey ~= building.key then
|
|
return { ok = false, error = ('Dein Apartment befindet sich in %s.'):format(building.label) }
|
|
end
|
|
|
|
return enterUnitFor(source, owned.propertyKey)
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:exitUnit', function(source)
|
|
local location = playerLocation[source]
|
|
if not location or location.type ~= 'unit' then return { ok = false, error = 'Du bist in keiner Wohnung.' } end
|
|
|
|
local building = findBuilding(location.buildingKey)
|
|
setLocation(source, {
|
|
type = 'floor',
|
|
buildingKey = location.buildingKey,
|
|
floor = location.floor
|
|
}, floorBucket(building, location.floor))
|
|
|
|
return { ok = true, position = DevXHousing.FloorShell.spawn, floor = location.floor }
|
|
end)
|
|
|
|
local function activeSourceForCharacter(characterId)
|
|
for source, player in pairs(exports.devx_core:GetActivePlayers()) do
|
|
if tonumber(player.characterId) == tonumber(characterId) then return tonumber(source) end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
DevXRegisterCallback('housing:ring', function(source, payload)
|
|
local visitor = exports.devx_core:GetPlayer(source)
|
|
local row = unitRow(payload.propertyKey)
|
|
local unit = findUnit(payload.propertyKey)
|
|
if not visitor or not row or not row.ownerCharacterId or not unit then
|
|
return { ok = false, error = 'Hier wohnt niemand.' }
|
|
end
|
|
|
|
local ownerSource = activeSourceForCharacter(row.ownerCharacterId)
|
|
if not ownerSource then return { ok = false, error = 'Der Bewohner ist derzeit nicht online.' } end
|
|
|
|
pendingDoorbells[ownerSource] = {
|
|
visitorSource = source,
|
|
visitorName = visitor.fullName,
|
|
propertyKey = payload.propertyKey,
|
|
expiresAt = os.time() + 30
|
|
}
|
|
TriggerClientEvent('devx_housing:client:doorbell', ownerSource, {
|
|
visitorSource = source,
|
|
visitorName = visitor.fullName,
|
|
propertyKey = payload.propertyKey,
|
|
label = unit.label
|
|
})
|
|
return { ok = true, message = 'Du hast geklingelt.' }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:knock', function(source, payload)
|
|
local visitor = exports.devx_core:GetPlayer(source)
|
|
local row = unitRow(payload.propertyKey)
|
|
if not visitor or not row or not row.ownerCharacterId then return { ok = false, error = 'Hier wohnt niemand.' } end
|
|
|
|
for target, location in pairs(playerLocation) do
|
|
if location.type == 'unit' and location.propertyKey == payload.propertyKey then
|
|
TriggerClientEvent('devx_housing:client:knock', target, visitor.fullName)
|
|
end
|
|
end
|
|
return { ok = true, message = 'Du hast angeklopft.' }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:acceptVisitor', function(source)
|
|
local owner = exports.devx_core:GetPlayer(source)
|
|
local location = playerLocation[source]
|
|
local bell = pendingDoorbells[source]
|
|
if not owner or not location or location.type ~= 'unit' or not bell or bell.expiresAt < os.time() then
|
|
return { ok = false, error = 'Keine aktuelle Klingelanfrage.' }
|
|
end
|
|
if bell.propertyKey ~= location.propertyKey then return { ok = false, error = 'Die Anfrage gehört zu einer anderen Wohnung.' } end
|
|
|
|
temporaryInvites[bell.visitorSource] = {
|
|
propertyKey = bell.propertyKey,
|
|
expiresAt = os.time() + DevXHousing.InvitationSeconds
|
|
}
|
|
pendingDoorbells[source] = nil
|
|
|
|
local result = enterUnitFor(bell.visitorSource, bell.propertyKey)
|
|
if result.ok then
|
|
TriggerClientEvent('devx_housing:client:enterGranted', bell.visitorSource, result)
|
|
TriggerClientEvent('devx_housing:client:visitorAccepted', source, bell.visitorName)
|
|
end
|
|
return result
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:inviteCandidates', function(source)
|
|
local owner = exports.devx_core:GetPlayer(source)
|
|
local location = playerLocation[source]
|
|
if not owner or not location or location.type ~= 'unit' then
|
|
return { ok = false, error = 'Du musst dich in deiner Wohnung befinden.' }
|
|
end
|
|
if tonumber(location.ownerCharacterId) ~= tonumber(owner.characterId) then
|
|
return { ok = false, error = 'Nur der Eigentümer kann Spieler einladen.' }
|
|
end
|
|
|
|
local players = {}
|
|
for playerSource, player in pairs(exports.devx_core:GetActivePlayers()) do
|
|
local numericSource = tonumber(playerSource)
|
|
if numericSource and numericSource ~= tonumber(source) then
|
|
players[#players + 1] = {
|
|
source = numericSource,
|
|
name = player.fullName or GetPlayerName(numericSource) or ('Spieler %s'):format(numericSource)
|
|
}
|
|
end
|
|
end
|
|
table.sort(players, function(a, b) return a.source < b.source end)
|
|
return { ok = true, players = players }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:invite', function(source, payload)
|
|
local owner = exports.devx_core:GetPlayer(source)
|
|
local target = tonumber(payload.target)
|
|
local location = playerLocation[source]
|
|
if not owner or not location or location.type ~= 'unit' then
|
|
return { ok = false, error = 'Du musst dich in deiner Wohnung befinden.' }
|
|
end
|
|
if tonumber(location.ownerCharacterId) ~= tonumber(owner.characterId) then
|
|
return { ok = false, error = 'Nur der Eigentümer kann Gäste einladen.' }
|
|
end
|
|
if not target or not exports.devx_core:GetPlayer(target) then return { ok = false, error = 'Spieler nicht gefunden.' } end
|
|
if target == tonumber(source) then return { ok = false, error = 'Du kannst dich nicht selbst einladen.' } end
|
|
|
|
local unit = findUnit(location.propertyKey)
|
|
directInvites[target] = {
|
|
propertyKey = location.propertyKey,
|
|
invitedBy = owner.fullName,
|
|
expiresAt = os.time() + DevXHousing.InvitationSeconds
|
|
}
|
|
TriggerClientEvent('devx_housing:client:invite', target, {
|
|
propertyKey = location.propertyKey,
|
|
invitedBy = owner.fullName,
|
|
label = unit and unit.label or 'Apartment',
|
|
expiresIn = DevXHousing.InvitationSeconds
|
|
})
|
|
return { ok = true }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:acceptInvite', function(source)
|
|
local invite = directInvites[source]
|
|
if not invite or invite.expiresAt < os.time() then
|
|
directInvites[source] = nil
|
|
return { ok = false, error = 'Du hast keine gültige Apartment-Einladung.' }
|
|
end
|
|
|
|
temporaryInvites[source] = {
|
|
propertyKey = invite.propertyKey,
|
|
expiresAt = invite.expiresAt
|
|
}
|
|
directInvites[source] = nil
|
|
return enterUnitFor(source, invite.propertyKey)
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:declineInvite', function(source)
|
|
directInvites[source] = nil
|
|
return { ok = true }
|
|
end)
|
|
|
|
DevXRegisterCallback('housing:declineVisitor', function(source)
|
|
pendingDoorbells[source] = nil
|
|
return { ok = true }
|
|
end)
|
|
|
|
-- /inviteapartment is handled client-side so it can open the player picker.
|
|
|
|
RegisterCommand('acceptapartment', function(source)
|
|
if source == 0 then return end
|
|
local invite = directInvites[source]
|
|
if not invite or invite.expiresAt < os.time() then
|
|
directInvites[source] = nil
|
|
TriggerClientEvent('devx_housing:client:notify', source, 'Keine gültige Apartment-Einladung.')
|
|
return
|
|
end
|
|
|
|
temporaryInvites[source] = { propertyKey = invite.propertyKey, expiresAt = invite.expiresAt }
|
|
directInvites[source] = nil
|
|
local result = enterUnitFor(source, invite.propertyKey)
|
|
if result.ok then TriggerClientEvent('devx_housing:client:enterGranted', source, result)
|
|
else TriggerClientEvent('devx_housing:client:notify', source, result.error) end
|
|
end, false)
|
|
|
|
RegisterCommand('acceptvisitor', function(source)
|
|
if source == 0 then return end
|
|
local bell = pendingDoorbells[source]
|
|
local location = playerLocation[source]
|
|
if not bell or bell.expiresAt < os.time() or not location or location.type ~= 'unit' then
|
|
TriggerClientEvent('devx_housing:client:notify', source, 'Keine aktuelle Klingelanfrage.')
|
|
return
|
|
end
|
|
|
|
temporaryInvites[bell.visitorSource] = { propertyKey = bell.propertyKey, expiresAt = os.time() + DevXHousing.InvitationSeconds }
|
|
pendingDoorbells[source] = nil
|
|
local result = enterUnitFor(bell.visitorSource, bell.propertyKey)
|
|
if result.ok then
|
|
TriggerClientEvent('devx_housing:client:enterGranted', bell.visitorSource, result)
|
|
TriggerClientEvent('devx_housing:client:visitorAccepted', source, bell.visitorName)
|
|
end
|
|
end, false)
|
|
|
|
exports('GetPlayerHousingLocation', function(source)
|
|
local location = playerLocation[tonumber(source)]
|
|
if not location then return nil end
|
|
local copy = {}
|
|
for key, value in pairs(location) do copy[key] = value end
|
|
if location.propertyKey then
|
|
local unit = findUnit(location.propertyKey)
|
|
if unit then
|
|
copy.style = unit.style
|
|
copy.interior = DevXHousing.Interiors[unit.style]
|
|
end
|
|
end
|
|
return copy
|
|
end)
|
|
|
|
exports('GetOwnedApartment', function(characterId)
|
|
return ownedUnitRow(tonumber(characterId))
|
|
end)
|
|
|
|
AddEventHandler('playerDropped', function()
|
|
resetPlayer(source)
|
|
end)
|
|
|
|
AddEventHandler('onResourceStart', function(resourceName)
|
|
if resourceName ~= GetCurrentResourceName() then return end
|
|
for _, playerId in ipairs(GetPlayers()) do SetPlayerRoutingBucket(tonumber(playerId), 0) end
|
|
end)
|
|
|
|
AddEventHandler('onResourceStop', function(resourceName)
|
|
if resourceName ~= GetCurrentResourceName() then return end
|
|
for _, playerId in ipairs(GetPlayers()) do SetPlayerRoutingBucket(tonumber(playerId), 0) end
|
|
end)
|