diff --git a/.ava-x2l-config.json b/.ava-x2l-config.json deleted file mode 100644 index d0a1999..0000000 --- a/.ava-x2l-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "input_folder": "./Xls", - "output_folder": "./Code", - "output_lua_template": "['World']['Global']['Xls']['{sheet_name}XlsModule'].ModuleScript.lua", - "kv_xls": "['GlobalSetting.xlsx']", - "translate_xls": "LanguagePack.xls" -} \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 26f7556..ebc8375 100644 --- a/.gitattributes +++ b/.gitattributes @@ -19,3 +19,10 @@ *.tar filter=lfs diff=lfs merge=lfs -text *.vsdx filter=lfs diff=lfs merge=lfs -text *.exe filter=lfs diff=lfs merge=lfs -text +*.smap filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.mkv filter=lfs diff=lfs merge=lfs -text +*.avi filter=lfs diff=lfs merge=lfs -text +*.xmind filter=lfs diff=lfs merge=lfs -text +*.aep filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1ca4226..2a64a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,70 @@ -*AutoSave_* -*AutoBackup_* -xls/*.lua -xls/*.csv -csv/*.lua -csv/*.xls -csv/*.xlsm -csv/*.xlsx -code/*.csv -code/*.xls -code/*.xlsm -code/*.xlsx +# Compiled Lua sources +luac.out + +# luarocks build files +*.src.rock +*.zip +*.tar.gz + +# Object files +*.o +*.os +*.ko +*.obj +*.elf + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo +*.def +*.exp + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# 策划文档 +Design/*.lua +Design/*.smap + +# smap相关 +Smap/*.lua +Smap/Code/*.csv +Smap/Code/*.xls +Smap/Code/*.xlsm +Smap/Code/*.xlsx +Smap/Code/*.txt +*AutoSave_*.smap +*AutoBackup_*.smap +*.smap.config + +# 代码相关 +Code/*.txt +Code/*.csv +Code/*.xls +Code/*.xlsm +Code/*.xlsx +Code/*.txt +Code/*.smap + +# other *.smap.storage *.iso ~$* *.idea/ *.vscode/ -Smap/avatar-ava-test.smap diff --git a/Code/['World']['Client']['Init']['ClientAwakeScript'].AutorunScript.lua b/Code/['World']['Client']['Init']['ClientAwakeScript'].AutorunScript.lua deleted file mode 100644 index 8398d99..0000000 --- a/Code/['World']['Client']['Init']['ClientAwakeScript'].AutorunScript.lua +++ /dev/null @@ -1,7 +0,0 @@ ---- 客户端代码入口 --- @script Client Awake Function --- @copyright Lilith Games, Avatar Team, Dead Ratman --- @author Dead Ratman -_G.C = Client -ModuleUtil.LoadModules(world.Client.Module, C) -C:Run() diff --git a/Code/['World']['Client']['Module']['LanguageUtilModule'].ModuleScript.lua b/Code/['World']['Client']['Module']['LanguageUtilModule'].ModuleScript.lua deleted file mode 100644 index d893eb4..0000000 --- a/Code/['World']['Client']['Module']['LanguageUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,32 +0,0 @@ ---- 语言包模块:根据游戏内语言设置返回对应的语言文本 ---- @module LanguageUtil, Client-side ---- @copyright Lilith Games, Avatar Team ---- @author Xiexy, Yuancheng Zhang -local LanguageUtil, this = ModuleUtil.New('LanguageUtil', ClientBase) -local lang = Config.GlobalSetting.DefaultLanguage -local defaultLang = K.LanguageEnum.CHS - ---- 设置当前语言 -function LanguageUtil.SetLanguage(_lang) - assert(K.LanguageEnum[_lang], string.format('[LanguageUtil] %s 语言码不存在,请检查ConstModule', _lang)) - print(string.format('[LanguageUtil] 更改当前语言:%s => %s', lang, _lang)) - lang = _lang -end - ---- 根据ID返回当前游戏语言对应的文本信息,如果对应语言为空,默认返回'*'+中文内容 --- @param @number _id LanguagePack.xls中的编号 -function LanguageUtil.GetText(_id) - assert(not string.isnilorempty(_id), '[LanguageUtil] 翻译ID为空,请检查策划表和LanguagePack') - assert( - Config.LanguagePack[_id], - string.format('[LanguageUtil] LanguagePack[%s] 不存在对应翻译ID,请检查策划表和LanguagePack', _id) - ) - local text = Config.LanguagePack[_id][lang] - if string.isnilorempty(text) then - print(string.format('[LanguageUtil] LanguagePack[%s][%s] 不存在对应语言翻译内容,默认使用中文', _id, lang)) - text = '*' .. Config.LanguagePack[_id][defaultLang] - end - return text -end - -return LanguageUtil diff --git a/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua b/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua deleted file mode 100644 index 98c605f..0000000 --- a/Code/['World']['Global']['AutoAssignTeamScript'].Script.lua +++ /dev/null @@ -1,8 +0,0 @@ ---- 玩家加入 --- @script Auto assign --- @copyright Lilith Games, Avatar Team - ---- 编辑器默认方法, 删了报错 --- run once when script init -autoAssign = function() -end diff --git a/Code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua deleted file mode 100644 index e9ad873..0000000 --- a/Code/['World']['Global']['Define']['ConfigModule'].ModuleScript.lua +++ /dev/null @@ -1,18 +0,0 @@ ---- CSV表格的定义,用于CSV表格载入 --- @module Csv Defines --- @copyright Lilith Games, Avatar Team -local Config = {} - --- 服务器预加载CSV --- csv: 对应的CSV表名 --- name: Config里面的lua table名称, 可自定义, 默认和csv相同 --- ids: 表格主键, 支持多主键 -Config.ServerPreload = {} - --- 客户端预加载CSV --- csv: 对应的CSV表名 --- name: Config里面的lua table名称, 可自定义, 默认和csv相同 --- ids: 表格主键, 支持多主键 -Config.ClientPreload = {} - -return Config diff --git a/Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua deleted file mode 100644 index 6989088..0000000 --- a/Code/['World']['Global']['Define']['ConstModule'].ModuleScript.lua +++ /dev/null @@ -1,23 +0,0 @@ ---- 全局常量的定义,全部定义在Const这张表下面,用于定义全局常量参数或者枚举类型 --- @module Constant Defines --- @copyright Lilith Games, Avatar Team -local Const = {} - --- 将全局变量Const替换成K -_G.K = Const - --- e.g. (need DELETE) -Const.MAX_PLAYERS = 4 - --- 语言枚举F -Const.LanguageEnum = { - CHS = 'CHS', -- 简体中文 - CHT = 'CHT', -- 繁体中文 - EN = 'EN', -- 英文 - JP = 'JP' -- 日文 -} - --- FixedUpdate执行间隔 -Const.FixedUpdateInterval = 1 - -return Const diff --git a/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua deleted file mode 100644 index 3595d73..0000000 --- a/Code/['World']['Global']['Define']['DefaultModule'].ModuleScript.lua +++ /dev/null @@ -1,27 +0,0 @@ ---- 全局默认定义:用于定义数据,节点属性等 ---- @module Data Default ---- @copyright Lilith Games, Avatar Team -local Default = {} - ---! 说明:这个module当作脚本使用 - ---* Data.Global和Data.Player中的默认值,用于框架初始化 - -Data.Default = Data.Default or {} - --- 全局变量定义 -Data.Default.Global = {} - --- 玩家数据,初始化定义 -Data.Default.Player = { - -- 玩家ID, 框架默认 - uid = '', - -- 玩家属性 - attr = {}, - -- 背包 - bag = {}, - -- 统计数据 - stats = {} -} - -return Default diff --git a/Code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua b/Code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua deleted file mode 100644 index 2b207d8..0000000 --- a/Code/['World']['Global']['Define']['EventsModule'].ModuleScript.lua +++ /dev/null @@ -1,14 +0,0 @@ ---- CustomEvent的定义,用于事件动态生成 --- @module Event Defines --- @copyright Lilith Games, Avatar Team -local Events = {} - --- 服务器事件列表 -Events.ServerEvents = {} - --- 客户端事件列表 -Events.ClientEvents = { - --通知事件 - 'NoticeEvent' -} -return Events diff --git a/Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua deleted file mode 100644 index 1d5ef92..0000000 --- a/Code/['World']['Global']['Framework']['Client']['ClientBaseModule'].ModuleScript.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- 客户端模块基础类, Client Module Base Class --- @module ClientBase, Client-side --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang, Dead Ratman -local ClientBase = class('ClientBase') - -function ClientBase:GetSelf() - return self -end - ---- 加载的时候运行的代码 -function ClientBase:InitDefault(_module) - -- print(string.format('[ClientBase][%s] InitDefault()', self.name)) - -- 初始化默认监听事件 - EventUtil.LinkConnects(localPlayer.C_Event, _module, self) - self.fixedUpdateInterval = K.FixedUpdateInterval -end - ---- Debug模式下打印日志 --- self.debug 针对模块本身的debug开关 --- FrameworkConfig.DebugMode 框架中的全局debug开关 -function ClientBase:Log(...) - if self.debug and FrameworkConfig.DebugMode then - print(string.format('[%s]', self.name), ...) - end -end - -return ClientBase diff --git a/Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua deleted file mode 100644 index b24f736..0000000 --- a/Code/['World']['Global']['Framework']['Client']['ClientHeartbeatModule'].ModuleScript.lua +++ /dev/null @@ -1,202 +0,0 @@ ---- 游戏心跳 ---- @module Client Heartbeat, Client-side ---- @copyright Lilith Games, Avatar Team ---- @author Yuancheng Zhang -local ClientHeartbeat = {} - --- Localize global vars -local FrameworkConfig = FrameworkConfig - --- 心跳包间隔时间,单位:秒 -local HEARTBEAT_DELTA = FrameworkConfig.Client.HeartbeatDelta - --- 心跳阈值,单位:秒,范围定义如下: --- 0s -> threshold_1 : connected --- threshold_1 -> threshold_2 : disconnected, weak network --- threshold_2 -> longer : disconnected, quit server -local HEARTBEAT_THRESHOLD_1 = FrameworkConfig.Client.HeartbeatThreshold1 * 1000 -- second => ms -local HEARTBEAT_THRESHOLD_2 = FrameworkConfig.Client.HeartbeatThreshold2 * 1000 -- second => ms - --- 玩家心跳连接状态 -local HeartbeatEnum = { - CONNECT = 1, -- 在线 - DISCONNECT = 2 -- 离线 -} - --- 正在运行 -local running = false - --- 上一次服务器发来的心跳时间戳缓存 -local cache = { - sTimestamp = nil, - cTimestamp = nil -} - --- 临时变量 -local diff -- 时间戳插值 -local sTmpTs, cTmpTs -- 时间戳缓存 - ---- 打印心跳日志 ---这段代码就是先判断Setting.ShowHeartbeatLog这个配置项是否为真 ---若为真则PrintHb 为一个打印日志的函数 若为假则为一个空函数 -local PrintHb = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowHeartbeatLog and function(...) - print('[Heartbeat][Client]', ...) - end or function() - end - ---! 外部接口 - ---- 初始化心跳包 -function ClientHeartbeat.Init() - print('[Heartbeat][Client] Init()') - --校验心跳参数 - CheckSetting() - --初始化事件和绑定Handler - InitEventsAndListeners() -end - ---- 开始发出心跳 -function ClientHeartbeat.Start() - print('[Heartbeat][Client] Start()') - local cTimestamp - running = true - while (running) do - Update() - -- 每隔心跳包间隔时间,则检查一次心跳 - wait(HEARTBEAT_DELTA) - end -end - --- 停止心跳 -function ClientHeartbeat.Stop() - print('[Heartbeat][Client] Stop()') - running = false -end - ---! 私有函数 - --- 校验心跳参数 -function CheckSetting() - -- API-assert() - --当第一个参数的值是false或者nil的时候展示一个错误,否则返回所有的参数值。 - assert(HEARTBEAT_DELTA >= 1, '[Heartbeat][Client] HEARTBEAT_DELTA 必须大于1秒') - assert(HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA, '[Heartbeat][Client] HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA') - assert( - HEARTBEAT_THRESHOLD_2 >= HEARTBEAT_THRESHOLD_1, - '[Heartbeat][Client] HEARTBEAT_THRESHOLD_2 >= HEARTBEAT_THRESHOLD_1' - ) -end - ---- 初始化事件和绑定Handler -function InitEventsAndListeners() - --创建客户端事件节点 - if localPlayer.C_Event == nil then - world:CreateObject('FolderObject', 'C_Event', localPlayer) - end - world:CreateObject('CustomEvent', 'HeartbeatS2CEvent', localPlayer.C_Event) - localPlayer.C_Event.HeartbeatS2CEvent:Connect(HeartbeatS2CEventHandler) - - -- OnPlayerJoinEvent(玩家第一次加入,类似现在的OnPlayerAdded) - -- OnPlayerRejoinEvent(玩家离线后重新进入同一个房间) - -- OnPlayerDisconnectEvent(未接收到服务器心跳,在客户端第二个阶段,玩家离线可重连,弱网,转菊花) - -- OnPlayerReconnectEvent(玩家断线后重连) - -- OnPlayerLeaveEvent(玩家彻底离开,退出房间) - --在world下创建CustomEvent - world:CreateObject('CustomEvent', 'OnPlayerJoinEvent', localPlayer.C_Event) - -- world:CreateObject('CustomEvent', 'OnPlayerRejoinEvent', localPlayer.C_Event) - world:CreateObject('CustomEvent', 'OnPlayerDisconnectEvent', localPlayer.C_Event) - world:CreateObject('CustomEvent', 'OnPlayerReconnectEvent', localPlayer.C_Event) - world:CreateObject('CustomEvent', 'OnPlayerLeaveEvent', localPlayer.C_Event) - - -- 掉线直接退出(默认,可选) - localPlayer.C_Event.OnPlayerLeaveEvent:Connect(QuitGame) -end - --- Update心跳 -function Update() - --获取客户端运行时间 - cTmpTs = Timer.GetTimeMillisecond() - --sTmpTs获取上一次服务器发来的服务端心跳时间戳缓存 - sTmpTs = cache.sTimestamp - PrintHb(string.format('=> C = %s, S = %s, %s', cTmpTs, sTmpTs, localPlayer)) - --根据心跳时间戳检查玩家状态 - --发包时,检查玩家是否连接服务器 - CheckPlayerState(p, cTmpTs) - --向服务端发送 心跳事件 - NetUtil.Fire_S('HeartbeatC2SEvent', localPlayer, cTmpTs, sTmpTs) -end - ---- 心跳事件Handler -function HeartbeatS2CEventHandler(_stimestamp, _cTimestamp) - if not running then - return - end - --- 打印心跳日志 - PrintHb(string.format('<= C = %s, S = %s, %s', _cTimestamp, _stimestamp, localPlayer)) - --- 收包时,检查玩家是否连接服务器,或者重新连接服务器 - CheckPlayerJoin(_player, _sTimestamp) - --更新上一次服务器发来的服务端心跳时间戳缓存的时间数据 - cache.sTimestamp = _stimestamp - cache.cTimestamp = _cTimestamp -end - ---- 收包时,检查玩家是否连接服务器,或者重新连接服务器 -function CheckPlayerJoin(_player, _sTimestamp) - --如果cache的sTimestamp不存在,则可以加入玩家 - if not cache.sTimestamp then - --* 玩家新加入 OnPlayerJoinEvent - print('[Heartbeat][Client] OnPlayerJoinEvent, 新玩家加入,', localPlayer, localPlayer.UserId) - --如果本地玩家的OnPlayerJoinEvent事件存在,则向客户端发送新玩家加入事件消息 - NetUtil.Fire_C('OnPlayerJoinEvent', localPlayer, localPlayer.UserId) - --设置状态为“在线” - cache.state = HeartbeatEnum.CONNECT - --如果状态为“离线” - elseif cache.state == HeartbeatEnum.DISCONNECT then - --* 玩家断线重连 OnPlayerReconnectEvent - print('[Heartbeat][Client] OnPlayerReconnectEvent, 玩家断线重连,', localPlayer, localPlayer.UserId) - --向客户端发送新玩家重连事件消息 - NetUtil.Fire_C('OnPlayerReconnectEvent', localPlayer, localPlayer.UserId) - --设置状态为“在线” - cache.state = HeartbeatEnum.CONNECT - end -end - ---- 发包时,检查玩家是否连接服务器 -function CheckPlayerState(_player, _cTimestamp) - -- 如果客户端的心跳时间戳缓存不存在,则返回 - if not cache.cTimestamp then - return - end - --diff 时间戳插值 = 当前客户端的时间值- 服务端保存的客户端时间值 - diff = _cTimestamp - cache.cTimestamp - PrintHb(string.format('==========================================> diff = %s, %s', diff * .001, localPlayer)) - --如果diff<心跳阈值1 - if diff < HEARTBEAT_THRESHOLD_1 then - --* 玩家在线 - --设置服务端的心跳时间戳状态是“在线”状态 - cache.state = HeartbeatEnum.CONNECT - --如果diff>=心跳阈值1并且服务端发来的心跳时间戳状态是“在线”状态 - elseif cache.state == HeartbeatEnum.CONNECT and diff >= HEARTBEAT_THRESHOLD_1 then - --* 玩家断线,弱网环境 - print('[Heartbeat][Client] OnPlayerDisconnectEvent, 玩家离线, 弱网环境,', localPlayer) - --向客户端发出玩家断线的事件消息 - NetUtil.Fire_C('OnPlayerDisconnectEvent', localPlayer, localPlayer.UserId) - --设置服务端的心跳时间戳状态是“离线”状态 - cache.state = HeartbeatEnum.DISCONNECT - --如果diff>=心跳阈值2并且玩家客户端发来的心跳时间戳状态是“离线”状态 - elseif cache.state == HeartbeatEnum.DISCONNECT and diff >= HEARTBEAT_THRESHOLD_2 then - --* 玩家断线, 退出游戏 - -- QuitGame() - --向客户端发出玩家离开的事件消息 - NetUtil.Fire_C('OnPlayerLeaveEvent', localPlayer, localPlayer.UserId) - end -end - ---- 退出游戏 -function QuitGame() - print('[Heartbeat][Client] Game.Quit(), 玩家退出游戏', localPlayer, localPlayer.UserId) - --关闭游戏 - Game.Quit() -end - -return ClientHeartbeat diff --git a/Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua deleted file mode 100644 index 7766a4b..0000000 --- a/Code/['World']['Global']['Framework']['Client']['ClientModule'].ModuleScript.lua +++ /dev/null @@ -1,219 +0,0 @@ ---- 游戏客户端主逻辑 --- @module Game Manager, Client-side --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang -local Client = {} - --- Localize global vars -local CsvUtil, XslUitl, ModuleUtil = CsvUtil, XslUitl, ModuleUtil --- Config:框架配置的客户端数据 -local Config = FrameworkConfig.Client -local this - --- 已经初始化,正在运行 -local initialized, running = false, false - --- 含有InitDefault(),Init(),Update()的模块列表 -local initDefaultList, awakeList, startList, onPreRenderList, updateList, lateUpdateList, fixedUpdateList = {}, - {}, - {}, - {}, - {}, - {}, - {} - ---- 运行客户端 -function Client:Run() - print('[Client] Run()') - this = self - --初始化客户端 - InitClient() - --开启客户端的Update - invoke(StartUpdate) - --开启客户端的FixUpdate - invoke(StartFixedUpdate) -end - ---- 停止Update -function Client:Stop() - print('[Client] Stop()') - running = false - -- 客户端心跳停止 - -- 位置:ClientHeartbeatModule - ClientHeartbeat.Stop() -end - ---- 初始化 -function InitClient() - if initialized then - return - end - print('[Client] InitClient()') - --- 初始化客户端随机种子 - InitRandomSeed() - --- 初始化心跳包 - InitHeartbeat() - --- 初始化数据同步 - InitDataSync() - --- 初始化客户端的CustomEvent - InitClientCustomEvents() - --- 生成需要Init和Update的模块列表 - GenInitAndUpdateList() - --- 执行默认的Init方法 - RunInitDefault() - --- 初始化包含Awake()和Start()方法的模块 - InitOtherModules() - initialized = true -end - ---- 初始化心跳包 -function InitHeartbeat() - assert(ClientHeartbeat, '[Client][Heartbeat] 找不到ClientHeartbeat,请联系张远程') - --位置:Framework.Client.ClientHeartbeatModule - ClientHeartbeat.Init() -end - ---- 初始化数据同步 -function InitDataSync() - assert(ClientDataSync, '[Server][DataSync] 找不到ClientDataSync,请联系张远程') - ClientDataSync.Init() -end - ---- 初始化客户端的CustomEvent -function InitClientCustomEvents() - --如果localPlayer目录下缺少C_Event节点则创建 - if localPlayer.C_Event == nil then - world:CreateObject('FolderObject', 'C_Event', localPlayer) - end - - -- 生成CustomEvent节点 - for _, evt in pairs(Events.ClientEvents) do - --如果localPlayer.C_Event不存在该节点则创建 - if localPlayer.C_Event[evt] == nil then - world:CreateObject('CustomEvent', evt, localPlayer.C_Event) - end - end -end - ---- 生成需要Init和Update的模块列表 -function GenInitAndUpdateList() - -- TODO: 改成在FrameworkConfig中配置 - -- Init Default - --位置 ModuleUtilModule - --- 将有包含特定方法的模块筛选出来,并放在一个table中 - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'InitDefault', initDefaultList, this) - -- Awake - ModuleUtil.GetModuleListWithFunc(Define, 'Awake', awakeList) - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'Awake', awakeList, this) - -- Start - ModuleUtil.GetModuleListWithFunc(Define, 'Start', startList) - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'Start', startList, this) - -- OnPreRender - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'OnPreRender', onPreRenderList, this) - -- Update - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'Update', updateList, this) - -- LateUpdate - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'LateUpdate', lateUpdateList, this) - -- FixedUpdate - ModuleUtil.GetModuleListWithFunc(world.Client.Module, 'FixedUpdate', fixedUpdateList, this) -end - ---- 执行默认的Init方法 -function RunInitDefault() - for _, m in ipairs(initDefaultList) do - m:InitDefault(m) - end -end - ---- 初始化客户端随机种子 -function InitRandomSeed() - math.randomseed(os.time()) -end - ---- 初始化包含Awake()和Start()方法的模块 -function InitOtherModules() - for _, m in ipairs(awakeList) do - m:Awake() - end - for _, m in ipairs(startList) do - m:Start() - end -end - ---- 开始Update -function StartUpdate() - print('[Client] StartUpdate()') - assert(not running, '[Client] StartUpdate() 正在运行') - - running = true - - -- 开启心跳 - if FrameworkConfig.HeartbeatStart then - invoke(ClientHeartbeat.Start) - end - - -- 开启数据同步 - ClientDataSync.Start() - - local dt = 0 -- delta time 每帧时间 - local tt = 0 -- total time 游戏总时间 - --GetTimeMillisecond()获取客户端游戏进行的总时间 - local now = Timer.GetTimeMillisecond --时间函数缓存 - local prev, curr = now() / 1000, nil -- two timestamps - - while (running and wait()) do - --每1秒执行一次Update函数 - curr = now() / 1000 - dt = curr - prev - tt = tt + dt - prev = curr - UpdateClient(dt, tt) - LateUpdateClient(dt, tt) - end -end - -world.OnRenderStepped:Connect( - function(dt) - OnPreRenderClient(dt) - end -) - ---- 开始FixedUpdate -function StartFixedUpdate() - for _, m in ipairs(fixedUpdateList) do - invoke( - function() - while running do - m:FixedUpdate() - wait(m.fixedUpdateInterval) - end - end - ) - end -end - ---- OnPreRender函数 --- @param dt delta time 每帧时间 -function OnPreRenderClient(_dt) - for _, m in ipairs(onPreRenderList) do - m:OnPreRender(_dt) - end -end - ---- Update函数 --- @param dt delta time 每帧时间 -function UpdateClient(_dt, _tt) - for _, m in ipairs(updateList) do - m:Update(_dt, _tt) - end -end - ---- LateUpdate函数 --- @param dt delta time 每帧时间 -function LateUpdateClient(_dt, _tt) - for _, m in ipairs(lateUpdateList) do - m:LateUpdate(_dt, _tt) - end -end - -return Client diff --git a/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua deleted file mode 100644 index 621752a..0000000 --- a/Code/['World']['Global']['Framework']['DataModule'].ModuleScript.lua +++ /dev/null @@ -1,18 +0,0 @@ ---- 游戏数据 ---- @module Game Data, Both-side ---- @copyright Lilith Games, Avatar Team ---- @author Yuancheng Zhang -local Data = {} - --- 客户端 --- 1. Data.Global --- 2. Data.Player - --- 服务器 --- 1. Data.Global --- 2. Data.Players - ---! 这个Module为空 ---! 数据定义在:ClientDataSyncModule、ServerDataSyncModule - -return Data diff --git a/Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua deleted file mode 100644 index 6cbb1f7..0000000 --- a/Code/['World']['Global']['Framework']['Server']['ServerBaseModule'].ModuleScript.lua +++ /dev/null @@ -1,28 +0,0 @@ ---- 服务器模块基础类, Server Module Base Class --- @module ServerBase, Server-side --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang, Dead Ratman -local ServerBase = class('ServerBase') - -function ServerBase:GetSelf() - return self -end - ---- 加载的时候运行的代码 -function ServerBase:InitDefault(_module) - -- print(string.format('[ServerBase][%s] InitDefault()', self.name)) - -- 初始化默认监听事件 - EventUtil.LinkConnects(world.S_Event, _module, self) - self.fixedUpdateInterval = K.FixedUpdateInterval -end - ---- Debug模式下打印日志 --- self.debug 针对模块本身的debug开关 --- FrameworkConfig.DebugMode 框架中的全局debug开关 -function ServerBase:Log(...) - if self.debug and FrameworkConfig.DebugMode then - print(string.format('[%s]', self.name), ...) - end -end - -return ServerBase diff --git a/Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua b/Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua deleted file mode 100644 index 6d32c56..0000000 --- a/Code/['World']['Global']['Framework']['Server']['ServerModule'].ModuleScript.lua +++ /dev/null @@ -1,218 +0,0 @@ ---- 游戏服务器主逻辑 ---- @module Game Server, Server-side ---- @copyright Lilith Games, Avatar Team ---- @author Yuancheng Zhang -local Server = {} - --- Localize global vars -local CsvUtil, ModuleUtil = CsvUtil, ModuleUtil --- Config:框架配置的数据 -local Config = FrameworkConfig.Server -local this - --- 已经初始化,正在运行 -local initialized, running = false, false - --- 含有InitDefault(),Init(),Update()的模块列表 -local initDefaultList, awakeList, startList, updateList, lateUpdateList, fixedUpdateList = {}, {}, {}, {}, {}, {} - ---- 运行服务器 -function Server:Run() - print('[Server] Run()') - this = self - --初始化服务器 - InitServer() - --正式开始运行 - invoke(StartUpdate) - invoke(StartFixedUpdate) -end - ---- 停止Update -function Server:Stop() - print('[Server] Stop()') - running = false - -- 游戏服务器心跳停止 - -- 位置:ServerHeartbeatModule - ServerHeartbeat.Stop() -end - ---- 初始化 -function InitServer() - --如果初始化过了,则不再初始化 - if initialized then - return - end - print('[Server] InitServer()') - --- 初始化服务器随机种子 - InitRandomSeed() - --- 初始化心跳包 - InitHeartbeat() - --- 初始化数据同步 - InitDataSync() - --- 初始化服务器的CustomEvent - InitServerCustomEvents() - --- 生成框架需要的节点 - InitCsvAndXls() - --- 生成需要Init和Update的模块列表 - GenInitAndUpdateList() - --- 执行默认的Init方法 - RunInitDefault() - --- 初始化包含Init()方法的模块 - InitOtherModules() - --初始化完成 - initialized = true -end - ---- 初始化服务器的CustomEvent -function InitServerCustomEvents() - print('[Server] InitServerCustomEvents()') - --如果服务端World目录下缺少S_Event节点则创建 - if world.S_Event == nil then - world:CreateObject('FolderObject', 'S_Event', world) - end - - -- 生成CustomEvent节点 - for _, evt in pairs(Events.ServerEvents) do - --如果服务的World.S_Event不存在该节点则创建 - if world.S_Event[evt] == nil then - world:CreateObject('CustomEvent', evt, world.S_Event) - end - end -end - ---- 初始化心跳包 ---说明:判定客户端是否与服务端处于连接状态 -function InitHeartbeat() - assert(ServerHeartbeat, '[Server][Heartbeat] 找不到ServerHeartbeat,请联系张远程') - -- 位置:ServerHeartbeatModule - ServerHeartbeat.Init() -end - ---- 初始化数据同步 -function InitDataSync() - assert(ServerDataSync, '[Server][DataSync] 找不到ServerDataSync,请联系张远程') - ServerDataSync.Init() -end - ---- 生成框架需要的节点 -function InitCsvAndXls() - --在world.Global下创建节点 - if not world.Global.Csv then - world:CreateObject('FolderObject', 'Csv', world.Global) - end - if not world.Global.Xls then - world:CreateObject('FolderObject', 'Xls', world.Global) - end -end - ---- 生成需要Init和Update的模块列表 -function GenInitAndUpdateList() - -- TODO: 改成在FrameworkConfig中配置 - -- Init Default - --位置 ModuleUtilModule - --- 将有包含特定方法的模块筛选出来,并放在一个table中 - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'InitDefault', initDefaultList, this) - -- Awake - ModuleUtil.GetModuleListWithFunc(Define, 'Awake', awakeList) - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'Awake', awakeList, this) - -- Start - ModuleUtil.GetModuleListWithFunc(Define, 'Start', startList) - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'Start', startList, this) - -- Update - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'Update', updateList, this) - -- LateUpdate - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'LateUpdate', lateUpdateList, this) - -- FixedUpdate - ModuleUtil.GetModuleListWithFunc(world.Server.Module, 'FixedUpdate', fixedUpdateList, this) -end - ---- 执行默认的Init方法 -function RunInitDefault() - for _, m in ipairs(initDefaultList) do - m:InitDefault(m) - end -end - ---- 初始化服务器随机种子 -function InitRandomSeed() - math.randomseed(os.time()) -end - ---- 初始化包含Init()方法的模块 -function InitOtherModules() - for _, m in ipairs(awakeList) do - m:Awake() - end - for _, m in ipairs(startList) do - m:Start() - end -end - ---- 开始Update -function StartUpdate() - print('[Server] StartUpdate()') - assert(not running, '[Server] StartUpdate() 正在运行') - - running = true - - -- 开启心跳 - -- 位置:Framework.ServerHeartbeatModule - if FrameworkConfig.HeartbeatStart then - invoke(ServerHeartbeat.Start) - end - - -- 开启数据同步 - ServerDataSync.Start() - - local dt = 0 -- delta time 每帧时间 - local tt = 0 -- total time 游戏总时间 - local now = Timer.GetTimeMillisecond --时间函数缓存 - local prev, curr = now() / 1000, nil -- two timestamps - - while (running and wait()) do - -- 每隔1000毫秒(1秒)运行一次UpdateServer() - curr = now() / 1000 - dt = curr - prev - tt = tt + dt - prev = curr - UpdateServer(dt, tt) - LateUpdateServer(dt, tt) - end -end - ---- 开始FixedUpdate -function StartFixedUpdate() - for _, m in ipairs(fixedUpdateList) do - invoke( - function() - while running do - m:FixedUpdate() - wait(m.fixedUpdateInterval) - end - end - ) - end -end - -function ErrorShow(err) - world.Global.ErrorGUI:SetActive(true) - world.Global.ErrorGUI.Error.Text = err -end - ---- Update函数 ---- @param dt delta time 每帧时间 -function UpdateServer(_dt, _tt) - for _, m in ipairs(updateList) do - m:Update(_dt, _tt) - end -end - ---- LateUpdate函数 --- @param dt delta time 每帧时间 -function LateUpdateServer(_dt, _tt) - for _, m in ipairs(lateUpdateList) do - m:LateUpdate(_dt, _tt) - end -end - -return Server diff --git a/Code/['World']['Global']['LuaFunctionScript'].Script.lua b/Code/['World']['Global']['LuaFunctionScript'].Script.lua deleted file mode 100644 index 6c88ae9..0000000 --- a/Code/['World']['Global']['LuaFunctionScript'].Script.lua +++ /dev/null @@ -1,968 +0,0 @@ ---- 提供一组常用函数,以及对 Lua 标准库的扩展 --- @script Lua function extension libraries --- @author Lilith Games, Avatar Team --- @see https://wiki.lilithgames.com/x/tSkMAg - ---- 检查并尝试转换为数值,如果无法转换则返回 0 --- @param mixed value 要检查的值 --- @param [integer base] 进制,默认为十进制 --- @return number -function checknumber(value, base) - return tonumber(value, base) or 0 -end - ---- 检查是否是有效的number类型 --- @param number -function isValidNumber(num) - return num ~= nil and num > 0 -end - ---- 检查并尝试转换为整数,如果无法转换则返回 0 --- @param mixed value 要检查的值 --- @return integer -function checkint(value) - return math.round(checknumber(value)) -end - ---- 检查并尝试转换为布尔值,除了 nil 和 false,其他任何值都会返回 true --- @param mixed value 要检查的值 --- @return boolean -function checkbool(value) - return (value ~= nil and value ~= false) -end - ---- 检查值是否是一个表格,如果不是则返回一个空表格 --- @param mixed value 要检查的值 --- @return table -function checktable(value) - if type(value) ~= 'table' then - value = {} - end - return value -end - ---- 处理对象 --- @param mixed obj Lua 对象 --- @param function method 对象方法 --- @return function -function handler(obj, method) - return function(...) - return method(obj, ...) - end -end - ---- 计算表格包含的字段数量 --- Lua table 的 "#" 操作只对依次排序的数值下标数组有效,table.nums() 则计算 table 中所有不为 nil 的值的个数。 --- @param table -function table.nums(t) - if t == nil then - return 0 - end - local count = 0 - for _ in pairs(t) do - count = count + 1 - end - return count -end - ---- 返回指定表格中的所有键 --- @param k-v table --- @return keys' table --- @usage example --- local hashtable = {a = 1, b = 2, c = 3} --- local keys = table.keys(hashtable) --- >> keys = {"a", "b", "c"} -function table.keys(hashtable) - local keys = {} - for k, _ in pairs(hashtable) do - table.insert(keys, k) - end - return keys -end - ---- 返回指定表格中的所有值 --- @param k-v table --- @return values' table --- @usage example --- local hashtable = {a = 1, b = 2, c = 3} --- local values = table.values(hashtable) --- >> values = {1, 2, 3} -function table.values(hashtable) - local values = {} - local i = 1 - for k, v in pairs(hashtable) do - values[i] = v - i = i + 1 - end - return values -end - ---- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 --- @param target table --- @param source table --- @usage example --- local dest = {a = 1, b = 2} --- local src = {c = 3, d = 4} --- table.merge(dest, src) --- >> dest = {a = 1, b = 2, c = 3, d = 4} -function table.merge(dest, src) - for k, v in pairs(src) do - dest[k] = v - end -end - ---- 深度将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值,如果存在子表,则遍历子表进行复制 -function table.deepMerge(dest, src) - for k, v in pairs(src) do - if type(v) == 'table' then - if dest[k] == nil then - dest[k] = {} - end - table.deepMerge(dest[k], v) - else - dest[k] = v - end - end -end - ---- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 --- @param ... 多个表,第一个是目标表格 --- @return 返回一个新表 ----@author Sharif Ma -function table.MergeTables(...) - local tabs = {...} - if not tabs or #tabs == 0 then - return {} - end - local origin = {} - for k, v in pairs(tabs[1]) do - origin[k] = v - end - for i = 2, #tabs do - if origin then - if tabs[i] then - for _, v in pairs(tabs[i]) do - table.insert(origin, v) - end - end - else - origin = tabs[i] - end - end - return origin -end - ---- 在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格 --- @param target table --- @param source table --- @param start index --- @usage example #1 --- local dest = {1, 2, 3} --- local src = {4, 5, 6} --- table.insertto(dest, src) --- >> dest = {1, 2, 3, 4, 5, 6} --- @usage example #2 --- local dest = {1, 2, 3} --- local src = {4, 5, 6} --- table.insertto(dest, src, 5) --- >> dest = {1, 2, 3, nil, 4, 5, 6} -function table.insertto(dest, src, begin) - if begin == nil then - begin = #dest + 1 - else - begin = checkint(begin) - if begin <= 0 then - begin = #dest + 1 - end - end - - local len = #src - for i = 0, len - 1 do - dest[i + begin] = src[i + 1] - end -end - ---- 从表格中查找指定值,返回其索引,如果没找到返回 false --- @param array table --- @param target value --- @param start index --- @return index or false --- @usage example --- local array = {"a", "b", "c"} --- print(table.indexof(array, "b")) --- >> 2 -function table.indexof(array, value, begin) - if array ~= nil then - for i = begin or 1, #array do - if array[i] == value then - return i - end - end - end - return 0 -end - ---- 检查表格中是否存在指定值 --- @param array table --- @param target value --- @return @boolean -function table.exists(array, value) - return table.indexof(array, value) > 0 -end - ---- 清空数组表格 --- @param array table -function table.cleararray(array) - if array ~= nil then - local count = #array - while count > 0 do - table.remove(array, count) - count = #array - end - end -end - ---- 清空k-v表格 --- @param k-v table -function table.clearhashtable(hashtable) - if hashtable ~= nil then - for k, v in pairs(hashtable) do - hashtable[k] = nil - end - end -end - ---- 清空表格 --- @param table --- @see table.clearhashtable -function table.cleartable(t) - table.clearhashtable(t) -end - ---- 截取Array其中一段,startIndex从1开始 return截取后的新数组 --- @param table array table --- @param @number start index --- @param @number length --- @return @table array table --- @usage example --- local array = {"a", "b", "c", "d"} --- print(table.subArray(array, 2, 2)) --- >> {"b", "c"} -function table.subArray(array, startIndex, length) - if array ~= nil then - local count = table.nums(array) - local tempArray = array - array = {} - if startIndex <= count then - local maxlength = count - startIndex + 1 - length = length > maxlength and maxlength or length - local endIndex = startIndex + length - 1 - for i = startIndex, endIndex do - table.insert(array, tempArray[i]) - end - end - end - return array -end - ---- 截取Array的后半段,startIndex从1开始 return截取后的新数组 --- @param table array table --- @param @number start index --- @return @table array table -function table.subArrayByStartIndex(array, startIndex) - if array ~= nil then - local count = table.nums(array) - local length = count - startIndex + 1 - return table.subArray(array, startIndex, length) - end - return array -end - ---- 从表格中查找指定值,返回其 key,如果没找到返回 nil --- @param table hash table --- @param any value --- @return key of value --- @usage --- local hashtable = {name = "dualface", comp = "chukong"} --- print(table.keyof(hashtable, "chukong")) --- >> comp -function table.keyof(hashtable, value) - for k, v in pairs(hashtable) do - if v == value then - return k - end - end - return nil -end - ---- 从表格中删除指定值,返回删除的值的个数 --- @usage --- local array = {"a", "b", "c", "c"} --- print(table.removebyvalue(array, "c", true)) --- >> 输出 2 -function table.removebyvalue(array, value, removeall) - local c, i, max = 0, 1, #array - while i <= max do - if array[i] == value then - table.remove(array, i) - c = c + 1 - i = i - 1 - max = max - 1 - if not removeall then - break - end - end - i = i + 1 - end - return c -end - ---- 数组混淆 -function table.shuffle(_tbl) - local j - for i = #_tbl, 2, -1 do - j = math.random(i) - _tbl[i], _tbl[j] = _tbl[j], _tbl[i] - end - return _tbl -end - ---- 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容 --- @param table --- @param function fn 参数指定的函数具有两个参数,并且返回一个值。原型如下: --- function map_function(value, key) --- return value --- end --- @usage --- local t = {name = "dualface", comp = "chukong"} --- table.map(t, function(v, k) --- -- 在每一个值前后添加括号 --- return "[" .. v .. "]" --- end) --- 输出修改后的表格内容 --- for k, v in pairs(t) do --- print(k, v) --- end --- >> 输出 --- name [dualface] --- comp [chukong] -function table.map(t, fn) - for k, v in pairs(t) do - t[k] = fn(v, k) - end -end - ---- 对表格中每一个值执行一次指定的函数,但不改变表格内容 --- @param table --- @param function fn 参数指定的函数具有两个参数,没有返回值。原型如下: --- function map_function(value, key) --- -- no return here --- end --- @usage --- local t = {name = "dualface", comp = "chukong"} --- table.walk(t, function(v, k) --- -- 输出每一个值 --- print(v) --- end) -function table.walk(t, fn) - for k, v in pairs(t) do - fn(v, k) - end -end - ---- 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除 --- @param table --- @param function fn 参数指定的函数具有两个参数,并且返回一个 boolean 值。原型如下: --- !!!!该方法有局限性,执行后会修改原表格t中值 --- function map_function(value, key) --- return true or false --- end --- @usage --- local t = {name = "dualface", comp = "chukong"} --- table.filter(t, function(v, k) --- return v ~= "dualface" -- 当值等于 dualface 时过滤掉该值 --- end) --- 输出修改后的表格内容 --- for k, v in pairs(t) do --- print(k, v) --- end --- >> 输出 comp chukong -function table.filter(t, fn) - for k, v in pairs(t) do - if not fn(v, k) then - t[k] = nil - end - end -end - ---- 找到表格中每个符合matchFunc的条目 --- @param array table --- @param match function, return T/F --- @return all elements matched, default is {} -function table.findAll(array, matchFunc) - local ret, idx = {}, 1 - for i = 1, #array do - if matchFunc(array[i]) then - ret[idx] = array[i] - idx = idx + 1 - end - end - return ret -end - ---- 找到表格中每个符合matchFunc的条目,并执行walkFunc --- @param array table --- @param match function, return T/F --- @param walk function -function table.findAllAndWalk(array, matchFunc, walkFunc) - for i = 1, #array do - if matchFunc(array[i]) then - walkFunc(array[i]) - end - end -end - ---- 在表格中插入一个新值 --- @param array table --- @param new element -function table.insert_once(T, elem) - for _, v in ipairs(T) do - if v == elem then - return - end - end - table.insert(T, elem) -end - ---- 遍历表格,确保其中的值唯一 --- @function [parent=#table] unique --- @param table t 表格 --- @param boolean bArray t是否是数组,是数组,t中重复的项被移除后,后续的项会前移 --- @return table#table 包含所有唯一值的新表格 --- @usage --- 遍历表格,确保其中的值唯一 --- local t = {"a", "a", "b", "c"} -- 重复的 a 会被过滤掉 --- local n = table.unique(t) --- for k, v in pairs(n) do --- print(v) --- end --- >> 输出 a b c -function table.unique(t, bArray) - local check = {} - local n = {} - local idx = 1 - for k, v in pairs(t) do - if not check[v] then - if bArray then - n[idx] = v - idx = idx + 1 - else - n[k] = v - end - check[v] = true - end - end - return n -end - ---- table 深度复制 --- @param table --- @return a net table with same data -function table.deepcopy(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= 'table' then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for key, value in pairs(object) do - new_table[_copy(key)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - return _copy(object) -end - ---- table 浅度复制(不处理metatable) -function table.shallowcopy(orig) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - for orig_key, orig_value in next, orig, nil do - copy[table.shallowcopy(orig_key)] = table.shallowcopy(orig_value) - end - else - copy = orig - end - return copy -end - ---- 获取or创建一个子表 -function table.need(tb, key) - if type(tb) == 'table' then - local subTb = tb[key] - if subTb == nil then - subTb = {} - tb[key] = subTb - end - return subTb - end - return -end - ---- 打印table中的所有内容 --- @param data table --- @param @boolean showMetatable 是否显示元表 -function table.dump(data, showMetatable) - local result, tab = {}, ' ' - local function _dump(data, showMetatable, lastCount) - if type(data) ~= 'table' then - if type(data) == 'string' then - table.insert(result, '"') - table.insert(result, data) - table.insert(result, '"') - else - table.insert(result, tostring(data)) - end - else - --Format - local count = lastCount or 0 - count = count + 1 - table.insert(result, '{\n') - --Metatable - if showMetatable then - for i = 1, count do - table.insert(result, tab) - end - local mt = getmetatable(data) - table.insert(result, '"__metatable" = ') - _dump(mt, showMetatable, count) - table.insert(result, ',\n') - end - --Key - for key, value in pairs(data) do - for i = 1, count do - table.insert(result, tab) - end - if type(key) == 'string' then - table.insert(result, '"') - table.insert(result, key) - table.insert(result, '" = ') - elseif type(key) == 'number' then - table.insert(result, '[') - table.insert(result, key) - table.insert(result, '] = ') - else - table.insert(result, tostring(key)) - end - _dump(value, showMetatable, count) - table.insert(result, ',\n') - end - --Format - for i = 1, lastCount or 0 do - table.insert(result, tab) - end - table.insert(result, '}') - end - --Format - if not lastCount then - table.insert(result, '\n') - end - end - _dump(data, showMetatable, 0) - - -- print('dump: \n' .. table.concat(result)) - return 'dump: \n' .. table.concat(result) -end - ---- 用指定字符或字符串分割输入字符串,返回包含分割结果的数组 --- @param @string input 输入的字符串 --- @param @string delimiter 分隔符 --- @return array --- @usage example #1 --- local input = "Hello,World" --- local res = string.split(input, ",") --- >> res = {"Hello", "World"} --- @usage example #2 --- local input = "Hello-+-World-+-Quick" --- local res = string.split(input, "-+-") --- >> res = {"Hello", "World", "Quick"} -function string.split(input, delimiter) - input = tostring(input) - delimiter = tostring(delimiter) - if (delimiter == '') then - return false - end - local pos, arr = 0, {} - -- for each divider found - for st, sp in function() - return string.find(input, delimiter, pos, true) - end do - table.insert(arr, string.sub(input, pos, st - 1)) - pos = sp + 1 - end - table.insert(arr, string.sub(input, pos)) - return arr -end - ---- 判断字符串是否为空或者长度为零 --- @param @string 输入的字符串 -function string.isnilorempty(inputStr) - return inputStr == nil or inputStr == '' -end - ---- 去除输入字符串头部的空白字符,返回结果 --- @param @string input --- @return @string --- @usage example --- local input = " ABC" --- print(string.ltrim(input)) --- >> 输出 ABC,输入字符串前面的两个空格被去掉了 --- 空白字符包括: --- - 空格 --- - 制表符 \t --- - 换行符 \n --- - 回到行首符 \r -function string.ltrim(input) - return string.gsub(input, '^[ \t\n\r]+', '') -end - ---- 去除输入字符串尾部的空白字符,返回结果 --- @param @string input --- @return @string --- @usage example --- local input = "ABC " --- print(string.rtrim(input)) --- >> 输出 ABC,输入字符串最后的两个空格被去掉了 -function string.rtrim(input) - return string.gsub(input, '[ \t\n\r]+$', '') -end - ---- 去掉字符串首尾的空白字符,返回结果 --- @param @string input --- @return @string -function string.trim(input) - input = string.gsub(input, '^[ \t\n\r]+', '') - return string.gsub(input, '[ \t\n\r]+$', '') -end - ---- 将字符串的第一个字符转为大写,返回结果 --- @param @string input --- @return @string --- @usage example --- local input = "hello" --- print(string.ucfirst(input)) --- >> 输出 Hello -function string.ucfirst(input) - return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2) -end - -function string.firstToUpper(str) - return (str:gsub('^%l', string.upper)) -end - ---- 计算 UTF8 字符串的长度,每一个中文算一个字符 --- @param @string input --- @return @number cnt --- @usage example --- local input = "你好World" --- print(string.utf8len(input)) --- >> 输出 7 -function string.utf8len(input) - local len = string.len(input) - local left = len - local cnt = 0 - local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc} - while left ~= 0 do - local tmp = string.byte(input, -left) - local i = #arr - while arr[i] do - if tmp >= arr[i] then - left = left - i - break - end - i = i - 1 - end - cnt = cnt + 1 - end - return cnt -end - ---- 替换字符串内容 --- @param @string input --- @param @number start index --- @param new context --- @return a new string -function string.replace(str, index, char) - return table.concat {str:sub(1, index - 1), char, str:sub(index + 1)} -end - ---- 检查字符串是否为指定字符串开头 --- @param @string target --- @param @string start --- @return @boolean -function string.startswith(str, start) - return str:sub(1, #start) == start -end - ---- 检查字符串是否以指定字符串结尾 --- @param @string target --- @param @string start --- @return @boolean -function string.endswith(str, ending) - return ending == '' or str:sub(-(#ending)) == ending -end - ---- 四舍五入 --- @param a number --- @return a round number -function math.round(value) - return math.floor(value + 0.5) -end - ---- [0, 1]区间限定函数 --- @param a number --- @return a clamped number -function math.clamp01(value) - return math.min(1, math.max(0, value)) -end - ----最小数值和最大数值指定返回值的范围 --- @param a number --- @param min threshold --- @param max threshold --- @return a clamped number -function math.Clamp(value, min, max) - if value < min then - return min - end - if value > max then - return max - end - return value -end - ---- 高斯岁间变量 -function math.GaussRandom() - local u = math.random() - local v = math.random() - local z = math.sqrt(-2 * math.log(u)) * math.cos(2 * math.pi * v) - z = (z + 3) / 6 - z = 2 * z - 1 - if (math.abs(z) > 1) then - return math.GaussRandom() - end - return z -end - ---- 数据结构 队列 --- @usage queue example --- local myQueue = Queue:New() --- myQueue:Enqueue('a') --- myQueue:Enqueue('b') --- myQueue:Enqueue('c') --- myQueue:PrintElement() --- print(myQueue:Dequeue()) --- myQueue:PrintElement() --- myQueue:Clear() --- myQueue:PrintElement() -Queue = {} -function Queue:New() - local inst = { - _first = -1, - _last = -1, - _size = 0, - _queue = {} - } - setmetatable(inst, {__index = self}) - return inst -end - -function Queue:IsEmpty() - if self._size == 0 then - return true - end - return false -end - -function Queue:Enqueue(inElement) - if self._size == 0 then - self._first = 0 - self._last = 1 - self._size = 1 - self._queue[self._last] = inElement - else - self._last = self._last + 1 - self._queue[self._last] = inElement - self._size = self._size + 1 - end -end - -function Queue:Dequeue() - if self:IsEmpty() then - print('Error: the queue is empty') - return - end - self._size = self._size - 1 - self._first = self._first + 1 - local value = self._queue[self._first] - return value -end - -function Queue:Clear() - self._queue = nil - self._queue = {} - self._size = 0 - self._first = -1 - self._last = -1 -end - -function Queue:Size() - return self._size or 0 -end - -function Queue:PrintElement() - if self._size == 0 then - print('{}') - else - local f = self._first + 1 - local l = self._last - local str - local flag = true - while f ~= l do - if flag == true then - str = '{' .. tostring(self._queue[f]) - f = f + 1 - flag = false - else - str = str .. ',' .. tostring(self._queue[f]) - f = f + 1 - end - end - str = str .. ',' .. tostring(self._queue[l]) .. '}' - print(str) - end -end - -function Queue:GetValue(index) - if self:IsEmpty() or index == nil or index == 0 then - print('Error: Get Value Failure!') - return - end - if index > 0 then - return self._queue[self._first + index] - else - return self._queue[self._last + index + 1] - end -end - -function Queue:GetValues() - if self:IsEmpty() then - return - end - local data = {} - for i = self._first + 1, self._last, 1 do - data[#data + 1] = self._queue[i] - end - return data -end - ---- 数据结构 栈 --- @usage example --- local myStack = Stack:New() --- myStack:Push("a") --- myStack:Push("b") --- myStack:Push("c") --- myStack:PrintElement() --- print(myStack:Pop()) --- myStack:PrintElement() --- myStack:Clear() --- myStack:PrintElement() -Stack = {} -function Stack:New() - local inst = { - _last = 0, - _stack = {} - } - setmetatable(inst, {__index = self}) - - return inst -end - -function Stack:IsEmpty() - if self._last == 0 then - return true - end - return false -end - -function Stack:Push(inElement) - self._last = self._last + 1 - self._stack[self._last] = inElement -end - -function Stack:Pop() - if self:IsEmpty() then - print('Error: the stack is empty') - return - end - local value = self._stack[self._last] - self._stack[self._last] = nil - self._last = self._last - 1 - return value -end - -function Stack:Exists(element, compairFunc) - if compairFunc == nil then - compairFunc = function(a, b) - return a == b - end - end - for i = self._last, 1, -1 do - if compairFunc(element, self._stack[i]) then - return i - end - end - return -1 -end - -function Stack:RemoveAt(index) - if index < 1 or index > self._last then - return - end - table.remove(self._stack, index) - self._last = self._last - 1 -end - -function Stack:Clear() - self._stack = nil - self._stack = {} - self._last = 0 -end - -function Stack:Size() - return self._last -end - -function Stack:PrintElement() - local str = '{' - for i = self._last, 1, -1 do - str = str .. tostring(self._stack[i]) .. ',' - end - str = str .. '}' - print(str) -end - ---- uid递增计数器 ---- @author Sid Zhang -function UidCounter() - local num = 0 - return function() - num = num + 1 - return string.format('%s-%s', os.clock(), num) - end -end diff --git a/Code/['World']['Global']['ModuleRequireScript'].Script.lua b/Code/['World']['Global']['ModuleRequireScript'].Script.lua deleted file mode 100644 index 2507407..0000000 --- a/Code/['World']['Global']['ModuleRequireScript'].Script.lua +++ /dev/null @@ -1,37 +0,0 @@ ---- 将Global.Module目录下每一个用到模块提前require,定义为全局变量 --- @script Module Defines --- @copyright Lilith Games, Avatar Team - --- Game Defines -GAME_ID = 'X0000' -print('GAME_ID = ', GAME_ID) - --- Utilities -ModuleUtil = require(Utility.ModuleUtilModule) -LuaJsonUtil = require(Utility.LuaJsonUtilModule) -NetUtil = require(Utility.NetUtilModule) -CsvUtil = require(Utility.CsvUtilModule) -XlsUtil = require(Utility.XlsUtilModule) -EventUtil = require(Utility.EventUtilModule) -UUID = require(Utility.UuidModule) -TweenController = require(Utility.TweenControllerModule) -GlobalFunc = require(Utility.GlobalFuncModule) -LinkedList = Utility.LinkedListModule -ValueChangeUtil = require(Utility.ValueChangeUtilModule) -TimeUtil = require(Utility.TimeUtilModule) -CloudLogUtil = require(Utility.CloudLogUtilModule) -ObjPoolUtil = require(Utility.ObjPoolUtilModule) -SoundUtil = require(Utility.SoundUtilModule) - --- Init Utilities -TimeUtil.Init() -CloudLogUtil.Init(GAME_ID) - --- Framework -ModuleUtil.LoadModules(Framework) -ModuleUtil.LoadModules(Framework.Server) -ModuleUtil.LoadModules(Framework.Client) - --- Globle Defines -ModuleUtil.LoadModules(Define) -ModuleUtil.LoadXlsModules(Xls, Config) \ No newline at end of file diff --git a/Code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua deleted file mode 100644 index 700f852..0000000 --- a/Code/['World']['Global']['Utility']['CamUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,41 +0,0 @@ ----摄像机工具类 ----@module Cam Utility ----@copyright Lilith Games, Avatar Team ----@author Sharif Ma ----@class CamUtil -local CamUtil = {} - ----将摄像机在水平面上转动到和角色朝向一致的角度 ----@param _player PlayerInstance 摄像机看向的物体 ----@param _cam Camera 转动的摄像机 ----@param _time number 转动过程的事件,不填则瞬间转动 -function CamUtil.ToRoleForward(_player, _cam, _time) - _time = _time or 0 - local dir = _player.Position - _cam.Position - local forward = _player.Forward - local alpha = Vector2.Angle(Vector2(dir.x, dir.z), Vector2(forward.x, forward.z)) - local left = _player.Left - if Vector3.Angle(left, dir) > 90 then - alpha = 360 - alpha - end - if _time == 0 then - _cam:CameraMoveInDegree(Vector2(alpha, 0)) - return - end - invoke( - function() - local curTime = 0 - while true do - local dt = wait() - local dtDe = alpha * dt / _time - _cam:CameraMoveInDegree(Vector2(dtDe, 0)) - curTime = curTime + dt - if curTime >= _time then - return - end - end - end - ) -end - -return CamUtil diff --git a/Code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua deleted file mode 100644 index 9c168a9..0000000 --- a/Code/['World']['Global']['Utility']['CsvUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,244 +0,0 @@ ---- 读表工具: 将CSV导入成Lua Table,支持单一主键和多主键 --- @module CSV Utility --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang --- @see https://wiki.lilithgames.com/x/RGEMAg -local CsvUtil = {} - ---! 打印事件日志, true:开启打印 -local showLog, PrintGlobalKV, PrintLog = true - ---- 将表中的字符串改为数字 --- @param _t input table -local function StrToNum(_t) - for k, v in pairs(_t) do - _t[k] = tonumber(v) - end - return _t -end - ---- 类型解析配置表 -local parser = { - int = function(_raw) - return tonumber(_raw) - end, - float = function(_raw) - return tonumber(_raw) - end, - string = function(_raw) - return _raw - end, - boolean = function(_raw) - return string.lower(_raw) == 'true' - end, - vector2 = function(_raw) - return Vector2(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - vector3 = function(_raw) - return Vector3(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - euler = function(_raw) - return EulerDegree(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - color = function(_raw) - return Color(table.unpack(StrToNum(string.split(_raw, ',')))) - end -} - ---- 读取配置表,会根据id生成lua表 --- @param _type String 数据类型 --- @parm _stringValue String 数据 --- @return value 解析出来的数值 -local function GetValue(_type, _stringValue) - _type = string.lower(_type) - assert(parser[_type], string.format('[CsvUtil][GlobalSetting] "%s" Type字段的值不是目前所支持的数据类型', _type)) - return parser[_type](_stringValue) -end - ---- 读取配置表,会根据id生成lua表 --- @param _csv 表格 --- @parma ... 表格的主键ids,可以为单一主键或多主键(多主键的id顺序决定lua table的结构) --- @usage exmaple #1 如果, 单一键值为主键 --- Level.csv 表格内容为: --- ---------------------------------- --- | String | String | Int | --- | level_id | level_name | reward | --- | easy_01 | Level 01 | 100 | --- | easy_02 | Level 02 | 140 | --- | hard_01 | Level 03 | 280 | --- | hard_02 | Level 04 | 320 | --- ---------------------------------- --- 调用函数 local levelCsv = CsvUtil.GetCsvInfo(Level, 'level_id') 导入的lua表格结果为: --- levelCsv = { --- easy_01 = { --- level_id = 'easy_01', --- level_name = 'Level 01', --- reward = 100 --- }, --- easy_02 = { --- level_id = 'easy_02', --- level_name = 'Level 02', --- reward = 140 --- }, --- hard_01 = { --- level_id = 'hard_01', --- level_name = 'Level 03', --- reward = 280 --- }, --- hard_02 = { --- level_id = 'hard_02', --- level_name = 'Level 04', --- reward = 320 --- } --- } --- @usage exmaple #2 如果, 多键值为主键 --- Enemy.csv 表格内容为: --- ---------------------------------- --- | String | String | Int | --- | enemy_id | difficulty | hp | --- | foe_01 | easy | 100 | --- | foe_01 | hard | 150 | --- | foe_02 | easy | 300 | --- | foe_02 | hard | 400 | --- ---------------------------------- --- 调用函数 local enemyCsv = CsvUtil.GetCsvInfo(Enemy, 'enemy_id', 'difficulty') 导入的lua表格结果为: --- enemyCsv = { --- foe_01 = { --- easy = { --- enemy_id = 'foe_01', --- difficulty = 'easy', --- hp = 100 --- }, --- hard = { --- enemy_id = 'foe_02', --- difficulty = 'hard', --- hp = 150 --- } --- }, --- foe_02 = { --- esay = { --- enemy_id = 'foe_02', --- difficulty = 'easy', --- hp = 300 --- }, --- hard = { --- enemy_id = 'foe_02', --- difficulty = 'hard', --- hp = 400 --- } --- } --- } --- 使用lua table中的数据方法: --- health = enemyCsv.foe_01.hard.hp 或 health = enemyCsv['foe_01']['hard']['hp'] --- health的值为150 -function CsvUtil.GetCsvInfo(_csv, ...) - local rawTable = _csv:GetRows() - local ids = {...} - if #ids < 1 or (#ids == 1 and ids[1] == 'Type') then - -- 默认用Type索引,直接返回 - return rawTable - end - local result = {} - local tmp, key, id, idstr -- 临时变量 - for _, v in pairs(rawTable) do - tmp = result - idstr = {} - for i = 1, #ids do - id = ids[i] - key = v[id] - idstr[i] = tostring(id) .. ',' - assert(not string.isnilorempty(key), string.format('[CsvUtil] CSV表格没有找到此id, CSV:%s, id: %s', _csv.Name, id)) - if i == #ids then - -- 最后的键,确定唯一性 - assert( - not tmp[key], - string.format('[CsvUtil] CSV数据重复, ids不是唯一的, CSV: %s, ids: %s', _csv.Name, table.concat(idstr)) - ) - tmp[key] = v - else - -- 多键,之后还有 - if tmp[key] == nil then - tmp[key] = {} - end - tmp = tmp[key] - end - end - end - return result -end - ---- 读取Config全局配置表 --- GlobleSetting.csv 表格内容为: --- --------------------------------------------------------- --- | String | String | String | String | --- | Key | Type | Value | Des | --- --------------------------------------------------------- --- | CubeMax | Int | 200 | 最大Cube数 | --- | BattleTime | Float | 5.45 | 战斗时间 | --- | GameTitle | String | Boom Party | 游戏标题 | --- | IsFree | Boolean | true | 是否免费 | --- | UiMapOrigin | Vector2 | 3,4 | UI地图原点位置 | --- | TreePos | Vector3 | 12,3,-3 | 树的位置 | --- | TreeRot | Euler | 45,90,0 | 树的旋转 | --- | TreeColor | Color | 255,255,255,0 | 树的颜色 | -function CsvUtil.GetGlobalCsvInfo(_csv) - local rawTable = _csv:GetRows() - if table.nums(rawTable) == 0 then - return - end - assert(rawTable['1'].Key, '[CsvUtil] 全局配置表的没有"Key"') - assert(rawTable['1'].Type, '[CsvUtil] 全局配置表的没有"Type"') - assert(rawTable['1'].Value, '[CsvUtil] 全局配置表的没有"Value"') - local result = {} - for _, v in pairs(rawTable) do - result[v.Key] = GetValue(v['Type'], v['Value']) - PrintGlobalKV(v.Key, v.Type, result[v.Key]) -- * 输出KV键值对 - end - return result -end - ---- 表格预加载,预加载配置模块:World.Global.Define.ConfigModule -function CsvUtil.PreloadCsv(_preloadList, _csvRoot, _config) - assert(_preloadList and #_preloadList > 0, '[CsvUtil] ConfigModule中没有预加载表格') - for _, pl in pairs(_preloadList) do - if not string.isnilorempty(pl.csv) then - pl.name = string.isnilorempty(pl.name) and pl.csv or pl.name - PrintLog(string.format('[CsvUtil] Load: %s.csv', pl.csv)) - if pl.csv == 'GlobalSetting' and _csvRoot[pl.csv] then - _config[pl.name] = CsvUtil.GetGlobalCsvInfo(_csvRoot[pl.csv]) - elseif not string.isnilorempty(pl.csv) and _csvRoot[pl.csv] then - pl.ids = pl.ids or {} - _config[pl.name] = CsvUtil.GetCsvInfo(_csvRoot[pl.csv], table.unpack(pl.ids)) - end - end - end -end - ---! 辅助功能 - ---- 输出全局变量键值对 -PrintGlobalKV = - showLog and - function(_key, _type, _value) - _type = string.lower(_type) - local showTypes = { - vector2 = 'Vector2', - vector3 = 'Vector3', - euler = 'EulerDegree', - color = 'Color' - } - if showTypes[_type] then - print(string.format('[CsvUtil][GlobalSetting] %s = %s%s ', _key, showTypes[_type], _value)) - else - print(string.format('[CsvUtil][GlobalSetting] %s = %s ', _key, _value)) - end - end or - function() - end - -PrintLog = showLog and function(...) - print(...) - end or function() - end - -return CsvUtil diff --git a/Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua deleted file mode 100644 index a294671..0000000 --- a/Code/['World']['Global']['Utility']['GlobalFuncModule'].ModuleScript.lua +++ /dev/null @@ -1,33 +0,0 @@ ---- 全局函数的定义 ---- @module GlobalFunc Defines ---- @copyright Lilith Games, Avatar Team ---- @author Sid Zhang, Yuancheng Zhang -local GlobalFunc = {} - ---- 埋点上传日志 ---- @param _tableName string 表名 -function GlobalFunc.UploadLogs(_tableName, ...) - local args = {...} - if localPlayer then - pcall( - function() - TrackService.CloudLogFromClient({_tableName, table.unpack(args)}) - end - ) - else - pcall( - function() - TrackService.CloudLogFromServer({_tableName, table.unpack(args)}) - end - ) - end -end - --- 检查碰撞对象是否为NPC --- Server-side 一般用于服务器端 -function GlobalFunc.CheckHitObjIsPlayer(_hitObj) - return _hitObj and _hitObj.ClassName == 'PlayerInstance' and _hitObj.Avatar and - _hitObj.Avatar.ClassName == 'PlayerAvatarInstance' -end - -return GlobalFunc diff --git a/Code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua deleted file mode 100644 index dc7fca4..0000000 --- a/Code/['World']['Global']['Utility']['LinkedListModule'].ModuleScript.lua +++ /dev/null @@ -1,444 +0,0 @@ ---- C# 双向链表 --- @module C# doubly linked list implemented with lua --- @copyright Lilith Games, Avatar Team --- @author Bruce Chen --- @see https://wiki.lilithgames.com/x/7yRZAg --- @see https://github.com/BruceCheng1995/LuaLinkedList - -local LinkedList = {} -local LinkedNode = {} -LinkedNode.__index = LinkedNode - -local NativePrint = print -local EmptuFunc = function() -end ---是否开放内部日志 -function LinkedList:EnableLog(_enable) - if _enable then - print = NativePrint - else - print = EmptuFunc - end -end -LinkedList:EnableLog(false) ---新建节点 -function LinkedNode:new(value, list) - local o = {} - setmetatable(o, self) - o.List = list - o.Next = nil - o.Prev = nil - o.Value = value - return o -end ---克隆这个节点 -function LinkedNode:Clone() - return LinkedNode:new(self.Value, nil) -end ---节点失效 -function LinkedNode:Invalidate() - self.Next = nil - self.Prev = nil - self.List = nil -end ---打印 -function LinkedNode:tostring() - return tostring(self.Value) -end -LinkedNode.__tostring = LinkedNode.tostring - ---验证新节点是否是自由节点 -function LinkedList:ValidateNewNode(node) - if not node then - return false - end - --assert(LinkedNode:include(node),"instance of LinkedNode needed.") - if node.List ~= nil then - return false - end - return true -end - ---验证该节点是否是属于该表 -function LinkedList:ValidateNode(node) - if not node then - return false - end - --assert(LinkedNode:include(node),"instance of LinkedNode needed.") - if node.List ~= self then - return false - end - return true -end - ---将节点插入到node节点之前(list:链表,node:插在这个节点前面,newnode:被插入的节点) -local function InternalInsertNodeBefore(list, node, newnode) - newnode.Next = node - newnode.Prev = node.Prev - node.Prev.Next = newnode - node.Prev = newnode - list.Count = list.Count + 1 -end - ---将节点插入到一个空链表之前(list:链表,newnode:被插入的节点) -local function InternalInsertNodeToEmptyList(list, newnode) - newnode.Next = newnode - newnode.Prev = newnode - list.First = newnode - list.Count = list.Count + 1 -end - ---移除链表中的节点(list:链表,node:被删除的节点) -local function InternalRemoveNode(list, node) - if node.Next == node then - list.First = nil - else - node.Next.Prev = node.Prev - node.Prev.Next = node.Next - if list.First == node then - list.First = node.Next - end - end - node:Invalidate() - list.Count = list.Count - 1 -end - ---新建双向链表 -function LinkedList:new(tab) - local o = {} - setmetatable(o, self) - o.Count = 0 - o.First = nil - if type(tab) == 'table' then - for _, v in pairs(tab) do - o:AddLast(v) - end - end - return o -end - ---Add Value ---在尾部添加值(若传入值是表,则遍历表,并将所有值添加到尾部) -function LinkedList:Add(value) - if type(value) == 'table' then - for _, v in pairs(value) do - self:AddLast(v) - end - else - self:AddLast(value) - end -end - ---在尾部添加值 -function LinkedList:AddLast(value) - local newnode = LinkedNode:new(value, self) - if not self.First then - InternalInsertNodeToEmptyList(self, newnode) - else - InternalInsertNodeBefore(self, self.First, newnode) - end - return newnode -end - ---在头部添加值 -function LinkedList:AddFirst(value) - local newnode = LinkedNode:new(value, self) - if not self.First then - InternalInsertNodeToEmptyList(self, newnode) - else - InternalInsertNodeBefore(self, self.First, newnode) - self.First = newnode - end - return newnode -end - ---在指定节点后面添加值(node:插入在这个节点后,value:被插入的值) -function LinkedList:AddAfter(node, value) - if not self:ValidateNewNode(node) then - return - end - local newnode = LinkedNode:new(value, self) - InternalInsertNodeBefore(self, node.Next, newnode) - return newnode -end - ---在指定节点前面添加值(node:插入在这个节点前,value:被插入的值) -function LinkedList:AddBefore(node, value) - if not self:ValidateNode(node) then - return - end - local newnode = LinkedNode:new(value, self) - InternalInsertNodeBefore(self, node, newnode) - if node == self.First then - self.First = newnode - end - return newnode -end - ---Add Node ---在头部添加节点 -function LinkedList:AddNodeFirst(node) - if not self:ValidateNewNode(node) then - return - end - if not self.First then - InternalInsertNodeToEmptyList(self, node) - else - InternalInsertNodeBefore(self, self.First, node) - self.First = node - end - node.List = self -end - ---在尾部添加节点 -function LinkedList:AddNodeLast(node) - if not self:ValidateNewNode(node) then - return - end - if not self.First then - InternalInsertNodeToEmptyList(self, node) - else - InternalInsertNodeBefore(self, self.First, node) - end - node.List = self -end - ---在指定节点后面添加值(node:插入在这个节点后,newnode:被插入的节点) -function LinkedList:AddNodeAfter(node, newnode) - if not self:ValidateNode(node) and not self:ValidateNewNode(newnode) then - return - end - InternalInsertNodeBefore(self, node.Next, newnode) - newnode.List = self -end - ---在指定节点后面添加值(node:插入在这个节点前,newnode:被插入的节点) -function LinkedList:AddNodeBefore(node, newnode) - if not self:ValidateNode(node) and not self:ValidateNewNode(newnode) then - return - end - InternalInsertNodeBefore(self, node, newnode) - newnode.List = self - if node ~= self.First then - return - end - self.First = newnode -end - ---Remove ---找到表中的第一个指定值,并删除,返回是否命中 -function LinkedList:Remove(value) - local node = self:Find(value) - if not node then - return false - end - InternalRemoveNode(self, node) - return true -end - ---找到表中的第一个指定节点,并删除,返回是否命中 -function LinkedList:RemoveNode(node) - if not self:ValidateNode(node) then - return - end - InternalRemoveNode(self, node) -end - ---移除头部节点 -function LinkedList:RemoveFirst() - if self.First == nil then - print('[LinkedList] list is empty.') - else - InternalRemoveNode(self, self.First) - end -end - ---移除尾部节点 -function LinkedList:RemoveLast() - if self.First == nil then - print('[LinkedList] list is empty.') - else - InternalRemoveNode(self, self.First.Prev) - end -end - ---Find ---尝试找到表中的第一个指定值,若有则返回这个节点 -function LinkedList:Find(value) - local ptrnode = self.First - if value ~= nil then - while ptrnode.Value ~= value do - ptrnode = ptrnode.Next - if ptrnode == self.First then - goto close1 - end - end - return ptrnode - else - while ptrnode.Value ~= nil do - ptrnode = ptrnode.Next - if ptrnode == self.First then - goto close1 - end - end - return ptrnode - end - ::close1:: - return -end - ---尝试反向找到表中第一个指定值,若有则返回这个节点 -function LinkedList:FindLast(value) - if self.First == nil then - return - end - local prev = self.First.Prev - local ptrnode = prev - if value ~= nil then - while ptrnode.Value ~= value do - ptrnode = ptrnode.Prev - if ptrnode == Prev then - goto close2 - end - end - return ptrnode - else - while ptrnode.Value ~= nil do - ptrnode = ptrnode.Prev - if ptrnode == prev then - goto close2 - end - end - return ptrnode - end - ::close2:: - return -end - ---Other ---清空链表 -function LinkedList:Clear() - local ptrnode = self.First - while ptrnode ~= nil do - local lastnode = ptrnode - ptrnode = ptrnode.Next - lastnode:Invalidate() - end - self.First = nil - self.Count = 0 -end - ---向给定table的指定位置插入数值(tab:被插入表,index:序号) -function LinkedList:CopyTo(tab, index) - assert(type(tab) == 'table', '[LinkedList] bad argument "table"') - assert(index >= 1, '[LinkedList] Index out of range') - local ptrnode = self.First - if ptrnode == nil then - return - end - repeat - table.insert(tab, index, ptrnode.Value) - ptrnode = ptrnode.Next - index = index + 1 - until (ptrnode == self.First) -end - ---将链表中的数据拷贝到新表中,并将这个表输出 -function LinkedList:ToTable() - local tab = {} - self:CopyTo(tab, 1) - return tab -end - ---克隆当前链表,并返回 -function LinkedList:Clone() - local newlist = LinkedList:new() - local ptrnode = self.First - repeat - local clnode = ptrnode:Clone() - newlist:AddNodeLast(clnode) - ptrnode = ptrnode.Next - until (ptrnode == self.First) - return newlist -end - ---检查链表中是否包含指定值 -function LinkedList:Contains(value) - return self:Find(value) and true or false -end - ---将链表反向 -function LinkedList:Reverse() - local tmp - if not self.First then - print('[LinkedList] list is empty') - return - end - self.First = self.First.Prev - for item in self:ipairer() do - tmp = item.Next - item.Next = item.Prev - item.Prev = tmp - end -end - ---返回头部节点 -function LinkedList:GetFirst() - return self.First -end - ---返回尾部节点 -function LinkedList:GetLast() - return self.First ~= nil and self.First.Prev or nil -end - ---返回第index个节点 -function LinkedList:GetNode(index) - if index < 1 or index > self.Count then - print('[LinkedList] Index out of range') - return - end - local ptrnode = self.First.Prev - while index > 0 do - ptrnode = ptrnode.Next - index = index - 1 - end - return ptrnode -end - ---返回链表长度 -function LinkedList:Len() - return self.Count -end - ---返回迭代器 -function LinkedList:ipairer() - local ptrnode = self:GetLast() - local passFirst = false - return function() - if ptrnode then - if ptrnode ~= self:GetLast() or not passFirst then - passFirst = true - ptrnode = ptrnode.Next - return ptrnode - end - end - end -end - ---以文本方式表示此表 -function LinkedList:tostring() - local t = {} - for item in self:ipairer() do - table.insert(t, tostring(item)) - end - return 'LinkedList:{' .. table.concat(t, ',') .. '}' -end - -LinkedList.__index = LinkedList -LinkedList.__tostring = LinkedList.tostring - -return { - list = setmetatable(LinkedList, {__call = LinkedList.new}), - node = setmetatable(LinkedNode, {__call = LinkedNode.new}) -} diff --git a/Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua deleted file mode 100644 index 45b6a31..0000000 --- a/Code/['World']['Global']['Utility']['ModuleUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,83 +0,0 @@ ---- 模块工具 --- @module Module utilities --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang - -local ModuleUtil = {} - ---- 加载模块目录 --- @param _root 模块目录的节点 --- @param _scope 载入后脚本的作用域 --- @param _isRec 是否递归寻找子节点文件夹 -function ModuleUtil.LoadModules(_root, _scope, _isRec) - _scope = _scope or _G - _isRec = _isRec or true - assert(_root, '[ModuleUtil] Node does NOT exist!') - local nodes, name = _root:GetChildren() - for _, v in pairs(nodes) do - if v.ClassName == 'ModuleScriptObject' then - name = (v.Name):gsub('Module', '') - print('[ModuleUtil] Load Module: ', name) - _scope[name] = require(v) - elseif v.ClassName == 'FolderObject' and _isRec then - ModuleUtil.LoadModules(v, _scope, _isRec) - end - end -end - ---- 加载XLS表格目录 --- @param _root 模块目录的节点 --- @param _config 所有Excel生成Lua文件的所在table,不允许是_G -function ModuleUtil.LoadXlsModules(_root, _config) - assert(_root, '[ModuleUtil] Node does NOT exist!') - assert(_config, '[ModuleUtil] Config does NOT exist!') - local tmp, name = _root:GetChildren() - for _, v in pairs(tmp) do - name = (v.Name):gsub('XlsModule', '') - print('[ModuleUtil] Load XLS: ', name) - _config[name] = require(v) - end -end - ---- 加载多个模块目录 -function ModuleUtil.LoadAllModules(...) - local args = table.pack(...) - for i = 1, args.n do - if args[i] then - ModuleUtil.LoadModules(args[i]) - end - end -end - ---- 将有包含特定方法的模块筛选出来,并放在一个table中 --- @param _root 模块目录的节点 --- @param @string _fn 方法名 function_name --- @param @table _list 存放的table --- @param _scope 该脚本的作用域 --- @param _isRec 是否递归寻找子节点文件夹 -function ModuleUtil.GetModuleListWithFunc(_root, _fn, _list, _scope, _isRec) - assert(_root, '[ModuleUtil] Node does NOT exist!') - assert(not string.isnilorempty(_fn), '[ModuleUtil] Function name is nil or empty!') - assert(_list, '[ModuleUtil] List is NOT initialized!') - _scope = _scope or _G - _isRec = _isRec or true - local nodes, name = _root:GetChildren() - for _, v in pairs(nodes) do - if v.ClassName == 'ModuleScriptObject' then - name = (v.Name):gsub('Module', '') - if _scope[name] and _scope[name][_fn] and type(_scope[name][_fn]) == 'function' then - table.insert(_list, _scope[name]) - end - elseif v.ClassName == 'FolderObject' and _isRec then - ModuleUtil.GetModuleListWithFunc(v, _fn, _list, _scope, _isRec) - end - end -end - ---- 新建一个模块实例(ServerBase or ClientBase) -function ModuleUtil.New(_name, _baseClass) - local t = class(_name, _baseClass) - return t, t:GetSelf() -end - -return ModuleUtil diff --git a/Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua deleted file mode 100644 index defe937..0000000 --- a/Code/['World']['Global']['Utility']['ObjPoolUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,77 +0,0 @@ ----对象池工具模块 ----@class ObjPoolUtil --- @copyright Lilith Games, Avatar Team --- @author Yen Yuan -local ObjPoolUtil = class('ObjPoolUtil') - ----创建某一个对象的对象池 ----@param _folderName Object 管理的目录 ----@param _objName string 对象的Archetype名 ----@param _maxCount number 对象池最大上限,不填则为100 ----@return ObjPoolUtil -function ObjPoolUtil.static.Newpool(_folderName, _objName, _maxCount) - if _folderName == nil or _objName == nil then - error('[ObjPoolUtil] 管理目录或管理对象为空') - end - if _maxCount == nil then - _maxCount = 100 - end - local realPool = class(_objName .. 'Pool', ObjPoolUtil) - realPool.static.obj = _objName - realPool.static.folder = _folderName - realPool.static.maxCount = _maxCount - realPool.pool = {} - print(string.format('[ObjPoolUtil] 创建了一个%s的对象池,目录为%s', _objName, _folderName)) - return realPool -end - ----从池中预创建对象到世界下 ----@param _position Vector3 -function ObjPoolUtil:PreSpawn(_position) - for i = 1, self.maxCount do - local realObj = world:CreateInstance(self.obj, self.obj, self.folder, _position) - realObj.IsStatic = true - table.insert(self.pool, realObj) - realObj:SetActive(false) - end -end - ----从池中创建对象到世界下 ----@param _position Vector3 ----@param _rotation EulerDegree -function ObjPoolUtil:Spawn(_position, _rotation) - local realObj = nil - if #self.pool == 0 then - realObj = world:CreateInstance(self.obj, self.obj, self.folder, _position, _rotation) - if realObj == nil then - error(string.format('[ObjPoolUtil] Archetype下没有名为%s的对象', self.obj)) - return - end - return realObj - else - realObj = self.pool[1] - self.pool[1].Position = _position - self.pool[1].Rotation = _rotation or EulerDegree(0, 0, 0) - self.pool[1].IsStatic = false - self.pool[1]:SetActive(true) - table.remove(self.pool, 1) - return realObj - end -end - ----从世界中销毁对象到池中 ----@param _obj Object -function ObjPoolUtil:Despawn(_obj) - if _obj == nil then - error('[ObjPoolUtil] 传入对象为空') - elseif #self.pool > self.maxCount then - print(string.format('[ObjPoolUtil] %s对象池已满,该对象会永久销毁', self.obj)) - _obj:Destroy() - else - table.insert(self.pool, _obj) - _obj:SetActive(false) - self.pool[1].IsStatic = true - end -end - -return ObjPoolUtil diff --git a/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua deleted file mode 100644 index 7342ee2..0000000 --- a/Code/['World']['Global']['Utility']['SoundUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,178 +0,0 @@ ---- 音效播放模块 ---- @module SoundUtil ---- @copyright Lilith Games, Avatar Team ---- @author Dead Ratman -local SoundUtil = {} - ---音频 -local clipTable = nil - ---音效对象池 -local audioSourcePool = { - SE3D = {}, --3D音效 - SE2D = {} --2D音效 -} - ---3D音效对象池上限 -local SE3DMax = 8 ---2D音效对象池上限 -local SE2DMax = 3 - ---! Debug模式 -local DebugMode = true - ---- 打印日志 -local PrintLog = DebugMode and function(...) - print('[SoundUtil]', ...) - end or function() - end - ---初始化音频表 -local function InitClipTable(_data) - clipTable[_data.ID] = { - id = _data.ID, - clip = ResourceManager.GetSoundClip(_data.Path), - isLoop = _data.IsLoop, - volume = _data.Volume, - minDistance = _data.MinDistance, - maxDistance = _data.MaxDistance - } - --PrintLog(table.dump(clipTable[_data.ID])) -end - ---初始化一个2D播放器 -local function Init2DAudioSource(_index, _uid) - audioSourcePool.SE2D[_uid][_index] = - world:CreateObject( - 'AudioSource', - 'AudioSource' .. _index, - world:GetPlayerByUserId(_uid).Local.Independent.GameCam.SENode, - world:GetPlayerByUserId(_uid).Local.Independent.GameCam.SENode.Position - ) - return audioSourcePool.SE2D[_uid][_index] -end - ---初始化一个3D播放器 -local function Init3DAudioSource(_index) - audioSourcePool.SE3D[_index] = world:CreateObject('AudioSource', 'AudioSource' .. _index, world.SENode) - return audioSourcePool.SE3D[_index] -end - -function SoundUtil.Init(_config) - print('[SoundUtil] Init(_config)') - if clipTable == nil then - clipTable = {} - for k, v in pairs(_config) do - InitClipTable(v) - end - end -end - ---初始化音效播放器对象池 -function SoundUtil.InitAudioSource(_uid) - if _uid then - audioSourcePool.SE2D[_uid] = {} - for i = 1, SE2DMax do - Init2DAudioSource(i, _uid) - end - else - for i = 1, SE3DMax do - Init3DAudioSource(i) - end - end -end - ---释放多余播放器 -local function ReleaeseSource(_uid) - local index = 0 - if _uid then - index = SE2DMax + 1 - while table.nums(audioSourcePool.SE2D[_uid]) > index - 1 do - if audioSourcePool.SE2D[_uid][index].State == Enum.AudioSourceState.Stopped then - local ReleaesedSource = audioSourcePool.SE2D[_uid][index] - table.remove(audioSourcePool.SE2D[_uid], index) - ReleaesedSource:Destroy() - else - index = index + 1 - end - end - else - index = SE3DMax + 1 - while table.nums(audioSourcePool.SE3D) > index - 1 do - if audioSourcePool.SE3D[index].State == Enum.AudioSourceState.Stopped then - local ReleaesedSource = audioSourcePool.SE3D[index] - table.remove(audioSourcePool.SE3D, index) - ReleaesedSource:Destroy() - else - index = index + 1 - end - end - end -end - ---播放2D音频 -function SoundUtil.Play2DSE(_uid, _SEID) - local index = nil - local source = nil - for k, v in pairs(audioSourcePool.SE2D[_uid]) do - if v.State == Enum.AudioSourceState.Stopped then - index = k - source = v - break - end - end - if source == nil then - index = table.nums(audioSourcePool.SE2D[_uid]) + 1 - source = Init2DAudioSource(table.nums(audioSourcePool.SE2D[_uid]) + 1, _uid) - end - --PrintLog('播放2D音频', _SEID) - source.MinDistance = clipTable[_SEID].minDistance - source.MaxDistance = clipTable[_SEID].maxDistance - source.Loop = clipTable[_SEID].isLoop - source.Volume = clipTable[_SEID].volume - source.SoundClip = clipTable[_SEID].clip - source:Play() - ReleaeseSource(_uid) - return index -end - ---播放3D音频 -function SoundUtil.Play3DSE(_pos, _SEID) - local index = nil - local source = nil - for k, v in pairs(audioSourcePool.SE3D) do - if v.State == Enum.AudioSourceState.Stopped then - index = k - source = v - break - end - end - if source == nil then - index = table.nums(audioSourcePool.SE3D) + 1 - source = Init3DAudioSource(table.nums(audioSourcePool.SE3D) + 1) - end - --PrintLog('播放3D音频', _SEID, table.dump(clipTable[_SEID]), _pos) - source.Position = _pos - source.MinDistance = clipTable[_SEID].minDistance - source.MaxDistance = clipTable[_SEID].maxDistance - source.Loop = clipTable[_SEID].isLoop - source.Volume = clipTable[_SEID].volume - source.SoundClip = clipTable[_SEID].clip - source:Play() - ReleaeseSource() - return index -end - ---停止播放2D音频 -function SoundUtil.Stop2DSE(_uid, _index) - audioSourcePool.SE2D[_uid][_index]:Stop() - ReleaeseSource(_uid) -end - ---停止播放3D音频 -function SoundUtil.Stop3DSE(_index) - audioSourcePool.SE3D[_index]:Stop() - ReleaeseSource() -end - -return SoundUtil diff --git a/Code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua deleted file mode 100644 index ead6bd1..0000000 --- a/Code/['World']['Global']['Utility']['TweenControllerModule'].ModuleScript.lua +++ /dev/null @@ -1,38 +0,0 @@ ----控制某个变量随时间变化的协程类 ----@module TweenController ----@copyright Lilith Games, Avatar Team ----@author An Dai -local TweenController = class('TweenController') - ----_name:类名,_sender:使用它的类,_getTotalTime:获得总时间的方法,_update _callback:回调函数 _isFix:是否在fixupdate中执行, _start: 开始函数 -function TweenController:initialize(_name, _sender, _getTotalTime, _update, _callback, _isFix, _start) - _start = _start or function() - return - end - - local updateStr = (_isFix and 'Fix' or '') .. 'Update' - - self.Start = function(self) - _start() - self.totalTime = _getTotalTime() - self.time = 0 - _sender[updateStr .. 'Table'][_name] = self - end - - self[updateStr] = function(self, _dt) - self.time = self.time + _dt - if (self.time > self.totalTime) then - self:Stop() - goto UpdateReturn - end - _update(self.time, self.totalTime, _dt) - ::UpdateReturn:: - end - - self.Stop = function(self) - _sender[updateStr .. 'Table'][_name] = nil - _callback() - end -end - -return TweenController diff --git a/Code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua deleted file mode 100644 index 05cb6a8..0000000 --- a/Code/['World']['Global']['Utility']['ValueChangeUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,137 +0,0 @@ ---- 值改变及值改变事件 ---- @module ValueChangeUtil Module ---- @copyright Lilith Games, Avatar Team ---- @author Xin Tan -local ValueChangeUtil = {} - ---- 数据变化事件 ---- @param _table table 事件表 ---- @param _index string 索引 ---- @param _oldValue mixed 旧值 ---- @param _newValue mixed 新值 ---- @param _targetPlayer PlayerInstance 这条数据对应的玩家实例 -function ValueChangeUtil.DataChangeEvent(_table, _index, _oldValue, _newValue) - if not _table[_index] or type(_table[_index]) ~= "function" then - return - end - _table[_index](_oldValue, _newValue) -end - ---- 将目标表(或其中某个值)改为新值 ---- @param _table table 目标表 ---- @param _index string 目标索引(改整个目标表时填nil) ---- @param _value mixed 新值 ---- @param _eventTable table 数值改变事件表(不响应时不传) -function ValueChangeUtil.ChangeValue(_table, _index, _value, _eventTable) - if type(_table) ~= "table" then - print("[error]传入的目标表类型错误") - return - end - - local tmp = _table - local eventtmp = _eventTable or false - - -- 参数含索引时 - if _index then - local idx = {} - if type(_index) == "string" then - -- 将索引通过'.'拆开 - idx = string.split(_index, '.') - elseif type(_index) == "table" then - idx = _index - end - -- 一层层向下索引 - for i = 1, #idx - 1 do - -- 若目标表没有对应的索引则建立空表 - if type(tmp[idx[i]]) ~= "table" then - tmp[idx[i]] = {} - end - tmp = tmp[idx[i]] - if eventtmp then - if type(eventtmp[idx[i]]) ~= "table" then - eventtmp[idx[i]] = {} - end - eventtmp = eventtmp[idx[i]] - end - end - - -- 若目标值不是table,则直接赋值 - if type(_value) ~= "table" then - local oldValue = table.shallowcopy(tmp[idx[#idx]]) - tmp[idx[#idx]] = _value - if eventtmp then - ValueChangeUtil.DataChangeEvent(eventtmp, idx[#idx], oldValue, _value) - end - return - else - -- 目标值是table - -- 若目标索引不是table,则创建table - if type(tmp[idx[#idx]]) ~= "table" then - tmp[idx[#idx]] = {} - if eventtmp and type( eventtmp[idx[#idx]]) ~= "table" then - eventtmp[idx[#idx]] = {} - end - end - tmp = tmp[idx[#idx]] - if eventtmp then - eventtmp = eventtmp[idx[#idx]] - end - end - else - -- 参数无索引时,从目标表根目录开始同步 - if type(_value) ~= "table" then - print("[error]传入的新值类型错误") - return - end - end - - -- 清除目标索引表与新值的差集 - for k, v in pairs(tmp) do - if not _value[k] then - local oldValue = tmp[k] - tmp[k] = nil - if eventtmp then - ValueChangeUtil.DataChangeEvent(eventtmp, k, oldValue, nil) - end - end - end - - -- 逐层覆盖数据 - for k, v in pairs(_value) do - -- 如果值为table则向下递归 - if type(v) == "table" then - ValueChangeUtil.ChangeValue(tmp, k, v, eventtmp) - else - -- 若值不是table,则直接赋值 - local oldValue = tmp[k] - tmp[k] = v - if eventtmp then - ValueChangeUtil.DataChangeEvent(eventtmp, k, oldValue, v) - end - end - end - if eventtmp then - ValueChangeUtil.DataChangeEvent(eventtmp, "parentTableEvent") - end -end - ---- 进行数据验证,将对照表中存在而目标表中不存在键补充至目标表中 ---- @param _table table 目标表 ---- @param _contrast table 对照表 -function ValueChangeUtil.VerifyTable(_table, _contrast) - for k, v in pairs(_contrast) do - -- 如果值为table则向下递归 - if type(v) == "table" then - if not _table[k] then - _table[k] = table.shallowcopy(v) - else - ValueChangeUtil.VerifyTable(_table[k], v) - end - else - -- 若值不是table,则直接校对 - if not _table[k] then _table[k] = v end - end - end -end - -return ValueChangeUtil diff --git a/Code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua b/Code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua deleted file mode 100644 index 2fb193e..0000000 --- a/Code/['World']['Global']['Utility']['XlsUtilModule'].ModuleScript.lua +++ /dev/null @@ -1,162 +0,0 @@ ---- 读表工具: 将导入成Lua Table,支持单一主键和多主键 --- @module XLS Utility --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang --- @see https://wiki.lilithgames.com/x/RGEMAg -local XlsUtil = {} - ---! 打印事件日志, true:开启打印 -local showLog, PrintGlobalKV, PrintLog = true - ---- 将表中的字符串改为数字 --- @param _t input table -local function StrToNum(_t) - for k, v in pairs(_t) do - _t[k] = tonumber(v) - end - return _t -end - ---- 类型解析配置表 -local parser = { - int = function(_raw) - return math.floor(tonumber(_raw)) - end, - float = function(_raw) - return tonumber(_raw) - end, - string = function(_raw) - return _raw - end, - boolean = function(_raw) - return string.lower(_raw) == 'true' - end, - vector2 = function(_raw) - return Vector2(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - vector3 = function(_raw) - return Vector3(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - euler = function(_raw) - return EulerDegree(table.unpack(StrToNum(string.split(_raw, ',')))) - end, - color = function(_raw) - return Color(table.unpack(StrToNum(string.split(_raw, ',')))) - end -} - ---- 读取配置表,会根据id生成lua表 --- @param _type String 数据类型 --- @parm _stringValue String 数据 --- @return value 解析出来的数值 -local function GetValue(_type, _stringValue) - _type = string.lower(_type) - assert(parser[_type], string.format('[XlsUtil][GlobalSetting] "%s" Type字段的值不是目前所支持的数据类型', _type)) - return parser[_type](_stringValue) -end - ---- 根据id转换lua table -function XlsUtil.GetXlsInfo(_xls, ...) - local ids = {...} - if #ids < 1 or (#ids == 1 and ids[1] == 'Type') then - -- 默认用Type索引,直接返回 - return _xls - end - local rawTable = _xls - local result = {} - local tmp, key, id, idstr -- 临时变量 - for _, v in pairs(rawTable) do - tmp = result - idstr = {} - for i = 1, #ids do - id = ids[i] - key = v[id] - idstr[i] = tostring(id) .. ',' - assert( - not string.isnilorempty(key), - string.format('[XlsUtil] Excel表格没有找到此id, Excel:%s, id: %s', _xls.Name, id) - ) - if i == #ids then - -- 最后的键,确定唯一性 - assert( - not tmp[key], - string.format('[XlsUtil] Excel数据重复, ids不是唯一的, Excel: %s, ids: %s', _xls.Name, table.concat(idstr)) - ) - tmp[key] = v - else - -- 多键,之后还有 - if tmp[key] == nil then - tmp[key] = {} - end - tmp = tmp[key] - end - end - end - return result -end - ---- 读取Config全局配置表 -function XlsUtil.GetGlobalXlsInfo(_xls) - local rawTable = _xls - if table.nums(rawTable) == 0 then - return - end - assert(rawTable[1].Key, '[XlsUtil] 全局配置表的没有"Key"') - assert(rawTable[1].Type, '[XlsUtil] 全局配置表的没有"Type"') - assert(rawTable[1].Value, '[XlsUtil] 全局配置表的没有"Value"') - local result = {} - for _, v in pairs(rawTable) do - result[v.Key] = GetValue(v['Type'], v['Value']) - PrintGlobalKV(v.Key, v.Type, result[v.Key]) -- * 输出KV键值对 - end - return result -end - ---- 表格预加载,预加载配置模块:World.Global.Define.ConfigModule -function XlsUtil.PreloadXls(_preloadList, _xlsRoot, _config) - -- todo: load xls lua talbe - assert(_preloadList and #_preloadList > 0, 'ConfigModule中没有预加载表格') - - for _, pl in pairs(_preloadList) do - if not string.isnilorempty(pl.xls) then - pl.name = string.isnilorempty(pl.name) and pl.xls or pl.name - pl.module = string.isnilorempty(pl.module) and pl.xls .. 'Xls' or pl.module - PrintLog(string.format('[XlsUtil] Load: %s', pl.module)) - if pl.xls == 'GlobalSetting' and _xlsRoot[pl.module .. 'Module'] then - _config[pl.name] = XlsUtil.GetGlobalXlsInfo(_G[pl.module]) - elseif not string.isnilorempty(pl.xls) and _G[pl.module] then - pl.ids = pl.ids or {} - _config[pl.name] = XlsUtil.GetXlsInfo(_G[pl.module], table.unpack(pl.ids)) - end - end - end -end - ---! 辅助功能 - ---- 输出全局变量键值对 -PrintGlobalKV = - showLog and - function(_key, _type, _value) - _type = string.lower(_type) - local showTypes = { - vector2 = 'Vector2', - vector3 = 'Vector3', - euler = 'EulerDegree', - color = 'Color' - } - if showTypes[_type] then - print(string.format('[XlsUtil][GlobalSetting] %s = %s%s ', _key, showTypes[_type], _value)) - else - print(string.format('[XlsUtil][GlobalSetting] %s = %s ', _key, _value)) - end - end or - function() - end - -PrintLog = showLog and function(...) - print(...) - end or function() - end - -return XlsUtil diff --git a/Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua deleted file mode 100644 index 3eec604..0000000 --- a/Code/['World']['Global']['Xls']['Example1XlsModule'].ModuleScript.lua +++ /dev/null @@ -1,111 +0,0 @@ ---- This file is generated by ava-x2l.exe, ---- Don't change it manaully. ---- @copyright Lilith Games, Project Da Vinci(Avatar Team) ---- @see Official Website: https://www.projectdavinci.com/ ---- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava ---- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua ---- source file: ./Xls/ExampleTable1.xlsx - -local Example1Xls = { - [1] = { - house = { - id = 1, - name = 'house', - use_money = 1000, - use_food = 2.33, - is_init = true, - defense = 100, - args_int_arr = {1, 2, 3}, - args_float_arr = {1.23, 2, 3.23}, - args_string_arr = {'sdf', '23e', 's'}, - args_bool_arr = {true, false, true}, - args_vect2 = Vector2(-1, 0.5), - args_vect3 = Vector3(2, 0.3, -4), - args_euler = EulerDegree(12, 23, 43), - args_color = Color(129, 12, 3, 0), - args_lua = function() print(23) end, - Des1 = 'Example1_Des1_1_house', - Des2 = 'Example1_Des2_1_house' - }, - MMM = { - id = 1, - name = 'MMM', - use_money = 123, - use_food = 336.2, - is_init = true, - defense = nil, - args_int_arr = {1, 2, 3}, - args_float_arr = {1, 2.3445, 3}, - args_string_arr = {'你好', '你在哪'}, - args_bool_arr = {true, false}, - args_vect2 = Vector2(0, 4), - args_vect3 = Vector3(-2, 3, 5), - args_euler = nil, - args_color = nil, - args_lua = {a = 2, b='234'}, - Des1 = 'Example1_Des1_1_MMM', - Des2 = 'Example1_Des2_1_MMM' - }, - ddd = { - id = 1, - name = 'ddd', - use_money = 456, - use_food = 222.33665, - is_init = false, - defense = 130, - args_int_arr = {3, 2, 5}, - args_float_arr = {3, 2, 2.5}, - args_string_arr = {'我在这里啊', '你在那', '呢'}, - args_bool_arr = {false, true}, - args_vect2 = Vector2(2, 0.5), - args_vect3 = Vector3(0.6, 3, -8.4), - args_euler = nil, - args_color = nil, - args_lua = nil, - Des1 = 'Example1_Des1_1_ddd', - Des2 = 'Example1_Des2_1_ddd' - } - }, - [2] = { - farm = { - id = 2, - name = 'farm', - use_money = 100, - use_food = 220.0, - is_init = false, - defense = 200, - args_int_arr = {2, 3}, - args_float_arr = {200.3, 3, 234.23}, - args_string_arr = {'df', 'ssd', 'dd', 'dd'}, - args_bool_arr = nil, - args_vect2 = nil, - args_vect3 = nil, - args_euler = nil, - args_color = nil, - args_lua = nil, - Des1 = 'Example1_Des1_2_farm', - Des2 = 'Example1_Des2_2_farm' - }, - MMM = { - id = 2, - name = 'MMM', - use_money = nil, - use_food = 22.1, - is_init = nil, - defense = 234, - args_int_arr = {3, 6, 6, 7}, - args_float_arr = {3, 6.3, 6, 7}, - args_string_arr = {'ss', 'd', 'd', 'd'}, - args_bool_arr = {true, true}, - args_vect2 = nil, - args_vect3 = nil, - args_euler = nil, - args_color = nil, - args_lua = "还没有添加检查", - Des1 = 'Example1_Des1_2_MMM', - Des2 = 'Example1_Des2_2_MMM' - } - } -} - -return Example1Xls diff --git a/Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua deleted file mode 100644 index a7a10e0..0000000 --- a/Code/['World']['Global']['Xls']['Example2XlsModule'].ModuleScript.lua +++ /dev/null @@ -1,84 +0,0 @@ ---- This file is generated by ava-x2l.exe, ---- Don't change it manaully. ---- @copyright Lilith Games, Project Da Vinci(Avatar Team) ---- @see Official Website: https://www.projectdavinci.com/ ---- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava ---- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua ---- source file: ./Xls/ExampleTable1.xlsx - -local Example2Xls = { - [1] = { - id = 1, - name = 'house', - use_money = 1000, - use_food = 2.33, - is_init = true, - defense = 100, - args1 = {1, 2, 3}, - args2 = {1.23, 2, 3.23}, - args3 = {'sdf', '23e', 's'}, - args4 = {true, false, true} - }, - [2] = { - id = 2, - name = '你好吗?', - use_money = 123, - use_food = 336.2, - is_init = true, - defense = nil, - args1 = {1, 2, 3}, - args2 = {1, 2.3445, 3}, - args3 = {'你好', '你在哪'}, - args4 = {true, false} - }, - [3] = { - id = 3, - name = '', - use_money = 456, - use_food = 222.33665, - is_init = false, - defense = 130, - args1 = {3, 2, 5}, - args2 = {3, 2, 2.5}, - args3 = {'我在这里啊', '你在那', '呢'}, - args4 = {false, true} - }, - [4] = { - id = 4, - name = 'farm', - use_money = 100, - use_food = 220.0, - is_init = false, - defense = 200, - args1 = {2, 3}, - args2 = {200.3, 3, 234.23}, - args3 = {'df', 'ssd', 'dd', 'dd'}, - args4 = nil - }, - [5] = { - id = 5, - name = 'house5', - use_money = nil, - use_food = 22.1, - is_init = nil, - defense = 234, - args1 = {3, 6, 6, 7}, - args2 = {3, 6.3, 6, 7}, - args3 = {'ss', 'd', 'd', 'd'}, - args4 = {true, true} - }, - [6] = { - id = 6, - name = 'horse3', - use_money = 200, - use_food = nil, - is_init = false, - defense = 333, - args1 = nil, - args2 = nil, - args3 = {'2e', 'w', 'e', 'we'}, - args4 = {false, false, false, false} - } -} - -return Example2Xls diff --git a/Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua deleted file mode 100644 index b50df0f..0000000 --- a/Code/['World']['Global']['Xls']['LanguagePackXlsModule'].ModuleScript.lua +++ /dev/null @@ -1,82 +0,0 @@ ---- This file is generated by ava-x2l.exe, ---- Don't change it manaully. ---- @copyright Lilith Games, Project Da Vinci(Avatar Team) ---- @see Official Website: https://www.projectdavinci.com/ ---- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava ---- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua ---- source file: ./Xls/LanguagePack.xls - -local LanguagePackXls = { - Example1_Des1_1_house = { - ID = 'Example1_Des1_1_house', - CHS = '我真的很想%s吃饭', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des2_1_house = { - ID = 'Example1_Des2_1_house', - CHS = '做什么', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des1_1_MMM = { - ID = 'Example1_Des1_1_MMM', - CHS = '我饿了', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des2_1_MMM = { - ID = 'Example1_Des2_1_MMM', - CHS = '工作是什么', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des1_1_ddd = { - ID = 'Example1_Des1_1_ddd', - CHS = '到底什么时候能吃饭', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des2_1_ddd = { - ID = 'Example1_Des2_1_ddd', - CHS = '我是谁', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des1_2_farm = { - ID = 'Example1_Des1_2_farm', - CHS = '今天晚上吃什么', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des2_2_farm = { - ID = 'Example1_Des2_2_farm', - CHS = '我从哪里来', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des1_2_MMM = { - ID = 'Example1_Des1_2_MMM', - CHS = '下班就去吃饭吧', - CHT = '', - EN = '', - JP = '' - }, - Example1_Des2_2_MMM = { - ID = 'Example1_Des2_2_MMM', - CHS = '就这样吧', - CHT = '', - EN = '', - JP = '' - } -} - -return LanguagePackXls diff --git a/Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua b/Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua deleted file mode 100644 index 72806fa..0000000 --- a/Code/['World']['Global']['Xls']['SoundXlsModule'].ModuleScript.lua +++ /dev/null @@ -1,22 +0,0 @@ ---- This file is generated by ava-x2l.exe, ---- Don't change it manaully. ---- @copyright Lilith Games, Project Da Vinci(Avatar Team) ---- @see Official Website: https://www.projectdavinci.com/ ---- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava ---- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua ---- source file: ./Xls/Sound.xls - -local SoundXls = { - test_01 = { - Type = 1, - ID = 'test_01', - IsLoop = nil, - Volume = nil, - FileName = '', - Detail = '', - Duration = nil, - CoverPlay = nil - } -} - -return SoundXls diff --git a/Code/['World']['Server']['Init']['ServerAwakeScript'].Script.lua b/Code/['World']['Server']['Init']['ServerAwakeScript'].Script.lua deleted file mode 100644 index 4e31657..0000000 --- a/Code/['World']['Server']['Init']['ServerAwakeScript'].Script.lua +++ /dev/null @@ -1,9 +0,0 @@ ---- 服务器代码入口 --- @script Server Awake Function --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang, Dead Ratman -_G.S = Server -ModuleUtil.LoadModules(world.Server.Module, S) -S:Run() - - diff --git a/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv b/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv deleted file mode 100644 index 410feec..0000000 --- a/Csv/['World']['Localization']['ResourcesTable'].LocalizationResourceTable.csv +++ /dev/null @@ -1 +0,0 @@ -LocalizeKey,Note,Default diff --git a/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv b/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv deleted file mode 100644 index 410feec..0000000 --- a/Csv/['World']['Localization']['TextTable'].LocalizationTextTable.csv +++ /dev/null @@ -1 +0,0 @@ -LocalizeKey,Note,Default diff --git "a/Design/\347\255\226\345\210\222\346\226\207\346\241\243\346\224\276\350\277\231\351\207\214.txt" "b/Design/\347\255\226\345\210\222\346\226\207\346\241\243\346\224\276\350\277\231\351\207\214.txt" new file mode 100644 index 0000000..e69de29 diff --git a/Log/ava_release_v1.1.md b/Log/ava_release_v1.1.md deleted file mode 100644 index 23989a4..0000000 --- a/Log/ava_release_v1.1.md +++ /dev/null @@ -1,40 +0,0 @@ -# Ava v1.1 Release Log -**Time**: 2020/05/28 - -## New Features - -### Utilities - -* `LinkedListModule`, C#双向链表, by 程智 -* `UuidModule`, UUID生成, by 马尚 -* `SoundUtilModule`, 音频播放模块, by 马尚, 袁晨曦 -* `CamUtilModule`, 相机控制模块, by 马尚, 袁晨曦 -* `LuaJsonUtilModule`, Lua table和Json模块互转, 用于发送table作为事件参数, by 马尚, 袁晨曦 -* `ObjPoolUtilModule`, 通用对象池, by 袁晨曦 -* `LogUtilModule`, 日志打印模块, by 张远程 - -### Modules - -* `S_Module/TimeMgrModule`, 时间管理模块, by 陈炳云 -* `C_Module/PlayerCsvModule`, 客户端CSV初始化, by 张远程 - -### Plugin -* `Plugin/FUNC_Guide`, 界面UI强引导, by 张心悟 - -## Improvement - -* smap支持波尔版本 -* 整理玩家客户端节点结构, by 张心悟 -* 按照代码规范整理框架 -* 将测试脚本整理 -* 优化UI动画插件 -* 优化CSV表中以Type为索引id的载入 -* 优化PlayerControl相关脚本 - -## Dev Process - -1. GitHub Issue 添加默认模板(`Debug`, `New Feature`), by 张远程 -2. GitHub的分支管理 - -## Other -* `v1.1`版本的debug和文档工作将在`v1.1.1`中陆续完成。 \ No newline at end of file diff --git a/README.md b/README.md index dc08d48..4bc4ab9 100644 --- a/README.md +++ b/README.md @@ -14,24 +14,41 @@ ## 爆炸派对 ![avatar logo en clear 512x512](https://user-images.githubusercontent.com/4829591/120153673-94239000-c221-11eb-9d00-25f5daf7f26f.png) -* :boom: 爆炸派对([Boom! Party](https://play.google.com/store/apps/details?id=com.boomparty.avatar))是一款神秘的游戏引擎,以及 [Project Davinci 编辑器](https://www.projectdavinci.com)。 -* :writing_hand: 编程语言是 [Lua v5.3](https://www.lua.org/manual/5.3/)。 -* :fox_face: [AvaKit](https://github.com/lilith-avatar/avatar-ava/releases) 是一款基于 [Project Davinci 编辑器面](https://www.projectdavinci.com) 向开发者使用的开发框架。 +* 💥 爆炸派对([Boom! Party](https://play.google.com/store/apps/details?id=com.boomparty.avatar))是一款神秘的游戏引擎,以及 [Project Davinci 编辑器](https://www.projectdavinci.com)。 +* ✍️ 编程语言是 [Lua v5.3](https://www.lua.org/manual/5.3/)。 +* 🦊 [AvaKit](https://github.com/lilith-avatar/avatar-ava/releases) 是一款基于 [Project Davinci 编辑器面](https://www.projectdavinci.com) 向开发者使用的开发框架。 +* ❣️ 编辑器版本:`Version:0.14.0.72152`。 ## 分支管理 -* :bust_in_silhouette: **主干分支 [main](https://github.com/lilith-avatar/avatar-ava)** trunk branch for debug after development finish, and documents -* :busts_in_silhouette: **开发分支 [dev](https://github.com/lilith-avatar/avatar-ava/tree/dev)** development branch for new feature, debug and new issue -* :speaking_head: **发布分支 [release](https://github.com/lilith-avatar/avatar-ava/tree/release)** release new version, can't change until next version release +* 👩‍⚕️ **主干分支 [main](https://github.com/lilith-avatar/avatar-ava)** 用于功能开发后合入、debug、更新文档。 +* 👪 **开发分支 [dev](https://github.com/lilith-avatar/avatar-ava/tree/dev)** `dev-`开头的分支,用于功能开发和debug。 +* 👨‍👩‍👧‍👦 **发布分支 [release](https://github.com/lilith-avatar/avatar-ava/tree/release)** 用于版本发布,只可以从`main`拉取,切必须对应tag标签,不允许直接`push`。 +* 👨‍👩‍👧‍👧 **发布分支 [release-avakit](https://github.com/lilith-avatar/avatar-ava/tree/release-avakit)** 用于`git submodule`,只包含`AvaKit`代码,无其他内容。 -## Ava Wiki -* [How to use](https://github.com/lilith-avatar/avatar-ava/wiki/Get-Started) -* [Ava Hierarchy](https://github.com/lilith-avatar/avatar-ava/wiki/Hierarchy) -* [Reference](https://github.com/lilith-avatar/avatar-ava/wiki/Reference) -* [Plugins](https://github.com/lilith-avatar/avatar-ava/wiki/Plugins) +## 快速使用 + +AvaKit有多种方法使用,应用与不同的使用需求。 + +#### 直接从GitHub下载 + +* 下载最新[release版本](https://github.com/lilith-avatar/avatar-ava/releases); +* 下载最新[zip](https://github.com/lilith-avatar/avatar-ava/archive/refs/heads/release.zip)。 + +#### 使用GitHub克隆仓库 -## Quick Start -Several quick start options are available: +* 克隆repo:`git clone https://github.com/lilith-avatar/avatar-ava.git`。 + +#### 使用Git Submodule功能引用AvaKit代码 + +* 开一个新的smap,例如`ProjectA.smap`; +* 开启Lua代码与Resource映射,添加`ProjectA.smap.config`; +* 添加`AvaKit`为git submodule,执行 `git submodule add --force -b release-avakit https://github.com/lilith-avatar/avatar-ava Smap/Lua/AvaKit`; +* 在`Smap/Lua`目录中添加`Common`、`Client`、`Server`目录,已经对应的`Manifest.lua`文件。 + +## Ava Wiki +* [如何使用 How to use](https://github.com/lilith-avatar/avatar-ava/wiki/Get-Started) +* [项目结构 Ava Hierarchy](https://github.com/lilith-avatar/avatar-ava/wiki/Hierarchy) +* [相关引用 Reference](https://github.com/lilith-avatar/avatar-ava/wiki/Reference) +* [插件系统 Plugins](https://github.com/lilith-avatar/avatar-ava/wiki/Plugins) - * [Download the latest release](https://github.com/lilith-avatar/avatar-ava/releases) - * Clone this repo: `git clone https://github.com/lilith-avatar/avatar-ava.git` diff --git "a/Resource/\347\276\216\346\234\257\350\265\204\346\272\220\346\224\276\350\277\231\351\207\214.txt" "b/Resource/\347\276\216\346\234\257\350\265\204\346\272\220\346\224\276\350\277\231\351\207\214.txt" new file mode 100644 index 0000000..e69de29 diff --git a/Smap/AvaKit.smap b/Smap/AvaKit.smap new file mode 100644 index 0000000..42d5b55 --- /dev/null +++ b/Smap/AvaKit.smap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b31d068000df85ce27dd27e885c87b79431d6e2d8f8e292f488b306dff01053 +size 122365 diff --git a/Smap/Lua/AvaKit/AvaKit.lua b/Smap/Lua/AvaKit/AvaKit.lua new file mode 100644 index 0000000..3fa5b7b --- /dev/null +++ b/Smap/Lua/AvaKit/AvaKit.lua @@ -0,0 +1,137 @@ +--- 将Global.Module目录下每一个用到模块提前require,定义为全局变量 +--- @module Module Defines +--- @copyright Lilith Games, Avatar Team + +local AvaKit = {} +local PATH_ROOT = 'Lua/' +local PATH_AVAKIT = 'Lua/AvaKit/' +local PATH_LUA_EXT = PATH_AVAKIT .. 'LuaExt/' +local PATH_UTIL = PATH_AVAKIT .. 'Util/' +local PATH_FRAMEWORK = PATH_AVAKIT .. 'Framework/' +local PATH_CLIENT = PATH_AVAKIT .. 'Framework/Client/' +local PATH_SERVER = PATH_AVAKIT .. 'Framework/Server/' + +local started = false + +--- 初始化Lua扩展库 +function InitLuaExt() + require(PATH_LUA_EXT .. 'GlobalExt') + require(PATH_LUA_EXT .. 'StringExt') + require(PATH_LUA_EXT .. 'TableExt') + require(PATH_LUA_EXT .. 'MathExt') + _G.Queue = require(PATH_LUA_EXT .. 'Queue') + _G.Stack = require(PATH_LUA_EXT .. 'Stack') +end + +--- 初始化AvaKit +function InitAvaKit() + InitGlobal() + RequireConfig() + RequireUtils() + RequireFramework() + RequireManifest() + InitCommonModules() +end + +--- 初始化Global +function InitGlobal() + _G.Ava = {} + _G.Data = {} + _G.C = {} + _G.S = {} +end + +--- 引用工具模块 +function RequireConfig() + Ava.Config = require(PATH_AVAKIT .. 'Config') +end + +--- 引用工具模块 +function RequireUtils() + Ava.Util = {} + + -- Require Utils + Ava.Util.Mod = require(PATH_UTIL .. 'Module') + Ava.Util.LuaJson = require(PATH_UTIL .. 'LuaJson') + Ava.Util.Net = require(PATH_UTIL .. 'Net') + Ava.Util.Event = require(PATH_UTIL .. 'Event') + Ava.Util.Time = require(PATH_UTIL .. 'Time') + + -- Init Utils + Ava.Util.Time.Init() + + --FIXME: 为了向下兼容 + _G.ModuleUtil = Ava.Util.Mod + _G.JSON = Ava.Util.LuaJson + _G.NetUtil = Ava.Util.Net +end + +--- 引用框架 +function RequireFramework() + -- Framework + Ava.Framework = {} + Ava.Framework.MetaData = require(PATH_FRAMEWORK .. 'MetaData') + + -- Client + Ava.Framework.Client = {} + Ava.Framework.Client.Base = require(PATH_CLIENT .. 'ClientBase') + Ava.Framework.Client.DataSync = require(PATH_CLIENT .. 'ClientDataSync') + Ava.Framework.Client.Heartbeat = require(PATH_CLIENT .. 'ClientHeartbeat') + Ava.Framework.Client.Main = require(PATH_CLIENT .. 'ClientMain') + + -- Server + Ava.Framework.Server = {} + Ava.Framework.Server.Base = require(PATH_SERVER .. 'ServerBase') + Ava.Framework.Server.DataSync = require(PATH_SERVER .. 'ServerDataSync') + Ava.Framework.Server.Heartbeat = require(PATH_SERVER .. 'ServerHeartbeat') + Ava.Framework.Server.Main = require(PATH_SERVER .. 'ServerMain') + + --FIXME: + _G.ClientBase = Ava.Framework.Client.Base + _G.ServerBase = Ava.Framework.Server.Base + _G.Data.Global = {} + _G.Data.Player = {} + _G.Data.Players = {} + _G.MetaData = Ava.Framework.MetaData +end + +--- 引用Manifest +function RequireManifest() + Ava.Manifest = {} + Ava.Manifest.Common = require(PATH_ROOT .. 'Common/Manifest') + Ava.Manifest.Client = require(PATH_ROOT .. 'Client/Manifest') + Ava.Manifest.Server = require(PATH_ROOT .. 'Server/Manifest') +end + +--- 加载Common脚本 +function InitCommonModules() + Ava.Util.Mod.LoadManifest(_G, Ava.Manifest.Common, Ava.Manifest.Common.ROOT_PATH) +end + +--- 开始AvaKit +function AvaKit.Start() + if started then + return + end + print('[AvaKit] Start()') + InitLuaExt() + InitAvaKit() +end + +--- 启动客户端 +function AvaKit.StartClient() + AvaKit.Start() + wait() --间隔1帧 + print('Ava.Framework.Client.Main:Run()') + Ava.Framework.Client.Main:Run() +end + +--- 启动服务器 +function AvaKit.StartServer() + AvaKit.Start() + wait() --间隔1帧 + print('Ava.Framework.Server.Main:Run()') + Ava.Framework.Server.Main:Run() +end + +return AvaKit diff --git a/Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Config.lua similarity index 91% rename from Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Config.lua index 24c0af7..0150176 100644 --- a/Code/['World']['Global']['Framework']['FrameworkConfigModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Config.lua @@ -1,4 +1,4 @@ ---- 框架配置 +--- 框架默认配置 --- @module Framework Global FrameworkConfig --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang @@ -7,6 +7,8 @@ local FrameworkConfig = { DebugMode = true, -- 启动心跳 HeartbeatStart = true, + -- 启动数据同步 + DataSyncStart = false, -- 长期存储:玩家数据定时保存时间间隔(秒) DatabaseAutoSaveTime = 30, -- 长期存储:重新读取游戏数据时间间隔(秒) @@ -20,7 +22,7 @@ local FrameworkConfig = { -- threshold_1 -> threshold_2 : disconnected, but player can reconnect -- threshold_2 -> longer : disconnected, remove player HeartbeatThreshold1 = 5, - HeartbeatThreshold2 = 10, + HeartbeatThreshold2 = 10 }, -- 客户端配置 Client = { @@ -31,7 +33,7 @@ local FrameworkConfig = { -- threshold_1 -> threshold_2 : disconnected, weak network, can reconnect -- threshold_2 -> longer : disconnected, quit server HeartbeatThreshold1 = 5, - HeartbeatThreshold2 = 10, + HeartbeatThreshold2 = 10 }, --! Debug相关 Debug = { diff --git a/Smap/Lua/AvaKit/Framework/Client/ClientBase.lua b/Smap/Lua/AvaKit/Framework/Client/ClientBase.lua new file mode 100644 index 0000000..0438820 --- /dev/null +++ b/Smap/Lua/AvaKit/Framework/Client/ClientBase.lua @@ -0,0 +1,18 @@ +--- 客户端模块基础类, Client Module Base Class +--- @module ClientBase, Client-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local ClientBase = class('ClientBase') + +function ClientBase:GetSelf() + return self +end + +--- 加载的时候运行的代码 +function ClientBase:InitDefault(_module) + -- print(string.format('[ClientBase][%s] InitDefault()', self.name)) + -- 初始化默认监听事件 + Ava.Util.Event.LinkConnects(localPlayer.C_Event, _module, self) +end + +return ClientBase diff --git a/Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Framework/Client/ClientDataSync.lua similarity index 72% rename from Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Framework/Client/ClientDataSync.lua index 22b413b..40cea7c 100644 --- a/Code/['World']['Global']['Framework']['Client']['ClientDataSyncModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Framework/Client/ClientDataSync.lua @@ -4,16 +4,16 @@ --- @author Yuancheng Zhang local ClientDataSync = {} --- Localize global vars -local FrameworkConfig, MetaData = FrameworkConfig, MetaData +--- Localize global vars +local Config, MetaData = Ava.Config, Ava.Framework.MetaData --- 客户端私有数据 +--- 客户端私有数据 local rawDataGlobal = {} local rawDataPlayer = {} --- 打印数据同步日志 -local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyncLog and function(...) - print('[DataSync][Client]', ...) +local PrintLog = Config.DebugMode and Config.Debug.ShowDataSyncLog and function(...) + print('[AvaKit][DataSync][Client]', ...) end or function() end @@ -21,7 +21,7 @@ local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyn --- 数据初始化 function ClientDataSync.Init() - print('[DataSync][Client] Init()') + print('[AvaKit][DataSync][Client] Init()') InitEventsAndListeners() InitDataDefines() end @@ -45,10 +45,16 @@ end --- 校验数据定义 function InitDataDefines() --* 客户端全局数据 - Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, MetaData.Enum.CLIENT) - -- 默认赋值 - for k, v in pairs(Data.Default.Global) do - Data.Global[k] = v + if Server.Exist then + -- 同虚拟机,不同步 + Data.Global = Data.Global or Data.Default.Global + else + -- 不同虚拟,同步 + Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, MetaData.Enum.CLIENT) + -- 默认赋值 + for k, v in pairs(Data.Default.Global) do + Data.Global[k] = v + end end --* 客户端玩家数据 @@ -63,7 +69,7 @@ end --- 开始同步 function ClientDataSync.Start() - print('[DataSync][Client] 客户端数据同步开启') + print('[AvaKit][DataSync][Client] 客户端数据同步开启') MetaData.ClientSync = true end @@ -89,7 +95,7 @@ function DataSyncS2CEventHandler(_path, _value) else error( string.format( - '[DataSync][Client] _path错误 _player = %s, _path = %s, _value = %s', + '[AvaKit][DataSync][Client] _path错误 _player = %s, _path = %s, _value = %s', localPlayer, _path, table.dump(_value) diff --git a/Smap/Lua/AvaKit/Framework/Client/ClientHeartbeat.lua b/Smap/Lua/AvaKit/Framework/Client/ClientHeartbeat.lua new file mode 100644 index 0000000..0fd800a --- /dev/null +++ b/Smap/Lua/AvaKit/Framework/Client/ClientHeartbeat.lua @@ -0,0 +1,166 @@ +--- 游戏心跳 +--- @module Client Heartbeat, Client-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local ClientHeartbeat = {} + +-- 心跳包间隔时间,单位:秒 +local HEARTBEAT_DELTA = Ava.Config.Client.HeartbeatDelta + +-- 心跳阈值,单位:秒,范围定义如下: +-- 0s -> threshold_1 : connected +-- threshold_1 -> threshold_2 : disconnected, weak network +-- threshold_2 -> longer : disconnected, quit server +local HEARTBEAT_THRESHOLD_1 = Ava.Config.Client.HeartbeatThreshold1 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_2 = Ava.Config.Client.HeartbeatThreshold2 * 1000 -- second => ms + +--- 玩家心跳连接状态 +local HeartbeatEnum = { + CONNECT = 1, -- 在线 + DISCONNECT = 2 -- 离线 +} + +--- 正在运行 +local running = false + +--- 上一次服务器发来的心跳时间戳缓存 +local cache = { + sTimestamp = nil, + cTimestamp = nil +} + +--- 临时变量 +local diff -- 时间戳插值 +local sTmpTs, cTmpTs -- 时间戳缓存 + +--- 打印心跳日志 +local PrintHb = Ava.DebugMode and Ava.Config.Debug.ShowHeartbeatLog and function(...) + print('[AvaKit][Heartbeat][Client]', ...) + end or function() + end + +--! 外部接口 + +--- 初始化心跳包 +function ClientHeartbeat.Init() + print('[AvaKit][Heartbeat][Client] Init()') + CheckConfig() + InitEventsAndListeners() +end + +--- 开始发出心跳 +function ClientHeartbeat.Start() + print('[AvaKit][Heartbeat][Client] Start()') + local cTimestamp + running = true + while (running) do + Update() + wait(HEARTBEAT_DELTA) + end +end + +-- 停止心跳 +function ClientHeartbeat.Stop() + print('[AvaKit][Heartbeat][Client] Stop()') + running = false +end + +--! 私有函数 + +--- 校验心跳参数 +function CheckConfig() + assert(HEARTBEAT_DELTA >= 1, '[AvaKit][Heartbeat][Client] HEARTBEAT_DELTA 必须大于1秒') + assert( + HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA, + '[AvaKit][Heartbeat][Client] HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA' + ) + assert( + HEARTBEAT_THRESHOLD_2 >= HEARTBEAT_THRESHOLD_1, + '[AvaKit][Heartbeat][Client] HEARTBEAT_THRESHOLD_2 >= HEARTBEAT_THRESHOLD_1' + ) +end + +--- 初始化事件和绑定Handler +function InitEventsAndListeners() + if localPlayer.C_Event == nil then + world:CreateObject('FolderObject', 'C_Event', localPlayer) + end + world:CreateObject('CustomEvent', 'HeartbeatS2CEvent', localPlayer.C_Event) + localPlayer.C_Event.HeartbeatS2CEvent:Connect(HeartbeatS2CEventHandler) + + -- OnPlayerJoinEvent(玩家第一次加入,类似现在的OnPlayerAdded) + -- OnPlayerRejoinEvent(玩家离线后重新进入同一个房间) + -- OnPlayerDisconnectEvent(未接收到服务器心跳,在客户端第二个阶段,玩家离线可重连,弱网,转菊花) + -- OnPlayerReconnectEvent(玩家断线后重连) + -- OnPlayerLeaveEvent(玩家彻底离开,退出房间) + world:CreateObject('CustomEvent', 'OnPlayerJoinEvent', localPlayer.C_Event) + -- world:CreateObject('CustomEvent', 'OnPlayerRejoinEvent', localPlayer.C_Event) + world:CreateObject('CustomEvent', 'OnPlayerDisconnectEvent', localPlayer.C_Event) + world:CreateObject('CustomEvent', 'OnPlayerReconnectEvent', localPlayer.C_Event) + world:CreateObject('CustomEvent', 'OnPlayerLeaveEvent', localPlayer.C_Event) + + -- 掉线直接退出(默认,可选) + localPlayer.C_Event.OnPlayerLeaveEvent:Connect(QuitGame) +end + +--- Update心跳 +function Update() + cTmpTs = Timer.GetTimeMillisecond() + sTmpTs = cache.sTimestamp + PrintHb(string.format('=> C = %s, S = %s, %s', cTmpTs, sTmpTs, localPlayer)) + CheckPlayerState(p, cTmpTs) + Ava.Util.Net.Fire_S('HeartbeatC2SEvent', localPlayer, cTmpTs, sTmpTs) +end + +--- 心跳事件Handler +function HeartbeatS2CEventHandler(_stimestamp, _cTimestamp) + if not running then + return + end + PrintHb(string.format('<= C = %s, S = %s, %s', _cTimestamp, _stimestamp, localPlayer)) + CheckPlayerJoin(_player, _sTimestamp) + cache.sTimestamp = _stimestamp + cache.cTimestamp = _cTimestamp +end + +--- 收包时,检查玩家是否连接服务器,或者重新连接服务器 +function CheckPlayerJoin(_player, _sTimestamp) + if not cache.sTimestamp then + --* 玩家新加入 OnPlayerJoinEvent + print('[AvaKit][Heartbeat][Client] OnPlayerJoinEvent, 新玩家加入,', localPlayer) + Ava.Util.Net.Fire_C('OnPlayerJoinEvent', localPlayer) + cache.state = HeartbeatEnum.CONNECT + elseif cache.state == HeartbeatEnum.DISCONNECT then + --* 玩家断线重连 OnPlayerReconnectEvent + print('[AvaKit][Heartbeat][Client] OnPlayerReconnectEvent, 玩家断线重连,', localPlayer) + Ava.Util.Net.Fire_C('OnPlayerReconnectEvent', localPlayer) + cache.state = HeartbeatEnum.CONNECT + end +end + +--- 发包时,检查玩家是否连接服务器 +function CheckPlayerState(_player, _cTimestamp) + if not cache.cTimestamp then + return + end + diff = _cTimestamp - cache.cTimestamp + PrintHb(string.format('==========================================> diff = %s, %s', diff * .001, localPlayer)) + if cache.state == HeartbeatEnum.CONNECT and diff > HEARTBEAT_THRESHOLD_1 then + --* 玩家断线,弱网环境 + print('[AvaKit][Heartbeat][Client] OnPlayerDisconnectEvent, 玩家离线, 弱网环境,', localPlayer) + Ava.Util.Net.Fire_C('OnPlayerDisconnectEvent', localPlayer) + cache.state = HeartbeatEnum.DISCONNECT + elseif cache.state == HeartbeatEnum.DISCONNECT and diff > HEARTBEAT_THRESHOLD_2 then + --* 玩家断线, 退出游戏 + -- QuitGame() + Ava.Util.Net.Fire_C('OnPlayerLeaveEvent', localPlayer) + end +end + +--- 退出游戏 +function QuitGame() + print('[AvaKit][Heartbeat][Client] Game.Quit(), 玩家退出游戏') + Game.Quit() +end + +return ClientHeartbeat diff --git a/Smap/Lua/AvaKit/Framework/Client/ClientMain.lua b/Smap/Lua/AvaKit/Framework/Client/ClientMain.lua new file mode 100644 index 0000000..a240db8 --- /dev/null +++ b/Smap/Lua/AvaKit/Framework/Client/ClientMain.lua @@ -0,0 +1,151 @@ +--- 游戏客户端主逻辑 +--- @module Game Manager, Client-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local Client = {} + +-- 缓存全局变量 +local Ava = Ava +local Heartbeat = Ava.Framework.Client.Heartbeat +local DataSync = Ava.Framework.Client.DataSync + +-- 已经初始化,正在运行 +local initialized, running = false, false + +-- 含有InitDefault(),Init(),Update()的模块列表 +local list = {} +local initDefaultList, initList, updateList = {}, {}, {} + +--! Public + +--- 运行客户端 +function Client:Run() + print('[AvaKit][Client] Run()') + InitClient() + StartUpdate() +end + +--- 停止Update +function Client:Stop() + print('[AvaKit][Client] Stop()') + running = false + Heartbeat.Stop() +end + +--! Private + +--- 初始化 +function InitClient() + if initialized then + return + end + print('[AvaKit][Client] InitClient()') + RequireClientModules() + InitRandomSeed() + InitHeartbeat() + InitDataSync() + InitClientCustomEvents() + GenInitAndUpdateList() + RunInitDefault() + InitOtherModules() + initialized = true +end + +--- 加载客户端模块 +function RequireClientModules() + print('[AvaKit][Client] RequireClientModules()') + _G.C.Events = Ava.Manifest.Client.Events + Ava.Util.Mod.LoadManifest(_G.C, Ava.Manifest.Client, Ava.Manifest.Client.ROOT_PATH, list) +end + +--- 初始化心跳包 +function InitHeartbeat() + assert(Heartbeat, '[AvaKit][Client][Heartbeat] 找不到Ava.Framework.Client.Heartbeat,请联系endaye') + Heartbeat.Init() +end + +--- 初始化数据同步 +function InitDataSync() + if not Ava.Config.DataSyncStart then + return + end + assert(DataSync, '[AvaKit][Server][DataSync] 找不到ServerDataSync,请联系endaye') + DataSync.Init() +end + +--- 初始化客户端的CustomEvent +function InitClientCustomEvents() + if localPlayer.C_Event == nil then + world:CreateObject('FolderObject', 'C_Event', localPlayer) + end + + -- 生成CustomEvent节点 + for _, evt in pairs(C.Events) do + if localPlayer.C_Event[evt] == nil then + world:CreateObject('CustomEvent', evt, localPlayer.C_Event) + end + end +end + +--- 生成需要Init和Update的模块列表 +function GenInitAndUpdateList() + -- print(table.dump(list)) + Ava.Util.Mod.GetModuleListWithFunc(list, 'InitDefault', initDefaultList) + Ava.Util.Mod.GetModuleListWithFunc(list, 'Init', initList) + Ava.Util.Mod.GetModuleListWithFunc(list, 'Update', updateList) +end + +--- 执行默认的Init方法 +function RunInitDefault() + for _, m in ipairs(initDefaultList) do + m:InitDefault(m) + end +end + +--- 初始化客户端随机种子 +function InitRandomSeed() + math.randomseed(os.time()) +end + +--- 初始化包含Init()方法的模块 +function InitOtherModules() + for _, m in ipairs(initList) do + m:Init() + end +end + +--- 开始Update +function StartUpdate() + print('[AvaKit][Client] StartUpdate()') + assert(not running, '[AvaKit][Client] StartUpdate() 正在运行') + + running = true + + -- 开启心跳 + if Ava.Config.HeartbeatStart then + invoke(Heartbeat.Start) + end + + local dt = 0 -- delta time 每帧时间 + local tt = 0 -- total time 游戏总时间 + local now = Timer.GetTimeMillisecond --时间函数缓存 + local prev, curr = now() / 1000, nil -- two timestamps + + while (running and wait()) do + curr = now() / 1000 + dt = curr - prev + tt = tt + dt + prev = curr + UpdateClient(dt, tt) + end +end + +--- Update函数 +-- @param dt delta time 每帧时间 +function UpdateClient(_dt, _tt) + for _, m in ipairs(updateList) do + m:Update(_dt, _tt) + end +end + +return Client diff --git a/Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Framework/MetaData.lua similarity index 73% rename from Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Framework/MetaData.lua index 5648c79..8012a92 100644 --- a/Code/['World']['Global']['Framework']['MetaDataModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Framework/MetaData.lua @@ -1,20 +1,20 @@ ---- 游戏同步数据基类 ---- @module Sync Data Base, Both-side ---- @copyright Lilith Games, Avatar Team ---- @author Yuancheng Zhang +---游戏同步数据基类 +---@module Sync Data Base, Both-side +---@copyright Lilith Games, Avatar Team +---@author Yuancheng Zhang local MetaData = {} --- Localize global vars -local FrameworkConfig = FrameworkConfig +---Localize global vars +local Config = Ava.Config --* 开关:Debug模式,开启后会打印日志 local debugMode = false --* 开关:数据校验 local valid = true --- enum +---enum MetaData.Enum = {} --- 数据类型:全局 or 玩家 +---数据类型:全局 or 玩家 MetaData.Enum.GLOBAL = 'Global' MetaData.Enum.PLAYER = 'Player' @@ -35,11 +35,11 @@ MetaData.ClientSync = false --! 私有方法 ---- 新建一个MetaData的proxy,用于数据同步 ---- @param _data 真实数据 ---- @param _path 当前节点索引路径 ---- @param _uid UserId ---- @return proxy 代理table,没有data,元表内包含方法和path +---新建一个MetaData的proxy,用于数据同步 +---@param _data 真实数据 +---@param _path 当前节点索引路径 +---@param _uid UserId +---@return proxy 代理table,没有data,元表内包含方法和path function NewData(_data, _path, _uid) local proxy = {} local mt = { @@ -70,20 +70,20 @@ function NewData(_data, _path, _uid) return proxy end ---- 获得原始数据 ---- @param _data 真实数据的存储位置 ---- @param _path 当前节点索引路径 ---- @return rawData 纯数据table,不包含元表 +---获得原始数据 +---@param _data 真实数据的存储位置 +---@param _path 当前节点索引路径 +---@return rawData 纯数据table,不包含元表 function GetData(_data, _path) local rawData = {} GetDataAux(_data, _path, rawData) return rawData end ---- GetData的辅助函数 ---- @param _data 真实数据的存储位置 ---- @param _path 当前节点索引路径 ---- @param _rawData 纯数据table,不包含元表 +---GetData的辅助函数 +---@param _data 真实数据的存储位置 +---@param _path 当前节点索引路径 +---@param _rawData 纯数据table,不包含元表 function GetDataAux(_data, _path, _rawData) local key, i local q, elem = Queue:New(), {} @@ -116,12 +116,12 @@ function GetDataAux(_data, _path, _rawData) end end ---- 设置原始数据 ---- @param _data 真实数据的存储位置 ---- @param _path 当前节点索引路径 ---- @param _value 传入的数据 ---- @param _uid UserId ---- @param _sync true:同步数据 +---设置原始数据 +---@param _data 真实数据的存储位置 +---@param _path 当前节点索引路径 +---@param _value 传入的数据 +---@param _uid UserId +---@param _sync true:同步数据 function SetData(_data, _path, _value, _uid, _sync) --* 数据同步:赋值的时候只要同步一次就可以的,存下newpath和_v,对方收到后赋值即可 if _sync and (MetaData.ServerSync or MetaData.ClientSync) then @@ -166,10 +166,10 @@ function SetData(_data, _path, _value, _uid, _sync) end end ---- 数据同步 ---- @param _path 当前节点索引路径 ---- @param _value 传入的数据 ---- @param _uid UserId +---数据同步 +---@param _path 当前节点索引路径 +---@param _value 传入的数据 +---@param _uid UserId function SyncData(_path, _value, _uid) if MetaData.ServerSync and MetaData.ClientSync and localPlayer and not string.isnilorempty(_uid) then --* 服务器/客户端(同虚拟机) @@ -177,54 +177,63 @@ function SyncData(_path, _value, _uid) local player = localPlayer -- local player = world:GetPlayerByUserId(_uid) -- assert(player == localPlayer, string.format('[MetaData] 玩家不存在 uid = %s', _uid)) - PrintLog(string.format('[Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value))) - NetUtil.Fire_C('DataSyncS2CEvent', player, _path, _value) - NetUtil.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) + PrintLog( + string.format('[AvaKit][Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value)) + ) + Ava.Util.Net.Fire_C('DataSyncS2CEvent', player, _path, _value) + Ava.Util.Net.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) elseif localPlayer == nil and string.isnilorempty(_uid) and MetaData.ServerSync then --* 服务器 => 客户端(多端),单向同步 -- Global 全局数据 - NetUtil.Broadcast('DataSyncS2CEvent', _path, _value) + Ava.Util.Net.Broadcast('DataSyncS2CEvent', _path, _value) elseif localPlayer == nil and MetaData.ServerSync then --* 服务器 => 客户端(多端),单向同步 -- Player 玩家数据同步 local player = world:GetPlayerByUserId(_uid) - assert(player, string.format('[MetaData] 玩家不存在 uid = %s', _uid)) - PrintLog(string.format('[Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value))) - NetUtil.Fire_C('DataSyncS2CEvent', player, _path, _value) + assert(player, string.format('[AvaKit][MetaData] 玩家不存在 uid = %s', _uid)) + PrintLog( + string.format('[AvaKit][Server] 发出 player = %s, _path = %s, _value = %s', player, _path, table.dump(_value)) + ) + Ava.Util.Net.Fire_C('DataSyncS2CEvent', player, _path, _value) elseif localPlayer and MetaData.ClientSync then --* 客户端 => 服务器(多端),单项同步 -- Global/Player 两种数据 PrintLog( - string.format('[Client] 发出 player = %s, _path = %s, _value = %s', localPlayer, _path, table.dump(_value)) + string.format( + '[AvaKit][Client] 发出 player = %s, _path = %s, _value = %s', + localPlayer, + _path, + table.dump(_value) + ) ) - NetUtil.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) + Ava.Util.Net.Fire_S('DataSyncC2SEvent', localPlayer, _path, _value) end end --! 公开API ---- 新建数据 +---新建数据 MetaData.New = NewData ---- 设置数据 +---设置数据 MetaData.Set = SetData ---- 从proxy中生成一个纯数据表格 +---从proxy中生成一个纯数据表格 MetaData.Get = function(_proxy) local mt = getmetatable(_proxy) - assert(mt, string.format('[MetaData] metatable为空,proxy = %s', table.dump(_proxy))) + assert(mt, string.format('[AvaKit][MetaData] metatable为空,proxy = %s', table.dump(_proxy))) return GetData(mt._data, mt._path) end --! 辅助方法 ---- 打印数据同步日志 -PrintLog = FrameworkConfig.DebugMode and debugMode and function(...) - print('[MetaData]', ...) +---打印数据同步日志 +PrintLog = Config.DebugMode and debugMode and function(...) + print('[AvaKit][MetaData]', ...) end or function() end ---- 数据校验 +---数据校验 function Validators(func) if not valid then return function() @@ -236,7 +245,7 @@ function Validators(func) assert( _data, string.format( - '[MetaData] data为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', + '[AvaKit][MetaData] data为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', _data, _path, _uid, @@ -247,7 +256,7 @@ function Validators(func) assert( not string.isnilorempty(_path), string.format( - '[MetaData] path为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', + '[AvaKit][MetaData] path为空 data = %s, path = %s, uid = %s, sync = %s, value = %s', _data, _path, _uid, diff --git a/Smap/Lua/AvaKit/Framework/Server/ServerBase.lua b/Smap/Lua/AvaKit/Framework/Server/ServerBase.lua new file mode 100644 index 0000000..c53fffb --- /dev/null +++ b/Smap/Lua/AvaKit/Framework/Server/ServerBase.lua @@ -0,0 +1,18 @@ +--- 服务器模块基础类, Server Module Base Class +--- @module ServerBase, Server-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang, Dead Ratman +local ServerBase = class('ServerBase') + +function ServerBase:GetSelf() + return self +end + +--- 加载的时候运行的代码 +function ServerBase:InitDefault(_module) + -- print(string.format('[ServerBase][%s] InitDefault()', self.name)) + -- 初始化默认监听事件 + Ava.Util.Event.LinkConnects(world.S_Event, _module, self) +end + +return ServerBase diff --git a/Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Framework/Server/ServerDataSync.lua similarity index 64% rename from Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Framework/Server/ServerDataSync.lua index 84322a2..b407f7c 100644 --- a/Code/['World']['Global']['Framework']['Server']['ServerDataSyncModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Framework/Server/ServerDataSync.lua @@ -4,24 +4,24 @@ --- @author Yuancheng Zhang local ServerDataSync = {} --- Localize global vars -local FrameworkConfig, MetaData, DataStore = FrameworkConfig, MetaData, DataStore +--- Localize global vars +local Config, MetaData, DataStore = Ava.Config, Ava.Framework.MetaData, DataStore --- 服务器端私有数据 +--- 服务器端私有数据 local rawDataGlobal = {} local rawDataPlayers = {} --- 玩家数据定时保存时间间隔(秒) -local AUTO_SAVE_TIME = FrameworkConfig.DatabaseAutoSaveTime --- 重新读取游戏数据时间间隔(秒) +--- 玩家数据定时保存时间间隔(秒) +local AUTO_SAVE_TIME = Config.DatabaseAutoSaveTime +--- 重新读取游戏数据时间间隔(秒) local RELOAD_TIME = 1 --- 玩家数据表格 +--- 玩家数据表格 local sheet --- 打印数据同步日志 -local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyncLog and function(...) - print('[DataSync][Server]', ...) +local PrintLog = Config.DebugMode and Config.Debug.ShowDataSyncLog and function(...) + print('[AvaKit][DataSync][Server]', ...) end or function() end @@ -29,7 +29,7 @@ local PrintLog = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowDataSyn --- 数据初始化 function ServerDataSync.Init() - print('[DataSync][Server] Init()') + print('[AvaKit][DataSync][Server] Init()') InitEventsAndListeners() InitDefines() sheet = DataStore:GetSheet('PlayerData') @@ -47,12 +47,12 @@ function InitEventsAndListeners() -- 玩家加入事件 local onPlayerJoinEvent = world.S_Event.OnPlayerJoinEvent - assert(onPlayerJoinEvent, '[DataSync][Server] 不存在 OnPlayerJoinEvent') + assert(onPlayerJoinEvent, '[AvaKit][DataSync][Server] 不存在 OnPlayerJoinEvent') onPlayerJoinEvent:Connect(OnPlayerJoinEventHandler) -- 玩家离开事件 local onPlayerLeaveEvent = world.S_Event.OnPlayerLeaveEvent - assert(onPlayerLeaveEvent, '[DataSync][Server] 不存在 OnPlayerLeaveEvent') + assert(onPlayerLeaveEvent, '[AvaKit][DataSync][Server] 不存在 OnPlayerLeaveEvent') onPlayerLeaveEvent:Connect(OnPlayerLeaveEventHandler) -- 长期存储成功事件 @@ -73,10 +73,16 @@ end --- 初始化Data.Global function InitDataGlobal() --* 服务器全局数据 - Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, nil) - -- 默认赋值 - for k, v in pairs(Data.Default.Global) do - Data.Global[k] = v + if localPlayer then + -- 同虚拟机,不同步 + Data.Global = Data.Global or Data.Default.Global + else + -- 不同虚拟,同步 + Data.Global = Data.Global or MetaData.New(rawDataGlobal, MetaData.Enum.GLOBAL, nil) + -- 默认赋值 + for k, v in pairs(Data.Default.Global) do + Data.Global[k] = v + end end end @@ -99,11 +105,11 @@ end --- 开始同步 function ServerDataSync.Start() - print('[DataSync][Server] 服务器数据同步开启') + print('[AvaKit][DataSync][Server] 服务器数据同步开启') MetaData.ServerSync = true -- 启动定时器 - TimeUtil.SetInterval(SaveAllGameDataAsync, AUTO_SAVE_TIME) + Ava.Util.Time.SetInterval(SaveAllGameDataAsync, AUTO_SAVE_TIME) end --! 长期存储:读取 @@ -112,7 +118,7 @@ end --- @param _uid string 玩家ID function LoadGameDataAsync(_uid) sheet = DataStore:GetSheet('PlayerData') - assert(sheet, '[DataSync][Server] DataPlayers的sheet不存在') + assert(sheet, '[AvaKit][DataSync][Server] DataPlayers的sheet不存在') sheet:GetValue( _uid, function(_val, _msg) @@ -126,32 +132,35 @@ end --- @param _msg int 消息码 --- @param _uid string 玩家ID function LoadGameDataAsyncCb(_val, _msg, _uid) + if not Config.DataSyncStart then + return + end local player = world:GetPlayerByUserId(_uid) - assert(player, string.format('[DataSync][Server] 玩家不存在, uid = %s', _uid)) + assert(player, string.format('[AvaKit][DataSync][Server] 玩家不存在, uid = %s', _uid)) if _msg == 0 or _msg == 101 then - print('[DataSync][Server] 获取玩家数据成功', player.Name) + print('[AvaKit][DataSync][Server] 获取玩家数据成功', player.Name) local hasData = _val ~= nil if hasData then - print('[DataSync][Server] 玩家数据,存在', player.Name) + print('[AvaKit][DataSync][Server] 玩家数据,存在', player.Name) --若以前的数据存在,更新 -- TODO: 数据兼容的处理 local data = _val - assert(data.uid == _uid, string.format('[DataSync][Server] uid校验不通过, uid = %s', _uid)) + assert(data.uid == _uid, string.format('[AvaKit][DataSync][Server] uid校验不通过, uid = %s', _uid)) --若已在此服务器的数据总表存在,则更新数据 for k, v in pairs(data) do Data.Players[_uid][k] = data[k] end else -- 不存在数据,用之前生成的默认数据 - print('[DataSync][Server] 玩家数据,不存在', player.Name) + print('[AvaKit][DataSync][Server] 玩家数据,不存在', player.Name) end - NetUtil.Fire_S('LoadPlayerDataSuccessEvent', player, hasData) - NetUtil.Fire_C('LoadPlayerDataSuccessEvent', player, hasData) + Ava.Util.Net.Fire_S('LoadPlayerDataSuccessEvent', player, hasData) + Ava.Util.Net.Fire_C('LoadPlayerDataSuccessEvent', player, hasData) return end print( string.format( - '[DataSync][Server] 获取玩家数据失败,%s秒后重试, uid = %s, player = %s, msg = %s', + '[AvaKit][DataSync][Server] 获取玩家数据失败,%s秒后重试, uid = %s, player = %s, msg = %s', RELOAD_TIME, _uid, player.Name, @@ -174,12 +183,12 @@ end --- @param _delete string 保存成功后是否删除缓存数据 function SaveGameDataAsync(_uid, _delete) sheet = DataStore:GetSheet('PlayerData') - assert(sheet, '[DataSync][Server] DataPlayers的sheet不存在') - assert(not string.isnilorempty(_uid), '[DataSync][Server] uid不存在或为空') - assert(Data.Players[_uid], string.format('[DataSync][Server] Data.Players[_uid]不存在 uid = %s', _uid)) + assert(sheet, '[AvaKit][DataSync][Server] DataPlayers的sheet不存在') + assert(not string.isnilorempty(_uid), '[AvaKit][DataSync][Server] uid不存在或为空') + assert(Data.Players[_uid], string.format('[AvaKit][DataSync][Server] Data.Players[_uid]不存在 uid = %s', _uid)) local newData = MetaData.Get(Data.Players[_uid]) - assert(newData, string.format('[DataSync][Server] 玩家数据不存在, uid = %s', _uid)) - assert(newData.uid == _uid, string.format('[DataSync][Server] uid校验不通过, uid = %s', _uid)) + assert(newData, string.format('[AvaKit][DataSync][Server] 玩家数据不存在, uid = %s', _uid)) + assert(newData.uid == _uid, string.format('[AvaKit][DataSync][Server] uid校验不通过, uid = %s', _uid)) sheet:SetValue( _uid, newData, @@ -196,9 +205,9 @@ end function SaveGameDataAsyncCb(_val, _msg, _uid, _delete) -- 保存成功 if _msg == 0 then - print('[DataSync][Server] 保存玩家数据,成功', _uid) + print('[AvaKit][DataSync][Server] 保存玩家数据,成功', _uid) if _delete == true then - print('[DataSync][Server] 删除服务器玩家数据', _uid) + print('[AvaKit][DataSync][Server] 删除服务器玩家数据', _uid) rawDataPlayers[_uid] = nil --* 删除玩家端数据 Data.Players[_uid] = nil @@ -207,7 +216,7 @@ function SaveGameDataAsyncCb(_val, _msg, _uid, _delete) end -- 保存失败 - print(string.format('[DataSync][Server] 保存玩家数据失败,%s秒后重试, uid = %s, msg = %s', RELOAD_TIME, _uid, _msg)) + print(string.format('[AvaKit][DataSync][Server] 保存玩家数据失败,%s秒后重试, uid = %s, msg = %s', RELOAD_TIME, _uid, _msg)) --若失败,则1秒后重新再读取一次 invoke( function() @@ -220,10 +229,10 @@ end --- 存储全部玩家数据 function SaveAllGameDataAsync() if not MetaData.ServerSync then - print('[DataSync][Server] ServerSync未开始') + print('[AvaKit][DataSync][Server] ServerSync未开始') return end - print('[DataSync][Server] 尝试保存全部玩家数据……') + print('[AvaKit][DataSync][Server] 尝试保存全部玩家数据……') for uid, data in pairs(Data.Players) do if not string.isnilorempty(uid) and data then SaveGameDataAsync(uid, false) @@ -245,7 +254,9 @@ function DataSyncC2SEventHandler(_player, _path, _value) if string.startswith(_path, MetaData.Enum.GLOBAL) then --* Data.Global:收到客户端改变数据的时候需要同步给其他玩家 - MetaData.Set(rawDataGlobal, _path, _value, nil, true) + if loadPlayer == nil then + MetaData.Set(rawDataGlobal, _path, _value, nil, true) + end elseif string.startswith(_path, MetaData.Enum.PLAYER .. uid) then --* Data.Players MetaData.Set(rawDataPlayers[uid], _path, _value, uid, false) @@ -263,11 +274,12 @@ end --- 新玩家加入事件Handler function OnPlayerJoinEventHandler(_player, _uid) - print('[DataSync][Server] OnPlayerJoinEventHandler', _player, _player.UserId, _uid) + print('[AvaKit][DataSync][Server] OnPlayerJoinEventHandler', _player, _player.UserId, _uid) --* 向客户端同步Data.Global - NetUtil.Fire_C('DataSyncS2CEvent', _player, MetaData.Enum.GLOBAL, MetaData.Get(Data.Global)) - + if localPlayer == nil then + Ava.Util.Net.Fire_C('DataSyncS2CEvent', _player, MetaData.Enum.GLOBAL, MetaData.Get(Data.Global)) + end -- 初始化玩家数据 InitDataPlayer(_uid) @@ -277,7 +289,7 @@ end --- 玩家离开事件Handler function OnPlayerLeaveEventHandler(_player, _uid) - print('[DataSync][Server] OnPlayerLeaveEventHandler', _player, _uid) + print('[AvaKit][DataSync][Server] OnPlayerLeaveEventHandler', _player, _uid) assert(not string.isnilorempty(_uid), '[ServerDataSync] OnPlayerLeaveEventHandler() uid不存在') --* 保存长期存储:rawDataPlayers[_uid] 保存成功后删掉 SaveGameDataAsync(_uid, true) diff --git a/Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Framework/Server/ServerHeartbeat.lua similarity index 57% rename from Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Framework/Server/ServerHeartbeat.lua index e3b6fa0..3871d10 100644 --- a/Code/['World']['Global']['Framework']['Server']['ServerHeartbeatModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Framework/Server/ServerHeartbeat.lua @@ -4,39 +4,39 @@ --- @author Yuancheng Zhang local ServerHeartbeat = {} --- Localize global vars -local FrameworkConfig = FrameworkConfig +--- Localize global vars +local Config = Ava.Config --- 心跳包间隔时间,单位:秒 -local HEARTBEAT_DELTA = FrameworkConfig.Server.HeartbeatDelta +--- 心跳包间隔时间,单位:秒 +local HEARTBEAT_DELTA = Config.Server.HeartbeatDelta --- 心跳阈值,单位:秒,范围定义如下: --- 0s -> threshold_1 : connected --- threshold_1 -> threshold_2 : disconnected, but player can rejoin --- threshold_2 -> longer : disconnected, remove player -local HEARTBEAT_THRESHOLD_1 = FrameworkConfig.Server.HeartbeatThreshold1 * 1000 -- second => ms -local HEARTBEAT_THRESHOLD_2 = FrameworkConfig.Server.HeartbeatThreshold2 * 1000 -- second => ms +--- 心跳阈值,单位:秒,范围定义如下: +--- 0s -> threshold_1 : connected +--- threshold_1 -> threshold_2 : disconnected, but player can rejoin +--- threshold_2 -> longer : disconnected, remove player +local HEARTBEAT_THRESHOLD_1 = Config.Server.HeartbeatThreshold1 * 1000 -- second => ms +local HEARTBEAT_THRESHOLD_2 = Config.Server.HeartbeatThreshold2 * 1000 -- second => ms --- 玩家心跳连接状态 +--- 玩家心跳连接状态 local HeartbeatEnum = { CONNECT = 1, -- 在线 DISCONNECT = 2 -- 离线 } --- 正在运行 +--- 正在运行 local running = false --- 上一次客户端发来的心跳时间戳缓存 +--- 上一次客户端发来的心跳时间戳缓存 local cache = {} --- 临时变量 +--- 临时变量 local diff -- 时间戳插值 local sTmpTs, cTmpTs -- 时间戳缓存 --- 打印心跳日志 --- 这段代码就是先判断Setting.ShowHeartbeatLog这个配置项是否为真 --- 若为真则PrintHb 为一个打印日志的函数 若为假则为一个空函数 -local PrintHb = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowHeartbeatLog and function(...) +--- 这段代码就是先判断Setting.ShowHeartbeatLog这个配置项是否为真 +--- 若为真则PrintHb 为一个打印日志的函数 若为假则为一个空函数 +local PrintHb = Config.DebugMode and Config.Debug.ShowHeartbeatLog and function(...) print('[Heartbeat][Server]', ...) end or function() end @@ -46,9 +46,9 @@ local PrintHb = FrameworkConfig.DebugMode and FrameworkConfig.Debug.ShowHeartbea --- 初始化心跳包 function ServerHeartbeat.Init() print('[Heartbeat][Server] Init()') - --校验心跳参数 + --校验心跳参数 CheckSetting() - --初始化事件和绑定Handler + --初始化事件和绑定Handler InitEventsAndListeners() end @@ -58,7 +58,6 @@ function ServerHeartbeat.Start() running = true while (running) do Update() - -- 每隔心跳包间隔时间,则检查一次每个加入的客户端心跳 wait(HEARTBEAT_DELTA) end end @@ -73,8 +72,6 @@ end --- 校验心跳参数 function CheckSetting() - -- API-assert() - --当第一个参数的值是false或者nil的时候展示一个错误,否则返回所有的参数值。 assert(HEARTBEAT_DELTA >= 1, '[Heartbeat][Server] HEARTBEAT_DELTA 必须大于1秒') assert(HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA, '[Heartbeat][Server] HEARTBEAT_THRESHOLD_1 >= HEARTBEAT_DELTA') assert( @@ -85,13 +82,11 @@ end --- 初始化事件和绑定Handler function InitEventsAndListeners() - --创建服务端事件节点 + --创建服务端事件节点 if world.S_Event == nil then world:CreateObject('FolderObject', 'S_Event', world) end - --在world.S_Event下创建CustomEvent事件 world:CreateObject('CustomEvent', 'HeartbeatC2SEvent', world.S_Event) - --为CustomEvent事件绑定心跳事件Handler world.S_Event.HeartbeatC2SEvent:Connect(HeartbeatC2SEventHandler) -- OnAwakeEvent(玩家加入前初始化) @@ -100,7 +95,6 @@ function InitEventsAndListeners() -- OnPlayerDisconnectEvent(未接收到玩家心跳等待重连,在服务器第二个阶段) -- OnPlayerReconnectEvent(玩家断线后重连) -- OnPlayerLeaveEvent(玩家彻底离开,退出房间) - --在world下创建CustomEvent的相关事件 world:CreateObject('CustomEvent', 'OnAwakeEvent', world.S_Event) world:CreateObject('CustomEvent', 'OnPlayerJoinEvent', world.S_Event) -- world:CreateObject('CustomEvent', 'OnPlayerRejoinEvent', world.S_Event) @@ -115,7 +109,7 @@ function InitEventsAndListeners() local uid = player.UserId if cache[player] then print('[Heartbeat][Server] OnPlayerLeaveEvent, 玩家主动离开游戏,', player, uid) - NetUtil.Fire_S('OnPlayerLeaveEvent', player, uid) + Ava.Util.Net.Fire_S('OnPlayerLeaveEvent', player, uid) cache[player] = nil end end @@ -124,20 +118,14 @@ end --- Update心跳 function Update() - --遍历每个加入的客户端的cache(上一次客户端发来的心跳时间戳缓存) + --遍历每个加入的客户端的cache(上一次客户端发来的心跳时间戳缓存) for p, v in pairs(cache) do if p and not p:IsNull() then - --逻辑: 记录服务端的时间轴,并传入客户端进行比较获得diff,diff大小即代表客户端延迟的大小 - -- 如果diff大于服务端设置的最大心跳阈值,则代表客户端断开了连接 - --获取从游戏开始到当前时刻所经历的时间,以毫秒为单位 - --sTmpTs:服务端的运行总时间 sTmpTs = Timer.GetTimeMillisecond() cTmpTs = v.cTimestamp PrintHb(string.format('=> S = %s, C = %s, %s', sTmpTs, cTmpTs, p)) - -- 发包时,检查玩家是否掉线 CheckPlayerStates(p, sTmpTs) - -- 向指定的玩家的客户端发送消息:心跳消息 - NetUtil.Fire_C('HeartbeatS2CEvent', p, sTmpTs, cTmpTs) + Ava.Util.Net.Fire_C('HeartbeatS2CEvent', p, sTmpTs, cTmpTs) else --* remove nil key from cache cache[p] = nil @@ -150,73 +138,60 @@ function HeartbeatC2SEventHandler(_player, _cTimestamp, _sTimestamp) if not running then return end - --- 打印心跳日志 + --- 打印心跳日志 PrintHb(string.format('<= S = %s, C = %s, %s', _sTimestamp, _cTimestamp, _player)) - --- 收包时,检查玩家是否加入或重连 + --- 收包时,检查玩家是否加入或重连 CheckPlayerJoin(_player) - -- 更新指定的玩家客户端的时间数据 + -- 更新指定的玩家客户端的时间数据 cache[_player].cTimestamp = _cTimestamp cache[_player].sTimestamp = _sTimestamp end --- 收包时,检查玩家是否加入或重连 function CheckPlayerJoin(_player) - --如果cache的_player位置存在空位,则可以加入玩家 + --如果cache的_player位置存在空位,则可以加入玩家 if not cache[_player] then --* 玩家新加入 OnPlayerJoinEvent print('[Heartbeat][Server] OnPlayerJoinEvent, 新玩家加入,', _player) - -- 向服务器发送消息:新玩家加入消息 - NetUtil.Fire_S('OnPlayerJoinEvent', _player, _player.UserId) + Ava.Util.Net.Fire_S('OnPlayerJoinEvent', _player, _player.UserId) cache[_player] = { - --设置状态为:“在线”状态 state = HeartbeatEnum.CONNECT } - -- 如果cache的_player位置存在,并且玩家的状态是“离线”,则对玩家进行断线重连 elseif cache[_player].state == HeartbeatEnum.DISCONNECT then --* 玩家断线重连 OnPlayerReconnectEvent print('[Heartbeat][Server] OnPlayerReconnectEvent, 玩家断线重连,', _player) - -- 向服务器发送消息:老玩家断线重连消息 - NetUtil.Fire_S('OnPlayerReconnectEvent', _player, _player.UserId) - --设置状态为:“在线”状态 + Ava.Util.Net.Fire_S('OnPlayerReconnectEvent', _player, _player.UserId) cache[_player].state = HeartbeatEnum.CONNECT end end --- 发包时,检查玩家是否掉线 function CheckPlayerStates(_player, _sTimestam) - -- 如果服务端的心跳时间戳缓存不存在,则返回 + -- 如果服务端的心跳时间戳缓存不存在,则返回 if not cache[_player].sTimestamp then return end - ---diff 时间戳插值 = 当前服务端的时间值- 当前玩家客户端保存的服务端时间值 + -- diff 时间戳插值 = 当前服务端的时间值- 当前玩家客户端保存的服务端时间值 diff = _sTimestam - cache[_player].sTimestamp PrintHb(string.format('==========================================> diff = %s, %s', diff * .001, _player)) - --如果diff<心跳阈值1 - if diff < HEARTBEAT_THRESHOLD_1 then + -- 如果 diff < 心跳阈值1 + if diff < HEARTBEAT_THRESHOLD_1 then --* 玩家在线 - --设置玩家客户端发来的心跳时间戳状态是“在线”状态 cache[_player].state = HeartbeatEnum.CONNECT - --如果diff>=心跳阈值1并且玩家客户端发来的心跳时间戳状态是“在线”状态 elseif cache[_player].state == HeartbeatEnum.CONNECT and diff >= HEARTBEAT_THRESHOLD_1 then --* 玩家断线 OnPlayerDisconnectEvent print('[Heartbeat][Server] OnPlayerDisconnectEvent, 玩家离线, 等待断线重连,', _player, _player.UserId) - --- 向服务器发送消息:玩家连接失败消息 - --位置 NetUtilModule - NetUtil.Fire_S('OnPlayerDisconnectEvent', _player, _player.UserId) - --设置玩家客户端发来的心跳时间戳状态是“离线”状态 - cache[_player].state = HeartbeatEnum.DISCONNECT - --如果diff>=心跳阈值2并且玩家客户端发来的心跳时间戳状态是“离线”状态 - elseif cache[_player].state == HeartbeatEnum.DISCONNECT and diff >= HEARTBEAT_THRESHOLD_2 then + Ava.Util.Net.Fire_S('OnPlayerDisconnectEvent', _player, _player.UserId) + cache[_player].state = HeartbeatEnum.DISCONNECT + elseif cache[_player].state == HeartbeatEnum.DISCONNECT and diff >= HEARTBEAT_THRESHOLD_2 then --* 玩家彻底断线,剔除玩家 local player = _player local uid = player.UserId print('[Heartbeat][Server] OnPlayerLeaveEvent, 剔除离线玩家,', player, uid) - -- 向服务器发送消息:玩家离开消息 - NetUtil.Fire_S('OnPlayerLeaveEvent', player, uid) + Ava.Util.Net.Fire_S('OnPlayerLeaveEvent', player, uid) print('[Heartbeat][Server] OnPlayerLeaveEvent, 发送客户端离线事件,', player, uid) - -- 向指定的玩家的客户端发送消息:玩家离开消息 - NetUtil.Fire_C('OnPlayerLeaveEvent', player, uid) - -- 将cache的该玩家数据删除 + Ava.Util.Net.Fire_C('OnPlayerLeaveEvent', player, uid) + -- 将cache的该玩家数据删除 cache[player] = nil end end diff --git a/Smap/Lua/AvaKit/Framework/Server/ServerMain.lua b/Smap/Lua/AvaKit/Framework/Server/ServerMain.lua new file mode 100644 index 0000000..188450b --- /dev/null +++ b/Smap/Lua/AvaKit/Framework/Server/ServerMain.lua @@ -0,0 +1,161 @@ +--- 游戏服务器主逻辑 +--- @module Game Server, Server-side +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang +local Server = {} + +--- 缓存全局变量 +local Ava = Ava +local Heartbeat = Ava.Framework.Server.Heartbeat +local DataSync = Ava.Framework.Server.DataSync + +--- 已经初始化,正在运行 +local initialized, running = false, false + +--- 模块列表: 含有InitDefault(), Init(), Update() +local list = {} +local initDefaultList, initList, updateList = {}, {}, {} + +--! Public +--- 确定Server存在 +Server.Exist = false + +--- 运行服务器 +function Server:Run() + print('[AvaKit][Server] Run()') + InitServer() + invoke(StartUpdate) + Server.Exist = true +end + +--- 停止Update +function Server:Stop() + print('[AvaKit][Server] Stop()') + running = false + Heartbeat.Stop() +end + +--! Private + +--- 初始化 +function InitServer() + if initialized then + return + end + print('[AvaKit][Server] InitServer()') + RequireServerModules() + InitRandomSeed() + InitHeartbeat() + InitDataSync() + InitServerCustomEvents() + GenInitAndUpdateList() + RunInitDefault() + InitOtherModules() + initialized = true +end + +function RequireServerModules() + print('[AvaKit][Server] RequireServerModules()') + _G.S.Events = Ava.Manifest.Server.Events + Ava.Util.Mod.LoadManifest(_G.S, Ava.Manifest.Server, Ava.Manifest.Server.ROOT_PATH, list) +end + +--- 初始化服务器的CustomEvent +function InitServerCustomEvents() + print('[AvaKit][Server] InitServerCustomEvents()') + if world.S_Event == nil then + world:CreateObject('FolderObject', 'S_Event', world) + end + + for _, evt in pairs(S.Events) do + if world.S_Event[evt] == nil then + world:CreateObject('CustomEvent', evt, world.S_Event) + end + end +end + +--- 初始化心跳包 +function InitHeartbeat() + if not Ava.Config.HeartbeatStart then + return + end + assert(Heartbeat, '[AvaKit][Server][Heartbeat] 找不到ServerHeartbeat,请联系endaye') + Heartbeat.Init() +end + +--- 初始化数据同步 +function InitDataSync() + if not Ava.Config.DataSyncStart then + return + end + assert(DataSync, '[AvaKit][Server][DataSync] 找不到ServerDataSync,请联系endaye') + DataSync.Init() +end + +--- 生成需要Init和Update的模块列表 +function GenInitAndUpdateList() + -- TODO: 改成在Ava.Config中配置 + Ava.Util.Mod.GetModuleListWithFunc(list, 'InitDefault', initDefaultList) + Ava.Util.Mod.GetModuleListWithFunc(list, 'Init', initList) + Ava.Util.Mod.GetModuleListWithFunc(list, 'Update', updateList) +end + +--- 执行默认的Init方法 +function RunInitDefault() + for _, m in ipairs(initDefaultList) do + m:InitDefault(m) + end +end + +--- 初始化服务器随机种子 +function InitRandomSeed() + math.randomseed(os.time()) +end + +--- 初始化包含Init()方法的模块 +function InitOtherModules() + for _, m in ipairs(initList) do + m:Init() + end +end + +--- 开始Update +function StartUpdate() + print('[Server] StartUpdate()') + assert(not running, '[AvaKit][Server] StartUpdate() 正在运行') + + running = true + + -- 开启心跳 + if Ava.Config.HeartbeatStart then + invoke(Heartbeat.Start) + end + + -- 开启数据同步 + if Ava.Config.DataSyncStart then + DataSync.Start() + end + + local dt = 0 -- delta time 每帧时间 + local tt = 0 -- total time 游戏总时间 + local now = Timer.GetTimeMillisecond --时间函数缓存 + local prev, curr = now() / 1000, nil -- two timestamps + + while (running and wait()) do + curr = now() / 1000 + dt = curr - prev + tt = tt + dt + prev = curr + UpdateServer(dt, tt) + end +end + +--- Update函数 +--- @param dt delta time 每帧时间 +function UpdateServer(_dt, _tt) + for _, m in ipairs(updateList) do + m:Update(_dt, _tt) + end +end + +return Server diff --git a/Smap/Lua/AvaKit/LuaExt/GlobalExt.lua b/Smap/Lua/AvaKit/LuaExt/GlobalExt.lua new file mode 100644 index 0000000..9380a34 --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/GlobalExt.lua @@ -0,0 +1,53 @@ +--- 提供一组常用函数,以及对 Lua 标准库的扩展 +--- @module Lua global function extension libraries +--- @copyright Lilith Games, Avatar Team + +--- 检查并尝试转换为数值,如果无法转换则返回 0 +--- @param mixed value 要检查的值 +--- @param [integer base] 进制,默认为十进制 +--- @return number +_G.checknumber = function(value, base) + return tonumber(value, base) or 0 +end + +--- 检查是否是有效的number类型 +--- @param number +_G.isValidNumber = function(num) + return num ~= nil and num > 0 +end + +--- 检查并尝试转换为整数,如果无法转换则返回 0 +--- @param mixed value 要检查的值 +--- @return integer +_G.checkint = function(value) + return math.floor(checknumber(value) + 0.5) +end + +--- 检查并尝试转换为布尔值,除了 nil 和 false,其他任何值都会返回 true +--- @param mixed value 要检查的值 +--- @return boolean +_G.checkbool = function(value) + return (value ~= nil and value ~= false) +end + +--- 检查值是否是一个表格,如果不是则返回一个空表格 +--- @param mixed value 要检查的值 +--- @return table +_G.checktable = function(value) + if type(value) ~= 'table' then + value = {} + end + return value +end + +--- 处理对象 +--- @param mixed obj Lua 对象 +--- @param function method 对象方法 +--- @return function +_G.handler = function(obj, method) + return function(...) + return method(obj, ...) + end +end + +return 0 diff --git a/Smap/Lua/AvaKit/LuaExt/MathExt.lua b/Smap/Lua/AvaKit/LuaExt/MathExt.lua new file mode 100644 index 0000000..72458ec --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/MathExt.lua @@ -0,0 +1,41 @@ +--- Lua常用数学库扩展 +--- @module Lua math function extension libraries +--- @copyright Lilith Games, Avatar Team + +--- 两点间距离 +--- @author xuyue +--- @param _vec1 Vector3 +--- @param _vec1 Vector3 +math.Distance = function(_vec1, _vec2) + return math.sqrt((_vec1.x - _vec2.x) ^ 2 + (_vec1.y - _vec2.y) ^ 2 + (_vec1.z - _vec2.z) ^ 2) +end + +--- 四舍五入 +--- @param a number +--- @return a round number +math.round = function(value) + return math.floor(value + 0.5) +end + +--- [0, 1]区间限定函数 +--- @param a number +--- @return a clamped number +math.clamp01 = function(value) + return math.min(1, math.max(0, value)) +end + +--- 高斯随机变量 +--- @author DaiAn +math.GaussRandom = function() + local u = math.random() + local v = math.random() + local z = math.sqrt(-2 * math.log(u)) * math.cos(2 * math.pi * v) + z = (z + 3) / 6 + z = 2 * z - 1 + if (math.abs(z) > 1) then + return math.GaussRandom() + end + return z +end + +return 0 diff --git a/Smap/Lua/AvaKit/LuaExt/Queue.lua b/Smap/Lua/AvaKit/LuaExt/Queue.lua new file mode 100644 index 0000000..cb3695b --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/Queue.lua @@ -0,0 +1,119 @@ +--- Lua数据结构:Queue(队列) +-- @module Lua data structure: queue +-- @copyright Lilith Games, Avatar Team +-- @author Chengzhi + +--- 数据结构 队列 +-- @usage queue example +-- local myQueue = Queue:New() +-- myQueue:Enqueue('a') +-- myQueue:Enqueue('b') +-- myQueue:Enqueue('c') +-- myQueue:PrintElement() +-- print(myQueue:Dequeue()) +-- myQueue:PrintElement() +-- myQueue:Clear() +-- myQueue:PrintElement() +local Queue = {} + +function Queue:New() + local inst = { + _first = -1, + _last = -1, + _size = 0, + _queue = {} + } + setmetatable(inst, {__index = self}) + return inst +end + +function Queue:IsEmpty() + if self._size == 0 then + return true + end + return false +end + +function Queue:Enqueue(inElement) + if self._size == 0 then + self._first = 0 + self._last = 1 + self._size = 1 + self._queue[self._last] = inElement + else + self._last = self._last + 1 + self._queue[self._last] = inElement + self._size = self._size + 1 + end +end + +function Queue:Dequeue() + if self:IsEmpty() then + print('Error: the queue is empty') + return + end + self._size = self._size - 1 + self._first = self._first + 1 + local value = self._queue[self._first] + return value +end + +function Queue:Clear() + self._queue = nil + self._queue = {} + self._size = 0 + self._first = -1 + self._last = -1 +end + +function Queue:Size() + return self._size or 0 +end + +function Queue:PrintElement() + if self._size == 0 then + print('{}') + else + local f = self._first + 1 + local l = self._last + local str + local flag = true + while f ~= l do + if flag == true then + str = '{' .. tostring(self._queue[f]) + f = f + 1 + flag = false + else + str = str .. ',' .. tostring(self._queue[f]) + f = f + 1 + end + end + str = str .. ',' .. tostring(self._queue[l]) .. '}' + print(str) + end +end + +function Queue:GetValue(index) + if self:IsEmpty() or index == nil or index == 0 then + print('Error: Get Value Failure!') + return + end + if index > 0 then + return self._queue[self._first + index] + else + return self._queue[self._last + index + 1] + end +end + +function Queue:GetValues() + if self:IsEmpty() then + return + end + local data = {} + for i = self._first + 1, self._last, 1 do + data[#data + 1] = self._queue[i] + end + return data +end + +return Queue diff --git a/Smap/Lua/AvaKit/LuaExt/Stack.lua b/Smap/Lua/AvaKit/LuaExt/Stack.lua new file mode 100644 index 0000000..feff4d6 --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/Stack.lua @@ -0,0 +1,93 @@ +--- Lua数据结构:Stack(栈) +-- @module Lua data structure: stack +-- @copyright Lilith Games, Avatar Team +-- @author Chengzhi + +--- 数据结构 栈 +-- @usage example +-- local myStack = Stack:New() +-- myStack:Push("a") +-- myStack:Push("b") +-- myStack:Push("c") +-- myStack:PrintElement() +-- print(myStack:Pop()) +-- myStack:PrintElement() +-- myStack:Clear() +-- myStack:PrintElement() +local Stack = {} + +function Stack:New() + local inst = { + _last = 0, + _stack = {} + } + setmetatable(inst, {__index = self}) + + return inst +end + +function Stack:IsEmpty() + if self._last == 0 then + return true + end + return false +end + +function Stack:Push(inElement) + self._last = self._last + 1 + self._stack[self._last] = inElement +end + +function Stack:Pop() + if self:IsEmpty() then + --print("Error: the stack is empty") + return + end + local value = self._stack[self._last] + self._stack[self._last] = nil + self._last = self._last - 1 + return value +end + +function Stack:Exists(element, compairFunc) + if compairFunc == nil then + compairFunc = function(a, b) + return a == b + end + end + for i = self._last, 1, -1 do + if compairFunc(element, self._stack[i]) then + return i + end + end + return -1 +end + +function Stack:RemoveAt(index) + if index < 1 or index > self._last then + return + end + table.remove(self._stack, index) + self._last = self._last - 1 +end + +function Stack:Clear() + self._stack = nil + self._stack = {} + self._last = 0 +end + +function Stack:Size() + return self._last +end + +function Stack:PrintElement() + local str = '{' + for i = self._last, 1, -1 do + str = str .. tostring(self._stack[i]) .. ',' + end + str = str .. '}' + print(str) +end + +return Stack diff --git a/Smap/Lua/AvaKit/LuaExt/StringExt.lua b/Smap/Lua/AvaKit/LuaExt/StringExt.lua new file mode 100644 index 0000000..e3c5f02 --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/StringExt.lua @@ -0,0 +1,143 @@ +--- Lua string 常用方法扩展 +--- @module Lua string function extension libraries +--- @copyright Lilith Games, Avatar Team + +--- 用指定字符或字符串分割输入字符串,返回包含分割结果的数组 +--- @param @string input 输入的字符串 +--- @param @string delimiter 分隔符 +--- @return array +--- @usage example #1 +--- local input = "Hello,World" +--- local res = string.split(input, ",") +--- >> res = {"Hello", "World"} +--- @usage example #2 +--- local input = "Hello-+-World-+-Quick" +--- local res = string.split(input, "-+-") +--- >> res = {"Hello", "World", "Quick"} +string.split = function(input, delimiter) + input = tostring(input) + delimiter = tostring(delimiter) + if (delimiter == '') then + return false + end + local pos, arr = 0, {} + -- for each divider found + for st, sp in function() + return string.find(input, delimiter, pos, true) + end do + table.insert(arr, string.sub(input, pos, st - 1)) + pos = sp + 1 + end + table.insert(arr, string.sub(input, pos)) + return arr +end + +--- 判断字符串是否为空或者长度为零 +--- @param @string 输入的字符串 +string.isnilorempty = function(inputStr) + return inputStr == nil or inputStr == '' +end + +--- 去除输入字符串头部的空白字符,返回结果 +--- @param @string input +--- @return @string +--- @usage example +--- local input = " ABC" +--- print(string.ltrim(input)) +--- >> 输出 ABC,输入字符串前面的两个空格被去掉了 +--- 空白字符包括: +--- 空格 +--- 制表符 \t +--- 换行符 \n +--- 回到行首符 \r +string.ltrim = function(input) + return string.gsub(input, '^[ \t\n\r]+', '') +end + +--- 去除输入字符串尾部的空白字符,返回结果 +--- @param @string input +--- @return @string +--- @usage example +--- local input = "ABC " +--- print(string.rtrim(input)) +--- >> 输出 ABC,输入字符串最后的两个空格被去掉了 +string.rtrim = function(input) + return string.gsub(input, '[ \t\n\r]+$', '') +end + +--- 去掉字符串首尾的空白字符,返回结果 +--- @param @string input +--- @return @string +string.trim = function(input) + input = string.gsub(input, '^[ \t\n\r]+', '') + return string.gsub(input, '[ \t\n\r]+$', '') +end + +--- 将字符串的第一个字符转为大写,返回结果 +--- @param @string input +--- @return @string +--- @usage example +--- local input = "hello" +--- print(string.ucfirst(input)) +--- >> 输出 Hello +string.ucfirst = function(input) + return string.upper(string.sub(input, 1, 1)) .. string.sub(input, 2) +end + +string.firstToUpper = function(str) + return (str:gsub('^%l', string.upper)) +end + +--- 计算 UTF8 字符串的长度,每一个中文算一个字符 +--- @param @string input +--- @return @number cnt +--- @usage example +--- local input = "你好World" +--- print(string.utf8len(input)) +--- >> 输出 7 +string.utf8len = function(input) + local len = string.len(input) + local left = len + local cnt = 0 + local arr = {0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc} + while left ~= 0 do + local tmp = string.byte(input, -left) + local i = #arr + while arr[i] do + if tmp >= arr[i] then + left = left - i + break + end + i = i - 1 + end + cnt = cnt + 1 + end + return cnt +end + +--- 替换字符串内容 +--- @param @string input +--- @param @number start index +--- @param new context +--- @return a new string +string.replace = function(str, index, char) + return table.concat {str:sub(1, index - 1), char, str:sub(index + 1)} +end + +--- 检查字符串是否为指定字符串开头 +--- @param @string target +--- @param @string start +--- @return @boolean +string.startswith = function(str, start) + return str:sub(1, #start) == start +end + +--- 检查字符串是否以指定字符串结尾 +--- @param @string target +--- @param @string start +--- @return @boolean +string.endswith = function(str, ending) + return ending == '' or str:sub(-(#ending)) == ending +end + +return 0 diff --git a/Smap/Lua/AvaKit/LuaExt/TableExt.lua b/Smap/Lua/AvaKit/LuaExt/TableExt.lua new file mode 100644 index 0000000..ce4ef61 --- /dev/null +++ b/Smap/Lua/AvaKit/LuaExt/TableExt.lua @@ -0,0 +1,564 @@ +--- Lua table 常用方法扩展 +--- @module Lua table function extension libraries +--- @copyright Lilith Games, Avatar Team + +--- 计算表格包含的字段数量 +--- Lua table 的 "#" 操作只对依次排序的数值下标数组有效, +--- table.nums() 则计算 table 中所有不为 nil 的值的个数。 +--- @param table +table.nums = function(t) + if t == nil then + return 0 + end + local count = 0 + for _ in pairs(t) do + count = count + 1 + end + return count +end + +--- 返回指定表格中的所有键 +--- @param k-v table +--- @return keys' table +--- @usage example +--- local hashtable = {a = 1, b = 2, c = 3} +--- local keys = table.keys(hashtable) +--- >> keys = {"a", "b", "c"} +table.keys = function(hashtable) + local keys = {} + for k, _ in pairs(hashtable) do + table.insert(keys, k) + end + return keys +end + +--- 返回指定表格中的所有值 +--- @param k-v table +--- @return values' table +--- @usage example +--- local hashtable = {a = 1, b = 2, c = 3} +--- local values = table.values(hashtable) +--- >> values = {1, 2, 3} +table.values = function(hashtable) + local values = {} + local i = 1 + for k, v in pairs(hashtable) do + values[i] = v + i = i + 1 + end + return values +end + +--- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 +--- @param target table +--- @param source table +--- @usage example +--- local dest = {a = 1, b = 2} +--- local src = {c = 3, d = 4} +--- table.merge(dest, src) +--- >> dest = {a = 1, b = 2, c = 3, d = 4} +table.merge = function(dest, src) + for k, v in pairs(src) do + dest[k] = v + end +end + +--- 深度将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值; +--- 如果存在子表,则遍历子表进行复制 +--- @param dest 目标表格 +--- @param src 被合入的表格 +table.deepMerge = function(dest, src) + for k, v in pairs(src) do + if type(v) == 'table' then + if dest[k] == nil then + dest[k] = {} + end + table.deepMerge(dest[k], v) + else + dest[k] = v + end + end +end + +--- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值 +--- @param ... 多个表,第一个是目标表格 +--- @return 返回一个新表 +--- @author Sharif Ma +table.MergeTables = function(...) + local tabs = {...} + if not tabs or #tabs == 0 then + return {} + end + local origin = {} + for k, v in pairs(tabs[1]) do + origin[k] = v + end + for i = 2, #tabs do + if origin then + if tabs[i] then + for _, v in pairs(tabs[i]) do + table.insert(origin, v) + end + end + else + origin = tabs[i] + end + end + return origin +end + +--- 在目标表格的指定位置插入来源表格,如果没有指定位置则连接两个表格 +--- @param target table +--- @param source table +--- @param start index +--- @usage example #1 +--- local dest = {1, 2, 3} +--- local src = {4, 5, 6} +--- table.insertto(dest, src) +--- >> dest = {1, 2, 3, 4, 5, 6} +--- @usage example #2 +--- local dest = {1, 2, 3} +--- local src = {4, 5, 6} +--- table.insertto(dest, src, 5) +--- >> dest = {1, 2, 3, nil, 4, 5, 6} +table.insertto = function(dest, src, begin) + if begin == nil then + begin = #dest + 1 + else + begin = checkint(begin) + if begin <= 0 then + begin = #dest + 1 + end + end + + local len = #src + for i = 0, len - 1 do + dest[i + begin] = src[i + 1] + end +end + +--- 从表格中查找指定值,返回其索引,如果没找到返回 false +--- @param array table +--- @param target value +--- @param start index +--- @return index or false +--- @usage example +--- local array = {"a", "b", "c"} +--- print(table.indexof(array, "b")) +--- >> 2 +table.indexof = function(array, value, begin) + if array ~= nil then + for i = begin or 1, #array do + if array[i] == value then + return i + end + end + end + return 0 +end + +--- 检查表格中是否存在指定值 +--- @param array table +--- @param target value +--- @return @boolean +table.exists = function(array, value) + return table.indexof(array, value) > 0 +end + +--- 清空数组表格 +--- @param array table +table.cleararray = function(array) + if array ~= nil then + local count = #array + while count > 0 do + table.remove(array, count) + count = #array + end + end +end + +--- 清空k-v表格 +--- @param k-v table +table.clearhashtable = function(hashtable) + if hashtable ~= nil then + for k, v in pairs(hashtable) do + hashtable[k] = nil + end + end +end + +--- 清空表格 +--- @param table +--- @see table.clearhashtable +table.cleartable = function(t) + table.clearhashtable(t) +end + +--- 截取Array其中一段,startIndex从1开始 return截取后的新数组 +--- @param table array table +--- @param @number start index +--- @param @number length +--- @return @table array table +--- @usage example +--- local array = {"a", "b", "c", "d"} +--- print(table.subArray(array, 2, 2)) +--- >> {"b", "c"} +table.subArray = function(array, startIndex, length) + if array ~= nil then + local count = table.nums(array) + local tempArray = array + array = {} + if startIndex <= count then + local maxlength = count - startIndex + 1 + length = length > maxlength and maxlength or length + local endIndex = startIndex + length - 1 + for i = startIndex, endIndex do + table.insert(array, tempArray[i]) + end + end + end + return array +end + +--- 截取Array的后半段,startIndex从1开始 return截取后的新数组 +--- @param table array table +--- @param @number start index +--- @return @table array table +table.subArrayByStartIndex = function(array, startIndex) + if array ~= nil then + local count = table.nums(array) + local length = count - startIndex + 1 + return table.subArray(array, startIndex, length) + end + return array +end + +--- 从表格中查找指定值,返回其 key,如果没找到返回 nil +--- @param table hash table +--- @param any value +--- @return key of value +--- @usage +--- local hashtable = {name = "dualface", comp = "chukong"} +--- print(table.keyof(hashtable, "chukong")) +--- >> comp +table.keyof = function(hashtable, value) + for k, v in pairs(hashtable) do + if v == value then + return k + end + end + return nil +end + +--- 从表格中删除指定值,返回删除的值的个数 +--- @usage +--- local array = {"a", "b", "c", "c"} +--- print(table.removebyvalue(array, "c", true)) +--- >> 输出 2 +table.removebyvalue = function(array, value, removeall) + local c, i, max = 0, 1, #array + while i <= max do + if array[i] == value then + table.remove(array, i) + c = c + 1 + i = i - 1 + max = max - 1 + if not removeall then + break + end + end + i = i + 1 + end + return c +end + +--- 数组混淆 +--- @param array 数组table +--- @return 混淆后的同一数组table +--- @author Yuancheng Zhang +table.shuffle = function(_tbl) + local j + for i = #_tbl, 2, -1 do + j = math.random(i) + _tbl[i], _tbl[j] = _tbl[j], _tbl[i] + end + return _tbl +end + +--- 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容 +--- @param table +--- @param function fn 参数指定的函数具有两个参数,并且返回一个值。原型如下: +--- function map_function(value, key) +--- return value +--- end +--- @usage +--- local t = {name = "dualface", comp = "chukong"} +--- table.map(t, function(v, k) +--- --在每一个值前后添加括号 +--- return "[" .. v .. "]" +--- end) +--- 输出修改后的表格内容 +--- for k, v in pairs(t) do +--- print(k, v) +--- end +--- >> 输出 +--- name [dualface] +--- comp [chukong] +table.map = function(t, fn) + for k, v in pairs(t) do + t[k] = fn(v, k) + end +end + +--- 对表格中每一个值执行一次指定的函数,但不改变表格内容 +--- @param table +--- @param function fn 参数指定的函数具有两个参数,没有返回值。原型如下: +--- function map_function(value, key) +--- --- no return here +--- end +--- @usage +--- local t = {name = "dualface", comp = "chukong"} +--- table.walk(t, function(v, k) +--- --- 输出每一个值 +--- print(v) +--- end) +table.walk = function(t, fn) + for k, v in pairs(t) do + fn(v, k) + end +end + +--- 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除 +--- @param table +--- @param function fn 参数指定的函数具有两个参数,并且返回一个 boolean 值。原型如下: +--- !该方法有局限性,执行后会修改原表格t中值 +--- function map_function(value, key) +--- return true or false +--- end +--- @usage +--- local t = {name = "dualface", comp = "chukong"} +--- table.filter(t, function(v, k) +--- return v ~= "dualface" -- 当值等于 dualface 时过滤掉该值 +--- end) +--- 输出修改后的表格内容 +--- for k, v in pairs(t) do +--- print(k, v) +--- end +--- >> 输出 comp chukong +table.filter = function(t, fn) + for k, v in pairs(t) do + if not fn(v, k) then + t[k] = nil + end + end +end + +--- 找到表格中每个符合matchFunc的条目 +--- @param array table +--- @param match function, return T/F +--- @return all elements matched, default is {} +table.findAll = function(array, matchFunc) + local ret, idx = {}, 1 + for i = 1, #array do + if matchFunc(array[i]) then + ret[idx] = array[i] + idx = idx + 1 + end + end + return ret +end + +--- 找到表格中每个符合matchFunc的条目,并执行walkFunc +--- @param array table +--- @param match function, return T/F +--- @param walk function +table.findAllAndWalk = function(array, matchFunc, walkFunc) + for i = 1, #array do + if matchFunc(array[i]) then + walkFunc(array[i]) + end + end +end + +--- 在表格中插入一个新值 +--- @param array table +--- @param new element +table.insert_once = function(T, elem) + for _, v in ipairs(T) do + if v == elem then + return + end + end + table.insert(T, elem) +end + +--- 遍历表格,确保其中的值唯一 +--- @function [parent=#table] unique +--- @param table t 表格 +--- @param boolean bArray t是否是数组,是数组,t中重复的项被移除后,后续的项会前移 +--- @return table#table 包含所有唯一值的新表格 +--- @usage +--- 遍历表格,确保其中的值唯一 +--- local t = {"a", "a", "b", "c"} -- 重复的 a 会被过滤掉 +--- local n = table.unique(t) +--- for k, v in pairs(n) do +--- print(v) +--- end +--- >> 输出 a b c +table.unique = function(t, bArray) + local check = {} + local n = {} + local idx = 1 + for k, v in pairs(t) do + if not check[v] then + if bArray then + n[idx] = v + idx = idx + 1 + else + n[k] = v + end + check[v] = true + end + end + return n +end + +--- table 深度复制 +--- @param table +--- @return a net table with same data +table.deepcopy = function(object) + local lookup_table = {} + local function _copy(object) + if type(object) ~= 'table' then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for key, value in pairs(object) do + new_table[_copy(key)] = _copy(value) + end + return setmetatable(new_table, getmetatable(object)) + end + return _copy(object) +end + +--- table 浅度复制(不处理metatable) +--- @param table +--- @return a net table with same data +function table.shallowcopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[table.shallowcopy(orig_key)] = table.shallowcopy(orig_value) + end + else + copy = orig + end + return copy +end + +--- 获取or创建一个子表 +table.need = function(tb, key) + if type(tb) == 'table' then + local subTb = tb[key] + if subTb == nil then + subTb = {} + tb[key] = subTb + end + return subTb + end +end + +--- 打印table中的所有内容 +--- @param data table +--- @param @boolean showMetatable 是否显示元表 +table.dump = function(data, showMetatable) + local result, tab = {}, ' ' + local function _dump(data, showMetatable, lastCount) + if type(data) ~= 'table' then + if type(data) == 'string' then + table.insert(result, '"') + table.insert(result, data) + table.insert(result, '"') + else + table.insert(result, tostring(data)) + end + else + --Format + local count = lastCount or 0 + count = count + 1 + table.insert(result, '{\n') + --Metatable + if showMetatable then + for i = 1, count do + table.insert(result, tab) + end + local mt = getmetatable(data) + table.insert(result, '"__metatable" = ') + _dump(mt, showMetatable, count) + table.insert(result, ',\n') + end + --Key + for key, value in pairs(data) do + for i = 1, count do + table.insert(result, tab) + end + if type(key) == 'string' then + table.insert(result, '"') + table.insert(result, key) + table.insert(result, '" = ') + elseif type(key) == 'number' then + table.insert(result, '[') + table.insert(result, key) + table.insert(result, '] = ') + else + table.insert(result, tostring(key)) + end + _dump(value, showMetatable, count) + table.insert(result, ',\n') + end + --Format + for i = 1, lastCount or 0 do + table.insert(result, tab) + end + table.insert(result, '}') + end + --Format + if not lastCount then + table.insert(result, '\n') + end + end + _dump(data, showMetatable, 0) + + --- print('dump: \n' .. table.concat(result)) + return 'dump: \n' .. table.concat(result) +end + +--- 两个表是否相同(元素数量,值相同) +--- @param _tableA table +--- @param _tableB table +--- @author xuyue +table.equal = function(_tableA, _tableB) + for k, v in pairs(_tableA) do + if _tableB[k] == nil then --元素数量不匹配 + return false + end + + if type(v) ~= 'table' then + if v ~= _tableB[k] then + return false + end + else + if not table.equal(v, _tableB[k]) then + return false + end + end + end + return true +end + +return 0 diff --git a/Code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Util/Event.lua similarity index 75% rename from Code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Util/Event.lua index 7fd554a..fb12e6c 100644 --- a/Code/['World']['Global']['Utility']['EventUtilModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Util/Event.lua @@ -1,34 +1,34 @@ --- 事件绑定工具 --- @module Event Connects Handler --- @copyright Lilith Games, Avatar Team --- @author Yuancheng Zhang, Yen Yuan +--- @module Event Connects Handler +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang, Yen Yuan local EventUtil = {} --- 检查是否为Json化的字符串 --- @param _str @string 输入的字符串 --- @return @boolean true: json table string +--- @param _str @string 输入的字符串 +--- @return @boolean true: json table string local function IsJsonTable(_str) return type(_str) == 'string' and string.endswith(_str, 'JSON') and string.startswith(_str, 'JSON') end --- 处理Handler的传入参数 ---@param variable args ---@return variable args +--- @param variable args +--- @return variable args local function ArgsAux(...) local _s = {...} for k, v in pairs(_s) do if IsJsonTable(v) then local json = string.sub(v, 5, -5) - _s[k] = LuaJsonUtil:decode(json) + _s[k] = JSON:decode(json) end end return table.unpack(_s) end --- 遍历所有的events,找到module中对应名称的handler,建立Connect --- @param _eventFolder 事件所在的节点folder --- @param _module 模块 --- @param _this module的self指针,用于闭包 +--- @param _eventFolder 事件所在的节点folder +--- @param _module 模块 +--- @param _this module的self指针,用于闭包 function EventUtil.LinkConnects(_eventFolder, _module, _this) assert( _eventFolder and _module and _this, diff --git a/Code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Util/LuaJson.lua similarity index 100% rename from Code/['World']['Global']['Utility']['LuaJsonUtilModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Util/LuaJson.lua diff --git a/Smap/Lua/AvaKit/Util/Module.lua b/Smap/Lua/AvaKit/Util/Module.lua new file mode 100644 index 0000000..7ba400c --- /dev/null +++ b/Smap/Lua/AvaKit/Util/Module.lua @@ -0,0 +1,93 @@ +--- 模块加载工具 +--- @module Module utilities +--- @copyright Lilith Games, Avatar Team +--- @author Yuancheng Zhang + +local ModuleUtil = {} + +-- Constants +local LOOP_MAX = 15 -- Manifest最多层级深度 + +--- 加载Manifest +--- @param _root 模块目录的节点 +--- @param _manifest Mainifest中的节点 +--- @param _res 资源路径 +--- @param _list 模块清单 +function ModuleUtil.LoadManifest(_root, _manifest, _res, _list) + assert(_root, '[AvaKit][ModuleUtil] _root is WRONG') + assert(_manifest and _manifest.Modules, '[AvaKit][ModuleUtil] _manifest is WRONG') + assert(_res, '[AvaKit][ModuleUtil] _res does NOT exist') + + -- 读取Manifest,并生成一个require array + local node, subNode, mod = {rt = _root, man = _manifest, res = _res} + local arr = {node} + local deep, done = LOOP_MAX, false + while not done and deep > 0 do + deep = deep - 1 + done = true + for i = 1, #arr do + node = arr[i] + if node.man ~= nil and node.man.Modules ~= nil then + done = false + table.remove(arr, i) + local cnt = 0 + for j = 1, #node.man.Modules do + mod = node.man.Modules[j] -- Manifest.Modules.XXXX + if type(mod) == 'table' and mod.Modules ~= nil then + -- print(node, node.rt, mod.Name) + node.rt[mod.Name] = node.rt[mod.Name] or {} + subNode = { + rt = node.rt[mod.Name], + man = mod, + res = string.format('%s%s/', node.res, mod.Name) + } + cnt = cnt + 1 + table.insert(arr, i + cnt - 1, subNode) + elseif type(mod) == 'string' then + subNode = { + rt = node.rt, + name = mod, + res = node.res .. mod + } + cnt = cnt + 1 + table.insert(arr, i + cnt - 1, subNode) + end + end + end + end + end + + -- Require Module脚本 + for k, v in ipairs(arr) do + --print(string.format('[AvaKit][Load][%02d] %s, %s', k, v.name, v.res)) + v.rt[v.name] = require(v.res) + if _list then + table.insert(_list, v.rt[v.name]) + end + -- FIXME: 暂时为全局变量,向下兼容 + _G[v.name] = v.rt[v.name] + end +end + +--- 将有包含特定方法的模块筛选出来,并放在一个table中 +--- @param _modules module列表 +--- @param @string _fn 方法名 function_name +--- @param @table _list 存放的table +function ModuleUtil.GetModuleListWithFunc(_modules, _fn, _list) + assert(_modules, '[ModuleUtil] Node does NOT exist!') + assert(not string.isnilorempty(_fn), '[ModuleUtil] Function name is nil or empty!') + assert(_list, '[ModuleUtil] List is NOT initialized!') + for _, m in pairs(_modules) do + if m[_fn] and type(m[_fn]) == 'function' then + table.insert(_list, m) + end + end +end + +--- 新建一个模块实例(ServerBase or ClientBase) +function ModuleUtil.New(_name, _baseClass) + local t = class(_name, _baseClass) + return t, t:GetSelf() +end + +return ModuleUtil diff --git a/Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Util/Net.lua similarity index 51% rename from Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Util/Net.lua index 7e4ccdd..d7bab80 100644 --- a/Code/['World']['Global']['Utility']['NetUtilModule'].ModuleScript.lua +++ b/Smap/Lua/AvaKit/Util/Net.lua @@ -1,11 +1,8 @@ --- 网路工具/事件工具 --- @module Network utilities --- @copyright Lilith Games, Avatar Team --- @author Sharif Ma, Yuancheng Zhang, Yen Yuan -local NetUtil = {} - --- 格式化事件参数, Table=>JSON -local FormatArgs +--- @module Network utilities +--- @copyright Lilith Games, Avatar Team +--- @author Sharif Ma, Yuancheng Zhang, Yen Yuan +local Net = {} --! 事件参数校验, true:开启校验 local valid, ValidateArgs = true @@ -22,9 +19,9 @@ local FireEnum = { --! 外部接口 --- 向服务器发送消息 --- @param @string _eventName 事件的名字(严格对应) --- @param ... 事件参数 -function NetUtil.Fire_S(_eventName, ...) +--- @param @string _eventName 事件的名字(严格对应) +--- @param ... 事件参数 +function Net.Fire_S(_eventName, ...) ValidateArgs(FireEnum.SERVER, _eventName) local args = {...} world.S_Event[_eventName]:Fire(table.unpack(args)) @@ -32,10 +29,10 @@ function NetUtil.Fire_S(_eventName, ...) end --- 向指定的玩家发送消息 --- @param @string _eventName 事件的名字 --- @param _player 玩家对象 --- @param ... 事件参数 -function NetUtil.Fire_C(_eventName, _player, ...) +--- @param @string _eventName 事件的名字 +--- @param _player 玩家对象 +--- @param ... 事件参数 +function Net.Fire_C(_eventName, _player, ...) if _player == nil then return end @@ -46,28 +43,15 @@ function NetUtil.Fire_C(_eventName, _player, ...) end --- 客户端广播 --- @param @string _eventName 事件的名字(严格对应) --- @param ... 事件参数 -function NetUtil.Broadcast(_eventName, ...) +--- @param @string _eventName 事件的名字(严格对应) +--- @param ... 事件参数 +function Net.Broadcast(_eventName, ...) ValidateArgs(FireEnum.BROADCAST, _eventName, ...) local args = {...} world.Players:BroadcastEvent(_eventName, table.unpack(args)) PrintEventLog(FireEnum.BROADCAST, _eventName, nil, args) end ---! 私有函数 - ---- 格式化事件参数 -FormatArgs = function(...) - local args = {...} - for k, v in pairs(args) do - if type(v) == 'table' then - args[k] = string.format('JSON%sJSON', LuaJsonUtil:encode(v)) - end - end - return args -end - --! 辅助功能 --- 事件参数校验 @@ -76,40 +60,38 @@ ValidateArgs = function(_fireEnum, _eventName, _player) if _fireEnum == FireEnum.SERVER then --! Fire_S 检查参数 - assert(not string.isnilorempty(_eventName), '[NetUtil][Fire_S] 事件名为空') - assert(world.S_Event[_eventName], string.format('[NetUtil][Fire_S] 服务器不存在事件: %s', _eventName)) + assert(not string.isnilorempty(_eventName), '[Net][Fire_S] 事件名为空') + assert(world.S_Event[_eventName], string.format('[Net][Fire_S] 服务器不存在事件: %s', _eventName)) elseif _fireEnum == FireEnum.CLIENT then --! Fire_C 检查参数 - assert(not string.isnilorempty(_eventName), '[NetUtil] 事件名为空') + assert(not string.isnilorempty(_eventName), '[Net] 事件名为空') assert( _player and _player.ClassName == 'PlayerInstance', - string.format('[NetUtil][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) + string.format('[Net][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) ) - assert(_player.C_Event, '[NetUtil][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) + assert(_player.C_Event, '[Net][Fire_C]第2个参数需要是玩家对象, 错误事件: %s', _eventName) assert( _player.C_Event[_eventName], - string.format('[NetUtil][Fire_C] 客户端玩家不存在事件: %s, 玩家: %s', _player.Name, _eventName) + string.format('[Net][Fire_C] 客户端玩家不存在事件: %s, 玩家: %s', _player.Name, _eventName) ) elseif _fireEnum == FireEnum.BROADCAST then --! Broadcase 检查参数 - assert(not string.isnilorempty(_eventName), '[NetUtil][Broadcast] 事件名为空') + assert(not string.isnilorempty(_eventName), '[Net][Broadcast] 事件名为空') end end or function() end --- 打印事件日志 -PrintEventLog = - showLog and - function(_fireEnum, _eventName, _player, _args) +PrintEventLog = showLog and function(_fireEnum, _eventName, _player, _args) if _fireEnum == FireEnum.SERVER then --* Fire_S 参数打印 - print(string.format('[NetUtil][发出服务器事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) + print(string.format('[Net][发出服务器事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) elseif _fireEnum == FireEnum.CLIENT then --* Fire_C 参数打印 print( string.format( - '[NetUtil][发出客户端事件] %s, 玩家=%s, 参数 = %s, %s', + '[Net][发出客户端事件] %s, 玩家=%s, 参数 = %s, %s', _eventName, _player.Name, #_args, @@ -118,10 +100,9 @@ PrintEventLog = ) elseif _fireEnum == FireEnum.BROADCAST then --* Broadcast 参数打印 - print(string.format('[NetUtil][发出客户端广播事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) + print(string.format('[Net][发出客户端广播事件] %s, 参数 = %s, %s', _eventName, #_args, table.dump(_args))) end - end or - function() + end or function() end -return NetUtil +return Net diff --git a/Code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua b/Smap/Lua/AvaKit/Util/Time.lua similarity index 100% rename from Code/['World']['Global']['Utility']['TimeUtilModule'].ModuleScript.lua rename to Smap/Lua/AvaKit/Util/Time.lua diff --git a/Smap/Lua/Client/Fsm/Base/ControllerBase.lua b/Smap/Lua/Client/Fsm/Base/ControllerBase.lua new file mode 100644 index 0000000..089113d --- /dev/null +++ b/Smap/Lua/Client/Fsm/Base/ControllerBase.lua @@ -0,0 +1,71 @@ +--- 状态机控制器基类 +--- @module ControllerBase +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local ControllerBase = class('ControllerBase') + +function ControllerBase:initialize(_stateMachineNode, _folder) + self.machine = _stateMachineNode + self.states = {} + self.statesInMachine = {} + self.lastState = nil + self.curState = nil + self:ConnectStates(self, _folder) +end + +--绑定所有状态 +function ControllerBase:ConnectStates(_controller, _folder) + for _, module in pairs(_folder) do + local tempStateClass = module + local stateModule = tempStateClass:new(_controller, module.name) + local stateInMachine = self.machine:CreateState(module.name) + self.statesInMachine[module.name] = stateInMachine + self.states[module.name] = stateModule + stateInMachine.OnEnter:Connect( + function() + stateModule:OnEnter() + end + ) + stateInMachine.OnUpdate:Connect( + function() + stateModule:OnUpdate() + end + ) + stateInMachine.OnExit:Connect( + function() + stateModule:OnLeave() + end + ) + end + for _, state in pairs(self.states) do + state:InitData() + end +end + +--初始化默认状态 +function ControllerBase:SetDefState(_stateName) + self.machine:SetDefaultState(self.statesInMachine[_stateName]) + self.curState = self.states[_stateName] + self.machine:Play() +end + +--切换状态 +function ControllerBase:Switch(_state) + if _state and self.curState ~= _state then + self.lastState = self.curState + self.curState = _state + print(self.curState.stateName) + self.machine:GotoState(self.statesInMachine[_state.stateName]) + end +end + +function ControllerBase:Update(dt) + if self.curState then + self:Switch(self.curState:TransUpdate(dt)) + for k, v in pairs(self.states) do + self:Switch(v:AnyStateCheck()) + end + end +end + +return ControllerBase diff --git a/Smap/Lua/Client/Fsm/Base/StateBase.lua b/Smap/Lua/Client/Fsm/Base/StateBase.lua new file mode 100644 index 0000000..853b044 --- /dev/null +++ b/Smap/Lua/Client/Fsm/Base/StateBase.lua @@ -0,0 +1,81 @@ +--- 状态机状态基类 +--- @module StateBase +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local StateBase = class('StateBase') + +function StateBase:initialize(_controller, _stateName) + --print(_stateName, 'initialize()') + self.stateName = _stateName + self.controller = _controller + + self.transitions = {} + self.anyState = {} +end + +function StateBase:InitData() +end + +--增加一个transition +function StateBase:AddTransition(_transitonName, _nextState, _dur, ...) + local transiton = TransitonBase:new(self.stateName .. '_' .. _transitonName, _nextState, _dur) + transiton:InitConditions(...) + table.insert(self.transitions, transiton) +end + +--增加一个anyState +function StateBase:AddAnyState(_transitonName, _dur, ...) + local transiton = TransitonBase:new(self.stateName .. '_' .. _transitonName, self, _dur) + transiton:InitConditions(...) + table.insert(self.anyState, transiton) +end + +--重置transition和\anyState +function StateBase:Reset() + for _, trans in pairs(self.transitions) do + trans:Reset() + end + for _, trans in pairs(self.anyState) do + trans:Reset() + end +end + +--变化运行 +function StateBase:TransUpdate(dt) + for _, trans in pairs(self.transitions) do + local nextState = trans:GetTransState(dt) + if nextState then + return nextState + end + end + return nil +end + +--anyState监测 +function StateBase:AnyStateCheck() + for _, trans in pairs(self.anyState) do + local nextState = trans:GetTransState(0) + if nextState then + return nextState + end + end + return nil +end + +--进入状态 +function StateBase:OnEnter() + --print('进入' .. self.stateName) + self:Reset() +end + +--更新状态 +function StateBase:OnUpdate() + --print('更新' .. self.stateName) +end + +--离开状态 +function StateBase:OnLeave() + --print('离开' .. self.stateName) +end + +return StateBase diff --git a/Smap/Lua/Client/Fsm/Base/TransitonBase.lua b/Smap/Lua/Client/Fsm/Base/TransitonBase.lua new file mode 100644 index 0000000..2a5179f --- /dev/null +++ b/Smap/Lua/Client/Fsm/Base/TransitonBase.lua @@ -0,0 +1,47 @@ +--- 状态机过渡基类 +--- @module TransitonBase +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local TransitonBase = class('TransitonBase') + +function TransitonBase:initialize(_transitonName, _nextState, _dur) + --print(_transitonName, 'initialize()') + self.transitonName = _transitonName + self.nextState = _nextState + self.dur = _dur or -1 + self.curTime = 0 + self.conditions = {} +end + +--初始化条件 +function TransitonBase:InitConditions(...) + for k, v in pairs({...}) do + table.insert(self.conditions, v) + end +end + +--变化更新 +function TransitonBase:GetTransState(dt) + if self.dur ~= -1 then + if self.curTime < self.dur then + self.curTime = self.curTime + dt + else + self.curTime = 0 + return self.nextState + end + end + for k, v in pairs(self.conditions) do + if v() then + self.curTime = 0 + return self.nextState + end + end + return nil +end + +--重置 +function TransitonBase:Reset() + self.curTime = 0 +end + +return TransitonBase diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActController.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActController.lua new file mode 100644 index 0000000..71ff15e --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActController.lua @@ -0,0 +1,91 @@ +--- 玩家动作状态机控制器 +--- @module PlayerActController +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local PlayerActController = class('PlayerActController', ControllerBase) + +function PlayerActController:initialize(_stateMachineNode, _folder) + print('PlayerActController:initialize()') + ControllerBase.initialize(self, _stateMachineNode, _folder) + self.triggers = {} + for k, v in pairs(self.states) do + self.triggers[k] = false + end + self.isCrouch = false + self.jumpCount = localPlayer.JumpMaxCount + self.seatObj = nil + self.stopInfo = { + footIndex = 2, + footDis = 0, + speed = 0 + } + self.actInfo = { + anim = {}, + dur = {}, + speed = 1, + layer = 0, + transIn = 0, + transOut = 0, + isInterrupt = true, + isLoop = false, + speedScale = 1 + } +end + +function PlayerActController:CallTrigger(_stateName) + if self.triggers[_stateName] == false then + self.triggers[_stateName] = true + end +end + +function PlayerActController:ResetTrigger() + for k, v in pairs(self.states) do + self.triggers[k] = false + end +end + +---开关下蹲 +function PlayerActController:SwitchCrouch() + if self.isCrouch then + self.isCrouch = false + else + self.isCrouch = true + end +end + +---获取停步时哪只脚在前以及双脚间距 +function PlayerActController:GetStopInfo() + local lToe = localPlayer.Avatar.Bone_L_Toe0 + local rToe = localPlayer.Avatar.Bone_R_Toe0 + local toeDir = Vector2(lToe.Position.x - rToe.Position.x, lToe.Position.z - rToe.Position.z) + local fDir = Vector2(localPlayer.Avatar.Forward.x, localPlayer.Avatar.Forward.z) + self.stopInfo.footIndex = (Vector2.Angle(toeDir, fDir) < 90) and 2 or 1 + self.stopInfo.footDis = Vector2(lToe.Position.x - rToe.Position.x, lToe.Position.z - rToe.Position.z).Magnitude + return self.stopInfo.footIndex == 2, self.stopInfo.footDis +end + +---切换状态 +function PlayerActController:Switch(_state) + if _state and self.curState ~= _state then + self.lastState = self.curState + self.curState = _state + self.machine:GotoState(self.statesInMachine[_state.stateName]) + self:ResetTrigger() + end +end + +---获取动作信息 +function PlayerActController:GetActInfo(_info) + for k, v in pairs(_info) do + self.actInfo[k] = v + end +end + +function PlayerActController:Update(dt) + ControllerBase.Update(self, dt) + if self.stopInfo and localPlayer.Velocity.Magnitude > 0 then + self.stopInfo.speed = localPlayer.Velocity.Magnitude + end +end + +return PlayerActController diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActState.lua new file mode 100644 index 0000000..ae3d9df --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/PlayerActState.lua @@ -0,0 +1,231 @@ +--- 玩家动作状态 +--- @class PlayerActState +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local PlayerActState = class('PlayerActState', StateBase) + +--水体 +local waterCol = world.StaticSpace.Water +local waterData = {} + +function PlayerActState:initialize(_controller, _stateName) + -- print('StateBase:initialize()') + StateBase.initialize(self, _controller, _stateName) + --- 水体数据 + waterData = { + --- 最小值 + rangeMin = waterCol.Position - Vector3(waterCol.Size.x / 2, waterCol.Size.y / 2, waterCol.Size.z / 2), + --- 最大值 + rangeMax = waterCol.Position + Vector3(waterCol.Size.x / 2, waterCol.Size.y / 2, waterCol.Size.z / 2) + } +end + +--- 移动,每帧调用 +--- @param _isSprint boolean 是否有冲刺输入 +function PlayerActState:Move(_isSprint) + _isSprint = _isSprint or false + local dir = C.Mgr.PlayerCtrl.finalDir + dir.y = 0 + if _isSprint then + if C.Mgr.PlayerCtrl.isSprint then + localPlayer:AddMovementInput(dir, 1) + else + localPlayer:AddMovementInput(dir, 0.5) + end + else + localPlayer:AddMovementInput(dir, 0.5) + end +end + +--- 游泳,每帧调用 +function PlayerActState:Swim() + --- 移动监测以及潜水设置 + --* 此处取消潜水 + --local lvY = self:MoveMonitor() and math.clamp((PlayerCam.playerGameCam.Forward.y + 0.2), -1, 1) or 0 + local lvY = self:MoveMonitor() and math.clamp((PlayerCam.playerGameCam.Forward.y + 0.2), 0, 0) or 0 + + if self:IsWaterSuface(1.1) and lvY > 0 then + lvY = -3 * localPlayer.Velocity.y + elseif localPlayer.Position.y > waterData.rangeMax.y - 0.5 and lvY >= 0 then + lvY = -3 * localPlayer.Velocity.y + end + if self:FloorMonitor(3) and lvY < 0 then + lvY = 0 + end + + --* 自动上浮至水平面 + if not self:IsWaterSuface(1.1) then + lvY = 1 + end + local dir = Vector3(C.Mgr.PlayerCtrl.finalDir.x, lvY, C.Mgr.PlayerCtrl.finalDir.z) + --print(dir, localPlayer.Velocity.y) + --[[ + if lvY < 0 then + print('下沉', lvY) + end + --]] + localPlayer:AddMovementInput(dir, 1) +end + +--- 保持在水面 +function PlayerActState:HoldOnWater() + if localPlayer.Position.y <= -3.9 then + return true + else + return false + end +end + +--- 飞行,每帧调用 +function PlayerActState:Fly() + local lvY = self:MoveMonitor() and math.clamp((PlayerCam.playerGameCam.Forward.y + 0.2), -1, 1) or 0 + local dir = Vector3(C.Mgr.PlayerCtrl.finalDir.Normalized.x, lvY, C.Mgr.PlayerCtrl.finalDir.Normalized.z) + if C.Mgr.PlayerCtrl.isSprint then + localPlayer:AddMovementInput(dir, 1) + else + localPlayer:AddMovementInput(dir, 0.5) + end +end + +--- 上下沉浮,每帧调用 +function PlayerActState:UpAndDown() + local lvY = C.Mgr.PlayerCtrl.upright + if self:IsWaterSuface(1.1) and localPlayer.Position.y > waterData.rangeMax.y - 2 and lvY > 0 then + lvY = 0 + end + --lvY = lvY + 1 + localPlayer:AddMovementInput(Vector3(0, lvY, 0)) +end + +--- 监听移动 +--- @return boolean 是否移动 +function PlayerActState:MoveMonitor() + local dir = C.Mgr.PlayerCtrl.finalDir + dir.y = 0 + if dir.Magnitude > 0 then + return true + else + return false + end +end + +--- 地面监听 +--- @param _dis boolean 距离地面的最大距离 +--- @return boolean 是否落地 +function PlayerActState:FloorMonitor(_dis) + local startPos = localPlayer.Position + local endPos = localPlayer.Position + Vector3.Down * (_dis or 0.03) + local hitResult = Physics:RaycastAll(startPos, endPos, false) + for i, v in pairs(hitResult.HitObjectAll) do + if v.Block and v ~= localPlayer then + return true + end + end + return false +end + +--- 涉水监听 +--- @param _height float avatar胸口高度 +--- @return boolean 是否处于涉水状态 +function PlayerActState:WadeMonitor(_height) + _height = _height or localPlayer.CharacterHeight - 0.7 + if self:SwimMonitor() and localPlayer.Position.y + _height > waterData.rangeMax.y and self:FloorMonitor(1) then + --print(localPlayer.Position.y + _height, waterData.rangeMax.y) + return true + else + return false + end +end + +--- 监听游泳 +--- @return boolean 是否满足游泳条件 +function PlayerActState:SwimMonitor() + if + localPlayer.Position.x > waterData.rangeMin.x and localPlayer.Position.x < waterData.rangeMax.x and + localPlayer.Position.y > waterData.rangeMin.y and + localPlayer.Position.y < waterData.rangeMax.y and + localPlayer.Position.z > waterData.rangeMin.z and + localPlayer.Position.z < waterData.rangeMax.z + then + if self:FloorMonitor(0.05) and localPlayer.Position.y > waterData.rangeMax.y - 0.2 then + return false + end + return true + else + return false + end +end + +---监听速度 更新speedY speedXZ speedX +function PlayerActState:SpeedMonitor(_maxSpeed) + local velocity = localPlayer.Velocity + localPlayer.Avatar:SetParamValue('speedY', math.clamp((velocity.y / 10), -1, 1)) + velocity.y = 0 + localPlayer.Avatar:SetParamValue( + 'speedXZ', + math.clamp((velocity.Magnitude / (_maxSpeed or localPlayer.MaxWalkSpeed)), 0, 1) + ) + --print(math.clamp((velocity.Magnitude / (_maxSpeed or 9)), 0, 1)) + velocity = math.cos(math.rad(Vector3.Angle(velocity, localPlayer.Left))) * velocity.Magnitude + localPlayer.Avatar:SetParamValue('speedX', math.clamp((velocity / (_maxSpeed or localPlayer.MaxWalkSpeed)), -1, 1)) +end + +--- 监听下落状态 +function PlayerActState:FallMonitor() + if not self:FloorMonitor(0.5) and localPlayer.Velocity.y < 0.5 and not localPlayer.IsOnGround then + self.controller:CallTrigger('JumpHighestState') + end +end + +---是否在水面 +function PlayerActState:IsWaterSuface(_dis) + if localPlayer.Position.y > waterData.rangeMax.y - (_dis or 0.25) then + return true + else + return false + end +end + +---镜头更新 +function PlayerActState:CamUpdate() + local maxFov = 0 + local changeSpeed = localPlayer.Velocity.Magnitude + if changeSpeed > 20 then + maxFov = 90 + elseif changeSpeed > 10 then + maxFov = 75 + elseif changeSpeed > 5 then + maxFov = 70 + elseif changeSpeed > 1 then + maxFov = 65 + else + maxFov = 60 + changeSpeed = -50 + end + changeSpeed = changeSpeed / 100 + --PlayerCam:CameraFOVZoom(changeSpeed, maxFov) +end + +--- 跳跃时的空中控制 +function PlayerActState:JumpAirControlUpdate() + local v = localPlayer.Velocity + v.y = 0 + --- 速度2维方向 + local vDir = v.Normalized + --- 输入2维方向 + local KDir = C.Mgr.PlayerCtrl.finalDir.Normalized + --print(Vector3.Angle(vDir, KDir)) + if not (Vector3.Angle(vDir, KDir) < 0.1) then + --print('转向--------------------------------------------') + localPlayer:AddForce(C.Mgr.PlayerCtrl.finalDir * 1200) + end + --print(v.Normalized, C.Mgr.PlayerCtrl.finalDir.Normalized, localPlayer.Velocity.Magnitude) +end + +function PlayerActState:OnUpdate() + StateBase.OnUpdate(self) + self:CamUpdate() + --print(self:WadeMonitor(1)) +end + +return PlayerActState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActBeginState.lua new file mode 100644 index 0000000..8fac15f --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActBeginState.lua @@ -0,0 +1,35 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ActBeginState = class('ActBeginState', PlayerActState) + +function ActBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) +end +function ActBeginState:InitData() + self:AddAnyState( + 'ToActBeginState', + -1, + function() + return self.controller.triggers['ActBeginState'] + end + ) +end + +function ActBeginState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:CreateSingleClipNode(self.controller.actInfo.anim[1], 1, self.stateName) + PlayerAnimMgr:Play(self.stateName, self.controller.actInfo.layer, 1, 0.2, 0.2, true, false, 1) + self:AddTransition('ToActState', self.controller.states['ActState'], self.controller.actInfo.dur[1]) +end + +function ActBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ActBeginState:OnLeave() + PlayerActState.OnLeave(self) + self.transitions = {} +end + +return ActBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActEndState.lua new file mode 100644 index 0000000..b0b8d4b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActEndState.lua @@ -0,0 +1,29 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ActEndState = class('ActEndState', PlayerActState) + +function ActEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) +end +function ActEndState:InitData() +end + +function ActEndState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:CreateSingleClipNode(self.controller.actInfo.anim[3], 1, self.stateName) + PlayerAnimMgr:Play(self.stateName, self.controller.actInfo.layer, 1, 0.2, 0.2, true, false, 1) + self:AddTransition('ToIdleState', self.controller.states['IdleState'], self.controller.actInfo.dur[3]) +end + +function ActEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ActEndState:OnLeave() + PlayerActState.OnLeave(self) + self.transitions = {} + EmoActionMgr:ActCallBack() +end + +return ActEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActState.lua new file mode 100644 index 0000000..0940e06 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ActState.lua @@ -0,0 +1,45 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ActState = class('ActState', PlayerActState) + +function ActState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_loop', 1, _stateName) +end +function ActState:InitData() +end + +function ActState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:CreateSingleClipNode(self.controller.actInfo.anim[2], self.controller.actInfo.speed, self.stateName) + PlayerAnimMgr:Play( + self.stateName, + self.controller.actInfo.layer, + 1, + self.controller.actInfo.transIn, + self.controller.actInfo.transOut, + self.controller.actInfo.isInterrupt, + self.controller.actInfo.isLoop, + self.controller.actInfo.speedScale + ) + self:AddTransition( + 'ToActEndState', + self.controller.states['ActEndState'], + self.controller.actInfo.dur[2], + function() + return self:MoveMonitor() or self.controller.triggers['JumpBeginState'] + end + ) +end + +function ActState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ActState:OnLeave() + PlayerActState.OnLeave(self) + self.transitions = {} +end + +return ActState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch1State.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch1State.lua new file mode 100644 index 0000000..d26e047 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch1State.lua @@ -0,0 +1,40 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---出拳交互动画 +local AttackPunch1State = class('AttackPunch1State', PlayerActState) + +function AttackPunch1State:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('AttackPunch1', 1, _stateName .. 1) +end + +function AttackPunch1State:InitData() + --self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) + self:AddTransition('ToAttackPunch2State', self.controller.states['AttackPunch2State'], 2.4) + self:AddTransition( + 'ToPunchEndState', + self.controller.states['PunchEndState'], + -1, + function() + return self.controller.triggers['PunchEndState'] + end + ) +end + +function AttackPunch1State:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.3, 0.3, true, true, 1) +end + +function AttackPunch1State:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function AttackPunch1State:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return AttackPunch1State diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch2State.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch2State.lua new file mode 100644 index 0000000..7f366c8 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/AttackPunch2State.lua @@ -0,0 +1,40 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---出拳交互动画 +local AttackPunch2State = class('AttackPunch2State', PlayerActState) + +function AttackPunch2State:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('AttackPunch2', 1, _stateName .. 1) +end + +function AttackPunch2State:InitData() + --self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) + self:AddTransition('ToAttackPunch1State', self.controller.states['AttackPunch1State'], 2.2) + self:AddTransition( + 'ToPunchEndState', + self.controller.states['PunchEndState'], + -1, + function() + return self.controller.triggers['PunchEndState'] + end + ) +end + +function AttackPunch2State:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.3, 0.3, true, true, 1) +end + +function AttackPunch2State:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function AttackPunch2State:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return AttackPunch2State diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchBeginState.lua new file mode 100644 index 0000000..a94a4b3 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchBeginState.lua @@ -0,0 +1,30 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local CrouchBeginState = class('CrouchBeginState', PlayerActState) + +function CrouchBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_standtocrouch_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_standtocrouch_01', 1, _stateName, 2) +end +function CrouchBeginState:InitData() + self:AddTransition('ToCrouchIdleState', self.controller.states['CrouchIdleState'], 0.2) +end + +function CrouchBeginState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer:StopMovementImmediately() + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, false, 1) +end + +function CrouchBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function CrouchBeginState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer:Crouch() +end + +return CrouchBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchEndState.lua new file mode 100644 index 0000000..a64746f --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchEndState.lua @@ -0,0 +1,37 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local CrouchEndState = class('CrouchEndState', PlayerActState) + +function CrouchEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_crouchtostand_02', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_crouchtostand_02', 1, _stateName, 2) +end +function CrouchEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.1) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +function CrouchEndState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, false, 1) +end + +function CrouchEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function CrouchEndState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer:UnCrouch() +end + +return CrouchEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchIdleState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchIdleState.lua new file mode 100644 index 0000000..ae6fc84 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchIdleState.lua @@ -0,0 +1,57 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local CrouchIdleState = class('CrouchIdleState', PlayerActState) + +function CrouchIdleState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_crouch_idle_02', 1, _stateName .. 'Right', 1) + PlayerAnimMgr:CreateSingleClipNode('anim_man_crouch_idle_01', 1, _stateName .. 'Left', 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_crouch_idle_02', 1, _stateName .. 'Right', 2) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_crouch_idle_01', 1, _stateName .. 'Left', 2) +end +function CrouchIdleState:InitData() + self:AddTransition( + 'ToCrouchMoveState', + self.controller.states['CrouchMoveState'], + -1, + function() + return self:MoveMonitor() + end + ) + self:AddTransition( + 'ToCrouchEndState', + self.controller.states['CrouchEndState'], + -1, + function() + return not self.controller.isCrouch + end + ) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +function CrouchIdleState:OnEnter() + PlayerActState.OnEnter(self) + if self.controller.stopInfo.footIndex == 1 then + PlayerAnimMgr:Play(self.stateName .. 'Right', 0, 1, 0.2, 0.2, true, true, 1) + else + PlayerAnimMgr:Play(self.stateName .. 'Left', 0, 1, 0.2, 0.2, true, true, 1) + end +end + +function CrouchIdleState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function CrouchIdleState:OnLeave() + PlayerActState.OnLeave(self) +end + +return CrouchIdleState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchMoveState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchMoveState.lua new file mode 100644 index 0000000..0fa7951 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/CrouchMoveState.lua @@ -0,0 +1,66 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local CrouchMoveState = class('CrouchMoveState', PlayerActState) + +function CrouchMoveState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + local animRight = { + {'anim_woman_crouch_idle_02', 0.0, 1.0}, + {'anim_woman_crouch_front_02', 0.25, 1.0} + } + local animLeft = { + {'anim_woman_crouch_idle_01', 0.0, 1.0}, + {'anim_woman_crouch_front_01', 0.25, 1.0} + } + self.animRightNode = PlayerAnimMgr:Create1DClipNode(animRight, 'speedXZ') + self.animLeftNode = PlayerAnimMgr:Create1DClipNode(animLeft, 'speedXZ') +end +function CrouchMoveState:InitData() + self:AddTransition( + 'ToCrouchIdleState', + self.controller.states['CrouchIdleState'], + -1, + function() + return not self:MoveMonitor() + end + ) + self:AddTransition( + 'ToCrouchEndState', + self.controller.states['CrouchEndState'], + -1, + function() + return not self.controller.isCrouch + end + ) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +function CrouchMoveState:OnEnter() + PlayerActState.OnEnter(self) + if self.controller.stopInfo.footIndex == 1 then + PlayerAnimMgr:Play(self.animRightNode, 0, 1, 0.2, 0.2, true, true, 1) + else + PlayerAnimMgr:Play(self.animLeftNode, 0, 1, 0.2, 0.2, true, true, 1) + end +end + +function CrouchMoveState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor(localPlayer.MaxWalkSpeedCrouched) + self:Move() +end + +function CrouchMoveState:OnLeave() + PlayerActState.OnLeave(self) + self.controller:GetStopInfo() +end + +return CrouchMoveState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/DanceState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DanceState.lua new file mode 100644 index 0000000..360384b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DanceState.lua @@ -0,0 +1,42 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local DanceState = class('DanceState', PlayerActState) + +function DanceState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('Exhibition06', 1, _stateName) +end +function DanceState:InitData() + self:AddAnyState( + 'ToDanceState', + -1, + function() + return self.controller.triggers['DanceState'] + end + ) + + self:AddTransition( + 'ToIdleState', + self.controller.states['IdleState'], + -1, + function() + return self.controller.triggers['IdleState'] + end + ) +end + +function DanceState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function DanceState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function DanceState:OnLeave() + PlayerActState.OnLeave(self) +end + +return DanceState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpSprintState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpSprintState.lua new file mode 100644 index 0000000..bf4a685 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpSprintState.lua @@ -0,0 +1,30 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local DoubleJumpSprintState = class('DoubleJumpSprintState', PlayerActState) + +function DoubleJumpSprintState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_doublejump_02', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_doublejump_02', 1, _stateName, 2) +end +function DoubleJumpSprintState:InitData() + self:AddTransition('ToFallState', self.controller.states['FallState'], 0.6) +end + +function DoubleJumpSprintState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, false, 1) + self.controller.jumpCount = self.controller.jumpCount - 1 + localPlayer:LaunchCharacter(Vector3(0, 10, 0) + localPlayer.Forward * 5, false, false) +end + +function DoubleJumpSprintState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function DoubleJumpSprintState:OnLeave() + PlayerActState.OnLeave(self) +end + +return DoubleJumpSprintState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpState.lua new file mode 100644 index 0000000..8eafd21 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DoubleJumpState.lua @@ -0,0 +1,31 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local DoubleJumpState = class('DoubleJumpState', PlayerActState) + +function DoubleJumpState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_doublejump_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_doublejump_01', 1, _stateName, 2) +end +function DoubleJumpState:InitData() + self:AddTransition('ToFallState', self.controller.states['FallState'], 0.4) +end + +function DoubleJumpState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) + self.controller.jumpCount = self.controller.jumpCount - 1 + localPlayer:AddImpulse(Vector3(0, 1200, 0)) +end + +function DoubleJumpState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Move() +end + +function DoubleJumpState:OnLeave() + PlayerActState.OnLeave(self) +end + +return DoubleJumpState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkBeginState.lua new file mode 100644 index 0000000..658583b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkBeginState.lua @@ -0,0 +1,35 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local DrinkBeginState = class('DrinkBeginState', PlayerActState) + +function DrinkBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTPickUp', 1, _stateName) +end +function DrinkBeginState:InitData() + self:AddAnyState( + 'ToDrinkBeginState', + -1, + function() + return self.controller.triggers['DrinkBeginState'] + end + ) + + self:AddTransition('ToDrinkState', self.controller.states['DrinkState'], 1) +end + +function DrinkBeginState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function DrinkBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function DrinkBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return DrinkBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkState.lua new file mode 100644 index 0000000..2a7ed99 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/DrinkState.lua @@ -0,0 +1,34 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local DrinkState = class('DrinkState', PlayerActState) + +function DrinkState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTDrinkwater', 1, _stateName) +end +function DrinkState:InitData() + self:AddTransition( + 'ToIdleState', + self.controller.states['IdleState'], + -1, + function() + return self.controller.triggers['IdleState'] + end + ) +end + +function DrinkState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function DrinkState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function DrinkState:OnLeave() + PlayerActState.OnLeave(self) +end + +return DrinkState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FallState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FallState.lua new file mode 100644 index 0000000..5839d88 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FallState.lua @@ -0,0 +1,65 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FallState = class('FallState', PlayerActState) + +function FallState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + local animsM = { + {'anim_man_jump_falldownloop_01', 0.0, 1.0}, + {'anim_man_jumpforward_falldownloop_01', 0.5, 1.0} + } + local animsW = { + {'anim_woman_jump_falldownloop_01', 0.0, 1.0}, + {'anim_woman_jumpforward_falldownloop_01', 0.5, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function FallState:InitData() + self:AddTransition( + 'ToLandState', + self.controller.states['LandState'], + -1, + function() + return self:FloorMonitor(0.5) + end + ) + self:AddTransition( + 'ToDoubleJumpState', + self.controller.states['DoubleJumpState'], + -1, + function() + return self.controller.triggers['DoubleJumpState'] + end + ) + self:AddTransition( + 'ToDoubleJumpSprintState', + self.controller.states['DoubleJumpSprintState'], + -1, + function() + return self.controller.triggers['DoubleJumpSprintState'] + end + ) +end + +function FallState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, true, 1) +end + +function FallState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Move() + self:SpeedMonitor() + + --- 空中控制 + PlayerActState:JumpAirControlUpdate() +end + +function FallState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FallState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyBeginState.lua new file mode 100644 index 0000000..0719a20 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyBeginState.lua @@ -0,0 +1,31 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlyBeginState = class('FlyBeginState', PlayerActState) + +function FlyBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_jumptohover_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_jumptohover_01', 1, _stateName, 2) +end +function FlyBeginState:InitData() + self:AddTransition('ToFlyIdleState', self.controller.states['FlyIdleState'], 0.4) +end + +function FlyBeginState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer:StopMovementImmediately() + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, false, 1) +end + +function FlyBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function FlyBeginState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer:AddImpulse(localPlayer.Up * 500) + wait() +end + +return FlyBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyEndState.lua new file mode 100644 index 0000000..97a1dc2 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyEndState.lua @@ -0,0 +1,29 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlyEndState = class('FlyEndState', PlayerActState) + +function FlyEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_hovertoland_02', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_hovertoland_02', 1, _stateName, 2) +end +function FlyEndState:InitData() + self:AddTransition('IdleState', self.controller.states['IdleState'], 0.8) +end + +function FlyEndState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer:StopMovementImmediately() + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.15, 0.15, true, false, 1) +end + +function FlyEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function FlyEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlyEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyIdleState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyIdleState.lua new file mode 100644 index 0000000..9597f1b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyIdleState.lua @@ -0,0 +1,56 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlyIdleState = class('FlyIdleState', PlayerActState) + +function FlyIdleState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + local animsM = { + {'anim_man_hoveridle_01', 0.0, 1.0}, + {'anim_man_hoverup_01', 0.5, 1.0}, + {'anim_man_hoverdown_01', -0.5, 1.0} + } + local animsW = { + {'anim_woman_hoveridle_01', 0.0, 1.0}, + {'anim_woman_hoverup_01', 0.5, 1.0}, + {'anim_woman_hoverdown_01', -0.5, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedY', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedY', _stateName, 2) +end +function FlyIdleState:InitData() + self:AddTransition( + 'ToFlyMoveState', + self.controller.states['FlyMoveState'], + -1, + function() + return self:MoveMonitor() + end + ) + self:AddTransition( + 'ToFlyEndState', + self.controller.states['FlyEndState'], + -1, + function() + return self:FloorMonitor() + end + ) +end + +function FlyIdleState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer:SetMovementMode(Enum.MovementMode.MOVE_Flying) + localPlayer.RotationRate = EulerDegree(0, 250, 0) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 0.5) +end + +function FlyIdleState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor() + self:UpAndDown() +end +function FlyIdleState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlyIdleState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyMoveState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyMoveState.lua new file mode 100644 index 0000000..68607ab --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlyMoveState.lua @@ -0,0 +1,60 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlyMoveState = class('FlyMoveState', PlayerActState) + +function FlyMoveState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + local animsM = { + {'anim_man_hoverIdle_01', 0.0, 1.0}, + {'anim_man_hoveforward_01', 0.5, 1.0} + } + local animsW = { + {'anim_woman_hoveridle_01', 0.0, 1.0}, + {'anim_woman_hoverforward_01', 0.5, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function FlyMoveState:InitData() + self:AddTransition( + 'ToFlyIdleState', + self.controller.states['FlyIdleState'], + -1, + function() + return not self:MoveMonitor() + end + ) + self:AddTransition( + 'ToFlyEndState', + self.controller.states['FlyEndState'], + -1, + function() + return self:FloorMonitor(0.06) + end + ) + self:AddTransition( + 'ToFlySprintBeginState', + self.controller.states['FlySprintBeginState'], + -1, + function() + return C.Mgr.PlayerCtrl.isSprint + end + ) +end + +function FlyMoveState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function FlyMoveState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor(localPlayer.MaxFlySpeed) + self:Fly() +end +function FlyMoveState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlyMoveState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintBeginState.lua new file mode 100644 index 0000000..4048d34 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintBeginState.lua @@ -0,0 +1,43 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlySprintBeginState = class('FlySprintBeginState', PlayerActState) + +function FlySprintBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_hovertofly_01', 1.3, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_hovertofly_01', 1, _stateName, 2) + --[[self.animNode:AddAnimationEvent(0.5):Connect( + function() + if self:MoveMonitor() then + localPlayer:AddImpulse(localPlayer.Forward * 500) + end + end + )]] +end +function FlySprintBeginState:InitData() + self:AddTransition('ToFlySprintState', self.controller.states['FlySprintState'], 0.6) + self:AddTransition( + 'ToFlyEndState', + self.controller.states['FlyEndState'], + -1, + function() + return self:FloorMonitor(0.06) + end + ) +end + +function FlySprintBeginState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 0.8) +end + +function FlySprintBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function FlySprintBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlySprintBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintEndState.lua new file mode 100644 index 0000000..0458fc1 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintEndState.lua @@ -0,0 +1,36 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlySprintEndState = class('FlySprintEndState', PlayerActState) + +function FlySprintEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_man_flytohover_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_flytohover_01', 1, _stateName, 2) +end +function FlySprintEndState:InitData() + self:AddTransition('FlyMoveState', self.controller.states['FlyMoveState'], 0.5) + self:AddTransition( + 'ToFlySprintEndState', + self.controller.states['FlySprintEndState'], + -1, + function() + return self:FloorMonitor(0.06) + end + ) +end + +function FlySprintEndState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function FlySprintEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function FlySprintEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlySprintEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintState.lua new file mode 100644 index 0000000..b7e184f --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/FlySprintState.lua @@ -0,0 +1,58 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local FlySprintState = class('FlySprintState', PlayerActState) + +function FlySprintState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + local animsM = { + {'anim_man_flyforward_01', 0.0, 0.0, 1.0}, + {'anim_man_flyturnleft_01', -1.0, 0.0, 1.0}, + {'anim_man_flyturnright_01', 1.0, 0.0, 1.0}, + {'anim_man_flyup_01', 0.0, 1.0, 1.0}, + {'anim_man_flydown_01', 0.0, -1.0, 1.0} + } + local animsW = { + {'anim_woman_flyforward_01', 0.0, 0.0, 1.0}, + {'anim_woman_flyturnleft_01', -1.0, 0.0, 1.0}, + {'anim_woman_flyturnright_01', 1.0, 0.0, 1.0}, + {'anim_woman_flyup_01', 0.0, 1.0, 1.0}, + {'anim_woman_flydown_01', 0.0, -1.0, 1.0} + } + PlayerAnimMgr:Create2DClipNode(animsM, 'speedX', 'speedY', _stateName, 1) + PlayerAnimMgr:Create2DClipNode(animsW, 'speedX', 'speedY', _stateName, 2) +end +function FlySprintState:InitData() + self:AddTransition( + 'ToFlySprintEndState', + self.controller.states['FlySprintEndState'], + -1, + function() + return not C.Mgr.PlayerCtrl.isSprint + end + ) + self:AddTransition( + 'ToFlyEndState', + self.controller.states['FlyEndState'], + -1, + function() + return self:FloorMonitor(0.07) + end + ) +end + +function FlySprintState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function FlySprintState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor(localPlayer.MaxFlySpeed) + self:Fly() +end +function FlySprintState:OnLeave() + PlayerActState.OnLeave(self) +end + +return FlySprintState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/IdleState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/IdleState.lua new file mode 100644 index 0000000..fa38865 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/IdleState.lua @@ -0,0 +1,68 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local IdleState = class('IdleState', PlayerActState) + +function IdleState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_man_idle_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_idle_01', 1, _stateName, 2) +end + +function IdleState:InitData() + self:AddTransition( + 'ToMoveState', + self.controller.states['MoveState'], + -1, + function() + return self:MoveMonitor() + end + ) + self:AddTransition( + 'ToJumpBeginState', + self.controller.states['JumpBeginState'], + -1, + function() + return self.controller.triggers['JumpBeginState'] + end + ) + self:AddTransition( + 'ToCrouchBeginState', + self.controller.states['CrouchBeginState'], + -1, + function() + return self.controller.isCrouch + end + ) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +function IdleState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer.CharacterWidth = 0.5 + localPlayer.CharacterHeight = 1.7 + localPlayer.Avatar.LocalPosition = Vector3.Zero + localPlayer.RotationRate = EulerDegree(0, 540, 0) + localPlayer:SetMovementMode(Enum.MovementMode.MOVE_Walking) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) + self:FallMonitor() + self.controller.jumpCount = localPlayer.JumpMaxCount +end + +function IdleState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function IdleState:OnLeave() + PlayerActState.OnLeave(self) +end + +return IdleState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpBeginState.lua new file mode 100644 index 0000000..bb3d536 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpBeginState.lua @@ -0,0 +1,38 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local JumpBeginState = class('JumpBeginState', PlayerActState) + +function JumpBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_man_jump_begin_01', 1, _stateName, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_jump_begin_01', 1, _stateName, 2) +end +function JumpBeginState:InitData() + self:AddTransition('ToJumpRiseState', self.controller.states['JumpRiseState'], 0.05) +end + +function JumpBeginState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0, 0, true, false, 1.1) +end + +function JumpBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + + --- 空中控制 + PlayerActState:JumpAirControlUpdate() +end + +function JumpBeginState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer:Jump() + --[[ + if self:MoveMonitor() then + localPlayer:AddImpulse(C.Mgr.PlayerCtrl.finalDir * 500) + end + --]] +end + +return JumpBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpHighestState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpHighestState.lua new file mode 100644 index 0000000..554c3e6 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpHighestState.lua @@ -0,0 +1,66 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local JumpHighestState = class('JumpHighestState', PlayerActState) + +function JumpHighestState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + local animsM = { + {'anim_man_jumpforward_highest_01', 0.0, 1.0}, + {'anim_man_jumpforward_highest_02', 0.3, 1.0} + } + local animsW = { + {'anim_woman_jumpforward_highest_01', 0.0, 1.0}, + {'anim_woman_jumpforward_highest_02', 0.3, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function JumpHighestState:InitData() + self:AddTransition('ToFallState', self.controller.states['FallState'], 0.5) + self:AddTransition( + 'ToLandState', + self.controller.states['LandState'], + -1, + function() + return self:FloorMonitor(0.5) + end + ) + self:AddTransition( + 'ToDoubleJumpState', + self.controller.states['DoubleJumpState'], + -1, + function() + return self.controller.triggers['DoubleJumpState'] + end + ) + self:AddTransition( + 'ToDoubleJumpSprintState', + self.controller.states['DoubleJumpSprintState'], + -1, + function() + return self.controller.triggers['DoubleJumpSprintState'] + end + ) +end + +function JumpHighestState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, false, 1) +end + +function JumpHighestState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Move() + self:SpeedMonitor() + + --- 空中控制 + PlayerActState:JumpAirControlUpdate() +end + +function JumpHighestState:OnLeave() + PlayerActState.OnLeave(self) +end + +return JumpHighestState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpRiseState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpRiseState.lua new file mode 100644 index 0000000..ad27f67 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/JumpRiseState.lua @@ -0,0 +1,44 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local JumpRiseState = class('JumpRiseState', PlayerActState) + +function JumpRiseState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + local animsM = { + {'anim_man_jump_riseuploop_01', 0.0, 1.0}, + {'anim_man_jumpforward_riseuploop_02', 0.5, 1.0} + } + local animsW = { + {'anim_woman_jump_riseuploop_01', 0.0, 1.0}, + {'anim_woman_jumpforward_riseuploop_02', 0.5, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function JumpRiseState:InitData() + self:AddTransition('ToJumpHighestState', self.controller.states['JumpHighestState'], 0.05) +end + +function JumpRiseState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.1, 0.1, true, true, 1) + self.controller.jumpCount = self.controller.jumpCount - 1 +end + +function JumpRiseState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:FallMonitor() + self:Move() + self:SpeedMonitor() + + --- 空中控制 + PlayerActState:JumpAirControlUpdate() +end + +function JumpRiseState:OnLeave() + PlayerActState.OnLeave(self) +end + +return JumpRiseState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/LandState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LandState.lua new file mode 100644 index 0000000..8e986c3 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LandState.lua @@ -0,0 +1,65 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local LandState = class('LandState', PlayerActState) + +function LandState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_man_jumptoidle_01', 1, _stateName .. 1, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_jumptoidle_01', 1, _stateName .. 1, 2) + PlayerAnimMgr:CreateSingleClipNode('anim_man_jumpforwardtorun_01', 1, _stateName .. 2, 1) + PlayerAnimMgr:CreateSingleClipNode('anim_woman_jumpforwardtorun_01', 1, _stateName .. 2, 2) +end +function LandState:InitData() + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) + self:AddTransition( + 'ToJumpBeginState', + self.controller.states['JumpBeginState'], + -1, + function() + return self.controller.triggers['JumpBeginState'] + end + ) +end + +function LandState:OnEnter() + PlayerActState.OnEnter(self) + local dir = C.Mgr.PlayerCtrl.finalDir + dir.y = 0 + if dir.Magnitude > 0 then + self:AddTransition('ToMoveState', self.controller.states['MoveState'], 0.01) + PlayerAnimMgr:Play(self.stateName .. 2, 0, 1, 0.1, 0.1, true, false, 0.8) + else + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.3) + self:AddTransition( + 'ToMoveState', + self.controller.states['MoveState'], + -1, + function() + return self:MoveMonitor() + end + ) + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.1, 0.1, true, false, 1) + end + self.controller.jumpCount = localPlayer.JumpMaxCount +end + +function LandState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Move() +end + +function LandState:OnLeave() + PlayerActState.OnLeave(self) + self.transitions = {} +end + +return LandState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieBeginState.lua new file mode 100644 index 0000000..1b05145 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieBeginState.lua @@ -0,0 +1,43 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local LieBeginState = class('LieBeginState', PlayerActState) + +function LieBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_pd_sitdown_01', 1, _stateName .. 1) + PlayerAnimMgr:CreateSingleClipNode('anim_human_pd_liedown_01', 1, _stateName .. 1) +end +function LieBeginState:InitData() + self:AddAnyState( + 'ToLieBeginState', + -1, + function() + return self.controller.triggers['LieBeginState'] + end + ) + self:AddTransition('ToLieState', self.controller.states['LieState'], 1) +end + +function LieBeginState:OnEnter() + PlayerActState.OnEnter(self) + + --localPlayer.FollowTarget = self.controller.seatObj + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.2, 0.2, true, false, 1) + local CallBack1 = localPlayer.Avatar:AddAnimationEvent('anim_human_pd_sitdown_01', 0.99) + CallBack1:Connect( + function() + PlayerAnimMgr:Play(self.stateName .. 2, 0, 1, 0.2, 0.2, true, false, 1) + end + ) +end + +function LieBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function LieBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return LieBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieEndState.lua new file mode 100644 index 0000000..7f52b85 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieEndState.lua @@ -0,0 +1,36 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local LieEndState = class('LieEndState', PlayerActState) + +function LieEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_pd_lieup_01', 1, _stateName .. 1) + PlayerAnimMgr:CreateSingleClipNode('anim_human_pd_liestand_01', 1, _stateName .. 2) +end +function LieEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 1) +end + +function LieEndState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.2, 0.2, true, false, 1) + + local CallBack1 = localPlayer.Avatar:AddAnimationEvent('anim_human_pd_lieup_01', 0.99) + CallBack1:Connect( + function() + PlayerAnimMgr:Play(self.stateName .. 2, 0, 1, 0.2, 0.2, true, false, 1) + end + ) +end + +function LieEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function LieEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return LieEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieState.lua new file mode 100644 index 0000000..f482ec2 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/LieState.lua @@ -0,0 +1,34 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local LieState = class('LieState', PlayerActState) + +function LieState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_pd_liedownloop_01', 1, _stateName) +end +function LieState:InitData() + self:AddTransition( + 'ToLieEndState', + self.controller.states['LieEndState'], + -1, + function() + return self.controller.triggers['LieEndState'] + end + ) +end + +function LieState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function LieState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function LieState:OnLeave() + PlayerActState.OnLeave(self) +end + +return LieState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveState.lua new file mode 100644 index 0000000..900e80d --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveState.lua @@ -0,0 +1,84 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local MoveState = class('MoveState', PlayerActState) + +function MoveState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + local animsM = { + {'anim_man_idle_01', 0.0, 1.0}, + {'anim_man_walkfront_01', 0.25, 1.0}, + {'anim_man_runfront_01', 0.5, 1.0}, + {'anim_man_sprint_01', 1, 1.0} + } + + local animsW = { + {'anim_woman_idle_01', 0.0, 1.0}, + {'anim_woman_walkfront_01', 0.25, 1.0}, + {'anim_woman_runfront_01', 0.5, 1.0}, + {'anim_woman_sprint_01', 1, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function MoveState:InitData() + self:AddTransition( + 'ToMoveStopState', + self.controller.states['MoveStopState'], + -1, + function() + return not self:MoveMonitor() + end + ) + self:AddTransition( + 'ToJumpBeginState', + self.controller.states['JumpBeginState'], + -1, + function() + return self.controller.triggers['JumpBeginState'] + end + ) + self:AddTransition( + 'ToJumpHighestState', + self.controller.states['JumpHighestState'], + -1, + function() + return self.controller.triggers['JumpHighestState'] + end + ) + self:AddTransition( + 'ToCrouchBeginState', + self.controller.states['CrouchBeginState'], + -1, + function() + return self.controller.isCrouch + end + ) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +function MoveState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function MoveState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor() + self:Move(true) + self:FallMonitor() +end +function MoveState:OnLeave() + PlayerActState.OnLeave(self) + self.controller:GetStopInfo() +end + +return MoveState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveStopState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveStopState.lua new file mode 100644 index 0000000..3c844b8 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/MoveStopState.lua @@ -0,0 +1,106 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local MoveStopState = class('MoveStopState', PlayerActState) + +function MoveStopState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + self.animsM = { + {'LW', 'anim_man_stop_l_05', 0.2, 1.0}, + {'LR1', 'anim_man_stop_l_03', 0.2, 1.0}, + {'LR2', 'anim_man_stop_l_07', 0.2, 1.0}, + {'LS', 'anim_man_stop_l_06', 0.2, 1.0}, + {'RW', 'anim_man_stop_r_05', 0.2, 1.0}, + {'RR1', 'anim_man_stop_r_03', 0.2, 1.0}, + {'RR2', 'anim_man_stop_r_07', 0.2, 1.0}, + {'RS', 'anim_man_stop_r_06', 0.2, 1.0} + } + self.animsW = { + {'LW', 'anim_woman_stop_l_05', 0.2, 1.0}, + {'LR1', 'anim_woman_stop_l_03', 0.2, 1.0}, + {'LR2', 'anim_woman_stop_l_07', 0.2, 1.0}, + {'LS', 'anim_woman_stop_l_06', 0.2, 1.0}, + {'RW', 'anim_woman_stop_r_05', 0.2, 1.0}, + {'RR1', 'anim_woman_stop_r_03', 0.2, 1.0}, + {'RR2', 'anim_woman_stop_r_07', 0.2, 1.0}, + {'RS', 'anim_woman_stop_r_06', 0.2, 1.0} + } + for i, v in pairs(self.animsM) do + PlayerAnimMgr:CreateSingleClipNode(v[2], v[4], _stateName .. i, 1) + end + for i, v in pairs(self.animsW) do + PlayerAnimMgr:CreateSingleClipNode(v[2], v[4], _stateName .. i, 2) + end +end +function MoveStopState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) + self:AddTransition( + 'ToMoveState', + self.controller.states['MoveState'], + -1, + function() + return self:MoveMonitor() + end + ) + self:AddTransition( + 'ToJumpBeginState', + self.controller.states['JumpBeginState'], + -1, + function() + return self.controller.triggers['JumpBeginState'] + end + ) + self:AddTransition( + 'ToCrouchBeginState', + self.controller.states['CrouchBeginState'], + -1, + function() + return self.controller.isCrouch + end + ) + self:AddTransition( + 'ToFlyBeginState', + self.controller.states['FlyBeginState'], + -1, + function() + return self.controller.triggers['FlyBeginState'] + end + ) +end + +--确定该播放哪个停步动作 +function MoveStopState:GetStopIndex() + local stopSSpeed = 0.9 + local stopRSpeed = 0.4 + local stopDisGap = 0.7 + local index = 1 + if math.clamp(self.controller.stopInfo.speed / localPlayer.MaxWalkSpeed, 0, 1) > stopSSpeed then + index = 4 + elseif math.clamp(self.controller.stopInfo.speed / localPlayer.MaxWalkSpeed, 0, 1) > stopRSpeed then + if self.controller.stopInfo.footDis > stopDisGap then + index = 2 + else + index = 3 + end + end + if self.controller.stopInfo.footIndex == 1 then + index = index + 4 + end + return index +end + +function MoveStopState:OnEnter() + PlayerActState.OnEnter(self) + local index = self:GetStopIndex() + PlayerAnimMgr:Play(self.stateName .. index, 0, 1, 0.2, 0.2, true, false, 1) +end + +function MoveStopState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end +function MoveStopState:OnLeave() + PlayerActState.OnLeave(self) +end + +return MoveStopState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/OpenState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/OpenState.lua new file mode 100644 index 0000000..7a6cee1 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/OpenState.lua @@ -0,0 +1,46 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---开启动画 +local OpenState = class('OpenState', PlayerActState) + +function OpenState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('OpenDoor', 1, _stateName) +end +function OpenState:InitData() + self:AddAnyState( + 'ToOpenState', + -1, + function() + return self.controller.triggers['OpenState'] + end + ) + + self:AddTransition( + ---要去哪个状态 + 'ToIdleState', + self.controller.states['IdleState'], + ---时间耗尽转移状态 + 1 + ) +end + +function OpenState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function OpenState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function OpenState:OnLeave() + PlayerActState.OnLeave(self) + --localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return OpenState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyBeginState.lua new file mode 100644 index 0000000..765ccdf --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyBeginState.lua @@ -0,0 +1,52 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---杠铃交互动画 +---继承PlayerActState +local PickUpHeavyBeginState = class('PickUpHeavyBeginState', PlayerActState) + +function PickUpHeavyBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + ---动画名,动画播放速度 + PlayerAnimMgr:CreateSingleClipNode('PickUpHeavy', 0.6, _stateName) +end +function PickUpHeavyBeginState:InitData() + ---可以从任意状态进入 + self:AddAnyState( + ---要去哪个状态,后缀带State + 'ToPickUpHeavyBeginState', + ---触发器进入 + -1, + function() + ---触发器实例化,后缀带State + return self.controller.triggers['PickUpHeavyBeginState'] + end + ) + + self:AddTransition( + ---要去哪个状态,后缀带State + 'ToPickUpHeavyState', + ---后缀带State + self.controller.states['PickUpHeavyState'], + ---时间耗尽转移状态 + 1 + ) +end + +function PickUpHeavyBeginState:OnEnter() + PlayerActState.OnEnter(self) + + --localPlayer.FollowTarget = self.controller.seatObj + ---上下半身动画设置,权重,进入动画过渡时间,退出动画过渡时间,是否可以打断,是否循环,动画播放速度 + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1.5) +end + +function PickUpHeavyBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function PickUpHeavyBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return PickUpHeavyBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyEndState.lua new file mode 100644 index 0000000..352ac5c --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyEndState.lua @@ -0,0 +1,30 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---杠铃交互动画 +local PickUpHeavyEndState = class('PickUpHeavyEndState', PlayerActState) + +function PickUpHeavyEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('Drop', 1, _stateName .. 1) +end + +function PickUpHeavyEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) +end + +function PickUpHeavyEndState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.2, 0.2, true, false, 1) +end + +function PickUpHeavyEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function PickUpHeavyEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return PickUpHeavyEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyState.lua new file mode 100644 index 0000000..71f21a6 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PickUpHeavyState.lua @@ -0,0 +1,39 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---杠铃交互动画 +local PickUpHeavyState = class('PickUpHeavyState', PlayerActState) + +function PickUpHeavyState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HoldOn', 1, _stateName) +end +function PickUpHeavyState:InitData() + self:AddTransition( + 'ToPickUpHeavyEndState', + self.controller.states['PickUpHeavyEndState'], + -1, + function() + return self.controller.triggers['PickUpHeavyEndState'] + end + ) +end + +function PickUpHeavyState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 2, 1, 0.2, 0.2, true, true, 1) +end + +function PickUpHeavyState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function PickUpHeavyState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(2) +end + +return PickUpHeavyState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PlayRockerState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PlayRockerState.lua new file mode 100644 index 0000000..f93d614 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PlayRockerState.lua @@ -0,0 +1,47 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---玩摇杆动画 +local PlayRockerState = class('PlayRockerState', PlayerActState) + +function PlayRockerState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTPlayRocker', 1, _stateName) +end +function PlayRockerState:InitData() + self:AddAnyState( + 'ToPlayRockerState', + -1, + function() + return self.controller.triggers['PlayRockerState'] + end + ) + + self:AddTransition( + 'ToIdleState', + self.controller.states['IdleState'], + -1, + function() + return self.controller.triggers['IdleState'] + end + ) +end + +function PlayRockerState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function PlayRockerState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function PlayRockerState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return PlayRockerState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchBeginState.lua new file mode 100644 index 0000000..b00798e --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchBeginState.lua @@ -0,0 +1,52 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---拳击交互动画 +---继承PlayerActState +local PunchBeginState = class('PunchBeginState', PlayerActState) + +function PunchBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + ---动画名,动画播放速度 + PlayerAnimMgr:CreateSingleClipNode('PunchEquip', 0.6, _stateName) +end +function PunchBeginState:InitData() + ---可以从任意状态进入 + self:AddAnyState( + ---要去哪个状态,后缀带State + 'ToPunchBeginState', + ---触发器进入 + -1, + function() + ---触发器实例化,后缀带State + return self.controller.triggers['PunchBeginState'] + end + ) + + self:AddTransition( + ---要去哪个状态,后缀带State + 'ToPunchState', + ---后缀带State + self.controller.states['PunchState'], + ---时间耗尽转移状态 + 0.6 + ) +end + +function PunchBeginState:OnEnter() + PlayerActState.OnEnter(self) + + --localPlayer.FollowTarget = self.controller.seatObj + ---上下半身动画设置,权重,进入动画过渡时间,退出动画过渡时间,是否可以打断,是否循环,动画播放速度 + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1.5) +end + +function PunchBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function PunchBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return PunchBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchEndState.lua new file mode 100644 index 0000000..a2f126d --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchEndState.lua @@ -0,0 +1,30 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---拳击交互动画 +local PunchEndState = class('PunchEndState', PlayerActState) + +function PunchEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('PunchUnEquip', 1, _stateName .. 1) +end + +function PunchEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) +end + +function PunchEndState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.2, 0.2, true, false, 1) +end + +function PunchEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function PunchEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return PunchEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchState.lua new file mode 100644 index 0000000..8984ceb --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/PunchState.lua @@ -0,0 +1,50 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---拳击交互动画 +local PunchState = class('PunchState', PlayerActState) + +function PunchState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('PunchIdle', 1, _stateName) +end +function PunchState:InitData() + self:AddTransition('ToAttackPunch1State', self.controller.states['AttackPunch1State'], 0.05) + + self:AddTransition( + 'ToAttackPunch2State', + self.controller.states['AttackPunch2State'], + -1, + function() + return self.controller.triggers['AttackPunch2State'] + end + ) + + self:AddTransition( + 'ToPunchEndState', + self.controller.states['PunchEndState'], + -1, + function() + return self.controller.triggers['PunchEndState'] + end + ) +end + +function PunchState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function PunchState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function PunchState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return PunchState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/RideState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/RideState.lua new file mode 100644 index 0000000..eabf77c --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/RideState.lua @@ -0,0 +1,47 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---骑行动画 +local RideState = class('RideState', PlayerActState) + +function RideState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTRiding', 1, _stateName) +end +function RideState:InitData() + self:AddAnyState( + 'ToRideState', + -1, + function() + return self.controller.triggers['RideState'] + end + ) + + self:AddTransition( + 'ToIdleState', + self.controller.states['IdleState'], + -1, + function() + return self.controller.triggers['IdleState'] + end + ) +end + +function RideState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function RideState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function RideState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return RideState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/RunOnMachineState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/RunOnMachineState.lua new file mode 100644 index 0000000..e847f15 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/RunOnMachineState.lua @@ -0,0 +1,47 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +---在跑步机上奔跑动画 +local RunOnMachineState = class('RunOnMachineState', PlayerActState) + +function RunOnMachineState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('RunFront', 1, _stateName) +end +function RunOnMachineState:InitData() + self:AddAnyState( + 'ToRunOnMachineState', + -1, + function() + return self.controller.triggers['RunOnMachineState'] + end + ) + + self:AddTransition( + 'ToIdleState', + self.controller.states['IdleState'], + -1, + function() + return self.controller.triggers['IdleState'] + end + ) +end + +function RunOnMachineState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function RunOnMachineState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --self:SpeedMonitor() + --self:Move(true) + --self:FallMonitor() +end + +function RunOnMachineState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(0) +end + +return RunOnMachineState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitBeginState.lua new file mode 100644 index 0000000..a8f0760 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitBeginState.lua @@ -0,0 +1,38 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SitBeginState = class('SitBeginState', PlayerActState) + +function SitBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_begin', 1, _stateName) +end +function SitBeginState:InitData() + self:AddAnyState( + 'ToSitBeginState', + -1, + function() + return self.controller.triggers['SitBeginState'] + end + ) + self:AddTransition('ToSitState', self.controller.states['SitState'], 1) +end + +function SitBeginState:OnEnter() + PlayerActState.OnEnter(self) + --[[localPlayer.Position = + self.controller.seatObj.Positive + self.controller.seatObj.Forward * self.controller.seatObj.Size.z / 2 + localPlayer.Rotation = self.controller.seatObj.Rotation]] + --localPlayer.FollowTarget = self.controller.seatObj + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function SitBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function SitBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SitBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitEndState.lua new file mode 100644 index 0000000..aad1b03 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitEndState.lua @@ -0,0 +1,28 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SitEndState = class('SitEndState', PlayerActState) + +function SitEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_end', 1, _stateName) +end +function SitEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 1) +end + +function SitEndState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function SitEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function SitEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SitEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitState.lua new file mode 100644 index 0000000..9bd57e7 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SitState.lua @@ -0,0 +1,34 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SitState = class('SitState', PlayerActState) + +function SitState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_loop', 1, _stateName) +end +function SitState:InitData() + self:AddTransition( + 'ToSitEndState', + self.controller.states['SitEndState'], + -1, + function() + return self.controller.triggers['SitEndState'] + end + ) +end + +function SitState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function SitState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function SitState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SitState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimBeginState.lua new file mode 100644 index 0000000..8df3daf --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimBeginState.lua @@ -0,0 +1,50 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimBeginState = class('SwimBeginState', PlayerActState) + +function SwimBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + --PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_swim_intowater', 1, _stateName) +end + +function SwimBeginState:InitData() + self:AddAnyState( + 'ToSwimBeginState', + -1, + function() + return self:SwimMonitor() and not localPlayer:IsSwimming() + end + ) + + self:AddTransition('ToSwimmingState', self.controller.states['SwimmingState'], 0.01) + self:AddTransition( + 'ToSwimEndState', + self.controller.states['SwimEndState'], + -1, + function() + return not self:SwimMonitor() + end + ) +end + +function SwimBeginState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer:StopMovementImmediately() + if not localPlayer:IsSwimming() then + localPlayer:SetSwimming(true) + localPlayer.RotationRate = EulerDegree(0, 240, 0) + end + EmoActionMgr:HideDanceBtn(false) + --PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function SwimBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function SwimBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SwimBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimEndState.lua new file mode 100644 index 0000000..c1846ec --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimEndState.lua @@ -0,0 +1,62 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimEndState = class('SwimEndState', PlayerActState) + +local check = nil + +function SwimEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('anim_human_sit_swim_goashore', 1, _stateName) +end + +function SwimEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.4) +end + +function SwimEndState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, false, false, 1.75) + invoke( + function() + localPlayer:StopMovementImmediately() + end, + 0.3 + ) + --[[ + invoke( + , + 0.3 + ) + --]] + --[[ + --* 动画偏移修正 + local checkT = + Tween:TweenProperty(localPlayer.Avatar, {LocalPosition = Vector3.Zero}, 0.2, Enum.EaseCurve.Linear) + checkT.OnComplete:Connect( + function() + checkT:Destroy() + end + ) + check = checkT + checkT:Play() + --]] +end + +function SwimEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function SwimEndState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer:SetSwimming(false) + EmoActionMgr:HideDanceBtn(true) + --[[ + if check then + print('swimEnd......................................') + check:Complete() + end + --]] +end + +return SwimEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimIdleState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimIdleState.lua new file mode 100644 index 0000000..454b710 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimIdleState.lua @@ -0,0 +1,60 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimIdleState = class('SwimIdleState', PlayerActState) + +local check = nil + +function SwimIdleState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + local anims = { + {'anim_human_swim_idle_01', 0.0, 1.0}, + {'anim_human_swimup_01', 0.2, 1.0}, + {'anim_human_swimdown_01', -0.2, 1.0} + } + PlayerAnimMgr:Create1DClipNode(anims, 'speedY', _stateName) +end + +function SwimIdleState:InitData() + self:AddTransition( + 'ToSwimmingStartState', + self.controller.states['SwimmingStartState'], + -1, + function() + return self:MoveMonitor() + end + ) + + self:AddTransition( + 'ToSwimEndState', + self.controller.states['SwimEndState'], + -1, + function() + return not self:SwimMonitor() + end + ) +end + +function SwimIdleState:OnEnter() + PlayerActState.OnEnter(self) + localPlayer:StopMovementImmediately() + + --- 屏蔽舞蹈动作 + EmoActionMgr:HideDanceBtn(false) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function SwimIdleState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor() + self:UpAndDown() + self:Swim() + --print(self:IsWaterSuface(1.5)) +end + +function SwimIdleState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SwimIdleState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingEndState.lua new file mode 100644 index 0000000..f71376b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingEndState.lua @@ -0,0 +1,43 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimmingEndState = class('SwimmingEndState', PlayerActState) + +function SwimmingEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_human_freestyletoidle_01', 1, _stateName .. 'Freestyle') + PlayerAnimMgr:CreateSingleClipNode('anim_human_breaststroketoidle_01', 1, _stateName .. 'Breaststroke') +end + +function SwimmingEndState:InitData() + self:AddTransition('ToSwimIdleState', self.controller.states['SwimIdleState'], 0.5) + self:AddTransition( + 'ToSwimEndState', + self.controller.states['SwimEndState'], + -1, + function() + return not self:SwimMonitor() + end + ) +end + +function SwimmingEndState:OnEnter() + PlayerActState.OnEnter(self) + if self:IsWaterSuface() then + PlayerAnimMgr:Play(self.stateName .. 'Freestyle', 0, 1, 0.1, 0.1, true, false, 1) + else + PlayerAnimMgr:Play(self.stateName .. 'Breaststroke', 0, 1, 0.1, 0.1, true, false, 1) + end +end + +function SwimmingEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Swim() +end + +function SwimmingEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SwimmingEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingStartState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingStartState.lua new file mode 100644 index 0000000..d9f6d94 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingStartState.lua @@ -0,0 +1,57 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimmingStartState = class('SwimmingStartState', PlayerActState) + +function SwimmingStartState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_human_idletofreestyle_01', 1, _stateName .. 'Freestyle') + PlayerAnimMgr:CreateSingleClipNode('anim_human_idletobreaststroke_01', 1, _stateName .. 'Breaststroke') +end + +function SwimmingStartState:InitData() + self:AddTransition('ToSwimmingState', self.controller.states['SwimmingState'], 1) + self:AddTransition( + 'ToSwimEndState', + self.controller.states['SwimEndState'], + -1, + function() + return not self:SwimMonitor() + end + ) + self:AddTransition( + 'ToSwimIdleState', + self.controller.states['SwimIdleState'], + -1, + function() + return not self:MoveMonitor() + end + ) +end + +function SwimmingStartState:OnEnter() + PlayerActState.OnEnter(self) + if self:IsWaterSuface() then + --print('Freestyle') + PlayerAnimMgr:Play(self.stateName .. 'Freestyle', 0, 1, 0.2, 0.2, true, true, 1) + else + PlayerAnimMgr:Play(self.stateName .. 'Breaststroke', 0, 1, 0.2, 0.2, true, true, 1.5) + end +end + +function SwimmingStartState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Swim() +end + +function SwimmingStartState:OnLeave() + PlayerActState.OnLeave(self) + if self:MoveMonitor() then + local dir = localPlayer.Forward + dir.y = 0 + localPlayer:AddImpulse(localPlayer.Forward * 400) + end +end + +return SwimmingStartState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingState.lua new file mode 100644 index 0000000..93c4848 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/SwimmingState.lua @@ -0,0 +1,56 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local SwimmingState = class('SwimmingState', PlayerActState) + +local isSufaceWater = true + +function SwimmingState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + PlayerAnimMgr:CreateSingleClipNode('anim_human_swim_freestyle_01', 1, _stateName .. 'Freestyle') + PlayerAnimMgr:CreateSingleClipNode('anim_human_swim_breaststroke_01', 1, _stateName .. 'Breaststroke') +end + +function SwimmingState:InitData() + self:AddTransition( + 'ToSwimmingEndState', + self.controller.states['SwimmingEndState'], + -1, + function() + return not self:MoveMonitor() + end + ) + self:AddTransition( + 'ToSwimEndState', + self.controller.states['SwimEndState'], + -1, + function() + return not self:SwimMonitor() + end + ) +end + +function SwimmingState:OnEnter() + PlayerActState.OnEnter(self) + isSufaceWater = self:IsWaterSuface(0.5) + if isSufaceWater then + PlayerAnimMgr:Play(self.stateName .. 'Freestyle', 0, 1, 0.2, 0.2, true, true, 1) + else + PlayerAnimMgr:Play(self.stateName .. 'Breaststroke', 0, 1, 0.2, 0.2, true, true, 1) + end +end + +function SwimmingState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:Swim() + if isSufaceWater ~= self:IsWaterSuface(0.5) then + self:OnEnter() + end +end + +function SwimmingState:OnLeave() + PlayerActState.OnLeave(self) +end + +return SwimmingState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowBeginState.lua new file mode 100644 index 0000000..5bfc69b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowBeginState.lua @@ -0,0 +1,38 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ThrowBeginState = class('ThrowBeginState', PlayerActState) + +function ThrowBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('PickUpLight', 0.6, _stateName) +end +function ThrowBeginState:InitData() + self:AddAnyState( + 'ToThrowBeginState', + -1, + function() + return self.controller.triggers['ThrowBeginState'] + end + ) + self:AddTransition('ToThrowState', self.controller.states['ThrowState'], 1) +end + +function ThrowBeginState:OnEnter() + PlayerActState.OnEnter(self) + + --localPlayer.FollowTarget = self.controller.seatObj + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1.5) + EmoActionMgr:HideDanceBtn(false) + C_TakePhoto:OpenCamBtnCtrl(false) +end + +function ThrowBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ThrowBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return ThrowBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowEndState.lua new file mode 100644 index 0000000..bca6782 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowEndState.lua @@ -0,0 +1,30 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ThrowEndState = class('ThrowEndState', PlayerActState) + +function ThrowEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('ThrowHighAttack', 1, _stateName .. 1) +end +function ThrowEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 0.5) +end + +function ThrowEndState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.FollowTarget = nil + PlayerAnimMgr:Play(self.stateName .. 1, 0, 1, 0.2, 0.2, true, false, 1) +end + +function ThrowEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ThrowEndState:OnLeave() + PlayerActState.OnLeave(self) + EmoActionMgr:HideDanceBtn(true) + C_TakePhoto:OpenCamBtnCtrl(true) +end + +return ThrowEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowState.lua new file mode 100644 index 0000000..ab60f9f --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/ThrowState.lua @@ -0,0 +1,74 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local ThrowState = class('ThrowState', PlayerActState) + +local moveAnim = 'MoveState' + +function ThrowState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('ThrowIdle', 1, _stateName) +end +function ThrowState:InitData() + self:AddTransition( + 'ToThrowEndState', + self.controller.states['ThrowEndState'], + -1, + function() + return self.controller.triggers['ThrowEndState'] + end + ) +end + +function ThrowState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 2, 1, 0.2, 0.2, true, true, 0.01) + PlayerAnimMgr:Play(moveAnim, 0, 1, 0.2, 0.2, true, true, 1) +end + +function ThrowState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor() + self:Move(true) + self:FallMonitor() +end + +function ThrowState:OnLeave() + PlayerActState.OnLeave(self) + localPlayer.Avatar:StopBlendSpaceNode(2) +end + +return ThrowState + +--[[local ThrowState = class('ThrowState', PlayerActState) + +function ThrowState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('ThrowIdle', 1, _stateName) +end +function ThrowState:InitData() + self:AddTransition( + 'ToThrowEndState', + self.controller.states['ThrowEndState'], + -1, + function() + return self.controller.triggers['ThrowEndState'] + end + ) +end + +function ThrowState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) +end + +function ThrowState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function ThrowState:OnLeave() + PlayerActState.OnLeave(self) +end + +return ThrowState +]] diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveState.lua new file mode 100644 index 0000000..836e61a --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveState.lua @@ -0,0 +1,51 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local WadeMoveState = class('WadeMoveState', PlayerActState) + +-- 玩家原始移动速度 +local prevMaxWallSpeed + +function WadeMoveState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + prevMaxWallSpeed = localPlayer.MaxWalkSpeed + + local animsM = { + {'anim_man_idle_01', 0.0, 1.0}, + {'anim_man_walkfront_01', 0.25, 1.0}, + {'anim_man_runfront_01', 0.5, 1.0}, + {'anim_man_sprint_01', 1, 1.0} + } + + local animsW = { + {'anim_woman_idle_01', 0.0, 1.0}, + {'anim_woman_walkfront_01', 0.25, 1.0}, + {'anim_woman_runfront_01', 0.5, 1.0}, + {'anim_woman_sprint_01', 1, 1.0} + } + PlayerAnimMgr:Create1DClipNode(animsM, 'speedXZ', _stateName, 1) + PlayerAnimMgr:Create1DClipNode(animsW, 'speedXZ', _stateName, 2) +end +function WadeMoveState:InitData() +end + +function WadeMoveState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, true, 1) + localPlayer.MaxWalkSpeed = localPlayer.MaxWalkSpeed * 0.75 +end + +function WadeMoveState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + self:SpeedMonitor() + self:Move(true) + self:FallMonitor() +end +function WadeMoveState:OnLeave() + PlayerActState.OnLeave(self) + self.controller:GetStopInfo() + localPlayer.MaxWalkSpeed = prevMaxWallSpeed +end + +return WadeMoveState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveStopState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveStopState.lua new file mode 100644 index 0000000..3e3c84b --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeMoveStopState.lua @@ -0,0 +1,73 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local WadeMoveStopState = class('WadeMoveStopState', PlayerActState) + +function WadeMoveStopState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + + self.animsM = { + {'LW', 'anim_man_stop_l_05', 0.2, 1.0}, + {'LR1', 'anim_man_stop_l_03', 0.2, 1.0}, + {'LR2', 'anim_man_stop_l_07', 0.2, 1.0}, + {'LS', 'anim_man_stop_l_06', 0.2, 1.0}, + {'RW', 'anim_man_stop_r_05', 0.2, 1.0}, + {'RR1', 'anim_man_stop_r_03', 0.2, 1.0}, + {'RR2', 'anim_man_stop_r_07', 0.2, 1.0}, + {'RS', 'anim_man_stop_r_06', 0.2, 1.0} + } + self.animsW = { + {'LW', 'anim_woman_stop_l_05', 0.2, 1.0}, + {'LR1', 'anim_woman_stop_l_03', 0.2, 1.0}, + {'LR2', 'anim_woman_stop_l_07', 0.2, 1.0}, + {'LS', 'anim_woman_stop_l_06', 0.2, 1.0}, + {'RW', 'anim_woman_stop_r_05', 0.2, 1.0}, + {'RR1', 'anim_woman_stop_r_03', 0.2, 1.0}, + {'RR2', 'anim_woman_stop_r_07', 0.2, 1.0}, + {'RS', 'anim_woman_stop_r_06', 0.2, 1.0} + } + for i, v in pairs(self.animsM) do + PlayerAnimMgr:CreateSingleClipNode(v[2], v[4], _stateName .. i, 1) + end + for i, v in pairs(self.animsW) do + PlayerAnimMgr:CreateSingleClipNode(v[2], v[4], _stateName .. i, 2) + end +end +function WadeMoveStopState:InitData() +end + +--确定该播放哪个停步动作 +function WadeMoveStopState:GetStopIndex() + local stopSSpeed = 0.9 + local stopRSpeed = 0.4 + local stopDisGap = 0.7 + local index = 1 + if math.clamp(self.controller.stopInfo.speed / localPlayer.MaxWalkSpeed, 0, 1) > stopSSpeed then + index = 4 + elseif math.clamp(self.controller.stopInfo.speed / localPlayer.MaxWalkSpeed, 0, 1) > stopRSpeed then + if self.controller.stopInfo.footDis > stopDisGap then + index = 2 + else + index = 3 + end + end + if self.controller.stopInfo.footIndex == 1 then + index = index + 4 + end + return index +end + +function WadeMoveStopState:OnEnter() + PlayerActState.OnEnter(self) + local index = self:GetStopIndex() + PlayerAnimMgr:Play(self.stateName .. index, 0, 1, 0.2, 0.2, true, false, 1) +end + +function WadeMoveStopState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end +function WadeMoveStopState:OnLeave() + PlayerActState.OnLeave(self) +end + +return WadeMoveStopState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeState.lua new file mode 100644 index 0000000..7ccb7ee --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WadeState.lua @@ -0,0 +1,48 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +--- 玩家处于涉水状态 +local WadeState = class('WadeState', PlayerActState) + +local check = nil +function WadeState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) +end + +function WadeState:InitData() +end + +function WadeState:OnEnter() + PlayerActState.OnEnter(self) + --localPlayer.Avatar.LocalPosition = Vector3.Zero + --[[ + if localPlayer.Avatar.LocalPosition.Magnitude ~= 0 then + --* 动画偏移修正 + local checkT = + Tween:TweenProperty(localPlayer.Avatar, {LocalPosition = Vector3.Zero}, 0.2, Enum.EaseCurve.Linear) + checkT.OnComplete:Connect( + function() + checkT:Destroy() + end + ) + check = checkT + checkT:Play() + end + --]] + --- 跳水后恢复跳跃 + self.controller.jumpCount = localPlayer.JumpMaxCount +end + +function WadeState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) + --- 限制最大速度 + self:SpeedMonitor(localPlayer.MaxWalkSpeed * 0.67) + --- 允许移动 + self:Move() +end + +function WadeState:OnLeave() + PlayerActState.OnLeave(self) +end + +return WadeState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantBeginState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantBeginState.lua new file mode 100644 index 0000000..63e8cae --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantBeginState.lua @@ -0,0 +1,34 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local WateringPlantBeginState = class('WateringPlantBeginState', PlayerActState) + +function WateringPlantBeginState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTWateringStart', 1, _stateName) +end +function WateringPlantBeginState:InitData() + self:AddAnyState( + 'ToWateringPlantBeginState', + -1, + function() + return self.controller.triggers['WateringPlantBeginState'] + end + ) + self:AddTransition('ToWateringPlantState', self.controller.states['WateringPlantState'], 1) +end + +function WateringPlantBeginState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 0.5) +end + +function WateringPlantBeginState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function WateringPlantBeginState:OnLeave() + PlayerActState.OnLeave(self) +end + +return WateringPlantBeginState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantEndState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantEndState.lua new file mode 100644 index 0000000..33dc145 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantEndState.lua @@ -0,0 +1,27 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local WateringPlantEndState = class('WateringPlantEndState', PlayerActState) + +function WateringPlantEndState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTWateringEnd', 1, _stateName) +end +function WateringPlantEndState:InitData() + self:AddTransition('ToIdleState', self.controller.states['IdleState'], 1) +end + +function WateringPlantEndState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function WateringPlantEndState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function WateringPlantEndState:OnLeave() + PlayerActState.OnLeave(self) +end + +return WateringPlantEndState diff --git a/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantState.lua b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantState.lua new file mode 100644 index 0000000..93c9440 --- /dev/null +++ b/Smap/Lua/Client/Fsm/PlayerActFsm/State/WateringPlantState.lua @@ -0,0 +1,27 @@ +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local WateringPlantState = class('WateringPlantState', PlayerActState) + +function WateringPlantState:initialize(_controller, _stateName) + PlayerActState.initialize(self, _controller, _stateName) + PlayerAnimMgr:CreateSingleClipNode('HTWateringLoop', 1, _stateName) +end +function WateringPlantState:InitData() + self:AddTransition('ToWateringPlantEndState', self.controller.states['WateringPlantEndState'], 1) +end + +function WateringPlantState:OnEnter() + PlayerActState.OnEnter(self) + PlayerAnimMgr:Play(self.stateName, 0, 1, 0.2, 0.2, true, false, 1) +end + +function WateringPlantState:OnUpdate(dt) + PlayerActState.OnUpdate(self, dt) +end + +function WateringPlantState:OnLeave() + PlayerActState.OnLeave(self) +end + +return WateringPlantState diff --git a/Smap/Lua/Client/Manifest.lua b/Smap/Lua/Client/Manifest.lua new file mode 100644 index 0000000..31abacb --- /dev/null +++ b/Smap/Lua/Client/Manifest.lua @@ -0,0 +1,113 @@ +--- 框架默认配置 +--- @module Framework Global FrameworkConfig +--- @copyright Lilith Games, Avatar Team +local Manifest = {} + +Manifest.ROOT_PATH = 'Lua/Client/' + +Manifest.Events = { + 'NoticeEvent' +} + +Manifest.Modules = { + { + Name = 'Fsm', + Modules = { + { + Name = 'Base', + Modules = { + 'ControllerBase', + 'StateBase', + 'TransitonBase' + } + }, + { + Name = 'PlayerActFsm', + Modules = { + 'PlayerActController', + 'PlayerActState', + { + Name = 'State', + Modules = { + 'ActBeginState', + 'ActEndState', + 'ActState', + 'AttackPunch1State', + 'AttackPunch2State', + 'CrouchBeginState', + 'CrouchEndState', + 'CrouchIdleState', + 'CrouchMoveState', + 'DoubleJumpSprintState', + 'DoubleJumpState', + 'FallState', + 'FlyBeginState', + 'FlyEndState', + 'FlyIdleState', + 'FlyMoveState', + 'FlySprintBeginState', + 'FlySprintEndState', + 'FlySprintState', + 'IdleState', + 'JumpBeginState', + 'JumpHighestState', + 'JumpRiseState', + 'LandState', + 'LieBeginState', + 'LieEndState', + 'LieState', + 'MoveState', + 'MoveStopState', + 'OpenState', + 'PickUpHeavyBeginState', + 'PickUpHeavyEndState', + 'PickUpHeavyState', + 'PlayRockerState', + 'PunchBeginState', + 'PunchEndState', + 'PunchState', + 'RideState', + 'RunOnMachineState', + 'SitBeginState', + 'SitEndState', + 'SitState', + 'SwimBeginState', + 'SwimEndState', + 'SwimIdleState', + 'SwimmingEndState', + 'SwimmingStartState', + 'SwimmingState', + 'ThrowBeginState', + 'ThrowEndState', + 'ThrowState', + 'WadeMoveState', + 'WadeMoveStopState', + 'WadeState', + 'DrinkBeginState', + 'DrinkState', + 'DanceState', + 'WateringPlantBeginState', + 'WateringPlantEndState', + 'WateringPlantState' + } + } + } + } + } + }, + { + Name = 'Mgr', + Modules = { + 'PlayerGuiDefault', + 'EmoActionMgr', + 'FsmMgr', + 'GuiControl', + 'PlayerAnimMgr', + 'PlayerCam', + 'PlayerCtrl', + 'PlayerActAnimeGui' + } + } +} + +return Manifest diff --git a/Smap/Lua/Client/Mgr/EmoActionMgr.lua b/Smap/Lua/Client/Mgr/EmoActionMgr.lua new file mode 100644 index 0000000..8bc3718 --- /dev/null +++ b/Smap/Lua/Client/Mgr/EmoActionMgr.lua @@ -0,0 +1,255 @@ +--- C端交互管理模块 +--- @module EmoActionMgr, Client-side +--- @copyright Lilith Games, Avatar Team +--- @author KeyHou +local EmoActionMgr, this = ModuleUtil.New('EmoActionMgr', ClientBase) + +function EmoActionMgr:Init() + print('[C_Module][EmoActionMgr:Init]') + -------------------------------------------声明节点------------------------------------------- + ---激活舞蹈面板的按钮 + self.DanceBtn = localPlayer.Local.DanceGui.DanceActiveBtn + ---DanceGui + self.Gui = localPlayer.Local.DanceGui + ---舞蹈动作选择面板总节点 + self.Pnl = localPlayer.Local.DanceGui.Panel + ---背板 + self.BackGround = localPlayer.Local.DanceGui.BackGround + ---按下的joystick + self.Jst = localPlayer.Local.DanceGui.DanceJst + ---跳跃按钮 + self.JumpBtn = localPlayer.Local.ControlGui.JumpBtn + ---移动遥感 + self.MoveJst = localPlayer.Local.ControlGui.Joystick + + ---分别给六个扇区声明 + for i = 1, 6 do + ---未选中状态的动作扇区 + self['Img' .. i] = localPlayer.Local.DanceGui.Panel['ActImg' .. i] + ---未选中的动作切图 + self['ActImg' .. i] = localPlayer.Local.DanceGui.Panel['ActImg' .. i].ActionImg + ---选中状态的用来覆盖的扇区 + self['CheckedImg' .. i] = localPlayer.Local.DanceGui.Panel['CheckedImg' .. i] + ---选中了的动作切图 + self['CheckedActImg' .. i] = localPlayer.Local.DanceGui.Panel['CheckedImg' .. i].ActionImg + end + + -------------------------------------------绑定事件------------------------------------------- + --[[ + ---触发舞蹈按钮 + self.DanceBtn.OnDown:Connect(function() + self.Jst:SetActive(true) + self.DanceBtn:SetActive(false) + end) ]] + Input.OnKeyDown:Connect( + function() + if Input.GetPressKeyData(Enum.KeyCode.K) == Enum.KeyState.KeyStatePress then + FsmMgr.playerActCtrl:CallTrigger('LieBeginState') + end + if Input.GetPressKeyData(Enum.KeyCode.N) == Enum.KeyState.KeyStatePress then + FsmMgr.playerActCtrl:CallTrigger('ThrowBeginState') + end + if Input.GetPressKeyData(Enum.KeyCode.M) == Enum.KeyState.KeyStatePress then + FsmMgr.playerActCtrl:CallTrigger('ThrowEndState') + end + end + ) + + ---触发舞蹈摇杆 + self.Jst.OnDragBegin:Connect( + function() + self.DanceBtn:SetActive(false) + self.BackGround:SetActive(true) + self.Pnl:SetActive(true) + --self.JumpBtn:SetActive(false) + self:HideJst(true) + end + ) + + ---拖拽舞蹈摇杆过程中(选择动作) + self.Jst.OnDragStay:Connect( + function() + self:ShowACT() + end + ) + + ---松开舞蹈遥感(播放动作或者退出) + self.Jst.OnDragEnd:Connect( + function() + self.BackGround:SetActive(false) + self.Pnl:SetActive(false) + --self.MoveJst:SetActive(false) + self.Jst:SetActive(false) + self:HideJst(false) + self:DoACT() + end + ) +end + +-------------------------------------------update------------------------------------------- +function EmoActionMgr:Update(_dt, _tt) + --print("[C_Module][TriggerManager:Update]".._dt.."//".._tt); +end + +-------------------------------------------功能方法------------------------------------------- +---选择动作 +function EmoActionMgr:ShowACT() + if self.Jst.DragAngle <= 0 and self.Jst.DragAngle > -30 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img1:SetActive(false) + self.CheckedImg1:SetActive(true) + elseif self.Jst.DragAngle <= -30 and self.Jst.DragAngle > -60 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img2:SetActive(false) + self.CheckedImg2:SetActive(true) + elseif self.Jst.DragAngle <= -60 and self.Jst.DragAngle > -90 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img3:SetActive(false) + self.CheckedImg3:SetActive(true) + elseif self.Jst.DragAngle <= -90 and self.Jst.DragAngle > -120 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img4:SetActive(false) + self.CheckedImg4:SetActive(true) + elseif self.Jst.DragAngle <= -120 and self.Jst.DragAngle > -150 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img5:SetActive(false) + self.CheckedImg5:SetActive(true) + elseif self.Jst.DragAngle <= -150 and self.Jst.DragAngle > -180 then + self:SetOnAllImage() + self:SetOffAllChecked() + self.Img6:SetActive(false) + self.CheckedImg6:SetActive(true) + else + self:SetOnAllImage() + self:SetOffAllChecked() + end +end + +---播放动作 +function EmoActionMgr:DoACT() + if self.Jst.DragAngle <= 0 and self.Jst.DragAngle > -30 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('SocialBow', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('SocialBow',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(1) + elseif self.Jst.DragAngle <= -30 and self.Jst.DragAngle > -60 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('Exhibition12', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('Exhibition12',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(2) + elseif self.Jst.DragAngle <= -60 and self.Jst.DragAngle > -90 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('Exhibition04', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('Exhibition04',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(3) + elseif self.Jst.DragAngle <= -90 and self.Jst.DragAngle > -120 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('SocialLaughing', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('SocialLaughing',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(4) + elseif self.Jst.DragAngle <= -120 and self.Jst.DragAngle > -150 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('SocialComeOn', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('SocialComeOn',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(5) + elseif self.Jst.DragAngle <= -150 and self.Jst.DragAngle > -180 then + local CallBack = localPlayer.Avatar:AddAnimationEvent('SocialAnger', 0.99) + CallBack:Connect( + function() + self:ActCallBack() + end + ) + --localPlayer.Avatar:PlayAnimation('SocialAnger',2,1,0,true,false,1) + PlayerActAnimeGui:PlayActAnim(6) + else + self.DanceBtn:SetActive(true) + self:HideJst(false) + self.Jst:SetActive(true) + self:ActCallBack() + end +end + +---关闭所有选中状态扇形 +function EmoActionMgr:SetOffAllChecked() + --print('!!!!!!!!!!!!!!!') + self.CheckedImg1:SetActive(false) + self.CheckedImg2:SetActive(false) + self.CheckedImg3:SetActive(false) + self.CheckedImg4:SetActive(false) + self.CheckedImg5:SetActive(false) + self.CheckedImg6:SetActive(false) +end + +---打开所有未选中的扇形 +function EmoActionMgr:SetOnAllImage() + self.Img1:SetActive(true) + self.Img2:SetActive(true) + self.Img3:SetActive(true) + self.Img4:SetActive(true) + self.Img5:SetActive(true) + self.Img6:SetActive(true) +end + +---控制透明化舞蹈触发遥感 +---@param _switch bool 是否显示遥感(true显示,false隐藏) +function EmoActionMgr:HideJst(_switch) + if _switch then + self.Jst.BackGround = ResourceManager.GetTexture('UI/Dance_Icon/PoseWheel_Handle_Bg_245x245') + self.Jst.Handle = ResourceManager.GetTexture('UI/Dance_Icon/PoseWheel_Handle_Joystick') + else + if self.Jst.BackGround == 'Transparent' and self.Jst.Handle == 'Transparent' then + print('!!!!!!!!!!!!!!!!!!!!!!') + else + self.Jst.BackGround = ResourceManager.GetTexture('UI/Dance_Icon/Transparent') + self.Jst.Handle = ResourceManager.GetTexture('UI/Dance_Icon/Transparent') + end + end +end + +---播放完动作后的回调 +function EmoActionMgr:ActCallBack() + self.JumpBtn:SetActive(true) + self.MoveJst:SetActive(true) + self.DanceBtn:SetActive(true) + self.Jst:SetActive(true) + -- C_GameTriggerManager:MatchTrigger() +end + +---关闭打开舞蹈触发按钮(外部用) +---@param _switch bool 是否显示(true显示,false隐藏) +function EmoActionMgr:HideDanceBtn(_switch) + if _switch then + self.DanceBtn:SetActive(true) + self.Jst:SetActive(true) + else + self.DanceBtn:SetActive(false) + self.Jst:SetActive(false) + end +end + +return EmoActionMgr diff --git a/Smap/Lua/Client/Mgr/FsmMgr.lua b/Smap/Lua/Client/Mgr/FsmMgr.lua new file mode 100644 index 0000000..25f8581 --- /dev/null +++ b/Smap/Lua/Client/Mgr/FsmMgr.lua @@ -0,0 +1,26 @@ +--- 角色动作状态机模块 +--- @module Fsm Mgr, client-side +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local FsmMgr, this = ModuleUtil.New('FsmMgr', ClientBase) + +--- 初始化 +function FsmMgr:Init() + print('FsmMgr:Init') + this:DataInit() +end + +--- 数据变量初始化 +function FsmMgr:DataInit() + -- 玩家动作状态机控制器 + this.playerActCtrl = PlayerActController:new(localPlayer.StateMachine, C.Fsm.PlayerActFsm.State) + this.playerActCtrl:SetDefState('IdleState') + + world.OnRenderStepped:Connect( + function(dt) + this.playerActCtrl:Update(dt) + end + ) +end + +return FsmMgr diff --git a/Smap/Lua/Client/Mgr/GuiControl.lua b/Smap/Lua/Client/Mgr/GuiControl.lua new file mode 100644 index 0000000..768317a --- /dev/null +++ b/Smap/Lua/Client/Mgr/GuiControl.lua @@ -0,0 +1,42 @@ +--- 玩家控制UI模块 +--- @module Player GuiControll, client-side +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local GuiControl, this = ModuleUtil.New('GuiControl', ClientBase) + +-- 手机端交互UI +local gui + +function GuiControl:Init() + --print('[GuiControl] Init()') + self:InitGui() + self:InitListener() +end + +function GuiControl:InitGui() + gui = localPlayer.Local.ControlGui + this.joystick = gui.Joystick + this.touchScreen = gui.TouchFig + this.jumpBtn = gui.JumpBtn +end + +function GuiControl:InitListener() + -- GUI + this.touchScreen.OnTouched:Connect( + function(touchInfo) + PlayerCam:CameraMove(touchInfo) + end + ) + this.touchScreen.OnPinchStay:Connect( + function(pos1, pos2, deltaSize, pinchSpeed) + PlayerCam:CameraZoom(pos1, pos2, deltaSize, pinchSpeed) + end + ) + this.jumpBtn.OnDown:Connect( + function() + C.Mgr.PlayerCtrl:PlayerJump() + end + ) +end + +return GuiControl diff --git a/Smap/Lua/Client/Mgr/PlayerActAnimeGui.lua b/Smap/Lua/Client/Mgr/PlayerActAnimeGui.lua new file mode 100644 index 0000000..317ba9c --- /dev/null +++ b/Smap/Lua/Client/Mgr/PlayerActAnimeGui.lua @@ -0,0 +1,73 @@ +--- 角色社交动作UI模块 +--- @module Player Cam Module +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman + +local PlayerActAnimeGui, this = ModuleUtil.New('PlayerActAnimeGui', ClientBase) + +local actBtn, childActBtnList +local actAnimTable = {} + +--- 初始化 +function PlayerActAnimeGui:Init() + this:NodeRef() + this:DataInit() + this:EventBind() +end + +--- 节点引用 +function PlayerActAnimeGui:NodeRef() + actBtn = localPlayer.Local.ControlGui.ActBtn + childActBtnList = actBtn.Panel:GetChildren() +end + +--- 数据变量初始化 +function PlayerActAnimeGui:DataInit() +end + +--- 节点事件绑定 +function PlayerActAnimeGui:EventBind() + actBtn.OnClick:Connect( + function() + actBtn.Panel:SetActive(not actBtn.Panel.ActiveSelf) + if actBtn.Panel.ActiveSelf then + self:ActiveChildActBtn() + end + end + ) +end + +---激活子按钮 +function PlayerActAnimeGui:ActiveChildActBtn() + actAnimTable = {} + for k, v in pairs(Xls.ActAnim) do + if v.Mode == FsmMgr.playerActCtrl.actAnimMode then + table.insert(actAnimTable, v) + end + end + if #actAnimTable == 0 then + actBtn.Panel:SetActive(false) + return + end + for i = 1, #childActBtnList do + childActBtnList[i]:SetActive(false) + childActBtnList[i].OnClick:Clear() + if i <= #actAnimTable then + --childActBtnList[i].ActAnimNameText.Text = Xls.ActAnim[actAnimTable[i].ID].ShowName + childActBtnList[i]:SetActive(true) + childActBtnList[i].OnClick:Connect( + function() + this:PlayActAnim(actAnimTable[i].ID) + end + ) + end + end + return +end + +function PlayerActAnimeGui:PlayActAnim(_id) + FsmMgr.playerActCtrl:GetActInfo(Xls.ActAnim[_id]) + FsmMgr.playerActCtrl:CallTrigger('ActBeginState') +end + +return PlayerActAnimeGui diff --git a/Smap/Lua/Client/Mgr/PlayerAnimMgr.lua b/Smap/Lua/Client/Mgr/PlayerAnimMgr.lua new file mode 100644 index 0000000..f2a1c21 --- /dev/null +++ b/Smap/Lua/Client/Mgr/PlayerAnimMgr.lua @@ -0,0 +1,115 @@ +--- 角色动画管理模块 +--- @module PlayerAnim Mgr, client-side +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local PlayerAnimMgr, this = ModuleUtil.New('PlayerAnimMgr', ClientBase) +local clipNodes = { + [0] = {}, + [1] = {}, + [2] = {} +} +--- 初始化 +function PlayerAnimMgr:Init() + print('PlayerAnimMgr:Init') + this:NodeRef() + this:DataInit() + this:EventBind() + + this:SetBlendSubtree() +end + +--- 节点引用 +function PlayerAnimMgr:NodeRef() +end + +--- 数据变量初始化 +function PlayerAnimMgr:DataInit() +end + +--- 节点事件绑定 +function PlayerAnimMgr:EventBind() +end + +--导入动画资源 +function PlayerAnimMgr:ImportAnimation(_anims, _path) + for _, animaName in pairs(_anims) do + ResourceManager.GetAnimation(_path .. animaName) + end +end + +--创建一个包含单个动作的混合空间节点,并设置动作速率 +function PlayerAnimMgr:CreateSingleClipNode(_animName, _speed, _nodeName, _gender) + _gender = _gender or 0 + --print(_gender, table.dump(clipNodes)) + local node = localPlayer.Avatar:AddBlendSpaceSingleNode(false) + node:AddClipSingle(_animName, _speed or 1) + if _nodeName then + clipNodes[_gender][_nodeName] = node + end + return node +end + +--创建一个一维混合空间节点并附带一个参数 +--[[anims = + { + {"anim_woman_idle_01", 0.0, 1.0}, + {"anim_woman_walkfront_01", 0.25, 1.0} + } +]] +function PlayerAnimMgr:Create1DClipNode(_anims, _param, _nodeName, _gender) + _gender = _gender or 0 + local node = localPlayer.Avatar:AddBlendSpace1DNode(_param) + for _, v in pairs(_anims) do + node:AddClip1D(v[1], v[2], v[3] or 1) + end + if _nodeName then + clipNodes[_gender][_nodeName] = node + end + return node +end + +function PlayerAnimMgr:Create2DClipNode(_anims, _param1, _param2, _nodeName, _gender) + _gender = _gender or 0 + local node = localPlayer.Avatar:AddBlendSpace2DNode(_param1, _param2) + for _, v in pairs(_anims) do + node:AddClip2D(v[1], v[2], v[3], v[4] or 1) + end + if _nodeName then + clipNodes[_gender][_nodeName] = node + end + return node +end + +function PlayerAnimMgr:Play(_animNode, _layer, _weight, _transIn, _transOut, _isInterrupt, _isLoop, _speedScale) + local node = nil + if type(_animNode) == 'string' then + node = clipNodes[localPlayer.Avatar.Gender][_animNode] or clipNodes[0][_animNode] + else + node = _animNode + end + localPlayer.Avatar:PlayBlendSpaceNode( + node, + _layer, + _weight or 1, + _transIn or 0, + _transOut or 0, + _isInterrupt or true, + _isLoop or false, + _speedScale or 1 + ) +end + +function PlayerAnimMgr:Update(dt) +end + +---设置avatar人物动画播放部位以及优先级 +function PlayerAnimMgr:SetBlendSubtree() + --localPlayer.Avatar:SetBoneBlendMask(2, Enum.BodyPart.UpperBody, true) + + ---上半身 + localPlayer.Avatar:SetBlendSubtree(Enum.BodyPart.UpperBody, 2) + ---上半身 (同一个部位一个layer,数字越大优先级越大) + localPlayer.Avatar:SetBlendSubtree(Enum.BodyPart.LowerBody, 3) +end + +return PlayerAnimMgr diff --git a/Smap/Lua/Client/Mgr/PlayerCam.lua b/Smap/Lua/Client/Mgr/PlayerCam.lua new file mode 100644 index 0000000..28a6242 --- /dev/null +++ b/Smap/Lua/Client/Mgr/PlayerCam.lua @@ -0,0 +1,75 @@ +--- 角色镜头模块 +--- @module Player Cam Module +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local PlayerCam, this = ModuleUtil.New('PlayerCam', ClientBase) + +--- 初始化 +function PlayerCam:Init() + --print('[PlayerCam] Init()') + this:DataInit() + this:InitCamera() +end + +--- 数据变量初始化 +function PlayerCam:DataInit() + -- 当前的相机 + this.curCamera = nil + + -- 玩家跟随相机 + this.playerGameCam = localPlayer.Local.Independent.GameCam +end + +function PlayerCam:InitCamera() + if not this.curCamera and this.playerGameCam then + this.curCamera = this.playerGameCam + end + this.playerGameCam.LookAt = localPlayer + world.CurrentCamera = this.curCamera +end + +-- 玩家移动方向是否遵循玩家摄像机方向 +function PlayerCam:IsFreeMode() + return (this.curCamera.CameraMode == Enum.CameraMode.Social and this.curCamera.Distance >= 0) or + this.curCamera.CameraMode == Enum.CameraMode.Orbital or + this.curCamera.CameraMode == Enum.CameraMode.Custom or + this.curCamera.CameraMode == Enum.CameraMode.Smart +end + +-- 滑屏转向 +function PlayerCam:CameraMove(touchInfo) + if #touchInfo == 1 then + if this:IsFreeMode() then + this.curCamera:CameraMove(touchInfo[1].DeltaPosition) + else + this.curCamera.LookAt:Rotate(0, touchInfo[1].DeltaPosition.x * 0.2, 0) + this.curCamera:CameraMove(Vector2(0, touchInfo[1].DeltaPosition.y)) + end + end +end + +-- 双指缩放摄像机距离 +function PlayerCam:CameraZoom(_pos1, _pos2, _dis, _speed) + if this.curCamera.CameraMode == Enum.CameraMode.Social then + this.curCamera.Distance = this.curCamera.Distance - _dis / 50 + end +end + +-- Fov缩放 +function PlayerCam:CameraFOVZoom(_fovChange, _maxFov) + if _fovChange > 0 and this.curCamera.FieldOfView > _maxFov + 1 then + _fovChange = -0.2 + elseif _fovChange > 0 and this.curCamera.FieldOfView > _maxFov then + _fovChange = 0 + end + this.curCamera.FieldOfView = math.clamp(this.curCamera.FieldOfView + _fovChange, 60, 90) +end + +-- 修改玩家当前相机 +function PlayerCam:SetCurCamEventHandler(_cam, _lookAt) + this.curCamera = _cam or this.playerGameCam + this.curCamera.LookAt = _lookAt or localPlayer + world.CurrentCamera = this.curCamera +end + +return PlayerCam diff --git a/Smap/Lua/Client/Mgr/PlayerCtrl.lua b/Smap/Lua/Client/Mgr/PlayerCtrl.lua new file mode 100644 index 0000000..6daeb57 --- /dev/null +++ b/Smap/Lua/Client/Mgr/PlayerCtrl.lua @@ -0,0 +1,146 @@ +--- 角色控制模块 +--- @module Player Ctrl Module +--- @copyright Lilith Games, Avatar Team +--- @author Dead Ratman +local PlayerCtrl, this = ModuleUtil.New('PlayerCtrl', ClientBase) + +--声明变量 +local isDead = false +local forwardDir = Vector3.Forward +local rightDir = Vector3.Right +local horizontal = 0 +local vertical = 0 + +-- PC端交互按键 +local FORWARD_KEY = Enum.KeyCode.W +local BACK_KEY = Enum.KeyCode.S +local LEFT_KEY = Enum.KeyCode.A +local RIGHT_KEY = Enum.KeyCode.D +local JUMP_KEY = Enum.KeyCode.Space +local SPRINT_KEY = Enum.KeyCode.LeftShift +local UP_KEY = Enum.KeyCode.Q +local DOWN_KEY = Enum.KeyCode.E +local CROUCH_KEY = Enum.KeyCode.C +local FLY_KEY = Enum.KeyCode.F + +-- 键盘的输入值 +local moveForwardAxis = 0 +local moveBackAxis = 0 +local moveLeftAxis = 0 +local moveRightAxis = 0 +local upAxis = 0 +local downAxis = 0 + +--- 初始化 +function PlayerCtrl:Init() + ----print('[PlayerCtrl] Init()') + this:DataInit() + this:EventBind() +end + +--- 数据变量初始化 +function PlayerCtrl:DataInit() + this.finalDir = Vector3.Zero + this.isSprint = true + this.upright = 0 +end + +--- 节点事件绑定 +function PlayerCtrl:EventBind() + -- Keyboard Input + Input.OnKeyDown:Connect( + function() + if Input.GetPressKeyData(JUMP_KEY) == 1 then + this:PlayerJump() + end + if Input.GetPressKeyData(CROUCH_KEY) == 1 then + this:PlayerCrouch() + end + if Input.GetPressKeyData(FLY_KEY) == 1 then + this:PlayerFly() + end + end + ) + localPlayer.OnCollisionBegin:Connect( + function(_hitObj, _hitPos) + if _hitObj.Name == 'SitPoint' then + this:PlayerSit(_hitObj) + end + end + ) +end + +--获取按键盘时的移动方向最终取值 +function GetKeyValue() + moveForwardAxis = Input.GetPressKeyData(FORWARD_KEY) > 0 and 1 or 0 + moveBackAxis = Input.GetPressKeyData(BACK_KEY) > 0 and -1 or 0 + moveLeftAxis = Input.GetPressKeyData(LEFT_KEY) > 0 and 1 or 0 + moveRightAxis = Input.GetPressKeyData(RIGHT_KEY) > 0 and -1 or 0 + this.isSprint = true --Input.GetPressKeyData(SPRINT_KEY) == 2 + upAxis = Input.GetPressKeyData(UP_KEY) == 2 and 1 or 0 + downAxis = Input.GetPressKeyData(DOWN_KEY) == 2 and 1 or 0 + if localPlayer.State == Enum.CharacterState.Died then + moveForwardAxis, moveBackAxis, moveLeftAxis, moveRightAxis = 0, 0, 0, 0 + end +end + +-- 获取移动方向 +function GetMoveDir() + forwardDir = C.Mgr.PlayerCam:IsFreeMode() and C.Mgr.PlayerCam.curCamera:GetDeferredForward() or localPlayer.Forward + forwardDir.y = 0 + rightDir = Vector3(0, 1, 0):Cross(forwardDir) + horizontal = C.Mgr.GuiControl.joystick.Horizontal + vertical = C.Mgr.GuiControl.joystick.Vertical + this.upright = upAxis - downAxis + if horizontal ~= 0 or vertical ~= 0 then + this.finalDir = rightDir * horizontal + forwardDir * vertical + else + GetKeyValue() + this.finalDir = forwardDir * (moveForwardAxis + moveBackAxis) - rightDir * (moveLeftAxis + moveRightAxis) + if this.finalDir.Magnitude > 0 then + this.finalDir = this.finalDir.Normalized + end + end +end + +-- 跳跃逻辑 +function PlayerCtrl:PlayerJump() + --print('jumpCount', FsmMgr.playerActCtrl.jumpCount) + FsmMgr.playerActCtrl:CallTrigger('SitEndState') + FsmMgr.playerActCtrl:CallTrigger('LieEndState') + if FsmMgr.playerActCtrl.jumpCount == 3 then + FsmMgr.playerActCtrl:CallTrigger('JumpBeginState') + elseif FsmMgr.playerActCtrl.jumpCount == 2 then + FsmMgr.playerActCtrl:CallTrigger('DoubleJumpState') + elseif FsmMgr.playerActCtrl.jumpCount == 1 then + --FsmMgr.playerActCtrl:CallTrigger('DoubleJumpSprintState') + FsmMgr.playerActCtrl:CallTrigger('JumpBeginState') + end +end + +-- 下蹲逻辑 +function PlayerCtrl:PlayerCrouch() + FsmMgr.playerActCtrl:SwitchCrouch() +end + +-- 飞行逻辑 +function PlayerCtrl:PlayerFly() + FsmMgr.playerActCtrl:CallTrigger('FlyBeginState') +end + +-- 坐下逻辑 +function PlayerCtrl:PlayerSit(_sitPoint) + FsmMgr.playerActCtrl.seatObj = _sitPoint + FsmMgr.playerActCtrl:CallTrigger('SitBeginState') +end + +function PlayerCtrl:Test(_id) + FsmMgr.playerActCtrl:GetActInfo(Xls.ActAnim[_id]) + FsmMgr.playerActCtrl:CallTrigger('ActBeginState') +end + +function PlayerCtrl:Update(dt) + GetMoveDir() +end + +return PlayerCtrl diff --git a/Code/['World']['Client']['Module']['PlayerGuiDefaultModule'].ModuleScript.lua b/Smap/Lua/Client/Mgr/PlayerGuiDefault.lua similarity index 72% rename from Code/['World']['Client']['Module']['PlayerGuiDefaultModule'].ModuleScript.lua rename to Smap/Lua/Client/Mgr/PlayerGuiDefault.lua index 9b52db4..dee7e31 100644 --- a/Code/['World']['Client']['Module']['PlayerGuiDefaultModule'].ModuleScript.lua +++ b/Smap/Lua/Client/Mgr/PlayerGuiDefault.lua @@ -7,17 +7,17 @@ local PlayerGuiDefault, this = ModuleUtil.New('PlayerGuiDefault', ClientBase) local player -- 姓名板 -local nameGui +local nameGUI -- 血条 -local healthGui, background, healthBar +local healthGUI, background, healthBar local RED_BAR = ResourceManager.GetTexture('Internal/Blood_Red') local GREEN_BAR = ResourceManager.GetTexture('Internal/Blood_Green') local ORANGE_BAR = ResourceManager.GetTexture('Internal/Blood_Orange') local HIT_LAST_TIME = 2 local healthBarShowTime = 0 -function PlayerGuiDefault:Awake() +function PlayerGuiDefault:Init() -- 获取本地玩家 player = localPlayer self:InitNameGui() @@ -27,29 +27,30 @@ end -- 姓名板 function PlayerGuiDefault:InitNameGui() - nameGui = player.NameGui - nameGui.NameBarTxt.Text = player.Name + nameGUI = player.NameGui + nameGUI.NameBarTxt1.Text = player.Name + nameGUI.NameBarTxt2.Text = player.Name end -- 血条 function PlayerGuiDefault:InitHealthBarGui() - healthGui = player.HealthGui - background = healthGui.BackgroundImg - healthBar = background.HealthBarImg + healthGUI = player.HealthGui + --background = healthGUI.BackgroundImg + --healthBar = background.HealthBarImg end -- 初始化事件 function PlayerGuiDefault:InitListener() player.OnHealthChange:Connect(HealthChange) - world.OnRenderStepped:Connect(MainGui) + world.OnRenderStepped:Connect(MainGUI) end -- 姓名板的显示逻辑 function NameBarLogic() - nameGui.Visible = player.DisplayName + nameGUI.Visible = player.DisplayName if player.DisplayName then - local addedHeight = (healthGui and healthGui.ActiveSelf) and 1.1 or 1 - nameGui.LocalPosition = Vector3(0, addedHeight + player.Avatar.Height, 0) + local addedHeight = (healthGUI and healthGUI.ActiveSelf) and 1.1 or 1 + nameGUI.LocalPosition = Vector3(0, addedHeight + player.Avatar.Height, 0) end end @@ -73,18 +74,18 @@ end function HealthBarLogic(_delta) healthBarShowTime = healthBarShowTime - _delta if player.HealthDisplayMode == Enum.HealthDisplayMode.Always then - healthGui.Visible = true + healthGUI.Visible = true elseif player.HealthDisplayMode == Enum.HealthDisplayMode.Never then - healthGui.Visible = false + healthGUI.Visible = false elseif player.HealthDisplayMode == Enum.HealthDisplayMode.OnHit then - healthGui.Visible = player.Health ~= player.MaxHealth + healthGUI.Visible = player.Health ~= player.MaxHealth else - healthGui.Visible = healthBarShowTime > 0 + healthGUI.Visible = healthBarShowTime > 0 end end -- 每个渲染帧更新姓名板和血条的显示逻辑 -function MainGui(_delta) +function MainGUI(_delta) NameBarLogic() HealthBarLogic(_delta) end diff --git a/Smap/Lua/Common/Define/Const.lua b/Smap/Lua/Common/Define/Const.lua new file mode 100644 index 0000000..69498ea --- /dev/null +++ b/Smap/Lua/Common/Define/Const.lua @@ -0,0 +1,21 @@ +--- 全局常量的定义,全部定义在Const这张表下面, +--- 用于定义全局常量参数或者枚举类型 +---! 在其他文件调用为 K.XXXX 例如:K.MAX_PLAYERS +--- @module Constant Defines +--- @copyright Lilith Games, Avatar Team +local Const = {} + +_G.K = Const + +-- e.g. (need DELETE) +Const.MAX_PLAYERS = 4 + +--语言枚举 +Const.LanguageEnum = { + CHS = 'CHS', -- 简体中文 + CHT = 'CHT', -- 繁体中文 + EN = 'EN', -- 英文 + JP = 'JP' -- 日文 +} + +return Const diff --git a/Smap/Lua/Common/Manifest.lua b/Smap/Lua/Common/Manifest.lua new file mode 100644 index 0000000..49b8b83 --- /dev/null +++ b/Smap/Lua/Common/Manifest.lua @@ -0,0 +1,34 @@ +--- 服务器/客户端文件路径配置 +--- @module Manifest, Both-side +--- @copyright Lilith Games, Avatar Team +local Manifest = {} + +Manifest.ROOT_PATH = 'Lua/Common/' + +Manifest.Modules = { + { + Name = 'Define', + Modules = { + 'Const' + } + }, + { + Name = 'Xls', + Modules = { + 'ActAnim', + 'Example', + 'GlobalSetting' + } + }, + { + Name = 'Util', + Modules = { + 'Uuid', + 'SoundUtil', + 'CloudLogUtil', + 'TimeUtil' + } + } +} + +return Manifest diff --git a/Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua b/Smap/Lua/Common/Util/CloudLogUtil.lua similarity index 78% rename from Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua rename to Smap/Lua/Common/Util/CloudLogUtil.lua index 65f6636..302dd64 100644 --- a/Code/['World']['Global']['Utility']['CloudLogUtilModule'].ModuleScript.lua +++ b/Smap/Lua/Common/Util/CloudLogUtil.lua @@ -1,7 +1,7 @@ --- 埋点数据工具 --- @module CloudLogUtil --- @copyright Lilith Games, Avatar Team ---- @author Sharif Ma, Xinwu Zhang +--- @author Sharif Ma local CloudLogUtil = {} ---埋点工具初始化 @@ -12,12 +12,12 @@ end ---触发埋点相应的事件调用 ---@param _key string 埋点的键 -function CloudLogUtil.UploadLog(_key, _table) - local arg = LuaJsonUtil:encode(_table) +function CloudLogUtil.UploadLog(_key, _table, _PlayerId) + local arg = JSON:encode(_table) if localPlayer then - TrackService.CloudLogFromClient({_key, CloudLogUtil.gameId, arg}) + TrackService.CloudLogFromClient({_key, CloudLogUtil.gameId, arg, _PlayerId}) else - TrackService.CloudLogFromServer({_key, CloudLogUtil.gameId, arg}) + TrackService.CloudLogFromServer({_key, CloudLogUtil.gameId, arg, _PlayerId}) end end diff --git a/Smap/Lua/Common/Util/SoundUtil.lua b/Smap/Lua/Common/Util/SoundUtil.lua new file mode 100644 index 0000000..549630b --- /dev/null +++ b/Smap/Lua/Common/Util/SoundUtil.lua @@ -0,0 +1,95 @@ +--- 音效播放模块 +--- @module SoundUtil +--- @copyright Lilith Games, Avatar Team +--- @author Sharif Ma +local SoundUtil = {} + +function SoundUtil:Init() + print('[SoundUtil] Init()') + self.SoundPlaying = {} + self.Table_Sound = Xls.Sound +end + +---创建一个新音效并播放 +---@param _ID number 音效的ID +---@param _SoundSourceObj Object 音效的挂载物体,不填则为2D音效,挂载在主摄像机上 +function SoundUtil:PlaySound(_ID, _SoundSourceObj) + local Info, _Duration + _SoundSourceObj = _SoundSourceObj or world.CurrentCamera + Info = self.Table_Sound[_ID] + assert(Info, '[SoundUtil] 表中不存在该ID的音效') + _Duration = Info.Duration + local sameSoundPlayingNum = 0 + for k, v in pairs(self.SoundPlaying) do + if v[1] == _ID then + sameSoundPlayingNum = sameSoundPlayingNum + 1 + end + end + if sameSoundPlayingNum > 0 and not Info.CoverPlay then + print(string.format('[SoundUtil] %s音效CoverPlay字段为false,不能覆盖播放', _ID)) + return + end + + local Audio = world:CreateObject('AudioSource', 'Audio_' .. Info.FileName, _SoundSourceObj) + Audio.LocalPosition = Vector3.Zero + Audio.SoundClip = ResourceManager.GetSoundClip('Audio/' .. Info.FileName) + Audio.Volume = Info.Volume + Audio.MaxDistance = 10 + Audio.MinDistance = 10 + Audio.Loop = Info.IsLoop + Audio:Play() + + ---------------------------------------------------------------------- + --- 临时方案 + --- 在播放下一个bgm的时候,将之前的bgm销毁 + if string.sub(_ID, 1, 3) == 'Bgm' then + for k, v in pairs(self.SoundPlaying) do + if string.sub(v[1], 1, 3) == 'Bgm' then + v[2]:Destroy() + table.remove(self.SoundPlaying, k) + end + end + Audio.PlayMode = 2 + end + ----------------------------------------------------------------------- + table.insert(self.SoundPlaying, {_ID, Audio}) + + --如果声音不循环,则在播放完成后及时删除 + if not Audio.Loop then + _Duration = _Duration or 1 + invoke( + function() + if Audio then + Audio:Destroy() + end + end, + _Duration + ) + invoke( + function() + for k, v in pairs(self.SoundPlaying) do + if v[1] == _ID then + table.remove(self.SoundPlaying, k) + end + end + end, + _Duration + ) + end +end + +---结束音效的播放 +---@param _ID number 音效的ID +---@param _SoundSourceObj Object 音效的挂载物体,不填则为2D音效,挂载在主摄像机上 +function SoundUtil:StopSound(_ID, _SoundSourceObj) + _SoundSourceObj = _SoundSourceObj or world.CurrentCamera + ---可能会 + for k, v in pairs(self.SoundPlaying) do + if v[1] == _ID and v[2].Parent == _SoundSourceObj then + v[2]:Destroy() + table.remove(self.SoundPlaying, k) + end + end +end + +return SoundUtil diff --git a/Smap/Lua/Common/Util/TimeUtil.lua b/Smap/Lua/Common/Util/TimeUtil.lua new file mode 100644 index 0000000..65d83de --- /dev/null +++ b/Smap/Lua/Common/Util/TimeUtil.lua @@ -0,0 +1,149 @@ +--- 时间管理器模块 +-- @module Module Time Manager +-- @copyright Lilith Games, Avatar Team +-- @author Bingyun Chen, Yuancheng Zhang +-- @see the functions defined by JavaScript syntax + +local TimeUtil = {} + +-- All registered events +local eventList = {} + +-- Current active event list +local activeEvents = {} + +local running = false + +-- Set update delta time +local DELTA_TIME = .05 + +--- Find all registered events to trigger +local function CheckEvents() + -- now = os.time() + local now = Timer.GetTimeMillisecond() + local i, event = 1 + while i <= #eventList do + event = eventList[i] + if event.triggerTime <= now then + table.insert(activeEvents, event) + if event.loop then + event.triggerTime = event.triggerTime + event.delay + i = i + 1 + else + table.remove(eventList, i) + end + else + i = i + 1 + end + end +end + +--- Trigger events +local function TriggerEvents() + local i = 1 + while i <= #activeEvents do + event = activeEvents[i] + invoke(event.func) + table.remove(activeEvents, i) + end +end + +--- Update +local function StartUpdate() + while running do + -- print(os.time()) + CheckEvents() + TriggerEvents() + wait(DELTA_TIME) + end +end + +--- Initialization +function TimeUtil.Init() + TimeUtil.Start() +end + +--- Run Update() +function TimeUtil.Start() + running = true + invoke(StartUpdate) +end + +--- Stop Update() +function TimeUtil.Stop() + running = false +end + +--- Call a function after a specified number of milliseconds, +-- use ClearTimeout() method to prevent the function from running +-- @param _func execution function to call +-- @param _delayTime +-- @return timer id +-- @see https://www.w3schools.com/jsref/met_win_settimeout.asp +function TimeUtil.SetTimeout(_func, _seconds) + assert(_func, '[TimeUtil] TimeUtil.SetTimeout() _func 不能为空') + assert(_seconds >= 0, '[TimeUtil] TimeUtil.SetTimeout() 延迟时间需大于等于0') + if _seconds == 0 then + print('[TimeUtil] TimeUtil.SetTimeout() 事件立即执行') + invoke(_func) + return + end + local id = #eventList + 1 + -- convert to milliseconds + local ms = math.floor(_seconds * 1000) + local timestamp = ms + Timer.GetTimeMillisecond() + table.insert( + eventList, + { + id = id, + func = _func, + delay = ms, + triggerTime = timestamp + } + ) + return id +end + +--- Call a function or evaluates an expression at specified intervals (in milliseconds), +-- the method will continue calling the function until ClearInterval() is called, or the game is over. +-- @param _func execution function to call +-- @param _delayTime +-- @return timer id +-- @see https://www.w3schools.com/jsref/met_win_setinterval.asp +function TimeUtil.SetInterval(_func, _seconds) + assert(_func, '[TimeUtil] TimeUtil.SetInterval() _func 不能为空') + assert(_seconds > 0, '[TimeUtil] TimeUtil.SetInterval() 延迟时间需大于0') + local id = #eventList + 1 + -- convert to milliseconds + local ms = math.floor(_seconds * 1000) + local timestamp = ms + Timer.GetTimeMillisecond() + table.insert( + eventList, + { + id = id, + func = _func, + delay = ms, + triggerTime = timestamp, + loop = true + } + ) + return id +end + +--- Clear a timer set with the SetTimeout() method +-- @param _id timmer id +-- @see https://www.w3schools.com/jsref/met_win_cleartimeout.asp +function TimeUtil.ClearTimeout(_id) + for k, e in pairs(eventList) do + if e.id == _id then + table.remove(eventList, k) + break + end + end +end + +--- Clear a timer set with the SetInterval() method, used as ClearTimeout() +-- @see https://www.w3schools.com/jsref/met_win_clearinterval.asp +TimeUtil.ClearInterval = TimeUtil.ClearTimeout + +return TimeUtil diff --git a/Code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua b/Smap/Lua/Common/Util/Uuid.lua similarity index 100% rename from Code/['World']['Global']['Utility']['UuidModule'].ModuleScript.lua rename to Smap/Lua/Common/Util/Uuid.lua diff --git a/Smap/Lua/Common/Xls/ActAnim.lua b/Smap/Lua/Common/Xls/ActAnim.lua new file mode 100644 index 0000000..1cd06f8 --- /dev/null +++ b/Smap/Lua/Common/Xls/ActAnim.lua @@ -0,0 +1,153 @@ +--- This file is generated by ava-x2l.exe, +--- Don't change it manaully. +--- @copyright Lilith Games, Project Da Vinci(Avatar Team) +--- @see Official Website: https://www.projectdavinci.com/ +--- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava +--- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua +--- source file: ./Xls/ActAnimations.xlsx + +local ActAnimXls = { + [1] = { + ID = 1, + Mode = 1, + ShowName = '鞠躬', + --anim = {'anim_human_social_applause_begin', 'anim_human_social_applause_loop', 'anim_human_social_applause_end'}, + anim = {'Idle', 'SocialBow', 'Idle'}, + dur = {0.3, 2, 0.3}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [2] = { + ID = 2, + Mode = 1, + ShowName = '打招呼', + anim = {'Idle', 'Exhibition12', 'Idle'}, + dur = {0.01, 2, 0.01}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [3] = { + ID = 3, + Mode = 1, + ShowName = '落败', + anim = {'anim_human_social_fail_begin', 'anim_human_social_fail_loop', 'anim_human_social_fail_end'}, + dur = {0.8, -1, 0.6}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [4] = { + ID = 4, + Mode = 1, + ShowName = '大笑', + anim = {'anim_human_social_laughing_begin', 'anim_human_social_laughing_loop', 'anim_human_social_laughing_end'}, + dur = {0.5, 2, 1}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [5] = { + ID = 5, + Mode = 1, + ShowName = '鼓掌', + anim = {'anim_human_social_applause_begin', 'anim_human_social_applause_loop', 'anim_human_social_applause_end'}, + dur = {0.3, -1, 0.3}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [6] = { + ID = 6, + Mode = 1, + ShowName = '装死', + anim = {'anim_human_social_playdead_begin', 'anim_human_social_playdead_loop', 'anim_human_social_playdead_end'}, + dur = {3, -1, 0.5}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [7] = { + ID = 7, + Mode = 2, + ShowName = '挥手', + anim = {'anim_human_sit_wave_begin', 'anim_human_sit_wave', 'anim_human_sit_wave_end'}, + dur = {0.3, -1, 0.5}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [8] = { + ID = 8, + Mode = 2, + ShowName = '翘腿', + anim = {'anim_human_sit_crosslegs_begin', 'anim_human_sit_crosslegs', 'anim_human_sit_crosslegs_end'}, + dur = {1.5, -1, 1}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [9] = { + ID = 9, + Mode = 2, + ShowName = '托腮', + anim = {'anim_human_sit_doubt_begin', 'anim_human_sit_doubt', 'anim_human_sit_doubt_end'}, + dur = {1, -1, 0.5}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + }, + [10] = { + ID = 10, + Mode = 2, + ShowName = '助威', + anim = {'anim_human_sit_cheer_begin', 'anim_human_sit_cheer', 'anim_human_sit_cheer_end'}, + dur = {0.3, -1, 0.3}, + speed = 1.0, + layer = 0, + transIn = 0.2, + transOut = 0.2, + isInterrupt = true, + isLoop = true, + speedScale = 1.0 + } +} + +return ActAnimXls diff --git a/Smap/Lua/Common/Xls/Example.lua b/Smap/Lua/Common/Xls/Example.lua new file mode 100644 index 0000000..4094994 --- /dev/null +++ b/Smap/Lua/Common/Xls/Example.lua @@ -0,0 +1,264 @@ +--- This file is generated by ava-x2l.exe, +--- Don't change it manaully. +--- @copyright Lilith Games, Project Da Vinci(Avatar Team) +--- @see Official Website: https://www.projectdavinci.com/ +--- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava +--- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua +--- source file: ./xls/Example.xlsx + +local ExampleXls = { + [1] = { + CameraID = 1, + StepNum = 1, + OriginPos = Vector3(-42.3148, 45.8259, -1144.97), + OriginRot = EulerDegree(21.8319, 20.51, 0), + FinalPos = Vector3(-21.3148, 40, -1132.97), + FinalRot = EulerDegree(21.8319, 10, 0), + Time = 4.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step1', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 4.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue1_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 1, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [2] = { + CameraID = 2, + StepNum = 2, + OriginPos = Vector3(-31.9448, 17.6902, -1044.29), + OriginRot = EulerDegree(26.3007, 44.2602, 0), + FinalPos = Vector3(23, 18, -1040.5), + FinalRot = EulerDegree(28, -34, 0), + Time = 4.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step1.5', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 4.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue2_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 2, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [3] = { + CameraID = 3, + StepNum = 3, + OriginPos = Vector3(5.5, -0.4, -1044.79), + OriginRot = EulerDegree(15.8007, 2, 0), + FinalPos = Vector3(5.5, 0.8, -1044.79), + FinalRot = EulerDegree(15.8007, 2, 0), + Time = 4.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step2', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 4.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue3_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 3, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [4] = { + CameraID = 4, + StepNum = 4, + OriginPos = Vector3(24.5552, -0.5, -1022.79), + OriginRot = EulerDegree(358.801, 324.76, 0), + FinalPos = Vector3(25.5552, 0, -1024.79), + FinalRot = EulerDegree(358.801, 322, 0), + Time = 4.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step3', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 4.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue4_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 4, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [5] = { + CameraID = 5, + StepNum = 5, + OriginPos = Vector3(-6.2712, 0.8, -1023.84), + OriginRot = EulerDegree(358.27, 0.0102, 0), + FinalPos = Vector3(-0.7712, 0.8, -1023.84), + FinalRot = EulerDegree(358.27, 0.0102, 0), + Time = 3.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step4', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 3.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue5_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 5, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [6] = { + CameraID = 6, + StepNum = 6, + OriginPos = Vector3(20.7532, 2.2, -1014.26), + OriginRot = EulerDegree(0.5508, 297.885, 0), + FinalPos = Vector3(23.2532, 2.2, -1007.76), + FinalRot = EulerDegree(0.5508, 297.885, 0), + Time = 3.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step5', + SubtitleText = '', + SubtitleOccurTime = 0.0, + SubtitleLastingTime = 3.0, + SubtitleBackgroundImg = 'UI/BeginnerText_Icon/Prototype_Dialogue6_Img', + SubtitleEffectTime = 0.5, + SubtitleCurve = 6, + SubtitleAnchorsX = Vector2(0, 0), + SubtitleAnchorsY = Vector2(0, 0), + SubtitleOffset = Vector2(373.5, 154) + }, + [7] = { + CameraID = 7, + StepNum = 7, + OriginPos = Vector3(8.7951, 13.2, -1029.72), + OriginRot = EulerDegree(18.8008, 340.947, 0), + FinalPos = Vector3(8.7951, 7.7, -1029.72), + FinalRot = EulerDegree(18.8008, 340.947, 0), + Time = 2.0, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step6', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + }, + [8] = { + CameraID = 8, + StepNum = 8, + OriginPos = Vector3(8.7951, 7.7, -1029.72), + OriginRot = EulerDegree(16.79, 342.4, 0), + FinalPos = Vector3(7.1548, 6, -1027.81), + FinalRot = EulerDegree(14.78, 343.85, 0), + Time = 0.6, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step7', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + }, + [9] = { + CameraID = 9, + StepNum = 9, + OriginPos = Vector3(7.1548, 6, -1027.81), + OriginRot = EulerDegree(14.78, 343.85, 0), + FinalPos = Vector3(6.1548, 4.4, -1025.61), + FinalRot = EulerDegree(12.78, 345.3, 0), + Time = 0.6, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step7', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + }, + [10] = { + CameraID = 10, + StepNum = 10, + OriginPos = Vector3(6.1548, 4.4, -1025.61), + OriginRot = EulerDegree(12.78, 345.3, 0), + FinalPos = Vector3(5.1548, 2.8, -1023.41), + FinalRot = EulerDegree(10.77, 346.75, 0), + Time = 0.6, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step7', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + }, + [11] = { + CameraID = 11, + StepNum = 11, + OriginPos = Vector3(5.1548, 2.8, -1023.41), + OriginRot = EulerDegree(10.77, 346.75, 0), + FinalPos = Vector3(4.5548, 2.1, -1021.61), + FinalRot = EulerDegree(8.77, 348.1, 0), + Time = 0.6, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step7', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + }, + [12] = { + CameraID = 12, + StepNum = 12, + OriginPos = Vector3(4.5548, 2.1, -1021.61), + OriginRot = EulerDegree(8.77, 348.1, 0), + FinalPos = Vector3(3.9548, 1.6, -1020.11), + FinalRot = EulerDegree(6.7695, 349.635, 0), + Time = 0.6, + Curve = 1, + Environment = 'Tutorial', + SceneName = 'Step7', + SubtitleText = '', + SubtitleOccurTime = nil, + SubtitleLastingTime = nil, + SubtitleBackgroundImg = '', + SubtitleEffectTime = nil, + SubtitleCurve = nil, + SubtitleAnchorsX = nil, + SubtitleAnchorsY = nil, + SubtitleOffset = nil + } +} + +return ExampleXls diff --git a/Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua b/Smap/Lua/Common/Xls/GlobalSetting.lua similarity index 50% rename from Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua rename to Smap/Lua/Common/Xls/GlobalSetting.lua index 34c4b32..79ccf24 100644 --- a/Code/['World']['Global']['Xls']['GlobalSettingXlsModule'].ModuleScript.lua +++ b/Smap/Lua/Common/Xls/GlobalSetting.lua @@ -4,14 +4,22 @@ --- @see Official Website: https://www.projectdavinci.com/ --- @see Dev Framework: https://github.com/lilith-avatar/avatar-ava --- @see X2L Tool: https://github.com/lilith-avatar/avatar-ava-xls2lua ---- source file: ./Xls/GlobalSetting.xls +--- source file: ./xls/Example.xlsx local GlobalSettingXls = { DefaultLanguage = "CHS", - PlayerPosition = Vector3(0,-1,0), - PlayerRotation = Euler(90,0,0), - MaxPlayerNumber = 100.0, - ScoreRate = 12.5 + TileWith = 1.0, + FoundationHeight = 0.0, + LevelHeight = 4.0, + DefaultInnerRowCol = Vector2(23, 24), + DefaultOutterRowCol = Vector2(30, 30), + InstanceFramingThreshold = 20.0, + DestoryFramingThreshold = 50.0, + RoofOffset = Vector3(0.5, 0, 0), + CurentDataVer = 0.8, + AutoSaveThreshold = 60.0, + Party2EditorOffset = {0,0,-1000}, + SlotNum = 3.0 } return GlobalSettingXls diff --git a/Smap/Lua/Server/Manifest.lua b/Smap/Lua/Server/Manifest.lua new file mode 100644 index 0000000..302fc6c --- /dev/null +++ b/Smap/Lua/Server/Manifest.lua @@ -0,0 +1,12 @@ +--- 服务器文件路径配置 +--- @module Manifest, Server-side +--- @copyright Lilith Games, Avatar Team +local Manifest = {} + +Manifest.ROOT_PATH = 'Lua/Server/' + +Manifest.Events = {} + +Manifest.Modules = {} + +return Manifest \ No newline at end of file diff --git a/Smap/avatar-ava.smap b/Smap/avatar-ava.smap deleted file mode 100644 index 2f4c8d7..0000000 Binary files a/Smap/avatar-ava.smap and /dev/null differ diff --git a/Xls/Example.xlsx b/Xls/Example.xlsx new file mode 100644 index 0000000..037a2da --- /dev/null +++ b/Xls/Example.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ded9fa96e6dc136dc8bdc51cff8cbe15382fdd5a16144561a0340666a4a51c60 +size 14471 diff --git a/Xls/ExampleTable1.xls b/Xls/ExampleTable1.xls deleted file mode 100644 index 6b02c5d..0000000 --- a/Xls/ExampleTable1.xls +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:97231d6bffc93b960f6faddb343335e5d34f5edab26cad8febf3134281f0fb5e -size 38912 diff --git a/Xls/ExampleTable1.xlsx b/Xls/ExampleTable1.xlsx deleted file mode 100644 index da90a5e..0000000 --- a/Xls/ExampleTable1.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a2b1df1b741ae1e77fd025994f4081103fabc3b7d26e99221fb83ca2f7730e55 -size 14896 diff --git a/Xls/GlobalSetting.xls b/Xls/GlobalSetting.xls deleted file mode 100644 index 432b47b..0000000 --- a/Xls/GlobalSetting.xls +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6b2f8b7bfeceefe33ad3f3e35e28b3b258c684dc4a4398e45d5929c48df58ba1 -size 24064 diff --git a/Xls/LanguagePack.xls b/Xls/LanguagePack.xls deleted file mode 100644 index 2c020e3..0000000 --- a/Xls/LanguagePack.xls +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f6463250613591736ae1da46ac0ecac332617da498bcb7a7e4417df7f57080a4 -size 5632 diff --git a/Xls/Sound.xls b/Xls/Sound.xls deleted file mode 100644 index d247fda..0000000 --- a/Xls/Sound.xls +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8a37659cba95b2aa8c4f1c62907a7cbc6075ebf165e2b1323823092770fb1414 -size 27136 diff --git a/Xls/localization.csv b/Xls/localization.csv new file mode 100644 index 0000000..11b65ee --- /dev/null +++ b/Xls/localization.csv @@ -0,0 +1,69 @@ +LocalizeKey,Note,Default,en,zh,hi +HomeSystem_Intro_Des_1,,ӭ԰ϵͳѧ̣,Welcome to the tutorial for Home building!,ӭ԰ϵͳѧ̣,Selamat datang di tutorial pembangunan Rumah! +HomeSystem_Intro_Des_2,,ʾĵطߣィһǽ,Draw a line as shown to give your Home a wall.,ʾĵطߣィһǽ,Gambar garis seperti yang ditunjukkan untuk menambahkan dinding ke Rumahmu. +HomeSystem_Intro_Des_3,,ǽɹ,Awesome! Youve built a wall!,ǽɹ,Keren! Kamu berhasil membuat dinding! +HomeSystem_Intro_Des_4,,ڣΪҵذͿɫ,"Now, try paint the floor.",ڣΪҵذͿɫ,Sekarang coba cat lantainya. +HomeSystem_Intro_Des_5,,߻ΪذͿɫ!,Tap or swipe to add color to the floor.,߻ΪذͿɫ!,Ketuk atau usap untuk menambahkan warna ke lantai. +HomeSystem_Intro_Des_6,,Ϳɫɹ!,Painted!,Ϳɫɹ!,Sudah dicat! +HomeSystem_Intro_Des_7,,ڣǵȥһ,"Now, lets check inside your Home!",ڣǵȥһ,Ayo lihat isi dalam Rumahmu! +HomeSystem_Intro_Des_8,,Ե򻬶ǽɫ,Try tapping or swiping to paint the wall!,Ե򻬶ǽɫ,Coba ketuk atau usap untuk mengecat dinding! +HomeSystem_Intro_Des_9,,ɫɹڼŵҾ߰ɣ,"Nice, now lets make your Home cozier with some furniture!",ɫɹڼŵҾ߰ɣ,"Bagus, ayo buat Rumahmu menjadi lebih nyaman dengan perabot!" +HomeSystem_Intro_Des_10,,һҾߣ,Tap to build a furniture!,һҾߣ,Ketuk untuk membuat perabot! +HomeSystem_Intro_Des_15,,һţ,Tap to build a door!,һţ,Ketuk untuk membuat pintu! +HomeSystem_Intro_Des_16,,һȴ,Tap to build a window!,һȴ,Ketuk untuk membuat jendela! +HomeSystem_Intro_Des_11,,ȷϷã,Tap to confirm!,ȷϷã,Ketuk untuk mengonfirmasi! +HomeSystem_Intro_Des_12,,Ҫļ԰,Important! Make sure you save your changes!,Ҫļ԰,Penting! Pastikan kamu menyimpan perubahanmu! +HomeSystem_Intro_Des_13,,ϲļ԰ѳɹ棬Ƿͬ԰ݵɶԡ,Congrats! Your Home has been saved. Sync to party mode?,ϲļ԰ѳɹ棬Ƿͬ԰ݵɶԡ,Selamat! Rumahmu sudah diselamatkan. Sinkronkan dengan mode pesta? +HomeSystem_Intro_Des_17,,ǰ԰: {1},Players online: {1},ǰ԰: {1},Pemain online: {1} +HomeSystem_Intro_Des_14,,ϲ˼԰Ľѧ̣,Congrats on completing the tutorial for Home building!,ϲ˼԰Ľѧ̣,Selamat karena telah menyelesaikan tutorial pembangunan Rumah! +HomeSystem_Intro_Des_18,,ϲļ԰ѳɹ棡,Congrats! Your Home has been saved.,ϲļ԰ѳɹ棡,Selamat! Rumahmu sudah diselamatkan. +HomeSystem_Intro_Des_19,,浵ѡ,Save Selection,浵ѡ,Simpan Pilihan +HomeSystem_Intro_Des_20,,ģѡ,Template Selection,ģѡ,Pilihan Template +HomeSystem_Intro_Des_21,,ȷҪɾ˴浵,Really delete this save?,ȷҪɾ˴浵,Yakin ingin menghapus simpanan ini? +HomeSystem_Intro_Image_1,,ǽڻ,Design wall,ǽڻ,Desain dinding +HomeSystem_Intro_Image_2,,ذͿɫ,Paint floor,ذͿɫ,Cat lantai +HomeSystem_Intro_Image_3,,ǽͿɫ,Paint wall,ǽͿɫ,Cat dinding +HomeSystem_Intro_Image_4,,Ҿ߷,Place furniture,Ҿ߷,Letakkan perabot +HomeSystem_Intro_Image_5,,۲ģʽ,Enter view mode,۲ģʽ,Buka mode lihat +HomeSystem_Intro_Image_6,,,Return,,Kembali +HomeSystem_Intro_Image_7,,ǽ,Build wall,ǽ,Bangun dinding +HomeSystem_Intro_Image_8,,ɾǽ,Delete wall,ɾǽ,Hapus dinding +HomeSystem_Intro_Image_9,,,Undo,,Urungkan +HomeSystem_Intro_Image_10,,ˢͿɫ,Paint with brush,ˢͿɫ,Cat dengan kuas +HomeSystem_Intro_Image_11,,ͰͿɫ,Paint with bucket,ͰͿɫ,Cat dengan ember +HomeSystem_Intro_Image_12,,𱳰,Close bag,𱳰,Tutup tas +HomeSystem_Intro_Image_13,,򿪱,Open bag,򿪱,Buka tas +HomeSystem_Mode_1,,ǽ,Build wall,ǽ,Bangun dinding +HomeSystem_Mode_2,,ɾǽ,Remove wall,ɾǽ,Singkirkan dinding +HomeSystem_Mode_3,,ˢ,Paintbrush,ˢ,Kuas cat +HomeSystem_Mode_4,,Ͱ,Paint bucket,Ͱ,Ember cat +HomeSystem_Mode_5,,Ϳǽ,Paint wall,Ϳǽ,Cat dinding +HomeSystem_Mode_6,,üҾ,Build furniture,üҾ,Bangun perabot +HomeSystem_Mode_7,,۲ģʽ,Overview mode,۲ģʽ,Mode tinjauan +HomeSystem_Mode_8,,ģʽ,Tour mode,ģʽ,Mode tur +HomeSystem_Mode_9,,Ŵ,Build door/window,Ŵ, +HomeSystem_Modechange_1,,ӭ뽨ģʽ,Welcome to building mode!,ӭ뽨ģʽ,Selamat datang di mode bangunan! +HomeSystem_Modechange_2,,ӭɶģʽ,Welcome to party mode!,ӭɶģʽ,Selamat datang di mode pesta! +HomeSystem_Warn_1,,ԭذɫͬ,Floor color is same as before.,ԭذɫͬ,Warna lantai sama seperti sebelumnya. +HomeSystem_Warn_2,,ԭǽɫͬ,Wall color is same as before.,ԭǽɫͬ,Warna dinding sama seperti sebelumnya. +HomeSystem_Warn_3,,˴޷,Cannot build here.,˴޷,Tidak bisa membangun di sini. +HomeSystem_Warn_4,,ѶԸ÷˸£${X}ͬ·䡣,The owner has updated the room. The room will be synced in {1}s.,ѶԸ÷˸£{1}ͬ·䡣,Pemilik telah memperbarui ruangan. Ruangan akan disinkronisasi dalam {1}dtk. +HomeSystem_Warn_5,,ýռãԺ,"Someone else is using it, try again later",ýռãԺ,"Orang lain sedang menggunakannya, coba lagi nanti" +HomeSystem_Upload_1,,ϴ,Upload,ϴ,Unggah +HomeSystem_Upload_2,,˳,Quit,˳,Keluar +HomeSystem_Intro_SkipConfirm,,Ƿȷֽѧ,Really skip tutorial?,Ƿȷֽѧ,Yakin lewati tutorial? +HomeSystem_Intro_FurnitureBag,,Ҿ߱,Furniture Bag,Ҿ߱,Perabot tas +HomeSystem_BeginnerIntro_1,,ӭREACH԰,ӭREACH԰,ӭREACH԰,ӭREACH԰ +HomeSystem_BeginnerIntro_2,,Ϊ׼һ,Ϊ׼һ,Ϊ׼һ,Ϊ׼һ +HomeSystem_BeginnerIntro_3,,ӽԺォΪרС磡,ӽԺォΪרС磡,ӽԺォΪרС磡,ӽԺォΪרС磡 +HomeSystem_BeginnerIntro_4,,ӭΪ׼ר԰ʼ֮ǰҪ¶Сijŵ,ӭΪ׼ר԰ʼ֮ǰҪ¶Сijŵ,ӭΪ׼ר԰ʼ֮ǰҪ¶Сijŵ,ӭΪ׼ר԰ʼ֮ǰҪ¶Сijŵ +ArchetypesPlayerLocalIntroGuiUploadImgUploadBtnText,,,,, +ArchetypesPlayerLocalUploadGuiUploadImgButton,,,,, +ArchetypesPlayerLocalUploadGuiUploadImgButton(0),,,,, +ArchetypesPlayerLocalControlGuiTouchBtn,,,,, +ArchetypesPlayerLocalLibrary_WallDecGuiPosFigObserBtn,,,,, +ArchetypesPlayerLocalFurnitureGuiMovement,,,,, +ArchetypesUIPrefabsWarnBarText,,Avatar,,, +ArchetypesPlayerLocalControlGuiSecondTriggerBtn3,,,,, +ArchetypesPlayerLocalControlGuiFirstTriggerButton,,,,, +ArchetypesPlayerLocalControlGuiFirstTriggerTrigger,,,,, diff --git a/ava-x2l.config b/ava-x2l.config new file mode 100644 index 0000000..a16ac5c --- /dev/null +++ b/ava-x2l.config @@ -0,0 +1,5 @@ +{ + "input_folder": "./xls", + "output_folder": "./Smap/Lua/Common/Xls", + "output_name_template": "{sheet_name}.lua" +} \ No newline at end of file diff --git a/ava-x2l.exe b/ava-x2l.exe index c39d2a7..162e34d 100644 --- a/ava-x2l.exe +++ b/ava-x2l.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a66e94c017cf3f97d43632d9a1de637e7a7ff0a9a15c15ea174a944168b5ff84 -size 18773021 +oid sha256:e585ab83d20c700de57f6189065988d48788ba56d106b5fbf3cdbf4f0c3f222e +size 18785287