This commit is contained in:
Ggafrik 2025-07-03 01:48:06 +08:00
parent 8f0b963cb9
commit 9900516b10
25 changed files with 628 additions and 630 deletions

BIN
excel/enemy.xlsx Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,90 +0,0 @@
import os
import json
import openpyxl
import re
excel_dir = 'excel'
output_dir = 'src/ReplicatedStorage/Data'
if not os.path.exists(output_dir):
os.makedirs(output_dir)
def parse_array_field(value, elem_type):
"""
解析数组类型字段支持[1,2,3]或1,2,3写法自动去除空格和空字符串
elem_type: 'int', 'float', 'string'
"""
if value is None:
return []
s = str(value).strip()
# 支持带中括号写法
if s.startswith("[") and s.endswith("]"):
s = s[1:-1]
# 支持英文逗号和中文逗号
items = re.split(r'[,]', s)
result = []
for v in items:
v = v.strip()
if v == "":
continue
if elem_type == "int":
try:
result.append(int(v))
except Exception:
pass
elif elem_type == "float":
try:
result.append(float(v))
except Exception:
pass
else:
result.append(v)
return result
for filename in os.listdir(excel_dir):
if filename.endswith('.xlsx'):
filepath = os.path.join(excel_dir, filename)
wb = openpyxl.load_workbook(filepath)
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
rows = list(ws.iter_rows(values_only=True))
if len(rows) < 2:
continue
headers = rows[0]
types = rows[1]
# 只保留字段名和类型都不为空的字段
valid_indices = [
i for i, (h, t) in enumerate(zip(headers, types))
if h is not None and str(h).strip() != "" and t is not None and str(t).strip() != ""
]
valid_headers = [headers[i] for i in valid_indices]
valid_types = [types[i] for i in valid_indices]
data = []
for row in rows[2:]:
filtered_row = [row[i] if i < len(row) else None for i in valid_indices]
row_dict = {}
for h, t, v in zip(valid_headers, valid_types, filtered_row):
if t.endswith("[]"):
elem_type = t[:-2]
row_dict[h] = parse_array_field(v, elem_type)
else:
row_dict[h] = v
id_value = row_dict.get("id")
# 只导出id为数字且不为空的行
if id_value is not None and isinstance(id_value, (int, float)) and str(int(id_value)).isdigit():
data.append(row_dict)
if not data:
continue
out_name = f"{sheet_name}.json"
out_path = os.path.join(output_dir, out_name)
# 写入json每个对象单独一行
with open(out_path, 'w', encoding='utf-8') as f:
f.write('[\n')
for i, obj in enumerate(data):
line = json.dumps(obj, ensure_ascii=False, separators=(',', ':'))
if i < len(data) - 1:
f.write(line + ',\n')
else:
f.write(line + '\n')
f.write(']')
print(f"导出: {out_path}")

BIN
excel/item.xlsx Normal file

Binary file not shown.

BIN
excel/level.xlsx Normal file

Binary file not shown.

BIN
excel/player.xlsx Normal file

Binary file not shown.

View File

@ -44,7 +44,8 @@ def parse_array_field(value, elem_type):
for filename in os.listdir(excel_dir):
if filename.endswith('.xlsx'):
filepath = os.path.join(excel_dir, filename)
wb = openpyxl.load_workbook(filepath)
# 用 data_only=True 读取公式的值
wb = openpyxl.load_workbook(filepath, data_only=True)
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
rows = list(ws.iter_rows(values_only=True))

View File

@ -0,0 +1,5 @@
[
{"id":1,"type":1,"name":1,"atk":10,"hp":100,"model":"Thief"},
{"id":2,"type":1,"name":2,"atk":30,"hp":300,"model":"Thief"},
{"id":1000,"type":2,"name":1000,"atk":50,"hp":1000,"model":"Thief"}
]

View File

@ -1,3 +1,3 @@
[
{"id":1,"type":1,"name":1,"attributes":[1,20,2,20]}
{"id":1,"type":1,"name":1,"attributes":[1,20,2,20],"recycle":10}
]

View File

@ -0,0 +1,4 @@
[
{"id":1,"type":1,"typeArgs":[],"quality":4,"iconId":1,"nameId":10001,"textId":20001,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null},
{"id":2,"type":1,"typeArgs":[],"quality":4,"iconId":2,"nameId":10002,"textId":20002,"buyPrice":[],"sellPrice":[],"use":[],"showPackage":null}
]

View File

@ -0,0 +1,22 @@
[
{"id":1,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":2,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":3,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":4,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":5,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
{"id":6,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":7,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":8,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":9,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":10,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
{"id":11,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":12,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":13,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":14,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":15,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]},
{"id":16,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":17,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":18,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2]},
{"id":19,"type":1,"timeLimit":null,"atkBonus":1000,"hpBonus":1000,"wave":[1,1,10,2,1,1,10,2,1,1,10,2,1,1,10,2]},
{"id":20,"type":2,"timeLimit":60,"atkBonus":1000,"hpBonus":1000,"wave":[1000]}
]

View File

@ -0,0 +1,12 @@
[
{"id":1,"exp":10},
{"id":2,"exp":20},
{"id":3,"exp":30},
{"id":4,"exp":40},
{"id":5,"exp":50},
{"id":6,"exp":60},
{"id":7,"exp":70},
{"id":8,"exp":80},
{"id":9,"exp":90},
{"id":10,"exp":100}
]

View File

@ -4,7 +4,7 @@ local Utils = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Variables
local PlayerDataFolder = ReplicatedStorage.PlayerData
local PlayerDataFolder = ReplicatedStorage:WaitForChild("PlayerData")
--> Constants
@ -38,15 +38,15 @@ function Utils:GenUniqueId(t: table)
return min_id
end
function Utils:GetJsonIdData(JsonName: string, id: number)
local JsonData = require(ReplicatedStorage.Json[JsonName])
for _, item in ipairs(JsonData) do
if item.id == id then
return item
end
end
return nil -- 没找到对应id
end
-- function Utils:GetJsonIdData(JsonName: string, id: number)
-- local JsonData = require(ReplicatedStorage.Json[JsonName])
-- for _, item in ipairs(JsonData) do
-- if item.id == id then
-- return item
-- end
-- end
-- return nil -- 没找到对应id
-- end
function Utils:GetIdDataFromJson(JsonData: table, id: number)
-- 遍历JsonData查找id字段等于目标id的项
@ -58,6 +58,34 @@ function Utils:GetIdDataFromJson(JsonData: table, id: number)
return nil -- 没有找到对应id
end
function Utils:GetMaxIdFromJson(JsonData: table)
local maxId = 0
for _, item in ipairs(JsonData) do
if item.id > maxId then
maxId = item.id
end
end
return maxId
end
-- 将字符串数组转化成真正的数组表,"[1,10,2,10]" -> {1,10,2,10}
function Utils:StringArrayToTable(StringArray: string)
if type(StringArray) ~= "string" then return {} end
-- 去除首尾的中括号
local content = StringArray:match("%[(.*)%]")
if not content or content == "" then return {} end
local result = {}
for value in string.gmatch(content, "[^,]+") do
local num = tonumber(value)
if num then
table.insert(result, num)
else
table.insert(result, value)
end
end
return result
end
--------------------------------------------------------------------------------
return Utils

View File

@ -1,303 +0,0 @@
--[[
Evercyan @ March 2023
DataManager
DataManager handles all user data, like Levels, Tools, Armor, and even misc values that save, such as active armor.
I would avoid messing around with the code here unless if you know what you're doing. Always make sure a change works properly
before shipping the update out to players to avoid data loss (scary!!).
"PlayerData" is a reference to the Configuration under ReplicatedStorage that loads during runtime. Make sure to yield for this to exist.
This Configuration houses all "pData" Configurations. These are individual player's data that houses many ValueBase objects,
such as NumberValues, to easily access on the client & server.
Like any instance, trying to change the data on the client will not replicate to the server.
While a solution like ProfileService & ReplicaService is recommended to avoid instances and lots of FindFirstChild calls, I still believe
that this is the best solution for beginners due to the ease of use, and you're relying on Roblox as the source of truth.
This makes it easier to edit values in run mode, especially if you aren't an experienced programmer.
IMPORTANT NOTE: ----
'leaderstats' is a folder games use to make stats appear on the player list in-game. Please note that this does exist on the client-side,
but attempting to change stats here will do nothing. All player data is actually stored under ReplicatedStorage.PlayerData.
If you're using third-party scripts that rely on leaderstats to be set to, you may need to alter the code to work with pData configs.
]]
-- 主要加载和保存数据
-- 生成对应实例,并把数据加载进去
--> Services
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
--> Dependencies
local GameConfig = require(ReplicatedStorage.Data.GameConfig)
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
--> Variables
local UserData = DataStoreService:GetDataStore("UserData")
local SameKeyCooldown = {}
--------------------------------------------------------------------------------
local PlayerData = Instance.new("Configuration")
PlayerData.Name = "PlayerData"
PlayerData.Parent = ReplicatedStorage
local _warn = warn
local function warn(warning: string)
_warn("DataManager Failure: ".. warning)
end
-- Clamp the passed ValueObject (must be int/number) to the given bounds.
local function ClampStat(ValueObject: IntValue, Min: number?, Max: number?)
ValueObject.Changed:Connect(function()
ValueObject.Value = math.clamp(ValueObject.Value, Min or -math.huge, Max or math.huge)
end)
end
-- Create and return a stat value object, used for player stats
-- ClampInfo is an optional table for clamping it to a min/max. If you don't need it, simply don't include it.
local function CreateStat(ClassName: string, Name: string, DefaultValue, ClampInfo)
local Stat = Instance.new(ClassName)
Stat.Name = Name
Stat.Value = DefaultValue
if ClampInfo and (Stat:IsA("NumberValue") or Stat:IsA("IntValue")) then
ClampStat(Stat, unpack(ClampInfo))
end
return Stat
end
-- 这里生成实例在Configuration里生成一个文件夹然后生成对应的ValueObject
local function CreateDataFolder(Player): Instance
local pData = Instance.new("Configuration")
pData.Name = Player.UserId
-- Stats
local Stats = Instance.new("Folder")
Stats.Name = "Stats"
Stats.Parent = pData
local Level = CreateStat("NumberValue", "Level", 1, {1, GameConfig.MaxLevel})
Level.Parent = Stats
local XP = CreateStat("NumberValue", "XP", 0, {0})
XP.Parent = Stats
local Gold = CreateStat("NumberValue", "Gold", 0, {0})
Gold.Parent = Stats
-- Items
local Items = Instance.new("Folder")
Items.Name = "Items"
Items.Parent = pData
local Tool = Instance.new("Folder")
Tool.Name = "Tool"
Tool.Parent = Items
local Armor = Instance.new("Folder")
Armor.Name = "Armor"
Armor.Parent = Items
-- Preferences / Misc
local ActiveArmor = CreateStat("StringValue", "ActiveArmor", "")
ActiveArmor.Parent = pData
local Hotbar = Instance.new("Folder")
Hotbar.Name = "Hotbar"
Hotbar.Parent = pData
for n = 1, 9 do
local ValueObject = CreateStat("StringValue", tostring(n), "")
ValueObject.Parent = Hotbar
end
return pData
end
-- Unloads loaded user data table into their game session
-- 这里把玩家数据加载到实例中
local function UnloadData(Player: Player, Data: any, pData: Instance)
-- Stats
local Stats = pData:FindFirstChild("Stats")
for StatName, StatValue in Data.Stats do
local Stat = Stats:FindFirstChild(StatName)
if Stat then
Stat.Value = StatValue
end
end
local Items = pData:FindFirstChild("Items")
-- Tool
local ToolFolder = Items:FindFirstChild("Tool")
for _, ItemName in Data.Items.Tool do
local Tool = ContentLibrary.Tool[ItemName]
if Tool then
Tool.Instance:Clone().Parent = Player:WaitForChild("StarterGear")
Tool.Instance:Clone().Parent = Player:WaitForChild("Backpack")
CreateStat("BoolValue", ItemName).Parent = ToolFolder
end
end
-- Armor
local ArmorFolder = Items:FindFirstChild("Armor")
for _, ItemName in Data.Items.Armor do
local Armor = ContentLibrary.Armor[ItemName]
if Armor then
CreateStat("BoolValue", ItemName).Parent = ArmorFolder
end
end
-- Preferences / Misc
(pData:FindFirstChild("ActiveArmor") :: StringValue).Value = Data.ActiveArmor
local HotbarFolder = pData:FindFirstChild("Hotbar")
for SlotNumber, ItemName in Data.Hotbar do
(HotbarFolder:FindFirstChild(SlotNumber) :: StringValue).Value = ItemName
end
end
-- Attempt to save user data. Returns whether or not the request was successful.
local function SaveData(Player: Player): boolean
if not Player:GetAttribute("DataLoaded") then
return false
end
local pData = PlayerData:FindFirstChild(Player.UserId)
local StarterGear = Player:FindFirstChild("StarterGear")
if not pData or not StarterGear then
return false
end
-- Same Key Cooldown (can't write to the same key within 6 seconds)
if SameKeyCooldown[Player.UserId] then
repeat task.wait() until not SameKeyCooldown[Player.UserId]
end
SameKeyCooldown[Player.UserId] = true
task.delay(6, function()
SameKeyCooldown[Player.UserId] = nil
end)
-- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync --
local DataToSave = {}
DataToSave.Stats = {}
DataToSave.Items = {
Tool = {},
Armor = {}
}
-- Stats
local Stats = pData:FindFirstChild("Stats")
for _, ValueObject in Stats:GetChildren() do
DataToSave.Stats[ValueObject.Name] = ValueObject.Value
end
local Items = pData:FindFirstChild("Items")
-- Tool
local Tool = Items:FindFirstChild("Tool")
for _, ValueObject in Tool:GetChildren() do
if ContentLibrary.Tool[ValueObject.Name] then
table.insert(DataToSave.Items.Tool, ValueObject.Name)
end
end
-- Armor
local Armor = Items:FindFirstChild("Armor")
for _, ValueObject in Armor:GetChildren() do
if ContentLibrary.Armor[ValueObject.Name] then
table.insert(DataToSave.Items.Armor, ValueObject.Name)
end
end
-- Preferences / Misc
DataToSave.ActiveArmor = pData.ActiveArmor.Value
DataToSave.Hotbar = {}
for _, ValueObject in pData.Hotbar:GetChildren() do
DataToSave.Hotbar[ValueObject.Name] = ValueObject.Value
end
-- Save to DataStore --
local Success
for i = 1, 3 do
Success = xpcall(function()
return UserData:SetAsync("user/".. Player.UserId, DataToSave, {Player.UserId})
end, warn)
if Success then
break
end
task.wait(6)
end
if Success then
print(("DataManager: User %s's data saved successfully."):format(Player.Name))
else
warn(("DataManager: User %s's data failed to save."):format(Player.Name))
end
return Success
end
-- Attempt to load user data. Returns whether or not the request was successful, as well as the data if it was.
local function LoadData(Player: Player): (boolean, any)
local Success, Response = xpcall(function()
return UserData:GetAsync("user/".. Player.UserId)
end, warn)
if Success and Response then
print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level))
else
print(("DataManager: User %s had no data to load from."):format(Player.Name))
end
return Success, Response
end
local function OnPlayerAdded(Player: Player)
local Success, Data = LoadData(Player)
if not Success then
CollectionService:AddTag(Player, "DataFailed")
Player:Kick("Data unable to load. DataStore Service may be down. Please rejoin later.")
return
end
local pData = CreateDataFolder(Player)
if Data then
UnloadData(Player, Data, pData)
end
pData.Parent = PlayerData
Player:SetAttribute("DataLoaded", true)
end
Players.PlayerAdded:Connect(OnPlayerAdded)
for _, Player in Players:GetPlayers() do
OnPlayerAdded(Player)
end
-- Save on leave
Players.PlayerRemoving:Connect(function(Player)
SaveData(Player)
end)
-- Server closing (save)
game:BindToClose(function()
task.wait(RunService:IsStudio() and 1 or 10)
end)
-- Auto-save
while true do
task.wait(60)
for _, Player in Players:GetPlayers() do
task.defer(SaveData, Player)
end
end

View File

@ -1,25 +0,0 @@
-- 玩家基础信息代理
local PlayerInfoProxy = {}
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
--> Variables
--> Constants
local STORE_NAME = "PlayerInfo"
--------------------------------------------------------------------------------
function PlayerInfoProxy:InitPlayer(Player: Player)
end
function PlayerInfoProxy:OnPlayerRemoving(Player: Player)
end
ReplicatedStorage.Remotes.PlayerRemoving:Connect(PlayerInfoProxy.OnPlayerRemoving)
return PlayerInfoProxy

View File

@ -12,17 +12,38 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
-- 加载Proxy目录下的所有代理
local Proxies = {}
local ProxyFolder = ServerStorage:FindFirstChild("Proxy")
if ProxyFolder then
for _, proxyModule in ipairs(ProxyFolder:GetChildren()) do
if proxyModule:IsA("ModuleScript") then
local success, result = pcall(require, proxyModule)
if success then
-- 去掉文件名后缀
local name = proxyModule.Name
Proxies[name] = result
else
warn("加载代理模块失败: " .. proxyModule.Name, result)
end
end
end
end
--> References
local PlayerData = ReplicatedStorage:WaitForChild("PlayerData")
-- 由ArchiveProxy生成
local PlayerDataFolder = ReplicatedStorage:WaitForChild("PlayerData")
--> Dependencies
local HumanoidAttributes = require(script.HumanoidAttributes)
local PlayerLeveling = require(script.PlayerLeveling)
-- local HumanoidAttributes = require(script.HumanoidAttributes)
-- local PlayerLeveling = require(script.PlayerLeveling)
local GameConfig = require(ReplicatedStorage.Data.GameConfig)
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
local ToolLib = require(ServerStorage.Modules.ToolLib)
local ArmorLib = require(ServerStorage.Modules.ArmorLib)
-- local GameConfig = require(ReplicatedStorage.Data.GameConfig)
-- local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
-- local ToolLib = require(ServerStorage.Modules.ToolLib)
-- local ArmorLib = require(ServerStorage.Modules.ArmorLib)
-- local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
--------------------------------------------------------------------------------
@ -39,29 +60,6 @@ local Temporary = CreateFolder("Temporary", workspace)
local ProjectileCache = CreateFolder("ProjectileCache", Temporary)
local Characters = CreateFolder("Characters", workspace)
-- 初始化玩家信息存储目录(沟通作用,具体数据还得后端处理)
local PlayerDataFolder = Instance.new("Configuration")
PlayerDataFolder.Name = "PlayerData"
PlayerDataFolder.Parent = ReplicatedStorage
-- 加载Proxy目录下的所有代理
local Proxies = {}
local ProxyFolder = script.Parent.Parent:FindFirstChild("Proxy")
if ProxyFolder then
for _, proxyModule in ipairs(ProxyFolder:GetChildren()) do
if proxyModule:IsA("ModuleScript") then
local success, result = pcall(require, proxyModule)
if success then
-- 去掉文件名后缀
local name = proxyModule.Name
Proxies[name] = result
else
warn("加载代理模块失败: " .. proxyModule.Name, result)
end
end
end
end
-- Initially require all server-sided & shared modules
-- 加载模块 ReplicatedStorage.Modules, ServerStorage.Modules
for _, Location in {ReplicatedStorage.Modules, ServerStorage.Modules} do
@ -79,16 +77,26 @@ local function OnPlayerAdded(Player: Player)
warn("玩家数据未加载: " .. Player.Name)
return
end
-- 玩家数据加载成功
print("玩家数据加载成功: ", Proxies.ArchiveProxy.pData[Player.UserId])
local pData = Instance.new("Configuration")
pData.Name = Player.UserId
pData.Parent = PlayerDataFolder
-- 初始化玩家数据目录(其他具体内容在Proxy中处理)
if not Proxies.ArchiveProxy.pData[Player.UserId] then
Proxies.ArchiveProxy.pData[Player.UserId] = {}
end
-- 加载对应玩家的其他系统代理
Proxies.EquipmentProxy:InitPlayer(Player)
Proxies.PlayerInfoProxy:InitPlayer(Player)
Proxies.LevelProxy:InitPlayer(Player)
end
local function OnPlayerRemoving(Player: Player)
-- 清理目录整体数据在ArchiveProxy中清除
local pData = PlayerDataFolder:FindFirstChild(Player.UserId)
if pData then pData:Destroy() end
end

View File

@ -1,165 +1,165 @@
--[[
Evercyan @ March 2023
ArmorLib
-- --[[
-- Evercyan @ March 2023
-- ArmorLib
ArmorLib is an item library that houses code that can be ran on the server relating
to Armor, such as ArmorLib:Give(Player, Armor (ContentLib.Armor[...])), as well
as equipping & unequipping armor, which is primarily ran through remotes fired from the
client's Inventory Gui.
]]
-- ArmorLib is an item library that houses code that can be ran on the server relating
-- to Armor, such as ArmorLib:Give(Player, Armor (ContentLib.Armor[...])), as well
-- as equipping & unequipping armor, which is primarily ran through remotes fired from the
-- client's Inventory Gui.
-- ]]
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
-- --> Services
-- local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- local Players = game:GetService("Players")
--> References
local PlayerData = ReplicatedStorage:WaitForChild("PlayerData")
-- --> References
-- local PlayerData = ReplicatedStorage:WaitForChild("PlayerData")
--> Dependencies
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
local Morph = require(script.Morph)
-- --> Dependencies
-- local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
-- local Morph = require(script.Morph)
--> Variables
-- --> Variables
local ArmorLib = {}
--------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------
-- Adds the armor to the player's data
function ArmorLib:Give(Player: Player, Armor)
local pData = PlayerData:WaitForChild(Player.UserId, 5)
-- -- Adds the armor to the player's data
-- function ArmorLib:Give(Player: Player, Armor)
-- local pData = PlayerData:WaitForChild(Player.UserId, 5)
if pData then
if not pData.Items.Armor:FindFirstChild(Armor.Name) then
local ValueObject = Instance.new("BoolValue")
ValueObject.Name = Armor.Name
ValueObject.Parent = pData.Items.Armor
end
else
warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name))
end
end
-- if pData then
-- if not pData.Items.Armor:FindFirstChild(Armor.Name) then
-- local ValueObject = Instance.new("BoolValue")
-- ValueObject.Name = Armor.Name
-- ValueObject.Parent = pData.Items.Armor
-- end
-- else
-- warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name))
-- end
-- end
function ArmorLib:Trash(Player: Player, Armor)
local pData = PlayerData:WaitForChild(Player.UserId, 5)
-- function ArmorLib:Trash(Player: Player, Armor)
-- local pData = PlayerData:WaitForChild(Player.UserId, 5)
if pData then
if pData.Items.Armor:FindFirstChild(Armor.Name) then
pData.Items.Armor[Armor.Name]:Destroy()
end
if pData.ActiveArmor.Value == Armor.Name then
pData.ActiveArmor.Value = ""
ArmorLib:UnequipArmor(Player)
end
else
warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name))
end
end
-- if pData then
-- if pData.Items.Armor:FindFirstChild(Armor.Name) then
-- pData.Items.Armor[Armor.Name]:Destroy()
-- end
-- if pData.ActiveArmor.Value == Armor.Name then
-- pData.ActiveArmor.Value = ""
-- ArmorLib:UnequipArmor(Player)
-- end
-- else
-- warn(("pData for Player '%s' doesn't exist! Did they leave?"):format(Player.Name))
-- end
-- end
function ArmorLib:EquipArmor(Player: Player, Armor)
local Character = Player.Character
local Humanoid = Character and Character:FindFirstChild("Humanoid")
local Attributes = Humanoid and Humanoid:WaitForChild("Attributes", 1)
if not Attributes or Humanoid.Health <= 0 then return end
-- function ArmorLib:EquipArmor(Player: Player, Armor)
-- local Character = Player.Character
-- local Humanoid = Character and Character:FindFirstChild("Humanoid")
-- local Attributes = Humanoid and Humanoid:WaitForChild("Attributes", 1)
-- if not Attributes or Humanoid.Health <= 0 then return end
-- Humanoid changes
Attributes.Health:SetAttribute("Armor", Armor.Config.Health)
Attributes.WalkSpeed:SetAttribute("Armor", Armor.Config.WalkSpeed)
Attributes.JumpPower:SetAttribute("Armor", Armor.Config.JumpPower)
-- -- Humanoid changes
-- Attributes.Health:SetAttribute("Armor", Armor.Config.Health)
-- Attributes.WalkSpeed:SetAttribute("Armor", Armor.Config.WalkSpeed)
-- Attributes.JumpPower:SetAttribute("Armor", Armor.Config.JumpPower)
-- Morph changes
Morph:ApplyOutfit(Player, Armor)
end
-- -- Morph changes
-- Morph:ApplyOutfit(Player, Armor)
-- end
function ArmorLib:UnequipArmor(Player: Player)
local Character = Player.Character
local Humanoid = Character and Character:FindFirstChild("Humanoid")
local Attributes = Humanoid and Humanoid:FindFirstChild("Attributes")
if not Attributes then return end
-- function ArmorLib:UnequipArmor(Player: Player)
-- local Character = Player.Character
-- local Humanoid = Character and Character:FindFirstChild("Humanoid")
-- local Attributes = Humanoid and Humanoid:FindFirstChild("Attributes")
-- if not Attributes then return end
-- Humanoid changes
Attributes.Health:SetAttribute("Armor", nil)
Attributes.WalkSpeed:SetAttribute("Armor", nil)
Attributes.JumpPower:SetAttribute("Armor", nil)
-- -- Humanoid changes
-- Attributes.Health:SetAttribute("Armor", nil)
-- Attributes.WalkSpeed:SetAttribute("Armor", nil)
-- Attributes.JumpPower:SetAttribute("Armor", nil)
-- Morph changes
Morph:ClearOutfit(Player)
end
-- -- Morph changes
-- Morph:ClearOutfit(Player)
-- end
---- Remotes -------------------------------------------------------------------
-- ---- Remotes -------------------------------------------------------------------
local ChangeCd = {}
-- local ChangeCd = {}
ReplicatedStorage.Remotes.EquipArmor.OnServerInvoke = function(Player, ArmorName: string)
if not ArmorName or typeof(ArmorName) ~= "string" then
return
end
-- ReplicatedStorage.Remotes.EquipArmor.OnServerInvoke = function(Player, ArmorName: string)
-- if not ArmorName or typeof(ArmorName) ~= "string" then
-- return
-- end
local Armor = ContentLibrary.Armor[ArmorName]
-- local Armor = ContentLibrary.Armor[ArmorName]
if Armor and not ChangeCd[Player.UserId] then
ChangeCd[Player.UserId] = true
task.delay(0.25, function()
ChangeCd[Player.UserId] = nil
end)
-- if Armor and not ChangeCd[Player.UserId] then
-- ChangeCd[Player.UserId] = true
-- task.delay(0.25, function()
-- ChangeCd[Player.UserId] = nil
-- end)
ArmorLib:EquipArmor(Player, Armor)
-- ArmorLib:EquipArmor(Player, Armor)
local pData = PlayerData:FindFirstChild(Player.UserId)
if pData and pData.Items.Armor[ArmorName] and Armor then
pData.ActiveArmor.Value = ArmorName
end
end
end
-- local pData = PlayerData:FindFirstChild(Player.UserId)
-- if pData and pData.Items.Armor[ArmorName] and Armor then
-- pData.ActiveArmor.Value = ArmorName
-- end
-- end
-- end
ReplicatedStorage.Remotes.UnequipArmor.OnServerInvoke = function(Player)
ArmorLib:UnequipArmor(Player)
-- ReplicatedStorage.Remotes.UnequipArmor.OnServerInvoke = function(Player)
-- ArmorLib:UnequipArmor(Player)
local pData = PlayerData:FindFirstChild(Player.UserId)
if pData then
pData.ActiveArmor.Value = ""
end
end
-- local pData = PlayerData:FindFirstChild(Player.UserId)
-- if pData then
-- pData.ActiveArmor.Value = ""
-- end
-- end
--------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------
local function OnPlayerAdded(Player: Player)
local pData = PlayerData:WaitForChild(Player.UserId)
-- local function OnPlayerAdded(Player: Player)
-- local pData = PlayerData:WaitForChild(Player.UserId)
local function OnCharacterAdded(Character)
local ActiveArmor = pData:WaitForChild("ActiveArmor")
local Armor = ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value]
-- local function OnCharacterAdded(Character)
-- local ActiveArmor = pData:WaitForChild("ActiveArmor")
-- local Armor = ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value]
-- Update any incoming accessories (CharacterAppearanceLoaded is really broken lol)
local Connection = Character.ChildAdded:Connect(function(Child)
if Child:IsA("Accessory") then
Morph:UpdateAccessoriesTransparency(Character, ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value])
end
end)
Player.CharacterRemoving:Once(function()
Connection:Disconnect()
end)
-- -- Update any incoming accessories (CharacterAppearanceLoaded is really broken lol)
-- local Connection = Character.ChildAdded:Connect(function(Child)
-- if Child:IsA("Accessory") then
-- Morph:UpdateAccessoriesTransparency(Character, ActiveArmor.Value ~= "" and ContentLibrary.Armor[ActiveArmor.Value])
-- end
-- end)
-- Player.CharacterRemoving:Once(function()
-- Connection:Disconnect()
-- end)
if Armor then
if Player:HasAppearanceLoaded() then
ArmorLib:EquipArmor(Player, Armor)
else
Player.CharacterAppearanceLoaded:Once(function()
ArmorLib:EquipArmor(Player, Armor)
end)
end
end
end
-- if Armor then
-- if Player:HasAppearanceLoaded() then
-- ArmorLib:EquipArmor(Player, Armor)
-- else
-- Player.CharacterAppearanceLoaded:Once(function()
-- ArmorLib:EquipArmor(Player, Armor)
-- end)
-- end
-- end
-- end
Player.CharacterAdded:Connect(OnCharacterAdded)
if Player.Character then
OnCharacterAdded(Player.Character)
end
end
-- Player.CharacterAdded:Connect(OnCharacterAdded)
-- if Player.Character then
-- OnCharacterAdded(Player.Character)
-- end
-- end
for _, Player in Players:GetChildren() do
task.defer(OnPlayerAdded, Player)
end
-- for _, Player in Players:GetChildren() do
-- task.defer(OnPlayerAdded, Player)
-- end
Players.PlayerAdded:Connect(OnPlayerAdded)
-- Players.PlayerAdded:Connect(OnPlayerAdded)
return ArmorLib

View File

@ -1,6 +1,8 @@
-- 数据存储代理
local ArchiveProxy = {}
print("进入")
--> Services
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
@ -14,7 +16,7 @@ local GameConfig = require(ReplicatedStorage.Data.GameConfig)
local ContentLibrary = require(ReplicatedStorage.Modules.ContentLibrary)
--> Variables
local UserData = DataStoreService:GetDataStore("UserData")
local UserData = DataStoreService:GetDataStore("UserData3")
local SameKeyCooldown = {}
--------------------------------------------------------------------------------
@ -48,23 +50,7 @@ local function SaveData(Player: Player): boolean
return false
end
local pData = PlayerData:FindFirstChild(Player.UserId)
local StarterGear = Player:FindFirstChild("StarterGear")
if not pData or not StarterGear then
return false
end
-- Same Key Cooldown (can't write to the same key within 6 seconds)
if SameKeyCooldown[Player.UserId] then
repeat task.wait() until not SameKeyCooldown[Player.UserId]
end
SameKeyCooldown[Player.UserId] = true
task.delay(6, function()
SameKeyCooldown[Player.UserId] = nil
end)
-- Compile "DataToSave" table, which we pass to GlobalDataStore:SetAsync --
local DataToSave = {}
local DataToSave = ArchiveProxy.pData[Player.UserId]
-- Save to DataStore --
local Success
@ -95,7 +81,8 @@ local function LoadData(Player: Player): (boolean, any)
end, warn)
if Success and Response then
print(("DataManager: User %s's data loaded into the game with Level '%s'."):format(Player.Name, Response.Stats.Level))
print(("DataManager: User %s's data loaded into the game."):format(Player.Name))
print(Response)
else
print(("DataManager: User %s had no data to load from."):format(Player.Name))
end
@ -115,11 +102,16 @@ local function OnPlayerAdded(Player: Player)
ArchiveProxy.pData = {}
end
-- 如果数据为空,则初始化数据
if not Data then
Data = {}
end
ArchiveProxy.pData[Player.UserId] = Data
Player:SetAttribute("DataLoaded", true)
end
local function OnPlayerRemoving(Player: Player)
local function OnPlayerRemoving(Player: string)
SaveData(Player)
ArchiveProxy.pData[Player.UserId] = nil
ReplicatedStorage.Remotes.PlayerRemoving:Fire(Player)
@ -139,7 +131,6 @@ game:BindToClose(function()
task.wait(RunService:IsStudio() and 1 or 10)
end)
-- Auto-save
task.spawn(function()
while true do

View File

@ -3,18 +3,22 @@ local EquipmentProxy = {}
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
--> Variables
local Utils = require(ReplicatedStorage.Tools.Utils)
local EquipmentJsonData = require(ReplicatedStorage.Json.Equipment)
local ArchiveProxy = require(ReplicatedStorage.Modules.ArchiveProxy)
local PlayerInfoProxy = require(ReplicatedStorage.Modules.PlayerInfoProxy)
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy)
--> Json
local JsonEquipment = require(ReplicatedStorage.Json.Equipment)
--> Constants
local STORE_NAME = "Equipment"
--------------------------------------------------------------------------------
-- 获取装备文件夹
local function GetPlayerEquipmentFolder(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
@ -22,10 +26,10 @@ local function GetPlayerEquipmentFolder(Player: Player)
return EquipmentFolder
end
-- 创建装备实例
local function CreateEquipmentInstance(Player: Player, UniqueId: number, EquipmentData: table)
if Player or UniqueId or EquipmentData then
warn('创建装备实例失败: ' .. Player.Name .. ' ' .. UniqueId .. ' ' .. EquipmentData)
warn('创建装备实例失败: ' , Player.Name, UniqueId, EquipmentData)
return
end
local PlayerEquipmentFolder = GetPlayerEquipmentFolder(Player)
@ -40,29 +44,30 @@ end
--------------------------------------------------------------------------------
-- 初始化玩家
function EquipmentProxy:InitPlayer(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local EquipmentFolder = Utils:CreateFolder("Equipment", pData)
Utils:CreateFolder("Equipment", pData)
-- 初始化数据存储
if not ArchiveProxy.pData[Player.UserId] then
ArchiveProxy.pData[Player.UserId] = {}
-- 新玩家数据初始化
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
end
-- 初始化装备
for uniqueId, EquipmentData in ArchiveProxy.pData[Player.UserId] do
for uniqueId, EquipmentData in ArchiveProxy.pData[Player.UserId][STORE_NAME] do
CreateEquipmentInstance(Player, uniqueId, EquipmentData)
end
end
local EXCEPT_KEYS = { "id", "orgId", "name"}
local EXCEPT_KEYS = { "id", "orgId", "name", "attributes"}
-- 添加装备到背包
function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local EquipmentData = Utils:GetJsonIdData("Equipment", EquipmentId)
local EquipmentData = Utils:GetIdDataFromJson(JsonEquipment, EquipmentId)
if not EquipmentData then return end
local UniqueId = Utils:GenUniqueId(ArchiveProxy.pData[Player.UserId])
@ -79,7 +84,7 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number)
ResultData.wearing = false
-- 其他随机词条内容添加在下面
-- 根据词条内容直接生成回收奖励
-- 之后回收修改随机生成
------------------------------------------------------------
@ -87,17 +92,16 @@ function EquipmentProxy:AddEquipment(Player: Player, EquipmentId: number)
CreateEquipmentInstance(Player, UniqueId, ResultData)
end
-- 回收装备
function EquipmentProxy:RecycleEquipment(Player: Player, EquipmentId: number)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local EquipmentData = Utils:GetJsonIdData("Equipment", EquipmentId)
local EquipmentData = ArchiveProxy.pData[Player.UserId][STORE_NAME][EquipmentId]
if not EquipmentData then return end
if not ArchiveProxy.pData[Player.UserId][EquipmentId] then return end
-- 回收装备返回金币
-- 调用PlayerInfoProxy来增加货币
PlayerInfoProxy:ChangeItem(Player, 1, EquipmentData.recycle)
ArchiveProxy.pData[Player.UserId][EquipmentId] = nil
local EquipmentInstance = GetPlayerEquipmentFolder(Player):FindFirstChild(EquipmentId)
@ -106,6 +110,7 @@ function EquipmentProxy:RecycleEquipment(Player: Player, EquipmentId: number)
end
end
-- 穿戴装备
function EquipmentProxy:WearEquipment(Player: Player, EquipmentId: number)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
@ -119,6 +124,8 @@ function EquipmentProxy:OnPlayerRemoving(Player: Player)
end
ReplicatedStorage.Remotes.PlayerRemoving:Connect(EquipmentProxy.OnPlayerRemoving)
ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string)
EquipmentProxy:OnPlayerRemoving(PlayerUserId)
end)
return EquipmentProxy

View File

@ -0,0 +1,31 @@
-- 道具代理
local ItemProxy = {}
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
--> Json
local JsonItem = require(ReplicatedStorage.Json.ItemProp)
--> Variables
local Utils = require(ReplicatedStorage.Tools.Utils)
-- local ArchiveProxy = require(ReplicatedStorage.Modules.ArchiveProxy)
local PlayerInfoProxy = require(ServerStorage.Proxy.PlayerInfoProxy)
--------------------------------------------------------------------------------
function ItemProxy:AddItem(Player: Player, ItemId: number, ItemCount: number)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local ItemData = Utils:GetIdDataFromJson(JsonItem, ItemId)
if not ItemData then return end
-- 之后根据不同类型做处理
if ItemData.type == 1 then
PlayerInfoProxy:ChangeItemCount(Player, ItemId, ItemCount)
end
end
return ItemProxy

View File

@ -0,0 +1,125 @@
-- 关卡代理
local LevelProxy = {}
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
--> Variables
local Utils = require(ReplicatedStorage.Tools.Utils)
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
--> Json
local JsonLevel = require(ReplicatedStorage.Json.Level)
--> Constants
local STORE_NAME = "Level"
local ENUM_LEVEL_TYPE = {
Main = 1,
}
--------------------------------------------------------------------------------
-- 初始化生成关卡目录
local LevelFolder = Utils:CreateFolder(STORE_NAME, game.Workspace)
--------------------------------------------------------------------------------
-- 获取玩家关卡文件夹
local function GetPlayerLevelFolder(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local LevelFolder = pData:FindFirstChild("Level")
return LevelFolder
end
-- 获取玩家关卡Workspace目录
local function GetPlayerLevelWorkspaceFolder(PlayerUserId: string)
return LevelFolder:FindFirstChild(PlayerUserId)
end
-- 创建关卡信息实例
local function CreateLevelInstance(Player: Player, Folder: Instance, LevelKey: string, LevelValue: number)
if not Player or not Folder or not LevelKey or not LevelValue then return end
local LevelInstance = Instance.new("NumberValue")
LevelInstance.Name = LevelKey
LevelInstance.Parent = Folder
LevelInstance.Value = LevelValue
return LevelInstance
end
-- 初始化玩家关卡信息
local function ExtraAddPlayerLevel(Player: Player, LevelData: table)
if not Player or not LevelData then return end
-- 如果列表中不包含信息就添加到表中
for LevelKey, LevelValue in ENUM_LEVEL_TYPE do
if not LevelData[LevelKey] then
LevelData[LevelKey] = LevelValue
end
end
end
--------------------------------------------------------------------------------
function LevelProxy:InitPlayer(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
local DungeonFolder = Utils:CreateFolder("Dungeon", LevelFolder)
-- 当前关卡状态
Utils:CreateFolder("Stats", LevelFolder)
-- 关卡目录下生成玩家目录
local spawnFloder = Utils:CreateFolder(Player.UserId, game.Workspace:FindFirstChild(STORE_NAME))
print("spawnFloder ", spawnFloder.Name)
-- 新玩家数据初始化
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress = {}
-- 副本之后再做
ArchiveProxy.pData[Player.UserId][STORE_NAME].Dungeon = {}
end
ExtraAddPlayerLevel(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress)
-- 前端变化
for LevelKey, LevelValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Progress do
CreateLevelInstance(Player, ProgressFolder, LevelKey, LevelValue)
end
end
-- 挑战关卡(挑战副本用另一个函数)
function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
-- 给前端传数据,做表现
-- 场景后端生成
-- 后端生成当前关卡状态数据
end
-- 挑战结束
function LevelProxy:ChallengeEnd(Player: Player)
-- 判断玩家是否通关
-- 通关后,没到最大关卡,关卡进度+1
-- 到达最大关卡不做处理
end
function LevelProxy:OnPlayerRemoving(Player: Player)
local PlayerLevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
if PlayerLevelFolder then
PlayerLevelFolder:Destroy()
end
end
-- ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(PlayerUserId: string)
-- LevelProxy:OnPlayerRemoving(PlayerUserId)
-- end)
Players.PlayerRemoving:Connect(function(Player: Player)
LevelProxy:OnPlayerRemoving(Player)
end)
return LevelProxy

View File

@ -0,0 +1,4 @@
-- 怪物AI代理
local MobAIProxy = {}
return MobAIProxy

View File

@ -0,0 +1,172 @@
-- 玩家基础信息代理
local PlayerInfoProxy = {}
--> Services
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
--> Variables
local Utils = require(ReplicatedStorage.Tools.Utils)
local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
--> Json
local JsonPlayerLv = require(ReplicatedStorage.Json.PlayerLv)
local JsonItem = require(ReplicatedStorage.Json.ItemProp)
--> Constants
local STORE_NAME = "PlayerInfo"
local ENUM_STATE_TYPE = {
Number = "NumberValue",
String = "StringValue",
Bool = "BoolValue",
Vector3 = "Vector3Value",
Vector2 = "Vector2Value",
}
--------------------------------------------------------------------------------
-- 获取玩家信息文件夹
local function GetPlayerInfoFolder(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local PlayerInfoFolder = pData:FindFirstChild("PlayerInfo")
return PlayerInfoFolder
end
-- 创建玩家信息实例
local function CreateInfoInstance(Player: Player, Folder: any, StateKey: string, StateType: string, StateValue: any)
if not Player and not Folder and not StateKey and not StateType then
warn('创建玩家信息实例失败: ' , Player.Name, Folder.Name, StateKey, StateType, StateValue)
return
end
local Info = Instance.new(StateType)
Info.Name = StateKey
Info.Parent = Folder
if StateValue then Info.Value = StateValue end
return Info
end
-- 改变玩家实例信息
local function ChangeInfoInstance(Player: Player, Folder: any, StateKey: string, StateValue: any)
if not Folder or not StateKey or not StateValue then return end
local Info = Folder:FindFirstChild(StateKey)
if not Info then warn('玩家信息实例不存在: ' , Player.Name, StateKey) return end
Info.Value = StateValue
end
local STATS_DATA = {
name = {type = ENUM_STATE_TYPE.String, value = "PlayerName"},
level = {type = ENUM_STATE_TYPE.Number, value = 1},
exp = {type = ENUM_STATE_TYPE.Number, value = 0},
}
-- 初始化玩家状态信息
local function ExtraAddPlayerStats(Player: Player, StatsData: table)
if not Player or not StatsData then return end
-- 如果列表中不包含信息就添加到表中
for StateKey, StateValue in STATS_DATA do
if not StatsData[StateKey] then
StatsData[StateKey] = StateValue
end
end
end
--------------------------------------------------------------------------------
-- 初始化玩家
function PlayerInfoProxy:InitPlayer(Player: Player)
local pData = Utils:GetPlayerDataFolder(Player)
if not pData then return end
local PlayerInfoFolder = Utils:CreateFolder("PlayerInfo", pData)
local StatsFolder = Utils:CreateFolder("Stats", PlayerInfoFolder)
local ItemsFolder = Utils:CreateFolder("Items", PlayerInfoFolder)
-- 新玩家数据初始化
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats = {}
ArchiveProxy.pData[Player.UserId][STORE_NAME].Items = {}
end
-- 放在外面是为了以后系统新增内容方便(同时不用在初始化数据是做写入了)
ExtraAddPlayerStats(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats)
-- 创建玩家信息实例
for StateKey, StateData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats do
CreateInfoInstance(Player, StatsFolder, StateKey, StateData.type, StateData.value)
end
end
-- 添加经验
function PlayerInfoProxy:AddExp(Player: Player, Exp: number)
if not Player or not Exp then warn('添加经验失败: ' .. Player.Name .. ' ' .. Exp) return end
local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME]
playerInfoData.Stats.exp = playerInfoData.Stats.exp + Exp
-- 数据变化
local currentLevel = playerInfoData.Stats.level
local maxLevel = Utils:GetMaxIdFromJson(JsonPlayerLv)
if currentLevel < maxLevel then
local requireExp = Utils:GetIdDataFromJson(JsonPlayerLv, currentLevel)
if playerInfoData.Stats.exp >= requireExp then
playerInfoData.Stats.level = currentLevel + 1
playerInfoData.Stats.exp = playerInfoData.Stats.exp - requireExp
end
end
-- 前端变化
local StatsFolder = GetPlayerInfoFolder(Player):FindFirstChild("Stats")
ChangeInfoInstance(Player, StatsFolder, "exp", playerInfoData.Stats.exp)
ChangeInfoInstance(Player, StatsFolder, "level", playerInfoData.Stats.level)
return true, playerInfoData.Stats.level, playerInfoData.Stats.exp
end
-- 改变物品数量记录
function PlayerInfoProxy:ChangeItemCount(Player: Player, ItemId: number, ItemCount: number)
if not Player or not ItemId or not ItemCount then warn('添加物品失败: ' , Player.Name, ItemId, ItemCount) return end
local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items
local isNew = false
if not playerInfoData[ItemId] then
playerInfoData[ItemId] = ItemCount
isNew = true
else
playerInfoData[ItemId] = playerInfoData[ItemId] + ItemCount
end
-- 前端变化
local ItemsFolder = GetPlayerInfoFolder(Player):FindFirstChild("Items")
if isNew then
CreateInfoInstance(Player, ItemsFolder, ItemId, "NumberValue")
else
ChangeInfoInstance(Player, ItemsFolder, ItemId, playerInfoData[ItemId])
end
return true, playerInfoData[ItemId]
end
-- 判断是否拥有足够物品
function PlayerInfoProxy:HasEnoughItem(Player: Player, ItemId: number, ItemCount: number)
if not Player or not ItemId or not ItemCount then warn('添加物品失败: ' .. Player.Name .. ' ' .. ItemId .. ' ' .. ItemCount) return end
local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items
if not playerInfoData[ItemId] then return false end
return playerInfoData[ItemId] >= ItemCount
end
-- 获取物品数量
function PlayerInfoProxy:GetItemCount(Player: Player, ItemId: number)
if not Player or not ItemId then warn('获取物品数量失败: ' .. Player.Name .. ' ' .. ItemId) return end
local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME].Items
if not playerInfoData[ItemId] then return 0 end
return playerInfoData[ItemId]
end
function PlayerInfoProxy:OnPlayerRemoving(Player: Player)
end
ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(Player: Player)
PlayerInfoProxy:OnPlayerRemoving(Player)
end)
return PlayerInfoProxy

View File

@ -0,0 +1,6 @@
-- 玩家循环代理
local PlayerLoopProxy = {}
return PlayerLoopProxy