更新
This commit is contained in:
parent
f1728a045b
commit
c1c22aa963
BIN
excel/cha.xlsx
BIN
excel/cha.xlsx
Binary file not shown.
13
src/ReplicatedStorage/Json/AttributesUpgrade.json
Normal file
13
src/ReplicatedStorage/Json/AttributesUpgrade.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[
|
||||||
|
{"id":1,"type":1,"effectAttribute":"attack","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null},
|
||||||
|
{"id":2,"type":1,"effectAttribute":"hp","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null},
|
||||||
|
{"id":3,"type":1,"effectAttribute":"swordAtk","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null},
|
||||||
|
{"id":4,"type":1,"effectAttribute":"swordWearBase","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null},
|
||||||
|
{"id":5,"type":1,"effectAttribute":"swordWearSpe","cost":[1,10,20],"lvAdd":[10,20],"battleValueLimit":[5,20],"maxLv":null},
|
||||||
|
{"id":10,"type":2,"effectAttribute":"wearNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":4},
|
||||||
|
{"id":11,"type":2,"effectAttribute":"skillNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":3},
|
||||||
|
{"id":12,"type":2,"effectAttribute":"extraAttributeNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":3},
|
||||||
|
{"id":13,"type":2,"effectAttribute":"elementNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":3},
|
||||||
|
{"id":14,"type":2,"effectAttribute":"elementDefNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":3},
|
||||||
|
{"id":15,"type":2,"effectAttribute":"gemNumber","cost":[1,10,20],"lvAdd":[1,1],"battleValueLimit":[5,20],"maxLv":3}
|
||||||
|
]
|
@ -40,22 +40,21 @@ end
|
|||||||
|
|
||||||
-- 检查当前状态是否可执行
|
-- 检查当前状态是否可执行
|
||||||
function Behaviour:CheckStat()
|
function Behaviour:CheckStat()
|
||||||
if not self.Character then warn("Behaviour Character not found") return false end
|
if not self.Character then return true end
|
||||||
-- 死亡检查
|
-- 死亡检查
|
||||||
if self.Character:GetState("Died") then return false end
|
if self.Character:GetState("Died") then return true end
|
||||||
|
|
||||||
-- 执行状态中检查
|
-- 执行状态中检查
|
||||||
local ExecutingState = self.PlayerAI.ExecutingState
|
local ExecutingState = self.PlayerAI.ExecutingState
|
||||||
-- 其他内容执行中,就false
|
-- 其他内容执行中,就false
|
||||||
if ExecutingState == true then return false end
|
if ExecutingState == true then return true end
|
||||||
return true
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 改变当前执行状态标记
|
-- 改变当前执行状态标记
|
||||||
function Behaviour:ChangeExecutingState(State: boolean)
|
function Behaviour:ChangeExecutingState(State: boolean)
|
||||||
if not self.Character then warn("Behaviour Character not found") return end
|
if not self.Character then warn("Behaviour Character not found") return end
|
||||||
local ExecutingState = self.PlayerAI.ExecutingState
|
self.PlayerAI.ExecutingState = State
|
||||||
ExecutingState.Value = State
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 销毁
|
-- 销毁
|
||||||
|
@ -20,6 +20,7 @@ function Character.new(Player: Player, CharacterModel: Model, CharacterData: tab
|
|||||||
self.Root = HumanoidRootPart
|
self.Root = HumanoidRootPart
|
||||||
self.Humanoid = mobHumanoid
|
self.Humanoid = mobHumanoid
|
||||||
self.Origin = HumanoidRootPart:GetPivot()
|
self.Origin = HumanoidRootPart:GetPivot()
|
||||||
|
self.Player = Player
|
||||||
self.TargetPlayerUserID = Player.UserId
|
self.TargetPlayerUserID = Player.UserId
|
||||||
self.Connections = {}
|
self.Connections = {}
|
||||||
self.Stats = {}
|
self.Stats = {}
|
||||||
|
@ -12,6 +12,7 @@ local MobsProxy = require(ServerStorage.Proxy.MobsProxy)
|
|||||||
|
|
||||||
local Move = {}
|
local Move = {}
|
||||||
Move.__index = Move
|
Move.__index = Move
|
||||||
|
setmetatable(Move, {__index = Behaviour})
|
||||||
|
|
||||||
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
function Move:Init(PlayerAI, Character: TypeList.Character, Player: Player)
|
||||||
local self = Behaviour:Init(PlayerAI, Character)
|
local self = Behaviour:Init(PlayerAI, Character)
|
||||||
@ -24,7 +25,7 @@ function Move:Check(CheckInfo: table)
|
|||||||
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
if Behaviour.CheckStat(self) then return -1, self.CheckData end
|
||||||
|
|
||||||
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
|
local PlayerMobs = MobsProxy:GetPlayerMobs(self.Player)
|
||||||
if not PlayerMobs then warn("PlayerMobs not found") return end
|
if not PlayerMobs then return end
|
||||||
|
|
||||||
local closestMob, minDistance = nil, math.huge
|
local closestMob, minDistance = nil, math.huge
|
||||||
for _, Mob in PlayerMobs do
|
for _, Mob in PlayerMobs do
|
||||||
@ -50,7 +51,7 @@ end
|
|||||||
function Move:Execute()
|
function Move:Execute()
|
||||||
self.ExeTask = task.spawn(function ()
|
self.ExeTask = task.spawn(function ()
|
||||||
self:ChangeExecutingState(true)
|
self:ChangeExecutingState(true)
|
||||||
self.Character.Humanoid.WalkToPart.CFrame = self.CheckData["ClosestCharacter"].Instance.PrimaryPart.CFrame
|
self.Character.Humanoid:MoveTo(self.CheckData["ClosestCharacter"].Instance:GetPivot().Position)
|
||||||
task.wait(0.5)
|
task.wait(0.5)
|
||||||
self:ChangeExecutingState(false)
|
self:ChangeExecutingState(false)
|
||||||
end)
|
end)
|
||||||
|
@ -31,6 +31,12 @@ function ArchiveProxy:IsPlayerDataLoaded(Player: Player)
|
|||||||
return true -- 成功加载
|
return true -- 成功加载
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ArchiveProxy:CleanPlayerData(Player: Player)
|
||||||
|
if not ArchiveProxy.pData[Player.UserId] then warn('CleanPlayerData: ', Player.Name, '数据不存在') return end
|
||||||
|
ArchiveProxy.pData[Player.UserId] = {}
|
||||||
|
print("清除数据成功")
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
local PlayerData = Instance.new("Configuration")
|
local PlayerData = Instance.new("Configuration")
|
||||||
|
@ -57,6 +57,7 @@ function DamageProxy:TakeDamage(Caster: TypeList.Character, Victim: TypeList.Cha
|
|||||||
-- 伤害计算
|
-- 伤害计算
|
||||||
local VictimHealth = Victim:GetAttributeValue("hp")
|
local VictimHealth = Victim:GetAttributeValue("hp")
|
||||||
Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
|
Victim:ChangeAttributeValue("hp", math.max(0, VictimHealth - Damage))
|
||||||
|
print("伤害数据打印", Damage, VictimHealth)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
24
src/ServerStorage/Proxy/HelpProxy.luau
Normal file
24
src/ServerStorage/Proxy/HelpProxy.luau
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
local HelpProxy = {}
|
||||||
|
|
||||||
|
--> Server
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_PlayerHelper = ReplicatedStorage.Events.RE_PlayerHelper
|
||||||
|
|
||||||
|
function HelpProxy:CleanPlayerData(Player: Player)
|
||||||
|
local ArchiveProxy = require(script.Parent.ArchiveProxy)
|
||||||
|
ArchiveProxy:CleanPlayerData(Player)
|
||||||
|
end
|
||||||
|
|
||||||
|
RE_PlayerHelper.OnServerEvent:Connect(function(Player: Player, EventName: string, EventData: any)
|
||||||
|
if EventName == "CleanPlayerData" then
|
||||||
|
HelpProxy:CleanPlayerData(Player)
|
||||||
|
elseif EventName == "AddItem" then
|
||||||
|
local PlayerInfoProxy = require(script.Parent.PlayerInfoProxy)
|
||||||
|
PlayerInfoProxy:ChangeItemCount(Player, EventData[1], EventData[2])
|
||||||
|
print("添加物品成功", PlayerInfoProxy:GetItemCount(Player, EventData[1]))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return HelpProxy
|
@ -15,6 +15,9 @@ local TypeList = require(ServerStorage.Base.TypeList)
|
|||||||
--> Json
|
--> Json
|
||||||
local JsonLevel = require(ReplicatedStorage.Json.Level)
|
local JsonLevel = require(ReplicatedStorage.Json.Level)
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local BD_ChallengeEnd = ReplicatedStorage.Events.BD_ChallengeEnd
|
||||||
|
|
||||||
--> Constants
|
--> Constants
|
||||||
local STORE_NAME = "Level"
|
local STORE_NAME = "Level"
|
||||||
local ENUM_LEVEL_TYPE = {
|
local ENUM_LEVEL_TYPE = {
|
||||||
@ -84,7 +87,7 @@ end
|
|||||||
local function OnMobDied(Player: Player, Mob: TypeList.Character)
|
local function OnMobDied(Player: Player, Mob: TypeList.Character)
|
||||||
for _, mob in LevelProxy.pData[Player.UserId].Mobs do
|
for _, mob in LevelProxy.pData[Player.UserId].Mobs do
|
||||||
if mob ~= Mob then continue end
|
if mob ~= Mob then continue end
|
||||||
table.remove(LevelProxy.pData[Player.UserId].Mobs, mob)
|
table.remove(LevelProxy.pData[Player.UserId].Mobs, table.find(LevelProxy.pData[Player.UserId].Mobs, mob))
|
||||||
|
|
||||||
-- 怪物清除判断
|
-- 怪物清除判断
|
||||||
local LevelData = Utils:GetJsonData(JsonLevel, LevelProxy.pData[Player.UserId].LevelId)
|
local LevelData = Utils:GetJsonData(JsonLevel, LevelProxy.pData[Player.UserId].LevelId)
|
||||||
@ -149,8 +152,6 @@ function LevelProxy:InitPlayer(Player: Player)
|
|||||||
if key == "Task" or key == "Mobs" then continue end
|
if key == "Task" or key == "Mobs" then continue end
|
||||||
CreateLevelInstance(Player, ChallengeFolder, key, value)
|
CreateLevelInstance(Player, ChallengeFolder, key, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- self:ChallengeLevel(Player, 1)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 挑战关卡(挑战副本用另一个函数)
|
-- 挑战关卡(挑战副本用另一个函数)
|
||||||
@ -165,7 +166,7 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
|
|||||||
local LevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
local LevelFolder = GetPlayerLevelWorkspaceFolder(Player.UserId)
|
||||||
local ChallengeFolder = LevelFolder:FindFirstChild("Challenge")
|
local ChallengeFolder = LevelFolder:FindFirstChild("Challenge")
|
||||||
if not ChallengeFolder then warn("ChallengeFolder not found") return end
|
if not ChallengeFolder then warn("ChallengeFolder not found") return end
|
||||||
local levelTask = task.defer(function()
|
local levelTask = task.spawn(function()
|
||||||
ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 2 and true or false)
|
ChangeValue(Player, ChallengeFolder, "IsBoss", LevelData.type == 2 and true or false)
|
||||||
ChangeValue(Player, ChallengeFolder, "LevelId", LevelId)
|
ChangeValue(Player, ChallengeFolder, "LevelId", LevelId)
|
||||||
ChangeValue(Player, ChallengeFolder, "Time", 0)
|
ChangeValue(Player, ChallengeFolder, "Time", 0)
|
||||||
@ -218,7 +219,7 @@ function LevelProxy:ChallengeLevel(Player: Player, LevelId: number)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
ChangeValue(Player, ChallengeFolder, "Task", levelTask)
|
LevelProxy.pData[Player.UserId].Task = levelTask
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 挑战结束
|
-- 挑战结束
|
||||||
@ -226,8 +227,15 @@ function LevelProxy:ChallengeEnd(Player: Player, result: boolean)
|
|||||||
local pData = Utils:GetPlayerDataFolder(Player)
|
local pData = Utils:GetPlayerDataFolder(Player)
|
||||||
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
|
local LevelFolder = Utils:CreateFolder(STORE_NAME, pData)
|
||||||
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
|
local ProgressFolder = Utils:CreateFolder("Progress", LevelFolder)
|
||||||
|
|
||||||
|
-- 停止关卡循环
|
||||||
|
if LevelProxy.pData[Player.UserId].Task then
|
||||||
|
task.cancel(LevelProxy.pData[Player.UserId].Task)
|
||||||
|
LevelProxy.pData[Player.UserId].Task = nil
|
||||||
|
end
|
||||||
|
|
||||||
-- 清除剩余怪物
|
-- 清除剩余怪物
|
||||||
for _, mob in LevelProxy.pData[Player.UserId].Mobs do mob:Died() end
|
for _, mob in LevelProxy.pData[Player.UserId].Mobs do mob:Died(true) end
|
||||||
LevelProxy.pData[Player.UserId].Mobs = {}
|
LevelProxy.pData[Player.UserId].Mobs = {}
|
||||||
|
|
||||||
-- 判断玩家是否通关
|
-- 判断玩家是否通关
|
||||||
@ -239,6 +247,7 @@ function LevelProxy:ChallengeEnd(Player: Player, result: boolean)
|
|||||||
ChangeValue(Player, ProgressFolder, "BossFail", LevelProxy.pData[Player.UserId].LevelId)
|
ChangeValue(Player, ProgressFolder, "BossFail", LevelProxy.pData[Player.UserId].LevelId)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
BD_ChallengeEnd:Fire(Player, LevelProxy.pData[Player.UserId].LevelId, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
function LevelProxy:OnPlayerRemoving(Player: Player)
|
function LevelProxy:OnPlayerRemoving(Player: Player)
|
||||||
@ -249,7 +258,7 @@ function LevelProxy:OnPlayerRemoving(Player: Player)
|
|||||||
end
|
end
|
||||||
-- 关卡存储数据清除
|
-- 关卡存储数据清除
|
||||||
if LevelProxy.pData[Player.UserId].Task then
|
if LevelProxy.pData[Player.UserId].Task then
|
||||||
LevelProxy.pData[Player.UserId].Task:Cancel()
|
task.cancel(LevelProxy.pData[Player.UserId].Task)
|
||||||
end
|
end
|
||||||
LevelProxy.pData[Player.UserId] = nil
|
LevelProxy.pData[Player.UserId] = nil
|
||||||
end
|
end
|
||||||
|
@ -20,33 +20,27 @@ function AI:GetModelDistance(Unit1: Model, Unit2: Model): number
|
|||||||
end
|
end
|
||||||
|
|
||||||
function AI:GetClosestPlayer(Mob: TypeList.Character): (Player?, number?)
|
function AI:GetClosestPlayer(Mob: TypeList.Character): (Player?, number?)
|
||||||
local Closest = {Player = nil, Magnitude = math.huge}
|
local Closest = {PlayerRole = nil, Magnitude = math.huge, Player = nil}
|
||||||
|
|
||||||
local ActivePlayer -- We retain a reference to this, so they have to get further away from the mob to stop following, instead of the usual distance.
|
local PlayerRole = Mob.PlayerRole
|
||||||
if Mob.Humanoid.WalkToPart then
|
local Character = PlayerRole.Instance
|
||||||
local Player = Players:GetPlayerFromCharacter(Mob.Humanoid.WalkToPart.Parent)
|
if Character == nil then warn("AI:GetClosestPlayer Character not found", Mob.Name) return end
|
||||||
if Player then
|
|
||||||
ActivePlayer = Player
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 临时的
|
|
||||||
for _, Player in Players:GetPlayers() do
|
for _, Player in Players:GetPlayers() do
|
||||||
if Mob.TargetPlayerUserID == Player.UserId then
|
if Mob.TargetPlayerUserID == Player.UserId then
|
||||||
local Character = Player.Character
|
Closest.Player = Player
|
||||||
if not Character then continue end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local Magnitude = (Character:GetPivot().Position - Mob.Instance:GetPivot().Position).Magnitude
|
local Magnitude = (Character:GetPivot().Position - Mob.Instance:GetPivot().Position).Magnitude
|
||||||
local MaxDistance = (ActivePlayer == Player and (Mob.Config.FollowDistance or 32) * 2) or Mob.Config.FollowDistance or 32
|
-- 最大距离暂时设置大值
|
||||||
|
local MaxDistance = Mob.Config.FollowDistance or 10000
|
||||||
if Magnitude <= MaxDistance and Magnitude < Closest.Magnitude then
|
if Magnitude <= MaxDistance and Magnitude < Closest.Magnitude then
|
||||||
Closest.Player = Player
|
Closest.PlayerRole = PlayerRole
|
||||||
Closest.Magnitude = Magnitude
|
Closest.Magnitude = Magnitude
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return Closest.Player, Closest.Player and Closest.Magnitude
|
return Closest.Player, Closest.PlayerRole, Closest.PlayerRole and Closest.Magnitude
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Adds the mob to the ActiveMobs table. This table runs a few times per second and updates follow & jump code for active mobs.
|
-- Adds the mob to the ActiveMobs table. This table runs a few times per second and updates follow & jump code for active mobs.
|
||||||
@ -66,7 +60,10 @@ task.defer(function()
|
|||||||
for MobInstance, Mob in ActiveMobs do
|
for MobInstance, Mob in ActiveMobs do
|
||||||
task.spawn(function()
|
task.spawn(function()
|
||||||
if not Mob.Stats.Died then
|
if not Mob.Stats.Died then
|
||||||
local Player, distance = AI:GetClosestPlayer(Mob)
|
-- 执行其他内容就停止
|
||||||
|
if Mob.ExecutingState then return end
|
||||||
|
|
||||||
|
local Player, PlayerRole, distance = AI:GetClosestPlayer(Mob)
|
||||||
local Enemy = Mob.Humanoid
|
local Enemy = Mob.Humanoid
|
||||||
|
|
||||||
-- Simulation
|
-- Simulation
|
||||||
@ -79,16 +76,22 @@ task.defer(function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Tracking
|
-- Tracking
|
||||||
if Player and Enemy then
|
if Player and Enemy and PlayerRole then
|
||||||
|
if PlayerRole.Stats.Died then return end
|
||||||
if distance > 5 then
|
if distance > 5 then
|
||||||
Enemy:MoveTo(Player.Character:GetPivot().Position, Player.Character.PrimaryPart)
|
Enemy:MoveTo(Player.Character:GetPivot().Position, Player.Character.PrimaryPart)
|
||||||
else
|
else
|
||||||
|
Mob.ExecutingState = true
|
||||||
-- 停止移动
|
-- 停止移动
|
||||||
Enemy:MoveTo(MobInstance:GetPivot().Position)
|
Enemy:MoveTo(MobInstance:GetPivot().Position)
|
||||||
-- 触发攻击逻辑
|
-- 触发攻击逻辑
|
||||||
task.wait(Mob.Config.attackSpeed)
|
task.wait(Mob.Config.attackSpeed)
|
||||||
|
|
||||||
|
-- 攻击间隔后再次判断状态
|
||||||
if Mob.Stats.Died then return end
|
if Mob.Stats.Died then return end
|
||||||
if not Player then return end
|
if not Player then return end
|
||||||
|
if PlayerRole.Stats.Died then return end
|
||||||
|
|
||||||
if AI:GetModelDistance(MobInstance, Player.Character) <= 5 then
|
if AI:GetModelDistance(MobInstance, Player.Character) <= 5 then
|
||||||
-- 调用伤害模块
|
-- 调用伤害模块
|
||||||
DamageProxy:TakeDamage(Mob, Mob.PlayerRole, {
|
DamageProxy:TakeDamage(Mob, Mob.PlayerRole, {
|
||||||
@ -99,6 +102,7 @@ task.defer(function()
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
Mob.ExecutingState = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
@ -62,6 +62,7 @@ function Mob.new(Player: Player, MobId: number, OnMobDied: ((Player: Player, Mob
|
|||||||
setmetatable(self, Mob) -- 继承Mob方法
|
setmetatable(self, Mob) -- 继承Mob方法
|
||||||
local PlayerFightProxy = require(ServerStorage.Proxy.PlayerFightProxy)
|
local PlayerFightProxy = require(ServerStorage.Proxy.PlayerFightProxy)
|
||||||
self.PlayerRole = PlayerFightProxy:GetPlayerRole(Player)
|
self.PlayerRole = PlayerFightProxy:GetPlayerRole(Player)
|
||||||
|
self.ExecutingState = false
|
||||||
|
|
||||||
-- 放入关卡中
|
-- 放入关卡中
|
||||||
newMobModel.Parent = playerMobsFolder
|
newMobModel.Parent = playerMobsFolder
|
||||||
@ -80,9 +81,11 @@ function Mob.new(Player: Player, MobId: number, OnMobDied: ((Player: Player, Mob
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
function Mob:Died()
|
function Mob:Died(IsSkinOnDied: boolean?)
|
||||||
MobsProxy:RemoveMob(self.Player, self.Instance)
|
MobsProxy:RemoveMob(self.Player, self.Instance)
|
||||||
|
if not IsSkinOnDied then
|
||||||
if self.OnDied then self.OnDied(self.Player, self) end
|
if self.OnDied then self.OnDied(self.Player, self) end
|
||||||
|
end
|
||||||
Character.Died(self)
|
Character.Died(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -93,8 +96,8 @@ function MobsProxy:CreateMob(Player: Player, MobId: number, AtkBonus: number?, H
|
|||||||
local Mob = Mob.new(Player, MobId, OnMobDied)
|
local Mob = Mob.new(Player, MobId, OnMobDied)
|
||||||
AI:StartTracking(Mob)
|
AI:StartTracking(Mob)
|
||||||
-- 关卡系数
|
-- 关卡系数
|
||||||
-- if AtkBonus then Mob:ChangeAttributeValue("attack", math.floor(Mob.Config.attack * (AtkBonus / 1000))) end
|
if AtkBonus then Mob:ChangeAttributeValue("attack", math.floor(Mob.Config.attack * (AtkBonus / 1000))) end
|
||||||
-- if HpBonus then Mob:ChangeAttributeValue("hp", math.floor(Mob.Config.hp * (HpBonus / 1000))) end
|
if HpBonus then Mob:ChangeAttributeValue("hp", math.floor(Mob.Config.hp * (HpBonus / 1000))) end
|
||||||
MobsProxy.pData[Player.UserId][Mob.Instance] = Mob
|
MobsProxy.pData[Player.UserId][Mob.Instance] = Mob
|
||||||
return Mob
|
return Mob
|
||||||
end
|
end
|
||||||
|
@ -20,12 +20,12 @@ local JsonLevel = require(ReplicatedStorage.Json.Level)
|
|||||||
local LevelLoop = {}
|
local LevelLoop = {}
|
||||||
LevelLoop.__index = LevelLoop
|
LevelLoop.__index = LevelLoop
|
||||||
|
|
||||||
function LevelLoop.new(Player: Player, Character: TypeList.Character)
|
function LevelLoop.new(Player: Player, PlayerRole: TypeList.Character)
|
||||||
print("LevelLoop:Init")
|
print("LevelLoop:Init")
|
||||||
local self = {}
|
local self = {}
|
||||||
setmetatable(self, LevelLoop)
|
setmetatable(self, LevelLoop)
|
||||||
self.Player = Player
|
self.Player = Player
|
||||||
self.Character = Character
|
self.PlayerRole = PlayerRole
|
||||||
self.TaskAutoChallenge = nil
|
self.TaskAutoChallenge = nil
|
||||||
|
|
||||||
self.ConChallengeEnd = BD_ChallengeEnd.Event:Connect(function(Player: Player, LevelId: number)
|
self.ConChallengeEnd = BD_ChallengeEnd.Event:Connect(function(Player: Player, LevelId: number)
|
||||||
@ -38,7 +38,9 @@ function LevelLoop.new(Player: Player, Character: TypeList.Character)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function LevelLoop:AutoChallenge()
|
function LevelLoop:AutoChallenge()
|
||||||
print("AutoChallenge")
|
-- 重置玩家状态(先临时调用角色复活,之后复杂的内容再说)
|
||||||
|
self.PlayerRole:Respawn()
|
||||||
|
|
||||||
local LevelFolder = game.Workspace:WaitForChild("Level"):WaitForChild(self.Player.UserId)
|
local LevelFolder = game.Workspace:WaitForChild("Level"):WaitForChild(self.Player.UserId)
|
||||||
local ProgressFolder = LevelFolder:FindFirstChild("Progress")
|
local ProgressFolder = LevelFolder:FindFirstChild("Progress")
|
||||||
|
|
||||||
@ -51,7 +53,7 @@ function LevelLoop:AutoChallenge()
|
|||||||
LevelProxy:ChallengeLevel(self.Player, LevelId)
|
LevelProxy:ChallengeLevel(self.Player, LevelId)
|
||||||
end
|
end
|
||||||
|
|
||||||
function LevelLoop:OnChallengeEnd(Player: Player, LevelId: number)
|
function LevelLoop:OnChallengeEnd(Player: Player, LevelId: number, result: boolean)
|
||||||
self.TaskAutoChallenge = task.defer(function()
|
self.TaskAutoChallenge = task.defer(function()
|
||||||
task.wait(3)
|
task.wait(3)
|
||||||
self:AutoChallenge()
|
self:AutoChallenge()
|
||||||
|
@ -5,10 +5,14 @@ PlayerAI.__index = PlayerAI
|
|||||||
--> Services
|
--> Services
|
||||||
local Players = game:GetService("Players")
|
local Players = game:GetService("Players")
|
||||||
local ServerStorage = game:GetService("ServerStorage")
|
local ServerStorage = game:GetService("ServerStorage")
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
--> Dependencies
|
--> Dependencies
|
||||||
local TypeList = require(ServerStorage.Base.TypeList)
|
local TypeList = require(ServerStorage.Base.TypeList)
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_PlayerAI = ReplicatedStorage.Events.RE_PlayerAI
|
||||||
|
|
||||||
--> Variables
|
--> Variables
|
||||||
local DamageProxy = require(ServerStorage.Proxy.DamageProxy)
|
local DamageProxy = require(ServerStorage.Proxy.DamageProxy)
|
||||||
local ActivePlayers = {}
|
local ActivePlayers = {}
|
||||||
@ -42,14 +46,21 @@ function PlayerAI.new(Player: Player, PlayerRole: TypeList.Character)
|
|||||||
self.Character = PlayerRole
|
self.Character = PlayerRole
|
||||||
self.Player = Player
|
self.Player = Player
|
||||||
self.ExecutingState = false
|
self.ExecutingState = false
|
||||||
|
self.PlayerControling = false
|
||||||
|
|
||||||
self.BehaviourList = {}
|
self.BehaviourList = {}
|
||||||
self.LoopTask = task.spawn(function()
|
self.LoopTask = task.spawn(function()
|
||||||
while task.wait(0.25) do
|
while task.wait(0.25) do
|
||||||
|
if self.Character.Stats.Died then continue end
|
||||||
|
if self.PlayerControling then continue end
|
||||||
self:Update()
|
self:Update()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
self.PlayerControlCon = RE_PlayerAI.OnServerEvent:Connect(function(Player: Player, ControlState: boolean)
|
||||||
|
if Player ~= self.Player then return end
|
||||||
|
self.PlayerControling = ControlState
|
||||||
|
end)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -48,11 +48,13 @@ end
|
|||||||
|
|
||||||
function PlayerRole:Died()
|
function PlayerRole:Died()
|
||||||
self:ChangeState("Died", true)
|
self:ChangeState("Died", true)
|
||||||
|
self.Humanoid.WalkSpeed = 0
|
||||||
LevelProxy:ChallengeEnd(self.Player, false)
|
LevelProxy:ChallengeEnd(self.Player, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
function PlayerRole:Respawn()
|
function PlayerRole:Respawn()
|
||||||
self:ChangeState("Died", false)
|
self:ChangeState("Died", false)
|
||||||
|
self.Humanoid.WalkSpeed = self.Config.walkSpeed
|
||||||
self:ChangeAttributeValue("hp", self.Config.maxhp)
|
self:ChangeAttributeValue("hp", self.Config.maxhp)
|
||||||
-- 重置玩家位置
|
-- 重置玩家位置
|
||||||
|
|
||||||
|
@ -12,6 +12,11 @@ local ArchiveProxy = require(ServerStorage.Proxy.ArchiveProxy)
|
|||||||
--> Json
|
--> Json
|
||||||
local JsonPlayerLv = require(ReplicatedStorage.Json.PlayerLv)
|
local JsonPlayerLv = require(ReplicatedStorage.Json.PlayerLv)
|
||||||
local JsonItem = require(ReplicatedStorage.Json.ItemProp)
|
local JsonItem = require(ReplicatedStorage.Json.ItemProp)
|
||||||
|
local JsonAttributesUpgrade = require(ReplicatedStorage.Json.AttributesUpgrade)
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_PlayerTip = ReplicatedStorage.Events.RE_PlayerTip
|
||||||
|
local RE_UpgradeAttributes = ReplicatedStorage.Events.RE_UpgradeAttributes
|
||||||
|
|
||||||
--> Constants
|
--> Constants
|
||||||
local STORE_NAME = "PlayerInfo"
|
local STORE_NAME = "PlayerInfo"
|
||||||
@ -34,7 +39,7 @@ local function GetPlayerInfoFolder(Player: Player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- 创建玩家信息实例
|
-- 创建玩家信息实例
|
||||||
local function CreateInfoInstance(Player: Player, Folder: any, StateKey: string, StateType: string, StateValue: any)
|
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
|
if not Player and not Folder and not StateKey and not StateType then
|
||||||
warn('创建玩家信息实例失败: ' , Player.Name, Folder.Name, StateKey, StateType, StateValue)
|
warn('创建玩家信息实例失败: ' , Player.Name, Folder.Name, StateKey, StateType, StateValue)
|
||||||
return
|
return
|
||||||
@ -81,21 +86,83 @@ function PlayerInfoProxy:InitPlayer(Player: Player)
|
|||||||
local PlayerInfoFolder = Utils:CreateFolder("PlayerInfo", pData)
|
local PlayerInfoFolder = Utils:CreateFolder("PlayerInfo", pData)
|
||||||
local StatsFolder = Utils:CreateFolder("Stats", PlayerInfoFolder)
|
local StatsFolder = Utils:CreateFolder("Stats", PlayerInfoFolder)
|
||||||
local ItemsFolder = Utils:CreateFolder("Items", PlayerInfoFolder)
|
local ItemsFolder = Utils:CreateFolder("Items", PlayerInfoFolder)
|
||||||
|
local AttributesUpgradeFolder = Utils:CreateFolder("AttributesUpgrade", PlayerInfoFolder)
|
||||||
|
|
||||||
-- 新玩家数据初始化
|
-- 新玩家数据初始化
|
||||||
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
|
if not ArchiveProxy.pData[Player.UserId][STORE_NAME] then
|
||||||
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
|
ArchiveProxy.pData[Player.UserId][STORE_NAME] = {}
|
||||||
ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats = {}
|
ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats = {}
|
||||||
ArchiveProxy.pData[Player.UserId][STORE_NAME].Items = {}
|
ArchiveProxy.pData[Player.UserId][STORE_NAME].Items = {}
|
||||||
|
ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 放在外面是为了以后系统新增内容方便(同时不用在初始化数据是做写入了)
|
-- 放在外面是为了以后系统新增内容方便(同时不用在初始化数据是做写入了)
|
||||||
ExtraAddPlayerStats(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats)
|
ExtraAddPlayerStats(Player, ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats)
|
||||||
|
|
||||||
-- 创建玩家信息实例
|
-- 创建玩家信息实例
|
||||||
|
for ItemId, ItemValue in ArchiveProxy.pData[Player.UserId][STORE_NAME].Items do
|
||||||
|
CreateInfoInstance(Player, ItemsFolder, ItemId, ENUM_STATE_TYPE.Number, ItemValue)
|
||||||
|
end
|
||||||
for StateKey, StateData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats do
|
for StateKey, StateData in ArchiveProxy.pData[Player.UserId][STORE_NAME].Stats do
|
||||||
CreateInfoInstance(Player, StatsFolder, StateKey, StateData.type, StateData.value)
|
CreateInfoInstance(Player, StatsFolder, StateKey, StateData.type, StateData.value)
|
||||||
end
|
end
|
||||||
|
for AttributeId, AttributeLv in ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade do
|
||||||
|
CreateInfoInstance(Player, AttributesUpgradeFolder, AttributeId, "NumberValue", AttributeLv)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 玩家属性升级
|
||||||
|
function PlayerInfoProxy:UpgradeAttribute(Player: Player, AttributeId: number)
|
||||||
|
if not Player or not AttributeId then warn('升级属性失败: ', Player.Name, AttributeId) return end
|
||||||
|
|
||||||
|
local AttributesUpgradeFolder = GetPlayerInfoFolder(Player):FindFirstChild("AttributesUpgrade")
|
||||||
|
|
||||||
|
local upgradeTable = ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade
|
||||||
|
local nowLv = upgradeTable[AttributeId]
|
||||||
|
if nowLv == nil then nowLv = 0 end
|
||||||
|
|
||||||
|
local attributeData = Utils:GetIdDataFromJson(JsonAttributesUpgrade, AttributeId)
|
||||||
|
local requireBattleValue, requireMoney
|
||||||
|
|
||||||
|
if nowLv == 0 then
|
||||||
|
requireMoney = attributeData["cost"][2]
|
||||||
|
requireBattleValue = attributeData["battleValueLimit"][1]
|
||||||
|
else
|
||||||
|
requireMoney = attributeData["cost"][2] + (nowLv - 1) * attributeData["cost"][3]
|
||||||
|
requireBattleValue = attributeData["battleValueLimit"][1] + (nowLv - 1) * attributeData["battleValueLimit"][2]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO 判断战力是否足够(暂无战力计算)
|
||||||
|
|
||||||
|
-- 判断是否到达等级上限
|
||||||
|
print(nowLv, attributeData["maxLv"])
|
||||||
|
if attributeData["maxLv"] ~= nil then
|
||||||
|
if nowLv >= attributeData["maxLv"] then
|
||||||
|
local tip = '升级属性失败: ' .. AttributeId .. ' 已到达等级上限'
|
||||||
|
RE_PlayerTip:FireClient(Player, tip)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 判断钱是否足够
|
||||||
|
if not self:HasEnoughItem(Player, attributeData["cost"][1], requireMoney) then
|
||||||
|
local tip = '升级属性失败: ' .. AttributeId .. ' 钱不够'
|
||||||
|
RE_PlayerTip:FireClient(Player, tip)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- 扣钱
|
||||||
|
self:ChangeItemCount(Player, 1, -requireMoney)
|
||||||
|
|
||||||
|
-- 升级
|
||||||
|
if nowLv == 0 then
|
||||||
|
-- 首次生成属性
|
||||||
|
upgradeTable[AttributeId] = 1
|
||||||
|
CreateInfoInstance(Player, AttributesUpgradeFolder, AttributeId, "NumberValue", upgradeTable[AttributeId])
|
||||||
|
else
|
||||||
|
-- 更新属性显示
|
||||||
|
upgradeTable[AttributeId] = nowLv + 1
|
||||||
|
ChangeInfoInstance(Player, AttributesUpgradeFolder, AttributeId, upgradeTable[AttributeId])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 添加经验
|
-- 添加经验
|
||||||
@ -161,6 +228,29 @@ function PlayerInfoProxy:GetItemCount(Player: Player, ItemId: number)
|
|||||||
return playerInfoData[ItemId]
|
return playerInfoData[ItemId]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- 获取升级加点属性
|
||||||
|
function PlayerInfoProxy:GetPlayerUpgradeAttributes(Player: Player)
|
||||||
|
if not Player then warn('获取玩家属性失败: ', Player.Name) return end
|
||||||
|
local playerInfoData = ArchiveProxy.pData[Player.UserId][STORE_NAME]
|
||||||
|
local attributes = {}
|
||||||
|
for AttributeId, AttributeLv in playerInfoData.AttributesUpgrade do
|
||||||
|
local attributeData = Utils:GetIdDataFromJson(JsonAttributesUpgrade, AttributeId)
|
||||||
|
attributes[attributeData["effectAttribute"]] = attributeData["lvAdd"][1] + (AttributeLv - 1) * attributeData["lvAdd"][2]
|
||||||
|
end
|
||||||
|
return attributes
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 获取玩家属性
|
||||||
|
function PlayerInfoProxy:GetPlayerAttributes(Player: Player)
|
||||||
|
local attributesList = {}
|
||||||
|
attributesList.UpgradeAttributes = self:GetPlayerUpgradeAttributes(Player)
|
||||||
|
return attributesList
|
||||||
|
end
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
function PlayerInfoProxy:OnPlayerRemoving(Player: Player)
|
function PlayerInfoProxy:OnPlayerRemoving(Player: Player)
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -169,4 +259,9 @@ ReplicatedStorage.Remotes.PlayerRemoving.Event:Connect(function(Player: Player)
|
|||||||
PlayerInfoProxy:OnPlayerRemoving(Player)
|
PlayerInfoProxy:OnPlayerRemoving(Player)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
RE_UpgradeAttributes.OnServerEvent:Connect(function(Player: Player, AttributeId: number)
|
||||||
|
PlayerInfoProxy:UpgradeAttribute(Player, AttributeId)
|
||||||
|
print(ArchiveProxy.pData[Player.UserId][STORE_NAME].AttributesUpgrade)
|
||||||
|
end)
|
||||||
|
|
||||||
return PlayerInfoProxy
|
return PlayerInfoProxy
|
32
src/StarterPlayerScripts/ClientMain/Helper.luau
Normal file
32
src/StarterPlayerScripts/ClientMain/Helper.luau
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
local Helper = {}
|
||||||
|
|
||||||
|
--> Server
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_PlayerHelper = ReplicatedStorage.Events.RE_PlayerHelper
|
||||||
|
local RE_UpgradeAttributes = ReplicatedStorage.Events.RE_UpgradeAttributes
|
||||||
|
|
||||||
|
local UserInputService = game:GetService("UserInputService")
|
||||||
|
|
||||||
|
UserInputService.InputBegan:Connect(function(input, gameProcessed)
|
||||||
|
if gameProcessed then return end
|
||||||
|
if input.UserInputType == Enum.UserInputType.Keyboard then
|
||||||
|
if input.KeyCode == Enum.KeyCode.H then
|
||||||
|
RE_PlayerHelper:FireServer("CleanPlayerData")
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.J then
|
||||||
|
print("添加物品")
|
||||||
|
RE_PlayerHelper:FireServer("AddItem", {1, 100})
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.K then
|
||||||
|
RE_UpgradeAttributes:FireServer(1)
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.L then
|
||||||
|
RE_UpgradeAttributes:FireServer(2)
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.M then
|
||||||
|
RE_UpgradeAttributes:FireServer(3)
|
||||||
|
elseif input.KeyCode == Enum.KeyCode.N then
|
||||||
|
RE_UpgradeAttributes:FireServer(4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return Helper
|
31
src/StarterPlayerScripts/ClientMain/PlayerControl.luau
Normal file
31
src/StarterPlayerScripts/ClientMain/PlayerControl.luau
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
local UserInputService = game:GetService("UserInputService")
|
||||||
|
|
||||||
|
local PlayerControl = {}
|
||||||
|
|
||||||
|
-- 监听按键按下
|
||||||
|
function PlayerControl.ListenMoveKeyDown(onKeyDown)
|
||||||
|
UserInputService.InputBegan:Connect(function(input, gameProcessed)
|
||||||
|
if gameProcessed then return end
|
||||||
|
if input.UserInputType == Enum.UserInputType.Keyboard then
|
||||||
|
local key = input.KeyCode
|
||||||
|
if key == Enum.KeyCode.W or key == Enum.KeyCode.A or key == Enum.KeyCode.S or key == Enum.KeyCode.D then
|
||||||
|
onKeyDown(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 监听按键松开
|
||||||
|
function PlayerControl.ListenMoveKeyUp(onKeyUp)
|
||||||
|
UserInputService.InputEnded:Connect(function(input, gameProcessed)
|
||||||
|
if gameProcessed then return end
|
||||||
|
if input.UserInputType == Enum.UserInputType.Keyboard then
|
||||||
|
local key = input.KeyCode
|
||||||
|
if key == Enum.KeyCode.W or key == Enum.KeyCode.A or key == Enum.KeyCode.S or key == Enum.KeyCode.D then
|
||||||
|
onKeyUp(key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
return PlayerControl
|
@ -19,6 +19,11 @@ local PlayerGui = Player:WaitForChild("PlayerGui")
|
|||||||
local FormatNumber = require(ReplicatedStorage.Modules.FormatNumber)
|
local FormatNumber = require(ReplicatedStorage.Modules.FormatNumber)
|
||||||
local Tween = require(ReplicatedStorage.Modules.Tween)
|
local Tween = require(ReplicatedStorage.Modules.Tween)
|
||||||
local SFX = require(ReplicatedStorage.Modules.SFX)
|
local SFX = require(ReplicatedStorage.Modules.SFX)
|
||||||
|
local PlayerControl = require(script.PlayerControl)
|
||||||
|
local Helper = require(script.Helper)
|
||||||
|
|
||||||
|
--> Events
|
||||||
|
local RE_PlayerAI = ReplicatedStorage.Events.RE_PlayerAI
|
||||||
|
|
||||||
--> Variables
|
--> Variables
|
||||||
local Random = Random.new()
|
local Random = Random.new()
|
||||||
@ -28,6 +33,16 @@ local ClientMainPrefabs = Player.PlayerScripts.ClientMainPrefabs
|
|||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PlayerControl.ListenMoveKeyDown(function(key)
|
||||||
|
-- print("按下了: ", key.Name)
|
||||||
|
RE_PlayerAI:FireServer(true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
PlayerControl.ListenMoveKeyUp(function(key)
|
||||||
|
-- print("松开了: ", key.Name)
|
||||||
|
RE_PlayerAI:FireServer(false)
|
||||||
|
end)
|
||||||
|
|
||||||
-- Initially require client-sided modules
|
-- Initially require client-sided modules
|
||||||
for _, Item in script:GetChildren() do
|
for _, Item in script:GetChildren() do
|
||||||
if Item:IsA("ModuleScript") then
|
if Item:IsA("ModuleScript") then
|
||||||
|
Loading…
x
Reference in New Issue
Block a user