1--! \page global_variables Global Variables
2--! \section Players Players
4--! See Players
for the
module on players.\n\n
5--! FrameworkZ.Players.List\n
6--! A list of all instanced players in the game.
8local getPlayer = getPlayer
9local isClient = isClient
11FrameworkZ = FrameworkZ or {}
13--! \brief Players module for FrameworkZ. Defines and interacts with PLAYER object.
14--! \class FrameworkZ.Players
15FrameworkZ.Players = {}
17--! \brief List of all instanced players in the game.
18FrameworkZ.Players.List = {}
20--! \brief Roles for players in FrameworkZ.
21FrameworkZ.Players.Roles = {
23 Operator = "Operator",
24 Moderator = "Moderator",
26 Super_Admin = "Super Admin",
29FrameworkZ.Players = FrameworkZ.Foundation:NewModule(FrameworkZ.Players, "Players")
32--! \brief Player class for FrameworkZ.
34PLAYER.__index = PLAYER
36--! \brief Initializes the player object.
37--! \return \string The username of the player.
38function PLAYER:Initialize()
39 if not self.isoPlayer then return end
41 local firstConnection = false
42 local characterModData = self.isoPlayer:getModData()["FZ_PLY"] or nil
44 if not characterModData then
45 firstConnection = true
47 self:InitializeDefaultFactionWhitelists()
49 self.isoPlayer:getModData()["FZ_PLY"] = {
50 username = self.username,
51 steamID = self.steamID,
53 maxCharacters = self.maxCharacters,
54 previousCharacter = self.previousCharacter,
55 whitelists = self.whitelists,
56 characters = self.characters
60 self.isoPlayer:transmitModData()
64 self:ValidatePlayerData()
66 --[[if isClient() then
67 FrameworkZ.Timers:Simple(5, function()
68 sendClientCommand("FZ_PLY", "initialize", {self.isoPlayer:getUsername()})
72 return FrameworkZ.Players:Initialize(self.username, self)
75--! \brief Saves the player's data.
76--! \param shouldTransmit \boolean (Optional) Whether or not to transmit the player's data to the server.
77--! \return \boolean Whether or not the player was successfully saved.
78--! \todo Test if localized variable (playerData) maintains referential integrity for transmitModData() to work on it.
79function PLAYER:Save(shouldTransmit)
80 if shouldTransmit == nil then shouldTransmit = true end
82 if not self.isoPlayer then return false end
84 local playerData = self:GetStoredData()
86 if not playerData then return false end
88 playerData.role = self.role
89 playerData.maxCharacters = self.maxCharacters
90 playerData.previousCharacter = self.previousCharacter
91 playerData.whitelists = self.whitelists
92 playerData.characters = self.characters
94 if shouldTransmit then
95 self.isoPlayer:transmitModData()
101--! \brief Destroys the player object.
102--! \return \mixed of \boolean Whether or not the player was successfully destroyed and \string The message on success or failure.
103function PLAYER:Destroy()
104 if not self.isoPlayer then return false, "Critical save fail: Iso Player is nil." end
106 local username = self.isoPlayer:getUsername()
107 local success1, success2, message
109 if FrameworkZ.Players.List[username] then
110 success1, message = FrameworkZ.Players:Save(username)
113 if FrameworkZ.Characters.List[username] then
114 FrameworkZ.Characters.List[username] = nil
117 if FrameworkZ.Players.List[username] then
118 FrameworkZ.Players.List[username] = nil
121 if success1 and success2 then
125 return false, message
128function PLAYER:InitializeDefaultFactionWhitelists()
129 local factions = FrameworkZ.Factions.List
131 for k, v in pairs(factions) do
132 if v.isWhitelistedByDefault then
133 self.whitelists[v.id] = true
138function PLAYER:ValidatePlayerData()
139 local characterModData = self.isoPlayer:getModData()["FZ_PLY"]
141 if not characterModData then return false end
143 local initializedNewData = false
145 if not characterModData.username then
146 initializedNewData = true
147 characterModData.username = self.username or getPlayer():getUsername()
150 if not characterModData.steamID then
151 initializedNewData = true
152 characterModData.steamID = self.steamID or getPlayer():getSteamID()
155 if not characterModData.role then
156 initializedNewData = true
157 characterModData.role = self.role or FrameworkZ.Players.Roles.User
160 if not characterModData.maxCharacters then
161 initializedNewData = true
162 characterModData.maxCharacters = self.maxCharacters or FrameworkZ.Config.DefaultMaxCharacters
165 if not characterModData.previousCharacter then
166 initializedNewData = true
167 characterModData.previousCharacter = self.previousCharacter or nil
170 if not characterModData.whitelists then
171 self:InitializeDefaultFactionWhitelists()
172 initializedNewData = true
173 characterModData.whitelists = self.whitelists
176 if not characterModData.characters then
177 initializedNewData = true
178 characterModData.characters = self.characters or {}
182 self.isoPlayer:transmitModData()
185 self.username = characterModData.username
186 self.steamID = characterModData.steamID
187 self.role = characterModData.role
188 self.maxCharacters = characterModData.maxCharacters
189 self.previousCharacter = characterModData.previousCharacter
190 self.whitelists = characterModData.whitelists
191 self.characters = characterModData.characters
193 return initializedNewData
196--! \brief Gets the stored player mod data table. Used internally. Do not use this unless you know what you are doing. Updating data on the mod data will cause inconsistencies between the mod data and the FrameworkZ player object.
197--! \return \table The stored player mod data table.
198function PLAYER:GetStoredData()
199 return self.isoPlayer:getModData()["FZ_PLY"]
202function PLAYER:GetWhitelists()
203 return self.whitelists
206function PLAYER:SetWhitelisted(factionID, whitelisted)
207 if not factionID then return false end
209 self.whitelists[factionID] = whitelisted
210 self:GetStoredData().whitelists[factionID] = whitelisted
215function PLAYER:IsWhitelisted(factionID)
216 if not factionID then return false end
218 return self.whitelists[factionID] or false
221--! \brief Plays a sound for the player that only they can hear.
222--! \param soundName \string The name of the sound to play.
223--! \return \integer The sound's ID.
224function PLAYER:PlayLocalSound(soundName)
225 return self.isoPlayer:getEmitter():playSoundImpl(soundName, nil)
228--! \brief Stops a sound for the player.
229--! \param soundNameOrID \mixed of \string or \integer The name or ID of the sound to stop.
230function PLAYER:StopSound(soundNameOrID)
231 if type(soundNameOrID) == "number" then
232 self.isoPlayer:getEmitter():stopSound(soundNameOrID)
233 elseif type(soundNameOrID) == "string" then
234 self.isoPlayer:getEmitter():stopSoundByName(soundNameOrID)
238function FrameworkZ.Players:New(isoPlayer)
239 if not isoPlayer then return false end
242 username = isoPlayer:getUsername(),
243 isoPlayer = isoPlayer,
244 steamID = isoPlayer:getSteamID(),
245 role = FrameworkZ.Players.Roles.User,
246 loadedCharacter = nil,
247 maxCharacters = FrameworkZ.Config.DefaultMaxCharacters,
248 previousCharacter = nil,
253 setmetatable(object, PLAYER)
258function FrameworkZ.Players:Initialize(username, player)
259 self.List[username] = player
264function FrameworkZ.Players:GetPlayerByID(username)
265 if not username then return false end
267 local player = self.List[username]
276function FrameworkZ.Players:GetCharacterByID(username, characterID)
277 if not username or not characterID then return false end
279 local player = self:GetPlayerByID(username)
282 local character = player.characters[characterID]
292--! \brief Gets saved character data by their ID.
293--! \param username \string The username of the player.
294--! \param characterID \integer The ID of the character.
295--! \return \table or \boolean The character data or false if the data failed to be retrieved.
296function FrameworkZ.Players:GetCharacterDataByID(username, characterID)
297 if not username or not characterID then return false end
299 local player = FrameworkZ.Players:GetPlayerByID(username)
302 local character = player.characters[characterID]
312function FrameworkZ.Players:ResetCharacterSaveInterval()
313 if FrameworkZ.Timers:Exists("FZ_CharacterSaveInterval") then
314 FrameworkZ.Timers:Start("FZ_CharacterSaveInterval")
318function FrameworkZ.Players:CreateCharacter(username, data)
319 if not username or not data then return false end
321 local player = self:GetPlayerByID(username)
323 if player and player.characters then
324 FrameworkZ.Players:ResetCharacterSaveInterval()
326 data.META_ID = #player.characters + 1
327 data.META_FIRST_LOAD = true
329 table.insert(player.characters, data)
330 player:GetStoredData().characters = player.characters
333 player.isoPlayer:transmitModData()
336 return true, #player.characters
342--! \brief Saves the player and their currently loaded character.
343--! \param username \string The username of the player.
344--! \param continueOnFailure \boolean (Optional) Whether or not to continue saving either the player or character if either should fail. Default = false. True not recommended.
345--! \return \boolean Whether or not the player was successfully saved.
346--! \return \string The failure message if the player or character failed to save.
347function FrameworkZ.Players:Save(username, continueOnFailure)
348 if continueOnFailure == nil then continueOnFailure = false end
350 local player = FrameworkZ.Players:GetPlayerByID(username)
352 if not player then return false end
355 local failureMessage = ""
356 local character = player.loadedCharacter
357 local characterSaved = false
358 local playerSaved = player:Save(false)
361 if not saved and not continueOnFailure then
362 return false, "Failed to save player data."
363 elseif not saved and continueOnFailure then
364 failureMessage = "Failed to save player data."
368 characterSaved = character:Save(false)
369 saved = characterSaved
371 if not saved and not continueOnFailure then
372 return false, "Failed to save character data."
373 elseif not saved and continueOnFailure then
374 failureMessage = failureMessage == "Failed to save player data." and "Failed to save both player data and character data." or "Player data saved, but failed to save character data."
377 characterSaved = true -- No character loaded, set true to prevent returning false.
381 player.isoPlayer:transmitModData()
384 if playerSaved and characterSaved then
390 return saved, failureMessage
393function FrameworkZ.Players:Destroy(username)
394 local properlyDestroyed = false
395 local message = "Failed to destroy player."
396 local player = self:GetPlayerByID(username)
399 properlyDestroyed, message = player:Destroy()
402 return properlyDestroyed, message
405function FrameworkZ.Players:SaveCharacter(username, character)
406 local player = FrameworkZ.Players:GetPlayerByID(username)
408 if not player or not character then return false end
410 local isoPlayer = player.isoPlayer
412 character.INVENTORY_PHYSICAL = {}
413 local inventory = isoPlayer:getInventory():getItems()
414 for i = 0, inventory:size() - 1 do
415 table.insert(character.INVENTORY_PHYSICAL, {id = inventory:get(i):getFullType()})
418 character.INVENTORY_LOGICAL = FrameworkZ.Characters:GetCharacterInventoryByID(username).items
420 character.EQUIPMENT_SLOT_HEAD = isoPlayer:getWornItem(EQUIPMENT_SLOT_HEAD) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_HEAD):getFullType()} or nil
421 character.EQUIPMENT_SLOT_FACE = isoPlayer:getWornItem(EQUIPMENT_SLOT_FACE) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_FACE):getFullType()} or nil
422 character.EQUIPMENT_SLOT_EARS = isoPlayer:getWornItem(EQUIPMENT_SLOT_EARS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_EARS):getFullType()} or nil
423 character.EQUIPMENT_SLOT_BACKPACK = isoPlayer:getWornItem(EQUIPMENT_SLOT_BACKPACK) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_BACKPACK):getFullType()} or nil
424 character.EQUIPMENT_SLOT_GLOVES = isoPlayer:getWornItem(EQUIPMENT_SLOT_GLOVES) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_GLOVES):getFullType()} or nil
425 character.EQUIPMENT_SLOT_UNDERSHIRT = isoPlayer:getWornItem(EQUIPMENT_SLOT_UNDERSHIRT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_UNDERSHIRT):getFullType()} or nil
426 character.EQUIPMENT_SLOT_OVERSHIRT = isoPlayer:getWornItem(EQUIPMENT_SLOT_OVERSHIRT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_OVERSHIRT):getFullType()} or nil
427 character.EQUIPMENT_SLOT_VEST = isoPlayer:getWornItem(EQUIPMENT_SLOT_VEST) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_VEST):getFullType()} or nil
428 character.EQUIPMENT_SLOT_BELT = isoPlayer:getWornItem(EQUIPMENT_SLOT_BELT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_BELT):getFullType()} or nil
429 character.EQUIPMENT_SLOT_PANTS = isoPlayer:getWornItem(EQUIPMENT_SLOT_PANTS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_PANTS):getFullType()} or nil
430 character.EQUIPMENT_SLOT_SOCKS = isoPlayer:getWornItem(EQUIPMENT_SLOT_SOCKS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_SOCKS):getFullType()} or nil
431 character.EQUIPMENT_SLOT_SHOES = isoPlayer:getWornItem(EQUIPMENT_SLOT_SHOES) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_SHOES):getFullType()} or nil
433 -- Save character position/direction angle
434 character.POSITION_X = isoPlayer:getX()
435 character.POSITION_Y = isoPlayer:getY()
436 character.POSITION_Z = isoPlayer:getZ()
437 character.DIRECTION_ANGLE = isoPlayer:getDirectionAngle()
439 local getStats = isoPlayer:getStats()
440 character.STAT_HUNGER = getStats:getHunger()
441 character.STAT_THIRST = getStats:getThirst()
442 character.STAT_FATIGUE = getStats:getFatigue()
443 character.STAT_STRESS = getStats:getStress()
444 character.STAT_PAIN = getStats:getPain()
445 character.STAT_PANIC = getStats:getPanic()
446 character.STAT_BOREDOM = getStats:getBoredom()
447 --character.STAT_UNHAPPINESS = getStats:getUnhappyness()
448 character.STAT_DRUNKENNESS = getStats:getDrunkenness()
449 character.STAT_ENDURANCE = getStats:getEndurance()
450 --character.STAT_TIREDNESS = getStats:getTiredness()
453 modData.status.health = character:getBodyDamage():getOverallBodyHealth()
454 modData.status.injuries = character:getBodyDamage():getInjurySeverity()
455 modData.status.hyperthermia = character:getBodyDamage():getTemperature()
456 modData.status.hypothermia = character:getBodyDamage():getColdStrength()
457 modData.status.wetness = character:getBodyDamage():getWetness()
458 modData.status.hasCold = character:getBodyDamage():HasACold()
459 modData.status.sick = character:getBodyDamage():getSicknessLevel()
463 isoPlayer:transmitModData()
469function FrameworkZ.Players:SaveCharacterByID(username, characterID)
475 1. Load equipment/items
478 4. Apply damage/wounds/moodles (if applicable)
485function FrameworkZ.Players:LoadCharacter(username, characterData, survivorDescriptor)
486 local player = FrameworkZ.Players:GetPlayerByID(username)
488 if not player or not characterData then return false end
490 local isoPlayer = player.isoPlayer
492 if characterData.META_FIRST_LOAD == true then
493 isoPlayer:setX(FrameworkZ.Config.SpawnX)
494 isoPlayer:setY(FrameworkZ.Config.SpawnY)
495 isoPlayer:setZ(FrameworkZ.Config.SpawnZ)
496 isoPlayer:setLx(FrameworkZ.Config.SpawnX)
497 isoPlayer:setLy(FrameworkZ.Config.SpawnY)
498 isoPlayer:setLz(FrameworkZ.Config.SpawnZ)
500 isoPlayer:setX(characterData.POSITION_X)
501 isoPlayer:setY(characterData.POSITION_Y)
502 isoPlayer:setZ(characterData.POSITION_Z)
503 isoPlayer:setLx(characterData.POSITION_X)
504 isoPlayer:setLy(characterData.POSITION_Y)
505 isoPlayer:setLz(characterData.POSITION_Z)
506 isoPlayer:setDirectionAngle(characterData.DIRECTION_ANGLE)
509 isoPlayer:clearWornItems()
510 isoPlayer:getInventory():clear()
512 for k, v in pairs(characterData) do
513 if string.match(k, "EQUIPMENT_SLOT_") then
515 local item = isoPlayer:getInventory():AddItem(v.id)
516 isoPlayer:setWornItem(item:getBodyLocation(), item)
521 local isFemale = survivorDescriptor:isFemale()
522 isoPlayer:setFemale(isFemale)
523 isoPlayer:getDescriptor():setFemale(isFemale)
524 isoPlayer:getHumanVisual():clear()
525 isoPlayer:getHumanVisual():copyFrom(survivorDescriptor:getHumanVisual())
526 isoPlayer:resetModel()
528 isoPlayer:setGodMod(false)
529 isoPlayer:setInvincible(false)
531 -- Apply damage/wounds/moodles
533 isoPlayer:setInvisible(false)
534 isoPlayer:setGhostMode(false)
535 isoPlayer:setNoClip(false)
537 if VoiceManager:playerGetMute(username) then
538 VoiceManager:playerSetMute(username)
541 local postLoadSuccessful, character = FrameworkZ.Characters:PostLoad(isoPlayer, characterData)
543 if not postLoadSuccessful or not character then return false end
545 player.loadedCharacter = character
546 character:OnPostLoad(characterData.META_FIRST_LOAD)
548 if characterData.META_FIRST_LOAD then
549 characterData.META_FIRST_LOAD = false
552 if not self:SaveCharacter(username, characterData) then return false end
557function FrameworkZ.Players:LoadCharacterByID(username, characterID)
561function FrameworkZ.Players:DeleteCharacter(username, character)
565function FrameworkZ.Players:DeleteCharacterByID(username, characterID)
569function FrameworkZ.Players:InitializeClient(isoPlayer)
570 local player = FrameworkZ.Players:New(isoPlayer)
577if not isClient() then
578 function FrameworkZ.Players.OnClientCommand(module, command, isoPlayer, args)
579 if module == "FZ_PLY" then
580 if command == "initialize" then
581 local player = FrameworkZ.Players:New(isoPlayer)
586 elseif command == "remove_limbo_protection" then
587 isoPlayer:setGodMod(false)
588 isoPlayer:setInvincible(false)
589 isoPlayer:setInvisible(false)
590 isoPlayer:setGhostMode(false)
591 isoPlayer:setNoClip(false)
592 sendPlayerExtraInfo(isoPlayer)
593 elseif command == "on_first_load" then
594 isoPlayer:setX(FrameworkZ.Config.SpawnX)
595 isoPlayer:setY(FrameworkZ.Config.SpawnY)
596 isoPlayer:setZ(FrameworkZ.Config.SpawnZ)
597 isoPlayer:setLx(FrameworkZ.Config.SpawnX)
598 isoPlayer:setLy(FrameworkZ.Config.SpawnY)
599 isoPlayer:setLz(FrameworkZ.Config.SpawnZ)
600 elseif command == "destroy" then
601 local username = args[1]
602 local player = FrameworkZ.Players:GetPlayerByID(username)
608 FrameworkZ.Characters.List[username] = nil
609 elseif command == "update" then
610 local username = args[1]
611 local field = args[2]
612 local newData = args[3]
614 FrameworkZ.Players.List[username][field] = newData
618 Events.OnClientCommand.Add(FrameworkZ.Players.OnClientCommand)
621FrameworkZ.Foundation:RegisterModule(FrameworkZ.Players)
void initializedNewData()
void local postLoadSuccessful
Characters module for FrameworkZ. Defines and interacts with CHARACTER object.
FrameworkZ Characters List
Players module for FrameworkZ. Defines and interacts with PLAYER object.
void DeleteCharacter(username, character)
table GetCharacterDataByID(username, characterID)
Gets saved character data by their ID.
void CreateCharacter(username, data)
void LoadCharacterByID(username, characterID)
void LoadCharacter(username, characterData, survivorDescriptor)
void ResetCharacterSaveInterval()
void SaveCharacterByID(username, characterID)
void OnClientCommand(module, command, isoPlayer, args)
void SaveCharacter(username, character)
FrameworkZ Players Roles
Roles for players in FrameworkZ.
void Initialize(username, player)
void GetCharacterByID(username, characterID)
void InitializeClient(isoPlayer)
FrameworkZ Players List
List of all instanced players in the game.
void GetPlayerByID(username)
void DeleteCharacterByID(username, characterID)
boolean Save(username, continueOnFailure)
Saves the player and their currently loaded character.
Player class for FrameworkZ.
void InitializeDefaultFactionWhitelists()
boolean Save(shouldTransmit)
Saves the player's data.
void StopSound(soundNameOrID)
Stops a sound for the player.
mixed Destroy()
Destroys the player object.
integer PlayLocalSound(soundName)
Plays a sound for the player that only they can hear.
table GetStoredData()
Gets the stored player mod data table. Used internally. Do not use this unless you know what you are ...
string Initialize()
Initializes the player object.
void SetWhitelisted(factionID, whitelisted)
void ValidatePlayerData()
void IsWhitelisted(factionID)