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:GetIsoPlayer() then return false end
41 self:InitializeDefaultFactionWhitelists()
44--! \brief Saves the player's data.
45--! \param shouldTransmit \boolean (Optional) Whether or not to transmit the player's data to the server.
46--! \return \boolean Whether or not the player was successfully saved.
47--! \todo Test if localized variable (playerData) maintains referential integrity for transmitModData() to work on it.
48function PLAYER:Save(shouldTransmit)
49 if shouldTransmit == nil then shouldTransmit = true end
51 if not self:GetIsoPlayer() then return false end
53 local playerData = self:GetStoredData()
55 if not playerData then return false end
57 playerData.role = self.role
58 playerData.maxCharacters = self.maxCharacters
59 playerData.previousCharacter = self.previousCharacter
60 playerData.whitelists = self.whitelists
61 playerData.Characters = self.Characters
63 if shouldTransmit then
64 self:GetIsoPlayer():transmitModData()
70--! \brief Destroys the player object.
71--! \return \mixed of \boolean Whether or not the player was successfully destroyed and \string The message on success or failure.
72function PLAYER:Destroy()
73 if not self:GetIsoPlayer() then return false, "Critical save fail: Iso Player is nil." end
75 local username = self:GetUsername()
76 local success1, success2, message
78 if FrameworkZ.Players.List[username] then
79 success1, message = FrameworkZ.Players:Save(username)
82 if FrameworkZ.Characters.List[username] then
83 FrameworkZ.Characters.List[username] = nil
86 if FrameworkZ.Players.List[username] then
87 FrameworkZ.Players.List[username] = nil
90 if success1 and success2 then
97function PLAYER:InitializeDefaultFactionWhitelists()
98 local factions = FrameworkZ.Factions.List
100 for k, v in pairs(factions) do
101 if v.isWhitelistedByDefault then
102 self.whitelists[v.id] = true
107function PLAYER:RestoreData(data)
108 self:SetRole(data.role)
109 self:SetMaxCharacters(data.maxCharacters)
110 self:SetPreviousCharacter(data.previousCharacter)
111 self:SetWhitelists(data.whitelists)
112 self:SetCustomData(data.CustomData)
115function PLAYER:GetRole()
119function PLAYER:SetRole(role)
120 print("Failed to set Role to: '" .. tostring(role) .. "'. Role is read-only and must be set upon object creation.")
123function PLAYER:GetPreviousCharacter()
124 return self.previousCharacter
127function PLAYER:SetPreviousCharacter(previousCharacter)
128 if not previousCharacter or type(previousCharacter) ~= "number" then
129 print("Failed to set Previous Character to: '" .. tostring(previousCharacter) .. "'. Previous Character must be a number.")
133 self.previousCharacter = previousCharacter
138function PLAYER:GetMaxCharacters()
139 return self.maxCharacters
142function PLAYER:SetMaxCharacters(maxCharacters)
143 if not maxCharacters or type(maxCharacters) ~= "number" then
144 print("Failed to set Max Characters to: '" .. tostring(maxCharacters) .. "'. Max Characters must be a number.")
148 if maxCharacters < 1 then
149 print("Failed to set Max Characters to: '" .. tostring(maxCharacters) .. "'. Max Characters must be at least 1.")
153 self.maxCharacters = maxCharacters
158function PLAYER:GetCustomData()
159 return self.CustomData
162function PLAYER:SetCustomData(customData)
163 if not customData or type(customData) ~= "table" then
164 print("Failed to set Custom Data to: '" .. tostring(customData) .. "'. Custom Data must be a table.")
168 self.CustomData = customData
173function PLAYER:GetCharacter()
174 return self.LoadedCharacter
177function PLAYER:SetCharacter(character)
178 self.LoadedCharacter = character
181function PLAYER:GetCharacters()
182 return self.Characters
185function PLAYER:SetCharacters(characters)
186 self.Characters = characters
189function PLAYER:GetCharacterDataByID(characterID)
190 if not characterID then return false, "Missing character ID." end
192 local character = self.Characters[characterID]
195 return character, "Successfully retrieved character data."
198 return false, "Character not found."
201function PLAYER:GetUsername()
205function PLAYER:SetUsername(username)
206 print("Failed to set Username to: '" .. tostring(username) .. "'. Username is read-only and must be set upon object creation.")
209function PLAYER:GetIsoPlayer()
210 return self.IsoPlayer
213function PLAYER:SetIsoPlayer(isoPlayer)
214 print("Failed to set IsoPlayer to: '" .. tostring(isoPlayer) .. "'. IsoPlayer is read-only and must be set upon object creation.")
217function PLAYER:GetSaveableData()
218 return FrameworkZ.Foundation:ProcessSaveableData(self, {"IsoPlayer", "Characters", "LoadedCharacter"})
221--! \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.
222--! \return \table The stored player mod data table.
223function PLAYER:GetStoredData()
224 return self.IsoPlayer:getModData()["FZ_PLY"]
227function PLAYER:GetWhitelists()
228 return self.whitelists
231function PLAYER:SetWhitelists(whitelists)
232 if not whitelists or type(whitelists) ~= "table" then
233 print("Failed to set Whitelists to: '" .. tostring(whitelists) .. "'. Whitelists must be a table.")
237 self.whitelists = whitelists
242function PLAYER:SetWhitelisted(factionID, whitelisted)
243 if not factionID then return false end
245 self.whitelists[factionID] = whitelisted
246 self:GetStoredData().whitelists[factionID] = whitelisted
251function PLAYER:IsWhitelisted(factionID)
252 if not factionID then return false end
254 return self.whitelists[factionID] or false
257--! \brief Plays a sound for the player that only they can hear.
258--! \param soundName \string The name of the sound to play.
259--! \return \integer The sound's ID.
260function PLAYER:PlayLocalSound(soundName)
261 return self:GetIsoPlayer():getEmitter():playSoundImpl(soundName, nil)
264--! \brief Stops a sound for the player.
265--! \param soundNameOrID \mixed of \string or \integer The name or ID of the sound to stop.
266function PLAYER:StopSound(soundNameOrID)
267 if type(soundNameOrID) == "number" then
268 self:GetIsoPlayer():getEmitter():stopSound(soundNameOrID)
269 elseif type(soundNameOrID) == "string" then
270 self:GetIsoPlayer():getEmitter():stopSoundByName(soundNameOrID)
275function PLAYER:ValidatePlayerData()
276 local characterModData = self.isoPlayer:getModData()["FZ_PLY"]
278 if not characterModData then return false end
280 local initializedNewData = false
282 if not characterModData.username then
283 initializedNewData = true
284 characterModData.username = self.username or getPlayer():getUsername()
287 if not characterModData.steamID then
288 initializedNewData = true
289 characterModData.steamID = self.steamID or getPlayer():getSteamID()
292 if not characterModData.role then
293 initializedNewData = true
294 characterModData.role = self.role or FrameworkZ.Players.Roles.User
297 if not characterModData.maxCharacters then
298 initializedNewData = true
299 characterModData.maxCharacters = self.maxCharacters or FrameworkZ.Config.Options.DefaultMaxCharacters
302 if not characterModData.previousCharacter then
303 initializedNewData = true
304 characterModData.previousCharacter = self.previousCharacter or nil
307 if not characterModData.whitelists then
308 self:InitializeDefaultFactionWhitelists()
309 initializedNewData = true
310 characterModData.whitelists = self.whitelists
313 if not characterModData.Characters then
314 initializedNewData = true
315 characterModData.Characters = self.Characters or {}
319 self.isoPlayer:transmitModData()
322 self.username = characterModData.username
323 self.steamID = characterModData.steamID
324 self.role = characterModData.role
325 self.maxCharacters = characterModData.maxCharacters
326 self.previousCharacter = characterModData.previousCharacter
327 self.whitelists = characterModData.whitelists
328 self.Characters = characterModData.Characters
330 return initializedNewData
334function FrameworkZ.Players:New(isoPlayer)
335 if not isoPlayer then return false end
337 -- TODO Reminder: Update FrameworkZ.Players:OnStorageSet() when updating here.
339 IsoPlayer = isoPlayer,
340 Username = isoPlayer:getUsername(),
341 steamID = tostring(isoPlayer:getSteamID()),
342 role = FrameworkZ.Players.Roles.User,
343 LoadedCharacter = nil,
344 maxCharacters = FrameworkZ.Config.Options.DefaultMaxCharacters,
345 previousCharacter = nil,
351 setmetatable(object, PLAYER)
356function FrameworkZ.Players:Initialize(isoPlayer)
357 local player = FrameworkZ.Players:New(isoPlayer) if not player then return false end
358 local username = player:GetUsername()
361 self.List[username] = player
362 self:StartPlayerTick(player)
364 return self.List[username]
367function FrameworkZ.Players:StartPlayerTick(player)
368 if not isClient() then return end
370 -- TODO rename CHAR to PLAYER
371 FrameworkZ.Timers:Create("FZ_PLY_TICK", FrameworkZ.Config.Options.PlayerTickInterval, 0, function()
372 FrameworkZ.Foundation:ExecuteAllHooks("PlayerTick", player)
376--! \brief Gets the player object by their username.
377--! \param username \string The username of the player.
378--! \return \object or \boolean The player object or false if the player was not found.
379function FrameworkZ.Players:GetPlayerByID(username)
380 if not username then return false, "Username not set." end
381 if not self.List[username] then return false, "Player does not exist." end
383 return self.List[username]
386function FrameworkZ.Players:GetLoadedCharacterByID(username)
387 if not username then return false end
389 local player = self:GetPlayerByID(username)
392 return player:GetCharacter() or false
398--! \brief Gets saved character data by their ID.
399--! \param username \string The username of the player.
400--! \param characterID \integer The ID of the character.
401--! \return \table or \boolean The character data or false if the data failed to be retrieved.
402function FrameworkZ.Players:GetCharacterDataByID(username, characterID)
403 if not username then return false, "Missing username." end
404 if not characterID then return false, "Missing character ID." end
406 local player = FrameworkZ.Players:GetPlayerByID(username) if not player then return false, "Player not found." end
408 return player:GetCharacterDataByID(characterID)
411function FrameworkZ.Players:GetNextCharacterID(username)
412 if not username then return false end
414 local player = self:GetPlayerByID(username)
417 local nextID = #player.Characters + 1
419 if nextID > player.maxCharacters then
420 return false, "Max characters reached."
426 return false, "Player not found."
429function FrameworkZ.Players:ResetCharacterSaveInterval()
430 if FrameworkZ.Timers:Exists("FZ_CharacterSaveInterval") then
431 FrameworkZ.Timers:Start("FZ_CharacterSaveInterval")
435function PLAYER:GenerateUID()
436 local username = self:GetUsername()
437 local characters = self:GetCharacters() if not characters then return false end
440 local uid = username .. "_" .. FrameworkZ.Utilities:GetRandomNumber(1, 999999, true)
442 for k, v in pairs(characters) do
443 if v.META_UID == uid then
454function FrameworkZ.Players.OnCreateCharacter(data, username, characterData)
455 if not username then return false, "Username is nil." end
456 if not characterData then return false, "Character data is nil." end
458 return FrameworkZ.Players:CreateCharacter(username, characterData)
460FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.OnCreateCharacter", FrameworkZ.Players.OnCreateCharacter)
462function FrameworkZ.Players:CreateCharacter(username, characterData, characterID)
463 if not username then return false, "Username is nil" end
464 if not characterData then return false, "Character data is nil." end
466 local player = self:GetPlayerByID(username)
468 if player and player.Characters then
470 FrameworkZ.Players:ResetCharacterSaveInterval()
473 characterData.META_ID = characterID and characterID or #player.Characters + 1
474 characterData.META_FIRST_LOAD = true
476 if not characterID then
477 table.insert(player.Characters, characterData)
479 player.Characters[characterID] = characterData
482 characterData.META_UID = player:GenerateUID()
485 FrameworkZ.Foundation:SetData(nil, "Characters", {username, characterData.META_ID}, characterData)
488 return characterData.META_ID
491 return false, "Player not found."
494--! \brief Saves the player and their currently loaded character.
495--! \param username \string The username of the player.
496--! \param continueOnFailure \boolean (Optional) Whether or not to continue saving either the player or character if either should fail. Default = false. True not recommended.
497--! \return \boolean Whether or not the player was successfully saved.
498--! \return \string The failure message if the player or character failed to save.
499function FrameworkZ.Players:Save(username, continueOnFailure)
500 if continueOnFailure == nil then continueOnFailure = false end
502 local player = FrameworkZ.Players:GetPlayerByID(username)
504 if not player then return false end
507 local failureMessage = ""
508 local character = player.LoadedCharacter
509 local characterSaved = false
510 local playerSaved = player:Save(false)
513 if not saved and not continueOnFailure then
514 return false, "Failed to save player data."
515 elseif not saved and continueOnFailure then
516 failureMessage = "Failed to save player data."
520 characterSaved = character:Save(false)
521 saved = characterSaved
523 if not saved and not continueOnFailure then
524 return false, "Failed to save character data."
525 elseif not saved and continueOnFailure then
526 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."
529 characterSaved = true -- No character loaded, set true to prevent returning false.
533 player:GetIsoPlayer():transmitModData()
536 if playerSaved and characterSaved then
542 return saved, failureMessage
545function FrameworkZ.Players:Destroy(username)
546 local properlyDestroyed = false
547 local message = "Failed to destroy player."
548 local player = self:GetPlayerByID(username)
551 properlyDestroyed, message = player:Destroy()
554 return properlyDestroyed, message
557function FrameworkZ.Players:SaveCharacter(username, character)
558 local player = FrameworkZ.Players:GetPlayerByID(username)
560 if not player or not character then return false end
562 local isoPlayer = player:GetIsoPlayer()
564 character.INVENTORY_PHYSICAL = {}
565 local inventory = isoPlayer:getInventory():getItems()
566 for i = 0, inventory:size() - 1 do
567 table.insert(character.INVENTORY_PHYSICAL, {id = inventory:get(i):getFullType()})
570 character.INVENTORY_LOGICAL = FrameworkZ.Characters:GetCharacterInventoryByID(username).items
572 character.EQUIPMENT_SLOT_HEAD = isoPlayer:getWornItem(EQUIPMENT_SLOT_HEAD) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_HEAD):getFullType()} or nil
573 character.EQUIPMENT_SLOT_FACE = isoPlayer:getWornItem(EQUIPMENT_SLOT_FACE) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_FACE):getFullType()} or nil
574 character.EQUIPMENT_SLOT_EARS = isoPlayer:getWornItem(EQUIPMENT_SLOT_EARS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_EARS):getFullType()} or nil
575 character.EQUIPMENT_SLOT_BACKPACK = isoPlayer:getWornItem(EQUIPMENT_SLOT_BACKPACK) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_BACKPACK):getFullType()} or nil
576 character.EQUIPMENT_SLOT_GLOVES = isoPlayer:getWornItem(EQUIPMENT_SLOT_GLOVES) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_GLOVES):getFullType()} or nil
577 character.EQUIPMENT_SLOT_UNDERSHIRT = isoPlayer:getWornItem(EQUIPMENT_SLOT_UNDERSHIRT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_UNDERSHIRT):getFullType()} or nil
578 character.EQUIPMENT_SLOT_OVERSHIRT = isoPlayer:getWornItem(EQUIPMENT_SLOT_OVERSHIRT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_OVERSHIRT):getFullType()} or nil
579 character.EQUIPMENT_SLOT_VEST = isoPlayer:getWornItem(EQUIPMENT_SLOT_VEST) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_VEST):getFullType()} or nil
580 character.EQUIPMENT_SLOT_BELT = isoPlayer:getWornItem(EQUIPMENT_SLOT_BELT) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_BELT):getFullType()} or nil
581 character.EQUIPMENT_SLOT_PANTS = isoPlayer:getWornItem(EQUIPMENT_SLOT_PANTS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_PANTS):getFullType()} or nil
582 character.EQUIPMENT_SLOT_SOCKS = isoPlayer:getWornItem(EQUIPMENT_SLOT_SOCKS) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_SOCKS):getFullType()} or nil
583 character.EQUIPMENT_SLOT_SHOES = isoPlayer:getWornItem(EQUIPMENT_SLOT_SHOES) and {id = isoPlayer:getWornItem(EQUIPMENT_SLOT_SHOES):getFullType()} or nil
585 -- Save character position/direction angle
586 character.POSITION_X = isoPlayer:getX()
587 character.POSITION_Y = isoPlayer:getY()
588 character.POSITION_Z = isoPlayer:getZ()
589 character.DIRECTION_ANGLE = isoPlayer:getDirectionAngle()
591 local getStats = isoPlayer:getStats()
592 character.STAT_HUNGER = getStats:getHunger()
593 character.STAT_THIRST = getStats:getThirst()
594 character.STAT_FATIGUE = getStats:getFatigue()
595 character.STAT_STRESS = getStats:getStress()
596 character.STAT_PAIN = getStats:getPain()
597 character.STAT_PANIC = getStats:getPanic()
598 character.STAT_BOREDOM = getStats:getBoredom()
599 --character.STAT_UNHAPPINESS = getStats:getUnhappyness()
600 character.STAT_DRUNKENNESS = getStats:getDrunkenness()
601 character.STAT_ENDURANCE = getStats:getEndurance()
602 --character.STAT_TIREDNESS = getStats:getTiredness()
605 modData.status.health = character:getBodyDamage():getOverallBodyHealth()
606 modData.status.injuries = character:getBodyDamage():getInjurySeverity()
607 modData.status.hyperthermia = character:getBodyDamage():getTemperature()
608 modData.status.hypothermia = character:getBodyDamage():getColdStrength()
609 modData.status.wetness = character:getBodyDamage():getWetness()
610 modData.status.hasCold = character:getBodyDamage():HasACold()
611 modData.status.sick = character:getBodyDamage():getSicknessLevel()
615 isoPlayer:transmitModData()
621function FrameworkZ.Players:SaveCharacterByID(username, characterID)
625function PLAYER:SetModel(characterData)
626 if not characterData then return false end
627 if not self:GetIsoPlayer() then return false end
628 local isoPlayer = self:GetIsoPlayer()
630 local isFemale = characterData.INFO_GENDER == "Female" or not characterData.INFO_GENDER == "Male"
631 isoPlayer:setFemale(isFemale)
632 isoPlayer:getDescriptor():setFemale(isFemale)
634 local hairColor = characterData.INFO_HAIR_COLOR
635 local beardColor = characterData.INFO_BEARD_COLOR
636 local visual = isoPlayer:getHumanVisual()
638 visual:setBeardColor(ImmutableColor.new(beardColor.r, beardColor.g, beardColor.b, 1))
639 visual:setBeardModel(characterData.INFO_BEARD_STYLE)
640 visual:setHairColor(ImmutableColor.new(hairColor.r, hairColor.g, hairColor.b, 1))
641 visual:setHairModel(characterData.INFO_HAIR_STYLE)
642 visual:setNaturalBeardColor(ImmutableColor.new(beardColor.r, beardColor.g, beardColor.b, 1))
643 visual:setNaturalHairColor(ImmutableColor.new(hairColor.r, hairColor.g, hairColor.b, 1))
644 visual:setSkinTextureIndex(characterData.INFO_SKIN_COLOR)
646 isoPlayer:resetModel()
649function PLAYER:LoadCharacter(characterID)
650 if not characterID then return false end
652 FrameworkZ.Players:OnLoadCharacter(self:GetUsername(), characterID)
655function FrameworkZ.Players:LoadCharacterByID(username, characterID)
656 local clientLoadCharacter = function(_data, success, message)
658 FrameworkZ.Notifications:AddToQueue("Failed to load character: " .. message, FrameworkZ.Notifications.Types.Danger, nil, FrameworkZ.UI.MainMenu.instance)
662 self:OnLoadCharacter(username, characterID)
665 FrameworkZ.Foundation:SendFire(FrameworkZ.Players:GetPlayerByID(username):GetIsoPlayer(), "FrameworkZ.Players.LoadCharacter", clientLoadCharacter, username, characterID)
669 function FrameworkZ.Players.LoadCharacter(_data, username, characterID)
670 return FrameworkZ.Players:OnLoadCharacter(username, characterID)
672 FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.LoadCharacter", FrameworkZ.Players.LoadCharacter)
675function FrameworkZ.Players:OnLoadCharacter(username, characterID)
676 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterLoaded", username, characterID)
678 local player, message = FrameworkZ.Players:GetPlayerByID(username) if not player then return false, message end
679 local character, message2 = FrameworkZ.Characters:Initialize(player:GetIsoPlayer(), characterID) if not character then return false, message2 end
680 local characterData, message3 = player:GetCharacterDataByID(characterID) if not characterData then return false, message3 end
681 local isoPlayer = player:GetIsoPlayer()
683 player:SetCharacter(character)
685 FrameworkZ.Players:OnPreLoadCharacter(isoPlayer, player, character, characterData)
686 FrameworkZ.Players:OnPostLoadCharacter(isoPlayer, player, character, characterData)
688 return true, "Successfully loaded character."
691function FrameworkZ.Players:OnPreLoadCharacter(isoPlayer, player, character, characterData)
692 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterPreLoad", isoPlayer, player, character, characterData)
694 isoPlayer:clearWornItems()
695 isoPlayer:getInventory():clear()
697 for k, v in pairs(characterData) do
698 if string.match(k, "EQUIPMENT_SLOT_") then
700 local item = isoPlayer:getInventory():AddItem(v.id)
701 isoPlayer:setWornItem(item:getBodyLocation(), item)
706 player:SetModel(characterData)
708 -- Apply damage/wounds/moodles
711function FrameworkZ.Players:OnPostLoadCharacter(isoPlayer, player, character, characterData)
712 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterPostLoad", isoPlayer, player, character, characterData)
715 FrameworkZ.Notifications:AddToQueue("Please wait a few seconds for the map to load.", FrameworkZ.Notifications.Types.Warning)
718 FrameworkZ.Timers:Simple(2, function()
719 if characterData.META_FIRST_LOAD == true then
720 local options = FrameworkZ.Config.Options
722 isoPlayer:setX(options.SpawnX)
723 isoPlayer:setY(options.SpawnY)
724 isoPlayer:setZ(options.SpawnZ)
725 isoPlayer:setLx(options.SpawnX)
726 isoPlayer:setLy(options.SpawnY)
727 isoPlayer:setLz(options.SpawnZ)
729 isoPlayer:setX(characterData.POSITION_X)
730 isoPlayer:setY(characterData.POSITION_Y)
731 isoPlayer:setZ(characterData.POSITION_Z)
732 isoPlayer:setLx(characterData.POSITION_X)
733 isoPlayer:setLy(characterData.POSITION_Y)
734 isoPlayer:setLz(characterData.POSITION_Z)
735 isoPlayer:setDirectionAngle(characterData.DIRECTION_ANGLE)
738 isoPlayer:setInvisible(false)
739 isoPlayer:setGhostMode(false)
740 isoPlayer:setNoClip(false)
742 if VoiceManager:playerGetMute(player:GetUsername()) then
743 VoiceManager:playerSetMute(player:GetUsername())
746 if isClient() and FrameworkZ.UI.MainMenu.instance then
747 FrameworkZ.UI.MainMenu.instance:onClose()
750 FrameworkZ.Timers:Simple(3, function()
751 isoPlayer:setGodMod(false)
752 isoPlayer:setInvincible(false)
755 FrameworkZ.Notifications:AddToQueue("Spawn protection has now been removed.", FrameworkZ.Notifications.Types.Warning)
758 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterFinishedLoading", isoPlayer, player, character, characterData)
765 1. Load equipment/items
768 4. Apply damage/wounds/moodles (if applicable)
776function FrameworkZ.Players:LoadCharacter(username, characterData, survivorDescriptor, loadCharacterStartTime)
777 local player = FrameworkZ.Players:GetPlayerByID(username)
778 if not player or not characterData then return false end
779 local isoPlayer = player.isoPlayer
781 FrameworkZ.Foundation:SendFire(isoPlayer, "FrameworkZ.Characters.PostLoad", function(data, _success, _character)
782 local character = FrameworkZ.Characters.PostLoad({isoPlayer = isoPlayer}, characterData)
784 if not character then
785 FrameworkZ.Notifications:AddToQueue("Failed to load character: Not found.", FrameworkZ.Notifications.Types.Danger, nil, FrameworkZ.UI.MainMenu.instance)
789 character:OnPostLoad(characterData.META_FIRST_LOAD)
791 isoPlayer:clearWornItems()
792 isoPlayer:getInventory():clear()
794 for k, v in pairs(characterData) do
795 if string.match(k, "EQUIPMENT_SLOT_") then
797 local item = isoPlayer:getInventory():AddItem(v.id)
798 isoPlayer:setWornItem(item:getBodyLocation(), item)
803 local isFemale = survivorDescriptor:isFemale()
804 isoPlayer:setFemale(isFemale)
805 isoPlayer:getDescriptor():setFemale(isFemale)
806 isoPlayer:getHumanVisual():clear()
807 isoPlayer:getHumanVisual():copyFrom(survivorDescriptor:getHumanVisual())
808 isoPlayer:resetModel()
810 isoPlayer:setGodMod(false)
811 isoPlayer:setInvincible(false)
813 -- Apply damage/wounds/moodles
815 isoPlayer:setInvisible(false)
816 isoPlayer:setGhostMode(false)
817 isoPlayer:setNoClip(false)
819 if VoiceManager:playerGetMute(username) then
820 VoiceManager:playerSetMute(username)
823 FrameworkZ.Foundation.LoadingNotifications = {} -- TODO store loading notifications and parent them, then remove from parent after main menu is finally closed.
825 FrameworkZ.Notifications:AddToQueue("Please wait a few seconds for the map to load.", FrameworkZ.Notifications.Types.Warning)
827 FrameworkZ.Timers:Simple(1, function()
828 if characterData.META_FIRST_LOAD == true then
829 isoPlayer:setX(FrameworkZ.Config.Options.SpawnX)
830 isoPlayer:setY(FrameworkZ.Config.Options.SpawnY)
831 isoPlayer:setZ(FrameworkZ.Config.Options.SpawnZ)
832 isoPlayer:setLx(FrameworkZ.Config.Options.SpawnX)
833 isoPlayer:setLy(FrameworkZ.Config.Options.SpawnY)
834 isoPlayer:setLz(FrameworkZ.Config.Options.SpawnZ)
836 isoPlayer:setX(characterData.POSITION_X)
837 isoPlayer:setY(characterData.POSITION_Y)
838 isoPlayer:setZ(characterData.POSITION_Z)
839 isoPlayer:setLx(characterData.POSITION_X)
840 isoPlayer:setLy(characterData.POSITION_Y)
841 isoPlayer:setLz(characterData.POSITION_Z)
842 isoPlayer:setDirectionAngle(characterData.DIRECTION_ANGLE)
845 if not self:SaveCharacter(username, characterData) then
846 FrameworkZ.Notifications:AddToQueue("Failed to load character: Not saved.", FrameworkZ.Notifications.Types.Danger, nil, FrameworkZ.UI.MainMenu.instance)
848 FrameworkZ.Foundation:SendFire(isoPlayer, "FrameworkZ.Foundation.TeleportToLimbo", function(success)
850 FrameworkZ.Foundation.TeleportToLimbo({isoPlayer = isoPlayer})
854 FrameworkZ.UI.MainMenu.instance:onClose()
855 FrameworkZ.Notifications:AddToQueue("Loaded character in " .. tostring(string.format(" %.2f", (getTimestampMs() - loadCharacterStartTime) / 1000)) .. " seconds.", FrameworkZ.Notifications.Types.Success)
857 if characterData.META_FIRST_LOAD then
858 characterData.META_FIRST_LOAD = false
867function FrameworkZ.Players.OnLoadCharacter(data, characterID)
870FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.OnLoadCharacter", FrameworkZ.Players.OnLoadCharacter)
873function FrameworkZ.Players:DeleteCharacter(username, character)
877function FrameworkZ.Players:DeleteCharacterByID(username, characterID)
881function FrameworkZ.Players:OnInitGlobalModData(isNewGame)
882 FrameworkZ.Foundation:RegisterNamespace("Players")
885function FrameworkZ.Players:OnStorageSet(isoPlayer, command, namespace, keys, value)
886 if namespace == "Players" then
887 if command == "Initialize" then
888 local username = keys
890 local player = self:GetPlayerByID(username) if not player then return end
893 for k, v in pairs(data) do
894 if data[k] and player[k] then
895 player[k] = data[k] -- Restore saved data on default fields
896 elseif data[k] and not player[k] then
897 player[k] = data[k] -- Restore saved custom data
901 for k, v in pairs(player) do
902 if player[k] and not data[k] then
903 data[k] = player[k] -- Add new data fields from default fields (probably from an update)
907 --FrameworkZ.Foundation:GetData(isoPlayer, "Initialize", "Characters", username)
913function FrameworkZ.Players:OnFillWorldObjectContextMenu(playerNumber, context, worldObjects, test)
914 worldObjects = FrameworkZ.Utilities:RemoveContextDuplicates(worldObjects)
918 local isoPlayer = getSpecificPlayer(playerNumber)
919 local inventory = isoPlayer:getInventory()
921 local menuManager = MenuManager.new(context)
922 local interactSubMenu = menuManager:addSubMenu("Interact") -- FZ specific interactions
923 local inspectSubMenu = menuManager:addSubMenu("Inspect") -- FZ flavour text
924 local manageSubMenu = menuManager:addSubMenu("Manage") -- Pickup, dissassemble, grab
925 local adminSubMenu = menuManager:addSubMenu("(ADMIN)") -- Admin/debug stuff
927 for _, v in pairs(worldObjects) do
928 if instanceof(v, "IsoDoor") or (instanceof(v, "IsoThumpable") and v:isDoor()) then
929 local lockUnlockOption
930 local closeOpenOption
933 if instanceof(v, "IsoDoor") and v:checkKeyId() ~= -1 then
934 keyID = v:checkKeyId()
939 if instanceof(v, "IsoThumpable") and v:getKeyId() ~= -1 then
940 keyID = v:getKeyId();
944 if not
v:isLockedByKey() then
945 lockUnlockOption = menuManager:addOption(
Options.new(
"Lock Door",
self, function(target, parameters)
946 ISWorldObjectContextMenu.onLockDoor(worldObjects, playerNumber,
v)
947 end), interactSubMenu)
949 lockUnlockOption = menuManager:addOption(
Options.new(
"Unlock Door",
self, function(target, parameters)
950 ISWorldObjectContextMenu.onUnLockDoor(worldObjects, playerNumber,
v)
951 end), interactSubMenu)
955 if not
v:isBarricaded() then
957 closeOpenOption = menuManager:addOption(
Options.new(
"Close Door",
self, function(target, parameters)
958 ISWorldObjectContextMenu.onOpenCloseDoor(worldObjects,
v, playerNumber)
959 end), interactSubMenu)
961 closeOpenOption = menuManager:addOption(
Options.new(
"Open Door",
self, function(target, parameters)
962 ISWorldObjectContextMenu.onOpenCloseDoor(worldObjects,
v, playerNumber)
963 end), interactSubMenu)
967 local tooltip = ISWorldObjectContextMenu.addToolTip()
968 tooltip:setName(
"The door is " .. (
v:IsOpen() and
"open" or
"closed") ..
" and " .. (
v:isLockedByKey() and
"locked" or
"unlocked") ..
".")
969 tooltip:setTexture(
v:getTextureName())
970 tooltip.
description =
"Open/Close Door:\n" .. getText(
"Tooltip_OpenClose", getKeyName(getCore():getKey(
"Interact")))
971 closeOpenOption.toolTip = tooltip
973 if lockUnlockOption then
974 lockUnlockOption.toolTip = tooltip
977 local climbThroughOption
978 local lockUnlockOption
979 local closeOpenOption
982 if
v:getKeyId() ~= -1 then
983 keyID =
v:checkKeyId()
989 climbThroughOption = menuManager:addOption(
Options.new(
"Climb Through Window",
self, function(target, parameters)
990 ISWorldObjectContextMenu.onClimbThroughWindow(worldObjects,
v, playerNumber)
991 end), interactSubMenu)
995 if not
v:isLocked() then
996 local lockWindow = function()
1002 lockUnlockOption = menuManager:addOption(
Options.
new(
"Lock Window",
self, function(target, parameters)
1004 end), interactSubMenu)
1006 local unlockWindow = function()
1012 lockUnlockOption = menuManager:addOption(
Options.new("Unlock Window",
self, function(target, parameters)
1014 end), interactSubMenu)
1018 if not
v:isBarricaded() then
1020 closeOpenOption = menuManager:addOption(
Options.new("Close Window",
self, function(target, parameters)
1021 ISWorldObjectContextMenu.onOpenCloseWindow(worldObjects,
v, playerNumber)
1022 end), interactSubMenu)
1024 closeOpenOption = menuManager:addOption(
Options.new("Open Window",
self, function(target, parameters)
1025 ISWorldObjectContextMenu.onOpenCloseWindow(worldObjects,
v, playerNumber)
1026 end), interactSubMenu)
1030 local tooltip = ISWorldObjectContextMenu.addToolTip()
1031 tooltip:setName("The window" .. (
v:isSmashed() and " (smashed) " or " ") .. "is " .. (
v:IsOpen() and "open" or "closed") .. " and " .. (
v:isLocked() and "locked" or "unlocked") .. ".")
1032 tooltip:setTexture(
v:getTextureName())
1033 tooltip.
description = "Open/Close Window:\n" .. getText("Tooltip_TapKey", getKeyName(getCore():getKey("Interact"))) .. "\n" ..
1034 "Climb Through Window:\n" .. getText("Tooltip_Climb", getKeyName(getCore():getKey("Interact")))
1035 closeOpenOption.toolTip = tooltip
1037 if climbThroughOption then
1038 climbThroughOption.toolTip = tooltip
1041 if lockUnlockOption then
1042 lockUnlockOption.toolTip = tooltip
1047 menuManager:buildMenu()
1049 if interactSubMenu:getContext():
isEmpty() then
1050 menuManager:addOption(
Options.new("No Interactions Available"), interactSubMenu)
1053 if inspectSubMenu:getContext():
isEmpty() then
1054 menuManager:addOption(
Options.new("No Inspections Available"), inspectSubMenu)
1057 if manageSubMenu:getContext():
isEmpty() then
1058 menuManager:addOption(
Options.new("No Management
Options Available"), manageSubMenu)
1061 if adminSubMenu:getContext():
isEmpty() then
1062 menuManager:addOption(
Options.new("No Admin Actions Available"), adminSubMenu)
void ISTimedActionQueue()
void self FrameworkZ UI self nil
void initializedNewData()
void self previousCharacter()
void characterModData role()
void self maxCharacters()
void FrameworkZ Foundation()
Characters module for FrameworkZ. Defines and interacts with CHARACTER object.
FrameworkZ Characters List
Foundation for FrameworkZ.
void TeleportToLimbo(isoPlayer)
Players module for FrameworkZ. Defines and interacts with PLAYER object.
FrameworkZ Players Roles
Roles for players in FrameworkZ.
FrameworkZ Players List
List of all instanced players in the game.
void new(text, target, callback, callbackParameters, addOnTop, useMultiple, count)
Player class for FrameworkZ.
void SetCustomData(customData)
void SetPreviousCharacter(previousCharacter)
void SetUsername(username)
void InitializeDefaultFactionWhitelists()
void SetWhitelists(whitelists)
void SetMaxCharacters(maxCharacters)
boolean Save(shouldTransmit)
Saves the player's data.
void StopSound(soundNameOrID)
Stops a sound for the player.
void SetIsoPlayer(isoPlayer)
void SetCharacter(character)
mixed Destroy()
Destroys the player object.
integer PlayLocalSound(soundName)
Plays a sound for the player that only they can hear.
void SetModel(characterData)
table GetStoredData()
Gets the stored player mod data table. Used internally. Do not use this unless you know what you are ...
void GetPreviousCharacter()
void SetCharacters(characters)
void LoadCharacter(characterID)
string Initialize()
Initializes the player object.
void SetWhitelisted(factionID, whitelisted)
void ValidatePlayerData()
void IsWhitelisted(factionID)
void GetCharacterDataByID(characterID)