FrameworkZ 10.8.3
Provides a framework for Project Zomboid with various systems.
Loading...
Searching...
No Matches
Players.lua
Go to the documentation of this file.
1--! \page global_variables Global Variables
2--! \section Players Players
3--! FrameworkZ.Players\n
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.
7
8local getPlayer = getPlayer
9local isClient = isClient
10
11FrameworkZ = FrameworkZ or {}
12
13--! \brief Players module for FrameworkZ. Defines and interacts with PLAYER object.
14--! \class FrameworkZ.Players
15FrameworkZ.Players = {}
16
17--! \brief List of all instanced players in the game.
18FrameworkZ.Players.List = {}
19
20--! \brief Roles for players in FrameworkZ.
21FrameworkZ.Players.Roles = {
22 User = "User",
23 Operator = "Operator",
24 Moderator = "Moderator",
25 Admin = "Admin",
26 Super_Admin = "Super Admin",
27 Owner = "Owner"
28}
29FrameworkZ.Players = FrameworkZ.Foundation:NewModule(FrameworkZ.Players, "Players")
30
31--! \class PLAYER
32--! \brief Player class for FrameworkZ.
33local PLAYER = {}
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
40
41 self:InitializeDefaultFactionWhitelists()
42end
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
52
53 local playerData = self:GetStoredData()
54
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()
65 end
66
67 return true
68end
69
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
74
75 local username = self:GetUsername()
76 local success1, success2, message
78 if FrameworkZ.Players.List[username] then
79 success1, message = FrameworkZ.Players:Save(username)
80 end
82 if FrameworkZ.Characters.List[username] then
83 FrameworkZ.Characters.List[username] = nil
84 end
86 if FrameworkZ.Players.List[username] then
87 FrameworkZ.Players.List[username] = nil
88 end
90 if success1 and success2 then
91 return true, message
92 end
94 return false, message
95end
96
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
103 end
104 end
105end
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)
113end
115function PLAYER:GetRole()
116 return self.role
117end
119function PLAYER:SetRole(role)
120 print("Failed to set Role to: '" .. tostring(role) .. "'. Role is read-only and must be set upon object creation.")
121end
122
123function PLAYER:GetPreviousCharacter()
124 return self.previousCharacter
125end
126
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.")
130 return false
131 end
133 self.previousCharacter = previousCharacter
135 return true
136end
137
138function PLAYER:GetMaxCharacters()
139 return self.maxCharacters
140end
141
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.")
145 return false
146 end
147
148 if maxCharacters < 1 then
149 print("Failed to set Max Characters to: '" .. tostring(maxCharacters) .. "'. Max Characters must be at least 1.")
150 return false
151 end
153 self.maxCharacters = maxCharacters
155 return true
156end
157
158function PLAYER:GetCustomData()
159 return self.CustomData
160end
161
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.")
165 return false
166 end
167
168 self.CustomData = customData
169
170 return true
171end
173function PLAYER:GetCharacter()
174 return self.LoadedCharacter
175end
177function PLAYER:SetCharacter(character)
178 self.LoadedCharacter = character
179end
181function PLAYER:GetCharacters()
182 return self.Characters
183end
184
185function PLAYER:SetCharacters(characters)
186 self.Characters = characters
187end
188
189function PLAYER:GetCharacterDataByID(characterID)
190 if not characterID then return false, "Missing character ID." end
192 local character = self.Characters[characterID]
194 if character then
195 return character, "Successfully retrieved character data."
196 end
198 return false, "Character not found."
199end
201function PLAYER:GetUsername()
202 return self.Username
203end
205function PLAYER:SetUsername(username)
206 print("Failed to set Username to: '" .. tostring(username) .. "'. Username is read-only and must be set upon object creation.")
207end
208
209function PLAYER:GetIsoPlayer()
210 return self.IsoPlayer
211end
213function PLAYER:SetIsoPlayer(isoPlayer)
214 print("Failed to set IsoPlayer to: '" .. tostring(isoPlayer) .. "'. IsoPlayer is read-only and must be set upon object creation.")
215end
217function PLAYER:GetSaveableData()
218 return FrameworkZ.Foundation:ProcessSaveableData(self, {"IsoPlayer", "Characters", "LoadedCharacter"})
219end
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"]
225end
226
227function PLAYER:GetWhitelists()
228 return self.whitelists
229end
230
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.")
234 return false
235 end
236
237 self.whitelists = whitelists
238
239 return true
240end
241
242function PLAYER:SetWhitelisted(factionID, whitelisted)
243 if not factionID then return false end
244
245 self.whitelists[factionID] = whitelisted
246 self:GetStoredData().whitelists[factionID] = whitelisted
247
248 return true
249end
250
251function PLAYER:IsWhitelisted(factionID)
252 if not factionID then return false end
253
254 return self.whitelists[factionID] or false
255end
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)
262end
263
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)
271 end
272end
273
274--[[
275function PLAYER:ValidatePlayerData()
276 local characterModData = self.isoPlayer:getModData()["FZ_PLY"]
277
278 if not characterModData then return false end
279
280 local initializedNewData = false
281
282 if not characterModData.username then
283 initializedNewData = true
284 characterModData.username = self.username or getPlayer():getUsername()
285 end
287 if not characterModData.steamID then
288 initializedNewData = true
289 characterModData.steamID = self.steamID or getPlayer():getSteamID()
290 end
291
292 if not characterModData.role then
293 initializedNewData = true
294 characterModData.role = self.role or FrameworkZ.Players.Roles.User
295 end
297 if not characterModData.maxCharacters then
298 initializedNewData = true
299 characterModData.maxCharacters = self.maxCharacters or FrameworkZ.Config.Options.DefaultMaxCharacters
300 end
301
302 if not characterModData.previousCharacter then
303 initializedNewData = true
304 characterModData.previousCharacter = self.previousCharacter or nil
305 end
307 if not characterModData.whitelists then
308 self:InitializeDefaultFactionWhitelists()
309 initializedNewData = true
310 characterModData.whitelists = self.whitelists
311 end
313 if not characterModData.Characters then
314 initializedNewData = true
315 characterModData.Characters = self.Characters or {}
316 end
317
318 if isClient() then
319 self.isoPlayer:transmitModData()
320 end
321
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
329
330 return initializedNewData
331end
332--]]
333
334function FrameworkZ.Players:New(isoPlayer)
335 if not isoPlayer then return false end
337 -- TODO Reminder: Update FrameworkZ.Players:OnStorageSet() when updating here.
338 local object = {
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,
346 whitelists = {},
347 Characters = {},
348 CustomData = {}
349 }
351 setmetatable(object, PLAYER)
353 return object
354end
355
356function FrameworkZ.Players:Initialize(isoPlayer)
357 local player = FrameworkZ.Players:New(isoPlayer) if not player then return false end
358 local username = player:GetUsername()
359 player:Initialize()
360
361 self.List[username] = player
362 self:StartPlayerTick(player)
363
364 return self.List[username]
365end
366
367function FrameworkZ.Players:StartPlayerTick(player)
368 if not isClient() then return end
369
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)
373 end)
374end
375
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
382
383 return self.List[username]
384end
385
386function FrameworkZ.Players:GetLoadedCharacterByID(username)
387 if not username then return false end
388
389 local player = self:GetPlayerByID(username)
390
391 if player then
392 return player:GetCharacter() or false
393 end
394
395 return false
396end
397
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
405
406 local player = FrameworkZ.Players:GetPlayerByID(username) if not player then return false, "Player not found." end
407
408 return player:GetCharacterDataByID(characterID)
409end
410
411function FrameworkZ.Players:GetNextCharacterID(username)
412 if not username then return false end
413
414 local player = self:GetPlayerByID(username)
415
416 if player then
417 local nextID = #player.Characters + 1
418
419 if nextID > player.maxCharacters then
420 return false, "Max characters reached."
421 end
422
423 return nextID
424 end
425
426 return false, "Player not found."
427end
428
429function FrameworkZ.Players:ResetCharacterSaveInterval()
430 if FrameworkZ.Timers:Exists("FZ_CharacterSaveInterval") then
431 FrameworkZ.Timers:Start("FZ_CharacterSaveInterval")
432 end
433end
434
435function PLAYER:GenerateUID()
436 local username = self:GetUsername()
437 local characters = self:GetCharacters() if not characters then return false end
438
439 function CreateUID()
440 local uid = username .. "_" .. FrameworkZ.Utilities:GetRandomNumber(1, 999999, true)
441
442 for k, v in pairs(characters) do
443 if v.META_UID == uid then
444 return CreateUID()
445 end
446 end
447
448 return uid
449 end
450
451 return CreateUID()
452end
453
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
457
458 return FrameworkZ.Players:CreateCharacter(username, characterData)
459end
460FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.OnCreateCharacter", FrameworkZ.Players.OnCreateCharacter)
461
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
465
466 local player = self:GetPlayerByID(username)
467
468 if player and player.Characters then
469 if isClient() then
470 FrameworkZ.Players:ResetCharacterSaveInterval()
471 end
472
473 characterData.META_ID = characterID and characterID or #player.Characters + 1
474 characterData.META_FIRST_LOAD = true
475
476 if not characterID then
477 table.insert(player.Characters, characterData)
478 else
479 player.Characters[characterID] = characterData
480 end
481
482 characterData.META_UID = player:GenerateUID()
483
484 if isServer() then
485 FrameworkZ.Foundation:SetData(nil, "Characters", {username, characterData.META_ID}, characterData)
486 end
487
488 return characterData.META_ID
489 end
490
491 return false, "Player not found."
492end
493
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
501
502 local player = FrameworkZ.Players:GetPlayerByID(username)
503
504 if not player then return false end
505
506 local saved = false
507 local failureMessage = ""
508 local character = player.LoadedCharacter
509 local characterSaved = false
510 local playerSaved = player:Save(false)
511 saved = playerSaved
512
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."
517 end
518
519 if character then
520 characterSaved = character:Save(false)
521 saved = characterSaved
522
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."
527 end
528 else
529 characterSaved = true -- No character loaded, set true to prevent returning false.
530 end
531
532 if isClient() then
533 player:GetIsoPlayer():transmitModData()
534 end
535
536 if playerSaved and characterSaved then
537 saved = true
538 else
539 saved = false
540 end
541
542 return saved, failureMessage
543end
544
545function FrameworkZ.Players:Destroy(username)
546 local properlyDestroyed = false
547 local message = "Failed to destroy player."
548 local player = self:GetPlayerByID(username)
549
550 if player then
551 properlyDestroyed, message = player:Destroy()
552 end
553
554 return properlyDestroyed, message
555end
556
557function FrameworkZ.Players:SaveCharacter(username, character)
558 local player = FrameworkZ.Players:GetPlayerByID(username)
559
560 if not player or not character then return false end
561
562 local isoPlayer = player:GetIsoPlayer()
563
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()})
568 end
569
570 character.INVENTORY_LOGICAL = FrameworkZ.Characters:GetCharacterInventoryByID(username).items
571
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
584
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()
590
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()
603
604 --[[
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()
612 --]]
613
614 if isClient() then
615 isoPlayer:transmitModData()
616 end
617
618 return true
619end
620
621function FrameworkZ.Players:SaveCharacterByID(username, characterID)
622
623end
624
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()
629
630 local isFemale = characterData.INFO_GENDER == "Female" or not characterData.INFO_GENDER == "Male"
631 isoPlayer:setFemale(isFemale)
632 isoPlayer:getDescriptor():setFemale(isFemale)
633
634 local hairColor = characterData.INFO_HAIR_COLOR
635 local beardColor = characterData.INFO_BEARD_COLOR
636 local visual = isoPlayer:getHumanVisual()
637 visual:clear()
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)
645
646 isoPlayer:resetModel()
647end
648
649function PLAYER:LoadCharacter(characterID)
650 if not characterID then return false end
651
652 FrameworkZ.Players:OnLoadCharacter(self:GetUsername(), characterID)
653end
654
655function FrameworkZ.Players:LoadCharacterByID(username, characterID)
656 local clientLoadCharacter = function(_data, success, message)
657 if not success then
658 FrameworkZ.Notifications:AddToQueue("Failed to load character: " .. message, FrameworkZ.Notifications.Types.Danger, nil, FrameworkZ.UI.MainMenu.instance)
659 return
660 end
661
662 self:OnLoadCharacter(username, characterID)
663 end
664
665 FrameworkZ.Foundation:SendFire(FrameworkZ.Players:GetPlayerByID(username):GetIsoPlayer(), "FrameworkZ.Players.LoadCharacter", clientLoadCharacter, username, characterID)
666end
667
668if isServer() then
669 function FrameworkZ.Players.LoadCharacter(_data, username, characterID)
670 return FrameworkZ.Players:OnLoadCharacter(username, characterID)
671 end
672 FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.LoadCharacter", FrameworkZ.Players.LoadCharacter)
673end
674
675function FrameworkZ.Players:OnLoadCharacter(username, characterID)
676 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterLoaded", username, characterID)
677
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()
682
683 player:SetCharacter(character)
684
685 FrameworkZ.Players:OnPreLoadCharacter(isoPlayer, player, character, characterData)
686 FrameworkZ.Players:OnPostLoadCharacter(isoPlayer, player, character, characterData)
687
688 return true, "Successfully loaded character."
689end
690
691function FrameworkZ.Players:OnPreLoadCharacter(isoPlayer, player, character, characterData)
692 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterPreLoad", isoPlayer, player, character, characterData)
693
694 isoPlayer:clearWornItems()
695 isoPlayer:getInventory():clear()
696
697 for k, v in pairs(characterData) do
698 if string.match(k, "EQUIPMENT_SLOT_") then
699 if v and v.id then
700 local item = isoPlayer:getInventory():AddItem(v.id)
701 isoPlayer:setWornItem(item:getBodyLocation(), item)
702 end
703 end
704 end
705
706 player:SetModel(characterData)
707
708 -- Apply damage/wounds/moodles
709end
710
711function FrameworkZ.Players:OnPostLoadCharacter(isoPlayer, player, character, characterData)
712 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterPostLoad", isoPlayer, player, character, characterData)
713
714 if isClient() then
715 FrameworkZ.Notifications:AddToQueue("Please wait a few seconds for the map to load.", FrameworkZ.Notifications.Types.Warning)
716 end
717
718 FrameworkZ.Timers:Simple(2, function()
719 if characterData.META_FIRST_LOAD == true then
720 local options = FrameworkZ.Config.Options
721
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)
728 else
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)
736 end
737
738 isoPlayer:setInvisible(false)
739 isoPlayer:setGhostMode(false)
740 isoPlayer:setNoClip(false)
741
742 if VoiceManager:playerGetMute(player:GetUsername()) then
743 VoiceManager:playerSetMute(player:GetUsername())
744 end
745
746 if isClient() and FrameworkZ.UI.MainMenu.instance then
747 FrameworkZ.UI.MainMenu.instance:onClose()
748 end
749
750 FrameworkZ.Timers:Simple(3, function()
751 isoPlayer:setGodMod(false)
752 isoPlayer:setInvincible(false)
753
754 if isClient() then
755 FrameworkZ.Notifications:AddToQueue("Spawn protection has now been removed.", FrameworkZ.Notifications.Types.Warning)
756 end
757
758 FrameworkZ.Foundation:ExecuteAllHooks("OnCharacterFinishedLoading", isoPlayer, player, character, characterData)
759 end)
760 end)
761end
762
763--[[
764 Steps:
765 1. Load equipment/items
766 2. Teleport
767 3. Ungod
768 4. Apply damage/wounds/moodles (if applicable)
769 5. Make visible
770 6. Unmute
771 7. Save
772 8. Post load
773 9. Return true
774--]]
775--[[
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
780
781 FrameworkZ.Foundation:SendFire(isoPlayer, "FrameworkZ.Characters.PostLoad", function(data, _success, _character)
782 local character = FrameworkZ.Characters.PostLoad({isoPlayer = isoPlayer}, characterData)
783
784 if not character then
785 FrameworkZ.Notifications:AddToQueue("Failed to load character: Not found.", FrameworkZ.Notifications.Types.Danger, nil, FrameworkZ.UI.MainMenu.instance)
786 return
787 end
788
789 character:OnPostLoad(characterData.META_FIRST_LOAD)
790
791 isoPlayer:clearWornItems()
792 isoPlayer:getInventory():clear()
793
794 for k, v in pairs(characterData) do
795 if string.match(k, "EQUIPMENT_SLOT_") then
796 if v and v.id then
797 local item = isoPlayer:getInventory():AddItem(v.id)
798 isoPlayer:setWornItem(item:getBodyLocation(), item)
799 end
800 end
801 end
802
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()
809
810 isoPlayer:setGodMod(false)
811 isoPlayer:setInvincible(false)
812
813 -- Apply damage/wounds/moodles
814
815 isoPlayer:setInvisible(false)
816 isoPlayer:setGhostMode(false)
817 isoPlayer:setNoClip(false)
818
819 if VoiceManager:playerGetMute(username) then
820 VoiceManager:playerSetMute(username)
821 end
822
823 FrameworkZ.Foundation.LoadingNotifications = {} -- TODO store loading notifications and parent them, then remove from parent after main menu is finally closed.
824
825 FrameworkZ.Notifications:AddToQueue("Please wait a few seconds for the map to load.", FrameworkZ.Notifications.Types.Warning)
826
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)
835 else
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)
843 end
844
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)
847
848 FrameworkZ.Foundation:SendFire(isoPlayer, "FrameworkZ.Foundation.TeleportToLimbo", function(success)
849 if success then
850 FrameworkZ.Foundation.TeleportToLimbo({isoPlayer = isoPlayer})
851 end
852 end)
853 else
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)
856
857 if characterData.META_FIRST_LOAD then
858 characterData.META_FIRST_LOAD = false
859 end
860 end
861 end)
862 end, characterData)
863end
864--]]
865
866--[[
867function FrameworkZ.Players.OnLoadCharacter(data, characterID)
868 return true
869end
870FrameworkZ.Foundation:Subscribe("FrameworkZ.Players.OnLoadCharacter", FrameworkZ.Players.OnLoadCharacter)
871--]]
872
873function FrameworkZ.Players:DeleteCharacter(username, character)
874
875end
876
877function FrameworkZ.Players:DeleteCharacterByID(username, characterID)
878
879end
880
881function FrameworkZ.Players:OnInitGlobalModData(isNewGame)
882 FrameworkZ.Foundation:RegisterNamespace("Players")
883end
884
885function FrameworkZ.Players:OnStorageSet(isoPlayer, command, namespace, keys, value)
886 if namespace == "Players" then
887 if command == "Initialize" then
888 local username = keys
889 local data = value
890 local player = self:GetPlayerByID(username) if not player then return end
891
892 if data then
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
898 end
899 end
900
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)
904 end
905 end
906
907 --FrameworkZ.Foundation:GetData(isoPlayer, "Initialize", "Characters", username)
908 end
909 end
910 end
911end
912
913function FrameworkZ.Players:OnFillWorldObjectContextMenu(playerNumber, context, worldObjects, test)
914 worldObjects = FrameworkZ.Utilities:RemoveContextDuplicates(worldObjects)
915
916 context:clear()
917
918 local isoPlayer = getSpecificPlayer(playerNumber)
919 local inventory = isoPlayer:getInventory()
920
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
926
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
931 local keyID
932
933 if instanceof(v, "IsoDoor") and v:checkKeyId() ~= -1 then
934 keyID = v:checkKeyId()
935 else
936 keyID = nil
937 end
938
939 if instanceof(v, "IsoThumpable") and v:getKeyId() ~= -1 then
940 keyID = v:getKeyId();
941 end
942
943 if keyID and inventory:haveThisKeyId(keyID) or FrameworkZ.Utilities:IsTrulyInterior(isoPlayer:getSquare()) then
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)
948 else
949 lockUnlockOption = menuManager:addOption(Options.new("Unlock Door", self, function(target, parameters)
950 ISWorldObjectContextMenu.onUnLockDoor(worldObjects, playerNumber, v)
951 end), interactSubMenu)
952 end
953 end
954
955 if not v:isBarricaded() then
956 if v:IsOpen() then
957 closeOpenOption = menuManager:addOption(Options.new("Close Door", self, function(target, parameters)
958 ISWorldObjectContextMenu.onOpenCloseDoor(worldObjects, v, playerNumber)
959 end), interactSubMenu)
960 else
961 closeOpenOption = menuManager:addOption(Options.new("Open Door", self, function(target, parameters)
962 ISWorldObjectContextMenu.onOpenCloseDoor(worldObjects, v, playerNumber)
963 end), interactSubMenu)
964 end
965 end
966
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
972
973 if lockUnlockOption then
974 lockUnlockOption.toolTip = tooltip
975 end
976 elseif instanceof(v, "IsoWindow") then
977 local climbThroughOption
978 local lockUnlockOption
979 local closeOpenOption
980 local keyID
981
982 if v:getKeyId() ~= -1 then
983 keyID = v:checkKeyId()
984 else
985 keyID = nil
986 end
987
988 if v:canClimbThrough(isoPlayer) then
989 climbThroughOption = menuManager:addOption(Options.new("Climb Through Window", self, function(target, parameters)
990 ISWorldObjectContextMenu.onClimbThroughWindow(worldObjects, v, playerNumber)
991 end), interactSubMenu)
992 end
993
994 if keyID and inventory:haveThisKeyId(keyID) or FrameworkZ.Utilities:IsTrulyInterior(isoPlayer:getSquare()) then
995 if not v:isLocked() then
996 local lockWindow = function()
997 if luautils.walkAdjWindowOrDoor(isoPlayer, v:getSquare(), v) then
999 end
1000 end
1001
1002 lockUnlockOption = menuManager:addOption(Options.new("Lock Window", self, function(target, parameters)
1003 lockWindow()
1004 end), interactSubMenu)
1005 else
1006 local unlockWindow = function()
1007 if luautils.walkAdjWindowOrDoor(isoPlayer, v:getSquare(), v) then
1009 end
1010 end
1011
1012 lockUnlockOption = menuManager:addOption(Options.new("Unlock Window", self, function(target, parameters)
1013 unlockWindow()
1014 end), interactSubMenu)
1015 end
1016 end
1017
1018 if not v:isBarricaded() then
1019 if v:IsOpen() then
1020 closeOpenOption = menuManager:addOption(Options.new("Close Window", self, function(target, parameters)
1021 ISWorldObjectContextMenu.onOpenCloseWindow(worldObjects, v, playerNumber)
1022 end), interactSubMenu)
1023 else
1024 closeOpenOption = menuManager:addOption(Options.new("Open Window", self, function(target, parameters)
1025 ISWorldObjectContextMenu.onOpenCloseWindow(worldObjects, v, playerNumber)
1026 end), interactSubMenu)
1027 end
1028 end
1029
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
1036
1037 if climbThroughOption then
1038 climbThroughOption.toolTip = tooltip
1039 end
1040
1041 if lockUnlockOption then
1042 lockUnlockOption.toolTip = tooltip
1043 end
1044 end
1045 end
1046
1047 menuManager:buildMenu()
1048
1049 if interactSubMenu:getContext():isEmpty() then
1050 menuManager:addOption(Options.new("No Interactions Available"), interactSubMenu)
1051 end
1052
1053 if inspectSubMenu:getContext():isEmpty() then
1054 menuManager:addOption(Options.new("No Inspections Available"), inspectSubMenu)
1055 end
1056
1057 if manageSubMenu:getContext():isEmpty() then
1058 menuManager:addOption(Options.new("No Management Options Available"), manageSubMenu)
1059 end
1060
1061 if adminSubMenu:getContext():isEmpty() then
1062 menuManager:addOption(Options.new("No Admin Actions Available"), adminSubMenu)
1063 end
1064end
1065
1066FrameworkZ.Foundation:RegisterModule(FrameworkZ.Players)
void context()
void description()
void ISTimedActionQueue()
void local instanceof()
void self FrameworkZ UI self nil
Definition MainMenu.lua:95
void self self
Definition MainMenu.lua:89
void local player()
void characterSaved()
void self whitelists()
void local getStats()
void local isFemale()
void local item()
void local isClient()
void local options()
void self username()
void local getPlayer()
void saved()
void initializedNewData()
void self previousCharacter()
void failureMessage()
void characterModData role()
void FrameworkZ()
void self maxCharacters()
void local getSquare()
void local isEmpty()
void if type v()
void local data()
void FrameworkZ Foundation()
void elseif command()
void isoPlayer()
void local characters()
void value()
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.
Definition Players.lua:13
FrameworkZ Players Roles
Roles for players in FrameworkZ.
Definition Players.lua:23
FrameworkZ Players List
List of all instanced players in the game.
Definition Players.lua:18
void new(text, target, callback, callbackParameters, addOnTop, useMultiple, count)
Player class for FrameworkZ.
Definition Players.lua:112
void SetCustomData(customData)
void GetCharacter()
void GenerateUID()
void SetPreviousCharacter(previousCharacter)
void GetRole()
void GetCharacters()
void SetUsername(username)
void GetUsername()
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 GetWhitelists()
PLAYER __index
Definition Players.lua:114
void SetCharacter(character)
void GetMaxCharacters()
void GetIsoPlayer()
mixed Destroy()
Destroys the player object.
integer PlayLocalSound(soundName)
Plays a sound for the player that only they can hear.
void SetModel(characterData)
void GetSaveableData()
table GetStoredData()
Gets the stored player mod data table. Used internally. Do not use this unless you know what you are ...
void GetPreviousCharacter()
void RestoreData(data)
void SetCharacters(characters)
void LoadCharacter(characterID)
string Initialize()
Initializes the player object.
void SetWhitelisted(factionID, whitelisted)
void ValidatePlayerData()
void IsWhitelisted(factionID)
void SetRole(role)
void GetCustomData()
void GetCharacterDataByID(characterID)