first commit
This commit is contained in:
@@ -0,0 +1,566 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user