diff --git a/Assets/Script/Dev/Entry.lua b/Assets/Script/Dev/Entry.lua new file mode 100644 index 000000000..03da7f9ab --- /dev/null +++ b/Assets/Script/Dev/Entry.lua @@ -0,0 +1,1664 @@ +-- [yue]: Script/Dev/Entry.yue +local App = dora.App -- 1 +local package = _G.package -- 1 +local Content = dora.Content -- 1 +local Path = dora.Path -- 1 +local DB = dora.DB -- 1 +local type = _G.type -- 1 +local View = dora.View -- 1 +local Director = dora.Director -- 1 +local Size = dora.Size -- 1 +local thread = dora.thread -- 1 +local sleep = dora.sleep -- 1 +local Vec2 = dora.Vec2 -- 1 +local Color = dora.Color -- 1 +local Buffer = dora.Buffer -- 1 +local yue = dora.yue -- 1 +local _module_0 = dora.ImGui -- 1 +local IsFontLoaded = _module_0.IsFontLoaded -- 1 +local LoadFontTTF = _module_0.LoadFontTTF -- 1 +local table = _G.table -- 1 +local Cache = dora.Cache -- 1 +local Texture2D = dora.Texture2D -- 1 +local pairs = _G.pairs -- 1 +local tostring = _G.tostring -- 1 +local string = _G.string -- 1 +local print = _G.print -- 1 +local xml = dora.xml -- 1 +local teal = dora.teal -- 1 +local wait = dora.wait -- 1 +local HttpServer = dora.HttpServer -- 1 +local Routine = dora.Routine -- 1 +local Entity = dora.Entity -- 1 +local Platformer = dora.Platformer -- 1 +local Audio = dora.Audio -- 1 +local ubox = dora.ubox -- 1 +local tolua = dora.tolua -- 1 +local collectgarbage = _G.collectgarbage -- 1 +local Wasm = dora.Wasm -- 1 +local xpcall = _G.xpcall -- 1 +local debug = _G.debug -- 1 +local math = _G.math -- 1 +local Label = dora.Label -- 1 +local Button = _module_0.Button -- 1 +local SetNextWindowPosCenter = _module_0.SetNextWindowPosCenter -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local PushStyleVar = _module_0.PushStyleVar -- 1 +local Begin = _module_0.Begin -- 1 +local TextColored = _module_0.TextColored -- 1 +local SameLine = _module_0.SameLine -- 1 +local TreeNode = _module_0.TreeNode -- 1 +local TextWrapped = _module_0.TextWrapped -- 1 +local OpenPopup = _module_0.OpenPopup -- 1 +local BeginPopup = _module_0.BeginPopup -- 1 +local Selectable = _module_0.Selectable -- 1 +local Separator = _module_0.Separator -- 1 +local BeginDisabled = _module_0.BeginDisabled -- 1 +local Checkbox = _module_0.Checkbox -- 1 +local threadLoop = dora.threadLoop -- 1 +local Keyboard = dora.Keyboard -- 1 +local ipairs = _G.ipairs -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local PushStyleColor = _module_0.PushStyleColor -- 1 +local SetNextWindowBgAlpha = _module_0.SetNextWindowBgAlpha -- 1 +local Dummy = _module_0.Dummy -- 1 +local ShowStats = _module_0.ShowStats -- 1 +local ShowConsole = _module_0.ShowConsole -- 1 +local SetNextItemWidth = _module_0.SetNextItemWidth -- 1 +local InputText = _module_0.InputText -- 1 +local Columns = _module_0.Columns -- 1 +local Text = _module_0.Text -- 1 +local PushID = _module_0.PushID -- 1 +local ImageButton = _module_0.ImageButton -- 1 +local NextColumn = _module_0.NextColumn -- 1 +local SetNextItemOpen = _module_0.SetNextItemOpen -- 1 +local ScrollWhenDraggingOnVoid = _module_0.ScrollWhenDraggingOnVoid -- 1 +local _module_0 = { } -- 1 +App.idled = true -- 3 +local moduleCache = { } -- 5 +local oldRequire = _G.require -- 6 +local require -- 7 +require = function(path) -- 7 + local loaded = package.loaded[path] -- 8 + if loaded == nil then -- 9 + moduleCache[#moduleCache + 1] = path -- 10 + return oldRequire(path) -- 11 + end -- 9 + return loaded -- 12 +end -- 7 +_G.require = require -- 13 +dora.require = require -- 14 +local searchPaths = Content.searchPaths -- 16 +local useChinese = (App.locale:match("^zh") ~= nil) -- 18 +local updateLocale -- 19 +updateLocale = function() -- 19 + useChinese = (App.locale:match("^zh") ~= nil) -- 20 + searchPaths[#searchPaths] = Path(Content.assetPath, "Script", "Lib", "Dora", useChinese and "zh-Hans" or "en") -- 21 + Content.searchPaths = searchPaths -- 22 +end -- 19 +if DB:exist("Config") then -- 24 + do -- 25 + local _exp_0 = DB:query("select value_str from Config where name = 'locale'") -- 25 + local _type_0 = type(_exp_0) -- 26 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 26 + if _tab_0 then -- 26 + local locale -- 26 + do -- 26 + local _obj_0 = _exp_0[1] -- 26 + local _type_1 = type(_obj_0) -- 26 + if "table" == _type_1 or "userdata" == _type_1 then -- 26 + locale = _obj_0[1] -- 26 + end -- 28 + end -- 28 + if locale ~= nil then -- 26 + if App.locale ~= locale then -- 26 + App.locale = locale -- 27 + updateLocale() -- 28 + end -- 26 + end -- 26 + end -- 28 + end -- 28 +end -- 24 +local Config = require("Config") -- 30 +local config = Config("", "fpsLimited", "targetFPS", "fixedFPS", "vsync", "fullScreen", "winX", "winY", "winWidth", "winHeight", "themeColor", "locale", "editingInfo", "showStats", "showConsole", "showFooter", "filter") -- 31 +config:load() -- 50 +if (config.fpsLimited ~= nil) then -- 51 + App.fpsLimited = config.fpsLimited == 1 -- 52 +else -- 54 + config.fpsLimited = App.fpsLimited and 1 or 0 -- 54 +end -- 51 +if (config.targetFPS ~= nil) then -- 56 + App.targetFPS = config.targetFPS -- 57 +else -- 59 + config.targetFPS = App.targetFPS -- 59 +end -- 56 +if (config.vsync ~= nil) then -- 61 + View.vsync = config.vsync == 1 -- 62 +else -- 64 + config.vsync = View.vsync and 1 or 0 -- 64 +end -- 61 +if (config.fixedFPS ~= nil) then -- 66 + Director.scheduler.fixedFPS = config.fixedFPS -- 67 +else -- 69 + config.fixedFPS = Director.scheduler.fixedFPS -- 69 +end -- 66 +local showEntry = true -- 71 +if (function() -- 73 + local _val_0 = App.platform -- 73 + return "Linux" == _val_0 or "Windows" == _val_0 or "macOS" == _val_0 -- 73 +end)() then -- 73 + if (config.fullScreen ~= nil) and config.fullScreen == 1 then -- 74 + App.winSize = Size.zero -- 75 + elseif (config.winWidth ~= nil) and (config.winHeight ~= nil) then -- 76 + local size = Size(config.winWidth, config.winHeight) -- 77 + if App.winSize ~= size then -- 78 + App.winSize = size -- 79 + showEntry = false -- 80 + thread(function() -- 81 + sleep() -- 82 + sleep() -- 83 + showEntry = true -- 84 + end) -- 81 + end -- 78 + local winX, winY -- 85 + do -- 85 + local _obj_0 = App.winPosition -- 85 + winX, winY = _obj_0.x, _obj_0.y -- 85 + end -- 85 + if (config.winX ~= nil) then -- 86 + winX = config.winX -- 87 + else -- 89 + config.winX = 0 -- 89 + end -- 86 + if (config.winY ~= nil) then -- 90 + winY = config.winY -- 91 + else -- 93 + config.winY = 0 -- 93 + end -- 90 + App.winPosition = Vec2(winX, winY) -- 94 + end -- 74 +end -- 73 +if (config.themeColor ~= nil) then -- 96 + App.themeColor = Color(config.themeColor) -- 97 +else -- 99 + config.themeColor = App.themeColor:toARGB() -- 99 +end -- 96 +if not (config.locale ~= nil) then -- 101 + config.locale = App.locale -- 102 +end -- 101 +local showStats = false -- 104 +if (config.showStats ~= nil) then -- 105 + showStats = config.showStats > 0 -- 106 +else -- 108 + config.showStats = showStats and 1 or 0 -- 108 +end -- 105 +local showConsole = true -- 110 +if (config.showConsole ~= nil) then -- 111 + showConsole = config.showConsole > 0 -- 112 +else -- 114 + config.showConsole = showConsole and 1 or 0 -- 114 +end -- 111 +local showFooter = true -- 116 +if (config.showFooter ~= nil) then -- 117 + showFooter = config.showFooter > 0 -- 118 +else -- 120 + config.showFooter = showFooter and 1 or 0 -- 120 +end -- 117 +local filterBuf = Buffer(20) -- 122 +if (config.filter ~= nil) then -- 123 + filterBuf:setString(config.filter) -- 124 +else -- 126 + config.filter = "" -- 126 +end -- 123 +_module_0.getConfig = function() -- 128 + return config -- 128 +end -- 128 +local Set, Struct, LintYueGlobals, GSplit -- 130 +do -- 130 + local _obj_0 = require("Utils") -- 130 + Set, Struct, LintYueGlobals, GSplit = _obj_0.Set, _obj_0.Struct, _obj_0.LintYueGlobals, _obj_0.GSplit -- 130 +end -- 130 +local yueext = yue.options.extension -- 131 +local isChineseSupported = IsFontLoaded() -- 133 +if not isChineseSupported then -- 134 + LoadFontTTF("Font/sarasa-mono-sc-regular.ttf", 20, "Chinese", function() -- 135 + isChineseSupported = true -- 136 + end) -- 135 +end -- 134 +local building = false -- 138 +local getAllFiles -- 140 +getAllFiles = function(path, exts) -- 140 + local filters = Set(exts) -- 141 + local _accum_0 = { } -- 142 + local _len_0 = 1 -- 142 + local _list_0 = Content:getAllFiles(path) -- 142 + for _index_0 = 1, #_list_0 do -- 142 + local file = _list_0[_index_0] -- 142 + if not filters[Path:getExt(file)] then -- 143 + goto _continue_0 -- 143 + end -- 143 + _accum_0[_len_0] = file -- 144 + _len_0 = _len_0 + 1 -- 144 + ::_continue_0:: -- 143 + end -- 144 + return _accum_0 -- 144 +end -- 140 +local getFileEntries -- 146 +getFileEntries = function(path) -- 146 + local entries = { } -- 147 + local _list_0 = getAllFiles(path, { -- 148 + "lua", -- 148 + "xml", -- 148 + yueext, -- 148 + "tl" -- 148 + }) -- 148 + for _index_0 = 1, #_list_0 do -- 148 + local file = _list_0[_index_0] -- 148 + local entryName = Path:getName(file) -- 149 + local entryAdded = false -- 150 + for _index_1 = 1, #entries do -- 151 + local _des_0 = entries[_index_1] -- 151 + local ename = _des_0[1] -- 151 + if entryName == ename then -- 152 + entryAdded = true -- 153 + break -- 154 + end -- 152 + end -- 154 + if entryAdded then -- 155 + goto _continue_0 -- 155 + end -- 155 + local fileName = Path:replaceExt(file, "") -- 156 + fileName = Path(Path:getName(path), fileName) -- 157 + local entry = { -- 158 + entryName, -- 158 + fileName -- 158 + } -- 158 + entries[#entries + 1] = entry -- 159 + ::_continue_0:: -- 149 + end -- 159 + table.sort(entries, function(a, b) -- 160 + return a[1] < b[1] -- 160 + end) -- 160 + return entries -- 161 +end -- 146 +local getProjectEntries -- 163 +getProjectEntries = function(path) -- 163 + local entries = { } -- 164 + local _list_0 = Content:getDirs(path) -- 165 + for _index_0 = 1, #_list_0 do -- 165 + local dir = _list_0[_index_0] -- 165 + if dir:match("^%.") then -- 166 + goto _continue_0 -- 166 + end -- 166 + local _list_1 = getAllFiles(Path(path, dir), { -- 167 + "lua", -- 167 + "xml", -- 167 + yueext, -- 167 + "tl", -- 167 + "wasm" -- 167 + }) -- 167 + for _index_1 = 1, #_list_1 do -- 167 + local file = _list_1[_index_1] -- 167 + if "init" == Path:getName(file):lower() then -- 168 + local fileName = Path:replaceExt(file, "") -- 169 + fileName = Path(dir, fileName) -- 170 + local entryName = Path:getName(Path:getPath(fileName)) -- 171 + local entryAdded = false -- 172 + for _index_2 = 1, #entries do -- 173 + local _des_0 = entries[_index_2] -- 173 + local ename = _des_0[1] -- 173 + if entryName == ename then -- 174 + entryAdded = true -- 175 + break -- 176 + end -- 174 + end -- 176 + if entryAdded then -- 177 + goto _continue_1 -- 177 + end -- 177 + local examples = { } -- 178 + local tests = { } -- 179 + local examplePath = Path(path, dir, Path:getPath(file), "Example") -- 180 + if Content:exist(examplePath) then -- 181 + local _list_2 = getFileEntries(examplePath) -- 182 + for _index_2 = 1, #_list_2 do -- 182 + local _des_0 = _list_2[_index_2] -- 182 + local name, ePath = _des_0[1], _des_0[2] -- 182 + local entry = { -- 183 + name, -- 183 + Path(dir, Path:getPath(file), ePath) -- 183 + } -- 183 + examples[#examples + 1] = entry -- 184 + end -- 184 + end -- 181 + local testPath = Path(path, dir, Path:getPath(file), "Test") -- 185 + if Content:exist(testPath) then -- 186 + local _list_2 = getFileEntries(testPath) -- 187 + for _index_2 = 1, #_list_2 do -- 187 + local _des_0 = _list_2[_index_2] -- 187 + local name, tPath = _des_0[1], _des_0[2] -- 187 + local entry = { -- 188 + name, -- 188 + Path(dir, Path:getPath(file), tPath) -- 188 + } -- 188 + tests[#tests + 1] = entry -- 189 + end -- 189 + end -- 186 + local entry = { -- 190 + entryName, -- 190 + fileName, -- 190 + examples, -- 190 + tests -- 190 + } -- 190 + local bannerFile = Path(path, Path:getPath(fileName), "Image", "banner.png") -- 191 + if not Content:exist(bannerFile) then -- 192 + bannerFile = Path(path, Path:getPath(fileName), "Image", "banner.jpg") -- 193 + if not Content:exist(bannerFile) then -- 194 + bannerFile = nil -- 194 + end -- 194 + end -- 192 + if bannerFile then -- 195 + thread(function() -- 195 + Cache:loadAsync(bannerFile) -- 196 + local bannerTex = Texture2D(bannerFile) -- 197 + if bannerTex then -- 198 + entry[#entry + 1] = bannerFile -- 199 + entry[#entry + 1] = bannerTex -- 200 + end -- 198 + end) -- 195 + end -- 195 + entries[#entries + 1] = entry -- 201 + end -- 168 + ::_continue_1:: -- 168 + end -- 201 + ::_continue_0:: -- 166 + end -- 201 + table.sort(entries, function(a, b) -- 202 + return a[1] < b[1] -- 202 + end) -- 202 + return entries -- 203 +end -- 163 +local gamesInDev, games -- 205 +local doraExamples, doraTests -- 206 +local cppTests, cppTestSet -- 207 +local allEntries -- 208 +local updateEntries -- 210 +updateEntries = function() -- 210 + gamesInDev = getProjectEntries(Content.writablePath) -- 211 + games = getProjectEntries(Path(Content.assetPath, "Script")) -- 212 + doraExamples = getFileEntries(Path(Content.assetPath, "Script", "Example")) -- 214 + doraTests = getFileEntries(Path(Content.assetPath, "Script", "Test")) -- 215 + cppTests = { } -- 217 + local _list_0 = App.testNames -- 218 + for _index_0 = 1, #_list_0 do -- 218 + local name = _list_0[_index_0] -- 218 + local entry = { -- 219 + name -- 219 + } -- 219 + cppTests[#cppTests + 1] = entry -- 220 + end -- 220 + cppTestSet = Set(cppTests) -- 221 + allEntries = { } -- 223 + for _index_0 = 1, #gamesInDev do -- 224 + local game = gamesInDev[_index_0] -- 224 + allEntries[#allEntries + 1] = game -- 225 + local examples, tests = game[3], game[4] -- 226 + for _index_1 = 1, #examples do -- 227 + local example = examples[_index_1] -- 227 + allEntries[#allEntries + 1] = example -- 228 + end -- 228 + for _index_1 = 1, #tests do -- 229 + local test = tests[_index_1] -- 229 + allEntries[#allEntries + 1] = test -- 230 + end -- 230 + end -- 230 + for _index_0 = 1, #games do -- 231 + local game = games[_index_0] -- 231 + allEntries[#allEntries + 1] = game -- 232 + local examples, tests = game[3], game[4] -- 233 + for _index_1 = 1, #examples do -- 234 + local example = examples[_index_1] -- 234 + doraExamples[#doraExamples + 1] = example -- 235 + end -- 235 + for _index_1 = 1, #tests do -- 236 + local test = tests[_index_1] -- 236 + doraTests[#doraTests + 1] = test -- 237 + end -- 237 + end -- 237 + local _list_1 = { -- 239 + doraExamples, -- 239 + doraTests, -- 240 + cppTests -- 241 + } -- 238 + for _index_0 = 1, #_list_1 do -- 242 + local group = _list_1[_index_0] -- 238 + for _index_1 = 1, #group do -- 243 + local entry = group[_index_1] -- 243 + allEntries[#allEntries + 1] = entry -- 244 + end -- 244 + end -- 244 +end -- 210 +updateEntries() -- 246 +local doCompile -- 248 +doCompile = function(minify) -- 248 + if building then -- 249 + return -- 249 + end -- 249 + building = true -- 250 + local startTime = App.runningTime -- 251 + local luaFiles = { } -- 252 + local yueFiles = { } -- 253 + local xmlFiles = { } -- 254 + local tlFiles = { } -- 255 + local writablePath = Content.writablePath -- 256 + local buildPaths = { -- 258 + { -- 259 + Path(Content.assetPath), -- 259 + Path(writablePath, ".build"), -- 260 + "" -- 261 + } -- 258 + } -- 257 + for _index_0 = 1, #gamesInDev do -- 264 + local _des_0 = gamesInDev[_index_0] -- 264 + local name, entryFile = _des_0[1], _des_0[2] -- 264 + local gamePath = Path:getPath(entryFile) -- 265 + buildPaths[#buildPaths + 1] = { -- 267 + Path(writablePath, gamePath), -- 267 + Path(writablePath, ".build", gamePath), -- 268 + Path(writablePath, gamePath, "Script", "?.lua") .. ";" .. Path(writablePath, gamePath, "?.lua"), -- 269 + gamePath -- 270 + } -- 266 + end -- 270 + for _index_0 = 1, #buildPaths do -- 271 + local _des_0 = buildPaths[_index_0] -- 271 + local inputPath, outputPath, searchPath, gamePath = _des_0[1], _des_0[2], _des_0[3], _des_0[4] -- 271 + if not Content:exist(inputPath) then -- 272 + goto _continue_0 -- 272 + end -- 272 + local _list_0 = getAllFiles(inputPath, { -- 274 + "lua" -- 274 + }) -- 274 + for _index_1 = 1, #_list_0 do -- 274 + local file = _list_0[_index_1] -- 274 + luaFiles[#luaFiles + 1] = { -- 276 + file, -- 276 + Path(inputPath, file), -- 277 + Path(outputPath, file), -- 278 + gamePath -- 279 + } -- 275 + end -- 279 + local _list_1 = getAllFiles(inputPath, { -- 281 + yueext -- 281 + }) -- 281 + for _index_1 = 1, #_list_1 do -- 281 + local file = _list_1[_index_1] -- 281 + yueFiles[#yueFiles + 1] = { -- 283 + file, -- 283 + Path(inputPath, file), -- 284 + Path(outputPath, Path:replaceExt(file, "lua")), -- 285 + searchPath, -- 286 + gamePath -- 287 + } -- 282 + end -- 287 + local _list_2 = getAllFiles(inputPath, { -- 289 + "xml" -- 289 + }) -- 289 + for _index_1 = 1, #_list_2 do -- 289 + local file = _list_2[_index_1] -- 289 + xmlFiles[#xmlFiles + 1] = { -- 291 + file, -- 291 + Path(inputPath, file), -- 292 + Path(outputPath, Path:replaceExt(file, "lua")), -- 293 + gamePath -- 294 + } -- 290 + end -- 294 + local _list_3 = getAllFiles(inputPath, { -- 296 + "tl" -- 296 + }) -- 296 + for _index_1 = 1, #_list_3 do -- 296 + local file = _list_3[_index_1] -- 296 + if not file:match(".*%.d%.tl$") then -- 297 + tlFiles[#tlFiles + 1] = { -- 299 + file, -- 299 + Path(inputPath, file), -- 300 + Path(outputPath, Path:replaceExt(file, "lua")), -- 301 + searchPath, -- 302 + gamePath -- 303 + } -- 298 + end -- 297 + end -- 303 + ::_continue_0:: -- 272 + end -- 303 + local paths -- 305 + do -- 305 + local _tbl_0 = { } -- 305 + local _list_0 = { -- 306 + luaFiles, -- 306 + yueFiles, -- 306 + xmlFiles, -- 306 + tlFiles -- 306 + } -- 306 + for _index_0 = 1, #_list_0 do -- 306 + local files = _list_0[_index_0] -- 306 + for _index_1 = 1, #files do -- 307 + local file = files[_index_1] -- 307 + _tbl_0[Path:getPath(file[3])] = true -- 305 + end -- 305 + end -- 305 + paths = _tbl_0 -- 305 + end -- 307 + for path in pairs(paths) do -- 309 + Content:mkdir(path) -- 309 + end -- 309 + local totalFiles = #yueFiles + #xmlFiles + #tlFiles -- 311 + local fileCount = 0 -- 312 + local errors = { } -- 313 + for _index_0 = 1, #yueFiles do -- 314 + local _des_0 = yueFiles[_index_0] -- 314 + local file, input, output, searchPath, gamePath = _des_0[1], _des_0[2], _des_0[3], _des_0[4], _des_0[5] -- 314 + local filename -- 315 + if gamePath then -- 315 + filename = Path(gamePath, file) -- 315 + else -- 315 + filename = file -- 315 + end -- 315 + yue.compile(input, output, searchPath, function(codes, err, globals) -- 316 + if not codes then -- 317 + errors[#errors + 1] = "Compile errors in " .. tostring(filename) .. ".\n" .. tostring(err) -- 318 + return -- 319 + end -- 317 + local success, result = LintYueGlobals(codes, globals) -- 320 + if success then -- 321 + codes = codes:gsub("%s*local%s*_ENV%s*=%s*Dora%([^%)]-%)[^\n\r]+[\n\r%s]*", "\n") -- 322 + codes = codes:gsub("^\n*", "") -- 323 + if not (result == "") then -- 324 + result = result .. "\n" -- 324 + end -- 324 + return "-- [yue]: " .. tostring(file) .. "\n" .. tostring(result) .. tostring(codes) -- 325 + else -- 327 + do -- 327 + local yueCodes = Content:load(input) -- 327 + if yueCodes then -- 327 + local globalErrors = { } -- 328 + for _index_1 = 1, #result do -- 329 + local _des_1 = result[_index_1] -- 329 + local name, line, col = _des_1[1], _des_1[2], _des_1[3] -- 329 + local countLine = 1 -- 330 + local code = "" -- 331 + for lineCode in yueCodes:gmatch("([^\r\n]*)\r?\n?") do -- 332 + if countLine == line then -- 333 + code = lineCode -- 334 + break -- 335 + end -- 333 + countLine = countLine + 1 -- 336 + end -- 336 + globalErrors[#globalErrors + 1] = "invalid global variable \"" .. tostring(name) .. "\"\nin \"" .. tostring(filename) .. "\", at line " .. tostring(line) .. ", col " .. tostring(col) .. ".\n" .. tostring(code:gsub("\t", " ") .. '\n' .. string.rep(" ", col - 1) .. "^") -- 337 + end -- 337 + errors[#errors + 1] = table.concat(globalErrors, "\n") -- 338 + else -- 340 + errors[#errors + 1] = "failed to load file " .. tostring(input) -- 340 + end -- 327 + end -- 327 + end -- 321 + end, function(success) -- 316 + if success then -- 341 + print("Yue compiled: " .. tostring(filename)) -- 341 + end -- 341 + fileCount = fileCount + 1 -- 342 + end) -- 316 + end -- 342 + thread(function() -- 344 + for _index_0 = 1, #xmlFiles do -- 345 + local _des_0 = xmlFiles[_index_0] -- 345 + local file, input, output, gamePath = _des_0[1], _des_0[2], _des_0[3], _des_0[4] -- 345 + local filename -- 346 + if gamePath then -- 346 + filename = Path(gamePath, file) -- 346 + else -- 346 + filename = file -- 346 + end -- 346 + local sourceCodes = Content:loadAsync(input) -- 347 + local codes, err = xml.tolua(sourceCodes) -- 348 + if not codes then -- 349 + errors[#errors + 1] = "Compile errors in " .. tostring(filename) .. ".\n" .. tostring(err) -- 350 + else -- 352 + Content:saveAsync(output, "-- [xml]: " .. tostring(file) .. "\n" .. tostring(codes)) -- 352 + print("Xml compiled: " .. tostring(filename)) -- 353 + end -- 349 + fileCount = fileCount + 1 -- 354 + end -- 354 + end) -- 344 + thread(function() -- 356 + for _index_0 = 1, #tlFiles do -- 357 + local _des_0 = tlFiles[_index_0] -- 357 + local file, input, output, searchPath, gamePath = _des_0[1], _des_0[2], _des_0[3], _des_0[4], _des_0[5] -- 357 + local filename -- 358 + if gamePath then -- 358 + filename = Path(gamePath, file) -- 358 + else -- 358 + filename = file -- 358 + end -- 358 + local sourceCodes = Content:loadAsync(input) -- 359 + local codes, err = teal.toluaAsync(sourceCodes, file, searchPath) -- 360 + if not codes then -- 361 + errors[#errors + 1] = "Compile errors in " .. tostring(filename) .. ".\n" .. tostring(err) -- 362 + else -- 364 + Content:saveAsync(output, codes) -- 364 + print("Teal compiled: " .. tostring(filename)) -- 365 + end -- 361 + fileCount = fileCount + 1 -- 366 + end -- 366 + end) -- 356 + return thread(function() -- 368 + wait(function() -- 369 + return fileCount == totalFiles -- 369 + end) -- 369 + if minify then -- 370 + local _list_0 = { -- 371 + yueFiles, -- 371 + xmlFiles, -- 371 + tlFiles -- 371 + } -- 371 + for _index_0 = 1, #_list_0 do -- 371 + local files = _list_0[_index_0] -- 371 + for _index_1 = 1, #files do -- 371 + local file = files[_index_1] -- 371 + local output = Path:replaceExt(file[3], "lua") -- 372 + luaFiles[#luaFiles + 1] = { -- 374 + Path:replaceExt(file[1], "lua"), -- 374 + output, -- 375 + output -- 376 + } -- 373 + end -- 376 + end -- 376 + local FormatMini -- 378 + do -- 378 + local _obj_0 = require("luaminify") -- 378 + FormatMini = _obj_0.FormatMini -- 378 + end -- 378 + for _index_0 = 1, #luaFiles do -- 379 + local _des_0 = luaFiles[_index_0] -- 379 + local file, input, output = _des_0[1], _des_0[2], _des_0[3] -- 379 + if Content:exist(input) then -- 380 + local sourceCodes = Content:loadAsync(input) -- 381 + local res, err = FormatMini(sourceCodes) -- 382 + if res then -- 383 + Content:saveAsync(output, res) -- 384 + print("Minify: " .. tostring(file)) -- 385 + else -- 387 + errors[#errors + 1] = "Minify errors in " .. tostring(file) .. ".\n" .. tostring(err) -- 387 + end -- 383 + else -- 389 + errors[#errors + 1] = "Minify errors in " .. tostring(file) .. ".\nTarget file is not exist!" -- 389 + end -- 380 + end -- 389 + package.loaded["luaminify.FormatMini"] = nil -- 390 + package.loaded["luaminify.ParseLua"] = nil -- 391 + package.loaded["luaminify.Scope"] = nil -- 392 + package.loaded["luaminify.Util"] = nil -- 393 + end -- 370 + local errorMessage = table.concat(errors, "\n") -- 394 + if errorMessage ~= "" then -- 395 + print("\n" .. errorMessage) -- 395 + end -- 395 + local builtFiles = totalFiles + (minify and #luaFiles or 0) - #errors -- 396 + print("\n" .. tostring(builtFiles) .. " " .. tostring(builtFiles == 1 and 'file' or 'files') .. " built! Cost " .. tostring(string.format('%.2f', App.runningTime - startTime)) .. "s") -- 397 + print(tostring(#errors) .. " " .. tostring(#errors == 1 and 'file fails' or 'files fail') .. " to build.") -- 398 + Content:clearPathCache() -- 399 + teal.clear() -- 400 + yue.clear() -- 401 + building = false -- 402 + end) -- 402 +end -- 248 +local doClean -- 404 +doClean = function() -- 404 + if building then -- 405 + return -- 405 + end -- 405 + local writablePath = Content.writablePath -- 406 + local targetDir = Path(writablePath, ".build") -- 407 + Content:clearPathCache() -- 408 + if Content:remove(targetDir) then -- 409 + print("Cleaned: " .. tostring(targetDir)) -- 410 + end -- 409 + Content:remove(Path(writablePath, ".upload")) -- 411 + return Content:remove(Path(writablePath, ".download")) -- 412 +end -- 404 +local screenScale = 2.0 -- 414 +local scaleContent = false -- 415 +local isInEntry = true -- 416 +local currentEntry = nil -- 417 +local footerWindow = nil -- 419 +local entryWindow = nil -- 420 +local setupEventHandlers -- 422 +setupEventHandlers = function() -- 422 + local _with_0 = Director.postNode -- 423 + _with_0:gslot("AppTheme", function(argb) -- 424 + config.themeColor = argb -- 425 + end) -- 424 + _with_0:gslot("AppLocale", function(locale) -- 426 + config.locale = locale -- 427 + updateLocale() -- 428 + return teal.clear(true) -- 429 + end) -- 426 + _with_0:gslot("AppWSClose", function() -- 430 + if HttpServer.wsConnectionCount == 0 then -- 431 + return updateEntries() -- 432 + end -- 431 + end) -- 430 + do -- 433 + local _exp_0 = App.platform -- 433 + if "Linux" == _exp_0 or "Windows" == _exp_0 or "macOS" == _exp_0 then -- 433 + _with_0:gslot("AppSizeChanged", function() -- 434 + local width, height -- 435 + do -- 435 + local _obj_0 = App.winSize -- 435 + width, height = _obj_0.width, _obj_0.height -- 435 + end -- 435 + config.winWidth = width -- 436 + config.winHeight = height -- 437 + end) -- 434 + _with_0:gslot("AppFullScreen", function(fullScreen) -- 438 + config.fullScreen = fullScreen and 1 or 0 -- 439 + end) -- 438 + _with_0:gslot("AppMoved", function() -- 440 + do -- 441 + local _obj_0 = App.winPosition -- 441 + config.winX, config.winY = _obj_0.x, _obj_0.y -- 441 + end -- 441 + end) -- 440 + end -- 441 + end -- 441 + return _with_0 -- 423 +end -- 422 +setupEventHandlers() -- 443 +local allClear -- 445 +allClear = function() -- 445 + local _list_0 = Routine -- 446 + for _index_0 = 1, #_list_0 do -- 446 + local routine = _list_0[_index_0] -- 446 + if footerWindow == routine or entryWindow == routine then -- 448 + goto _continue_0 -- 449 + else -- 451 + Routine:remove(routine) -- 451 + end -- 451 + ::_continue_0:: -- 447 + end -- 451 + for _index_0 = 1, #moduleCache do -- 452 + local module = moduleCache[_index_0] -- 452 + package.loaded[module] = nil -- 453 + end -- 453 + moduleCache = { } -- 454 + Director:cleanup() -- 455 + Cache:unload() -- 456 + Entity:clear() -- 457 + Platformer.Data:clear() -- 458 + Platformer.UnitAction:clear() -- 459 + Audio:stopStream(0.2) -- 460 + Struct:clear() -- 461 + View.postEffect = nil -- 462 + View.scale = scaleContent and screenScale or 1 -- 463 + Director.clearColor = Color(0xff1a1a1a) -- 464 + teal.clear() -- 465 + yue.clear() -- 466 + for _, item in pairs(ubox()) do -- 467 + do -- 468 + local node = tolua.cast(item, "Node") -- 468 + if node then -- 468 + node:cleanup() -- 468 + end -- 468 + end -- 468 + end -- 468 + collectgarbage() -- 469 + collectgarbage() -- 470 + setupEventHandlers() -- 471 + Content.searchPaths = searchPaths -- 472 + App.idled = true -- 473 + return Wasm:clear() -- 474 +end -- 445 +_module_0["allClear"] = allClear -- 474 +local stop -- 476 +stop = function() -- 476 + if isInEntry then -- 477 + return false -- 477 + end -- 477 + allClear() -- 478 + isInEntry = true -- 479 + currentEntry = nil -- 480 + return true -- 481 +end -- 476 +_module_0["stop"] = stop -- 481 +local enterEntryAsync -- 483 +enterEntryAsync = function(entry) -- 483 + isInEntry = false -- 484 + App.idled = false -- 485 + currentEntry = entry -- 486 + local name, file = entry[1], entry[2] -- 487 + if cppTestSet[entry] then -- 488 + if App:runTest(name) then -- 489 + return true -- 490 + else -- 492 + return false, "failed to run cpp test '" .. tostring(name) .. "'" -- 492 + end -- 489 + end -- 488 + sleep() -- 493 + return xpcall(function() -- 494 + local scriptPath = Path:getPath(file) -- 495 + Content:insertSearchPath(1, scriptPath) -- 496 + scriptPath = Path(scriptPath, "Script") -- 497 + if Content:exist(scriptPath) then -- 498 + Content:insertSearchPath(1, scriptPath) -- 499 + end -- 498 + local result = require(file) -- 500 + if "function" == type(result) then -- 501 + result() -- 501 + end -- 501 + return nil -- 502 + end, function(msg) -- 502 + local err = debug.traceback(msg) -- 504 + allClear() -- 505 + print(err) -- 506 + local ScrollArea = require("UI.Control.Basic.ScrollArea") -- 507 + local AlignNode = require("UI.Control.Basic.AlignNode") -- 508 + local LineRect = require("UI.View.Shape.LineRect") -- 509 + local viewWidth, viewHeight -- 510 + do -- 510 + local _obj_0 = View.size -- 510 + viewWidth, viewHeight = _obj_0.width, _obj_0.height -- 510 + end -- 510 + local width, height = viewWidth - 20, viewHeight - 20 -- 511 + local fontSize = math.floor(20 * App.devicePixelRatio) -- 512 + do -- 513 + local _with_0 = AlignNode({ -- 513 + isRoot = true, -- 513 + inUI = false -- 513 + }) -- 513 + _with_0:addChild((function() -- 514 + local root = AlignNode({ -- 514 + alignWidth = "w", -- 514 + alignHeight = "h" -- 514 + }) -- 514 + root:addChild((function() -- 515 + local scroll = ScrollArea({ -- 516 + width = width, -- 516 + height = height, -- 517 + paddingX = 0, -- 518 + paddingY = 50, -- 519 + viewWidth = height, -- 520 + viewHeight = height -- 521 + }) -- 515 + scroll:slot("AlignLayout", function(w, h) -- 523 + scroll.position = Vec2(w / 2, h / 2) -- 524 + w = w - 20 -- 525 + h = h - 20 -- 526 + scroll.view.children.first.textWidth = w - fontSize -- 527 + return scroll:adjustSizeWithAlign("Auto", 10, Size(w, h)) -- 528 + end) -- 523 + scroll.view:addChild((function() -- 529 + local label = Label("sarasa-mono-sc-regular", fontSize) -- 529 + label.alignment = "Left" -- 530 + label.textWidth = width - fontSize -- 531 + label.text = err -- 532 + return label -- 529 + end)()) -- 529 + return scroll -- 515 + end)()) -- 515 + return root -- 514 + end)()) -- 514 + _with_0:alignLayout() -- 533 + end -- 513 + return err -- 534 + end) -- 534 +end -- 483 +_module_0["enterEntryAsync"] = enterEntryAsync -- 534 +local enterDemoEntry -- 536 +enterDemoEntry = function(entry) -- 536 + return thread(function() -- 536 + return enterEntryAsync(entry) -- 536 + end) -- 536 +end -- 536 +local reloadCurrentEntry -- 538 +reloadCurrentEntry = function() -- 538 + if currentEntry then -- 539 + allClear() -- 540 + return enterDemoEntry(currentEntry) -- 541 + end -- 539 +end -- 538 +Director.clearColor = Color(0xff1a1a1a) -- 543 +local waitForWebStart = true -- 545 +thread(function() -- 546 + sleep(2) -- 547 + waitForWebStart = false -- 548 +end) -- 546 +local reloadDevEntry -- 550 +reloadDevEntry = function() -- 550 + return thread(function() -- 550 + waitForWebStart = true -- 551 + doClean() -- 552 + allClear() -- 553 + _G.require = oldRequire -- 554 + dora.require = oldRequire -- 555 + package.loaded["Dev.Entry"] = nil -- 556 + return Director.systemScheduler:schedule(function() -- 557 + Routine:clear() -- 558 + oldRequire("Dev.Entry") -- 559 + return true -- 560 + end) -- 560 + end) -- 560 +end -- 550 +local isOSSLicenseExist = Content:exist("LICENSES") -- 562 +local ossLicenses = nil -- 563 +local ossLicenseOpen = false -- 564 +local extraOperations -- 566 +extraOperations = function() -- 566 + local zh = useChinese and isChineseSupported -- 567 + if isOSSLicenseExist then -- 568 + if Button(zh and '开源协议' or 'OSS Licenses') then -- 569 + if not ossLicenses then -- 570 + ossLicenses = { } -- 571 + local licenseText = Content:load("LICENSES") -- 572 + ossLicenseOpen = (licenseText ~= nil) -- 573 + if ossLicenseOpen then -- 573 + licenseText = licenseText:gsub("\r\n", "\n") -- 574 + for license in GSplit(licenseText, "\n--------\n", true) do -- 575 + do -- 576 + local name, text = license:match("[%s\n]*([^\n]*)[\n]*(.*)") -- 576 + if name then -- 576 + ossLicenses[#ossLicenses + 1] = { -- 577 + name, -- 577 + text -- 577 + } -- 577 + end -- 576 + end -- 576 + end -- 577 + end -- 573 + else -- 579 + ossLicenseOpen = true -- 579 + end -- 570 + end -- 569 + if ossLicenseOpen then -- 580 + local width, height, themeColor -- 581 + do -- 581 + local _obj_0 = App -- 581 + width, height, themeColor = _obj_0.visualSize.width, _obj_0.visualSize.height, _obj_0.themeColor -- 581 + end -- 581 + SetNextWindowPosCenter("Appearing", Vec2(0.5, 0.5)) -- 582 + SetNextWindowSize(Vec2(math.min(width * 0.8, 750), height * 0.8), "Appearing") -- 583 + PushStyleVar("WindowPadding", Vec2(20, 10), function() -- 584 + ossLicenseOpen = Begin(zh and '开源协议' or 'OSS Licenses', ossLicenseOpen, { -- 587 + "NoSavedSettings" -- 587 + }, function() -- 588 + for _index_0 = 1, #ossLicenses do -- 588 + local _des_0 = ossLicenses[_index_0] -- 588 + local firstLine, text = _des_0[1], _des_0[2] -- 588 + local name, license = firstLine:match("(.+): (.+)") -- 589 + TextColored(themeColor, name) -- 590 + SameLine() -- 591 + TreeNode(tostring(license) .. "###" .. tostring(name), function() -- 592 + return TextWrapped(text) -- 592 + end) -- 592 + end -- 592 + end) -- 584 + end) -- 584 + end -- 580 + end -- 568 + return TreeNode(zh and "开发操作" or "Development", function() -- 594 + if Button(zh and "脚本编译测试" or "Script Build Test") then -- 595 + OpenPopup("build") -- 595 + end -- 595 + PushStyleVar("WindowPadding", Vec2(10, 10), function() -- 596 + return BeginPopup("build", function() -- 596 + if Selectable(zh and "编译" or "Compile") then -- 597 + doCompile(false) -- 597 + end -- 597 + Separator() -- 598 + if Selectable(zh and "压缩" or "Minify") then -- 599 + doCompile(true) -- 599 + end -- 599 + Separator() -- 600 + if Selectable(zh and "清理" or "Clean") then -- 601 + return doClean() -- 601 + end -- 601 + end) -- 601 + end) -- 596 + if isInEntry then -- 602 + if waitForWebStart then -- 603 + BeginDisabled(function() -- 604 + return Button(zh and "重载开发程序(Ctrl+Z)" or "Reload Dev Entry(Ctrl+Z)") -- 604 + end) -- 604 + elseif Button(zh and "重载开发程序(Ctrl+Z)" or "Reload Dev Entry(Ctrl+Z)") then -- 605 + reloadDevEntry() -- 606 + end -- 603 + end -- 602 + do -- 607 + local changed -- 607 + changed, scaleContent = Checkbox(string.format("%.1fx " .. tostring(zh and '屏幕缩放' or 'Screen'), screenScale), scaleContent) -- 607 + if changed then -- 607 + View.scale = scaleContent and screenScale or 1 -- 608 + end -- 607 + end -- 607 + end) -- 594 +end -- 566 +local transparant = Color(0x0) -- 610 +local windowFlags = { -- 612 + "NoTitleBar", -- 612 + "NoResize", -- 613 + "NoMove", -- 614 + "NoCollapse", -- 615 + "NoSavedSettings", -- 616 + "NoBringToFrontOnFocus" -- 617 +} -- 611 +local initFooter = true -- 618 +footerWindow = threadLoop(function() -- 619 + local zh = useChinese and isChineseSupported -- 620 + if HttpServer.wsConnectionCount > 0 then -- 621 + return -- 622 + end -- 621 + if Keyboard:isKeyDown("Escape") then -- 623 + App:shutdown() -- 623 + end -- 623 + do -- 624 + local ctrl = Keyboard:isKeyPressed("LCtrl") -- 625 + if ctrl and Keyboard:isKeyDown("Q") then -- 626 + stop() -- 627 + end -- 626 + if ctrl and Keyboard:isKeyDown("Z") then -- 628 + reloadCurrentEntry() -- 629 + end -- 628 + if ctrl and Keyboard:isKeyDown(",") then -- 630 + if showFooter then -- 631 + showStats = not showStats -- 631 + else -- 631 + showStats = true -- 631 + end -- 631 + showFooter = true -- 632 + config.showFooter = showFooter and 1 or 0 -- 633 + config.showStats = showStats and 1 or 0 -- 634 + end -- 630 + if ctrl and Keyboard:isKeyDown(".") then -- 635 + if showFooter then -- 636 + showConsole = not showConsole -- 636 + else -- 636 + showConsole = true -- 636 + end -- 636 + showFooter = true -- 637 + config.showFooter = showFooter and 1 or 0 -- 638 + config.showConsole = showConsole and 1 or 0 -- 639 + end -- 635 + if ctrl and Keyboard:isKeyDown("/") then -- 640 + showFooter = not showFooter -- 641 + config.showFooter = showFooter and 1 or 0 -- 642 + end -- 640 + local left = ctrl and Keyboard:isKeyDown("Left") -- 643 + local right = ctrl and Keyboard:isKeyDown("Right") -- 644 + local currentIndex = nil -- 645 + for i, entry in ipairs(allEntries) do -- 646 + if currentEntry == entry then -- 647 + currentIndex = i -- 648 + end -- 647 + end -- 648 + if left then -- 649 + allClear() -- 650 + if currentIndex == nil then -- 651 + currentIndex = #allEntries + 1 -- 651 + end -- 651 + enterDemoEntry((function() -- 652 + if currentIndex > 1 then -- 652 + return allEntries[currentIndex - 1] -- 653 + else -- 655 + return allEntries[#allEntries] -- 655 + end -- 652 + end)()) -- 652 + end -- 649 + if right then -- 656 + allClear() -- 657 + if currentIndex == nil then -- 658 + currentIndex = 0 -- 658 + end -- 658 + enterDemoEntry((function() -- 659 + if currentIndex < #allEntries then -- 659 + return allEntries[currentIndex + 1] -- 660 + else -- 662 + return allEntries[1] -- 662 + end -- 659 + end)()) -- 659 + end -- 656 + end -- 662 + if not showEntry then -- 663 + return -- 663 + end -- 663 + local width, height -- 665 + do -- 665 + local _obj_0 = App.visualSize -- 665 + width, height = _obj_0.width, _obj_0.height -- 665 + end -- 665 + SetNextWindowSize(Vec2(50, 50)) -- 666 + SetNextWindowPos(Vec2(width - 50, height - 50)) -- 667 + PushStyleColor("WindowBg", transparant, function() -- 668 + return Begin("Show", windowFlags, function() -- 668 + if isInEntry or width >= 540 then -- 669 + do -- 670 + local changed -- 670 + changed, showFooter = Checkbox("##dev", showFooter) -- 670 + if changed then -- 670 + config.showFooter = showFooter and 1 or 0 -- 671 + end -- 670 + end -- 670 + end -- 669 + end) -- 671 + end) -- 668 + if isInEntry and not waitForWebStart and Keyboard:isKeyPressed("LCtrl") and Keyboard:isKeyDown("Z") then -- 673 + reloadDevEntry() -- 677 + end -- 673 + if initFooter then -- 678 + initFooter = false -- 679 + else -- 681 + if not showFooter then -- 681 + return -- 681 + end -- 681 + end -- 678 + SetNextWindowSize(Vec2(width, 50)) -- 683 + SetNextWindowPos(Vec2(0, height - 50)) -- 684 + SetNextWindowBgAlpha(0.35) -- 685 + return PushStyleVar("WindowPadding", Vec2(10, 0), function() -- 686 + return Begin("Footer", windowFlags, function() -- 686 + Dummy(Vec2(width - 20, 0)) -- 687 + do -- 688 + local changed -- 688 + changed, showStats = Checkbox(zh and "状态" or "Stats", showStats) -- 688 + if changed then -- 688 + config.showStats = showStats and 1 or 0 -- 689 + end -- 688 + end -- 688 + SameLine() -- 690 + do -- 691 + local changed -- 691 + changed, showConsole = Checkbox(zh and "控制台" or "Log", showConsole) -- 691 + if changed then -- 691 + config.showConsole = showConsole and 1 or 0 -- 692 + end -- 691 + end -- 691 + if not isInEntry then -- 693 + SameLine() -- 694 + if Button(zh and "主页" or "Home", Vec2(70, 30)) then -- 695 + allClear() -- 696 + isInEntry = true -- 697 + currentEntry = nil -- 698 + end -- 695 + local currentIndex = nil -- 699 + for i, entry in ipairs(allEntries) do -- 700 + if currentEntry == entry then -- 701 + currentIndex = i -- 702 + end -- 701 + end -- 702 + if currentIndex then -- 703 + if currentIndex > 1 then -- 704 + SameLine() -- 705 + if Button(zh and "前一个" or "Prev", Vec2(70, 30)) then -- 706 + allClear() -- 707 + enterDemoEntry(allEntries[currentIndex - 1]) -- 708 + end -- 706 + end -- 704 + if currentIndex < #allEntries then -- 709 + SameLine() -- 710 + if Button(zh and "后一个" or "Next", Vec2(70, 30)) then -- 711 + allClear() -- 712 + enterDemoEntry(allEntries[currentIndex + 1]) -- 713 + end -- 711 + end -- 709 + end -- 703 + SameLine() -- 714 + if Button(zh and "刷新" or "Reload", Vec2(70, 30)) then -- 715 + reloadCurrentEntry() -- 716 + end -- 715 + end -- 693 + return PushStyleVar("WindowPadding", Vec2(10, 10), function() -- 717 + if showStats then -- 718 + SetNextWindowPos(Vec2(10, 50), "FirstUseEver") -- 719 + showStats = ShowStats(showStats, extraOperations) -- 720 + config.showStats = showStats and 1 or 0 -- 721 + end -- 718 + if showConsole then -- 722 + SetNextWindowPos(Vec2(width - 425, height - 375), "FirstUseEver") -- 723 + showConsole = ShowConsole(showConsole) -- 724 + config.showConsole = showConsole and 1 or 0 -- 725 + end -- 722 + end) -- 725 + end) -- 725 + end) -- 725 +end) -- 619 +local MaxWidth = 800 -- 727 +local displayWindowFlags = { -- 730 + "NoDecoration", -- 730 + "NoSavedSettings", -- 731 + "NoFocusOnAppearing", -- 732 + "NoNav", -- 733 + "NoMove", -- 734 + "NoScrollWithMouse", -- 735 + "AlwaysAutoResize", -- 736 + "NoBringToFrontOnFocus" -- 737 +} -- 729 +local webStatus = nil -- 739 +local descColor = Color(0xffa1a1a1) -- 740 +local gameOpen = #gamesInDev == 0 -- 741 +local exampleOpen = false -- 742 +local testOpen = false -- 743 +local filterText = nil -- 744 +local anyEntryMatched = false -- 745 +local match -- 746 +match = function(name) -- 746 + local res = not filterText or name:lower():match(filterText) -- 747 + if res then -- 748 + anyEntryMatched = true -- 748 + end -- 748 + return res -- 749 +end -- 746 +entryWindow = threadLoop(function() -- 751 + if App.fpsLimited ~= (config.fpsLimited == 1) then -- 752 + config.fpsLimited = App.fpsLimited and 1 or 0 -- 753 + end -- 752 + if App.targetFPS ~= config.targetFPS then -- 754 + config.targetFPS = App.targetFPS -- 755 + end -- 754 + if View.vsync ~= (config.vsync == 1) then -- 756 + config.vsync = View.vsync and 1 or 0 -- 757 + end -- 756 + if Director.scheduler.fixedFPS ~= config.fixedFPS then -- 758 + config.fixedFPS = Director.scheduler.fixedFPS -- 759 + end -- 758 + if not showEntry then -- 760 + return -- 760 + end -- 760 + if not isInEntry then -- 761 + return -- 761 + end -- 761 + local zh = useChinese and isChineseSupported -- 762 + if HttpServer.wsConnectionCount > 0 then -- 763 + local themeColor = App.themeColor -- 764 + local width, height -- 765 + do -- 765 + local _obj_0 = App.visualSize -- 765 + width, height = _obj_0.width, _obj_0.height -- 765 + end -- 765 + SetNextWindowBgAlpha(0.5) -- 766 + SetNextWindowPos(Vec2(width / 2, height / 2), "Always", Vec2(0.5, 0.5)) -- 767 + Begin("Web IDE Connected", displayWindowFlags, function() -- 768 + Separator() -- 769 + TextColored(themeColor, tostring(zh and '网页 IDE 已连接 ……' or 'Web IDE connected ...')) -- 770 + local slogon = zh and 'Dora 启动!' or 'Dora Start!' -- 771 + TextColored(descColor, slogon) -- 772 + return Separator() -- 773 + end) -- 768 + return -- 774 + end -- 763 + local themeColor = App.themeColor -- 776 + local fullWidth, height -- 777 + do -- 777 + local _obj_0 = App.visualSize -- 777 + fullWidth, height = _obj_0.width, _obj_0.height -- 777 + end -- 777 + SetNextWindowBgAlpha(0.85) -- 779 + SetNextWindowPos(Vec2(fullWidth - 30, height - 130), "Always", Vec2(1, 0)) -- 780 + PushStyleVar("WindowPadding", Vec2(10, 5), function() -- 781 + return Begin("Web IDE", displayWindowFlags, function() -- 782 + Separator() -- 783 + TextColored(themeColor, tostring(zh and '网页' or 'Web') .. " IDE") -- 784 + local url -- 785 + do -- 785 + local _exp_0 -- 785 + if webStatus ~= nil then -- 785 + _exp_0 = webStatus.url -- 785 + end -- 785 + if _exp_0 ~= nil then -- 785 + url = _exp_0 -- 785 + else -- 785 + url = zh and '不可用' or 'not available' -- 785 + end -- 785 + end -- 785 + TextColored(descColor, url) -- 786 + return Separator() -- 787 + end) -- 787 + end) -- 781 + local width = math.min(MaxWidth, fullWidth) -- 789 + local paddingX = math.max(10, fullWidth / 2 - width / 2 - 10) -- 790 + local maxColumns = math.max(math.floor(width / 200), 1) -- 791 + SetNextWindowPos(Vec2.zero) -- 792 + SetNextWindowBgAlpha(0) -- 793 + PushStyleVar("WindowPadding", Vec2(10, 0), function() -- 794 + return Begin("Dora Dev", displayWindowFlags, function() -- 795 + Dummy(Vec2(fullWidth - 20, 0)) -- 796 + TextColored(themeColor, "DORA SSR " .. tostring(zh and '开发' or 'DEV')) -- 797 + SameLine() -- 798 + if fullWidth >= 320 then -- 799 + Dummy(Vec2(fullWidth - 320, 0)) -- 800 + SameLine() -- 801 + SetNextItemWidth(-50) -- 802 + if InputText(zh and '筛选' or 'Filter', filterBuf, { -- 803 + "AutoSelectAll" -- 803 + }) then -- 803 + config.filter = filterBuf:toString() -- 804 + end -- 803 + end -- 799 + Separator() -- 805 + return Dummy(Vec2(fullWidth - 20, 0)) -- 806 + end) -- 806 + end) -- 794 + anyEntryMatched = false -- 808 + SetNextWindowPos(Vec2(0, 50)) -- 809 + SetNextWindowSize(Vec2(fullWidth, height - 100)) -- 810 + return PushStyleColor("WindowBg", transparant, function() -- 811 + return PushStyleVar("WindowPadding", Vec2(paddingX, 10), function() -- 811 + return Begin("Content", windowFlags, function() -- 812 + filterText = filterBuf:toString():match("[^%%%.%[]+") -- 813 + if filterText then -- 814 + filterText = filterText:lower() -- 814 + end -- 814 + if #gamesInDev > 0 then -- 815 + for _index_0 = 1, #gamesInDev do -- 816 + local game = gamesInDev[_index_0] -- 816 + local gameName, fileName, examples, tests, bannerFile, bannerTex = game[1], game[2], game[3], game[4], game[5], game[6] -- 817 + local showSep = false -- 818 + if match(gameName) then -- 819 + Columns(1, false) -- 820 + TextColored(themeColor, zh and "项目:" or "Project:") -- 821 + SameLine() -- 822 + Text(gameName) -- 823 + Separator() -- 824 + if bannerFile then -- 825 + local texWidth, texHeight = bannerTex.width, bannerTex.height -- 826 + local displayWidth = (fullWidth / 2 - paddingX) * 2 - 35 -- 827 + local sizing = 0.8 -- 828 + texHeight = displayWidth * sizing * texHeight / texWidth -- 829 + texWidth = displayWidth * sizing -- 830 + local padding = displayWidth * (1 - sizing) / 2 - 10 -- 831 + Dummy(Vec2(padding, 0)) -- 832 + SameLine() -- 833 + PushID(fileName, function() -- 834 + if ImageButton(gameName, bannerFile, Vec2(texWidth, texHeight)) then -- 835 + return enterDemoEntry(game) -- 836 + end -- 835 + end) -- 834 + else -- 838 + PushID(fileName, function() -- 838 + if Button(zh and "开始运行" or "Game Start", Vec2(-1, 40)) then -- 839 + return enterDemoEntry(game) -- 840 + end -- 839 + end) -- 838 + end -- 825 + NextColumn() -- 841 + showSep = true -- 842 + end -- 819 + if #examples > 0 then -- 843 + local showExample = false -- 844 + for _index_1 = 1, #examples do -- 845 + local example = examples[_index_1] -- 845 + if match(example[1]) then -- 846 + showExample = true -- 847 + break -- 848 + end -- 846 + end -- 848 + if showExample then -- 849 + Columns(1, false) -- 850 + TextColored(themeColor, zh and "示例:" or "Example:") -- 851 + SameLine() -- 852 + Text(gameName) -- 853 + PushStyleVar("ItemSpacing", Vec2(20, 10), function() -- 854 + Columns(maxColumns, false) -- 855 + for _index_1 = 1, #examples do -- 856 + local example = examples[_index_1] -- 856 + if not match(example[1]) then -- 857 + goto _continue_0 -- 857 + end -- 857 + PushID(tostring(gameName) .. " " .. tostring(example[1]) .. " example", function() -- 858 + if Button(example[1], Vec2(-1, 40)) then -- 859 + enterDemoEntry(example) -- 860 + end -- 859 + return NextColumn() -- 861 + end) -- 858 + showSep = true -- 862 + ::_continue_0:: -- 857 + end -- 862 + end) -- 854 + end -- 849 + end -- 843 + if #tests > 0 then -- 863 + local showTest = false -- 864 + for _index_1 = 1, #tests do -- 865 + local test = tests[_index_1] -- 865 + if match(test[1]) then -- 866 + showTest = true -- 867 + break -- 868 + end -- 866 + end -- 868 + if showTest then -- 869 + Columns(1, false) -- 870 + TextColored(themeColor, zh and "测试:" or "Test:") -- 871 + SameLine() -- 872 + Text(gameName) -- 873 + PushStyleVar("ItemSpacing", Vec2(20, 10), function() -- 874 + Columns(maxColumns, false) -- 875 + for _index_1 = 1, #tests do -- 876 + local test = tests[_index_1] -- 876 + if not match(test[1]) then -- 877 + goto _continue_0 -- 877 + end -- 877 + PushID(tostring(gameName) .. " " .. tostring(test[1]) .. " test", function() -- 878 + if Button(test[1], Vec2(-1, 40)) then -- 879 + enterDemoEntry(test) -- 880 + end -- 879 + return NextColumn() -- 881 + end) -- 878 + showSep = true -- 882 + ::_continue_0:: -- 877 + end -- 882 + end) -- 874 + end -- 869 + end -- 863 + if showSep then -- 883 + Columns(1, false) -- 884 + Separator() -- 885 + end -- 883 + end -- 885 + end -- 815 + if #games > 0 or #doraExamples > 0 or #doraTests > 0 then -- 886 + local showGame = false -- 887 + for _index_0 = 1, #games do -- 888 + local _des_0 = games[_index_0] -- 888 + local name = _des_0[1] -- 888 + if match(name) then -- 889 + showGame = true -- 889 + end -- 889 + end -- 889 + local showExample = false -- 890 + for _index_0 = 1, #doraExamples do -- 891 + local _des_0 = doraExamples[_index_0] -- 891 + local name = _des_0[1] -- 891 + if match(name) then -- 892 + showExample = true -- 892 + end -- 892 + end -- 892 + local showTest = false -- 893 + for _index_0 = 1, #doraTests do -- 894 + local _des_0 = doraTests[_index_0] -- 894 + local name = _des_0[1] -- 894 + if match(name) then -- 895 + showTest = true -- 895 + end -- 895 + end -- 895 + for _index_0 = 1, #cppTests do -- 896 + local _des_0 = cppTests[_index_0] -- 896 + local name = _des_0[1] -- 896 + if match(name) then -- 897 + showTest = true -- 897 + end -- 897 + end -- 897 + if not (showGame or showExample or showTest) then -- 898 + goto endEntry -- 898 + end -- 898 + Columns(1, false) -- 899 + TextColored(themeColor, "Dora SSR:") -- 900 + SameLine() -- 901 + Text(zh and "开发示例" or "Development Showcase") -- 902 + Separator() -- 903 + local demoViewWith = 400 -- 904 + if #games > 0 and showGame then -- 905 + local opened -- 906 + if (filterText ~= nil) then -- 906 + opened = showGame -- 906 + else -- 906 + opened = false -- 906 + end -- 906 + SetNextItemOpen(gameOpen) -- 907 + TreeNode(zh and "游戏演示" or "Game Demo", function() -- 908 + local columns = math.max(math.floor(width / demoViewWith), 1) -- 909 + Columns(columns, false) -- 910 + for _index_0 = 1, #games do -- 911 + local game = games[_index_0] -- 911 + if not match(game[1]) then -- 912 + goto _continue_0 -- 912 + end -- 912 + local gameName, fileName, bannerFile, bannerTex = game[1], game[2], game[5], game[6] -- 913 + if columns > 1 then -- 914 + if bannerFile then -- 915 + local texWidth, texHeight = bannerTex.width, bannerTex.height -- 916 + local displayWidth = demoViewWith - 40 -- 917 + texHeight = displayWidth * texHeight / texWidth -- 918 + texWidth = displayWidth -- 919 + Text(gameName) -- 920 + PushID(fileName, function() -- 921 + if ImageButton(gameName, bannerFile, Vec2(texWidth, texHeight)) then -- 922 + return enterDemoEntry(game) -- 923 + end -- 922 + end) -- 921 + else -- 925 + PushID(fileName, function() -- 925 + if Button(gameName, Vec2(-1, 40)) then -- 926 + return enterDemoEntry(game) -- 927 + end -- 926 + end) -- 925 + end -- 915 + else -- 929 + if bannerFile then -- 929 + local texWidth, texHeight = bannerTex.width, bannerTex.height -- 930 + local displayWidth = (fullWidth / 2 - paddingX) * 2 - 35 -- 931 + local sizing = 0.8 -- 932 + texHeight = displayWidth * sizing * texHeight / texWidth -- 933 + texWidth = displayWidth * sizing -- 934 + if texWidth > 500 then -- 935 + sizing = 0.6 -- 936 + texHeight = displayWidth * sizing * texHeight / texWidth -- 937 + texWidth = displayWidth * sizing -- 938 + end -- 935 + local padding = displayWidth * (1 - sizing) / 2 - 10 -- 939 + Dummy(Vec2(padding, 0)) -- 940 + SameLine() -- 941 + Text(gameName) -- 942 + Dummy(Vec2(padding, 0)) -- 943 + SameLine() -- 944 + PushID(fileName, function() -- 945 + if ImageButton(gameName, bannerFile, Vec2(texWidth, texHeight)) then -- 946 + return enterDemoEntry(game) -- 947 + end -- 946 + end) -- 945 + else -- 949 + PushID(fileName, function() -- 949 + if Button(gameName, Vec2(-1, 40)) then -- 950 + return enterDemoEntry(game) -- 951 + end -- 950 + end) -- 949 + end -- 929 + end -- 914 + NextColumn() -- 952 + ::_continue_0:: -- 912 + end -- 952 + Columns(1, false) -- 953 + opened = true -- 954 + end) -- 908 + gameOpen = opened -- 955 + end -- 905 + if #doraExamples > 0 and showExample then -- 956 + local opened -- 957 + if (filterText ~= nil) then -- 957 + opened = showExample -- 957 + else -- 957 + opened = false -- 957 + end -- 957 + SetNextItemOpen(exampleOpen) -- 958 + TreeNode(zh and "引擎示例" or "Engine Example", function() -- 959 + return PushStyleVar("ItemSpacing", Vec2(20, 10), function() -- 960 + Columns(maxColumns, false) -- 961 + for _index_0 = 1, #doraExamples do -- 962 + local example = doraExamples[_index_0] -- 962 + if not match(example[1]) then -- 963 + goto _continue_0 -- 963 + end -- 963 + if Button(example[1], Vec2(-1, 40)) then -- 964 + enterDemoEntry(example) -- 965 + end -- 964 + NextColumn() -- 966 + ::_continue_0:: -- 963 + end -- 966 + Columns(1, false) -- 967 + opened = true -- 968 + end) -- 960 + end) -- 959 + exampleOpen = opened -- 969 + end -- 956 + if (#doraTests > 0 or #cppTests > 0) and showTest then -- 970 + local opened -- 971 + if (filterText ~= nil) then -- 971 + opened = showTest -- 971 + else -- 971 + opened = false -- 971 + end -- 971 + SetNextItemOpen(testOpen) -- 972 + TreeNode(zh and "引擎测试" or "Engine Test", function() -- 973 + return PushStyleVar("ItemSpacing", Vec2(20, 10), function() -- 974 + Columns(maxColumns, false) -- 975 + for _index_0 = 1, #doraTests do -- 976 + local test = doraTests[_index_0] -- 976 + if not match(test[1]) then -- 977 + goto _continue_0 -- 977 + end -- 977 + if Button(test[1], Vec2(-1, 40)) then -- 978 + enterDemoEntry(test) -- 979 + end -- 978 + NextColumn() -- 980 + ::_continue_0:: -- 977 + end -- 980 + for _index_0 = 1, #cppTests do -- 981 + local test = cppTests[_index_0] -- 981 + if not match(test[1]) then -- 982 + goto _continue_1 -- 982 + end -- 982 + if Button(test[1], Vec2(-1, 40)) then -- 983 + enterDemoEntry(test) -- 984 + end -- 983 + NextColumn() -- 985 + ::_continue_1:: -- 982 + end -- 985 + opened = true -- 986 + end) -- 974 + end) -- 973 + testOpen = opened -- 987 + end -- 970 + end -- 886 + ::endEntry:: -- 988 + if not anyEntryMatched then -- 989 + SetNextWindowBgAlpha(0) -- 990 + SetNextWindowPos(Vec2(fullWidth / 2, height / 2), "Always", Vec2(0.5, 0.5)) -- 991 + Begin("Entries Not Found", displayWindowFlags, function() -- 992 + Separator() -- 993 + TextColored(themeColor, zh and "多萝西:" or "Dora:") -- 994 + TextColored(descColor, zh and '别担心,改变一些咒语,我们会找到新的冒险~' or 'Don\'t worry, more magic words and we\'ll find a new adventure!') -- 995 + return Separator() -- 996 + end) -- 992 + end -- 989 + Columns(1, false) -- 997 + Dummy(Vec2(100, 80)) -- 998 + return ScrollWhenDraggingOnVoid() -- 999 + end) -- 999 + end) -- 999 + end) -- 999 +end) -- 751 +webStatus = require("WebServer") -- 1001 +return _module_0 -- 1001 diff --git a/Assets/Script/Dev/Entry.yue b/Assets/Script/Dev/Entry.yue index 0b653f871..c57fd02bf 100644 --- a/Assets/Script/Dev/Entry.yue +++ b/Assets/Script/Dev/Entry.yue @@ -663,16 +663,11 @@ footerWindow = threadLoop -> return unless showEntry :width, :height = App.visualSize - SetNextWindowSize Vec2 170, 50 - SetNextWindowPos if width >= 600 - Vec2 width - 170, height - 50 - else - Vec2 width, height + SetNextWindowSize Vec2 50, 50 + SetNextWindowPos Vec2 width - 50, height - 50 PushStyleColor "WindowBg", transparant, -> Begin "Show", windowFlags, -> - if width >= 600 - Columns 2, false - NextColumn! - if changed, showFooter := Checkbox zh and "开发" or "Dev", showFooter + if isInEntry or width >= 540 + if changed, showFooter := Checkbox "##dev", showFooter config.showFooter = showFooter and 1 or 0 if isInEntry and diff --git a/Assets/Script/Example/Body.lua b/Assets/Script/Example/Body.lua new file mode 100644 index 000000000..edbeae243 --- /dev/null +++ b/Assets/Script/Example/Body.lua @@ -0,0 +1,86 @@ +-- [yue]: Script/Example/Body.yue +local Vec2 = dora.Vec2 -- 1 +local BodyDef = dora.BodyDef -- 1 +local PhysicsWorld = dora.PhysicsWorld -- 1 +local Body = dora.Body -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local gravity = Vec2(0, -10) -- 3 +local groupZero = 0 -- 5 +local groupOne = 1 -- 6 +local groupTwo = 2 -- 7 +local terrainDef -- 9 +do -- 9 + local _with_0 = BodyDef() -- 9 + _with_0.type = "Static" -- 10 + _with_0:attachPolygon(800, 10, 1, 0.8, 0.2) -- 11 + terrainDef = _with_0 -- 9 +end -- 9 +local polygonDef -- 13 +do -- 13 + local _with_0 = BodyDef() -- 13 + _with_0.type = "Dynamic" -- 14 + _with_0.linearAcceleration = gravity -- 15 + _with_0:attachPolygon({ -- 17 + Vec2(60, 0), -- 17 + Vec2(30, -30), -- 18 + Vec2(-30, -30), -- 19 + Vec2(-60, 0), -- 20 + Vec2(-30, 30), -- 21 + Vec2(30, 30) -- 22 + }, 1, 0.4, 0.4) -- 16 + polygonDef = _with_0 -- 13 +end -- 13 +local diskDef -- 25 +do -- 25 + local _with_0 = BodyDef() -- 25 + _with_0.type = "Dynamic" -- 26 + _with_0.linearAcceleration = gravity -- 27 + _with_0:attachDisk(60, 1, 0.4, 0.4) -- 28 + diskDef = _with_0 -- 25 +end -- 25 +do -- 30 + local world = PhysicsWorld() -- 30 + world.y = -200 -- 31 + world.showDebug = true -- 32 + world:setShouldContact(groupZero, groupOne, false) -- 34 + world:setShouldContact(groupZero, groupTwo, true) -- 35 + world:setShouldContact(groupOne, groupTwo, true) -- 36 + world:addChild((function() -- 38 + local _with_0 = Body(terrainDef, world, Vec2.zero) -- 38 + _with_0.group = groupTwo -- 39 + return _with_0 -- 38 + end)()) -- 38 + world:addChild((function() -- 41 + local _with_0 = Body(polygonDef, world, Vec2(0, 500), 15) -- 41 + _with_0.group = groupOne -- 42 + return _with_0 -- 41 + end)()) -- 41 + world:addChild((function() -- 44 + local _with_0 = Body(diskDef, world, Vec2(50, 800)) -- 44 + _with_0.group = groupZero -- 45 + _with_0.angularRate = 90 -- 46 + return _with_0 -- 44 + end)()) -- 44 +end -- 30 +local windowFlags = { -- 51 + "NoDecoration", -- 51 + "AlwaysAutoResize", -- 52 + "NoSavedSettings", -- 53 + "NoFocusOnAppearing", -- 54 + "NoNav", -- 55 + "NoMove" -- 56 +} -- 50 +return threadLoop(function() -- 57 + local width -- 58 + width = App.visualSize.width -- 58 + ImGui.SetNextWindowBgAlpha(0.35) -- 59 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 60 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 61 + return ImGui.Begin("Body", windowFlags, function() -- 62 + ImGui.Text("Body") -- 63 + ImGui.Separator() -- 64 + return ImGui.TextWrapped("Basic usage to create physics bodies!") -- 65 + end) -- 65 +end) -- 65 diff --git a/Assets/Script/Example/Camera.lua b/Assets/Script/Example/Camera.lua new file mode 100644 index 000000000..4624fcb75 --- /dev/null +++ b/Assets/Script/Example/Camera.lua @@ -0,0 +1,72 @@ +-- [yue]: Script/Example/Camera.yue +local Node = dora.Node -- 1 +local Model = dora.Model -- 1 +local Sprite = dora.Sprite -- 1 +local Vec2 = dora.Vec2 -- 1 +local once = dora.once -- 1 +local Director = dora.Director -- 1 +local cycle = dora.cycle -- 1 +local Ease = dora.Ease -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +do -- 3 + local _with_0 = Node() -- 3 + _with_0:addChild((function() -- 4 + local _with_1 = Model("Model/xiaoli.model") -- 4 + _with_1.look = "happy" -- 5 + _with_1:play("idle", true) -- 6 + return _with_1 -- 4 + end)()) -- 4 + _with_0:addChild((function() -- 8 + local _with_1 = Sprite("Image/logo.png") -- 8 + _with_1.scaleX = 0.4 -- 9 + _with_1.scaleY = 0.4 -- 10 + _with_1.position = Vec2(200, -100) -- 11 + _with_1.angleY = 45 -- 12 + _with_1.z = -300 -- 13 + return _with_1 -- 8 + end)()) -- 8 + _with_0:schedule(once(function() -- 15 + local _with_1 = Director.currentCamera -- 15 + cycle(1.5, function(dt) -- 16 + _with_1.position = Vec2(200 * Ease:func(Ease.InOutQuad, dt), 0) -- 16 + end) -- 16 + cycle(0.1, function(dt) -- 17 + _with_1.rotation = 25 * Ease:func(Ease.OutSine, dt) -- 17 + end) -- 17 + cycle(0.2, function(dt) -- 18 + _with_1.rotation = 25 - 50 * Ease:func(Ease.InOutQuad, dt) -- 18 + end) -- 18 + cycle(0.1, function(dt) -- 19 + _with_1.rotation = -25 + 25 * Ease:func(Ease.OutSine, dt) -- 19 + end) -- 19 + cycle(1.5, function(dt) -- 20 + _with_1.position = Vec2(200 * Ease:func(Ease.InOutQuad, 1 - dt), 0) -- 20 + end) -- 20 + local zoom = _with_1.zoom -- 21 + cycle(2.5, function(dt) -- 22 + _with_1.zoom = zoom + Ease:func(Ease.InOutQuad, dt) -- 22 + end) -- 22 + return _with_1 -- 15 + end)) -- 15 +end -- 3 +local windowFlags = { -- 27 + "NoDecoration", -- 27 + "AlwaysAutoResize", -- 28 + "NoSavedSettings", -- 29 + "NoFocusOnAppearing", -- 30 + "NoNav", -- 31 + "NoMove" -- 32 +} -- 26 +return threadLoop(function() -- 33 + local width -- 34 + width = App.visualSize.width -- 34 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 35 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 36 + return ImGui.Begin("Camera", windowFlags, function() -- 37 + ImGui.Text("Camera") -- 38 + ImGui.Separator() -- 39 + return ImGui.TextWrapped("View camera motions, use 3D camera as default!") -- 40 + end) -- 40 +end) -- 40 diff --git a/Assets/Script/Example/ClipNode.lua b/Assets/Script/Example/ClipNode.lua new file mode 100644 index 000000000..1ae1faa98 --- /dev/null +++ b/Assets/Script/Example/ClipNode.lua @@ -0,0 +1,146 @@ +-- [yue]: Script/Example/ClipNode.yue +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local DrawNode = dora.DrawNode -- 1 +local Model = dora.Model -- 1 +local Sequence = dora.Sequence -- 1 +local X = dora.X -- 1 +local Event = dora.Event -- 1 +local ClipNode = dora.ClipNode -- 1 +local Line = dora.Line -- 1 +local App = dora.App -- 1 +local Node = dora.Node -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local StarVertices -- 3 +StarVertices = function(radius, line) -- 3 + if line == nil then -- 3 + line = false -- 3 + end -- 3 + local a = math.rad(36) -- 4 + local c = math.rad(72) -- 5 + local f = math.sin(a) * math.tan(c) + math.cos(a) -- 6 + local R = radius -- 7 + local r = R / f -- 8 + local _accum_0 = { } -- 9 + local _len_0 = 1 -- 9 + for i = 9, line and -1 or 0, -1 do -- 9 + local angle = i * a -- 10 + local cr = i % 2 == 1 and r or R -- 11 + _accum_0[_len_0] = Vec2(cr * math.sin(angle), cr * math.cos(angle)) -- 12 + _len_0 = _len_0 + 1 -- 12 + end -- 12 + return _accum_0 -- 12 +end -- 3 +local maskA -- 16 +do -- 16 + local _with_0 = DrawNode() -- 16 + _with_0:drawPolygon(StarVertices(160)) -- 17 + maskA = _with_0 -- 16 +end -- 16 +local targetA -- 19 +do -- 19 + local _with_0 = Model("Model/xiaoli.model") -- 19 + _with_0.look = "happy" -- 20 + _with_0.fliped = true -- 21 + _with_0:play("walk", true) -- 22 + _with_0:runAction(Sequence(X(1.5, -200, 200), Event("Turn"), X(1.5, 200, -200), Event("Turn"))) -- 23 + _with_0:slot("ActionEnd", function(action) -- 29 + return _with_0:runAction(action) -- 29 + end) -- 29 + _with_0:slot("Turn", function() -- 30 + _with_0.fliped = not _with_0.fliped -- 30 + end) -- 30 + targetA = _with_0 -- 19 +end -- 19 +local clipNodeA -- 32 +do -- 32 + local _with_0 = ClipNode(maskA) -- 32 + _with_0:addChild(targetA) -- 33 + _with_0.inverted = true -- 34 + clipNodeA = _with_0 -- 32 +end -- 32 +local frame -- 35 +do -- 35 + local _with_0 = Line(StarVertices(160, true), App.themeColor) -- 35 + _with_0.visible = false -- 36 + frame = _with_0 -- 35 +end -- 35 +local exampleA -- 37 +do -- 37 + local _with_0 = Node() -- 37 + _with_0:addChild(clipNodeA) -- 38 + _with_0:addChild(frame) -- 39 + _with_0.visible = false -- 40 + exampleA = _with_0 -- 37 +end -- 37 +local maskB -- 44 +do -- 44 + local _with_0 = Model("Model/xiaoli.model") -- 44 + _with_0.look = "happy" -- 45 + _with_0.fliped = true -- 46 + _with_0:play("walk", true) -- 47 + maskB = _with_0 -- 44 +end -- 44 +local targetB -- 49 +do -- 49 + local _with_0 = DrawNode() -- 49 + _with_0:drawPolygon(StarVertices(160)) -- 50 + _with_0:runAction(Sequence(X(1.5, -200, 200), X(1.5, 200, -200))) -- 51 + _with_0:slot("ActionEnd", function(action) -- 55 + return _with_0:runAction(action) -- 55 + end) -- 55 + targetB = _with_0 -- 49 +end -- 49 +local clipNodeB -- 57 +do -- 57 + local _with_0 = ClipNode(maskB) -- 57 + _with_0:addChild(targetB) -- 58 + _with_0.inverted = true -- 59 + _with_0.alphaThreshold = 0.3 -- 60 + clipNodeB = _with_0 -- 57 +end -- 57 +local exampleB -- 61 +do -- 61 + local _with_0 = Node() -- 61 + _with_0:addChild(clipNodeB) -- 62 + exampleB = _with_0 -- 61 +end -- 61 +local inverted = true -- 66 +local withAlphaThreshold = true -- 67 +local windowFlags = { -- 69 + "NoDecoration", -- 69 + "AlwaysAutoResize", -- 70 + "NoSavedSettings", -- 71 + "NoFocusOnAppearing", -- 72 + "NoNav", -- 73 + "NoMove" -- 74 +} -- 68 +return threadLoop(function() -- 75 + local width -- 76 + width = App.visualSize.width -- 76 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 77 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 78 + return ImGui.Begin("Clip Node", windowFlags, function() -- 79 + ImGui.Text("Clip Node") -- 80 + ImGui.Separator() -- 81 + ImGui.TextWrapped("Render children nodes with mask!") -- 82 + do -- 83 + local changed -- 83 + changed, inverted = ImGui.Checkbox("Inverted", inverted) -- 83 + if changed then -- 83 + clipNodeA.inverted = inverted -- 84 + clipNodeB.inverted = inverted -- 85 + frame.visible = not inverted -- 86 + end -- 83 + end -- 83 + do -- 87 + local changed -- 87 + changed, withAlphaThreshold = ImGui.Checkbox("With alphaThreshold", withAlphaThreshold) -- 87 + if changed then -- 87 + exampleB.visible = withAlphaThreshold -- 88 + exampleA.visible = not withAlphaThreshold -- 89 + end -- 87 + end -- 87 + end) -- 89 +end) -- 89 diff --git a/Assets/Script/Example/Contact.lua b/Assets/Script/Example/Contact.lua new file mode 100644 index 000000000..92dbecf1a --- /dev/null +++ b/Assets/Script/Example/Contact.lua @@ -0,0 +1,114 @@ +-- [yue]: Script/Example/Contact.yue +local Vec2 = dora.Vec2 -- 1 +local PhysicsWorld = dora.PhysicsWorld -- 1 +local Label = dora.Label -- 1 +local BodyDef = dora.BodyDef -- 1 +local math = _G.math -- 1 +local Body = dora.Body -- 1 +local Line = dora.Line -- 1 +local App = dora.App -- 1 +local string = _G.string -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local gravity = Vec2(0, -10) -- 3 +local world -- 5 +do -- 5 + local _with_0 = PhysicsWorld() -- 5 + _with_0:setShouldContact(0, 0, true) -- 6 + _with_0.showDebug = true -- 7 + world = _with_0 -- 5 +end -- 5 +local label -- 9 +do -- 9 + local _with_0 = Label("sarasa-mono-sc-regular", 30) -- 9 + _with_0:addTo(world) -- 10 + label = _with_0 -- 9 +end -- 9 +local terrainDef -- 12 +do -- 12 + local _with_0 = BodyDef() -- 12 + local count = 50 -- 13 + local radius = 300 -- 14 + local vertices -- 15 + do -- 15 + local _accum_0 = { } -- 15 + local _len_0 = 1 -- 15 + for i = 1, count + 1 do -- 15 + local angle = 2 * math.pi * i / count -- 16 + _accum_0[_len_0] = Vec2(radius * math.cos(angle), radius * math.sin(angle)) -- 17 + _len_0 = _len_0 + 1 -- 17 + end -- 17 + vertices = _accum_0 -- 15 + end -- 17 + _with_0:attachChain(vertices, 0.4, 0) -- 18 + _with_0:attachDisk(Vec2(0, -270), 30, 1, 0, 1.0) -- 19 + _with_0:attachPolygon(Vec2(0, 80), 120, 30, 0, 1, 0, 1.0) -- 20 + terrainDef = _with_0 -- 12 +end -- 12 +local terrain -- 22 +do -- 22 + local _with_0 = Body(terrainDef, world) -- 22 + _with_0:addTo(world) -- 23 + terrain = _with_0 -- 22 +end -- 22 +local drawNode -- 25 +do -- 25 + local _with_0 = Line({ -- 26 + Vec2(-20, 0), -- 26 + Vec2(20, 0), -- 27 + Vec2.zero, -- 28 + Vec2(0, -20), -- 29 + Vec2(0, 20) -- 30 + }, App.themeColor) -- 25 + _with_0:addTo(world) -- 32 + drawNode = _with_0 -- 25 +end -- 25 +local diskDef -- 34 +do -- 34 + local _with_0 = BodyDef() -- 34 + _with_0.type = "Dynamic" -- 35 + _with_0.linearAcceleration = gravity -- 36 + _with_0:attachDisk(20, 5, 0.8, 1) -- 37 + diskDef = _with_0 -- 34 +end -- 34 +local disk -- 39 +do -- 39 + local _with_0 = Body(diskDef, world, Vec2(100, 200)) -- 39 + _with_0:addTo(world) -- 40 + _with_0.angularRate = -1800 -- 41 + _with_0.receivingContact = true -- 42 + _with_0:slot("ContactStart", function(_, point) -- 43 + drawNode.position = point -- 44 + label.text = string.format("Contact: [%.0f,%.0f]", point.x, point.y) -- 45 + end) -- 43 + disk = _with_0 -- 39 +end -- 39 +local windowFlags = { -- 50 + "NoDecoration", -- 50 + "AlwaysAutoResize", -- 51 + "NoSavedSettings", -- 52 + "NoFocusOnAppearing", -- 53 + "NoNav", -- 54 + "NoMove" -- 55 +} -- 49 +local receivingContact = disk.receivingContact -- 56 +return threadLoop(function() -- 57 + local width -- 58 + width = App.visualSize.width -- 58 + ImGui.SetNextWindowBgAlpha(0.35) -- 59 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 60 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 61 + return ImGui.Begin("Contact", windowFlags, function() -- 62 + ImGui.Text("Contact") -- 63 + ImGui.Separator() -- 64 + ImGui.TextWrapped("Receive events when physics bodies contact.") -- 65 + do -- 66 + local changed -- 66 + changed, receivingContact = ImGui.Checkbox("Receiving Contact", receivingContact) -- 66 + if changed then -- 66 + disk.receivingContact = receivingContact -- 67 + label.text = "" -- 68 + end -- 66 + end -- 66 + end) -- 68 +end) -- 68 diff --git a/Assets/Script/Example/Dora Xml.lua b/Assets/Script/Example/Dora Xml.lua new file mode 100644 index 000000000..4b6a60b50 --- /dev/null +++ b/Assets/Script/Example/Dora Xml.lua @@ -0,0 +1,42 @@ +-- [xml]: Script/Example/Dora Xml.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local root = Node() -- 1 +local rotate = Action(Spawn(Sequence(Move(1,Vec2(0,0),Vec2(200,0),Ease.InSine),Move(2,Vec2(200,0),Vec2(0,200),Ease.OutSine),Move(2,Vec2(0,200),Vec2(0,0),Ease.InSine)),Angle(6,0,360,Ease.OutQuad))) -- 3 +local scale = Action(Sequence(Scale(0.2,1,1.3,Ease.OutBack),Scale(0.2,1.3,1,Ease.OutQuad))) -- 11 +local sprite1 = Sprite("Image/logo.png") -- 16 +sprite1.touchEnabled = true -- 16 +root:addChild(sprite1) -- 16 +sprite1:slot("TapBegan",function() -- 17 +sprite1:perform(scale) -- 17 +end) -- 17 +root:slot("Enter",function() -- 19 +root:perform(rotate) -- 19 +end) -- 19 +do -- 21 + local _ENV = Dora() -- 23 + local xmlCodes = Content:load("Example/Dora Xml.xml") -- 24 + local luaCodes = xml.tolua(xmlCodes) -- 25 + print("[Xml Codes]\n\n" .. tostring(xmlCodes) .. "\n[Compiled Lua Codes]\n\n" .. tostring(luaCodes)) -- 26 + local windowFlags = { -- 28 + "NoDecoration", -- 28 + "AlwaysAutoResize", -- 29 + "NoSavedSettings", -- 30 + "NoFocusOnAppearing", -- 31 + "NoNav", -- 32 + "NoMove" -- 33 + } -- 27 + root:schedule(function() -- 34 + local width = App.visualSize.width -- 35 + ImGui.SetNextWindowBgAlpha(0.35) -- 36 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 37 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 38 + return ImGui.Begin("Dora Xml", windowFlags, function() -- 39 + ImGui.Text("Dora Xml") -- 40 + ImGui.Separator() -- 41 + return ImGui.TextWrapped("View related codes in log window!") -- 42 + end) -- 42 + end) -- 34 +end -- 42 +return root -- 22 +end \ No newline at end of file diff --git a/Assets/Script/Example/DragonBones.lua b/Assets/Script/Example/DragonBones.lua new file mode 100644 index 000000000..471a2bb6c --- /dev/null +++ b/Assets/Script/Example/DragonBones.lua @@ -0,0 +1,85 @@ +-- [yue]: Script/Example/DragonBones.yue +local DragonBone = dora.DragonBone -- 1 +local p = _G.p -- 1 +local print = _G.print -- 1 +local tostring = _G.tostring -- 1 +local Label = dora.Label -- 1 +local App = dora.App -- 1 +local Sequence = dora.Sequence -- 1 +local Spawn = dora.Spawn -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local Delay = dora.Delay -- 1 +local Opacity = dora.Opacity -- 1 +local Event = dora.Event -- 1 +local Vec2 = dora.Vec2 -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local boneStr = "DragonBones/NewDragon" -- 3 +local animations = DragonBone:getAnimations(boneStr) -- 5 +local looks = DragonBone:getLooks(boneStr) -- 6 +p(animations, looks) -- 8 +local bone -- 10 +do -- 10 + local _with_0 = DragonBone(boneStr) -- 10 + _with_0.look = looks[1] -- 11 + _with_0:play(animations[1], true) -- 12 + _with_0:slot("AnimationEnd", function(name) -- 13 + return print(tostring(name) .. " end!") -- 13 + end) -- 13 + _with_0.y = -200 -- 14 + _with_0.touchEnabled = true -- 15 + _with_0:slot("TapBegan", function(touch) -- 16 + local x, y -- 17 + do -- 17 + local _obj_0 = touch.location -- 17 + x, y = _obj_0.x, _obj_0.y -- 17 + end -- 17 + do -- 18 + local name = _with_0:containsPoint(x, y) -- 18 + if name then -- 18 + return _with_0:addChild((function() -- 19 + local _with_1 = Label("sarasa-mono-sc-regular", 30) -- 19 + _with_1.text = name -- 20 + _with_1.color = App.themeColor -- 21 + _with_1:perform(Sequence(Spawn(Scale(1, 0, 2, Ease.OutQuad), Sequence(Delay(0.5), Opacity(0.5, 1, 0))), Event("Stop"))) -- 22 + _with_1.position = Vec2(x, y) -- 29 + _with_1.order = 100 -- 30 + _with_1:slot("Stop", function() -- 31 + return _with_1:removeFromParent() -- 31 + end) -- 31 + return _with_1 -- 19 + end)()) -- 31 + end -- 18 + end -- 18 + end) -- 16 + bone = _with_0 -- 10 +end -- 10 +local windowFlags = { -- 36 + "NoDecoration", -- 36 + "AlwaysAutoResize", -- 37 + "NoSavedSettings", -- 38 + "NoFocusOnAppearing", -- 39 + "NoNav", -- 40 + "NoMove" -- 41 +} -- 35 +local showDebug = bone.showDebug -- 42 +return threadLoop(function() -- 43 + local width -- 44 + width = App.visualSize.width -- 44 + ImGui.SetNextWindowBgAlpha(0.35) -- 45 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 46 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 47 + return ImGui.Begin("DragonBones", windowFlags, function() -- 48 + ImGui.Text("DragonBones") -- 49 + ImGui.Separator() -- 50 + ImGui.TextWrapped("Basic usage to create dragonBones! Tap it for a hit test.") -- 51 + do -- 52 + local changed -- 52 + changed, showDebug = ImGui.Checkbox("BoundingBox", showDebug) -- 52 + if changed then -- 52 + bone.showDebug = showDebug -- 53 + end -- 52 + end -- 52 + end) -- 53 +end) -- 53 diff --git a/Assets/Script/Example/DrawNode.lua b/Assets/Script/Example/DrawNode.lua new file mode 100644 index 000000000..71b2f311c --- /dev/null +++ b/Assets/Script/Example/DrawNode.lua @@ -0,0 +1,133 @@ +-- [yue]: Script/Example/DrawNode.yue +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local Node = dora.Node -- 1 +local DrawNode = dora.DrawNode -- 1 +local Color = dora.Color -- 1 +local App = dora.App -- 1 +local Line = dora.Line -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local CircleVertices -- 3 +CircleVertices = function(radius, verts) -- 3 + if verts == nil then -- 3 + verts = 20 -- 3 + end -- 3 + local newV -- 4 + newV = function(index, radius) -- 4 + local angle = 2 * math.pi * index / verts -- 5 + return Vec2(radius * math.cos(angle), radius * math.sin(angle)) + Vec2(radius, radius) -- 6 + end -- 4 + local _accum_0 = { } -- 7 + local _len_0 = 1 -- 7 + for index = 0, verts do -- 7 + _accum_0[_len_0] = newV(index, radius) -- 7 + _len_0 = _len_0 + 1 -- 7 + end -- 7 + return _accum_0 -- 7 +end -- 3 +local StarVertices -- 9 +StarVertices = function(radius) -- 9 + local a = math.rad(36) -- 10 + local c = math.rad(72) -- 11 + local f = math.sin(a) * math.tan(c) + math.cos(a) -- 12 + local R = radius -- 13 + local r = R / f -- 14 + local _accum_0 = { } -- 15 + local _len_0 = 1 -- 15 + for i = 9, 0, -1 do -- 15 + local angle = i * a -- 16 + local cr = i % 2 == 1 and r or R -- 17 + _accum_0[_len_0] = Vec2(cr * math.sin(angle), cr * math.cos(angle)) -- 18 + _len_0 = _len_0 + 1 -- 18 + end -- 18 + return _accum_0 -- 18 +end -- 9 +do -- 20 + local _with_0 = Node() -- 20 + _with_0:addChild((function() -- 21 + local _with_1 = DrawNode() -- 21 + _with_1.position = Vec2(200, 200) -- 22 + _with_1:drawPolygon(StarVertices(60), Color(0x80ff0080), 1, Color(0xffff0080)) -- 23 + return _with_1 -- 21 + end)()) -- 21 + local themeColor = App.themeColor -- 25 + _with_0:addChild((function() -- 27 + local _with_1 = Line(CircleVertices(60), themeColor) -- 27 + _with_1.position = Vec2(-200, 200) -- 28 + return _with_1 -- 27 + end)()) -- 27 + _with_0:addChild((function() -- 30 + local _with_1 = Node() -- 30 + _with_1.color = themeColor -- 31 + _with_1.scaleX = 2 -- 32 + _with_1.scaleY = 2 -- 33 + _with_1:addChild((function() -- 34 + local _with_2 = DrawNode() -- 34 + _with_2.opacity = 0.5 -- 35 + _with_2:drawPolygon({ -- 37 + Vec2(-20, -10), -- 37 + Vec2(20, -10), -- 38 + Vec2(20, 10), -- 39 + Vec2(-20, 10) -- 40 + }) -- 36 + _with_2:drawPolygon({ -- 43 + Vec2(20, 3), -- 43 + Vec2(32, 10), -- 44 + Vec2(32, -10), -- 45 + Vec2(20, -3) -- 46 + }) -- 42 + _with_2:drawDot(Vec2(-11, 20), 10) -- 48 + _with_2:drawDot(Vec2(11, 20), 10) -- 49 + return _with_2 -- 34 + end)()) -- 34 + _with_1:addChild((function() -- 50 + local _with_2 = Line({ -- 51 + Vec2(0, 0), -- 51 + Vec2(40, 0), -- 52 + Vec2(40, 20), -- 53 + Vec2(0, 20), -- 54 + Vec2(0, 0) -- 55 + }) -- 50 + _with_2.position = Vec2(-20, -10) -- 57 + return _with_2 -- 50 + end)()) -- 50 + _with_1:addChild((function() -- 58 + local _with_2 = Line(CircleVertices(10)) -- 58 + _with_2.position = Vec2(-21, 10) -- 59 + return _with_2 -- 58 + end)()) -- 58 + _with_1:addChild((function() -- 60 + local _with_2 = Line(CircleVertices(10)) -- 60 + _with_2.position = Vec2(1, 10) -- 61 + return _with_2 -- 60 + end)()) -- 60 + _with_1:addChild(Line({ -- 63 + Vec2(20, 3), -- 63 + Vec2(32, 10), -- 64 + Vec2(32, -10), -- 65 + Vec2(20, -3) -- 66 + })) -- 62 + return _with_1 -- 30 + end)()) -- 30 +end -- 20 +local windowFlags = { -- 72 + "NoDecoration", -- 72 + "AlwaysAutoResize", -- 73 + "NoSavedSettings", -- 74 + "NoFocusOnAppearing", -- 75 + "NoNav", -- 76 + "NoMove" -- 77 +} -- 71 +return threadLoop(function() -- 78 + local width -- 79 + width = App.visualSize.width -- 79 + ImGui.SetNextWindowBgAlpha(0.35) -- 80 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 81 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 82 + return ImGui.Begin("Draw Node", windowFlags, function() -- 83 + ImGui.Text("Draw Node") -- 84 + ImGui.Separator() -- 85 + return ImGui.TextWrapped("Draw shapes and lines!") -- 86 + end) -- 86 +end) -- 86 diff --git a/Assets/Script/Example/Entity Move.lua b/Assets/Script/Example/Entity Move.lua new file mode 100644 index 000000000..938956a28 --- /dev/null +++ b/Assets/Script/Example/Entity Move.lua @@ -0,0 +1,167 @@ +-- [yue]: Script/Example/Entity Move.yue +local Group = dora.Group -- 1 +local Observer = dora.Observer -- 1 +local Sprite = dora.Sprite -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local print = _G.print -- 1 +local tostring = _G.tostring -- 1 +local math = _G.math -- 1 +local Roll = dora.Roll -- 1 +local Entity = dora.Entity -- 1 +local Node = dora.Node -- 1 +local Vec2 = dora.Vec2 -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local Sequence = dora.Sequence -- 1 +local Event = dora.Event -- 1 +local sceneGroup = Group({ -- 3 + "scene" -- 3 +}) -- 3 +local positionGroup = Group({ -- 4 + "position" -- 4 +}) -- 4 +do -- 6 + local _with_0 = Observer("Add", { -- 6 + "scene" -- 6 + }) -- 6 + _with_0:watch(function(self, scene) -- 7 + scene.touchEnabled = true -- 8 + scene:slot("TapEnded", function(touch) -- 9 + local location = touch.location -- 10 + return positionGroup:each(function(entity) -- 11 + entity.target = location -- 12 + end) -- 12 + end) -- 9 + return scene -- 7 + end) -- 7 +end -- 6 +do -- 14 + local _with_0 = Observer("Add", { -- 14 + "image" -- 14 + }) -- 14 + _with_0:watch(function(self, image) -- 15 + return sceneGroup:each(function(e) -- 15 + do -- 16 + local _with_1 = Sprite(image) -- 16 + self.sprite = _with_1 -- 16 + _with_1:addTo(e.scene) -- 17 + _with_1:runAction(Scale(0.5, 0, 0.5, Ease.OutBack)) -- 18 + end -- 16 + return true -- 19 + end) -- 19 + end) -- 15 +end -- 14 +do -- 21 + local _with_0 = Observer("Remove", { -- 21 + "sprite" -- 21 + }) -- 21 + _with_0:watch(function(self) -- 22 + return self.oldValues.sprite:removeFromParent() -- 22 + end) -- 22 +end -- 21 +do -- 24 + local _with_0 = Observer("Remove", { -- 24 + "target" -- 24 + }) -- 24 + _with_0:watch(function(self) -- 25 + return print("remove target from entity " .. tostring(self.index)) -- 25 + end) -- 25 +end -- 24 +do -- 27 + local _with_0 = Group({ -- 27 + "position", -- 27 + "direction", -- 27 + "speed", -- 27 + "target" -- 27 + }) -- 27 + _with_0:watch(function(self, position, direction, speed, target) -- 28 + if target == position then -- 29 + return -- 29 + end -- 29 + local dir = target - position -- 30 + dir = dir:normalize() -- 31 + local angle = math.deg(math.atan(dir.x, dir.y)) -- 32 + local newPos = position + dir * speed -- 33 + newPos = newPos:clamp(position, target) -- 34 + self.position = newPos -- 35 + self.direction = angle -- 36 + if newPos == target then -- 37 + self.target = nil -- 37 + end -- 37 + end) -- 28 +end -- 27 +do -- 39 + local _with_0 = Observer("AddOrChange", { -- 39 + "position", -- 39 + "direction", -- 39 + "sprite" -- 39 + }) -- 39 + _with_0:watch(function(self, position, direction, sprite) -- 40 + sprite.position = position -- 41 + local lastDirection = self.oldValues.direction or sprite.angle -- 42 + if math.abs(direction - lastDirection) > 1 then -- 43 + return sprite:runAction(Roll(0.3, lastDirection, direction)) -- 44 + end -- 43 + end) -- 40 +end -- 39 +Entity({ -- 47 + scene = Node() -- 47 +}) -- 46 +Entity({ -- 50 + image = "Image/logo.png", -- 50 + position = Vec2.zero, -- 51 + direction = 45.0, -- 52 + speed = 4.0 -- 53 +}) -- 49 +Entity({ -- 56 + image = "Image/logo.png", -- 56 + position = Vec2(-100, 200), -- 57 + direction = 90.0, -- 58 + speed = 10.0 -- 59 +}) -- 55 +local windowFlags = { -- 64 + "NoDecoration", -- 64 + "AlwaysAutoResize", -- 65 + "NoSavedSettings", -- 66 + "NoFocusOnAppearing", -- 67 + "NoNav", -- 68 + "NoMove" -- 69 +} -- 63 +return threadLoop(function() -- 70 + local width -- 71 + width = App.visualSize.width -- 71 + ImGui.SetNextWindowBgAlpha(0.35) -- 72 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 73 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 74 + return ImGui.Begin("ECS System", windowFlags, function() -- 75 + ImGui.Text("ECS System") -- 76 + ImGui.Separator() -- 77 + ImGui.TextWrapped("Tap any place to move entities.") -- 78 + if ImGui.Button("Create Random Entity") then -- 79 + Entity({ -- 81 + image = "Image/logo.png", -- 81 + position = Vec2(6 * math.random(1, 100), 6 * math.random(1, 100)), -- 82 + direction = 1.0 * math.random(0, 360), -- 83 + speed = 1.0 * math.random(1, 20) -- 84 + }) -- 80 + end -- 79 + if ImGui.Button("Destroy An Entity") then -- 85 + return Group({ -- 86 + "sprite", -- 86 + "position" -- 86 + }):each(function(entity) -- 86 + entity.position = nil -- 87 + do -- 88 + local _with_0 = entity.sprite -- 88 + _with_0:runAction(Sequence(Scale(0.5, 0.5, 0, Ease.InBack), Event("Destroy"))) -- 89 + _with_0:slot("Destroy", function() -- 90 + return entity:destroy() -- 90 + end) -- 90 + end -- 88 + return true -- 91 + end) -- 91 + end -- 85 + end) -- 91 +end) -- 91 diff --git a/Assets/Script/Example/Gesture.lua b/Assets/Script/Example/Gesture.lua new file mode 100644 index 000000000..ebe4bdc59 --- /dev/null +++ b/Assets/Script/Example/Gesture.lua @@ -0,0 +1,47 @@ +-- [yue]: Script/Example/Gesture.yue +local nvg = dora.nvg -- 1 +local Sprite = dora.Sprite -- 1 +local Vec2 = dora.Vec2 -- 1 +local View = dora.View -- 1 +local Node = dora.Node -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local texture = nvg.GetDoraSSR() -- 3 +local sprite = Sprite(texture) -- 4 +local length = Vec2(View.size).length -- 5 +local width, height = sprite.width, sprite.height -- 6 +local size = Vec2(width, height).length -- 7 +local scaledSize = size -- 8 +do -- 10 + local _with_0 = Node() -- 10 + _with_0:addChild(sprite) -- 11 + _with_0.touchEnabled = true -- 12 + _with_0:slot("Gesture", function(center, _numTouches, delta, angle) -- 13 + sprite.position = center -- 17 + sprite.angle = sprite.angle + angle -- 18 + scaledSize = scaledSize + (delta * length) -- 19 + sprite.scaleX = scaledSize / size -- 20 + sprite.scaleY = scaledSize / size -- 21 + end) -- 13 +end -- 10 +local windowFlags = { -- 26 + "NoDecoration", -- 26 + "AlwaysAutoResize", -- 27 + "NoSavedSettings", -- 28 + "NoFocusOnAppearing", -- 29 + "NoNav", -- 30 + "NoMove" -- 31 +} -- 25 +return threadLoop(function() -- 32 + local width -- 33 + width = App.visualSize.width -- 33 + ImGui.SetNextWindowBgAlpha(0.35) -- 34 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 35 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 36 + return ImGui.Begin("Gesture", windowFlags, function() -- 37 + ImGui.Text("Gesture") -- 38 + ImGui.Separator() -- 39 + return ImGui.TextWrapped("Interact with multi-touches!") -- 40 + end) -- 40 +end) -- 40 diff --git a/Assets/Script/Example/Hello World.lua b/Assets/Script/Example/Hello World.lua new file mode 100644 index 000000000..a7c6d0203 --- /dev/null +++ b/Assets/Script/Example/Hello World.lua @@ -0,0 +1,48 @@ +-- [yue]: Script/Example/Hello World.yue +local Node = dora.Node -- 1 +local print = _G.print -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local Vec2 = dora.Vec2 -- 1 +do -- 3 + local _with_0 = Node() -- 3 + _with_0:slot("Enter", function() -- 4 + return print("on enter event") -- 4 + end) -- 4 + _with_0:slot("Exit", function() -- 5 + return print("on exit event") -- 5 + end) -- 5 + _with_0:slot("Cleanup", function() -- 6 + return print("on node destoyed event") -- 6 + end) -- 6 + _with_0:schedule(once(function() -- 7 + for i = 5, 1, -1 do -- 8 + print(i) -- 9 + sleep(1) -- 10 + end -- 10 + return print("Hello World!") -- 11 + end)) -- 7 +end -- 3 +local windowFlags = { -- 16 + "NoDecoration", -- 16 + "AlwaysAutoResize", -- 17 + "NoSavedSettings", -- 18 + "NoFocusOnAppearing", -- 19 + "NoNav", -- 20 + "NoMove" -- 21 +} -- 15 +return threadLoop(function() -- 22 + local width -- 23 + width = App.visualSize.width -- 23 + ImGui.SetNextWindowBgAlpha(0.35) -- 24 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 25 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 26 + return ImGui.Begin("Hello World", windowFlags, function() -- 27 + ImGui.Text("Hello World") -- 28 + ImGui.Separator() -- 29 + return ImGui.TextWrapped("Basic Dora schedule and signal function usage. Written in Yuescript. View outputs in log window!") -- 30 + end) -- 30 +end) -- 30 diff --git a/Assets/Script/Example/Label.lua b/Assets/Script/Example/Label.lua new file mode 100644 index 000000000..9e00adf26 --- /dev/null +++ b/Assets/Script/Example/Label.lua @@ -0,0 +1,49 @@ +-- [yue]: Script/Example/Label.yue +local Label = dora.Label -- 1 +local Sequence = dora.Sequence -- 1 +local Delay = dora.Delay -- 1 +local Scale = dora.Scale -- 1 +local App = dora.App -- 1 +local Vec2 = dora.Vec2 -- 1 +local Opacity = dora.Opacity -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +do -- 3 + local _with_0 = Label("sarasa-mono-sc-regular", 40) -- 3 + _with_0.batched = false -- 4 + _with_0.text = "你好,Dora SSR!" -- 5 + for i = 1, _with_0.characterCount do -- 6 + local char = _with_0:getCharacter(i) -- 7 + if char ~= nil then -- 8 + char:runAction(Sequence(Delay(i / 5), Scale(0.2, 1, 2), Scale(0.2, 2, 1))) -- 8 + end -- 8 + end -- 12 +end -- 3 +do -- 14 + local _with_0 = Label("sarasa-mono-sc-regular", 30) -- 14 + _with_0.text = "-- from Jin." -- 15 + _with_0.color = App.themeColor -- 16 + _with_0.opacity = 0 -- 17 + _with_0.position = Vec2(120, -70) -- 18 + _with_0:runAction(Sequence(Delay(2), Opacity(0.2, 0, 1))) -- 19 +end -- 14 +local windowFlags = { -- 27 + "NoDecoration", -- 27 + "AlwaysAutoResize", -- 28 + "NoSavedSettings", -- 29 + "NoFocusOnAppearing", -- 30 + "NoNav", -- 31 + "NoMove" -- 32 +} -- 26 +return threadLoop(function() -- 33 + local width -- 34 + width = App.visualSize.width -- 34 + ImGui.SetNextWindowBgAlpha(0.35) -- 35 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 36 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 37 + return ImGui.Begin("Label", windowFlags, function() -- 38 + ImGui.Text("Label") -- 39 + ImGui.Separator() -- 40 + return ImGui.TextWrapped("Render labels with unbatched and batched methods!") -- 41 + end) -- 41 +end) -- 41 diff --git a/Assets/Script/Example/ML.lua b/Assets/Script/Example/ML.lua new file mode 100644 index 000000000..fa59f2b08 --- /dev/null +++ b/Assets/Script/Example/ML.lua @@ -0,0 +1,1020 @@ +-- [yue]: Script/Example/ML.yue +local App = dora.App -- 1 +local table = _G.table -- 1 +local ML = dora.ML -- 1 +local math = _G.math -- 1 +local pairs = _G.pairs -- 1 +local threadLoop = dora.threadLoop -- 1 +local _module_0 = dora.ImGui -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local Vec2 = dora.Vec2 -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local TextWrapped = _module_0.TextWrapped -- 1 +local tostring = _G.tostring -- 1 +local Button = _module_0.Button -- 1 +local SameLine = _module_0.SameLine -- 1 +local Separator = _module_0.Separator -- 1 +local Checkbox = _module_0.Checkbox -- 1 +local thread = dora.thread -- 1 +local string = _G.string -- 1 +local actions = { -- 3 + "观察", -- 3 + "侦查", -- 3 + "攀爬", -- 3 + "挥舞", -- 3 + "攻击", -- 3 + "破坏", -- 3 + "投掷", -- 3 + "采集", -- 3 + "挖掘", -- 3 + "采收", -- 3 + "沟通", -- 3 + "鼓舞", -- 3 + "恐吓" -- 3 +} -- 3 +local relationTags = { -- 5 + "友善", -- 5 + "中立", -- 5 + "敌对" -- 5 +} -- 5 +local bodyTypes = { -- 7 + "大型", -- 7 + "巨型" -- 7 +} -- 7 +local skills = { -- 9 + "迅速Lv1", -- 9 + "迅速Lv2" -- 9 +} -- 9 +local unitTags = { -- 11 + "生物", -- 11 + "挖掘资源", -- 11 + "采集资源", -- 11 + "可破坏", -- 11 + "可攀爬", -- 11 + "飞行" -- 11 +} -- 11 +local effectNames = { -- 13 + "揭示", -- 13 + "伤害", -- 13 + "破坏", -- 13 + "采集", -- 13 + "擒抱", -- 13 + "攀爬", -- 13 + "交涉", -- 13 + "恐吓" -- 13 +} -- 13 +local actionEffects = { -- 16 + ["观察"] = { -- 17 + ["生物"] = { -- 17 + "揭示", -- 17 + 0, -- 17 + 1 -- 17 + }, -- 17 + ["友善"] = { -- 18 + "揭示", -- 18 + 0, -- 18 + 1 -- 18 + }, -- 18 + ["中立"] = { -- 19 + "揭示", -- 19 + 0, -- 19 + 1 -- 19 + }, -- 19 + ["敌对"] = { -- 20 + "揭示", -- 20 + 0, -- 20 + 1 -- 20 + }, -- 20 + ["挖掘资源"] = { -- 21 + "揭示", -- 21 + 0, -- 21 + 1 -- 21 + }, -- 21 + ["采集资源"] = { -- 22 + "揭示", -- 22 + 0, -- 22 + 1 -- 22 + }, -- 22 + ["可破坏"] = { -- 23 + "揭示", -- 23 + 0, -- 23 + 1 -- 23 + }, -- 23 + ["可攀爬"] = { -- 24 + "揭示", -- 24 + 0, -- 24 + 1 -- 24 + }, -- 24 + ["巨型"] = { -- 25 + "揭示", -- 25 + 0, -- 25 + 0 -- 25 + }, -- 25 + ["迅速Lv2"] = { -- 26 + "揭示", -- 26 + 0, -- 26 + 0 -- 26 + } -- 26 + }, -- 16 + ["侦查"] = { -- 29 + ["生物"] = { -- 29 + "揭示", -- 29 + 1, -- 29 + 1 -- 29 + }, -- 29 + ["挖掘资源"] = { -- 30 + "揭示", -- 30 + 1, -- 30 + 1 -- 30 + }, -- 30 + ["采集资源"] = { -- 31 + "揭示", -- 31 + 1, -- 31 + 1 -- 31 + }, -- 31 + ["可破坏"] = { -- 32 + "揭示", -- 32 + 1, -- 32 + 1 -- 32 + }, -- 32 + ["可攀爬"] = { -- 33 + "揭示", -- 33 + 1, -- 33 + 1 -- 33 + }, -- 33 + ["飞行"] = { -- 34 + "揭示", -- 34 + 1, -- 34 + 1 -- 34 + }, -- 34 + ["大型"] = { -- 35 + "揭示", -- 35 + 1, -- 35 + 1 -- 35 + }, -- 35 + ["巨型"] = { -- 36 + "揭示", -- 36 + 1, -- 36 + 1 -- 36 + }, -- 36 + ["迅速Lv1"] = { -- 37 + "揭示", -- 37 + 1, -- 37 + 1 -- 37 + }, -- 37 + ["迅速Lv2"] = { -- 38 + "揭示", -- 38 + 1, -- 38 + 1 -- 38 + } -- 38 + }, -- 28 + ["攀爬"] = { -- 41 + ["友善"] = { -- 41 + "擒抱", -- 41 + 0, -- 41 + 1 -- 41 + }, -- 41 + ["中立"] = { -- 42 + "擒抱", -- 42 + 0, -- 42 + 1 -- 42 + }, -- 42 + ["可攀爬"] = { -- 43 + "攀爬", -- 43 + 1, -- 43 + 1 -- 43 + }, -- 43 + ["大型"] = { -- 44 + "攀爬", -- 44 + 1, -- 44 + 1 -- 44 + }, -- 44 + ["巨型"] = { -- 45 + "攀爬", -- 45 + 1, -- 45 + 1 -- 45 + } -- 45 + }, -- 40 + ["挥舞"] = { -- 48 + ["生物"] = { -- 48 + "伤害", -- 48 + 0, -- 48 + 1 -- 48 + }, -- 48 + ["友善"] = { -- 49 + "取消伤害" -- 49 + }, -- 49 + ["中立"] = { -- 50 + "取消伤害" -- 50 + }, -- 50 + ["敌对"] = { -- 51 + "伤害", -- 51 + 0, -- 51 + 1 -- 51 + }, -- 51 + ["挖掘资源"] = { -- 52 + "采集", -- 52 + 0, -- 52 + 1 -- 52 + }, -- 52 + ["采集资源"] = { -- 53 + "采集", -- 53 + 0, -- 53 + 1 -- 53 + }, -- 53 + ["可破坏"] = { -- 54 + "破坏", -- 54 + 0, -- 54 + 1 -- 54 + }, -- 54 + ["可攀爬"] = { -- 55 + "破坏", -- 55 + 0, -- 55 + 1 -- 55 + }, -- 55 + ["飞行"] = { -- 56 + "伤害", -- 56 + -1, -- 56 + 1 -- 56 + }, -- 56 + ["大型"] = { -- 57 + "伤害", -- 57 + 0, -- 57 + 1 -- 57 + }, -- 57 + ["巨型"] = { -- 58 + "伤害", -- 58 + 0, -- 58 + 1 -- 58 + }, -- 58 + ["迅速Lv1"] = { -- 59 + "伤害", -- 59 + 0, -- 59 + 0 -- 59 + }, -- 59 + ["迅速Lv2"] = { -- 60 + "伤害", -- 60 + 0, -- 60 + 0 -- 60 + } -- 60 + }, -- 47 + ["攻击"] = { -- 63 + ["生物"] = { -- 63 + "伤害", -- 63 + 1, -- 63 + 1 -- 63 + }, -- 63 + ["友善"] = { -- 64 + "取消伤害" -- 64 + }, -- 64 + ["中立"] = { -- 65 + "伤害", -- 65 + 0, -- 65 + 1 -- 65 + }, -- 65 + ["敌对"] = { -- 66 + "伤害", -- 66 + 1, -- 66 + 1 -- 66 + }, -- 66 + ["挖掘资源"] = { -- 67 + "破坏", -- 67 + 0, -- 67 + 1 -- 67 + }, -- 67 + ["采集资源"] = { -- 68 + "采集", -- 68 + 0, -- 68 + 1 -- 68 + }, -- 68 + ["可破坏"] = { -- 69 + "破坏", -- 69 + 1, -- 69 + 1 -- 69 + }, -- 69 + ["飞行"] = { -- 70 + "伤害", -- 70 + -1, -- 70 + 1 -- 70 + }, -- 70 + ["大型"] = { -- 71 + "伤害", -- 71 + 1, -- 71 + 1 -- 71 + }, -- 71 + ["巨型"] = { -- 72 + "伤害", -- 72 + 1, -- 72 + 1 -- 72 + }, -- 72 + ["迅速Lv1"] = { -- 73 + "伤害", -- 73 + 0, -- 73 + 1 -- 73 + }, -- 73 + ["迅速Lv2"] = { -- 74 + "伤害", -- 74 + 0, -- 74 + 1 -- 74 + } -- 74 + }, -- 62 + ["破坏"] = { -- 77 + ["生物"] = { -- 77 + "伤害", -- 77 + 0, -- 77 + 1 -- 77 + }, -- 77 + ["友善"] = { -- 78 + "取消伤害" -- 78 + }, -- 78 + ["中立"] = { -- 79 + "伤害", -- 79 + 0, -- 79 + 1 -- 79 + }, -- 79 + ["敌对"] = { -- 80 + "伤害", -- 80 + 0, -- 80 + 1 -- 80 + }, -- 80 + ["挖掘资源"] = { -- 81 + "采集", -- 81 + 0, -- 81 + 1 -- 81 + }, -- 81 + ["采集资源"] = { -- 82 + "破坏", -- 82 + 1, -- 82 + 1 -- 82 + }, -- 82 + ["可破坏"] = { -- 83 + "破坏", -- 83 + 1, -- 83 + 1 -- 83 + }, -- 83 + ["巨型"] = { -- 84 + "伤害", -- 84 + 1, -- 84 + 1 -- 84 + }, -- 84 + ["迅速Lv1"] = { -- 85 + "伤害", -- 85 + 0, -- 85 + 0 -- 85 + }, -- 85 + ["迅速Lv2"] = { -- 86 + "伤害", -- 86 + 0, -- 86 + 0 -- 86 + } -- 86 + }, -- 76 + ["投掷"] = { -- 89 + ["生物"] = { -- 89 + "伤害", -- 89 + 1, -- 89 + 1 -- 89 + }, -- 89 + ["友善"] = { -- 90 + "取消伤害" -- 90 + }, -- 90 + ["中立"] = { -- 91 + "伤害", -- 91 + 0, -- 91 + 1 -- 91 + }, -- 91 + ["敌对"] = { -- 92 + "伤害", -- 92 + 1, -- 92 + 1 -- 92 + }, -- 92 + ["可破坏"] = { -- 93 + "破坏", -- 93 + 1, -- 93 + 1 -- 93 + }, -- 93 + ["飞行"] = { -- 94 + "伤害", -- 94 + 1, -- 94 + 1 -- 94 + }, -- 94 + ["大型"] = { -- 95 + "伤害", -- 95 + 1, -- 95 + 1 -- 95 + }, -- 95 + ["巨型"] = { -- 96 + "伤害", -- 96 + 1, -- 96 + 1 -- 96 + }, -- 96 + ["迅速Lv1"] = { -- 97 + "伤害", -- 97 + 0, -- 97 + 1 -- 97 + }, -- 97 + ["迅速Lv2"] = { -- 98 + "伤害", -- 98 + 0, -- 98 + 1 -- 98 + } -- 98 + }, -- 88 + ["采集"] = { -- 101 + ["生物"] = { -- 101 + "伤害", -- 101 + 0, -- 101 + 1 -- 101 + }, -- 101 + ["友善"] = { -- 102 + "伤害", -- 102 + 0, -- 102 + 1 -- 102 + }, -- 102 + ["中立"] = { -- 103 + "伤害", -- 103 + 0, -- 103 + 1 -- 103 + }, -- 103 + ["敌对"] = { -- 104 + "伤害", -- 104 + 0, -- 104 + 1 -- 104 + }, -- 104 + ["挖掘资源"] = { -- 105 + "采集", -- 105 + 0, -- 105 + 1 -- 105 + }, -- 105 + ["采集资源"] = { -- 106 + "采集", -- 106 + 0, -- 106 + 1 -- 106 + }, -- 106 + ["可破坏"] = { -- 107 + "揭示", -- 107 + 0, -- 107 + 1 -- 107 + }, -- 107 + ["可攀爬"] = { -- 108 + "揭示", -- 108 + 0, -- 108 + 1 -- 108 + }, -- 108 + ["大型"] = { -- 109 + "伤害", -- 109 + 0, -- 109 + 1 -- 109 + } -- 109 + }, -- 100 + ["挖掘"] = { -- 112 + ["挖掘资源"] = { -- 112 + "采集", -- 112 + 1, -- 112 + 1 -- 112 + }, -- 112 + ["采集资源"] = { -- 113 + "采集", -- 113 + 0, -- 113 + 1 -- 113 + }, -- 113 + ["可破坏"] = { -- 114 + "破坏", -- 114 + 1, -- 114 + 1 -- 114 + }, -- 114 + ["可攀爬"] = { -- 115 + "破坏", -- 115 + 0, -- 115 + 1 -- 115 + } -- 115 + }, -- 111 + ["采收"] = { -- 118 + ["友善"] = { -- 118 + "采集", -- 118 + 0, -- 118 + 1 -- 118 + }, -- 118 + ["挖掘资源"] = { -- 119 + "采集", -- 119 + 0, -- 119 + 1 -- 119 + }, -- 119 + ["采集资源"] = { -- 120 + "采集", -- 120 + 1, -- 120 + 1 -- 120 + } -- 120 + }, -- 117 + ["沟通"] = { -- 123 + ["生物"] = { -- 123 + "揭示", -- 123 + 0, -- 123 + 1 -- 123 + }, -- 123 + ["友善"] = { -- 124 + "交涉", -- 124 + 0, -- 124 + 1 -- 124 + }, -- 124 + ["中立"] = { -- 125 + "交涉", -- 125 + 0, -- 125 + 1 -- 125 + }, -- 125 + ["敌对"] = { -- 126 + "揭示", -- 126 + 0, -- 126 + 1 -- 126 + }, -- 126 + ["挖掘资源"] = { -- 127 + "揭示", -- 127 + 0, -- 127 + 1 -- 127 + }, -- 127 + ["采集资源"] = { -- 128 + "揭示", -- 128 + 0, -- 128 + 1 -- 128 + }, -- 128 + ["巨型"] = { -- 129 + "交涉", -- 129 + 0, -- 129 + 0 -- 129 + } -- 129 + }, -- 122 + ["鼓舞"] = { -- 132 + ["友善"] = { -- 132 + "交涉", -- 132 + 0, -- 132 + 1 -- 132 + }, -- 132 + ["中立"] = { -- 133 + "交涉", -- 133 + 0, -- 133 + 1 -- 133 + } -- 133 + }, -- 131 + ["恐吓"] = { -- 136 + ["生物"] = { -- 136 + "恐吓", -- 136 + 1, -- 136 + 1 -- 136 + }, -- 136 + ["友善"] = { -- 137 + "恐吓", -- 137 + 1, -- 137 + 1 -- 137 + }, -- 137 + ["中立"] = { -- 138 + "恐吓", -- 138 + 1, -- 138 + 1 -- 138 + }, -- 138 + ["敌对"] = { -- 139 + "恐吓", -- 139 + 1, -- 139 + 1 -- 139 + }, -- 139 + ["飞行"] = { -- 140 + "恐吓", -- 140 + 1, -- 140 + 1 -- 140 + }, -- 140 + ["大型"] = { -- 141 + "恐吓", -- 141 + 0, -- 141 + 1 -- 141 + } -- 141 + } -- 135 +} -- 15 +local newCreature -- 145 +newCreature = function() -- 145 + local hints = { } -- 146 + local values = { } -- 147 + local tags = { } -- 148 + local record = { } -- 149 + local relationIndex = App.rand % #relationTags -- 150 + tags[#tags + 1] = relationTags[relationIndex + 1] -- 151 + hints[#hints + 1] = #relationTags -- 152 + values[#values + 1] = relationIndex -- 153 + record[#record + 1] = relationTags[relationIndex + 1] -- 154 + local bodyTypeIndex = App.rand % (#bodyTypes + 1) -- 155 + if bodyTypeIndex ~= 0 then -- 156 + tags[#tags + 1] = bodyTypes[bodyTypeIndex] -- 157 + record[#record + 1] = bodyTypes[bodyTypeIndex] -- 158 + else -- 160 + record[#record + 1] = "无" -- 160 + end -- 156 + hints[#hints + 1] = #bodyTypes + 1 -- 161 + values[#values + 1] = bodyTypeIndex -- 162 + local skillIndex = App.rand % (#skills + 1) -- 163 + if skillIndex ~= 0 then -- 164 + tags[#tags + 1] = skills[skillIndex] -- 165 + record[#record + 1] = skills[skillIndex] -- 166 + else -- 168 + record[#record + 1] = "无" -- 168 + end -- 164 + hints[#hints + 1] = #skills + 1 -- 169 + values[#values + 1] = skillIndex -- 170 + for i = 1, #unitTags do -- 171 + hints[#hints + 1] = 2 -- 172 + if App.rand % 2 == 1 then -- 173 + tags[#tags + 1] = unitTags[i] -- 174 + values[#values + 1] = 1 -- 175 + record[#record + 1] = "有" -- 176 + else -- 178 + values[#values + 1] = 0 -- 178 + record[#record + 1] = "无" -- 179 + end -- 173 + end -- 179 + return { -- 181 + name = table.concat(tags, ","), -- 181 + tags = tags, -- 182 + hints = hints, -- 183 + values = values, -- 184 + record = record -- 185 + } -- 186 +end -- 145 +local ql = ML.QLearner() -- 188 +local getEffect -- 190 +getEffect = function(tags, action) -- 190 + local effects = actionEffects[actions[action]] -- 191 + local cancelHarm = false -- 192 + local eset = { } -- 193 + for _index_0 = 1, #tags do -- 194 + local tag = tags[_index_0] -- 194 + local eff = effects[tag] -- 195 + if not eff then -- 196 + goto _continue_0 -- 196 + end -- 196 + if eff[1] == "取消伤害" then -- 197 + cancelHarm = true -- 198 + else -- 200 + do -- 200 + local e = eset[eff[1]] -- 200 + if e then -- 200 + local _update_0 = 1 -- 201 + e[_update_0] = e[_update_0] + eff[2] -- 201 + e[2] = math.max(e[2], eff[3]) -- 202 + else -- 204 + eset[eff[1]] = { -- 204 + eff[2], -- 204 + eff[3] -- 204 + } -- 204 + end -- 200 + end -- 200 + end -- 197 + ::_continue_0:: -- 195 + end -- 204 + if cancelHarm then -- 205 + eset["伤害"] = nil -- 206 + end -- 205 + local _accum_0 = { } -- 207 + local _len_0 = 1 -- 207 + for k, v in pairs(eset) do -- 207 + local p = math.min(100, 50 + 20 * v[1]) -- 208 + if (1 + App.rand % 100) <= p then -- 209 + _accum_0[_len_0] = { -- 210 + k, -- 210 + v[2] -- 210 + } -- 210 + else -- 211 + goto _continue_1 -- 211 + end -- 209 + _len_0 = _len_0 + 1 -- 211 + ::_continue_1:: -- 208 + end -- 211 + return _accum_0 -- 211 +end -- 190 +local newRoundTraining -- 213 +newRoundTraining = function() -- 213 + local result = { } -- 214 + while #result == 0 do -- 215 + local unit = newCreature() -- 216 + local state = ML.QLearner:pack(unit.hints, unit.values) -- 217 + local action = ql:getBestAction(state) -- 218 + local randomAction = false -- 219 + if action == 0 then -- 220 + randomAction = true -- 221 + action = App.rand % #actions + 1 -- 222 + end -- 220 + do -- 223 + local _obj_0 = unit.record -- 223 + _obj_0[#_obj_0 + 1] = actions[action] -- 223 + end -- 223 + result = getEffect(unit.tags, action) -- 224 + if #result > 0 then -- 225 + return { -- 227 + name = unit.name, -- 227 + state = state, -- 228 + action = action, -- 229 + result = result, -- 230 + rand = randomAction, -- 231 + record = unit.record -- 232 + } -- 233 + else -- 235 + ql:update(state, action, -1) -- 235 + end -- 225 + end -- 235 +end -- 213 +local training = nil -- 237 +local laborResult = nil -- 238 +local effectFlags -- 239 +do -- 239 + local _accum_0 = { } -- 239 + local _len_0 = 1 -- 239 + for i = 1, #effectNames do -- 239 + _accum_0[_len_0] = false -- 239 + _len_0 = _len_0 + 1 -- 239 + end -- 239 + effectFlags = _accum_0 -- 239 +end -- 239 +local manualOp = 0 -- 240 +local selfTrained = false -- 241 +local records = { -- 243 + { -- 243 + "关系", -- 243 + "体型", -- 243 + "技能", -- 243 + "生物", -- 243 + "挖掘资源", -- 243 + "采集资源", -- 243 + "可破坏", -- 243 + "可攀爬", -- 243 + "飞行", -- 243 + "行动" -- 243 + }, -- 243 + { -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C", -- 244 + "C" -- 244 + } -- 244 +} -- 242 +local decisionStr = nil -- 246 +local windowFlags = { -- 247 + "NoResize", -- 247 + "NoSavedSettings" -- 247 +} -- 247 +return threadLoop(function() -- 248 + local width, height -- 249 + do -- 249 + local _obj_0 = App.visualSize -- 249 + width, height = _obj_0.width, _obj_0.height -- 249 + end -- 249 + SetNextWindowPos(Vec2(width / 2 - 300, height / 2 - 300), "FirstUseEver") -- 250 + SetNextWindowSize(Vec2(600, 600), "FirstUseEver") -- 251 + return Begin("Fairy", windowFlags, function() -- 252 + if training then -- 253 + TextWrapped("生物: " .. tostring(training.name)) -- 254 + TextWrapped("执行动作: " .. tostring(actions[training.action])) -- 255 + TextWrapped("取得效果: " .. tostring(table.concat((function() -- 256 + local _accum_0 = { } -- 256 + local _len_0 = 1 -- 256 + local _list_0 = training.result -- 256 + for _index_0 = 1, #_list_0 do -- 256 + local item = _list_0[_index_0] -- 256 + _accum_0[_len_0] = tostring(item[1]) .. ":" .. tostring(item[2]) -- 256 + _len_0 = _len_0 + 1 -- 256 + end -- 256 + return _accum_0 -- 256 + end)(), ", "))) -- 256 + TextWrapped("手工训练记录数: " .. tostring(manualOp)) -- 257 + if training.rand then -- 258 + TextWrapped("[执行了随机动作]") -- 259 + else -- 261 + TextWrapped("[执行了已习得动作]") -- 261 + end -- 258 + if Button("表扬") then -- 262 + manualOp = manualOp + 1 -- 263 + ql:update(training.state, training.action, 1) -- 264 + training = newRoundTraining() -- 265 + records[#records + 1] = training.record -- 266 + end -- 262 + SameLine() -- 267 + if Button("批评") then -- 268 + manualOp = manualOp + 1 -- 269 + ql:update(training.state, training.action, -1) -- 270 + training = newRoundTraining() -- 271 + end -- 268 + SameLine() -- 272 + if Button("跳过") then -- 273 + training = newRoundTraining() -- 274 + end -- 273 + else -- 276 + if Button("开始人工训练") then -- 276 + training = newRoundTraining() -- 277 + end -- 276 + end -- 253 + Separator() -- 278 + if Button("对付100个随机生物") then -- 279 + local result = { } -- 280 + local validAction = 0 -- 281 + for i = 1, 100 do -- 282 + local res = newRoundTraining() -- 283 + if not res.rand then -- 284 + validAction = validAction + 1 -- 284 + end -- 284 + local _list_0 = res.result -- 285 + for _index_0 = 1, #_list_0 do -- 285 + local item = _list_0[_index_0] -- 285 + if result[item[1]] then -- 286 + local _update_0 = item[1] -- 287 + result[_update_0] = result[_update_0] + item[2] -- 287 + else -- 289 + result[item[1]] = item[2] -- 289 + end -- 286 + end -- 289 + end -- 289 + do -- 290 + local _accum_0 = { } -- 290 + local _len_0 = 1 -- 290 + for k, v in pairs(result) do -- 290 + _accum_0[_len_0] = { -- 290 + k, -- 290 + v -- 290 + } -- 290 + _len_0 = _len_0 + 1 -- 290 + end -- 290 + result = _accum_0 -- 290 + end -- 290 + table.sort(result, function(a, b) -- 291 + return b[2] < a[2] -- 291 + end) -- 291 + laborResult = table.concat((function() -- 292 + local _accum_0 = { } -- 292 + local _len_0 = 1 -- 292 + for _index_0 = 1, #result do -- 292 + local _des_0 = result[_index_0] -- 292 + local k, v = _des_0[1], _des_0[2] -- 292 + _accum_0[_len_0] = tostring(k) .. ":" .. tostring(v) -- 292 + _len_0 = _len_0 + 1 -- 292 + end -- 292 + return _accum_0 -- 292 + end)(), ", ") -- 292 + laborResult = laborResult .. "\n习得动作生效次数: " .. tostring(validAction) .. "/100" -- 293 + end -- 279 + if laborResult then -- 294 + TextWrapped(laborResult) -- 294 + end -- 294 + Separator() -- 295 + local doSelfTraining = false -- 296 + if selfTrained then -- 297 + local target = table.concat((function() -- 298 + local _accum_0 = { } -- 298 + local _len_0 = 1 -- 298 + for i = 1, #effectFlags do -- 298 + if effectFlags[i] then -- 298 + _accum_0[_len_0] = effectNames[i] -- 298 + _len_0 = _len_0 + 1 -- 298 + end -- 298 + end -- 298 + return _accum_0 -- 298 + end)(), ", ") -- 298 + TextWrapped("已完成自我训练, 目标: " .. tostring(target)) -- 299 + if Button("遗忘") then -- 300 + selfTrained = false -- 301 + ql = ML.QLearner() -- 302 + end -- 300 + else -- 304 + TextWrapped("选择训练目标") -- 304 + for i = 1, #effectFlags do -- 305 + local _ -- 306 + _, effectFlags[i] = Checkbox(effectNames[i], effectFlags[i]) -- 306 + end -- 306 + doSelfTraining = Button("进行自我训练") -- 307 + end -- 297 + if doSelfTraining then -- 308 + selfTrained = true -- 309 + ql = ML.QLearner() -- 310 + local targetEffects -- 311 + do -- 311 + local _tbl_0 = { } -- 311 + for i = 1, #effectFlags do -- 311 + if effectFlags[i] then -- 311 + _tbl_0[effectNames[i]] = true -- 311 + end -- 311 + end -- 311 + targetEffects = _tbl_0 -- 311 + end -- 311 + local hints = { -- 313 + #relationTags, -- 313 + #bodyTypes + 1, -- 314 + #skills + 1 -- 315 + } -- 312 + for i = 1, #unitTags do -- 317 + hints[#hints + 1] = 2 -- 318 + end -- 318 + local values = { } -- 319 + local l1 = #relationTags - 1 -- 320 + local l2 = #bodyTypes -- 321 + local l3 = #skills -- 322 + for i1 = 0, l1 do -- 323 + for i2 = 0, l2 do -- 323 + for i3 = 0, l3 do -- 323 + for i4 = 0, 1 do -- 324 + for i5 = 0, 1 do -- 324 + for i6 = 0, 1 do -- 324 + for i7 = 0, 1 do -- 325 + for i8 = 0, 1 do -- 325 + for i9 = 0, 1 do -- 325 + local tags = { } -- 326 + tags[#tags + 1] = relationTags[i1 + 1] -- 327 + local bodyTypeIndex = i2 -- 328 + if bodyTypeIndex ~= 0 then -- 329 + tags[#tags + 1] = bodyTypes[bodyTypeIndex] -- 330 + end -- 329 + local skillIndex = i3 -- 331 + if skillIndex ~= 0 then -- 332 + tags[#tags + 1] = skills[skillIndex] -- 333 + end -- 332 + if i4 ~= 0 then -- 334 + tags[#tags + 1] = unitTags[1] -- 334 + end -- 334 + if i5 ~= 0 then -- 335 + tags[#tags + 1] = unitTags[2] -- 335 + end -- 335 + if i6 ~= 0 then -- 336 + tags[#tags + 1] = unitTags[3] -- 336 + end -- 336 + if i7 ~= 0 then -- 337 + tags[#tags + 1] = unitTags[4] -- 337 + end -- 337 + if i8 ~= 0 then -- 338 + tags[#tags + 1] = unitTags[5] -- 338 + end -- 338 + if i9 ~= 0 then -- 339 + tags[#tags + 1] = unitTags[6] -- 339 + end -- 339 + local state = ML.QLearner:pack(hints, { -- 340 + i1, -- 340 + i2, -- 340 + i3, -- 340 + i4, -- 340 + i5, -- 340 + i6, -- 340 + i7, -- 340 + i8, -- 340 + i9 -- 340 + }) -- 340 + for action = 1, #actions do -- 341 + local result = getEffect(tags, action) -- 342 + local r = 0 -- 343 + for _index_0 = 1, #result do -- 344 + local _des_0 = result[_index_0] -- 344 + local k, v = _des_0[1], _des_0[2] -- 344 + if targetEffects[k] then -- 345 + r = r + v -- 346 + end -- 345 + end -- 346 + ql:update(state, action, r == 0 and -1 or r) -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 347 + end -- 308 + Separator() -- 348 + TextWrapped("总结人工训练思维逻辑") -- 349 + if Button("开始总结") and #records > 2 then -- 350 + local dataStr = table.concat((function() -- 351 + local _accum_0 = { } -- 351 + local _len_0 = 1 -- 351 + for _index_0 = 1, #records do -- 351 + local r = records[_index_0] -- 351 + _accum_0[_len_0] = table.concat(r, ",") -- 351 + _len_0 = _len_0 + 1 -- 351 + end -- 351 + return _accum_0 -- 351 + end)(), "\n") -- 351 + thread(function() -- 352 + local lines = { } -- 353 + ML.BuildDecisionTreeAsync(dataStr, 0, function(depth, name, op, value) -- 354 + local line = string.rep("\t", depth) .. (function() -- 355 + if name ~= "" then -- 355 + return "if " .. tostring(name) .. " " .. tostring(op) .. " " .. tostring(op == '==' and "\"" .. tostring(value) .. "\"" or value) -- 356 + else -- 358 + return tostring(op) .. " \"" .. tostring(value) .. "\"" -- 358 + end -- 355 + end)() -- 355 + lines[#lines + 1] = line -- 359 + end) -- 354 + decisionStr = table.concat(lines, "\n") -- 360 + end) -- 352 + end -- 350 + if decisionStr then -- 361 + return TextWrapped(decisionStr) -- 361 + end -- 361 + end) -- 361 +end) -- 361 diff --git a/Assets/Script/Example/Model.lua b/Assets/Script/Example/Model.lua new file mode 100644 index 000000000..9835d9b87 --- /dev/null +++ b/Assets/Script/Example/Model.lua @@ -0,0 +1,83 @@ +-- [yue]: Script/Example/Model.yue +local Model = dora.Model -- 1 +local print = _G.print -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local Vec2 = dora.Vec2 -- 1 +local modelFile = "Model/xiaoli.model" -- 3 +local model -- 5 +do -- 5 + local _with_0 = Model(modelFile) -- 5 + _with_0.recovery = 0.2 -- 6 + _with_0.look = "happy" -- 7 + _with_0:play("walk", true) -- 8 + _with_0:slot("AnimationEnd", function(name) -- 9 + return print(name, "end") -- 9 + end) -- 9 + model = _with_0 -- 5 +end -- 5 +local looks = Model:getLooks(modelFile) -- 13 +if #looks == 0 then -- 14 + looks[#looks + 1] = "" -- 14 +end -- 14 +local animations = Model:getAnimations(modelFile) -- 15 +if #animations == 0 then -- 16 + animations[#animations + 1] = "" -- 16 +end -- 16 +local currentLook = #looks -- 17 +local currentAnim = #animations -- 18 +local loop = true -- 19 +local windowFlags = { -- 20 + "NoResize", -- 20 + "NoSavedSettings" -- 20 +} -- 20 +return threadLoop(function() -- 21 + local width -- 22 + width = App.visualSize.width -- 22 + ImGui.SetNextWindowPos(Vec2(width - 250, 10), "FirstUseEver") -- 23 + ImGui.SetNextWindowSize(Vec2(240, 325), "FirstUseEver") -- 24 + return ImGui.Begin("Model", windowFlags, function() -- 25 + do -- 26 + local changed -- 26 + changed, currentLook = ImGui.Combo("Look", currentLook, looks) -- 26 + if changed then -- 26 + model.look = looks[currentLook] -- 27 + end -- 26 + end -- 26 + do -- 28 + local changed -- 28 + changed, currentAnim = ImGui.Combo("Anim", currentAnim, animations) -- 28 + if changed then -- 28 + model:play(animations[currentAnim], loop) -- 29 + end -- 28 + end -- 28 + do -- 30 + local changed -- 30 + changed, loop = ImGui.Checkbox("Loop", loop) -- 30 + if changed then -- 30 + model:play(animations[currentAnim], loop) -- 31 + end -- 30 + end -- 30 + ImGui.SameLine() -- 32 + do -- 33 + local changed -- 33 + changed, model.reversed = ImGui.Checkbox("Reversed", model.reversed) -- 33 + if changed then -- 33 + model:play(animations[currentAnim], loop) -- 34 + end -- 33 + end -- 33 + ImGui.PushItemWidth(-70, function() -- 35 + local _ -- 36 + _, model.speed = ImGui.DragFloat("Speed", model.speed, 0.01, 0, 10, "%.2f") -- 36 + _, model.recovery = ImGui.DragFloat("Recovery", model.recovery, 0.01, 0, 10, "%.2f") -- 37 + end) -- 35 + local scale = model.scaleX -- 38 + local _ -- 39 + _, scale = ImGui.DragFloat("Scale", scale, 0.01, 0.5, 2, "%.2f") -- 39 + model.scaleX, model.scaleY = scale, scale -- 40 + if ImGui.Button("Play", Vec2(140, 30)) then -- 41 + return model:play(animations[currentAnim], loop) -- 42 + end -- 41 + end) -- 42 +end) -- 42 diff --git a/Assets/Script/Example/MultiTasking.lua b/Assets/Script/Example/MultiTasking.lua new file mode 100644 index 000000000..049061f62 --- /dev/null +++ b/Assets/Script/Example/MultiTasking.lua @@ -0,0 +1,68 @@ +-- [yue]: Script/Example/MultiTasking.yue +local thread = dora.thread -- 1 +local print = _G.print -- 1 +local Content = dora.Content -- 1 +local sleep = dora.sleep -- 1 +local string = _G.string -- 1 +local tostring = _G.tostring -- 1 +local App = dora.App -- 1 +local threadLoop = dora.threadLoop -- 1 +local math = _G.math -- 1 +local Node = dora.Node -- 1 +local once = dora.once -- 1 +local loop = dora.loop -- 1 +local ImGui = dora.ImGui -- 1 +local Vec2 = dora.Vec2 -- 1 +thread(function() -- 3 + print("thread 1") -- 4 + local yueCodes = Content:loadAsync("Script/Example/MultiTasking.yue") -- 5 + sleep(2) -- 6 + local yue = require("yue") -- 7 + local luaCodes = yue.to_lua(yueCodes) -- 8 + print(luaCodes) -- 9 + print("thread 1 done") -- 10 + return thread(function() -- 12 + print("thread 2 stared") -- 13 + repeat -- 14 + print("thread 2 Time passed: " .. tostring(string.format("%.2fs", App.totalTime))) -- 15 + sleep(1) -- 16 + until false -- 17 + end) -- 17 +end) -- 3 +threadLoop(function() -- 19 + print("thread 3") -- 20 + sleep(math.random(3)) -- 21 + print("do nothing") -- 22 + return sleep(0.2) -- 23 +end) -- 19 +do -- 25 + local _with_0 = Node() -- 25 + _with_0:schedule(once(function() -- 26 + sleep(5) -- 27 + print("5 seconds later") -- 28 + return _with_0:schedule(loop(function() -- 29 + sleep(3) -- 30 + return print("another 3 seconds") -- 31 + end)) -- 31 + end)) -- 26 +end -- 25 +local windowFlags = { -- 36 + "NoDecoration", -- 36 + "AlwaysAutoResize", -- 37 + "NoSavedSettings", -- 38 + "NoFocusOnAppearing", -- 39 + "NoNav", -- 40 + "NoMove" -- 41 +} -- 35 +return threadLoop(function() -- 42 + local width -- 43 + width = App.visualSize.width -- 43 + ImGui.SetNextWindowBgAlpha(0.35) -- 44 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 45 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 46 + return ImGui.Begin("Multi-tasking", windowFlags, function() -- 47 + ImGui.Text("Multi-tasking") -- 48 + ImGui.Separator() -- 49 + return ImGui.TextWrapped("Basic Dora multi-tasking usage. Powered by View outputs in log window!") -- 50 + end) -- 50 +end) -- 50 diff --git a/Assets/Script/Example/Particle.lua b/Assets/Script/Example/Particle.lua new file mode 100644 index 000000000..bc24de781 --- /dev/null +++ b/Assets/Script/Example/Particle.lua @@ -0,0 +1,369 @@ +-- [yue]: Script/Example/Particle.yue +local BlendFunc = dora.BlendFunc -- 1 +local Color = dora.Color -- 1 +local Vec2 = dora.Vec2 -- 1 +local Buffer = dora.Buffer -- 1 +local Rect = dora.Rect -- 1 +local tolua = dora.tolua -- 1 +local tostring = _G.tostring -- 1 +local string = _G.string -- 1 +local Cache = dora.Cache -- 1 +local table = _G.table -- 1 +local pairs = _G.pairs -- 1 +local Particle = dora.Particle -- 1 +local Node = dora.Node -- 1 +local _module_0 = dora.ImGui -- 1 +local PushItemWidth = _module_0.PushItemWidth -- 1 +local DragFloat = _module_0.DragFloat -- 1 +local DragInt = _module_0.DragInt -- 1 +local math = _G.math -- 1 +local LabelText = _module_0.LabelText -- 1 +local DragInt2 = _module_0.DragInt2 -- 1 +local SetColorEditOptions = _module_0.SetColorEditOptions -- 1 +local ColorEdit4 = _module_0.ColorEdit4 -- 1 +local Checkbox = _module_0.Checkbox -- 1 +local InputText = _module_0.InputText -- 1 +local coroutine = _G.coroutine -- 1 +local sleep = dora.sleep -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local Button = _module_0.Button -- 1 +local print = _G.print -- 1 +local Data = { -- 4 + Angle = { -- 4 + "B", -- 4 + "float", -- 4 + 90 -- 4 + }, -- 4 + AngleVariance = { -- 5 + "C", -- 5 + "float", -- 5 + 360 -- 5 + }, -- 5 + BlendFuncDestination = { -- 6 + "D", -- 6 + "BlendFunc", -- 6 + BlendFunc:get("One") -- 6 + }, -- 6 + BlendFuncSource = { -- 7 + "E", -- 7 + " BlendFunc", -- 7 + BlendFunc:get("SrcAlpha") -- 7 + }, -- 7 + Duration = { -- 8 + "F", -- 8 + "floatN", -- 8 + -1 -- 8 + }, -- 8 + EmissionRate = { -- 9 + "G", -- 9 + "float", -- 9 + 350 -- 9 + }, -- 9 + FinishColor = { -- 10 + "H", -- 10 + "Color", -- 10 + Color(0xff000000) -- 10 + }, -- 10 + FinishColorVariance = { -- 11 + "I", -- 11 + "Color", -- 11 + Color(0x0) -- 11 + }, -- 11 + RotationStart = { -- 12 + "J", -- 12 + "float", -- 12 + 0 -- 12 + }, -- 12 + RotationStartVariance = { -- 13 + "K", -- 13 + "float", -- 13 + 0 -- 13 + }, -- 13 + RotationEnd = { -- 14 + "L", -- 14 + "float", -- 14 + 0 -- 14 + }, -- 14 + RotationEndVariance = { -- 15 + "M", -- 15 + "float", -- 15 + 0 -- 15 + }, -- 15 + FinishParticleSize = { -- 16 + "N", -- 16 + "floatN", -- 16 + -1 -- 16 + }, -- 16 + FinishParticleSizeVariance = { -- 17 + "O", -- 17 + "float", -- 17 + 0 -- 17 + }, -- 17 + MaxParticles = { -- 18 + "P", -- 18 + "Uint32", -- 18 + 100 -- 18 + }, -- 18 + ParticleLifespan = { -- 19 + "Q", -- 19 + "float", -- 19 + 1 -- 19 + }, -- 19 + ParticleLifespanVariance = { -- 20 + "R", -- 20 + "float", -- 20 + 0.5 -- 20 + }, -- 20 + StartPosition = { -- 21 + "S", -- 21 + "Vec2", -- 21 + Vec2(0, 0) -- 21 + }, -- 21 + StartPositionVariance = { -- 22 + "T", -- 22 + "Vec2", -- 22 + Vec2(0, 0) -- 22 + }, -- 22 + StartColor = { -- 23 + "U", -- 23 + "Color", -- 23 + Color(194, 64, 31, 255) -- 23 + }, -- 23 + StartColorVariance = { -- 24 + "V", -- 24 + "Color", -- 24 + Color(0x0) -- 24 + }, -- 24 + StartParticleSize = { -- 25 + "W", -- 25 + "float", -- 25 + 30 -- 25 + }, -- 25 + StartParticleSizeVariance = { -- 26 + "X", -- 26 + "float", -- 26 + 10 -- 26 + }, -- 26 + TextureName = { -- 27 + "Y", -- 27 + "string", -- 27 + "", -- 27 + Buffer(256) -- 27 + }, -- 27 + TextureRect = { -- 28 + "Z", -- 28 + "Rect", -- 28 + Rect(0, 0, 0, 0) -- 28 + }, -- 28 + EmitterType = { -- 29 + "a", -- 29 + "EmitterType", -- 29 + 0 -- 29 + }, -- 29 + RotationIsDir = { -- 31 + "b", -- 31 + "bool", -- 31 + false -- 31 + }, -- 31 + Gravity = { -- 32 + "c", -- 32 + "Vec2", -- 32 + Vec2(0, 100) -- 32 + }, -- 32 + Speed = { -- 33 + "d", -- 33 + "float", -- 33 + 20 -- 33 + }, -- 33 + SpeedVariance = { -- 34 + "e", -- 34 + "float", -- 34 + 5 -- 34 + }, -- 34 + RadialAcceleration = { -- 35 + "f", -- 35 + "float", -- 35 + 0 -- 35 + }, -- 35 + RadialAccelVariance = { -- 36 + "g", -- 36 + "float", -- 36 + 0 -- 36 + }, -- 36 + TangentialAcceleration = { -- 37 + "h", -- 37 + "float", -- 37 + 0 -- 37 + }, -- 37 + TangentialAccelVariance = { -- 38 + "i", -- 38 + "float", -- 38 + 0 -- 38 + } -- 38 +} -- 3 +local toString -- 48 +toString = function(value) -- 48 + local _exp_0 = tolua.type(value) -- 49 + if "number" == _exp_0 then -- 50 + return tostring(value) -- 51 + elseif "string" == _exp_0 then -- 52 + return value -- 53 + elseif "Rect" == _exp_0 then -- 54 + return tostring(value.x) .. "," .. tostring(value.y) .. "," .. tostring(value.width) .. "," .. tostring(value.height) -- 55 + elseif "boolean" == _exp_0 then -- 56 + return value and "1" or "0" -- 57 + elseif "Vec2" == _exp_0 then -- 58 + return tostring(value.x) .. "," .. tostring(value.y) -- 59 + elseif "Color" == _exp_0 then -- 60 + return string.format("%.2f,%.2f,%.2f,%.2f", value.r / 255, value.g / 255, value.b / 255, value.a / 255) -- 61 + end -- 61 +end -- 48 +Cache:update("__test__.par", "" .. table.concat((function() -- 63 + local _accum_0 = { } -- 63 + local _len_0 = 1 -- 63 + for k, v in pairs(Data) do -- 63 + _accum_0[_len_0] = "<" .. tostring(v[1]) .. " A=\"" .. tostring(toString(v[3])) .. "\"/>" -- 63 + _len_0 = _len_0 + 1 -- 63 + end -- 63 + return _accum_0 -- 63 +end)()) .. "") -- 63 +local particle -- 65 +do -- 65 + local _with_0 = Particle("__test__.par") -- 65 + _with_0:start() -- 66 + particle = _with_0 -- 65 +end -- 65 +local root -- 68 +do -- 68 + local _with_0 = Node() -- 68 + _with_0.scaleX = 2 -- 69 + _with_0.scaleY = 2 -- 70 + _with_0:addChild(particle) -- 71 + _with_0.touchEnabled = true -- 72 + _with_0:slot("TapMoved", function(touch) -- 73 + if not touch.first then -- 74 + return -- 74 + end -- 74 + particle.position = particle.position + (touch.delta / 2) -- 75 + end) -- 73 + root = _with_0 -- 68 +end -- 68 +local DataDirty = false -- 79 +local Item -- 81 +Item = function(name) -- 81 + return PushItemWidth(-180, function() -- 82 + local _exp_0 = Data[name][2] -- 83 + if "float" == _exp_0 then -- 84 + local changed -- 85 + changed, Data[name][3] = DragFloat(name, Data[name][3], 0.1, -1000, 1000, "%.1f") -- 85 + if changed then -- 86 + DataDirty = true -- 86 + end -- 86 + elseif "floatN" == _exp_0 then -- 87 + local changed -- 88 + changed, Data[name][3] = DragFloat(name, Data[name][3], 0.1, -1, 1000, "%.1f") -- 88 + if changed then -- 89 + DataDirty = true -- 89 + end -- 89 + elseif "Uint32" == _exp_0 then -- 90 + local changed -- 91 + changed, Data[name][3] = DragInt(name, math.floor(Data[name][3]), 1, 0, 1000) -- 91 + if changed then -- 92 + DataDirty = true -- 92 + end -- 92 + elseif "EmitterType" == _exp_0 then -- 93 + return LabelText("EmitterType", "Gravity") -- 94 + elseif "BlendFunc" == _exp_0 then -- 95 + return LabelText("BlendFunc", "Additive") -- 96 + elseif "Vec2" == _exp_0 then -- 97 + local x, y -- 98 + do -- 98 + local _obj_0 = Data[name][3] -- 98 + x, y = _obj_0.x, _obj_0.y -- 98 + end -- 98 + local changed -- 99 + changed, x, y = DragInt2(name, math.floor(x), math.floor(y), 1, -1000, 1000) -- 99 + if changed then -- 100 + DataDirty, Data[name][3] = true, Vec2(x, y) -- 100 + end -- 100 + elseif "Color" == _exp_0 then -- 101 + return PushItemWidth(-150, function() -- 102 + SetColorEditOptions("RGB") -- 103 + local changed = ColorEdit4(name, Data[name][3]) -- 104 + if changed then -- 105 + DataDirty = true -- 105 + end -- 105 + end) -- 105 + elseif "bool" == _exp_0 then -- 106 + local changed -- 107 + changed, Data[name][3] = Checkbox(name, Data[name][3]) -- 107 + if changed then -- 108 + DataDirty = true -- 108 + end -- 108 + elseif "string" == _exp_0 then -- 109 + local buffer = Data[name][4] -- 110 + local changed = InputText(name, buffer) -- 111 + if changed then -- 112 + DataDirty = true -- 113 + Data[name][3] = buffer:toString() -- 114 + end -- 112 + end -- 114 + end) -- 114 +end -- 81 +local work = coroutine.wrap(function() -- 116 + while true do -- 117 + sleep(1) -- 118 + if DataDirty then -- 119 + DataDirty = false -- 120 + Cache:update("__test__.par", "" .. table.concat((function() -- 121 + local _accum_0 = { } -- 121 + local _len_0 = 1 -- 121 + for k, v in pairs(Data) do -- 121 + _accum_0[_len_0] = "<" .. tostring(v[1]) .. " A=\"" .. tostring(toString(v[3])) .. "\"/>" -- 121 + _len_0 = _len_0 + 1 -- 121 + end -- 121 + return _accum_0 -- 121 + end)()) .. "") -- 121 + particle:removeFromParent() -- 122 + do -- 123 + local _with_0 = Particle("__test__.par") -- 123 + _with_0:start() -- 124 + particle = _with_0 -- 123 + end -- 123 + root:addChild(particle) -- 125 + end -- 119 + end -- 125 +end) -- 116 +return threadLoop(function() -- 127 + local width, height -- 128 + do -- 128 + local _obj_0 = App.visualSize -- 128 + width, height = _obj_0.width, _obj_0.height -- 128 + end -- 128 + SetNextWindowPos(Vec2(width - 400, 10), "FirstUseEver") -- 129 + SetNextWindowSize(Vec2(390, height - 80), "FirstUseEver") -- 130 + Begin("Particle", { -- 131 + "NoResize", -- 131 + "NoSavedSettings" -- 131 + }, function() -- 131 + for k in pairs(Data) do -- 132 + Item(k) -- 133 + end -- 133 + if Button("Export") then -- 134 + return print("" .. table.concat((function() -- 135 + local _accum_0 = { } -- 135 + local _len_0 = 1 -- 135 + for k, v in pairs(Data) do -- 135 + _accum_0[_len_0] = "<" .. tostring(v[1]) .. " A=\"" .. tostring(toString(v[3])) .. "\"/>" -- 135 + _len_0 = _len_0 + 1 -- 135 + end -- 135 + return _accum_0 -- 135 + end)()) .. "") -- 135 + end -- 134 + end) -- 131 + return work() -- 136 +end) -- 136 diff --git a/Assets/Script/Example/RenderGroup.lua b/Assets/Script/Example/RenderGroup.lua new file mode 100644 index 000000000..5918c5ca4 --- /dev/null +++ b/Assets/Script/Example/RenderGroup.lua @@ -0,0 +1,94 @@ +-- [yue]: Script/Example/RenderGroup.yue +local Class = dora.Class -- 1 +local Node = dora.Node -- 1 +local Vec2 = dora.Vec2 -- 1 +local Sprite = dora.Sprite -- 1 +local DrawNode = dora.DrawNode -- 1 +local Color = dora.Color -- 1 +local App = dora.App -- 1 +local Line = dora.Line -- 1 +local Angle = dora.Angle -- 1 +local Size = dora.Size -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local Item = Class(Node, { -- 4 + __init = function(self) -- 4 + self.width = 144 -- 5 + self.height = 144 -- 6 + self.anchor = Vec2.zero -- 7 + self:addChild((function() -- 9 + local _with_0 = Sprite("Image/logo.png") -- 9 + _with_0.scaleX = 0.1 -- 10 + _with_0.scaleY = 0.1 -- 11 + _with_0.renderOrder = 1 -- 12 + return _with_0 -- 9 + end)()) -- 9 + self:addChild((function() -- 14 + local _with_0 = DrawNode() -- 14 + _with_0:drawPolygon({ -- 16 + Vec2(-60, -60), -- 16 + Vec2(60, -60), -- 17 + Vec2(60, 60), -- 18 + Vec2(-60, 60) -- 19 + }, Color(App.themeColor:toColor3(), 0x30)) -- 15 + _with_0.renderOrder = 2 -- 21 + _with_0.angle = 45 -- 22 + return _with_0 -- 14 + end)()) -- 14 + self:addChild((function() -- 24 + local _with_0 = Line({ -- 25 + Vec2(-60, -60), -- 25 + Vec2(60, -60), -- 26 + Vec2(60, 60), -- 27 + Vec2(-60, 60), -- 28 + Vec2(-60, -60) -- 29 + }, Color(0xffff0080)) -- 24 + _with_0.renderOrder = 3 -- 31 + _with_0.angle = 45 -- 32 + return _with_0 -- 24 + end)()) -- 24 + self:runAction(Angle(5, 0, 360)) -- 34 + return self:slot("ActionEnd", function(action) -- 35 + return self:runAction(action) -- 35 + end) -- 35 + end -- 4 +}) -- 3 +local currentEntry -- 37 +do -- 37 + local _with_0 = Node() -- 37 + _with_0.renderGroup = true -- 38 + _with_0.size = Size(750, 750) -- 39 + for i = 1, 16 do -- 40 + _with_0:addChild(Item()) -- 40 + end -- 40 + _with_0:alignItems() -- 41 + currentEntry = _with_0 -- 37 +end -- 37 +local renderGroup = currentEntry.renderGroup -- 45 +local windowFlags = { -- 47 + "NoDecoration", -- 47 + "AlwaysAutoResize", -- 48 + "NoSavedSettings", -- 49 + "NoFocusOnAppearing", -- 50 + "NoNav", -- 51 + "NoMove" -- 52 +} -- 46 +return threadLoop(function() -- 53 + local width -- 54 + width = App.visualSize.width -- 54 + ImGui.SetNextWindowBgAlpha(0.35) -- 55 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 56 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 57 + return ImGui.Begin("Render Group", windowFlags, function() -- 58 + ImGui.Text("Render Group") -- 59 + ImGui.Separator() -- 60 + ImGui.TextWrapped("When render group is enabled, the nodes in the sub render tree will be grouped by \"renderOrder\" property, and get rendered in ascending order!\nNotice the draw call changes in stats window.") -- 61 + do -- 62 + local changed -- 62 + changed, renderGroup = ImGui.Checkbox("Grouped", renderGroup) -- 62 + if changed then -- 62 + currentEntry.renderGroup = renderGroup -- 63 + end -- 62 + end -- 62 + end) -- 63 +end) -- 63 diff --git a/Assets/Script/Example/RenderTarget.lua b/Assets/Script/Example/RenderTarget.lua new file mode 100644 index 000000000..8ad1ef4fd --- /dev/null +++ b/Assets/Script/Example/RenderTarget.lua @@ -0,0 +1,81 @@ +-- [yue]: Script/Example/RenderTarget.yue +local Node = dora.Node -- 1 +local Model = dora.Model -- 1 +local Sequence = dora.Sequence -- 1 +local X = dora.X -- 1 +local Event = dora.Event -- 1 +local RenderTarget = dora.RenderTarget -- 1 +local Color = dora.Color -- 1 +local Sprite = dora.Sprite -- 1 +local Line = dora.Line -- 1 +local Vec2 = dora.Vec2 -- 1 +local App = dora.App -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local node -- 3 +do -- 3 + local _with_0 = Node() -- 3 + _with_0.order = 2 -- 4 + _with_0:addChild((function() -- 5 + local _with_1 = Model("Model/xiaoli.model") -- 5 + _with_1.y = -80 -- 6 + _with_1.fliped = true -- 7 + _with_1.look = "happy" -- 8 + _with_1:play("walk", true) -- 9 + _with_1:runAction(Sequence(X(2, -150, 250), Event("Turn"), X(2, 250, -150), Event("Turn"))) -- 10 + _with_1:slot("ActionEnd", function(action) -- 16 + return _with_1:runAction(action) -- 16 + end) -- 16 + _with_1:slot("Turn", function() -- 17 + _with_1.fliped = not _with_1.fliped -- 17 + end) -- 17 + return _with_1 -- 5 + end)()) -- 5 + node = _with_0 -- 3 +end -- 3 +local renderTarget -- 19 +do -- 19 + local _with_0 = RenderTarget(300, 400) -- 19 + _with_0:renderWithClear(Color(0xff8a8a8a)) -- 20 + renderTarget = _with_0 -- 19 +end -- 19 +local surface -- 22 +do -- 22 + local _with_0 = Sprite(renderTarget.texture) -- 22 + _with_0.order = 1 -- 23 + _with_0.z = 300 -- 24 + _with_0.angleY = 25 -- 25 + _with_0:addChild(Line({ -- 27 + Vec2.zero, -- 27 + Vec2(300, 0), -- 28 + Vec2(300, 400), -- 29 + Vec2(0, 400), -- 30 + Vec2.zero -- 31 + }, App.themeColor)) -- 26 + _with_0:schedule(function() -- 33 + node.y = 200 -- 34 + renderTarget:renderWithClear(node, Color(0xff8a8a8a)) -- 35 + node.y = 0 -- 36 + end) -- 33 + surface = _with_0 -- 22 +end -- 22 +local windowFlags = { -- 41 + "NoDecoration", -- 41 + "AlwaysAutoResize", -- 42 + "NoSavedSettings", -- 43 + "NoFocusOnAppearing", -- 44 + "NoNav", -- 45 + "NoMove" -- 46 +} -- 40 +return threadLoop(function() -- 47 + local width -- 48 + width = App.visualSize.width -- 48 + ImGui.SetNextWindowBgAlpha(0.35) -- 49 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 50 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 51 + return ImGui.Begin("Render Target", windowFlags, function() -- 52 + ImGui.Text("Render Target") -- 53 + ImGui.Separator() -- 54 + return ImGui.TextWrapped("Use render target node as a mirror!") -- 55 + end) -- 55 +end) -- 55 diff --git a/Assets/Script/Example/SQLite.lua b/Assets/Script/Example/SQLite.lua new file mode 100644 index 000000000..01f4410b9 --- /dev/null +++ b/Assets/Script/Example/SQLite.lua @@ -0,0 +1,64 @@ +-- [yue]: Script/Example/SQLite.yue +local DB = dora.DB -- 1 +local print = _G.print -- 1 +local p = _G.p -- 1 +local thread = dora.thread -- 1 +local pairs = _G.pairs -- 1 +local result = DB:transaction({ -- 4 + "DROP TABLE IF EXISTS test", -- 4 + "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", -- 5 + { -- 7 + "INSERT INTO test VALUES(?, ?)", -- 7 + { -- 9 + { -- 9 + false, -- 9 + "hello" -- 9 + }, -- 9 + { -- 10 + false, -- 10 + "world" -- 10 + }, -- 10 + { -- 11 + false, -- 11 + "ok" -- 11 + } -- 11 + } -- 8 + } -- 6 +}) -- 3 +print(result and "Success" or "Failure") -- 16 +print(DB:exist("test")) -- 18 +p(DB:query("SELECT * FROM test", true)) -- 20 +print("row changed:", DB:exec("DELETE FROM test WHERE id > 1")) -- 22 +print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", { -- 24 + "hello world!" -- 24 +})) -- 24 +thread(function() -- 26 + print("insert async") -- 27 + local data -- 28 + do -- 28 + local _accum_0 = { } -- 28 + local _len_0 = 1 -- 28 + for k in pairs(_G) do -- 28 + _accum_0[_len_0] = { -- 28 + false, -- 28 + k -- 28 + } -- 28 + _len_0 = _len_0 + 1 -- 28 + end -- 28 + data = _accum_0 -- 28 + end -- 28 + p(DB:insertAsync("test", data)) -- 29 + print("query async...") -- 31 + local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") -- 32 + return p((function() -- 33 + local _accum_0 = { } -- 33 + local _len_0 = 1 -- 33 + for _index_0 = 1, #items do -- 33 + local item = items[_index_0] -- 33 + _accum_0[_len_0] = item[1] -- 33 + _len_0 = _len_0 + 1 -- 33 + end -- 33 + return _accum_0 -- 33 + end)()) -- 33 +end) -- 26 +return print("OK") -- 35 diff --git a/Assets/Script/Example/SVG.lua b/Assets/Script/Example/SVG.lua new file mode 100644 index 000000000..68f70fd6a --- /dev/null +++ b/Assets/Script/Example/SVG.lua @@ -0,0 +1,9 @@ +-- [yue]: Script/Example/SVG.yue +local SVG = dora.SVG -- 1 +local threadLoop = dora.threadLoop -- 1 +local nvg = dora.nvg -- 1 +local svg = SVG("Image/dora.svg") -- 3 +return threadLoop(function() -- 5 + nvg.Scale(0.5, 0.5) -- 6 + return svg:render() -- 7 +end) -- 7 diff --git a/Assets/Script/Example/Spine.lua b/Assets/Script/Example/Spine.lua new file mode 100644 index 000000000..08549f9b2 --- /dev/null +++ b/Assets/Script/Example/Spine.lua @@ -0,0 +1,83 @@ +-- [yue]: Script/Example/Spine.yue +local Spine = dora.Spine -- 1 +local p = _G.p -- 1 +local print = _G.print -- 1 +local tostring = _G.tostring -- 1 +local Label = dora.Label -- 1 +local App = dora.App -- 1 +local Sequence = dora.Sequence -- 1 +local Spawn = dora.Spawn -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local Delay = dora.Delay -- 1 +local Opacity = dora.Opacity -- 1 +local Event = dora.Event -- 1 +local Vec2 = dora.Vec2 -- 1 +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +local spineStr = "Spine/dragon-ess" -- 3 +local animations = Spine:getAnimations(spineStr) -- 5 +local looks = Spine:getLooks(spineStr) -- 6 +p(animations, looks) -- 8 +local spine -- 10 +do -- 10 + local _with_0 = Spine(spineStr) -- 10 + _with_0.look = looks[1] -- 11 + _with_0:play(animations[1], true) -- 12 + _with_0:slot("AnimationEnd", function(name) -- 13 + return print(tostring(name) .. " end!") -- 13 + end) -- 13 + _with_0.touchEnabled = true -- 14 + _with_0:slot("TapBegan", function(touch) -- 15 + local x, y -- 16 + do -- 16 + local _obj_0 = touch.location -- 16 + x, y = _obj_0.x, _obj_0.y -- 16 + end -- 16 + do -- 17 + local name = _with_0:containsPoint(x, y) -- 17 + if name then -- 17 + return _with_0:addChild((function() -- 18 + local _with_1 = Label("sarasa-mono-sc-regular", 30) -- 18 + _with_1.text = name -- 19 + _with_1.color = App.themeColor -- 20 + _with_1:perform(Sequence(Spawn(Scale(1, 0, 2, Ease.OutQuad), Sequence(Delay(0.5), Opacity(0.5, 1, 0))), Event("Stop"))) -- 21 + _with_1.position = Vec2(x, y) -- 28 + _with_1:slot("Stop", function() -- 29 + return _with_1:removeFromParent() -- 29 + end) -- 29 + return _with_1 -- 18 + end)()) -- 29 + end -- 17 + end -- 17 + end) -- 15 + spine = _with_0 -- 10 +end -- 10 +local windowFlags = { -- 34 + "NoDecoration", -- 34 + "AlwaysAutoResize", -- 35 + "NoSavedSettings", -- 36 + "NoFocusOnAppearing", -- 37 + "NoNav", -- 38 + "NoMove" -- 39 +} -- 33 +local showDebug = spine.showDebug -- 40 +return threadLoop(function() -- 41 + local width -- 42 + width = App.visualSize.width -- 42 + ImGui.SetNextWindowBgAlpha(0.35) -- 43 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 44 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 45 + return ImGui.Begin("Spine", windowFlags, function() -- 46 + ImGui.Text("Spine") -- 47 + ImGui.Separator() -- 48 + ImGui.TextWrapped("Basic usage to create spine! Tap it for a hit test.") -- 49 + do -- 50 + local changed -- 50 + changed, showDebug = ImGui.Checkbox("BoundingBox", showDebug) -- 50 + if changed then -- 50 + spine.showDebug = showDebug -- 51 + end -- 50 + end -- 50 + end) -- 51 +end) -- 51 diff --git a/Assets/Script/Example/Sprite.lua b/Assets/Script/Example/Sprite.lua new file mode 100644 index 000000000..65caf253c --- /dev/null +++ b/Assets/Script/Example/Sprite.lua @@ -0,0 +1,143 @@ +-- [yue]: Script/Example/Sprite.yue +local Sprite = dora.Sprite -- 1 +local Node = dora.Node -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local math = _G.math -- 1 +local sprite -- 3 +do -- 3 + local _with_0 = Sprite("Image/logo.png") -- 3 + _with_0.scaleX = 0.5 -- 4 + _with_0.scaleY = 0.5 -- 4 + sprite = _with_0 -- 3 +end -- 3 +do -- 6 + local _with_0 = Node() -- 6 + _with_0.touchEnabled = true -- 7 + _with_0:slot("TapMoved", function(touch) -- 8 + if not touch.first then -- 9 + return -- 9 + end -- 9 + sprite.position = sprite.position + touch.delta -- 10 + end) -- 8 + _with_0:addChild(sprite) -- 11 +end -- 6 +local windowFlags = { -- 16 + "NoResize", -- 16 + "NoSavedSettings" -- 17 +} -- 15 +return threadLoop(function() -- 18 + local width -- 19 + width = App.visualSize.width -- 19 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 20 + ImGui.SetNextWindowSize(Vec2(240, 520), "FirstUseEver") -- 21 + return ImGui.Begin("Sprite", windowFlags, function() -- 22 + ImGui.BeginChild("SpriteSetting", Vec2(-1, -40), function() -- 23 + local z = sprite.z -- 24 + do -- 25 + local changed -- 25 + changed, z = ImGui.DragFloat("Z", z, 1, -1000, 1000, "%.2f") -- 25 + if changed then -- 25 + sprite.z = z -- 26 + end -- 25 + end -- 25 + local x, y -- 27 + do -- 27 + local _obj_0 = sprite.anchor -- 27 + x, y = _obj_0.x, _obj_0.y -- 27 + end -- 27 + do -- 28 + local changed -- 28 + changed, x, y = ImGui.DragFloat2("Anchor", x, y, 0.01, 0, 1, "%.2f") -- 28 + if changed then -- 28 + sprite.anchor = Vec2(x, y) -- 29 + end -- 28 + end -- 28 + local spriteW, height -- 30 + do -- 30 + local _obj_0 = sprite.size -- 30 + spriteW, height = _obj_0.width, _obj_0.height -- 30 + end -- 30 + do -- 31 + local changed -- 31 + changed, spriteW, height = ImGui.DragFloat2("Size", spriteW, height, 0.1, 0, 1000, "%.f") -- 31 + if changed then -- 31 + sprite.size = Size(spriteW, height) -- 32 + end -- 31 + end -- 31 + local scaleX, scaleY = sprite.scaleX, sprite.scaleY -- 33 + do -- 34 + local changed -- 34 + changed, scaleX, scaleY = ImGui.DragFloat2("Scale", scaleX, scaleY, 0.01, -2, 2, "%.2f") -- 34 + if changed then -- 34 + sprite.scaleX, sprite.scaleY = scaleX, scaleY -- 35 + end -- 34 + end -- 34 + ImGui.PushItemWidth(-60, function() -- 36 + local angle = sprite.angle -- 37 + do -- 38 + local changed -- 38 + changed, angle = ImGui.DragInt("Angle", math.floor(angle), 1, -360, 360) -- 38 + if changed then -- 38 + sprite.angle = angle -- 39 + end -- 38 + end -- 38 + end) -- 36 + ImGui.PushItemWidth(-60, function() -- 40 + local angleX = sprite.angleX -- 41 + do -- 42 + local changed -- 42 + changed, angleX = ImGui.DragInt("AngleX", math.floor(angleX), 1, -360, 360) -- 42 + if changed then -- 42 + sprite.angleX = angleX -- 43 + end -- 42 + end -- 42 + end) -- 40 + ImGui.PushItemWidth(-60, function() -- 44 + local angleY = sprite.angleY -- 45 + do -- 46 + local changed -- 46 + changed, angleY = ImGui.DragInt("AngleY", math.floor(angleY), 1, -360, 360) -- 46 + if changed then -- 46 + sprite.angleY = angleY -- 47 + end -- 46 + end -- 46 + end) -- 44 + local skewX, skewY = sprite.skewX, sprite.skewY -- 48 + do -- 49 + local changed -- 49 + changed, skewX, skewY = ImGui.DragInt2("Skew", math.floor(skewX), math.floor(skewY), 1, -360, 360) -- 49 + if changed then -- 49 + sprite.skewX, sprite.skewY = skewX, skewY -- 50 + end -- 49 + end -- 49 + ImGui.PushItemWidth(-70, function() -- 51 + local opacity = sprite.opacity -- 52 + do -- 53 + local changed -- 53 + changed, opacity = ImGui.DragFloat("Opacity", opacity, 0.01, 0, 1, "%.2f") -- 53 + if changed then -- 53 + sprite.opacity = opacity -- 54 + end -- 53 + end -- 53 + end) -- 51 + return ImGui.PushItemWidth(-1, function() -- 55 + local color3 = sprite.color3 -- 56 + ImGui.SetColorEditOptions("RGB") -- 57 + if ImGui.ColorEdit3("", color3) then -- 58 + sprite.color3 = color3 -- 59 + end -- 58 + end) -- 59 + end) -- 23 + if ImGui.Button("Reset", Vec2(140, 30)) then -- 60 + local _with_0 = sprite.parent -- 61 + _with_0:removeChild(sprite) -- 62 + sprite = Sprite("Image/logo.png") -- 63 + _with_0:addChild(sprite) -- 64 + return _with_0 -- 61 + end -- 60 + end) -- 64 +end) -- 64 diff --git a/Assets/Script/Example/Vector Graphic.lua b/Assets/Script/Example/Vector Graphic.lua new file mode 100644 index 000000000..f7dc99245 --- /dev/null +++ b/Assets/Script/Example/Vector Graphic.lua @@ -0,0 +1,85 @@ +-- [yue]: Script/Example/Vector Graphic.yue +local nvg = dora.nvg -- 1 +local Color = dora.Color -- 1 +local VGNode = dora.VGNode -- 1 +local Sequence = dora.Sequence -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local coroutine = _G.coroutine -- 1 +local cycle = dora.cycle -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local ImGui = dora.ImGui -- 1 +local Vec2 = dora.Vec2 -- 1 +local drawHeart -- 3 +drawHeart = function() -- 3 + local _with_0 = nvg -- 3 + _with_0.BeginPath() -- 4 + _with_0.MoveTo(36.29, 0) -- 5 + _with_0.BezierTo(32.5244, 0.0, 28.9316, 1.3173, 26.0742, 3.7275) -- 6 + _with_0.BezierTo(23.2168, 1.3173, 19.624, 0, 15.8593, 0) -- 7 + _with_0.BezierTo(5.4843, 0, 0, 5.4838, 0, 15.8588) -- 8 + _with_0.BezierTo(0.0, 23.5278, 9.248, 33.1123, 14.7607, 38.143) -- 9 + _with_0.BezierTo(17.2099, 40.3779, 23.8379, 46.0322, 25.9765, 46.2172) -- 10 + _with_0.BezierTo(26.0097, 46.2207, 26.0478, 46.2226, 26.08, 46.2216) -- 11 + _with_0.BezierTo(26.1093, 46.2216, 26.1377, 46.2207, 26.165, 46.2177) -- 12 + _with_0.LineTo(26.165, 46.2163) -- 13 + _with_0.BezierTo(28.2246, 46.0263, 34.748, 40.4858, 37.165, 38.2939) -- 14 + _with_0.BezierTo(42.7607, 33.2197, 52.1484, 23.5581, 52.1484, 15.8588) -- 15 + _with_0.BezierTo(52.1484, 5.4838, 46.665, 0, 36.29, 0) -- 16 + _with_0.ClosePath() -- 17 + _with_0.FillColor(Color(253, 90, 90, 255)) -- 18 + _with_0.Fill() -- 19 + return _with_0 -- 3 +end -- 3 +local stopRendering = false -- 21 +do -- 23 + local _with_0 = VGNode(60, 50, 5) -- 23 + _with_0:render(drawHeart) -- 24 + _with_0:slot("Cleanup", function() -- 25 + stopRendering = true -- 25 + end) -- 25 + _with_0:runAction(Sequence(Scale(0.2, 1.0, 0.3), Scale(0.5, 0.3, 1.0, Ease.OutBack))) -- 26 + _with_0:slot("ActionEnd", function(self) -- 30 + return _with_0:runAction(self) -- 30 + end) -- 30 +end -- 23 +local drawAnimated = coroutine.wrap(function() -- 32 + while true do -- 33 + cycle(0.2, function(time) -- 34 + local scale = 1 - 0.7 * time -- 35 + nvg.Scale(scale, scale) -- 36 + return drawHeart() -- 37 + end) -- 34 + cycle(0.5, function(time) -- 38 + local scale = 0.3 + 0.7 * Ease:func(Ease.OutBack, time) -- 39 + nvg.Scale(scale, scale) -- 40 + return drawHeart() -- 41 + end) -- 38 + end -- 41 +end) -- 32 +threadLoop(function() -- 43 + nvg.Scale(2, 2) -- 44 + drawAnimated() -- 45 + return stopRendering -- 46 +end) -- 43 +local windowFlags = { -- 51 + "NoDecoration", -- 51 + "AlwaysAutoResize", -- 52 + "NoSavedSettings", -- 53 + "NoFocusOnAppearing", -- 54 + "NoNav", -- 55 + "NoMove" -- 56 +} -- 50 +return threadLoop(function() -- 57 + local width -- 58 + width = App.visualSize.width -- 58 + ImGui.SetNextWindowBgAlpha(0.35) -- 59 + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 60 + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") -- 61 + return ImGui.Begin("Vector Graphic Rendering", windowFlags, function() -- 62 + ImGui.Text("Vector Graphic Rendering") -- 63 + ImGui.Separator() -- 64 + return ImGui.TextWrapped("Use nanoVG lib to do vector graphic rendering, render to a texture or do instant render!") -- 65 + end) -- 65 +end) -- 65 diff --git a/Assets/Script/Game/AI Fighter/init.lua b/Assets/Script/Game/AI Fighter/init.lua new file mode 100644 index 000000000..bf8c17bca --- /dev/null +++ b/Assets/Script/Game/AI Fighter/init.lua @@ -0,0 +1,1827 @@ +-- [yue]: Script/Game/AI Fighter/init.yue +local _module_1 = dora.Platformer -- 1 +local Data = _module_1.Data -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local Group = dora.Group -- 1 +local App = dora.App -- 1 +local _module_2 = dora.Platformer.Decision -- 1 +local Seq = _module_2.Seq -- 1 +local Con = _module_2.Con -- 1 +local AI = _module_2.AI -- 1 +local math = _G.math -- 1 +local Sel = _module_2.Sel -- 1 +local Act = _module_2.Act -- 1 +local Node = dora.Node -- 1 +local type = _G.type -- 1 +local tostring = _G.tostring -- 1 +local table = _G.table -- 1 +local thread = dora.thread -- 1 +local ML = dora.ML -- 1 +local string = _G.string -- 1 +local print = _G.print -- 1 +local load = _G.load -- 1 +local emit = dora.emit -- 1 +local Accept = _module_2.Accept -- 1 +local BulletDef = _module_1.BulletDef -- 1 +local Face = _module_1.Face -- 1 +local Reject = _module_2.Reject -- 1 +local Dictionary = dora.Dictionary -- 1 +local TargetAllow = _module_1.TargetAllow -- 1 +local Array = dora.Array -- 1 +local _module_0 = dora.ImGui -- 1 +local Columns = _module_0.Columns -- 1 +local TextColored = _module_0.TextColored -- 1 +local NextColumn = _module_0.NextColumn -- 1 +local PushID = _module_0.PushID -- 1 +local Button = _module_0.Button -- 1 +local ImageButton = _module_0.ImageButton -- 1 +local Text = _module_0.Text -- 1 +local Color = dora.Color -- 1 +local TextWrapped = _module_0.TextWrapped -- 1 +local DrawNode = dora.DrawNode -- 1 +local Line = dora.Line -- 1 +local PlatformWorld = _module_1.PlatformWorld -- 1 +local Rect = dora.Rect -- 1 +local View = dora.View -- 1 +local Director = dora.Director -- 1 +local BodyDef = dora.BodyDef -- 1 +local Body = dora.Body -- 1 +local Sprite = dora.Sprite -- 1 +local Model = dora.Model -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local OpenPopup = _module_0.OpenPopup -- 1 +local BeginPopupModal = _module_0.BeginPopupModal -- 1 +local RadioButton = _module_0.RadioButton -- 1 +local SameLine = _module_0.SameLine -- 1 +local CloseCurrentPopup = _module_0.CloseCurrentPopup -- 1 +local Entity = dora.Entity -- 1 +local Sequence = dora.Sequence -- 1 +local Spawn = dora.Spawn -- 1 +local Opacity = dora.Opacity -- 1 +local Event = dora.Event -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local Begin = _module_0.Begin -- 1 +local Menu = dora.Menu -- 1 +local Keyboard = dora.Keyboard -- 1 +local UnitAction = _module_1.UnitAction -- 1 +local once = dora.once -- 1 +local Bullet = _module_1.Bullet -- 1 +local sleep = dora.sleep -- 1 +local cycle = dora.cycle -- 1 +local Observer = dora.Observer -- 1 +local Unit = _module_1.Unit -- 1 +local Visual = _module_1.Visual -- 1 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 7 +local CircleButton = require("UI.Control.Basic.CircleButton") -- 8 +local Store = Data.store -- 9 +local characters = { -- 14 + { -- 14 + body = "character_roundGreen", -- 14 + lhand = "character_handGreen", -- 15 + rhand = "character_handGreen" -- 16 + }, -- 14 + { -- 18 + body = "character_roundRed", -- 18 + lhand = "character_handRed", -- 19 + rhand = "character_handRed" -- 20 + }, -- 18 + { -- 22 + body = "character_roundYellow", -- 22 + lhand = "character_handYellow", -- 23 + rhand = "character_handYellow" -- 24 + } -- 22 +} -- 13 +local headItems = { -- 27 + "item_hat", -- 27 + "item_hatTop", -- 28 + "item_helmet", -- 29 + "item_helmetModern" -- 30 +} -- 26 +local lhandItems = { -- 33 + "item_shield", -- 33 + "item_shieldRound", -- 34 + "tile_heart", -- 35 + "ui_hand" -- 36 +} -- 32 +local rhandItems = { -- 39 + "item_bow", -- 39 + "item_sword", -- 40 + "item_rod", -- 41 + "item_spear" -- 42 +} -- 38 +local characterTypes = { -- 45 + "square", -- 45 + "round" -- 46 +} -- 44 +local characterColors = { -- 49 + "Green", -- 49 + "Red", -- 50 + "Yellow" -- 51 +} -- 48 +local itemSettings = { -- 54 + item_hat = { -- 55 + name = "普通帽子", -- 55 + desc = "就是很普通的帽子,增加许些防御力", -- 56 + cost = 1, -- 57 + skill = "jump", -- 58 + skillDesc = "跳跃", -- 59 + offset = Vec2(0, 30) -- 60 + }, -- 54 + item_hatTop = { -- 63 + name = "高帽子", -- 63 + desc = "就是很普通的帽子,增加许些防御力", -- 64 + cost = 1, -- 65 + skill = "evade", -- 66 + skillDesc = "闪避", -- 67 + offset = Vec2(0, 30) -- 68 + }, -- 62 + item_helmet = { -- 71 + name = "战盔", -- 71 + desc = "就是很普通的帽子,增加许些防御力", -- 72 + cost = 1, -- 73 + skill = "evade", -- 74 + skillDesc = "闪避", -- 75 + offset = Vec2(0, 0) -- 76 + }, -- 70 + item_helmetModern = { -- 79 + name = "橄榄球盔", -- 79 + desc = "就是很普通的帽子,增加许些防御力", -- 80 + cost = 1, -- 81 + skill = "", -- 82 + skillDesc = "无", -- 83 + offset = Vec2(0, 0) -- 84 + }, -- 78 + item_shield = { -- 87 + name = "方形盾", -- 87 + desc = "无", -- 88 + cost = 1, -- 89 + skill = "evade", -- 90 + skillDesc = "闪避", -- 91 + offset = Vec2(0, 0) -- 92 + }, -- 86 + item_shieldRound = { -- 95 + name = "小圆盾", -- 95 + desc = "无", -- 96 + cost = 1, -- 97 + skill = "jump", -- 98 + skillDesc = "跳跃", -- 99 + offset = Vec2(0, 0) -- 100 + }, -- 94 + tile_heart = { -- 103 + name = "爱心", -- 103 + desc = "无", -- 104 + cost = 1, -- 105 + skill = "jump", -- 106 + skillDesc = "跳跃", -- 107 + offset = Vec2(0, 0) -- 108 + }, -- 102 + ui_hand = { -- 111 + name = "手套", -- 111 + desc = "无", -- 112 + cost = 1, -- 113 + skill = "evade", -- 114 + skillDesc = "闪避", -- 115 + offset = Vec2(0, 0) -- 116 + }, -- 110 + item_bow = { -- 119 + name = "短弓", -- 119 + desc = "无", -- 120 + cost = 1, -- 121 + skill = "range", -- 122 + skillDesc = "远程攻击", -- 123 + offset = Vec2(10, 0), -- 124 + attackRange = Size(630, 150) -- 125 + }, -- 118 + item_sword = { -- 128 + name = "剑", -- 128 + desc = "无", -- 129 + cost = 1, -- 130 + skill = "meleeAttack", -- 131 + skillDesc = "近程攻击", -- 132 + offset = Vec2(15, 50), -- 133 + attackRange = Size(120, 150) -- 134 + }, -- 127 + item_rod = { -- 137 + name = "法杖", -- 137 + desc = "无", -- 138 + cost = 1, -- 139 + skill = "meleeAttack", -- 140 + skillDesc = "近程攻击", -- 141 + offset = Vec2(15, 50), -- 142 + attackRange = Size(200, 150) -- 143 + }, -- 136 + item_spear = { -- 146 + name = "长矛", -- 146 + desc = "无", -- 147 + cost = 1, -- 148 + skill = "meleeAttack", -- 149 + skillDesc = "近程攻击", -- 150 + offset = Vec2(15, 50), -- 151 + attackRange = Size(200, 150) -- 152 + } -- 145 +} -- 53 +local itemSlots = { -- 155 + "head", -- 155 + "lhand", -- 156 + "rhand" -- 157 +} -- 154 +characters = { -- 160 + { -- 160 + head = nil, -- 160 + lhand = nil, -- 161 + rhand = nil, -- 162 + type = 1, -- 163 + color = 1, -- 164 + learnedAI = function() -- 165 + return "unknown" -- 165 + end -- 165 + }, -- 160 + { -- 167 + head = nil, -- 167 + lhand = nil, -- 168 + rhand = nil, -- 169 + type = 1, -- 170 + color = 2, -- 171 + learnedAI = function() -- 172 + return "unknown" -- 172 + end -- 172 + }, -- 167 + { -- 174 + head = nil, -- 174 + lhand = nil, -- 175 + rhand = nil, -- 176 + type = 1, -- 177 + color = 3, -- 178 + learnedAI = function() -- 179 + return "unknown" -- 179 + end -- 179 + } -- 174 +} -- 159 +local bossGroup = Group({ -- 181 + "boss" -- 181 +}) -- 181 +local lastAction = "idle" -- 183 +local lastActionFrame = App.frame -- 184 +local data = { } -- 185 +local row = nil -- 186 +local Do -- 187 +Do = function(name) -- 187 + return Seq({ -- 188 + Con("Collect data", function(self) -- 188 + if self:isDoing(name) then -- 189 + row = nil -- 190 + return true -- 191 + end -- 189 + if not (AI:getNearestUnit("Enemy") ~= nil) then -- 193 + row = nil -- 194 + return true -- 195 + end -- 193 + local attack_ready -- 197 + do -- 197 + local attackUnits = AI:getUnitsInAttackRange() -- 198 + local ready = false -- 199 + for _index_0 = 1, #attackUnits do -- 200 + local unit = attackUnits[_index_0] -- 200 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 201 + ready = true -- 203 + break -- 204 + end -- 201 + end -- 204 + attack_ready = ready -- 205 + end -- 205 + local not_facing_enemy -- 207 + do -- 207 + local enemy = AI:getNearestUnit("Enemy") -- 208 + if enemy then -- 209 + not_facing_enemy = (self.x > enemy.x) == self.faceRight -- 210 + else -- 212 + not_facing_enemy = false -- 212 + end -- 209 + end -- 212 + local enemy_in_attack_range -- 214 + do -- 214 + local enemy = AI:getNearestUnit("Enemy") -- 215 + local attackUnits = AI:getUnitsInAttackRange() -- 216 + enemy_in_attack_range = attackUnits and attackUnits:contains(enemy) or false -- 217 + end -- 217 + local nearest_enemy_distance -- 219 + do -- 219 + local enemy = AI:getNearestUnit("Enemy") -- 220 + if (enemy ~= nil) then -- 221 + nearest_enemy_distance = math.abs(enemy.x - self.x) -- 222 + else -- 224 + nearest_enemy_distance = 999999 -- 224 + end -- 221 + end -- 224 + local enemy_hero_action -- 226 + do -- 226 + local enemy = AI:getNearestUnit("Enemy") -- 227 + enemy_hero_action = (function() -- 228 + local _obj_0 = enemy.currentAction -- 228 + if _obj_0 ~= nil then -- 228 + return _obj_0.name -- 228 + end -- 228 + return nil -- 228 + end)() or "unknown" -- 228 + end -- 228 + row = { -- 231 + not_facing_enemy = not_facing_enemy, -- 231 + enemy_in_attack_range = enemy_in_attack_range, -- 232 + attack_ready = attack_ready, -- 233 + enemy_hero_action = enemy_hero_action, -- 234 + nearest_enemy_distance = nearest_enemy_distance, -- 235 + action = name -- 236 + } -- 230 + return true -- 238 + end), -- 188 + Sel({ -- 240 + Con("is doing", function(self) -- 240 + return self:isDoing(name) -- 240 + end), -- 240 + Seq({ -- 242 + Act(name), -- 242 + Con("action succeeded", function(self) -- 243 + lastAction = name -- 244 + lastActionFrame = App.frame -- 245 + return true -- 246 + end) -- 243 + }) -- 241 + }), -- 239 + Con("Save data", function(self) -- 249 + if row == nil then -- 250 + return true -- 250 + end -- 250 + data[#data + 1] = row -- 251 + return true -- 252 + end) -- 249 + }) -- 253 +end -- 187 +local rowNames = { -- 256 + "not_facing_enemy", -- 256 + "enemy_in_attack_range", -- 257 + "attack_ready", -- 259 + "enemy_hero_action", -- 260 + "nearest_enemy_distance", -- 261 + "action" -- 263 +} -- 255 +local rowTypes = { -- 267 + 'C', -- 267 + 'C', -- 267 + 'C', -- 268 + 'C', -- 268 + 'N', -- 268 + 'C' -- 269 +} -- 266 +do -- 272 + local _with_0 = Node() -- 272 + _with_0:gslot("TrainAI", function(charSet) -- 273 + local csvData -- 274 + do -- 274 + local _accum_0 = { } -- 274 + local _len_0 = 1 -- 274 + for _index_0 = 1, #data do -- 274 + local row = data[_index_0] -- 274 + local rd -- 275 + do -- 275 + local _accum_1 = { } -- 275 + local _len_1 = 1 -- 275 + for _index_1 = 1, #rowNames do -- 275 + local name = rowNames[_index_1] -- 275 + local val -- 276 + if (row[name] ~= nil) then -- 276 + val = row[name] -- 276 + else -- 276 + val = "N" -- 276 + end -- 276 + if "boolean" == type(val) then -- 277 + if val then -- 278 + val = "T" -- 278 + else -- 278 + val = "F" -- 278 + end -- 278 + end -- 277 + _accum_1[_len_1] = tostring(val) -- 279 + _len_1 = _len_1 + 1 -- 279 + end -- 279 + rd = _accum_1 -- 275 + end -- 279 + _accum_0[_len_0] = table.concat(rd, ",") -- 280 + _len_0 = _len_0 + 1 -- 280 + end -- 280 + csvData = _accum_0 -- 274 + end -- 280 + local names = tostring(table.concat(rowNames, ',')) .. "\n" -- 281 + local dataStr = tostring(names) .. tostring(table.concat(rowTypes, ',')) .. "\n" .. tostring(table.concat(csvData, '\n')) -- 282 + data = { } -- 283 + return thread(function() -- 284 + local lines = { -- 285 + "(_ENV) ->" -- 285 + } -- 285 + local accuracy = ML.BuildDecisionTreeAsync(dataStr, 0, function(depth, name, op, value) -- 286 + local line = string.rep("\t", depth + 1) .. (function() -- 287 + if name ~= "" then -- 287 + return "if " .. tostring(name) .. " " .. tostring(op) .. " " .. tostring(op == '==' and "\"" .. tostring(value) .. "\"" or value) -- 288 + else -- 290 + return tostring(op) .. " \"" .. tostring(value) .. "\"" -- 290 + end -- 287 + end)() -- 287 + lines[#lines + 1] = line -- 291 + end) -- 286 + local codes = table.concat(lines, "\n") -- 292 + print("learning accuracy: " .. tostring(accuracy)) -- 293 + print(codes) -- 294 + local yue = require("yue") -- 296 + local luaCodes = yue.to_lua(codes, { -- 297 + reserve_line_number = false -- 297 + }) -- 297 + local learnedAI = (function() -- 298 + local _obj_0 = load(luaCodes) -- 298 + if _obj_0 ~= nil then -- 298 + return _obj_0() -- 298 + end -- 298 + return nil -- 298 + end)() or function() -- 298 + return "unknown" -- 298 + end -- 298 + characters[charSet].learnedAI = learnedAI -- 299 + return emit("LearnedAI", learnedAI) -- 300 + end) -- 300 + end) -- 273 +end -- 272 +Store["AI_Learned"] = Sel({ -- 303 + Seq({ -- 304 + Con("is dead", function(self) -- 304 + return self.entity.hp <= 0 -- 304 + end), -- 304 + Accept() -- 305 + }), -- 303 + Seq({ -- 308 + Con("is falling", function(self) -- 308 + return not self.onSurface -- 308 + end), -- 308 + Act("fallOff") -- 309 + }), -- 307 + Seq({ -- 312 + Con("run learned AI", function(self) -- 312 + local _obj_0 = self.data -- 313 + _obj_0.lastActionTime = _obj_0.lastActionTime or 0.0 -- 313 + if not (AI:getNearestUnit("Enemy") ~= nil) then -- 315 + return false -- 315 + end -- 315 + if App.totalTime - self.data.lastActionTime < 0.1 then -- 317 + return false -- 318 + else -- 320 + self.data.lastActionTime = App.totalTime -- 320 + end -- 317 + local attack_ready -- 322 + do -- 322 + local attackUnits = AI:getUnitsInAttackRange() -- 323 + local ready = "F" -- 324 + for _index_0 = 1, #attackUnits do -- 325 + local unit = attackUnits[_index_0] -- 325 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 326 + ready = "T" -- 328 + break -- 329 + end -- 326 + end -- 329 + attack_ready = ready -- 330 + end -- 330 + local not_facing_enemy -- 332 + do -- 332 + local enemy = AI:getNearestUnit("Enemy") -- 333 + if enemy then -- 334 + if (self.x > enemy.x) == self.faceRight then -- 335 + not_facing_enemy = "T" -- 336 + else -- 338 + not_facing_enemy = "F" -- 338 + end -- 335 + else -- 340 + not_facing_enemy = "F" -- 340 + end -- 334 + end -- 340 + local enemy_in_attack_range -- 342 + do -- 342 + local enemy = AI:getNearestUnit("Enemy") -- 343 + local attackUnits = AI:getUnitsInAttackRange() -- 344 + enemy_in_attack_range = (attackUnits and attackUnits:contains(enemy)) and "T" or "F" -- 345 + end -- 345 + local nearest_enemy_distance -- 347 + do -- 347 + local enemy = AI:getNearestUnit("Enemy") -- 348 + if (enemy ~= nil) then -- 349 + nearest_enemy_distance = math.abs(enemy.x - self.x) -- 350 + else -- 352 + nearest_enemy_distance = 999999 -- 352 + end -- 349 + end -- 352 + local enemy_hero_action -- 354 + do -- 354 + local enemy = AI:getNearestUnit("Enemy") -- 355 + enemy_hero_action = (function() -- 356 + local _obj_1 = enemy.currentAction -- 356 + if _obj_1 ~= nil then -- 356 + return _obj_1.name -- 356 + end -- 356 + return nil -- 356 + end)() or "unknown" -- 356 + end -- 356 + self.entity.learnedAction = characters[self.entity.charSet].learnedAI({ -- 359 + not_facing_enemy = not_facing_enemy, -- 359 + enemy_in_attack_range = enemy_in_attack_range, -- 360 + attack_ready = attack_ready, -- 361 + enemy_hero_action = enemy_hero_action, -- 362 + nearest_enemy_distance = nearest_enemy_distance -- 363 + }) or "unknown" -- 358 + return true -- 365 + end), -- 312 + Sel({ -- 367 + Con("is doing", function(self) -- 367 + return self:isDoing(self.entity.learnedAction) -- 367 + end), -- 367 + Seq({ -- 369 + Act(function(self) -- 369 + return self.entity.learnedAction -- 369 + end), -- 369 + Con("Succeeded prediction", function(self) -- 370 + emit("Prediction", true) -- 371 + return true -- 372 + end) -- 370 + }), -- 368 + Con("Failed prediction", function(self) -- 374 + emit("Prediction", false) -- 375 + return false -- 376 + end) -- 374 + }) -- 366 + }), -- 311 + Seq({ -- 380 + Con("not facing enemy", function(self) -- 380 + return bossGroup:each(function(boss) -- 380 + local unit = boss.unit -- 381 + if Data:isEnemy(unit, self) then -- 382 + if (self.x > unit.x) == self.faceRight then -- 383 + return true -- 384 + end -- 383 + end -- 382 + end) -- 384 + end), -- 380 + Act("turn") -- 385 + }), -- 379 + Seq({ -- 388 + Con("need turn", function(self) -- 388 + return (self.x < -1000 and not self.faceRight) or (self.x > 1000 and self.faceRight) -- 389 + end), -- 388 + Act("turn") -- 390 + }), -- 387 + Sel({ -- 393 + Seq({ -- 394 + Con("take a break", function(self) -- 394 + return App.rand % 60 == 0 -- 394 + end), -- 394 + Act("idle") -- 395 + }), -- 393 + Act("walk") -- 397 + }) -- 392 +}) -- 302 +do -- 401 + local _with_0 = BulletDef() -- 401 + _with_0.tag = "" -- 402 + _with_0.endEffect = "" -- 403 + _with_0.lifeTime = 5 -- 404 + _with_0.damageRadius = 0 -- 405 + _with_0.highSpeedFix = false -- 406 + _with_0.gravity = Vec2(0, -10) -- 407 + _with_0.face = Face("Model/patreon.clip|item_arrow", Vec2(0, 0)) -- 408 + _with_0:setAsCircle(10) -- 409 + _with_0:setVelocity(25, 800) -- 410 + Store["Bullet_Arrow"] = _with_0 -- 401 +end -- 401 +Store["AI_Boss"] = Sel({ -- 413 + Seq({ -- 414 + Con("is dead", function(self) -- 414 + return self.entity.hp <= 0 -- 414 + end), -- 414 + Accept() -- 415 + }), -- 413 + Seq({ -- 418 + Con("is falling", function(self) -- 418 + return not self.onSurface -- 418 + end), -- 418 + Act("fallOff") -- 419 + }), -- 417 + Seq({ -- 422 + Con("is not attacking", function(self) -- 422 + return not self:isDoing("meleeAttack") and not self:isDoing("multiArrow") and not self:isDoing("spearAttack") -- 425 + end), -- 422 + Con("need attack", function(self) -- 426 + local attackUnits = AI:getUnitsInAttackRange() -- 427 + for _index_0 = 1, #attackUnits do -- 428 + local unit = attackUnits[_index_0] -- 428 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 429 + return true -- 431 + end -- 429 + end -- 431 + return false -- 432 + end), -- 426 + Sel({ -- 434 + Seq({ -- 435 + Con("melee attack", function(self) -- 435 + return App.rand % 250 == 0 -- 435 + end), -- 435 + Act("meleeAttack") -- 436 + }), -- 434 + Seq({ -- 439 + Con("range attack", function(self) -- 439 + return App.rand % 250 == 0 -- 439 + end), -- 439 + Act("multiArrow") -- 440 + }), -- 438 + Seq({ -- 443 + Con("spear attack", function(self) -- 443 + return App.rand % 250 == 0 -- 443 + end), -- 443 + Act("spearAttack") -- 444 + }), -- 442 + Act("idle") -- 446 + }) -- 433 + }), -- 421 + Seq({ -- 450 + Con("need turn", function(self) -- 450 + return (self.x < -1000 and not self.faceRight) or (self.x > 1000 and self.faceRight) -- 451 + end), -- 450 + Act("turn") -- 452 + }), -- 449 + Act("walk") -- 454 +}) -- 412 +Store["AI_PlayerControl"] = Sel({ -- 458 + Seq({ -- 459 + Con("is dead", function(self) -- 459 + return self.entity.hp <= 0 -- 459 + end), -- 459 + Accept() -- 460 + }), -- 458 + Seq({ -- 463 + Seq({ -- 464 + Con("move key down", function(self) -- 464 + return not (self.data.keyLeft and self.data.keyRight) and ((self.data.keyLeft and self.faceRight) or (self.data.keyRight and not self.faceRight)) -- 469 + end), -- 464 + Act("turn") -- 470 + }), -- 463 + Reject() -- 472 + }), -- 462 + Seq({ -- 475 + Con("evade key down", function(self) -- 475 + return self.data.keyE -- 475 + end), -- 475 + Do("evade") -- 476 + }), -- 474 + Seq({ -- 479 + Con("attack key down", function(self) -- 479 + return self.data.keyF -- 479 + end), -- 479 + Sel({ -- 481 + Do("meleeAttack"), -- 481 + Do("range") -- 482 + }) -- 480 + }), -- 478 + Sel({ -- 486 + Seq({ -- 487 + Con("is falling", function(self) -- 487 + return not self.onSurface and not self:isDoing("evade") -- 487 + end), -- 487 + Act("fallOff") -- 488 + }), -- 486 + Seq({ -- 491 + Con("jump key down", function(self) -- 491 + return self.data.keyUp -- 491 + end), -- 491 + Do("jump") -- 492 + }) -- 490 + }), -- 485 + Seq({ -- 496 + Con("move key down", function(self) -- 496 + return self.data.keyLeft or self.data.keyRight -- 496 + end), -- 496 + Do("walk") -- 497 + }), -- 495 + Act("idle") -- 499 +}) -- 457 +local NewFighterDef -- 502 +NewFighterDef = function() -- 502 + local _with_0 = Dictionary() -- 502 + _with_0.linearAcceleration = Vec2(0, -10) -- 503 + _with_0.bodyType = "Dynamic" -- 504 + _with_0.scale = 1 -- 505 + _with_0.density = 1.0 -- 506 + _with_0.friction = 1.0 -- 507 + _with_0.restitution = 0.0 -- 508 + _with_0.playable = "model:Model/patreon" -- 509 + _with_0.size = Size(64, 128) -- 510 + _with_0.tag = "Fighter" -- 511 + _with_0.sensity = 0 -- 512 + _with_0.move = 250 -- 513 + _with_0.moveSpeed = 1.0 -- 514 + _with_0.jump = 700 -- 515 + _with_0.detectDistance = 800 -- 516 + _with_0.hp = 50.0 -- 517 + _with_0.attackSpeed = 1.0 -- 518 + _with_0.attackBase = 2.5 -- 519 + _with_0.attackDelay = 20.0 / 60.0 -- 520 + _with_0.attackEffectDelay = 20.0 / 60.0 -- 521 + _with_0.attackBonus = 0.0 -- 522 + _with_0.attackFactor = 1.0 -- 523 + _with_0.attackRange = Size(350, 150) -- 524 + _with_0.attackPower = Vec2(100, 100) -- 525 + _with_0.attackTarget = "Single" -- 526 + do -- 527 + local conf -- 528 + do -- 528 + local _with_1 = TargetAllow() -- 528 + _with_1.terrainAllowed = true -- 529 + _with_1:allow("Enemy", true) -- 530 + conf = _with_1 -- 528 + end -- 528 + _with_0.targetAllow = conf:toValue() -- 531 + end -- 531 + _with_0.damageType = 0 -- 532 + _with_0.defenceType = 0 -- 533 + _with_0.bulletType = "Bullet_Arrow" -- 534 + _with_0.attackEffect = "" -- 535 + _with_0.hitEffect = "Particle/bloodp.par" -- 536 + _with_0.name = "Fighter" -- 537 + _with_0.desc = "" -- 538 + _with_0.sndAttack = "" -- 539 + _with_0.sndFallen = "" -- 540 + _with_0.decisionTree = "AI_PlayerControl" -- 541 + _with_0.usePreciseHit = true -- 542 + _with_0.actions = Array({ -- 544 + "walk", -- 544 + "turn", -- 545 + "idle", -- 546 + "cancel", -- 547 + "hit", -- 548 + "fall", -- 549 + "fallOff" -- 550 + }) -- 543 + return _with_0 -- 502 +end -- 502 +local NewBossDef -- 553 +NewBossDef = function() -- 553 + local _with_0 = Dictionary() -- 553 + _with_0.linearAcceleration = Vec2(0, -10) -- 554 + _with_0.bodyType = "Dynamic" -- 555 + _with_0.scale = 2 -- 556 + _with_0.density = 10.0 -- 557 + _with_0.friction = 1.0 -- 558 + _with_0.restitution = 0.0 -- 559 + _with_0.playable = "model:Model/bossp.model" -- 560 + _with_0.size = Size(150, 410) -- 561 + _with_0.tag = "Boss" -- 562 + _with_0.sensity = 0 -- 563 + _with_0.move = 100 -- 564 + _with_0.moveSpeed = 1.0 -- 565 + _with_0.jump = 600 -- 566 + _with_0.detectDistance = 1500 -- 567 + _with_0.hp = 200.0 -- 568 + _with_0.attackSpeed = 1.0 -- 569 + _with_0.attackBase = 2.5 -- 570 + _with_0.attackDelay = 50.0 / 60.0 -- 571 + _with_0.attackEffectDelay = 50.0 / 60.0 -- 572 + _with_0.attackBonus = 0.0 -- 573 + _with_0.attackFactor = 1.0 -- 574 + _with_0.attackRange = Size(780, 300) -- 575 + _with_0.attackPower = Vec2(200, 200) -- 576 + _with_0.attackTarget = "Multi" -- 577 + do -- 578 + local conf -- 579 + do -- 579 + local _with_1 = TargetAllow() -- 579 + _with_1.terrainAllowed = true -- 580 + _with_1:allow("Enemy", true) -- 581 + conf = _with_1 -- 579 + end -- 579 + _with_0.targetAllow = conf:toValue() -- 582 + end -- 582 + _with_0.damageType = 0 -- 583 + _with_0.defenceType = 0 -- 584 + _with_0.bulletType = "Bullet_Arrow" -- 585 + _with_0.attackEffect = "" -- 586 + _with_0.hitEffect = "Particle/bloodp.par" -- 587 + _with_0.sndAttack = "" -- 588 + _with_0.sndFallen = "" -- 589 + _with_0.decisionTree = "AI_Boss" -- 590 + _with_0.usePreciseHit = true -- 591 + _with_0.actions = Array({ -- 593 + "walk", -- 593 + "turn", -- 594 + "meleeAttack", -- 595 + "multiArrow", -- 596 + "spearAttack", -- 597 + "idle", -- 598 + "cancel", -- 599 + "jump", -- 600 + "fall", -- 601 + "fallOff" -- 602 + }) -- 592 + return _with_0 -- 553 +end -- 553 +local UnitDefFuncs = { -- 606 + fighter = NewFighterDef, -- 606 + boss = NewBossDef -- 607 +} -- 605 +local themeColor = App.themeColor -- 610 +local itemSize = 64 -- 611 +local NewItemPanel -- 612 +NewItemPanel = function(displayName, itemName, itemOptions, currentSet) -- 612 + local selectItems = false -- 613 + return function() -- 614 + Columns(1, false) -- 615 + TextColored(themeColor, displayName) -- 616 + NextColumn() -- 617 + if selectItems then -- 618 + Columns(#itemOptions + 1, false) -- 619 + PushID(tostring(itemName) .. "x", function() -- 620 + if Button("x", Vec2(itemSize + 10, itemSize + 10)) then -- 621 + currentSet[itemName] = nil -- 622 + selectItems = false -- 623 + end -- 621 + end) -- 620 + NextColumn() -- 624 + for i = 1, #itemOptions do -- 625 + local item = itemOptions[i] -- 626 + if ImageButton(tostring(itemName) .. tostring(i), "Model/patreon.clip|" .. tostring(item), Vec2(itemSize, itemSize)) then -- 627 + currentSet[itemName] = item -- 628 + selectItems = false -- 629 + end -- 627 + NextColumn() -- 630 + end -- 630 + else -- 632 + if not currentSet[itemName] then -- 632 + Columns(3, false) -- 633 + PushID(tostring(itemName) .. "c1", function() -- 634 + if Button("x", Vec2(itemSize + 10, itemSize + 10)) then -- 635 + selectItems = true -- 635 + end -- 635 + end) -- 634 + NextColumn() -- 636 + return Text("未装备") -- 637 + else -- 639 + Columns(3, false) -- 639 + local item = currentSet[itemName] -- 640 + if ImageButton(tostring(itemName) .. "c2", "Model/patreon.clip|" .. tostring(item), Vec2(itemSize, itemSize)) then -- 641 + selectItems = true -- 641 + end -- 641 + NextColumn() -- 642 + TextColored(Color(0xfffffa0a), itemSettings[item].name) -- 643 + TextWrapped(itemSettings[item].desc) -- 644 + NextColumn() -- 645 + TextColored(Color(0xffff0a90), "消耗: " .. tostring(itemSettings[item].cost)) -- 646 + Text("特技: " .. tostring(itemSettings[item].skillDesc)) -- 647 + return NextColumn() -- 648 + end -- 632 + end -- 618 + end -- 648 +end -- 612 +local size, grid = 2000, 150 -- 652 +local background -- 654 +background = function() -- 654 + local _with_0 = DrawNode() -- 654 + _with_0.depthWrite = true -- 655 + _with_0:drawPolygon({ -- 657 + Vec2(-size, size), -- 657 + Vec2(size, size), -- 658 + Vec2(size, -size), -- 659 + Vec2(-size, -size) -- 660 + }, Color(0xff888888)) -- 656 + _with_0:addChild((function() -- 662 + local _with_1 = Line() -- 662 + _with_1.depthWrite = true -- 663 + _with_1.z = -10 -- 664 + for i = -size / grid, size / grid do -- 665 + _with_1:add({ -- 667 + Vec2(i * grid, size), -- 667 + Vec2(i * grid, -size) -- 668 + }, Color(0xff000000)) -- 666 + _with_1:add({ -- 671 + Vec2(-size, i * grid), -- 671 + Vec2(size, i * grid) -- 672 + }, Color(0xff000000)) -- 670 + end -- 673 + return _with_1 -- 662 + end)()) -- 662 + return _with_0 -- 654 +end -- 654 +do -- 675 + local _with_0 = background() -- 675 + _with_0.z = 600 -- 676 +end -- 675 +do -- 677 + local _with_0 = background() -- 677 + _with_0.angleX = 45 -- 678 +end -- 677 +local TerrainLayer = 0 -- 682 +local EnemyLayer = 1 -- 683 +local PlayerLayer = 2 -- 684 +local PlayerGroup = 1 -- 686 +local EnemyGroup = 2 -- 687 +local DesignWidth = 1500 -- 689 +Data:setRelation(PlayerGroup, EnemyGroup, "Enemy") -- 691 +Data:setShouldContact(PlayerGroup, EnemyGroup, true) -- 692 +local world -- 694 +do -- 694 + local _with_0 = PlatformWorld() -- 694 + _with_0.camera.boundary = Rect(-1250, -500, 2500, 1000) -- 695 + _with_0.camera.followRatio = Vec2(0.01, 0.01) -- 696 + _with_0.camera.zoom = View.size.width / DesignWidth -- 697 + _with_0:gslot("AppSizeChanged", function() -- 698 + local zoom = View.size.width / DesignWidth -- 699 + _with_0.camera.zoom = zoom -- 700 + local _with_1 = Director.ui -- 701 + _with_1.scaleX = zoom -- 702 + _with_1.scaleY = zoom -- 702 + return _with_1 -- 701 + end) -- 698 + world = _with_0 -- 694 +end -- 694 +Store["world"] = world -- 703 +local terrainDef -- 705 +do -- 705 + local _with_0 = BodyDef() -- 705 + _with_0.type = "Static" -- 706 + _with_0:attachPolygon(Vec2(0, 0), 2500, 10, 0, 1, 1, 0) -- 707 + _with_0:attachPolygon(Vec2(0, 1000), 2500, 10, 0, 1, 1, 0) -- 708 + _with_0:attachPolygon(Vec2(1250, 500), 10, 1000, 0, 1, 1, 0) -- 709 + _with_0:attachPolygon(Vec2(-1250, 500), 10, 1000, 0, 1, 1, 0) -- 710 + terrainDef = _with_0 -- 705 +end -- 705 +do -- 712 + local _with_0 = Body(terrainDef, world, Vec2.zero) -- 712 + _with_0.order = TerrainLayer -- 713 + _with_0.group = Data.groupTerrain -- 714 + _with_0:addTo(world) -- 715 +end -- 712 +local updateModel -- 717 +updateModel = function(model, currentSet) -- 717 + local node = model:getNodeByName("body") -- 718 + node:removeAllChildren() -- 719 + local charType = characterTypes[currentSet.type] -- 720 + local charColor = characterColors[currentSet.color] -- 721 + node:addChild(Sprite("Model/patreon.clip|character_" .. tostring(charType) .. tostring(charColor))) -- 722 + node = model:getNodeByName("lhand") -- 723 + node:removeAllChildren() -- 724 + node:addChild(Sprite("Model/patreon.clip|character_hand" .. tostring(charColor))) -- 725 + node = model:getNodeByName("rhand") -- 726 + node:removeAllChildren() -- 727 + node:addChild(Sprite("Model/patreon.clip|character_hand" .. tostring(charColor))) -- 728 + model:getNodeByName("head"):removeAllChildren() -- 729 + for _index_0 = 1, #itemSlots do -- 730 + local slot = itemSlots[_index_0] -- 730 + node = model:getNodeByName(slot) -- 731 + local item = currentSet[slot] -- 732 + if item then -- 733 + local offset = itemSettings[item].offset -- 734 + node:addChild((function() -- 735 + local _with_0 = Sprite("Model/patreon.clip|" .. tostring(item)) -- 735 + _with_0.position = offset -- 736 + return _with_0 -- 735 + end)()) -- 735 + end -- 733 + end -- 736 +end -- 717 +local NewFighter -- 738 +NewFighter = function(name, currentSet) -- 738 + local assembleFighter = false -- 739 + local fighter -- 740 + do -- 740 + local _with_0 = Model("Model/patreon.model") -- 740 + local modelRect = Rect(-128, -128, 256, 256) -- 741 + _with_0.recovery = 0.2 -- 742 + _with_0.order = PlayerLayer -- 743 + _with_0.touchEnabled = true -- 744 + _with_0.swallowTouches = true -- 745 + _with_0:slot("TapFilter", function(touch) -- 746 + if not modelRect:containsPoint(touch.location) then -- 747 + touch.enabled = false -- 748 + end -- 747 + end) -- 746 + _with_0:slot("Tapped", function() -- 749 + if not fighter:getChildByTag("select") then -- 750 + local selectFrame -- 751 + do -- 751 + local _with_1 = Sprite("Model/patreon.clip|ui_select") -- 751 + _with_1:addTo(fighter, 0, "select") -- 752 + _with_1:runAction(Scale(0.3, 0, 1.8, Ease.OutBack)) -- 753 + assembleFighter = true -- 754 + selectFrame = _with_1 -- 751 + end -- 751 + end -- 750 + end) -- 749 + _with_0:play("idle", true) -- 755 + fighter = _with_0 -- 740 + end -- 740 + updateModel(fighter, currentSet) -- 756 + local HeadItemPanel = NewItemPanel("头部", "head", headItems, currentSet) -- 757 + local LHandItemPanel = NewItemPanel("副手", "lhand", lhandItems, currentSet) -- 758 + local RHandItemPanel = NewItemPanel("主手", "rhand", rhandItems, currentSet) -- 759 + return fighter, function() -- 760 + SetNextWindowSize(Vec2(445, 600), "FirstUseEver") -- 761 + if assembleFighter then -- 762 + assembleFighter = false -- 763 + OpenPopup("战士" .. tostring(name)) -- 764 + end -- 762 + return BeginPopupModal("战士" .. tostring(name), { -- 765 + "NoResize", -- 765 + "NoSavedSettings" -- 765 + }, function() -- 765 + HeadItemPanel() -- 766 + RHandItemPanel() -- 767 + LHandItemPanel() -- 768 + Columns(1, false) -- 769 + TextColored(themeColor, "性别") -- 770 + NextColumn() -- 771 + local _ -- 772 + _, currentSet.type = RadioButton("男", currentSet.type, 1) -- 772 + SameLine() -- 773 + _, currentSet.type = RadioButton("女", currentSet.type, 2) -- 774 + Columns(1, false) -- 775 + local cost = 0 -- 776 + for _index_0 = 1, #itemSlots do -- 777 + local slot = itemSlots[_index_0] -- 777 + local item = currentSet[slot] -- 778 + cost = cost + (item and itemSettings[item].cost or 0) -- 779 + end -- 779 + TextColored(themeColor, "累计消耗资源:" .. tostring(cost)) -- 780 + NextColumn() -- 781 + Columns(2, false) -- 782 + if Button("进行训练!", Vec2(200, 80)) then -- 783 + updateModel(fighter, currentSet) -- 784 + CloseCurrentPopup() -- 785 + do -- 786 + local _with_0 = fighter:getChildByTag("select") -- 786 + _with_0:removeFromParent() -- 787 + end -- 786 + emit("ShowSetting", false) -- 788 + local charSet = 1 -- 789 + for i = 1, #characters do -- 790 + if currentSet == characters[i] then -- 791 + charSet = i -- 792 + break -- 793 + end -- 791 + end -- 793 + Entity({ -- 795 + unitDef = "fighter", -- 795 + charSet = charSet, -- 796 + order = PlayerLayer, -- 797 + position = Vec2(-400, 400), -- 798 + group = PlayerGroup, -- 799 + faceRight = true, -- 800 + player = true, -- 801 + decisionTree = "AI_PlayerControl" -- 802 + }) -- 794 + Entity({ -- 804 + unitDef = "boss", -- 804 + order = EnemyLayer, -- 805 + position = Vec2(400, 400), -- 806 + group = EnemyGroup, -- 807 + faceRight = false, -- 808 + boss = true -- 809 + }) -- 803 + emit("ShowTraining", true) -- 810 + end -- 783 + NextColumn() -- 811 + if Button("装备完成!", Vec2(200, 80)) then -- 812 + updateModel(fighter, currentSet) -- 813 + CloseCurrentPopup() -- 814 + do -- 815 + local _with_0 = fighter:getChildByTag("select") -- 815 + _with_0:runAction(Sequence(Spawn(Scale(0.3, 1.8, 2.5), Opacity(0.3, 1, 0)), Event("End"))) -- 816 + _with_0:slot("End", function() -- 820 + return _with_0:removeFromParent() -- 820 + end) -- 820 + end -- 815 + end -- 812 + return NextColumn() -- 821 + end) -- 821 + end -- 821 +end -- 738 +local fighterFigures = { } -- 823 +local fighterPanels = { } -- 824 +for i = 1, #characters do -- 825 + local fighter, fighterPanel = NewFighter(string.rep("I", i), characters[i]) -- 826 + table.insert(fighterFigures, fighter) -- 827 + table.insert(fighterPanels, fighterPanel) -- 828 +end -- 828 +local playerGroup = Group({ -- 830 + "player", -- 830 + "unit" -- 830 +}) -- 830 +local updatePlayerControl -- 831 +updatePlayerControl = function(key, flag) -- 831 + return playerGroup:each(function(self) -- 831 + self.unit.data[key] = flag -- 831 + end) -- 831 +end -- 831 +local uiScale = App.devicePixelRatio -- 833 +Director.ui:addChild((function() -- 835 + local _with_0 = AlignNode({ -- 835 + isRoot = true -- 835 + }) -- 835 + _with_0:schedule(function() -- 836 + local width, height -- 837 + do -- 837 + local _obj_0 = App.visualSize -- 837 + width, height = _obj_0.width, _obj_0.height -- 837 + end -- 837 + SetNextWindowPos(Vec2(10, 10), "FirstUseEver") -- 838 + SetNextWindowSize(Vec2(350, 160), "FirstUseEver") -- 839 + return Begin("AI军团", { -- 840 + "NoResize", -- 840 + "NoSavedSettings" -- 840 + }, function() -- 840 + local isPC -- 841 + do -- 841 + local _exp_0 = App.platform -- 841 + if "macOS" == _exp_0 or "Windows" == _exp_0 or "Linux" == _exp_0 then -- 842 + isPC = true -- 842 + else -- 843 + isPC = false -- 843 + end -- 843 + end -- 843 + return TextWrapped("点击你的学员部队配备装备,并亲自进行战斗方法的训练,最后带领部队挑战敌人。\n学员战斗AI通过玩家操作自动学习生成。" .. tostring(isPC and '训练操作按键:向左A,向右D,闪避E,攻击J,跳跃K' or '')) -- 844 + end) -- 844 + end) -- 836 + _with_0:addChild((function() -- 845 + local _with_1 = AlignNode() -- 845 + _with_1.size = Size(0, 0) -- 846 + _with_1.hAlign = "Center" -- 847 + _with_1.vAlign = "Center" -- 848 + _with_1.alignOffset = Vec2(0, 32) -- 849 + _with_1.visible = false -- 850 + _with_1:gslot("ShowTraining", function(show) -- 851 + _with_1.visible = show -- 852 + if show then -- 853 + return _with_1:addChild((function() -- 854 + local _with_2 = CircleButton({ -- 855 + text = "训练\n结束!", -- 855 + y = -300, -- 856 + radius = 80, -- 857 + fontName = "sarasa-mono-sc-regular", -- 858 + fontSize = 48 -- 859 + }) -- 854 + _with_2:slot("Tapped", function() -- 861 + emit("ShowTraining", false) -- 862 + Group({ -- 863 + "player" -- 863 + }):each(function(e) -- 863 + if e.charSet then -- 864 + emit("TrainAI", e.charSet) -- 865 + return e.unit:removeFromParent() -- 866 + end -- 864 + end) -- 863 + Group({ -- 867 + "boss" -- 867 + }):each(function(e) -- 867 + return e.unit:removeFromParent() -- 868 + end) -- 867 + return emit("ShowSetting", true) -- 869 + end) -- 861 + return _with_2 -- 854 + end)()) -- 869 + else -- 871 + return _with_1:removeAllChildren() -- 871 + end -- 853 + end) -- 851 + _with_1:gslot("ShowFight", function(show) -- 872 + _with_1.visible = show -- 873 + if show then -- 874 + return _with_1:addChild((function() -- 875 + local _with_2 = CircleButton({ -- 876 + text = "离开\n战斗", -- 876 + y = -300, -- 877 + radius = 80, -- 878 + fontName = "sarasa-mono-sc-regular", -- 879 + fontSize = 48 -- 880 + }) -- 875 + _with_2:slot("Tapped", function() -- 882 + Group({ -- 883 + "unitDef" -- 883 + }):each(function(e) -- 883 + local _obj_0 = e.unit -- 884 + if _obj_0 ~= nil then -- 884 + return _obj_0:removeFromParent() -- 884 + end -- 884 + return nil -- 884 + end) -- 883 + emit("ShowSetting", true) -- 885 + return thread(function() -- 886 + return emit("ShowFight", false) -- 886 + end) -- 886 + end) -- 882 + return _with_2 -- 875 + end)()) -- 886 + else -- 888 + return _with_1:removeAllChildren() -- 888 + end -- 874 + end) -- 872 + return _with_1 -- 845 + end)()) -- 845 + _with_0:addChild((function() -- 889 + local _with_1 = AlignNode() -- 889 + _with_1.size = Size(0, 0) -- 890 + _with_1.hAlign = "Center" -- 891 + _with_1.vAlign = "Center" -- 892 + _with_1.alignOffset = Vec2(0, 32) -- 893 + _with_1:gslot("ShowSetting", function(show) -- 894 + _with_1.visible = show -- 894 + end) -- 894 + _with_1:addChild((function() -- 895 + local _with_2 = Model("Model/bossp.model") -- 895 + _with_2.x = 500 -- 896 + _with_2.y = 100 -- 897 + _with_2.fliped = true -- 898 + _with_2.speed = 0.8 -- 899 + _with_2.scaleX, _with_2.scaleY = 2, 2 -- 900 + _with_2.recovery = 0.2 -- 901 + _with_2:play("idle", true) -- 902 + return _with_2 -- 895 + end)()) -- 895 + for i = 1, #fighterFigures do -- 903 + local fighter = fighterFigures[i] -- 904 + _with_1:addChild((function() -- 905 + fighter.x = -500 + (i - 1) * 200 -- 906 + return fighter -- 905 + end)()) -- 905 + end -- 906 + _with_1:addChild((function() -- 907 + local _with_2 = CircleButton({ -- 908 + text = "开战!", -- 908 + y = -300, -- 909 + radius = 80, -- 910 + fontName = "sarasa-mono-sc-regular", -- 911 + fontSize = 48 -- 912 + }) -- 907 + local showItems -- 914 + showItems = function(show) -- 914 + for _index_0 = 1, #fighterFigures do -- 915 + local fighter = fighterFigures[_index_0] -- 915 + fighter.touchEnabled = not show -- 916 + end -- 916 + _with_2.visible = not show -- 917 + end -- 914 + _with_2:gslot("ShowFight", showItems) -- 918 + _with_2:gslot("ShowTraining", showItems) -- 919 + _with_2:slot("Tapped", function() -- 920 + if not _with_2.visible then -- 921 + return -- 921 + end -- 921 + for i = 1, #characters do -- 922 + local char = characters[i] -- 923 + Entity({ -- 925 + unitDef = "fighter", -- 925 + charSet = i, -- 926 + order = PlayerLayer, -- 927 + position = Vec2(-600 + (i - 1) * 200, 400), -- 928 + group = PlayerGroup, -- 929 + faceRight = true, -- 930 + decisionTree = "AI_Learned", -- 931 + player = true -- 932 + }) -- 924 + end -- 932 + Entity({ -- 934 + unitDef = "boss", -- 934 + order = EnemyLayer, -- 935 + position = Vec2(400, 400), -- 936 + group = EnemyGroup, -- 937 + faceRight = false, -- 938 + boss = true -- 939 + }) -- 933 + emit("ShowSetting", false) -- 940 + return emit("ShowFight", true) -- 941 + end) -- 920 + return _with_2 -- 907 + end)()) -- 907 + return _with_1 -- 889 + end)()) -- 889 + do -- 942 + local _exp_0 = App.platform -- 942 + if "iOS" == _exp_0 or "Android" == _exp_0 then -- 943 + _with_0:addChild((function() -- 944 + local _with_1 = AlignNode() -- 944 + _with_1.hAlign = "Left" -- 945 + _with_1.vAlign = "Bottom" -- 946 + _with_1.visible = false -- 947 + _with_1:gslot("ShowTraining", function(show) -- 948 + _with_1.visible = show -- 948 + end) -- 948 + _with_1:addChild((function() -- 949 + local _with_2 = Menu() -- 949 + _with_2:addChild((function() -- 950 + local _with_3 = CircleButton({ -- 951 + text = "左", -- 951 + x = 20 * uiScale, -- 952 + y = 90 * uiScale, -- 953 + radius = 30 * uiScale, -- 954 + fontSize = math.floor(18 * uiScale) -- 955 + }) -- 950 + _with_3.anchor = Vec2.zero -- 957 + _with_3:slot("TapBegan", function() -- 958 + return updatePlayerControl("keyLeft", true) -- 958 + end) -- 958 + _with_3:slot("TapEnded", function() -- 959 + return updatePlayerControl("keyLeft", false) -- 959 + end) -- 959 + return _with_3 -- 950 + end)()) -- 950 + _with_2:addChild((function() -- 960 + local _with_3 = CircleButton({ -- 961 + text = "右", -- 961 + x = 90 * uiScale, -- 962 + y = 90 * uiScale, -- 963 + radius = 30 * uiScale, -- 964 + fontSize = math.floor(18 * uiScale) -- 965 + }) -- 960 + _with_3.anchor = Vec2.zero -- 967 + _with_3:slot("TapBegan", function() -- 968 + return updatePlayerControl("keyRight", true) -- 968 + end) -- 968 + _with_3:slot("TapEnded", function() -- 969 + return updatePlayerControl("keyRight", false) -- 969 + end) -- 969 + return _with_3 -- 960 + end)()) -- 960 + return _with_2 -- 949 + end)()) -- 949 + return _with_1 -- 944 + end)()) -- 944 + _with_0:addChild((function() -- 970 + local _with_1 = AlignNode() -- 970 + _with_1.hAlign = "Right" -- 971 + _with_1.vAlign = "Bottom" -- 972 + _with_1.visible = false -- 973 + _with_1:gslot("ShowTraining", function(show) -- 974 + _with_1.visible = show -- 974 + end) -- 974 + _with_1:addChild((function() -- 975 + local _with_2 = Menu() -- 975 + _with_2:addChild((function() -- 976 + local _with_3 = CircleButton({ -- 977 + text = "闪", -- 977 + x = -80 * uiScale, -- 978 + y = 160 * uiScale, -- 979 + radius = 30 * uiScale, -- 980 + fontSize = math.floor(18 * uiScale) -- 981 + }) -- 976 + _with_3.anchor = Vec2.zero -- 983 + _with_3:slot("TapBegan", function() -- 984 + return updatePlayerControl("keyE", true) -- 984 + end) -- 984 + _with_3:slot("TapEnded", function() -- 985 + return updatePlayerControl("keyE", false) -- 985 + end) -- 985 + return _with_3 -- 976 + end)()) -- 976 + _with_2:addChild((function() -- 986 + local _with_3 = CircleButton({ -- 987 + text = "跳", -- 987 + x = -80 * uiScale, -- 988 + y = 90 * uiScale, -- 989 + radius = 30 * uiScale, -- 990 + fontSize = math.floor(18 * uiScale) -- 991 + }) -- 986 + _with_3.anchor = Vec2.zero -- 993 + _with_3:slot("TapBegan", function() -- 994 + return updatePlayerControl("keyUp", true) -- 994 + end) -- 994 + _with_3:slot("TapEnded", function() -- 995 + return updatePlayerControl("keyUp", false) -- 995 + end) -- 995 + return _with_3 -- 986 + end)()) -- 986 + _with_2:addChild((function() -- 996 + local _with_3 = CircleButton({ -- 997 + text = "打", -- 997 + x = -150 * uiScale, -- 998 + y = 90 * uiScale, -- 999 + radius = 30 * uiScale, -- 1000 + fontSize = math.floor(18 * uiScale) -- 1001 + }) -- 996 + _with_3.anchor = Vec2.zero -- 1003 + _with_3:slot("TapBegan", function() -- 1004 + return updatePlayerControl("keyF", true) -- 1004 + end) -- 1004 + _with_3:slot("TapEnded", function() -- 1005 + return updatePlayerControl("keyF", false) -- 1005 + end) -- 1005 + return _with_3 -- 996 + end)()) -- 996 + return _with_2 -- 975 + end)()) -- 975 + return _with_1 -- 970 + end)()) -- 970 + elseif "macOS" == _exp_0 or "Windows" == _exp_0 or "Linux" == _exp_0 then -- 1006 + do -- 1007 + local _with_1 = Node() -- 1007 + _with_1:schedule(function() -- 1008 + updatePlayerControl("keyLeft", Keyboard:isKeyPressed("A")) -- 1009 + updatePlayerControl("keyRight", Keyboard:isKeyPressed("D")) -- 1010 + updatePlayerControl("keyUp", Keyboard:isKeyPressed("K")) -- 1011 + updatePlayerControl("keyF", Keyboard:isKeyPressed("J")) -- 1012 + return updatePlayerControl("keyE", Keyboard:isKeyPressed("E")) -- 1013 + end) -- 1008 + end -- 1007 + end -- 1013 + end -- 1013 + return _with_0 -- 835 +end)()) -- 835 +do -- 1015 + local _with_0 = Node() -- 1015 + _with_0:schedule(function() -- 1016 + local width, height -- 1017 + do -- 1017 + local _obj_0 = App.visualSize -- 1017 + width, height = _obj_0.width, _obj_0.height -- 1017 + end -- 1017 + for _index_0 = 1, #fighterPanels do -- 1018 + local panel = fighterPanels[_index_0] -- 1018 + panel() -- 1018 + end -- 1018 + end) -- 1016 +end -- 1015 +local rangeAttackEnd -- 1020 +rangeAttackEnd = function(name, playable) -- 1020 + if name == "range" then -- 1021 + return playable.parent:stop() -- 1021 + end -- 1021 +end -- 1020 +UnitAction:add("range", { -- 1024 + priority = 3, -- 1024 + reaction = 10, -- 1025 + recovery = 0.1, -- 1026 + queued = true, -- 1027 + available = function(self) -- 1028 + return true -- 1028 + end, -- 1028 + create = function(self) -- 1029 + local attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor -- 1030 + do -- 1030 + local _obj_0 = self.entity -- 1035 + attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.targetAllow, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 1030 + end -- 1035 + do -- 1036 + local _with_0 = self.playable -- 1036 + _with_0.speed = attackSpeed -- 1037 + _with_0:play("range") -- 1038 + _with_0:slot("AnimationEnd", rangeAttackEnd) -- 1039 + end -- 1036 + return once(function(self) -- 1040 + local bulletDef = Store[self.unitDef.bulletType] -- 1041 + local onAttack -- 1042 + onAttack = function() -- 1042 + local _with_0 = Bullet(bulletDef, self) -- 1043 + _with_0.targetAllow = targetAllow -- 1044 + _with_0:slot("HitTarget", function(bullet, target, pos) -- 1045 + do -- 1046 + local _with_1 = target.data -- 1046 + _with_1.hitPoint = pos -- 1047 + _with_1.hitPower = attackPower -- 1048 + _with_1.hitFromRight = bullet.velocityX < 0 -- 1049 + end -- 1046 + local entity = target.entity -- 1050 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 1051 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 1052 + entity.hp = entity.hp - damage -- 1053 + bullet.hitStop = true -- 1054 + end) -- 1045 + _with_0:addTo(self.world, self.order) -- 1055 + return _with_0 -- 1043 + end -- 1042 + sleep(0.5 * 28.0 / 30.0 / attackSpeed) -- 1056 + onAttack() -- 1057 + while true do -- 1058 + sleep() -- 1058 + end -- 1058 + end) -- 1058 + end, -- 1029 + stop = function(self) -- 1059 + return self.playable:slot("AnimationEnd"):remove(rangeAttackEnd) -- 1060 + end -- 1059 +}) -- 1023 +local BigArrow -- 1062 +do -- 1062 + local _with_0 = BulletDef() -- 1062 + _with_0.tag = "" -- 1063 + _with_0.endEffect = "" -- 1064 + _with_0.lifeTime = 5 -- 1065 + _with_0.damageRadius = 0 -- 1066 + _with_0.highSpeedFix = false -- 1067 + _with_0.gravity = Vec2(0, -10) -- 1068 + _with_0.face = Face("Model/patreon.clip|item_arrow", Vec2(-100, 0), 2) -- 1069 + _with_0:setAsCircle(10) -- 1070 + _with_0:setVelocity(25, 800) -- 1071 + BigArrow = _with_0 -- 1062 +end -- 1062 +UnitAction:add("multiArrow", { -- 1074 + priority = 3, -- 1074 + reaction = 10, -- 1075 + recovery = 0.1, -- 1076 + queued = true, -- 1077 + available = function(self) -- 1078 + return true -- 1078 + end, -- 1078 + create = function(self) -- 1079 + local attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor -- 1080 + do -- 1080 + local _obj_0 = self.entity -- 1085 + attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.targetAllow, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 1080 + end -- 1085 + do -- 1086 + local _with_0 = self.playable -- 1086 + _with_0.speed = attackSpeed -- 1087 + _with_0:play("range") -- 1088 + _with_0:slot("AnimationEnd", rangeAttackEnd) -- 1089 + end -- 1086 + return once(function(self) -- 1090 + local onAttack -- 1091 + onAttack = function(angle, speed) -- 1091 + BigArrow:setVelocity(angle, speed) -- 1092 + local _with_0 = Bullet(BigArrow, self) -- 1093 + _with_0.targetAllow = targetAllow -- 1094 + _with_0:slot("HitTarget", function(bullet, target, pos) -- 1095 + do -- 1096 + local _with_1 = target.data -- 1096 + _with_1.hitPoint = pos -- 1097 + _with_1.hitPower = attackPower -- 1098 + _with_1.hitFromRight = bullet.velocityX < 0 -- 1099 + end -- 1096 + local entity = target.entity -- 1100 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 1101 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 1102 + entity.hp = entity.hp - damage -- 1103 + bullet.hitStop = true -- 1104 + end) -- 1095 + _with_0:addTo(self.world, self.order) -- 1105 + return _with_0 -- 1093 + end -- 1091 + sleep(30.0 / 60.0 / attackSpeed) -- 1106 + onAttack(30, 1100) -- 1107 + onAttack(10, 1000) -- 1108 + onAttack(-10, 900) -- 1109 + onAttack(-30, 800) -- 1110 + onAttack(-50, 700) -- 1111 + while true do -- 1112 + sleep() -- 1112 + end -- 1112 + end) -- 1112 + end, -- 1079 + stop = function(self) -- 1113 + return self.playable:slot("AnimationEnd"):remove(rangeAttackEnd) -- 1114 + end -- 1113 +}) -- 1073 +UnitAction:add("fallOff", { -- 1117 + priority = 1, -- 1117 + reaction = 1, -- 1118 + recovery = 0, -- 1119 + available = function(self) -- 1120 + return not self.onSurface -- 1120 + end, -- 1120 + create = function(self) -- 1121 + if self.velocityY <= 0 then -- 1122 + self.data.fallDown = true -- 1123 + do -- 1124 + local _with_0 = self.playable -- 1124 + _with_0.speed = 2.5 -- 1125 + _with_0:play("idle") -- 1126 + end -- 1124 + else -- 1127 + self.data.fallDown = false -- 1127 + end -- 1122 + return function(self, action) -- 1128 + if self.onSurface then -- 1129 + return true -- 1129 + end -- 1129 + if not self.data.fallDown and self.velocityY <= 0 then -- 1130 + self.data.fallDown = true -- 1131 + do -- 1132 + local _with_0 = self.playable -- 1132 + _with_0.speed = 2.5 -- 1133 + _with_0:play("idle") -- 1134 + end -- 1132 + end -- 1130 + return false -- 1135 + end -- 1135 + end -- 1121 +}) -- 1116 +UnitAction:add("evade", { -- 1138 + priority = 10, -- 1138 + reaction = 10, -- 1139 + recovery = 0, -- 1140 + queued = true, -- 1141 + available = function(self) -- 1142 + return true -- 1142 + end, -- 1142 + create = function(self) -- 1143 + do -- 1144 + local _with_0 = self.playable -- 1144 + _with_0.speed = 1.0 -- 1145 + _with_0.recovery = 0.0 -- 1146 + _with_0:play("bevade") -- 1147 + end -- 1144 + return once(function(self) -- 1148 + local group = self.group -- 1149 + self.group = Data.groupHide -- 1150 + local dir = self.faceRight and -1 or 1 -- 1151 + cycle(0.2, function() -- 1152 + self.velocityX = 800 * dir -- 1152 + end) -- 1152 + self.group = group -- 1153 + do -- 1154 + local _with_0 = self.playable -- 1154 + _with_0.speed = 1.0 -- 1155 + _with_0:play("idle") -- 1156 + end -- 1154 + sleep(1) -- 1157 + return true -- 1158 + end) -- 1158 + end -- 1143 +}) -- 1137 +local spearAttackEnd -- 1160 +spearAttackEnd = function(name, playable) -- 1160 + if name == "spear" then -- 1161 + return playable.parent:stop() -- 1161 + end -- 1161 +end -- 1160 +UnitAction:add("spearAttack", { -- 1164 + priority = 3, -- 1164 + reaction = 10, -- 1165 + recovery = 0.1, -- 1166 + queued = true, -- 1167 + available = function(self) -- 1168 + return true -- 1168 + end, -- 1168 + create = function(self) -- 1169 + local attackSpeed, attackPower, damageType, attackBase, attackBonus, attackFactor -- 1170 + do -- 1170 + local _obj_0 = self.entity -- 1174 + attackSpeed, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 1170 + end -- 1174 + do -- 1175 + local _with_0 = self.playable -- 1175 + _with_0.speed = attackSpeed -- 1176 + _with_0.recovery = 0.2 -- 1177 + _with_0:play("spear") -- 1178 + _with_0:slot("AnimationEnd", spearAttackEnd) -- 1179 + end -- 1175 + return once(function(self) -- 1180 + sleep(50.0 / 60.0) -- 1181 + local dir = self.faceRight and 0 or -900 -- 1182 + local origin = self.position - Vec2(0, 205) + Vec2(dir, 0) -- 1183 + size = Size(900, 40) -- 1184 + world:query(Rect(origin, size), function(body) -- 1185 + local entity = body.entity -- 1186 + if entity and Data:isEnemy(body, self) then -- 1187 + do -- 1188 + local _with_0 = body.data -- 1188 + _with_0.hitPoint = body.position -- 1189 + _with_0.hitPower = attackPower -- 1190 + _with_0.hitFromRight = not self.faceRight -- 1191 + end -- 1188 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 1192 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 1193 + entity.hp = entity.hp - damage -- 1194 + end -- 1187 + return false -- 1195 + end) -- 1185 + while true do -- 1196 + sleep() -- 1196 + end -- 1196 + end) -- 1196 + end, -- 1169 + stop = function(self) -- 1197 + return self.playable:slot("AnimationEnd"):remove(spearAttackEnd) -- 1198 + end -- 1197 +}) -- 1163 +local mutables = { -- 1201 + "hp", -- 1201 + "moveSpeed", -- 1202 + "move", -- 1203 + "jump", -- 1204 + "targetAllow", -- 1205 + "attackBase", -- 1206 + "attackPower", -- 1207 + "attackSpeed", -- 1208 + "damageType", -- 1209 + "attackBonus", -- 1210 + "attackFactor", -- 1211 + "attackTarget", -- 1212 + "defenceType" -- 1213 +} -- 1200 +do -- 1216 + local _with_0 = Observer("Add", { -- 1216 + "unitDef", -- 1216 + "position", -- 1216 + "order", -- 1216 + "group", -- 1216 + "faceRight" -- 1216 + }) -- 1216 + _with_0:watch(function(self, unitDef, position, order, group) -- 1217 + local player, faceRight, charSet, decisionTree = self.player, self.faceRight, self.charSet, self.decisionTree -- 1218 + world = Store.world -- 1219 + local func = UnitDefFuncs[unitDef] -- 1220 + local def = func() -- 1221 + for _index_0 = 1, #mutables do -- 1222 + local var = mutables[_index_0] -- 1222 + self[var] = def[var] -- 1223 + end -- 1223 + if charSet then -- 1224 + local set = characters[charSet] -- 1225 + local actions = def.actions -- 1226 + local actionSet -- 1227 + do -- 1227 + local _tbl_0 = { } -- 1227 + for _index_0 = 1, #actions do -- 1227 + local a = actions[_index_0] -- 1227 + _tbl_0[a] = true -- 1227 + end -- 1227 + actionSet = _tbl_0 -- 1227 + end -- 1227 + for _index_0 = 1, #itemSlots do -- 1228 + local slot = itemSlots[_index_0] -- 1228 + local item = set[slot] -- 1229 + if not item then -- 1230 + goto _continue_0 -- 1230 + end -- 1230 + local skill = itemSettings[item].skill -- 1231 + if skill and not actionSet[skill] then -- 1232 + actions:add(skill) -- 1233 + end -- 1232 + local attackRange = itemSettings[item].attackRange -- 1234 + if attackRange then -- 1235 + def.attackRange = attackRange -- 1235 + end -- 1235 + ::_continue_0:: -- 1229 + end -- 1235 + end -- 1224 + if decisionTree then -- 1236 + def.decisionTree = decisionTree -- 1236 + end -- 1236 + local unit -- 1237 + do -- 1237 + local _with_1 = Unit(def, world, self, position) -- 1237 + _with_1.group = group -- 1238 + _with_1.order = order -- 1239 + _with_1.faceRight = faceRight -- 1240 + _with_1:addTo(world) -- 1241 + unit = _with_1 -- 1237 + end -- 1237 + if charSet then -- 1242 + updateModel(unit.playable, characters[charSet]) -- 1242 + end -- 1242 + if player then -- 1243 + world.camera.followTarget = unit -- 1244 + end -- 1243 + return false -- 1244 + end) -- 1217 +end -- 1216 +local _with_0 = Observer("Change", { -- 1246 + "hp", -- 1246 + "unit" -- 1246 +}) -- 1246 +_with_0:watch(function(self, hp, unit) -- 1247 + local boss = self.boss -- 1248 + local lastHp = self.oldValues.hp -- 1249 + if hp < lastHp then -- 1250 + if not boss and unit:isDoing("hit") then -- 1251 + unit:start("cancel") -- 1251 + end -- 1251 + if boss then -- 1252 + do -- 1253 + local _with_1 = Visual("Particle/bloodp.par") -- 1253 + _with_1.position = unit.data.hitPoint -- 1254 + _with_1:addTo(world, unit.order) -- 1255 + _with_1:autoRemove() -- 1256 + _with_1:start() -- 1257 + end -- 1253 + end -- 1252 + if hp > 0 then -- 1258 + unit:start("hit") -- 1259 + else -- 1261 + unit:start("hit") -- 1261 + unit:start("fall") -- 1262 + unit.group = Data.groupHide -- 1263 + if self.player then -- 1264 + playerGroup:each(function(p) -- 1265 + if p and p.unit and p.hp > 0 then -- 1266 + world.camera.followTarget = p.unit -- 1267 + return true -- 1268 + else -- 1269 + return false -- 1269 + end -- 1266 + end) -- 1265 + end -- 1264 + end -- 1258 + end -- 1250 + return false -- 1269 +end) -- 1247 +return _with_0 -- 1246 diff --git a/Assets/Script/Game/Dismantlism/init.lua b/Assets/Script/Game/Dismantlism/init.lua new file mode 100644 index 000000000..6e394999c --- /dev/null +++ b/Assets/Script/Game/Dismantlism/init.lua @@ -0,0 +1,460 @@ +-- [yue]: Script/Game/Dismantlism/init.yue +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +local Node = dora.Node -- 1 +local Audio = dora.Audio -- 1 +local Sprite = dora.Sprite -- 1 +local Label = dora.Label -- 1 +local Color3 = dora.Color3 -- 1 +local PhysicsWorld = dora.PhysicsWorld -- 1 +local Grid = dora.Grid -- 1 +local Vec2 = dora.Vec2 -- 1 +local X = dora.X -- 1 +local Sequence = dora.Sequence -- 1 +local Delay = dora.Delay -- 1 +local Spawn = dora.Spawn -- 1 +local Opacity = dora.Opacity -- 1 +local Ease = dora.Ease -- 1 +local Y = dora.Y -- 1 +local Event = dora.Event -- 1 +local Rect = dora.Rect -- 1 +local Size = dora.Size -- 1 +local Joint = dora.Joint -- 1 +local Show = dora.Show -- 1 +local Hide = dora.Hide -- 1 +local Scale = dora.Scale -- 1 +local Color = dora.Color -- 1 +local App = dora.App -- 1 +local tostring = _G.tostring -- 1 +local scriptPath = Path:getScriptPath(...) -- 4 +if not scriptPath then -- 5 + return -- 5 +end -- 5 +Content:insertSearchPath(1, scriptPath) -- 6 +local BodyEx = require("BodyEx") -- 8 +local SolidRect = require("UI.View.Shape.SolidRect") -- 9 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 10 +local Struct, Set -- 11 +do -- 11 + local _obj_0 = require("Utils") -- 11 + Struct, Set = _obj_0.Struct, _obj_0.Set -- 11 +end -- 11 +local root -- 13 +do -- 13 + local _with_0 = Node() -- 13 + _with_0:slot("Cleanup", function() -- 14 + return Audio:stopStream(0.2) -- 14 + end) -- 14 + root = _with_0 -- 13 +end -- 13 +local ui -- 15 +do -- 15 + local _with_0 = Node() -- 15 + _with_0.x = -100 -- 16 + _with_0.y = -50 -- 17 + _with_0:addChild(Sprite("Model/duality.clip|stat")) -- 18 + ui = _with_0 -- 15 +end -- 15 +local score = 0 -- 19 +local scoreTxt -- 20 +do -- 20 + local _with_0 = Label("sarasa-mono-sc-regular", 40) -- 20 + _with_0.textAlign = "Center" -- 21 + _with_0.color3 = Color3(0x0) -- 22 + _with_0.text = "0" -- 23 + _with_0:addTo(ui) -- 24 + scoreTxt = _with_0 -- 20 +end -- 20 +local world -- 26 +do -- 26 + local _with_0 = PhysicsWorld() -- 26 + _with_0.y = 405 -- 27 + _with_0:setShouldContact(0, 0, true) -- 28 + _with_0:setShouldContact(0, 1, false) -- 29 + _with_0:addTo(root) -- 30 + world = _with_0 -- 26 +end -- 26 +local isSpace = true -- 32 +local switchScene = nil -- 33 +local spaceBack = nil -- 35 +local dailyBack = nil -- 36 +local center = AlignNode({ -- 37 + hAlign = "Center", -- 37 + vAlign = "Center" -- 37 +}) -- 37 +do -- 38 + local _with_0 = AlignNode({ -- 38 + isRoot = true, -- 38 + inUI = false -- 38 + }) -- 38 + _with_0:slot("AlignLayout", function(w, h) -- 39 + local worldScale = w / 2970 -- 40 + root.scaleX = worldScale -- 41 + root.scaleY = worldScale -- 42 + ui.scaleX = worldScale -- 43 + ui.scaleY = worldScale -- 44 + if spaceBack then -- 45 + spaceBack:removeFromParent() -- 45 + end -- 45 + do -- 46 + local _with_1 = Node() -- 46 + _with_1.visible = isSpace -- 47 + _with_1.order = -1 -- 48 + _with_1:addChild((function() -- 49 + local _with_2 = Grid("Model/duality.clip|space", 1, 1) -- 49 + _with_2:moveUV(1, 1, Vec2(1, 1)) -- 50 + _with_2:moveUV(2, 1, Vec2(-1, 1)) -- 51 + _with_2:moveUV(1, 2, Vec2(1, -1)) -- 52 + _with_2:moveUV(2, 2, Vec2(-1, -1)) -- 53 + _with_2.scaleX = w / 8 -- 54 + _with_2.scaleY = h / 1078 -- 55 + return _with_2 -- 49 + end)()) -- 49 + _with_1:addChild((function() -- 56 + local _with_2 = Node() -- 56 + _with_2.scaleX = worldScale -- 57 + _with_2.scaleY = worldScale -- 58 + for y = 1, 8 do -- 59 + for x = 1, 8 do -- 59 + _with_2:addChild((function() -- 60 + local _with_3 = Sprite("Model/duality.clip|stary") -- 60 + _with_3.anchorX = 0 -- 61 + _with_3.x = -3000 + (x - 1) * 1000 -- 62 + _with_3.y = 3000 - (y - 1) * 1000 -- 63 + return _with_3 -- 60 + end)()) -- 60 + end -- 63 + end -- 63 + _with_2:perform(X(10, 0, -1000 * worldScale)) -- 64 + _with_2:slot("ActionEnd", function() -- 65 + return _with_2:perform(X(10, 0, -1000 * worldScale)) -- 65 + end) -- 65 + return _with_2 -- 56 + end)()) -- 56 + spaceBack = _with_1 -- 46 + end -- 46 + if dailyBack then -- 66 + dailyBack:removeFromParent() -- 66 + end -- 66 + do -- 67 + local _with_1 = Grid("Model/duality.clip|day", 1, 1) -- 67 + _with_1.visible = not isSpace -- 68 + _with_1.order = -1 -- 69 + _with_1:moveUV(1, 1, Vec2(1, 1)) -- 70 + _with_1:moveUV(2, 1, Vec2(-1, 1)) -- 71 + _with_1:moveUV(1, 2, Vec2(1, -1)) -- 72 + _with_1:moveUV(2, 2, Vec2(-1, -1)) -- 73 + _with_1.scaleX = w / 8 -- 74 + _with_1.scaleY = h / 1078 -- 75 + dailyBack = _with_1 -- 67 + end -- 67 + end) -- 39 + _with_0:addChild((function() -- 76 + local _with_1 = AlignNode({ -- 76 + hAlign = "Center", -- 76 + vAlign = "Bottom" -- 76 + }) -- 76 + _with_1:addChild(root) -- 77 + return _with_1 -- 76 + end)()) -- 76 + _with_0:addChild((function() -- 78 + local _with_1 = AlignNode({ -- 78 + hAlign = "Right", -- 78 + vAlign = "Top" -- 78 + }) -- 78 + _with_1:addChild(ui) -- 79 + return _with_1 -- 78 + end)()) -- 78 + _with_0:addChild((function() -- 80 + center:addChild((function() -- 81 + local _with_1 = Node() -- 81 + _with_1.touchEnabled = true -- 82 + _with_1.swallowTouches = true -- 83 + _with_1:addChild(Sprite("Model/duality.clip|dismantlism")) -- 84 + _with_1:perform(Sequence(Delay(1), Spawn(Opacity(1, 1, 0, Ease.OutQuad), Y(1, 0, 100, Ease.InQuad)), Event("Start"))) -- 85 + _with_1:slot("Start", function() -- 93 + isSpace = false -- 94 + switchScene() -- 95 + return _with_1:removeFromParent() -- 96 + end) -- 93 + return _with_1 -- 81 + end)()) -- 81 + return center -- 80 + end)()) -- 80 + _with_0:alignLayout() -- 97 +end -- 38 +local moveJoint = nil -- 99 +local movingBody = nil -- 100 +do -- 101 + local _with_0 = Node() -- 101 + _with_0.touchEnabled = true -- 102 + _with_0:slot("TapBegan", function(touch) -- 103 + local worldPos = _with_0:convertToWorldSpace(touch.location) -- 104 + local pos = world:convertToNodeSpace(worldPos) -- 105 + return world:query(Rect(pos - Vec2(1, 1), Size(2, 2)), function(body) -- 106 + if body.tag ~= "" and body.tag ~= (isSpace and "space" or "daily") then -- 107 + return false -- 107 + end -- 107 + if moveJoint then -- 108 + moveJoint:destroy() -- 108 + end -- 108 + moveJoint = Joint:move(true, body, pos, 400) -- 109 + movingBody = body -- 110 + return true -- 111 + end) -- 111 + end) -- 103 + _with_0:slot("TapMoved", function(touch) -- 112 + if moveJoint then -- 113 + local worldPos = _with_0:convertToWorldSpace(touch.location) -- 114 + local pos = world:convertToNodeSpace(worldPos) -- 115 + moveJoint.position = pos -- 116 + end -- 113 + end) -- 112 + _with_0:slot("TapEnded", function() -- 117 + if moveJoint then -- 118 + moveJoint:destroy() -- 119 + moveJoint = nil -- 120 + movingBody = nil -- 121 + end -- 118 + end) -- 117 +end -- 101 +local scene = require("scene") -- 123 +Struct.Body("name", "file", "position", "angle") -- 124 +Struct:load(scene) -- 125 +local spaceItems = Set({ -- 128 + "rocket", -- 128 + "satlite", -- 129 + "spacestation", -- 130 + "star1", -- 131 + "star2", -- 132 + "ufo", -- 133 + "get" -- 134 +}) -- 127 +local dailyItems = Set({ -- 137 + "baseball", -- 137 + "burger", -- 138 + "donut", -- 139 + "fish", -- 140 + "radio", -- 141 + "tv", -- 142 + "pizza" -- 143 +}) -- 136 +local spaceBodies = { } -- 145 +local dailyBodies = { } -- 146 +switchScene = function() -- 148 + if isSpace then -- 149 + Audio:playStream("Audio/Dismantlism Space.ogg", true, 0.2) -- 150 + dailyBack:perform(Sequence(Show(), Opacity(0.5, 1, 0), Hide())) -- 151 + spaceBack:perform(Sequence(Show(), Opacity(0.5, 0, 1))) -- 156 + for _index_0 = 1, #dailyBodies do -- 160 + local body = dailyBodies[_index_0] -- 160 + do -- 161 + local _with_0 = body.children[1] -- 161 + if _with_0.actionCount == 0 then -- 162 + _with_0:perform(Sequence(Show(), Scale(0.5, 1, 0, Ease.OutBack), Hide())) -- 163 + end -- 162 + end -- 161 + end -- 167 + for _index_0 = 1, #spaceBodies do -- 168 + local body = spaceBodies[_index_0] -- 168 + do -- 169 + local _with_0 = body.children[1] -- 169 + if _with_0.actionCount == 0 then -- 170 + _with_0:perform(Sequence(Show(), Scale(0.5, 0, 1, Ease.OutBack))) -- 171 + end -- 170 + end -- 169 + end -- 174 + else -- 176 + Audio:playStream("Audio/Dismantlism Daily.ogg", true, 0.2) -- 176 + spaceBack:perform(Sequence(Show(), Opacity(0.5, 1, 0), Hide())) -- 177 + dailyBack:perform(Sequence(Show(), Opacity(0.5, 0, 1))) -- 182 + for _index_0 = 1, #spaceBodies do -- 186 + local body = spaceBodies[_index_0] -- 186 + do -- 187 + local _with_0 = body.children[1] -- 187 + if _with_0.actionCount == 0 then -- 188 + _with_0:perform(Sequence(Show(), Scale(0.5, 1, 0, Ease.OutBack), Hide())) -- 189 + end -- 188 + end -- 187 + end -- 193 + for _index_0 = 1, #dailyBodies do -- 194 + local body = dailyBodies[_index_0] -- 194 + do -- 195 + local _with_0 = body.children[1] -- 195 + if _with_0.actionCount == 0 then -- 196 + _with_0:perform(Sequence(Show(), Scale(0.5, 0, 1, Ease.OutBack))) -- 197 + end -- 196 + end -- 195 + end -- 200 + end -- 149 +end -- 148 +local restartScene = nil -- 202 +local gameEnded = false -- 203 +local buildScene -- 204 +buildScene = function() -- 204 + for i = 1, scene:count() do -- 205 + local name, file, position, angle -- 206 + do -- 206 + local _obj_0 = scene:get(i) -- 211 + name, file, position, angle = _obj_0.name, _obj_0.file, _obj_0.position, _obj_0.angle -- 206 + if position == nil then -- 209 + position = Vec2.zero -- 209 + end -- 209 + if angle == nil then -- 210 + angle = 0 -- 210 + end -- 210 + end -- 211 + local node = BodyEx(require(Path("Physics", file)), world, position, angle) -- 212 + world:addChild(node) -- 213 + if spaceItems[file] then -- 214 + node.data:each(function(self) -- 215 + self.tag = "space" -- 216 + self.children[1].tag = file -- 217 + spaceBodies[#spaceBodies + 1] = self -- 218 + end) -- 215 + elseif dailyItems[file] then -- 219 + node.data:each(function(self) -- 220 + self.tag = "daily" -- 221 + self.children[1].tag = file -- 222 + dailyBodies[#dailyBodies + 1] = self -- 223 + end) -- 220 + else -- 225 + node.data:each(function(self) -- 225 + if self.children and #self.children > 0 then -- 226 + self.children[1].tag = file -- 226 + end -- 226 + end) -- 225 + end -- 214 + if "removearea" == file then -- 228 + do -- 229 + local _with_0 = node.data.rect -- 229 + _with_0:addChild(SolidRect({ -- 230 + x = -200, -- 230 + y = -200, -- 230 + width = 400, -- 230 + height = 400, -- 230 + color = 0x66000000 -- 230 + })) -- 230 + _with_0:addChild((function() -- 231 + local _with_1 = Label("sarasa-mono-sc-regular", 80) -- 231 + _with_1.textAlign = "Center" -- 232 + _with_1.color = Color(0x66ffffff) -- 233 + _with_1.text = "Drag It\nHere" -- 234 + return _with_1 -- 231 + end)()) -- 231 + _with_0:slot("BodyEnter", function(body) -- 235 + if body.tag ~= "" and body.tag ~= (isSpace and "space" or "daily") then -- 236 + return -- 236 + end -- 236 + if body.group == 1 then -- 237 + return -- 237 + end -- 237 + body.group = 1 -- 238 + local _with_1 = body.children[1] -- 239 + _with_1:perform(Sequence(Spawn(Opacity(0.5, 1, 0), Scale(0.5, 1, 1.5, Ease.OutBack)), Event("Destroy"))) -- 240 + _with_1:slot("Destroy", function() -- 244 + do -- 245 + local _exp_0 = _with_1.tag -- 245 + if "star2" == _exp_0 or "pizza" == _exp_0 then -- 246 + score = score + 10 -- 247 + isSpace = not isSpace -- 248 + switchScene() -- 249 + elseif "quit" == _exp_0 then -- 250 + App:shutdown() -- 251 + elseif "get" == _exp_0 or "fish" == _exp_0 then -- 252 + score = score + 100 -- 253 + elseif "credit" == _exp_0 then -- 254 + score = score + 50 -- 255 + world:addChild((function() -- 256 + local _with_2 = Node() -- 256 + _with_2:addChild(Sprite("Model/duality.clip|window")) -- 257 + _with_2:addChild(Sprite("Model/duality.clip|credits1")) -- 258 + _with_2.position = body.position -- 259 + _with_2:perform(Sequence(Spawn(Scale(0.5, 0, 1, Ease.OutBack), Opacity(0.5, 0, 1)), Delay(3), Scale(0.5, 1, 0, Ease.InBack), Event("End"))) -- 260 + _with_2:slot("End", function() -- 269 + return _with_2:removeFromParent() -- 269 + end) -- 269 + return _with_2 -- 256 + end)()) -- 256 + else -- 271 + score = score + 10 -- 271 + end -- 271 + end -- 271 + scoreTxt.text = tostring(score) -- 272 + if score > 600 then -- 273 + gameEnded = true -- 274 + center:addChild((function() -- 275 + local _with_2 = Node() -- 275 + _with_2:addChild(Sprite("Model/duality.clip|window")) -- 276 + _with_2:addChild(Sprite("Model/duality.clip|win")) -- 277 + _with_2:perform(Sequence(Spawn(Scale(0.5, 0, 1, Ease.OutBack), Opacity(0.5, 0, 1)), Delay(3), Scale(0.5, 1, 0, Ease.InBack), Event("End"))) -- 278 + _with_2:slot("End", function() -- 287 + _with_2:removeFromParent() -- 288 + return restartScene() -- 289 + end) -- 287 + return _with_2 -- 275 + end)()) -- 275 + end -- 273 + if movingBody == body and moveJoint then -- 290 + moveJoint:destroy() -- 291 + moveJoint = nil -- 292 + movingBody = nil -- 293 + end -- 290 + return body:removeFromParent() -- 294 + end) -- 244 + return _with_1 -- 239 + end) -- 235 + end -- 229 + elseif "safearea" == file then -- 295 + do -- 296 + local _with_0 = node.data.rect -- 296 + _with_0:slot("BodyEnter", function(body) -- 297 + if body == movingBody then -- 298 + return -- 298 + end -- 298 + local tag = body.children[1].tag -- 299 + if (name == "safe1" and tag == "get") or (name == "safe2" and tag == "fish") then -- 300 + if not gameEnded then -- 302 + gameEnded = true -- 303 + return world:addChild((function() -- 304 + local _with_1 = Node() -- 304 + _with_1:addChild(Sprite("Model/duality.clip|window")) -- 305 + _with_1:addChild(Sprite("Model/duality.clip|lose")) -- 306 + _with_1.position = body.position -- 307 + _with_1:perform(Sequence(Spawn(Scale(0.5, 0, 1, Ease.OutBack), Opacity(0.5, 0, 1)), Delay(2), Scale(0.5, 1, 0, Ease.InBack), Event("End"))) -- 308 + _with_1:slot("End", function() -- 317 + return restartScene() -- 317 + end) -- 317 + return _with_1 -- 304 + end)()) -- 317 + end -- 302 + end -- 300 + end) -- 297 + end -- 296 + end -- 317 + end -- 317 +end -- 204 +buildScene() -- 319 +switchScene() -- 320 +restartScene = function() -- 322 + score = 0 -- 323 + scoreTxt.text = "0" -- 324 + isSpace = false -- 325 + gameEnded = false -- 326 + if moveJoint then -- 327 + moveJoint:destroy() -- 328 + moveJoint = nil -- 329 + movingBody = nil -- 330 + end -- 327 + world:removeFromParent() -- 331 + do -- 332 + local _with_0 = PhysicsWorld() -- 332 + _with_0.y = 405 -- 333 + _with_0:setShouldContact(0, 0, true) -- 334 + _with_0:setShouldContact(0, 1, false) -- 335 + _with_0:addTo(root) -- 336 + world = _with_0 -- 332 + end -- 332 + buildScene() -- 337 + return switchScene() -- 338 +end -- 322 diff --git a/Assets/Script/Game/Involution Warrior/init.lua b/Assets/Script/Game/Involution Warrior/init.lua new file mode 100644 index 000000000..13ee83324 --- /dev/null +++ b/Assets/Script/Game/Involution Warrior/init.lua @@ -0,0 +1,1382 @@ +-- [yue]: Script/Game/Involution Warrior/init.yue +local _module_1 = dora.Platformer -- 1 +local Data = _module_1.Data -- 1 +local App = dora.App -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local DrawNode = dora.DrawNode -- 1 +local Color = dora.Color -- 1 +local Line = dora.Line -- 1 +local Group = dora.Group -- 1 +local PlatformWorld = _module_1.PlatformWorld -- 1 +local table = _G.table -- 1 +local math = _G.math -- 1 +local View = dora.View -- 1 +local BodyDef = dora.BodyDef -- 1 +local Body = dora.Body -- 1 +local UnitAction = _module_1.UnitAction -- 1 +local once = dora.once -- 1 +local Audio = dora.Audio -- 1 +local Bullet = _module_1.Bullet -- 1 +local sleep = dora.sleep -- 1 +local BulletDef = _module_1.BulletDef -- 1 +local Face = _module_1.Face -- 1 +local cycle = dora.cycle -- 1 +local Rect = dora.Rect -- 1 +local Dictionary = dora.Dictionary -- 1 +local TargetAllow = _module_1.TargetAllow -- 1 +local Array = dora.Array -- 1 +local Unit = _module_1.Unit -- 1 +local tostring = _G.tostring -- 1 +local Sprite = dora.Sprite -- 1 +local pairs = _G.pairs -- 1 +local _module_2 = dora.Platformer.Decision -- 1 +local Sel = _module_2.Sel -- 1 +local Seq = _module_2.Seq -- 1 +local Con = _module_2.Con -- 1 +local Accept = _module_2.Accept -- 1 +local Act = _module_2.Act -- 1 +local AI = _module_2.AI -- 1 +local Observer = dora.Observer -- 1 +local Action = dora.Action -- 1 +local Scale = dora.Scale -- 1 +local Ease = dora.Ease -- 1 +local Label = dora.Label -- 1 +local Sequence = dora.Sequence -- 1 +local Y = dora.Y -- 1 +local Opacity = dora.Opacity -- 1 +local Event = dora.Event -- 1 +local Visual = _module_1.Visual -- 1 +local emit = dora.emit -- 1 +local Spawn = dora.Spawn -- 1 +local Director = dora.Director -- 1 +local string = _G.string -- 1 +local Delay = dora.Delay -- 1 +local Entity = dora.Entity -- 1 +local _module_0 = dora.ImGui -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local ProgressBar = _module_0.ProgressBar -- 1 +local Node = dora.Node -- 1 +local PushStyleVar = _module_0.PushStyleVar -- 1 +local Text = _module_0.Text -- 1 +local Image = _module_0.Image -- 1 +local SameLine = _module_0.SameLine -- 1 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 7 +local CircleButton = require("UI.Control.Basic.CircleButton") -- 8 +local Set = require("Utils").Set -- 9 +local Store = Data.store -- 10 +local themeColor = App.themeColor -- 12 +local mutables = { -- 17 + "hp", -- 17 + "moveSpeed", -- 18 + "move", -- 19 + "jump", -- 20 + "targetAllow", -- 21 + "attackBase", -- 22 + "attackPower", -- 23 + "attackSpeed", -- 24 + "damageType", -- 25 + "attackBonus", -- 26 + "attackFactor", -- 27 + "attackTarget", -- 28 + "defenceType" -- 29 +} -- 16 +local elementTypes = { -- 33 + Green = 1, -- 33 + Red = 2, -- 34 + Yellow = 3, -- 35 + Purple = 4 -- 36 +} -- 32 +do -- 38 + local _with_0 = Data -- 38 + _with_0:setDamageFactor(elementTypes.Green, elementTypes.Red, 3) -- 39 + _with_0:setDamageFactor(elementTypes.Red, elementTypes.Yellow, 3) -- 40 + _with_0:setDamageFactor(elementTypes.Yellow, elementTypes.Green, 3) -- 41 +end -- 38 +local itemSlots = { -- 44 + "head", -- 44 + "mask", -- 45 + "body", -- 46 + "lhand", -- 47 + "rhand" -- 48 +} -- 43 +local headItems = { -- 51 + "item_hat", -- 51 + "item_hatTop", -- 52 + "item_helmet", -- 53 + "item_helmetModern" -- 54 +} -- 50 +local lhandItems = { -- 57 + "item_shield", -- 57 + "item_shieldRound", -- 58 + "tile_heart", -- 59 + "ui_hand" -- 60 +} -- 56 +local rhandItems = { -- 63 + "item_bow", -- 63 + "item_sword", -- 64 + "item_rod", -- 65 + "item_spear" -- 66 +} -- 62 +local characterTypes = { -- 69 + "square", -- 69 + "round" -- 70 +} -- 68 +local characterColors = { -- 73 + "Green", -- 73 + "Red", -- 74 + "Yellow" -- 75 +} -- 72 +local masks = { -- 78 + "bear", -- 78 + "buffalo", -- 79 + "chick", -- 80 + "chicken", -- 81 + "cow", -- 82 + "crocodile", -- 83 + "dog", -- 84 + "duck", -- 85 + "elephant", -- 86 + "frog", -- 87 + "giraffe", -- 88 + "goat", -- 89 + "gorilla", -- 90 + "hippo", -- 91 + "horse", -- 92 + "monkey", -- 93 + "moose", -- 94 + "narwhal", -- 95 + "owl", -- 96 + "panda", -- 97 + "parrot", -- 98 + "penguin", -- 99 + "pig", -- 100 + "rabbit", -- 101 + "rhino", -- 102 + "sloth", -- 103 + "snake", -- 104 + "walrus", -- 105 + "whale", -- 106 + "zebra" -- 107 +} -- 77 +local itemSettings = { -- 111 + item_hat = { -- 112 + skill = "jump", -- 112 + offset = Vec2(0, 30) -- 113 + }, -- 111 + item_hatTop = { -- 116 + skill = "evade", -- 116 + offset = Vec2(0, 30) -- 117 + }, -- 115 + item_helmet = { -- 120 + skill = "rush", -- 120 + offset = Vec2(0, 0) -- 121 + }, -- 119 + item_helmetModern = { -- 124 + skill = "rush", -- 124 + offset = Vec2(0, 0) -- 125 + }, -- 123 + item_shield = { -- 128 + skill = "", -- 128 + offset = Vec2(0, 0) -- 129 + }, -- 127 + item_shieldRound = { -- 132 + skill = "jump", -- 132 + offset = Vec2(0, 0) -- 133 + }, -- 131 + tile_heart = { -- 136 + skill = "jump", -- 136 + offset = Vec2(0, 0), -- 137 + attackPower = Vec2(600, 0) -- 138 + }, -- 135 + ui_hand = { -- 141 + skill = "evade", -- 141 + offset = Vec2(0, 0) -- 142 + }, -- 140 + item_bow = { -- 145 + skill = "range", -- 145 + offset = Vec2(10, 0), -- 146 + attackRange = Size(550, 150), -- 147 + sndAttack = "Audio/d_att.wav" -- 148 + }, -- 144 + item_sword = { -- 151 + skill = "meleeAttack", -- 151 + offset = Vec2(15, 50), -- 152 + attackRange = Size(120, 150), -- 153 + sndAttack = "Audio/f_att.wav" -- 154 + }, -- 150 + item_rod = { -- 157 + skill = "meleeAttack", -- 157 + offset = Vec2(15, 50), -- 158 + attackRange = Size(200, 150), -- 159 + attackPower = Vec2(100, 800), -- 160 + sndAttack = "Audio/b_att.wav" -- 161 + }, -- 156 + item_spear = { -- 164 + skill = "meleeAttack", -- 164 + offset = Vec2(15, 50), -- 165 + attackRange = Size(200, 150), -- 166 + sndAttack = "Audio/f_att.wav" -- 167 + } -- 163 +} -- 110 +local GamePaused = true -- 169 +local size, grid = 1500, 150 -- 173 +local background -- 175 +background = function() -- 175 + local _with_0 = DrawNode() -- 175 + _with_0.depthWrite = true -- 176 + _with_0:drawPolygon({ -- 178 + Vec2(-size, size), -- 178 + Vec2(size, size), -- 179 + Vec2(size, -size), -- 180 + Vec2(-size, -size) -- 181 + }, Color(0xff888888)) -- 177 + _with_0:addChild((function() -- 183 + local _with_1 = Line() -- 183 + _with_1.depthWrite = true -- 184 + _with_1.z = -10 -- 185 + for i = -size / grid, size / grid do -- 186 + _with_1:add({ -- 188 + Vec2(i * grid, size), -- 188 + Vec2(i * grid, -size) -- 189 + }, Color(0xff000000)) -- 187 + _with_1:add({ -- 192 + Vec2(-size, i * grid), -- 192 + Vec2(size, i * grid) -- 193 + }, Color(0xff000000)) -- 191 + end -- 194 + return _with_1 -- 183 + end)()) -- 183 + return _with_0 -- 175 +end -- 175 +do -- 196 + local _with_0 = background() -- 196 + _with_0.z = 600 -- 197 +end -- 196 +do -- 198 + local _with_0 = background() -- 198 + _with_0.angleX = 45 -- 199 +end -- 198 +local TerrainLayer = 0 -- 203 +local EnemyLayer = 1 -- 204 +local PlayerLayer = 2 -- 205 +local PlayerGroup = 1 -- 207 +local EnemyGroup = 2 -- 208 +Data:setRelation(PlayerGroup, EnemyGroup, "Enemy") -- 210 +Data:setShouldContact(PlayerGroup, EnemyGroup, true) -- 211 +local unitGroup = Group({ -- 213 + "unit" -- 213 +}) -- 213 +local world -- 215 +do -- 215 + local _with_0 = PlatformWorld() -- 215 + _with_0:schedule(function() -- 216 + local origin = Vec2.zero -- 217 + local locs = { -- 218 + origin -- 218 + } -- 218 + unitGroup:each(function(self) -- 219 + return table.insert(locs, self.unit.position) -- 219 + end) -- 219 + local dist = 0 -- 220 + for _index_0 = 1, #locs do -- 221 + local loc = locs[_index_0] -- 221 + dist = math.max(dist, loc:distance(origin)) -- 222 + end -- 222 + local DesignWidth = 1250 -- 223 + local currentZoom = _with_0.camera.zoom -- 224 + local baseZoom = View.size.width / DesignWidth -- 225 + local targetZoom = baseZoom * math.max(math.min(3.0, (DesignWidth / dist / 4)), 0.8) -- 226 + _with_0.camera.zoom = currentZoom + (targetZoom - currentZoom) * 0.005 -- 227 + end) -- 216 + world = _with_0 -- 215 +end -- 215 +Store["world"] = world -- 229 +local terrainDef -- 231 +do -- 231 + local _with_0 = BodyDef() -- 231 + _with_0.type = "Static" -- 232 + _with_0:attachPolygon(Vec2(0, 0), 2500, 10, 0, 1, 1, 0) -- 233 + _with_0:attachPolygon(Vec2(0, 1000), 2500, 10, 0, 1, 1, 0) -- 234 + _with_0:attachPolygon(Vec2(800, 1000), 10, 2000, 0, 1, 1, 0) -- 235 + _with_0:attachPolygon(Vec2(-800, 1000), 10, 2000, 0, 1, 1, 0) -- 236 + terrainDef = _with_0 -- 231 +end -- 231 +do -- 238 + local _with_0 = Body(terrainDef, world, Vec2.zero) -- 238 + _with_0.order = TerrainLayer -- 239 + _with_0.group = Data.groupTerrain -- 240 + _with_0:addTo(world) -- 241 +end -- 238 +local rangeAttackEnd -- 245 +rangeAttackEnd = function(name, playable) -- 245 + if name == "range" then -- 246 + return playable.parent:stop() -- 246 + end -- 246 +end -- 245 +UnitAction:add("range", { -- 249 + priority = 3, -- 249 + reaction = 10, -- 250 + recovery = 0.1, -- 251 + queued = true, -- 252 + available = function(self) -- 253 + return true -- 253 + end, -- 253 + create = function(self) -- 254 + local attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor -- 255 + do -- 255 + local _obj_0 = self.entity -- 260 + attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.targetAllow, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 255 + end -- 260 + do -- 261 + local _with_0 = self.playable -- 261 + _with_0.speed = attackSpeed -- 262 + _with_0:play("range") -- 263 + _with_0:slot("AnimationEnd", rangeAttackEnd) -- 264 + end -- 261 + return once(function(self) -- 265 + local bulletDef = Store[self.unitDef.bulletType] -- 266 + local onAttack -- 267 + onAttack = function() -- 267 + Audio:play(self.unitDef.sndAttack) -- 268 + local _with_0 = Bullet(bulletDef, self) -- 269 + if self.group == EnemyGroup then -- 270 + _with_0.color = Color(0xff666666) -- 270 + end -- 270 + _with_0.targetAllow = targetAllow -- 271 + _with_0:slot("HitTarget", function(bullet, target, pos) -- 272 + do -- 273 + local _with_1 = target.data -- 273 + _with_1.hitPoint = pos -- 274 + _with_1.hitPower = attackPower -- 275 + _with_1.hitFromRight = bullet.velocityX < 0 -- 276 + end -- 273 + local entity = target.entity -- 277 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 278 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 279 + entity.hp = entity.hp - damage -- 280 + bullet.hitStop = true -- 281 + end) -- 272 + _with_0:addTo(self.world, self.order) -- 282 + return _with_0 -- 269 + end -- 267 + sleep(0.5 * 28.0 / 30.0 / attackSpeed) -- 283 + onAttack() -- 284 + while true do -- 285 + sleep() -- 285 + end -- 285 + end) -- 285 + end, -- 254 + stop = function(self) -- 286 + return self.playable:slot("AnimationEnd"):remove(rangeAttackEnd) -- 287 + end -- 286 +}) -- 248 +local BigArrow -- 289 +do -- 289 + local _with_0 = BulletDef() -- 289 + _with_0.tag = "" -- 290 + _with_0.endEffect = "" -- 291 + _with_0.lifeTime = 5 -- 292 + _with_0.damageRadius = 0 -- 293 + _with_0.highSpeedFix = false -- 294 + _with_0.gravity = Vec2(0, -10) -- 295 + _with_0.face = Face("Model/patreon.clip|item_arrow", Vec2(-100, 0), 2) -- 296 + _with_0:setAsCircle(10) -- 297 + _with_0:setVelocity(25, 800) -- 298 + BigArrow = _with_0 -- 289 +end -- 289 +UnitAction:add("multiArrow", { -- 301 + priority = 3, -- 301 + reaction = 10, -- 302 + recovery = 0.1, -- 303 + queued = true, -- 304 + available = function(self) -- 305 + return true -- 305 + end, -- 305 + create = function(self) -- 306 + local attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor -- 307 + do -- 307 + local _obj_0 = self.entity -- 312 + attackSpeed, targetAllow, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.targetAllow, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 307 + end -- 312 + do -- 313 + local _with_0 = self.playable -- 313 + _with_0.speed = attackSpeed -- 314 + _with_0:play("range") -- 315 + _with_0:slot("AnimationEnd", rangeAttackEnd) -- 316 + end -- 313 + return once(function(self) -- 317 + local onAttack -- 318 + onAttack = function(angle, speed) -- 318 + BigArrow:setVelocity(angle, speed) -- 319 + local _with_0 = Bullet(BigArrow, self) -- 320 + if self.group == EnemyGroup then -- 321 + _with_0.color = Color(0xff666666) -- 321 + end -- 321 + _with_0.targetAllow = targetAllow -- 322 + _with_0:slot("HitTarget", function(bullet, target, pos) -- 323 + do -- 324 + local _with_1 = target.data -- 324 + _with_1.hitPoint = pos -- 325 + _with_1.hitPower = attackPower -- 326 + _with_1.hitFromRight = bullet.velocityX < 0 -- 327 + end -- 324 + local entity = target.entity -- 328 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 329 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 330 + entity.hp = entity.hp - damage -- 331 + bullet.hitStop = true -- 332 + end) -- 323 + _with_0:addTo(self.world, self.order) -- 333 + return _with_0 -- 320 + end -- 318 + sleep(30.0 / 60.0 / attackSpeed) -- 334 + Audio:play("Audio/d_att.wav") -- 335 + onAttack(30, 1100) -- 336 + onAttack(10, 1000) -- 337 + onAttack(-10, 900) -- 338 + onAttack(-30, 800) -- 339 + onAttack(-50, 700) -- 340 + while true do -- 341 + sleep() -- 341 + end -- 341 + end) -- 341 + end, -- 306 + stop = function(self) -- 342 + return self.playable:slot("AnimationEnd"):remove(rangeAttackEnd) -- 343 + end -- 342 +}) -- 300 +UnitAction:add("fallOff", { -- 346 + priority = 1, -- 346 + reaction = 1, -- 347 + recovery = 0, -- 348 + available = function(self) -- 349 + return not self.onSurface -- 349 + end, -- 349 + create = function(self) -- 350 + if self.velocityY <= 0 then -- 351 + self.data.fallDown = true -- 352 + do -- 353 + local _with_0 = self.playable -- 353 + _with_0.speed = 2.5 -- 354 + _with_0:play("idle") -- 355 + end -- 353 + else -- 356 + self.data.fallDown = false -- 356 + end -- 351 + return function(self, action) -- 357 + if self.onSurface then -- 358 + return true -- 358 + end -- 358 + if not self.data.fallDown and self.velocityY <= 0 then -- 359 + self.data.fallDown = true -- 360 + do -- 361 + local _with_0 = self.playable -- 361 + _with_0.speed = 2.5 -- 362 + _with_0:play("idle") -- 363 + end -- 361 + end -- 359 + return false -- 364 + end -- 364 + end -- 350 +}) -- 345 +UnitAction:add("evade", { -- 367 + priority = 10, -- 367 + reaction = 10, -- 368 + recovery = 0, -- 369 + queued = true, -- 370 + available = function(self) -- 371 + return true -- 371 + end, -- 371 + create = function(self) -- 372 + do -- 373 + local _with_0 = self.playable -- 373 + _with_0.speed = 1.0 -- 374 + _with_0.recovery = 0.0 -- 375 + _with_0:play("bevade") -- 376 + end -- 373 + return once(function(self) -- 377 + local group = self.group -- 378 + self.group = Data.groupHide -- 379 + local dir = self.faceRight and -1 or 1 -- 380 + cycle(0.1, function() -- 381 + self.velocityX = 400 * dir -- 381 + end) -- 381 + self.group = group -- 382 + sleep(0.1) -- 383 + do -- 384 + local _with_0 = self.playable -- 384 + _with_0.speed = 1.0 -- 385 + _with_0:play("idle") -- 386 + end -- 384 + sleep(0.3) -- 387 + return true -- 388 + end) -- 388 + end -- 372 +}) -- 366 +UnitAction:add("rush", { -- 391 + priority = 10, -- 391 + reaction = 10, -- 392 + recovery = 0, -- 393 + queued = true, -- 394 + available = function(self) -- 395 + return true -- 395 + end, -- 395 + create = function(self) -- 396 + do -- 397 + local _with_0 = self.playable -- 397 + _with_0.speed = 1.0 -- 398 + _with_0.recovery = 0.0 -- 399 + _with_0:play("fevade") -- 400 + end -- 397 + return once(function(self) -- 401 + local group = self.group -- 402 + self.group = Data.groupHide -- 403 + local dir = self.faceRight and 1 or -1 -- 404 + cycle(0.1, function() -- 405 + self.velocityX = 800 * dir -- 405 + end) -- 405 + self.group = group -- 406 + sleep(0.1) -- 407 + do -- 408 + local _with_0 = self.playable -- 408 + _with_0.speed = 1.0 -- 409 + _with_0:play("idle") -- 410 + end -- 408 + sleep(0.3) -- 411 + return true -- 412 + end) -- 412 + end -- 396 +}) -- 390 +local spearAttackEnd -- 414 +spearAttackEnd = function(name, playable) -- 414 + if name == "spear" then -- 415 + return playable.parent:stop() -- 415 + end -- 415 +end -- 414 +UnitAction:add("spearAttack", { -- 418 + priority = 3, -- 418 + reaction = 10, -- 419 + recovery = 0.1, -- 420 + queued = true, -- 421 + available = function(self) -- 422 + return true -- 422 + end, -- 422 + create = function(self) -- 423 + local attackSpeed, attackPower, damageType, attackBase, attackBonus, attackFactor -- 424 + do -- 424 + local _obj_0 = self.entity -- 428 + attackSpeed, attackPower, damageType, attackBase, attackBonus, attackFactor = _obj_0.attackSpeed, _obj_0.attackPower, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor -- 424 + end -- 428 + do -- 429 + local _with_0 = self.playable -- 429 + _with_0.speed = attackSpeed -- 430 + _with_0.recovery = 0.2 -- 431 + _with_0:play("spear") -- 432 + _with_0:slot("AnimationEnd", spearAttackEnd) -- 433 + end -- 429 + return once(function(self) -- 434 + sleep(50.0 / 60.0) -- 435 + Audio:play("Audio/f_att.wav") -- 436 + local dir = self.faceRight and 0 or -900 -- 437 + local origin = self.position - Vec2(0, 205) + Vec2(dir, 0) -- 438 + size = Size(900, 40) -- 439 + world:query(Rect(origin, size), function(body) -- 440 + local entity = body.entity -- 441 + if entity and Data:isEnemy(body, self) then -- 442 + do -- 443 + local _with_0 = body.data -- 443 + _with_0.hitPoint = body.position -- 444 + _with_0.hitPower = attackPower -- 445 + _with_0.hitFromRight = not self.faceRight -- 446 + end -- 443 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 447 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 448 + entity.hp = entity.hp - damage -- 449 + end -- 442 + return false -- 450 + end) -- 440 + while true do -- 451 + sleep() -- 451 + end -- 451 + end) -- 451 + end -- 423 +}) -- 417 +do -- 453 + local _with_0 = BulletDef() -- 453 + _with_0.tag = "" -- 454 + _with_0.endEffect = "" -- 455 + _with_0.lifeTime = 5 -- 456 + _with_0.damageRadius = 0 -- 457 + _with_0.highSpeedFix = false -- 458 + _with_0.gravity = Vec2(0, -10) -- 459 + _with_0.face = Face("Model/patreon.clip|item_arrow", Vec2.zero) -- 460 + _with_0:setAsCircle(10) -- 461 + _with_0:setVelocity(25, 800) -- 462 + Store["Bullet_Arrow"] = _with_0 -- 453 +end -- 453 +local GetBoss -- 464 +GetBoss = function(entity, pos, black) -- 464 + local unitDef -- 465 + do -- 465 + local _with_0 = Dictionary() -- 465 + _with_0.linearAcceleration = Vec2(0, -10) -- 466 + _with_0.bodyType = "Dynamic" -- 467 + _with_0.scale = 2 -- 468 + _with_0.density = 10.0 -- 469 + _with_0.friction = 1.0 -- 470 + _with_0.restitution = 0.0 -- 471 + _with_0.playable = "model:Model/bossp.model" -- 472 + _with_0.size = Size(150, 410) -- 473 + _with_0.tag = "Boss" -- 474 + _with_0.sensity = 0 -- 475 + _with_0.move = 100 -- 476 + _with_0.moveSpeed = 1.0 -- 477 + _with_0.jump = 600 -- 478 + _with_0.detectDistance = 1500 -- 479 + _with_0.hp = 30.0 -- 480 + _with_0.attackSpeed = 1.0 -- 481 + _with_0.attackBase = 2.5 -- 482 + _with_0.attackDelay = 50.0 / 60.0 -- 483 + _with_0.attackEffectDelay = 50.0 / 60.0 -- 484 + _with_0.attackBonus = 0.0 -- 485 + _with_0.attackFactor = 1.0 -- 486 + _with_0.attackRange = Size(780, 300) -- 487 + _with_0.attackPower = Vec2(200, 200) -- 488 + _with_0.attackTarget = "Multi" -- 489 + do -- 490 + local conf -- 491 + do -- 491 + local _with_1 = TargetAllow() -- 491 + _with_1.terrainAllowed = true -- 492 + _with_1:allow("Enemy", true) -- 493 + conf = _with_1 -- 491 + end -- 491 + _with_0.targetAllow = conf:toValue() -- 494 + end -- 494 + _with_0.damageType = elementTypes.Purple -- 495 + _with_0.defenceType = elementTypes.Purple -- 496 + _with_0.bulletType = "Bullet_Arrow" -- 497 + _with_0.attackEffect = "" -- 498 + _with_0.hitEffect = "Particle/bloodp.par" -- 499 + _with_0.sndAttack = "Audio/f_att.wav" -- 500 + _with_0.sndFallen = "" -- 501 + _with_0.decisionTree = "AI_Boss" -- 502 + _with_0.usePreciseHit = true -- 503 + _with_0.actions = Array({ -- 505 + "walk", -- 505 + "turn", -- 506 + "meleeAttack", -- 507 + "multiArrow", -- 508 + "spearAttack", -- 509 + "idle", -- 510 + "cancel", -- 511 + "jump", -- 512 + "fall", -- 513 + "fallOff" -- 514 + }) -- 504 + unitDef = _with_0 -- 465 + end -- 465 + for _index_0 = 1, #mutables do -- 516 + local var = mutables[_index_0] -- 516 + entity[var] = unitDef[var] -- 517 + end -- 517 + local _with_0 = Unit(unitDef, world, entity, pos) -- 518 + if black then -- 519 + for i = 1, 7 do -- 520 + do -- 521 + local node = _with_0.playable:getNodeByName("w" .. tostring(i)) -- 521 + if node then -- 521 + node.color = Color(0xff666666) -- 522 + end -- 521 + end -- 521 + end -- 522 + end -- 519 + do -- 523 + local node = _with_0.playable:getNodeByName("mask") -- 523 + if node then -- 523 + node:addChild(Sprite("Model/patreon.clip|" .. tostring(masks[math.random(1, #masks)]))) -- 524 + end -- 523 + end -- 523 + return _with_0 -- 518 +end -- 464 +local GetUnit -- 526 +GetUnit = function(entity, pos, black) -- 526 + local characterType = characterTypes[math.random(1, #characterTypes)] -- 527 + local characterColor = characterColors[math.random(1, #characterColors)] -- 528 + local character = { -- 530 + body = "character_" .. tostring(characterType) .. tostring(characterColor), -- 530 + lhand = "character_hand" .. tostring(characterColor), -- 531 + rhand = "character_hand" .. tostring(characterColor), -- 532 + mask = masks[math.random(1, #masks)] -- 533 + } -- 529 + local items = { -- 535 + head = headItems[math.random(1, #headItems)], -- 535 + lhand = lhandItems[math.random(1, #lhandItems)], -- 536 + rhand = rhandItems[math.random(1, #rhandItems)] -- 537 + } -- 534 + local attackRange = itemSettings[items.rhand].attackRange or Size(350, 150) -- 538 + local bonusPower = itemSettings[items.lhand].attackPower or Vec2.zero -- 539 + local attackPower = bonusPower + (itemSettings[items.rhand].attackPower or Vec2(100, 100)) -- 540 + local sndAttack = itemSettings[items.rhand].sndAttack or "" -- 541 + local skills = Set((function() -- 542 + local _accum_0 = { } -- 542 + local _len_0 = 1 -- 542 + for _, v in pairs(items) do -- 542 + do -- 543 + local skill = itemSettings[v].skill -- 543 + if skill then -- 543 + entity[tostring(skill) .. "Skill"] = true -- 544 + _accum_0[_len_0] = skill -- 545 + end -- 543 + end -- 543 + _len_0 = _len_0 + 1 -- 545 + end -- 545 + return _accum_0 -- 545 + end)()) -- 542 + local actions = Array({ -- 547 + "walk", -- 547 + "turn", -- 548 + "idle", -- 549 + "cancel", -- 550 + "hit", -- 551 + "fall", -- 552 + "fallOff" -- 553 + }) -- 546 + for k in pairs(skills) do -- 555 + actions:add(k) -- 556 + end -- 556 + local unitDef -- 557 + do -- 557 + local _with_0 = Dictionary() -- 557 + _with_0.linearAcceleration = Vec2(0, -10) -- 558 + _with_0.bodyType = "Dynamic" -- 559 + _with_0.scale = 1 -- 560 + _with_0.density = 1.0 -- 561 + _with_0.friction = 1.0 -- 562 + _with_0.restitution = 0.0 -- 563 + _with_0.playable = "model:Model/patreon.model" -- 564 + _with_0.size = Size(64, 128) -- 565 + _with_0.tag = "Fighter" -- 566 + _with_0.sensity = 0 -- 567 + _with_0.move = 250 -- 568 + _with_0.moveSpeed = 1.0 -- 569 + _with_0.jump = 700 -- 570 + _with_0.detectDistance = 800 -- 571 + _with_0.hp = 10.0 -- 572 + _with_0.attackSpeed = 1.0 -- 573 + _with_0.attackBase = 2.5 -- 574 + _with_0.attackDelay = 20.0 / 60.0 -- 575 + _with_0.attackEffectDelay = 20.0 / 60.0 -- 576 + _with_0.attackBonus = 0.0 -- 577 + _with_0.attackFactor = 1.0 -- 578 + _with_0.attackRange = attackRange -- 579 + _with_0.attackPower = attackPower -- 580 + _with_0.attackTarget = "Single" -- 581 + do -- 582 + local conf -- 583 + do -- 583 + local _with_1 = TargetAllow() -- 583 + _with_1.terrainAllowed = true -- 584 + _with_1:allow("Enemy", true) -- 585 + conf = _with_1 -- 583 + end -- 583 + _with_0.targetAllow = conf:toValue() -- 586 + end -- 586 + _with_0.damageType = elementTypes[characterColor] -- 587 + _with_0.defenceType = elementTypes[characterColor] -- 588 + _with_0.bulletType = "Bullet_Arrow" -- 589 + _with_0.attackEffect = "" -- 590 + _with_0.hitEffect = "Particle/bloodp.par" -- 591 + _with_0.name = "Fighter" -- 592 + _with_0.desc = "" -- 593 + _with_0.sndAttack = sndAttack -- 594 + _with_0.sndFallen = "" -- 595 + _with_0.decisionTree = "AI_Common" -- 596 + _with_0.usePreciseHit = true -- 597 + _with_0.actions = actions -- 598 + unitDef = _with_0 -- 557 + end -- 557 + for _index_0 = 1, #mutables do -- 599 + local var = mutables[_index_0] -- 599 + entity[var] = unitDef[var] -- 600 + end -- 600 + local _with_0 = Unit(unitDef, world, entity, pos) -- 601 + for _index_0 = 1, #itemSlots do -- 602 + local slot = itemSlots[_index_0] -- 602 + local node = _with_0.playable:getNodeByName(slot) -- 603 + do -- 604 + local item = character[slot] -- 604 + if item then -- 604 + node:addChild(Sprite("Model/patreon.clip|" .. tostring(item))) -- 605 + end -- 604 + end -- 604 + do -- 606 + local item = items[slot] -- 606 + if item then -- 606 + node:addChild((function() -- 607 + local _with_1 = Sprite("Model/patreon.clip|" .. tostring(item)) -- 607 + if black then -- 608 + _with_1.color = Color(0xff666666) -- 608 + end -- 608 + _with_1.position = itemSettings[item].offset -- 609 + return _with_1 -- 607 + end)()) -- 607 + end -- 606 + end -- 606 + end -- 609 + return _with_0 -- 601 +end -- 526 +Store["AI_Common"] = Sel({ -- 614 + Seq({ -- 615 + Con("is dead", function(self) -- 615 + return self.entity.hp <= 0 -- 615 + end), -- 615 + Accept() -- 616 + }), -- 614 + Seq({ -- 619 + Con("is falling", function(self) -- 619 + return not self.onSurface -- 619 + end), -- 619 + Act("fallOff") -- 620 + }), -- 618 + Seq({ -- 623 + Con("game paused", function() -- 623 + return GamePaused -- 623 + end), -- 623 + Act("idle") -- 624 + }), -- 622 + Seq({ -- 627 + Con("is not attacking", function(self) -- 627 + return not self:isDoing("melee") and not self:isDoing("range") -- 629 + end), -- 627 + Con("need attack", function(self) -- 630 + local attackUnits = AI:getUnitsInAttackRange() -- 631 + for _index_0 = 1, #attackUnits do -- 632 + local unit = attackUnits[_index_0] -- 632 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 633 + return true -- 635 + end -- 633 + end -- 635 + return false -- 636 + end), -- 630 + Sel({ -- 638 + Seq({ -- 639 + Con("attack", function(self) -- 639 + return App.rand % 10 == 0 -- 639 + end), -- 639 + Sel({ -- 641 + Act("meleeAttack"), -- 641 + Act("range") -- 642 + }) -- 640 + }), -- 638 + Act("idle") -- 645 + }) -- 637 + }), -- 626 + Seq({ -- 649 + Con("rush or evade", function(self) -- 649 + return not self:isDoing("rush") and not self:isDoing("evade") and App.rand % 300 == 0 -- 650 + end), -- 649 + Sel({ -- 652 + Seq({ -- 653 + Con("too far away", function(self) -- 653 + if self.entity.rushSkill then -- 654 + local units = AI:getDetectedUnits() -- 655 + for _index_0 = 1, #units do -- 656 + local unit = units[_index_0] -- 656 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight and self.position:distance(unit.position) > 300 then -- 657 + return true -- 659 + end -- 657 + end -- 659 + end -- 654 + return false -- 660 + end), -- 653 + Act("rush") -- 661 + }), -- 652 + Seq({ -- 664 + Con("too close", function(self) -- 664 + if self.entity.evadeSkill then -- 665 + local units = AI:getDetectedUnits() -- 666 + for _index_0 = 1, #units do -- 667 + local unit = units[_index_0] -- 667 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight and self.position:distance(unit.position) < 300 then -- 668 + return true -- 670 + end -- 668 + end -- 670 + end -- 665 + return false -- 671 + end), -- 664 + Act("evade") -- 672 + }) -- 663 + }) -- 651 + }), -- 648 + Seq({ -- 677 + Con("need turn", function(self) -- 677 + return (self.x < -750 and not self.faceRight) or (self.x > 750 and self.faceRight) -- 678 + end), -- 677 + Act("turn") -- 679 + }), -- 676 + Act("walk") -- 681 +}) -- 613 +Store["AI_Boss"] = Sel({ -- 685 + Seq({ -- 686 + Con("is dead", function(self) -- 686 + return self.entity.hp <= 0 -- 686 + end), -- 686 + Accept() -- 687 + }), -- 685 + Seq({ -- 690 + Con("is falling", function(self) -- 690 + return not self.onSurface -- 690 + end), -- 690 + Act("fallOff") -- 691 + }), -- 689 + Seq({ -- 694 + Con("game paused", function() -- 694 + return GamePaused -- 694 + end), -- 694 + Act("idle") -- 695 + }), -- 693 + Seq({ -- 698 + Con("is not attacking", function(self) -- 698 + return not self:isDoing("meleeAttack") and not self:isDoing("multiArrow") and not self:isDoing("spearAttack") -- 701 + end), -- 698 + Con("need attack", function(self) -- 702 + local attackUnits = AI:getUnitsInAttackRange() -- 703 + for _index_0 = 1, #attackUnits do -- 704 + local unit = attackUnits[_index_0] -- 704 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 705 + return true -- 707 + end -- 705 + end -- 707 + return false -- 708 + end), -- 702 + Sel({ -- 710 + Seq({ -- 711 + Con("melee attack", function(self) -- 711 + return App.rand % 40 == 0 -- 711 + end), -- 711 + Act("meleeAttack") -- 712 + }), -- 710 + Seq({ -- 715 + Con("multi Arrow", function(self) -- 715 + return App.rand % 40 == 0 -- 715 + end), -- 715 + Act("multiArrow") -- 716 + }), -- 714 + Seq({ -- 719 + Con("spear attack", function(self) -- 719 + return App.rand % 40 == 0 -- 719 + end), -- 719 + Act("spearAttack") -- 720 + }), -- 718 + Act("idle") -- 722 + }) -- 709 + }), -- 697 + Seq({ -- 726 + Con("need turn", function(self) -- 726 + return (self.x < -750 and not self.faceRight) or (self.x > 750 and self.faceRight) -- 727 + end), -- 726 + Act("turn") -- 728 + }), -- 725 + Act("walk") -- 730 +}) -- 684 +do -- 733 + local _with_0 = Observer("Add", { -- 733 + "position", -- 733 + "order", -- 733 + "group", -- 733 + "faceRight" -- 733 + }) -- 733 + _with_0:watch(function(self, position, order, group, faceRight) -- 734 + world = Store.world -- 735 + if group == PlayerGroup then -- 736 + self.player = true -- 736 + end -- 736 + if group == EnemyGroup then -- 737 + self.enemy = true -- 737 + end -- 737 + do -- 738 + local _with_1 -- 738 + if self.boss then -- 738 + _with_1 = GetBoss(self, position, group == EnemyGroup) -- 739 + else -- 741 + _with_1 = GetUnit(self, position, group == EnemyGroup) -- 741 + end -- 738 + _with_1.group = group -- 742 + _with_1.order = order -- 743 + _with_1.playable:runAction(Action(Scale(0.5, 0, self.unit.unitDef.scale, Ease.OutBack))) -- 744 + _with_1.faceRight = faceRight -- 745 + _with_1:addTo(world) -- 746 + end -- 738 + return false -- 746 + end) -- 734 +end -- 733 +do -- 748 + local _with_0 = Observer("Change", { -- 748 + "hp", -- 748 + "unit" -- 748 + }) -- 748 + _with_0:watch(function(self, hp, unit) -- 749 + local boss = self.boss -- 750 + local lastHp = self.oldValues.hp -- 751 + if hp < lastHp then -- 752 + if not boss and unit:isDoing("hit") then -- 753 + unit:start("cancel") -- 753 + end -- 753 + do -- 754 + local _with_1 = Label("sarasa-mono-sc-regular", 30) -- 754 + _with_1.order = PlayerLayer -- 755 + _with_1.color = Color(0xffff0000) -- 756 + _with_1.position = unit.position + Vec2(0, 40) -- 757 + _with_1.text = "-" .. tostring(lastHp - hp) -- 758 + _with_1:runAction(Action(Sequence(Y(0.5, _with_1.y, _with_1.y + 100), Opacity(0.2, 1, 0), Event("End")))) -- 759 + _with_1:slot("End", function() -- 764 + return _with_1:removeFromParent() -- 764 + end) -- 764 + _with_1:addTo(world) -- 765 + end -- 754 + if boss then -- 766 + do -- 767 + local _with_1 = Visual("Particle/bloodp.par") -- 767 + _with_1.position = unit.data.hitPoint -- 768 + _with_1:addTo(world, unit.order) -- 769 + _with_1:autoRemove() -- 770 + _with_1:start() -- 771 + end -- 767 + end -- 766 + if hp > 0 then -- 772 + unit:start("hit") -- 773 + else -- 775 + unit:start("cancel") -- 775 + unit:start("hit") -- 776 + unit:start("fall") -- 777 + unit.group = Data.groupHide -- 778 + unit:schedule(once(function(self) -- 779 + sleep(3) -- 780 + unit:removeFromParent() -- 781 + if not Group({ -- 782 + "unit" -- 782 + }):each(function(self) -- 782 + return self.group == PlayerGroup -- 782 + end) then -- 782 + return emit("Lost") -- 783 + elseif not Group({ -- 784 + "unit" -- 784 + }):each(function(self) -- 784 + return self.group == EnemyGroup -- 784 + end) then -- 784 + return emit("Win") -- 785 + end -- 782 + end)) -- 779 + end -- 772 + end -- 752 + return false -- 785 + end) -- 749 +end -- 748 +local WaitForSignal -- 787 +WaitForSignal = function(text, duration) -- 787 + local _with_0 = Label("sarasa-mono-sc-regular", 100) -- 788 + _with_0.color = themeColor -- 789 + _with_0.text = text -- 790 + _with_0:runAction(Spawn(Scale(0.5, 0.3, 1, Ease.OutBack), Opacity(0.3, 0, 1))) -- 791 + sleep(duration - 0.3) -- 795 + _with_0:runAction(Spawn(Scale(0.3, 1, 1.5, Ease.OutQuad), Opacity(0.3, 1, 0, Ease.OutQuad))) -- 796 + sleep(0.3) -- 800 + _with_0:removeFromParent() -- 801 + return _with_0 -- 788 +end -- 787 +local GameScore = 20 -- 803 +local uiScale = App.devicePixelRatio -- 805 +Director.ui:addChild((function() -- 806 + local _with_0 = AlignNode({ -- 806 + isRoot = true -- 806 + }) -- 806 + _with_0:addChild((function() -- 807 + local _with_1 = AlignNode() -- 807 + _with_1.size = Size(0, 0) -- 808 + _with_1.hAlign = "Left" -- 809 + _with_1.vAlign = "Top" -- 810 + _with_1.alignOffset = Vec2(360, 80) * (uiScale / 2) -- 811 + _with_1:gslot("AddScore", function(value) -- 812 + if value < 0 and GameScore == 0 then -- 813 + return -- 813 + end -- 813 + _with_1:addChild((function() -- 814 + local _with_2 = Label("sarasa-mono-sc-regular", 64) -- 814 + _with_2.color = themeColor -- 815 + _with_2.text = string.format(tostring(value > 0 and '+' or '') .. "%d", value) -- 816 + _with_2:runAction(Sequence(Spawn(Scale(0.5, 0.3, 1, Ease.OutBack), Opacity(0.5, 0, 1)), Delay(0.5), Spawn(Scale(0.3, 1, 1.5, Ease.OutQuad), Opacity(0.3, 1, 0, Ease.OutQuad)), Event("End"))) -- 817 + _with_2:slot("End", function() -- 829 + return _with_2:removeFromParent() -- 829 + end) -- 829 + return _with_2 -- 814 + end)()) -- 814 + GameScore = math.max(0, GameScore + value) -- 830 + if GameScore == 0 then -- 831 + return _with_1:schedule(once(function() -- 832 + Audio:play("Audio/game_over.wav") -- 833 + WaitForSignal("FOREVER LOST!", 3) -- 834 + return emit("GameLost") -- 835 + end)) -- 835 + end -- 831 + end) -- 812 + return _with_1 -- 807 + end)()) -- 807 + _with_0:addChild((function() -- 836 + local _with_1 = AlignNode() -- 836 + _with_1.size = Size(0, 0) -- 837 + _with_1.hAlign = "Center" -- 838 + _with_1.vAlign = "Center" -- 839 + _with_1.alignOffset = Vec2(0, -300 * (uiScale / 2)) -- 840 + _with_1:addChild((function() -- 841 + local _with_2 = CircleButton({ -- 842 + text = "STRIKE\nBACK", -- 842 + radius = 80, -- 843 + fontName = "sarasa-mono-sc-regular", -- 844 + fontSize = 48 -- 845 + }) -- 841 + _with_2.visible = false -- 847 + _with_2.touchEnabled = false -- 848 + _with_2:gslot("GameLost", function() -- 849 + _with_2.visible = true -- 850 + _with_2.touchEnabled = true -- 851 + end) -- 849 + _with_2:slot("Tapped", function() -- 852 + _with_2.touchEnabled = false -- 853 + Audio:play("Audio/v_att.wav") -- 854 + return _with_2:schedule(once(function() -- 855 + sleep(0.5) -- 856 + _with_2.visible = false -- 857 + emit("AddScore", 20) -- 858 + return emit("Start") -- 859 + end)) -- 859 + end) -- 852 + return _with_2 -- 841 + end)()) -- 841 + _with_1:addChild((function() -- 860 + local _with_2 = CircleButton({ -- 861 + text = "FIGHT", -- 861 + x = -200, -- 862 + radius = 80, -- 863 + fontName = "sarasa-mono-sc-regular", -- 864 + fontSize = 48 -- 865 + }) -- 860 + _with_2:slot("Tapped", function() -- 867 + if GameScore <= 0 then -- 868 + return -- 868 + end -- 868 + GamePaused = false -- 869 + return _with_2:schedule(once(function() -- 870 + emit("Fight") -- 871 + Audio:play("Audio/choose.wav") -- 872 + return WaitForSignal("FIGHT!", 1) -- 873 + end)) -- 873 + end) -- 867 + return _with_2 -- 860 + end)()) -- 860 + _with_1:addChild((function() -- 874 + local _with_2 = CircleButton({ -- 875 + text = "ANOTHER\nWAY", -- 875 + x = 200, -- 876 + radius = 80, -- 877 + fontName = "sarasa-mono-sc-regular", -- 878 + fontSize = 48 -- 879 + }) -- 874 + _with_2:slot("Tapped", function() -- 881 + Audio:play("Audio/switch.wav") -- 882 + emit("AddScore", -5) -- 883 + return emit("Start") -- 884 + end) -- 881 + return _with_2 -- 874 + end)()) -- 874 + _with_1:gslot("Lost", function() -- 885 + return _with_1:schedule(once(function() -- 886 + emit("AddScore", -(10 + math.floor(GameScore / 20) * 5)) -- 887 + if GameScore == 0 then -- 888 + return -- 888 + end -- 888 + Audio:play("Audio/hero_fall.wav") -- 889 + WaitForSignal("LOST!", 1.5) -- 890 + return emit("Start") -- 891 + end)) -- 891 + end) -- 885 + _with_1:gslot("Win", function() -- 892 + return _with_1:schedule(once(function() -- 893 + local score = 5 * Group({ -- 894 + "player" -- 894 + }).count -- 894 + emit("AddScore", score) -- 895 + Audio:play("Audio/hero_win.wav") -- 896 + WaitForSignal("WIN!", 1.5) -- 897 + return emit("Start") -- 898 + end)) -- 898 + end) -- 892 + _with_1:gslot("Wasted", function() -- 899 + _with_1:eachChild(function(self) -- 900 + self.visible = false -- 901 + self.touchEnabled = false -- 902 + end) -- 900 + return emit("AddScore", -20) -- 903 + end) -- 899 + _with_1:gslot("Fight", function() -- 904 + _with_1:eachChild(function(self) -- 905 + self.visible = false -- 906 + self.touchEnabled = false -- 907 + end) -- 905 + return _with_1:unschedule() -- 908 + end) -- 904 + _with_1:gslot("Start", function() -- 909 + if GameScore == 0 then -- 910 + return -- 910 + end -- 910 + GamePaused = true -- 911 + _with_1:eachChild(function(self) -- 912 + if self.text ~= "STRIKE\nBACK" then -- 913 + self.touchEnabled = true -- 914 + self.visible = true -- 915 + end -- 913 + end) -- 912 + Group({ -- 916 + "unit" -- 916 + }):each(function(self) -- 916 + return self.unit:removeFromParent() -- 916 + end) -- 916 + local unitCount -- 917 + if GameScore < 40 then -- 917 + unitCount = 1 + math.min(2, math.floor(math.max(0, GameScore - 20) / 5)) -- 918 + else -- 920 + unitCount = 3 + math.min(3, math.floor(GameScore / 35)) -- 920 + end -- 917 + if math.random(1, 100) == 1 then -- 921 + Entity({ -- 923 + position = Vec2(-200, 100), -- 923 + order = PlayerLayer, -- 924 + group = PlayerGroup, -- 925 + boss = true, -- 926 + faceRight = true -- 927 + }) -- 922 + else -- 929 + for i = 1, unitCount do -- 929 + Entity({ -- 931 + position = Vec2(-100 * i, 100), -- 931 + order = PlayerLayer, -- 932 + group = PlayerGroup, -- 933 + faceRight = true -- 934 + }) -- 930 + end -- 934 + end -- 921 + if math.random(1, 100) == 1 then -- 935 + Entity({ -- 937 + position = Vec2(200, 100), -- 937 + order = EnemyLayer, -- 938 + group = EnemyGroup, -- 939 + boss = true, -- 940 + faceRight = false -- 941 + }) -- 936 + else -- 943 + for i = 1, unitCount do -- 943 + Entity({ -- 945 + position = Vec2(100 * i, 100), -- 945 + order = EnemyLayer, -- 946 + group = EnemyGroup, -- 947 + faceRight = false -- 948 + }) -- 944 + end -- 948 + end -- 935 + return _with_1:schedule(once(function() -- 949 + local time = 2 -- 950 + cycle(time, function(dt) -- 951 + local width, height -- 952 + do -- 952 + local _obj_0 = App.visualSize -- 952 + width, height = _obj_0.width, _obj_0.height -- 952 + end -- 952 + SetNextWindowPos(Vec2(width / 2 - 150, height / 2 + 30)) -- 953 + SetNextWindowSize(Vec2(300, 50), "FirstUseEver") -- 954 + return Begin("CountDown", { -- 955 + "NoResize", -- 955 + "NoSavedSettings", -- 955 + "NoTitleBar", -- 955 + "NoMove" -- 955 + }, function() -- 955 + return ProgressBar(1.0 - dt, Vec2(-1, 30), string.format("%.2fs", (1 - dt) * time)) -- 956 + end) -- 956 + end) -- 951 + emit("Wasted") -- 957 + if GameScore == 0 then -- 958 + return -- 958 + end -- 958 + Audio:play("Audio/choose.wav") -- 959 + WaitForSignal("WASTED!", 1.5) -- 960 + return emit("Start") -- 961 + end)) -- 961 + end) -- 909 + _with_1:addChild((function() -- 962 + local _with_2 = Node() -- 962 + _with_2:schedule(function() -- 963 + local width, height -- 964 + do -- 964 + local _obj_0 = App.visualSize -- 964 + width, height = _obj_0.width, _obj_0.height -- 964 + end -- 964 + SetNextWindowPos(Vec2(20, 20)) -- 965 + SetNextWindowSize(Vec2(120, 280), "FirstUseEver") -- 966 + return PushStyleVar("ItemSpacing", Vec2.zero, function() -- 967 + return Begin("Stats", { -- 968 + "NoResize", -- 968 + "NoSavedSettings", -- 968 + "NoTitleBar", -- 968 + "NoMove" -- 968 + }, function() -- 968 + Text("VALUE: " .. tostring(GameScore)) -- 969 + Image("Model/patreon.clip|character_handGreen", Vec2(30, 30)) -- 970 + SameLine() -- 971 + Text("->") -- 972 + SameLine() -- 973 + Image("Model/patreon.clip|character_handRed", Vec2(30, 30)) -- 974 + SameLine() -- 975 + Text("x3") -- 976 + Image("Model/patreon.clip|character_handRed", Vec2(30, 30)) -- 977 + SameLine() -- 978 + Text("->") -- 979 + SameLine() -- 980 + Image("Model/patreon.clip|character_handYellow", Vec2(30, 30)) -- 981 + SameLine() -- 982 + Text("x3") -- 983 + Image("Model/patreon.clip|character_handYellow", Vec2(30, 30)) -- 984 + SameLine() -- 985 + Text("->") -- 986 + SameLine() -- 987 + Image("Model/patreon.clip|character_handGreen", Vec2(30, 30)) -- 988 + SameLine() -- 989 + Text("x3") -- 990 + Image("Model/patreon.clip|item_bow", Vec2(30, 30)) -- 991 + SameLine() -- 992 + Text(">") -- 993 + SameLine() -- 994 + Image("Model/patreon.clip|item_sword", Vec2(30, 30)) -- 995 + Image("Model/patreon.clip|item_hatTop", Vec2(30, 30)) -- 996 + SameLine() -- 997 + Text("dodge") -- 998 + Image("Model/patreon.clip|item_helmet", Vec2(30, 30)) -- 999 + SameLine() -- 1000 + Text("rush") -- 1001 + Image("Model/patreon.clip|item_rod", Vec2(30, 30)) -- 1002 + SameLine() -- 1003 + Text("knock") -- 1004 + Image("Model/patreon.clip|tile_heart", Vec2(30, 30)) -- 1005 + SameLine() -- 1006 + return Text("bash") -- 1007 + end) -- 1007 + end) -- 1007 + end) -- 963 + return _with_2 -- 962 + end)()) -- 962 + return _with_1 -- 836 + end)()) -- 836 + return _with_0 -- 806 +end)()) -- 806 +return emit("Start") -- 1009 diff --git a/Assets/Script/Game/Loli War/AI.lua b/Assets/Script/Game/Loli War/AI.lua new file mode 100644 index 000000000..16a9b7ebb --- /dev/null +++ b/Assets/Script/Game/Loli War/AI.lua @@ -0,0 +1,306 @@ +-- [yue]: Script/Game/Loli War/AI.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Group = dora.Group -- 1 +local _module_1 = dora.Platformer.Decision -- 1 +local Seq = _module_1.Seq -- 1 +local Con = _module_1.Con -- 1 +local Sel = _module_1.Sel -- 1 +local Act = _module_1.Act -- 1 +local Accept = _module_1.Accept -- 1 +local Reject = _module_1.Reject -- 1 +local AI = _module_1.AI -- 1 +local App = dora.App -- 1 +local math = _G.math -- 1 +local Store = Data.store -- 6 +local MaxBunnies, GroupPlayer, GroupEnemyPoke = Store.MaxBunnies, Store.GroupPlayer, Store.GroupEnemyPoke -- 7 +local heroes = Group({ -- 13 + "hero" -- 13 +}) -- 13 +local gameEndWait = Seq({ -- 16 + Con("game end", function(self) -- 16 + return (Store.winner ~= nil) -- 16 + end), -- 16 + Sel({ -- 18 + Seq({ -- 19 + Con("need wait", function(self) -- 19 + return self.onSurface and not self:isDoing("wait") -- 19 + end), -- 19 + Act("cancel"), -- 20 + Act("wait") -- 21 + }), -- 18 + Accept() -- 23 + }) -- 17 +}) -- 15 +Store["PlayerControlAI"] = Sel({ -- 28 + Seq({ -- 29 + Con("is dead", function(self) -- 29 + return self.entity.hp <= 0 -- 29 + end), -- 29 + Accept() -- 30 + }), -- 28 + Seq({ -- 33 + Con("pushing switch", function(self) -- 33 + return self:isDoing("pushSwitch") -- 33 + end), -- 33 + Accept() -- 34 + }), -- 32 + Seq({ -- 37 + Seq({ -- 38 + Con("move key down", function(self) -- 38 + return not (self.data.keyLeft and self.data.keyRight) and ((self.data.keyLeft and self.faceRight) or (self.data.keyRight and not self.faceRight)) -- 43 + end), -- 38 + Act("turn") -- 44 + }), -- 37 + Reject() -- 46 + }), -- 36 + Seq({ -- 49 + Con("attack key down", function(self) -- 49 + return Store.winner == nil and self.data.keyF -- 49 + end), -- 49 + Sel({ -- 51 + Seq({ -- 52 + Con("at switch", function(self) -- 52 + local theSwitch = self.data.atSwitch -- 53 + return (theSwitch ~= nil) and not theSwitch.data.pushed and ((self.x < theSwitch.x) == self.faceRight) -- 55 + end), -- 52 + Act("pushSwitch") -- 56 + }), -- 51 + Act("villyAttack"), -- 58 + Act("meleeAttack"), -- 59 + Act("rangeAttack") -- 60 + }) -- 50 + }), -- 48 + Sel({ -- 64 + Seq({ -- 65 + Con("is falling", function(self) -- 65 + return not self.onSurface -- 65 + end), -- 65 + Act("fallOff") -- 66 + }), -- 64 + Seq({ -- 69 + Con("jump key down", function(self) -- 69 + return self.data.keyUp -- 69 + end), -- 69 + Act("jump") -- 70 + }) -- 68 + }), -- 63 + Seq({ -- 74 + Con("move key down", function(self) -- 74 + return self.data.keyLeft or self.data.keyRight -- 74 + end), -- 74 + Act("walk") -- 75 + }), -- 73 + Act("idle") -- 77 +}) -- 27 +Store["HeroAI"] = Sel({ -- 81 + Seq({ -- 82 + Con("is dead", function(self) -- 82 + return self.entity.hp <= 0 -- 82 + end), -- 82 + Accept() -- 83 + }), -- 81 + Seq({ -- 86 + Con("is falling", function(self) -- 86 + return not self.onSurface -- 86 + end), -- 86 + Act("fallOff") -- 87 + }), -- 85 + gameEndWait, -- 89 + Seq({ -- 91 + Con("need attack", function(self) -- 91 + local attackUnits = AI:getUnitsInAttackRange() -- 92 + for _index_0 = 1, #attackUnits do -- 93 + local unit = attackUnits[_index_0] -- 93 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 94 + return true -- 96 + end -- 94 + end -- 96 + return false -- 97 + end), -- 91 + Sel({ -- 99 + Act("villyAttack"), -- 99 + Act("rangeAttack"), -- 100 + Act("meleeAttack") -- 101 + }) -- 98 + }), -- 90 + Seq({ -- 105 + Con("not facing enemy", function(self) -- 105 + return heroes:each(function(hero) -- 105 + local unit = hero.unit -- 106 + if Data:isEnemy(unit, self) then -- 107 + if (self.x > unit.x) == self.faceRight then -- 108 + return true -- 109 + end -- 108 + end -- 107 + end) -- 109 + end), -- 105 + Act("turn") -- 110 + }), -- 104 + Seq({ -- 113 + Con("need turn", function(self) -- 113 + return (self.x < 100 and not self.faceRight) or (self.x > 3990 and self.faceRight) -- 114 + end), -- 113 + Act("turn") -- 115 + }), -- 112 + Seq({ -- 118 + Con("wanna jump", function(self) -- 118 + return App.rand % 20 == 0 -- 118 + end), -- 118 + Act("jump") -- 119 + }), -- 117 + Seq({ -- 122 + Con("is at enemy side", function(self) -- 122 + return heroes:each(function(hero) -- 122 + local unit = hero.unit -- 123 + if Data:isEnemy(unit, self) then -- 124 + if math.abs(self.x - unit.x) < 50 then -- 125 + return true -- 126 + end -- 125 + end -- 124 + end) -- 126 + end), -- 122 + Act("idle") -- 127 + }), -- 121 + Act("walk") -- 129 +}) -- 80 +Store["BunnyForwardReturnAI"] = Sel({ -- 133 + Seq({ -- 134 + Con("is dead", function(self) -- 134 + return self.entity.hp <= 0 -- 134 + end), -- 134 + Accept() -- 135 + }), -- 133 + Seq({ -- 138 + Con("is falling", function(self) -- 138 + return not self.onSurface -- 138 + end), -- 138 + Act("fallOff") -- 139 + }), -- 137 + gameEndWait, -- 141 + Seq({ -- 143 + Con("need attack", function(self) -- 143 + local attackUnits = AI:getUnitsInAttackRange() -- 144 + for _index_0 = 1, #attackUnits do -- 145 + local unit = attackUnits[_index_0] -- 145 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 146 + return App.rand % 5 ~= 0 -- 148 + end -- 146 + end -- 148 + return false -- 149 + end), -- 143 + Act("meleeAttack") -- 150 + }), -- 142 + Seq({ -- 153 + Con("need turn", function(self) -- 153 + return (self.x < 100 and not self.faceRight) or (self.x > 3990 and self.faceRight) -- 154 + end), -- 153 + Act("turn") -- 155 + }), -- 152 + Act("walk") -- 157 +}) -- 132 +Store["SwitchAI"] = Sel({ -- 161 + Seq({ -- 162 + Con("is pushed", function(self) -- 162 + return self.data.pushed -- 162 + end), -- 162 + Act("pushed") -- 163 + }), -- 161 + Act("waitUser") -- 165 +}) -- 160 +local switches = Group({ -- 168 + "switch" -- 168 +}) -- 168 +local turnToSwitch = Seq({ -- 170 + Con("go to switch", function(self) -- 170 + return switches:each(function(item) -- 170 + if item.group == self.group and self.entity and self.entity.targetSwitch == item.switch then -- 171 + return (self.x > item.unit.x) == self.faceRight -- 172 + end -- 171 + end) -- 172 + end), -- 170 + Act("turn"), -- 173 + Reject() -- 174 +}) -- 169 +Store["BunnySwitcherAI"] = Sel({ -- 177 + Seq({ -- 178 + Con("is dead", function(self) -- 178 + return self.entity.hp <= 0 -- 178 + end), -- 178 + Accept() -- 179 + }), -- 177 + Seq({ -- 182 + Con("is falling", function(self) -- 182 + return not self.onSurface -- 182 + end), -- 182 + Act("fallOff") -- 183 + }), -- 181 + gameEndWait, -- 185 + Seq({ -- 187 + Con("need attack", function(self) -- 187 + local attackUnits = AI:getUnitsInAttackRange() -- 188 + for _index_0 = 1, #attackUnits do -- 189 + local unit = attackUnits[_index_0] -- 189 + if Data:isEnemy(self, unit) and (self.x < unit.x) == self.faceRight then -- 190 + return App.rand % 5 ~= 0 -- 192 + end -- 190 + end -- 192 + return false -- 193 + end), -- 187 + Act("meleeAttack") -- 194 + }), -- 186 + Seq({ -- 197 + Con("at switch", function(self) -- 197 + local _with_0 = self.data -- 197 + return (_with_0.atSwitch ~= nil) and _with_0.atSwitch.entity.switch == self.entity.targetSwitch -- 198 + end), -- 197 + Sel({ -- 200 + Seq({ -- 201 + Con("switch available", function(self) -- 201 + return heroes:each(function(hero) -- 201 + if self.group ~= hero.group then -- 202 + return false -- 202 + end -- 202 + local needEP, available -- 203 + do -- 203 + local _exp_0 = self.entity.targetSwitch -- 203 + if "Switch" == _exp_0 then -- 204 + local bunnyCount = 0 -- 205 + Group({ -- 206 + "bunny" -- 206 + }):each(function(bunny) -- 206 + if bunny.group == self.group then -- 207 + bunnyCount = bunnyCount + 1 -- 207 + end -- 207 + end) -- 206 + needEP, available = 1, bunnyCount < MaxBunnies -- 208 + elseif "SwitchG" == _exp_0 then -- 209 + needEP, available = 2, hero.defending -- 210 + end -- 210 + end -- 210 + if hero.ep >= needEP and available then -- 211 + if not self.data.atSwitch:isDoing("pushed") then -- 212 + if self.entity.targetSwitch == "SwitchG" then -- 213 + hero.defending = false -- 213 + end -- 213 + return true -- 214 + end -- 212 + end -- 211 + return false -- 215 + end) -- 215 + end), -- 201 + Act("pushSwitch") -- 216 + }), -- 200 + turnToSwitch, -- 218 + Act("idle") -- 219 + }) -- 199 + }), -- 196 + Seq({ -- 223 + Con("need turn", function(self) -- 223 + return (self.x < 100 and not self.faceRight) or (self.x > 3990 and self.faceRight) -- 224 + end), -- 223 + Act("turn") -- 225 + }), -- 222 + turnToSwitch, -- 227 + Act("walk") -- 228 +}) -- 176 diff --git a/Assets/Script/Game/Loli War/Action.lua b/Assets/Script/Game/Loli War/Action.lua new file mode 100644 index 000000000..18632449d --- /dev/null +++ b/Assets/Script/Game/Loli War/Action.lua @@ -0,0 +1,297 @@ +-- [yue]: Script/Game/Loli War/Action.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local UnitAction = _module_0.UnitAction -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local Audio = dora.Audio -- 1 +local Group = dora.Group -- 1 +local Entity = dora.Entity -- 1 +local Vec2 = dora.Vec2 -- 1 +local emit = dora.emit -- 1 +local Bullet = _module_0.Bullet -- 1 +local Store = Data.store -- 3 +local LayerBunny, LayerBlock, GroupPlayerPoke, GroupEnemyPoke, GroupPlayer, GroupEnemy, MaxBunnies = Store.LayerBunny, Store.LayerBlock, Store.GroupPlayerPoke, Store.GroupEnemyPoke, Store.GroupPlayer, Store.GroupEnemy, Store.MaxBunnies -- 4 +UnitAction:add("fallOff", { -- 15 + priority = 1, -- 15 + reaction = 0.1, -- 16 + recovery = 0, -- 17 + available = function(self) -- 18 + return not self.onSurface -- 18 + end, -- 18 + create = function(self) -- 19 + do -- 20 + local _with_0 = self.playable -- 20 + _with_0.speed = 1.5 -- 21 + _with_0:play("jump", true) -- 22 + end -- 20 + return function(self) -- 23 + return self.onSurface -- 23 + end -- 23 + end -- 19 +}) -- 14 +local pushSwitchEnd -- 25 +pushSwitchEnd = function(name, playable) -- 25 + if "switch" == name or "attack" == name then -- 26 + return playable.parent:stop() -- 27 + end -- 27 +end -- 25 +UnitAction:add("pushSwitch", { -- 30 + priority = 4, -- 30 + reaction = 3, -- 31 + recovery = 0.2, -- 32 + queued = true, -- 33 + available = function(self) -- 34 + return self.onSurface -- 34 + end, -- 34 + create = function(self) -- 35 + do -- 36 + local _with_0 = self.playable -- 36 + _with_0.speed = 1.5 -- 37 + _with_0.look = "noweapon" -- 38 + _with_0:play("switch") -- 39 + if not _with_0.playing then -- 40 + _with_0:play("attack") -- 41 + end -- 40 + _with_0:slot("AnimationEnd", pushSwitchEnd) -- 42 + end -- 36 + do -- 43 + local _with_0 = self.data.atSwitch -- 43 + _with_0.data.pushed = true -- 44 + _with_0.data.fromRight = self.x > _with_0.x -- 45 + end -- 43 + return function() -- 46 + return false -- 46 + end -- 46 + end, -- 35 + stop = function(self) -- 47 + return self.playable:slot("AnimationEnd"):remove(pushSwitchEnd) -- 48 + end -- 47 +}) -- 29 +UnitAction:add("waitUser", { -- 51 + priority = 1, -- 51 + reaction = 0.1, -- 52 + recovery = 0.2, -- 53 + available = function() -- 54 + return true -- 54 + end, -- 54 + create = function(self) -- 55 + do -- 56 + local _with_0 = self.playable -- 56 + _with_0.speed = 1 -- 57 + _with_0:play("idle", true) -- 58 + end -- 56 + return function() -- 59 + return false -- 59 + end -- 59 + end -- 55 +}) -- 50 +local switchPushed -- 61 +switchPushed = function(name, playable) -- 61 + if "pushRight" == name or "pushLeft" == name then -- 62 + return playable.parent:stop() -- 63 + end -- 63 +end -- 61 +UnitAction:add("pushed", { -- 66 + priority = 2, -- 66 + reaction = 0.1, -- 67 + recovery = 0.2, -- 68 + queued = true, -- 69 + available = function() -- 70 + return true -- 70 + end, -- 70 + create = function(self) -- 71 + do -- 72 + local _with_0 = self.playable -- 72 + _with_0.recovery = 0.2 -- 73 + _with_0.speed = 1.5 -- 74 + _with_0:play(self.data.fromRight and "pushLeft" or "pushRight") -- 75 + _with_0:slot("AnimationEnd", switchPushed) -- 76 + end -- 72 + return once(function(self) -- 77 + sleep(0.5) -- 78 + Audio:play("Audio/switch.wav") -- 79 + local heroes = Group({ -- 80 + "hero" -- 80 + }) -- 80 + do -- 81 + local _exp_0 = self.entity.switch -- 81 + if "Switch" == _exp_0 then -- 82 + heroes:each(function(hero) -- 83 + if self.group == hero.group and hero.ep >= 1 then -- 84 + Entity((function() -- 85 + local _with_0 = { } -- 85 + do -- 86 + local _exp_1 = self.group -- 86 + if GroupPlayer == _exp_1 then -- 87 + _with_0.bunny, _with_0.group, _with_0.faceRight, _with_0.position = "BunnyG", GroupPlayer, true, Vec2(1000, 1004 - 500) -- 88 + elseif GroupEnemy == _exp_1 then -- 89 + _with_0.bunny, _with_0.group, _with_0.faceRight, _with_0.position = "BunnyP", GroupEnemy, false, Vec2(3130, 1004 - 500) -- 90 + end -- 90 + end -- 90 + _with_0.AI = "BunnyForwardReturnAI" -- 91 + _with_0.layer = LayerBunny -- 92 + local bunnyCount = 0 -- 93 + Group({ -- 94 + "bunny" -- 94 + }):each(function(bunny) -- 94 + if bunny.group == self.group then -- 95 + bunnyCount = bunnyCount + 1 -- 95 + end -- 95 + end) -- 94 + if bunnyCount > MaxBunnies then -- 96 + _with_0.hp = 0.0 -- 96 + end -- 96 + return _with_0 -- 85 + end)()) -- 85 + emit("EPChange", self.group, -1) -- 97 + return true -- 98 + end -- 84 + end) -- 83 + elseif "SwitchG" == _exp_0 then -- 99 + heroes:each(function(hero) -- 100 + if self.group == hero.group and hero.ep >= 2 then -- 101 + Entity((function() -- 102 + local _with_0 = { } -- 102 + _with_0.layer = LayerBlock -- 103 + do -- 104 + local _exp_1 = self.group -- 104 + if GroupPlayer == _exp_1 then -- 105 + _with_0.poke, _with_0.group, _with_0.position = "pokeb", GroupPlayerPoke, Vec2(192, 1004 - 512) -- 106 + elseif GroupEnemy == _exp_1 then -- 107 + _with_0.poke, _with_0.group, _with_0.position = "pokep", GroupEnemyPoke, Vec2(3904, 1004 - 512) -- 108 + end -- 108 + end -- 108 + return _with_0 -- 102 + end)()) -- 102 + emit("EPChange", self.group, -2) -- 109 + return true -- 110 + end -- 101 + end) -- 100 + end -- 110 + end -- 110 + while true do -- 111 + sleep() -- 112 + end -- 112 + end) -- 112 + end, -- 71 + stop = function(self) -- 113 + self.data.pushed = false -- 114 + return self.playable:slot("AnimationEnd"):remove(pushSwitchEnd) -- 115 + end -- 113 +}) -- 65 +local strikeEnd -- 117 +strikeEnd = function(name, playable) -- 117 + if name == "hit" then -- 118 + return playable.parent:stop() -- 118 + end -- 118 +end -- 117 +UnitAction:add("strike", { -- 121 + priority = 4, -- 121 + reaction = 3, -- 122 + recovery = 0, -- 123 + queued = true, -- 124 + available = function(self) -- 125 + return true -- 125 + end, -- 125 + create = function(self) -- 126 + do -- 127 + local _with_0 = self.playable -- 127 + _with_0.speed = 1 -- 128 + _with_0.look = "fail" -- 129 + _with_0:play("hit") -- 130 + _with_0:slot("AnimationEnd", strikeEnd) -- 131 + end -- 127 + Audio:play("Audio/hit.wav") -- 132 + return function() -- 133 + return false -- 133 + end -- 133 + end, -- 126 + stop = function(self) -- 134 + return self.playable:slot("AnimationEnd"):remove(strikeEnd) -- 135 + end -- 134 +}) -- 120 +local villyAttackEnd -- 137 +villyAttackEnd = function(name, playable) -- 137 + if name == "attack" then -- 138 + return playable.parent:stop() -- 138 + end -- 138 +end -- 137 +UnitAction:add("villyAttack", { -- 141 + priority = 3, -- 141 + reaction = 10, -- 142 + recovery = 0.1, -- 143 + queued = true, -- 144 + available = function(self) -- 145 + return true -- 145 + end, -- 145 + create = function(self) -- 146 + local attackSpeed, targetAllow, damageType, attackBase, attackBonus, attackFactor, attackPower -- 147 + do -- 147 + local _obj_0 = self.entity -- 155 + attackSpeed, targetAllow, damageType, attackBase, attackBonus, attackFactor, attackPower = _obj_0.attackSpeed, _obj_0.targetAllow, _obj_0.damageType, _obj_0.attackBase, _obj_0.attackBonus, _obj_0.attackFactor, _obj_0.attackPower -- 147 + end -- 155 + do -- 156 + local _with_0 = self.playable -- 156 + _with_0.speed = attackSpeed -- 157 + _with_0.look = "fight" -- 158 + _with_0:play("attack") -- 159 + _with_0:slot("AnimationEnd", villyAttackEnd) -- 160 + end -- 156 + return once(function(self) -- 161 + local bulletDef = Store[self.unitDef.bulletType] -- 162 + local onAttack -- 163 + onAttack = function() -- 163 + Audio:play("Audio/v_att.wav") -- 164 + local _with_0 = Bullet(bulletDef, self) -- 165 + _with_0.targetAllow = targetAllow -- 166 + _with_0:slot("HitTarget", function(bullet, target, pos) -- 167 + do -- 168 + local _with_1 = target.data -- 168 + _with_1.hitPoint = pos -- 169 + _with_1.hitPower = attackPower -- 170 + _with_1.hitFromRight = bullet.velocityX < 0 -- 171 + end -- 168 + local entity = target.entity -- 172 + local factor = Data:getDamageFactor(damageType, entity.defenceType) -- 173 + local damage = (attackBase + attackBonus) * (attackFactor + factor) -- 174 + entity.hp = entity.hp - damage -- 175 + bullet.hitStop = true -- 176 + end) -- 167 + _with_0:addTo(self.world, self.order) -- 177 + return _with_0 -- 165 + end -- 163 + sleep(0.17 / attackSpeed) -- 178 + onAttack() -- 179 + sleep(0.63 / attackSpeed) -- 180 + onAttack() -- 181 + sleep(1.0) -- 182 + return true -- 183 + end) -- 183 + end, -- 146 + stop = function(self) -- 184 + return self.playable:slot("AnimationEnd"):remove(villyAttackEnd) -- 185 + end -- 184 +}) -- 140 +return UnitAction:add("wait", { -- 188 + priority = 1, -- 188 + reaction = 0.1, -- 189 + recovery = 0, -- 190 + available = function(self) -- 191 + return self.onSurface -- 191 + end, -- 191 + create = function(self) -- 192 + do -- 193 + local _with_0 = self.playable -- 193 + _with_0.speed = 1 -- 194 + _with_0.look = Store.winner == self.group and "happy" or "fail" -- 195 + _with_0:play("idle", true) -- 196 + end -- 193 + return function(self) -- 197 + if not self.onSurface then -- 198 + return true -- 199 + end -- 198 + return false -- 200 + end -- 200 + end -- 192 +}) -- 200 diff --git a/Assets/Script/Game/Loli War/Bullet.lua b/Assets/Script/Game/Loli War/Bullet.lua new file mode 100644 index 000000000..00421625d --- /dev/null +++ b/Assets/Script/Game/Loli War/Bullet.lua @@ -0,0 +1,35 @@ +-- [yue]: Script/Game/Loli War/Bullet.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local BulletDef = _module_0.BulletDef -- 1 +local Vec2 = dora.Vec2 -- 1 +local Face = _module_0.Face -- 1 +local Rectangle = require("UI.View.Shape.Rectangle") -- 2 +local Star = require("UI.View.Shape.Star") -- 3 +local Store = Data.store -- 5 +do -- 7 + local _with_0 = BulletDef() -- 7 + _with_0.tag = "" -- 8 + _with_0.endEffect = "" -- 9 + _with_0.lifeTime = 1 -- 10 + _with_0.damageRadius = 0 -- 11 + _with_0.highSpeedFix = false -- 12 + _with_0.gravity = Vec2(0, -10) -- 13 + _with_0.face = Face("Model/misc.clip|arrow", Vec2(-20, 0), 2) -- 14 + _with_0:setAsCircle(10) -- 15 + _with_0:setVelocity(15, 1200) -- 16 + Store["Arrow"] = _with_0 -- 7 +end -- 7 +do -- 18 + local _with_0 = BulletDef() -- 18 + _with_0.tag = "" -- 19 + _with_0.endEffect = "" -- 20 + _with_0.lifeTime = 3.0 -- 21 + _with_0.damageRadius = 0 -- 22 + _with_0.highSpeedFix = false -- 23 + _with_0.gravity = Vec2(0, 4) -- 24 + _with_0.face = Face("Model/misc.clip|heartbullet", Vec2.zero, 2) -- 25 + _with_0:setAsCircle(15) -- 26 + _with_0:setVelocity(0, 400) -- 27 + Store["Bubble"] = _with_0 -- 18 +end -- 18 diff --git a/Assets/Script/Game/Loli War/Constant.lua b/Assets/Script/Game/Loli War/Constant.lua new file mode 100644 index 000000000..7c82b66fe --- /dev/null +++ b/Assets/Script/Game/Loli War/Constant.lua @@ -0,0 +1,47 @@ +-- [yue]: Script/Game/Loli War/Constant.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local _with_0 = Data.store -- 3 +_with_0.GroupPlayer = 1 -- 4 +_with_0.GroupPlayerBlock = 2 -- 5 +_with_0.GroupPlayerPoke = 3 -- 6 +_with_0.GroupEnemy = 4 -- 7 +_with_0.GroupEnemyBlock = 5 -- 8 +_with_0.GroupEnemyPoke = 6 -- 9 +_with_0.GroupDisplay = 7 -- 10 +_with_0.GroupTerrain = Data.groupTerrain -- 11 +_with_0.GroupHide = Data.groupHide -- 12 +_with_0.LayerBackground = 0 -- 14 +_with_0.LayerBlock = 1 -- 15 +_with_0.LayerSwitch = 2 -- 16 +_with_0.LayerBunny = 3 -- 17 +_with_0.LayerEnemyHero = 4 -- 18 +_with_0.LayerPlayerHero = 5 -- 19 +_with_0.LayerForeground = 6 -- 20 +_with_0.LayerReadMe = 7 -- 21 +_with_0.MaxBunnies = 6 -- 23 +_with_0.MaxEP = 8.0 -- 24 +_with_0.MaxHP = 8.0 -- 25 +Data:setShouldContact(_with_0.GroupPlayerBlock, _with_0.GroupPlayerBlock, true) -- 27 +Data:setShouldContact(_with_0.GroupEnemyBlock, _with_0.GroupEnemyBlock, true) -- 28 +Data:setShouldContact(_with_0.GroupPlayerBlock, _with_0.GroupEnemyBlock, true) -- 29 +Data:setShouldContact(_with_0.GroupEnemy, _with_0.GroupPlayerBlock, true) -- 31 +Data:setShouldContact(_with_0.GroupPlayer, _with_0.GroupEnemyBlock, true) -- 32 +Data:setShouldContact(_with_0.GroupPlayerPoke, _with_0.GroupEnemy, true) -- 34 +Data:setShouldContact(_with_0.GroupPlayerPoke, _with_0.GroupEnemyBlock, true) -- 35 +Data:setShouldContact(_with_0.GroupEnemyPoke, _with_0.GroupPlayer, true) -- 37 +Data:setShouldContact(_with_0.GroupEnemyPoke, _with_0.GroupPlayerBlock, true) -- 38 +Data:setShouldContact(_with_0.GroupEnemyPoke, _with_0.GroupPlayerPoke, true) -- 39 +Data:setShouldContact(_with_0.GroupDisplay, _with_0.GroupDisplay, true) -- 41 +Data:setRelation(_with_0.GroupPlayer, _with_0.GroupPlayerBlock, "Friend") -- 43 +Data:setRelation(_with_0.GroupPlayer, _with_0.GroupPlayerPoke, "Friend") -- 44 +Data:setRelation(_with_0.GroupEnemy, _with_0.GroupEnemyBlock, "Friend") -- 45 +Data:setRelation(_with_0.GroupEnemy, _with_0.GroupEnemyPoke, "Friend") -- 46 +Data:setRelation(_with_0.GroupPlayer, _with_0.GroupEnemy, "Enemy") -- 48 +Data:setRelation(_with_0.GroupPlayer, _with_0.GroupEnemyBlock, "Enemy") -- 49 +Data:setRelation(_with_0.GroupPlayer, _with_0.GroupEnemyPoke, "Enemy") -- 50 +Data:setRelation(_with_0.GroupEnemy, _with_0.GroupPlayerBlock, "Enemy") -- 51 +Data:setRelation(_with_0.GroupEnemy, _with_0.GroupPlayerPoke, "Enemy") -- 52 +Data:setRelation(_with_0.GroupPlayerPoke, _with_0.GroupEnemyBlock, "Enemy") -- 54 +Data:setRelation(_with_0.GroupEnemyPoke, _with_0.GroupPlayerBlock, "Enemy") -- 55 +return _with_0 -- 3 diff --git a/Assets/Script/Game/Loli War/Control.lua b/Assets/Script/Game/Loli War/Control.lua new file mode 100644 index 000000000..81779c9ce --- /dev/null +++ b/Assets/Script/Game/Loli War/Control.lua @@ -0,0 +1,110 @@ +-- [yue]: Script/Game/Loli War/Control.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Group = dora.Group -- 1 +local App = dora.App -- 1 +local Node = dora.Node -- 1 +local Keyboard = dora.Keyboard -- 1 +local Director = dora.Director -- 1 +local HPWheel = require("UI.Control.HPWheel") -- 2 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 3 +local LeftTouchPad = require("UI.View.LeftTouchPad") -- 4 +local RightTouchPad = require("UI.View.RightTouchPad") -- 5 +local RestartPad = require("UI.View.RestartPad") -- 6 +local StartPanel = require("UI.Control.StartPanel") -- 7 +local Store = Data.store -- 9 +local GroupPlayer = Store.GroupPlayer -- 10 +local playerGroup = Group({ -- 12 + "hero", -- 12 + "unit" -- 12 +}) -- 12 +local updatePlayerControl -- 13 +updatePlayerControl = function(key, flag) -- 13 + return playerGroup:each(function(self) -- 14 + if self.group == GroupPlayer then -- 14 + self.unit.data[key] = flag -- 14 + end -- 14 + end) -- 14 +end -- 13 +local root = AlignNode({ -- 16 + isRoot = true -- 16 +}) -- 16 +root:addChild(HPWheel()) -- 17 +do -- 18 + local _exp_0 = App.platform -- 18 + if "iOS" == _exp_0 or "Android" == _exp_0 then -- 19 + root:addChild((function() -- 20 + local _with_0 = LeftTouchPad() -- 20 + _with_0:slot("KeyLeftUp", function() -- 21 + return updatePlayerControl("keyLeft", false) -- 21 + end) -- 21 + _with_0:slot("KeyLeftDown", function() -- 22 + return updatePlayerControl("keyLeft", true) -- 22 + end) -- 22 + _with_0:slot("KeyRightUp", function() -- 23 + return updatePlayerControl("keyRight", false) -- 23 + end) -- 23 + _with_0:slot("KeyRightDown", function() -- 24 + return updatePlayerControl("keyRight", true) -- 24 + end) -- 24 + return _with_0 -- 20 + end)()) -- 20 + root:addChild((function() -- 25 + local _with_0 = RightTouchPad() -- 25 + _with_0:slot("KeyFUp", function() -- 26 + return updatePlayerControl("keyF", false) -- 26 + end) -- 26 + _with_0:slot("KeyFDown", function() -- 27 + return updatePlayerControl("keyF", true) -- 27 + end) -- 27 + _with_0:slot("KeyUpUp", function() -- 28 + return updatePlayerControl("keyUp", false) -- 28 + end) -- 28 + _with_0:slot("KeyUpDown", function() -- 29 + return updatePlayerControl("keyUp", true) -- 29 + end) -- 29 + return _with_0 -- 25 + end)()) -- 25 + elseif "macOS" == _exp_0 or "Windows" == _exp_0 or "Linux" == _exp_0 then -- 30 + root:addChild((function() -- 31 + local _with_0 = Node() -- 31 + _with_0:schedule(function() -- 32 + updatePlayerControl("keyLeft", Keyboard:isKeyPressed("A")) -- 33 + updatePlayerControl("keyRight", Keyboard:isKeyPressed("D")) -- 34 + updatePlayerControl("keyUp", Keyboard:isKeyPressed("K")) -- 35 + return updatePlayerControl("keyF", Keyboard:isKeyPressed("J")) -- 36 + end) -- 32 + return _with_0 -- 31 + end)()) -- 31 + end -- 36 +end -- 36 +local showStartPanel -- 37 +showStartPanel = function() -- 37 + root:addChild((function() -- 38 + local _with_0 = StartPanel() -- 38 + _with_0:slot("AlignLayout", function(w) -- 39 + w = w * 0.6 -- 40 + local width = 210 * 2 * App.devicePixelRatio -- 41 + if w < width then -- 42 + do -- 42 + local _tmp_0 = w / width -- 42 + _with_0.scaleX = _tmp_0 -- 42 + _with_0.scaleY = _tmp_0 -- 42 + end -- 42 + end -- 42 + end) -- 39 + return _with_0 -- 38 + end)()) -- 38 + return root:alignLayout() -- 43 +end -- 37 +root:addChild((function() -- 44 + local _with_0 = RestartPad() -- 44 + _with_0:slot("Tapped", function() -- 45 + Store.winner = -1 -- 46 + return showStartPanel() -- 47 + end) -- 45 + return _with_0 -- 44 +end)()) -- 44 +root:addTo(Director.ui) -- 48 +showStartPanel() -- 49 +return root -- 16 diff --git a/Assets/Script/Game/Loli War/Logic.lua b/Assets/Script/Game/Loli War/Logic.lua new file mode 100644 index 000000000..14b85f063 --- /dev/null +++ b/Assets/Script/Game/Loli War/Logic.lua @@ -0,0 +1,675 @@ +-- [yue]: Script/Game/Loli War/Logic.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Group = dora.Group -- 1 +local Observer = dora.Observer -- 1 +local Vec2 = dora.Vec2 -- 1 +local Rect = dora.Rect -- 1 +local math = _G.math -- 1 +local thread = dora.thread -- 1 +local sleep = dora.sleep -- 1 +local emit = dora.emit -- 1 +local Audio = dora.Audio -- 1 +local App = dora.App -- 1 +local Entity = dora.Entity -- 1 +local Unit = _module_0.Unit -- 1 +local Visual = _module_0.Visual -- 1 +local Action = dora.Action -- 1 +local Sequence = dora.Sequence -- 1 +local Y = dora.Y -- 1 +local Ease = dora.Ease -- 1 +local Sprite = dora.Sprite -- 1 +local Spawn = dora.Spawn -- 1 +local Opacity = dora.Opacity -- 1 +local Scale = dora.Scale -- 1 +local BodyDef = dora.BodyDef -- 1 +local Body = dora.Body -- 1 +local tostring = _G.tostring -- 1 +local once = dora.once -- 1 +local Node = dora.Node -- 1 +local View = dora.View -- 1 +local SpriteEffect = dora.SpriteEffect -- 1 +local cycle = dora.cycle -- 1 +local Director = dora.Director -- 1 +local Store = Data.store -- 3 +local GroupPlayer, GroupEnemy, GroupDisplay, GroupTerrain, GroupPlayerBlock, GroupEnemyBlock, GroupPlayerPoke, GroupEnemyPoke, LayerBunny, LayerReadMe, LayerPlayerHero, LayerEnemyHero, LayerBackground, LayerBlock, MaxEP, MaxHP = Store.GroupPlayer, Store.GroupEnemy, Store.GroupDisplay, Store.GroupTerrain, Store.GroupPlayerBlock, Store.GroupEnemyBlock, Store.GroupPlayerPoke, Store.GroupEnemyPoke, Store.LayerBunny, Store.LayerReadMe, Store.LayerPlayerHero, Store.LayerEnemyHero, Store.LayerBackground, Store.LayerBlock, Store.MaxEP, Store.MaxHP -- 4 +local heroes = Group({ -- 23 + "hero" -- 23 +}) -- 23 +do -- 24 + local _with_0 = Observer("Add", { -- 24 + "world" -- 24 + }) -- 24 + _with_0:watch(function(self, world) -- 25 + do -- 27 + local _with_1 = world.camera -- 27 + _with_1.followRatio = Vec2(0.03, 0.03) -- 28 + _with_1.boundary = Rect(0, -110, 4096, 1004) -- 29 + _with_1.position = Vec2(1024, 274) -- 30 + end -- 27 + world:setIterations(2, 3) -- 31 + world:gslot("BlockValue", function(group, value) -- 32 + return heroes:each(function(hero) -- 32 + if hero.group == group then -- 33 + hero.blocks = value -- 33 + end -- 33 + end) -- 33 + end) -- 32 + world:gslot("BlockChange", function(group, value) -- 34 + return heroes:each(function(hero) -- 34 + if hero.group == group and hero.blocks then -- 35 + hero.blocks = math.max(hero.blocks + value, 0) -- 36 + if value < 0 then -- 37 + hero.defending = true -- 37 + end -- 37 + end -- 35 + end) -- 37 + end) -- 34 + world:gslot("EPChange", function(group, value) -- 38 + return heroes:each(function(hero) -- 38 + if hero.group == group then -- 39 + hero.ep = hero.ep + value -- 39 + end -- 39 + end) -- 39 + end) -- 38 + world:gslot("PlayerSelect", function(hero) -- 40 + return thread(function() -- 40 + sleep(1) -- 41 + world:clearScene() -- 42 + local _list_0 = { -- 43 + 6, -- 43 + 1, -- 43 + 1 -- 43 + } -- 43 + for _index_0 = 1, #_list_0 do -- 43 + local ep = _list_0[_index_0] -- 43 + emit("EPChange", GroupPlayer, ep) -- 44 + emit("EPChange", GroupEnemy, ep) -- 45 + end -- 45 + Store.winner = nil -- 46 + world.playing = true -- 47 + Audio:playStream("Audio/LOOP14.ogg", true) -- 48 + local names -- 49 + do -- 49 + local _accum_0 = { } -- 49 + local _len_0 = 1 -- 49 + local _list_1 = { -- 49 + "Flandre", -- 49 + "Dorothy", -- 49 + "Villy" -- 49 + } -- 49 + for _index_0 = 1, #_list_1 do -- 49 + local n = _list_1[_index_0] -- 49 + if n ~= hero then -- 49 + _accum_0[_len_0] = n -- 49 + _len_0 = _len_0 + 1 -- 49 + end -- 49 + end -- 49 + names = _accum_0 -- 49 + end -- 49 + local enemyHero = names[(App.rand % 2) + 1] -- 50 + Entity({ -- 52 + hero = hero, -- 52 + group = GroupPlayer, -- 53 + faceRight = true, -- 54 + AI = "PlayerControlAI", -- 55 + layer = LayerPlayerHero, -- 56 + position = Vec2(512, 1004 - 712), -- 57 + ep = MaxEP -- 58 + }) -- 51 + Entity({ -- 60 + hero = enemyHero, -- 60 + group = GroupEnemy, -- 61 + faceRight = false, -- 62 + AI = "HeroAI", -- 63 + layer = LayerEnemyHero, -- 64 + position = Vec2(3584, 1004 - 712), -- 65 + ep = MaxEP -- 66 + }) -- 59 + world:buildCastles() -- 67 + world:addBunnySwither(GroupPlayer) -- 68 + return world:addBunnySwither(GroupEnemy) -- 69 + end) -- 69 + end) -- 40 + Store.world = world -- 26 + world:buildBackground() -- 71 + world:buildSwitches() -- 72 + world:buildGameReadme() -- 73 + Audio:playStream("Audio/LOOP13.ogg", true) -- 74 + return false -- 74 + end) -- 25 +end -- 24 +local mutables = { -- 77 + "hp", -- 77 + "moveSpeed", -- 78 + "move", -- 79 + "jump", -- 80 + "targetAllow", -- 81 + "attackBase", -- 82 + "attackPower", -- 83 + "attackSpeed", -- 84 + "damageType", -- 85 + "attackBonus", -- 86 + "attackFactor", -- 87 + "attackTarget", -- 88 + "defenceType" -- 89 +} -- 76 +do -- 91 + local _with_0 = Observer("Add", { -- 91 + "hero", -- 91 + "group", -- 91 + "layer", -- 91 + "position", -- 91 + "faceRight", -- 91 + "AI" -- 91 + }) -- 91 + _with_0:watch(function(self, hero, group, layer, position, faceRight, AI) -- 92 + local world = Store.world -- 93 + local def = Store[hero] -- 94 + for _index_0 = 1, #mutables do -- 95 + local var = mutables[_index_0] -- 95 + self[var] = def[var] -- 96 + end -- 96 + local unit -- 97 + do -- 97 + local _with_1 = Unit(def, world, self, position) -- 97 + _with_1.group = group -- 98 + _with_1.faceRight = faceRight -- 99 + _with_1.order = layer -- 100 + _with_1.decisionTree = AI -- 101 + if "Dorothy" == hero then -- 103 + self.attackSpeed = 1.2 -- 103 + elseif "Villy" == hero then -- 104 + self.attackSpeed = 1.3 -- 104 + elseif "Flandre" == hero then -- 105 + self.attackSpeed = 1.8 -- 105 + end -- 105 + self.moveSpeed = 1.5 -- 106 + _with_1:eachAction(function(self) -- 107 + self.recovery = 0.05 -- 107 + end) -- 107 + _with_1:addTo(world) -- 108 + _with_1:addChild((function() -- 109 + local _with_2 = Visual("Particle/select.par") -- 109 + _with_2:autoRemove() -- 110 + _with_2:start() -- 111 + return _with_2 -- 109 + end)()) -- 109 + unit = _with_1 -- 97 + end -- 97 + if group == GroupPlayer then -- 112 + world.camera.followTarget = unit -- 113 + end -- 112 + emit("HPChange", self.group, self.hp) -- 114 + return false -- 114 + end) -- 92 +end -- 91 +do -- 116 + local _with_0 = Observer("Add", { -- 116 + "bunny", -- 116 + "group", -- 116 + "layer", -- 116 + "position", -- 116 + "faceRight", -- 116 + "AI" -- 116 + }) -- 116 + _with_0:watch(function(self, bunny, group, layer, position, faceRight, AI) -- 117 + local world = Store.world -- 118 + local def = Store[bunny] -- 119 + for _index_0 = 1, #mutables do -- 120 + local var = mutables[_index_0] -- 120 + if var == "hp" and self[var] ~= nil then -- 121 + goto _continue_0 -- 122 + end -- 121 + self[var] = def[var] -- 123 + ::_continue_0:: -- 121 + end -- 123 + local unit -- 124 + do -- 124 + local _with_1 = Unit(def, world, self, position) -- 124 + _with_1.group = group -- 125 + _with_1.faceRight = faceRight -- 126 + _with_1.order = layer -- 127 + _with_1.decisionTree = AI -- 128 + _with_1:eachAction(function(self) -- 129 + self.recovery = 0.1 -- 129 + end) -- 129 + _with_1:addTo(world) -- 130 + if self.hp == 0.0 then -- 131 + self.hp = self.hp - 1.0 -- 131 + end -- 131 + unit = _with_1 -- 124 + end -- 124 + return false -- 131 + end) -- 117 +end -- 116 +do -- 133 + local _with_0 = Observer("Add", { -- 133 + "switch", -- 133 + "group", -- 133 + "layer", -- 133 + "look", -- 133 + "position" -- 133 + }) -- 133 + _with_0:watch(function(self, switchType, group, layer, look, position) -- 134 + local world = Store.world -- 135 + local unit -- 136 + do -- 136 + local _with_1 = Unit(Store[switchType], world, self, position) -- 136 + _with_1.group = group -- 137 + _with_1.order = layer -- 138 + do -- 139 + local _with_2 = _with_1.playable -- 139 + _with_2.look = look -- 140 + _with_2.scaleX = 2 -- 141 + _with_2.scaleY = 2 -- 142 + end -- 139 + _with_1:addTo(world) -- 143 + _with_1.emittingEvent = true -- 144 + _with_1:slot("BodyEnter", function(self, sensorTag) -- 145 + if _with_1.attackSensor.tag == sensorTag and self.entity and Data:isFriend(self, unit) then -- 146 + if self.group == GroupPlayer and self.entity.hero and not self.data.tip then -- 147 + local floating = Action(Sequence(Y(0.5, 140, 150, Ease.OutQuad), Y(0.3, 150, 140, Ease.InQuad))) -- 148 + do -- 152 + local _with_2 = Sprite("Model/items.clip|keyf_down") -- 152 + _with_2.y = 140 -- 153 + local scaleOut = Action(Spawn(Opacity(0.3, 0, 1), Scale(0.3, 0, 1, Ease.OutQuad))) -- 154 + _with_2:runAction(scaleOut) -- 158 + _with_2:slot("ActionEnd", function(self) -- 159 + return _with_2:runAction(floating) -- 159 + end) -- 159 + _with_2:addTo(self) -- 160 + self.data.tip = _with_2 -- 152 + end -- 152 + end -- 147 + self.data.atSwitch = unit -- 161 + end -- 146 + end) -- 145 + _with_1:slot("BodyLeave", function(self, sensorTag) -- 162 + if _with_1.attackSensor.tag == sensorTag and Data:isFriend(self, unit) then -- 163 + self.data.atSwitch = nil -- 164 + if self.data.tip then -- 165 + do -- 166 + local _with_2 = self.data.tip -- 166 + _with_2:perform(Spawn(Scale(0.3, _with_2.scaleX, 0), Opacity(0.3, 1, 0))) -- 167 + _with_2:slot("ActionEnd"):set(function() -- 171 + return _with_2:removeFromParent() -- 171 + end) -- 171 + end -- 166 + self.data.tip = nil -- 172 + end -- 165 + end -- 163 + end) -- 162 + unit = _with_1 -- 136 + end -- 136 + return false -- 172 + end) -- 134 +end -- 133 +do -- 174 + local _with_0 = Observer("Add", { -- 174 + "block", -- 174 + "group", -- 174 + "layer", -- 174 + "look", -- 174 + "position" -- 174 + }) -- 174 + _with_0:watch(function(self, block, group, layer, look, position) -- 175 + local world = Store.world -- 176 + local def = Store[block] -- 177 + self.hp = def.hp -- 178 + self.defenceType = 0 -- 179 + do -- 180 + local _with_1 = Unit(def, world, self, position) -- 180 + _with_1.group = group -- 181 + _with_1.order = layer -- 182 + _with_1.playable.look = look -- 183 + _with_1:addTo(world) -- 184 + end -- 180 + return false -- 184 + end) -- 175 +end -- 174 +do -- 186 + local _with_0 = Observer("Add", { -- 186 + "poke", -- 186 + "group", -- 186 + "layer", -- 186 + "position" -- 186 + }) -- 186 + _with_0:watch(function(self, poke, group, layer, position) -- 187 + local world = Store.world -- 188 + local pokeDef -- 189 + do -- 189 + local _with_1 = BodyDef() -- 189 + _with_1.linearAcceleration = Vec2(0, -10) -- 190 + _with_1.type = "Dynamic" -- 191 + _with_1:attachDisk(192, 10.0, 0.1, 0.4) -- 192 + _with_1:attachDiskSensor(0, 194) -- 193 + pokeDef = _with_1 -- 189 + end -- 189 + do -- 194 + local _with_1 = Body(pokeDef, world, position) -- 194 + _with_1.group = group -- 195 + if GroupPlayerPoke == group then -- 197 + _with_1.velocityX = 400 -- 197 + elseif GroupEnemyPoke == group then -- 198 + _with_1.velocityX = -400 -- 198 + end -- 198 + local normal -- 199 + do -- 199 + local _with_2 = Sprite("Model/poke.clip|" .. tostring(poke)) -- 199 + _with_2.scaleX = 4 -- 200 + _with_2.scaleY = 4 -- 201 + _with_2.filter = "Point" -- 202 + normal = _with_2 -- 199 + end -- 199 + _with_1:addChild(normal) -- 203 + local glow -- 204 + do -- 204 + local _with_2 = Sprite("Model/poke.clip|" .. tostring(poke) .. "l") -- 204 + _with_2.scaleX = 4 -- 205 + _with_2.scaleY = 4 -- 206 + _with_2.filter = "Point" -- 207 + _with_2.visible = false -- 208 + glow = _with_2 -- 204 + end -- 204 + _with_1:addChild(glow) -- 209 + _with_1:slot("BodyEnter", function(self, sensorTag) -- 210 + if sensorTag == 0 and (function() -- 211 + local _exp_0 = self.group -- 211 + if GroupPlayer == _exp_0 or GroupEnemy == _exp_0 then -- 212 + return Data:isEnemy(self.group, _with_1.group) -- 212 + else -- 213 + return false -- 213 + end -- 213 + end)() then -- 211 + if (_with_1.x < self.x) == (_with_1.velocityX > 0) then -- 214 + self.velocity = Vec2(_with_1.velocityX > 0 and 500 or -500, 400) -- 215 + return self:start("strike") -- 216 + end -- 214 + end -- 211 + end) -- 210 + _with_1:schedule(once(function() -- 217 + while 50 < math.abs(_with_1.velocityX) do -- 218 + sleep(0.1) -- 219 + end -- 219 + for i = 1, 6 do -- 220 + Audio:play("Audio/di.wav") -- 221 + normal.visible = not normal.visible -- 222 + glow.visible = not glow.visible -- 223 + sleep(0.5) -- 224 + end -- 224 + local sensor = _with_1:attachSensor(1, BodyDef:disk(500)) -- 225 + sleep() -- 227 + local _list_0 = sensor.sensedBodies -- 228 + for _index_0 = 1, #_list_0 do -- 228 + local body = _list_0[_index_0] -- 228 + if Data:isEnemy(body.group, _with_1.group) then -- 229 + local entity = body.entity -- 230 + if entity and entity.hp > 0 then -- 231 + local x = _with_1.x -- 232 + do -- 233 + local _with_2 = body.data -- 233 + _with_2.hitPoint = body:convertToWorldSpace(Vec2.zero) -- 234 + _with_2.hitPower = Vec2(2000, 2400) -- 235 + _with_2.hitFromRight = body.x < x -- 236 + end -- 233 + entity.hp = entity.hp - 1 -- 237 + end -- 231 + end -- 229 + end -- 237 + local pos = _with_1:convertToWorldSpace(Vec2.zero) -- 238 + do -- 239 + local _with_2 = Visual("Particle/boom.par") -- 239 + _with_2.position = pos -- 240 + _with_2.scaleX = 4 -- 241 + _with_2.scaleY = 4 -- 242 + _with_2:addTo(world, LayerBlock) -- 243 + _with_2:autoRemove() -- 244 + _with_2:start() -- 245 + end -- 239 + _with_1:removeFromParent() -- 246 + return Audio:play("Audio/explosion.wav") -- 247 + end)) -- 217 + _with_1:addTo(world, layer) -- 248 + end -- 194 + Audio:play("Audio/quake.wav") -- 249 + return false -- 249 + end) -- 187 +end -- 186 +do -- 251 + local _with_0 = Observer("Change", { -- 251 + "hp", -- 251 + "unit", -- 251 + "block" -- 251 + }) -- 251 + _with_0:watch(function(self, hp, unit) -- 252 + local lastHp = self.oldValues.hp -- 253 + if hp < lastHp then -- 254 + if unit:isDoing("hit") then -- 255 + unit:start("cancel") -- 255 + end -- 255 + if hp > 0 then -- 256 + unit:start("hit") -- 257 + unit.faceRight = true -- 258 + do -- 259 + local _with_1 = unit.playable -- 259 + _with_1.recovery = 0.5 -- 260 + _with_1:play("hp" .. tostring(math.floor(hp))) -- 261 + end -- 259 + else -- 263 + unit:start("hit") -- 263 + unit.faceRight = true -- 264 + unit.group = Data.groupHide -- 265 + unit.playable:perform(Scale(0.3, 1, 0, Ease.OutQuad)) -- 266 + unit:schedule(once(function() -- 267 + sleep(5) -- 268 + return unit:removeFromParent() -- 269 + end)) -- 267 + local group -- 270 + do -- 270 + local _exp_0 = self.group -- 270 + if GroupPlayerBlock == _exp_0 then -- 271 + group = GroupEnemy -- 271 + elseif GroupEnemyBlock == _exp_0 then -- 272 + group = GroupPlayer -- 272 + end -- 272 + end -- 272 + emit("EPChange", group, 1) -- 273 + end -- 256 + local group -- 274 + do -- 274 + local _exp_0 = self.group -- 274 + if GroupPlayerBlock == _exp_0 then -- 275 + group = GroupPlayer -- 275 + elseif GroupEnemyBlock == _exp_0 then -- 276 + group = GroupEnemy -- 276 + end -- 276 + end -- 276 + emit("BlockChange", group, math.max(hp, 0) - lastHp) -- 277 + end -- 254 + return false -- 277 + end) -- 252 +end -- 251 +do -- 279 + local _with_0 = Observer("Change", { -- 279 + "hp", -- 279 + "bunny" -- 279 + }) -- 279 + _with_0:watch(function(self, hp) -- 280 + local unit = self.unit -- 281 + local lastHp = self.oldValues.hp -- 282 + if hp < lastHp then -- 283 + if unit:isDoing("hit") then -- 284 + unit:start("cancel") -- 284 + end -- 284 + if hp > 0 then -- 285 + unit:start("hit") -- 286 + else -- 288 + unit:start("hit") -- 288 + unit:start("fall") -- 289 + unit.group = Data.groupHide -- 290 + local group -- 291 + do -- 291 + local _exp_0 = self.group -- 291 + if GroupPlayer == _exp_0 then -- 292 + group = GroupEnemy -- 292 + elseif GroupEnemy == _exp_0 then -- 293 + group = GroupPlayer -- 293 + end -- 293 + end -- 293 + emit("EPChange", group, 1) -- 294 + unit:schedule(once(function() -- 295 + sleep(5) -- 296 + return unit:removeFromParent() -- 297 + end)) -- 295 + end -- 285 + end -- 283 + return false -- 297 + end) -- 280 +end -- 279 +do -- 299 + local _with_0 = Observer("Change", { -- 299 + "hp", -- 299 + "hero" -- 299 + }) -- 299 + _with_0:watch(function(self, hp) -- 300 + local unit = self.unit -- 301 + local lastHp = self.oldValues.hp -- 302 + local world = Store.world -- 303 + if hp < lastHp then -- 304 + if unit:isDoing("hit") then -- 305 + unit:start("cancel") -- 305 + end -- 305 + if hp > 0 then -- 306 + unit:start("hit") -- 307 + else -- 309 + unit:start("hit") -- 309 + unit:start("fall") -- 310 + local lastGroup = unit.group -- 311 + unit.group = Data.groupHide -- 312 + if unit.data.tip then -- 313 + unit.data.tip:removeFromParent() -- 314 + unit.data.tip = nil -- 315 + end -- 313 + emit("EPChange", lastGroup, 6) -- 316 + if GroupPlayer == lastGroup then -- 318 + Audio:play("Audio/hero_fall.wav") -- 319 + elseif GroupEnemy == lastGroup then -- 320 + Audio:play("Audio/hero_kill.wav") -- 321 + end -- 321 + if lastGroup == GroupPlayer then -- 322 + world:addChild((function() -- 323 + local _with_1 = Node() -- 323 + _with_1:schedule(once(function() -- 324 + do -- 325 + local _with_2 = SpriteEffect("builtin:vs_sprite", "builtin:fs_spritesaturation") -- 325 + _with_2:get(1):set("u_adjustment", 0) -- 326 + View.postEffect = _with_2 -- 325 + end -- 325 + sleep(3) -- 327 + cycle(5, function(dt) -- 328 + return View.postEffect:get(1):set("u_adjustment", dt) -- 328 + end) -- 328 + View.postEffect = nil -- 329 + end)) -- 324 + _with_1:slot("Cleanup", function() -- 330 + View.postEffect = nil -- 331 + Director.scheduler.timeScale = 1 -- 332 + end) -- 330 + return _with_1 -- 323 + end)()) -- 323 + end -- 322 + unit:schedule(once(function() -- 333 + Director.scheduler.timeScale = 0.25 -- 334 + sleep(3) -- 335 + Director.scheduler.timeScale = 1 -- 336 + sleep(2) -- 337 + world:addBunnySwither(lastGroup) -- 338 + unit.visible = false -- 339 + local start = unit.position -- 340 + local stop -- 341 + if GroupPlayer == lastGroup then -- 342 + stop = Vec2(512, 1004 - 512) -- 342 + elseif GroupEnemy == lastGroup then -- 343 + stop = Vec2(3584, 1004 - 512) -- 343 + end -- 343 + cycle(5, function(dt) -- 344 + unit.position = start + (stop - start) * Ease:func(Ease.OutQuad, dt) -- 344 + end) -- 344 + unit.playable.look = "happy" -- 345 + unit.visible = true -- 346 + unit.velocityY = 1 -- 347 + unit.group = lastGroup -- 348 + self.hp = MaxHP -- 349 + emit("HPChange", lastGroup, MaxHP) -- 350 + local _with_1 = Visual("Particle/select.par") -- 351 + _with_1:addTo(unit) -- 352 + _with_1:start() -- 353 + _with_1:autoRemove() -- 354 + return _with_1 -- 351 + end)) -- 333 + local group -- 355 + do -- 355 + local _exp_0 = self.group -- 355 + if GroupPlayer == _exp_0 then -- 356 + group = GroupEnemy -- 356 + elseif GroupEnemy == _exp_0 then -- 357 + group = GroupPlayer -- 357 + end -- 357 + end -- 357 + emit("EPChange", group, 1) -- 358 + end -- 306 + end -- 304 + emit("HPChange", self.group, math.max(hp, 0) - lastHp) -- 359 + return false -- 359 + end) -- 300 +end -- 299 +local _with_0 = Observer("Change", { -- 361 + "blocks", -- 361 + "group" -- 361 +}) -- 361 +_with_0:watch(function(self, blocks, group) -- 362 + local world = Store.world -- 363 + if not world.playing then -- 364 + return false -- 364 + end -- 364 + if blocks == 0 then -- 365 + world.playing = false -- 366 + Audio:playStream("Audio/LOOP11.ogg", true) -- 367 + local clip, sound -- 368 + if GroupPlayer == group then -- 369 + Store.winner, clip, sound = GroupEnemy, "lose", "hero_lose" -- 369 + elseif GroupEnemy == group then -- 370 + Store.winner, clip, sound = GroupPlayer, "win", "hero_win" -- 370 + end -- 370 + Audio:play("Audio/" .. tostring(sound) .. ".wav") -- 371 + local sp -- 372 + do -- 372 + local _with_1 = Sprite("Model/misc.clip|" .. tostring(clip)) -- 372 + _with_1.scaleX = 2 -- 373 + _with_1.scaleY = 2 -- 374 + _with_1.filter = "Point" -- 375 + sp = _with_1 -- 372 + end -- 372 + local rectDef -- 376 + do -- 376 + local _with_1 = BodyDef() -- 376 + _with_1.linearAcceleration = Vec2(0, -10) -- 377 + _with_1.type = "Dynamic" -- 378 + _with_1:attachPolygon(sp.width * 2, sp.height * 2, 1, 0, 1) -- 379 + rectDef = _with_1 -- 376 + end -- 376 + heroes:each(function(hero) -- 380 + if hero.group == GroupPlayer then -- 381 + local _with_1 = Body(rectDef, world, Vec2(hero.unit.x, 512)) -- 382 + _with_1.order = LayerBunny -- 383 + _with_1.group = GroupDisplay -- 384 + _with_1:addChild(sp) -- 385 + _with_1:addTo(world) -- 386 + return _with_1 -- 382 + end -- 381 + end) -- 380 + end -- 365 + return false -- 386 +end) -- 362 +return _with_0 -- 361 diff --git a/Assets/Script/Game/Loli War/Scene.lua b/Assets/Script/Game/Loli War/Scene.lua new file mode 100644 index 000000000..fc507bb7b --- /dev/null +++ b/Assets/Script/Game/Loli War/Scene.lua @@ -0,0 +1,326 @@ +-- [yue]: Script/Game/Loli War/Scene.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Class = dora.Class -- 1 +local PlatformWorld = _module_0.PlatformWorld -- 1 +local Entity = dora.Entity -- 1 +local View = dora.View -- 1 +local BodyDef = dora.BodyDef -- 1 +local Vec2 = dora.Vec2 -- 1 +local Body = dora.Body -- 1 +local Sprite = dora.Sprite -- 1 +local Node = dora.Node -- 1 +local Visual = _module_0.Visual -- 1 +local tostring = _G.tostring -- 1 +local Group = dora.Group -- 1 +local emit = dora.emit -- 1 +local _module_0 = nil -- 1 +local Store = Data.store -- 3 +local GroupPlayer, GroupEnemy, GroupPlayerBlock, GroupEnemyBlock, GroupTerrain, GroupDisplay, LayerReadMe, LayerBlock, LayerBunny, LayerSwitch, LayerPlayerHero, LayerEnemyHero, LayerBackground = Store.GroupPlayer, Store.GroupEnemy, Store.GroupPlayerBlock, Store.GroupEnemyBlock, Store.GroupTerrain, Store.GroupDisplay, Store.LayerReadMe, Store.LayerBlock, Store.LayerBunny, Store.LayerSwitch, Store.LayerPlayerHero, Store.LayerEnemyHero, Store.LayerBackground -- 4 +local GameWorld = Class(PlatformWorld, { -- 21 + __init = function(self) -- 21 + Entity({ -- 22 + world = self -- 22 + }) -- 22 + local DesignWidth = 1024 -- 23 + self.camera.zoom = View.size.width / DesignWidth -- 24 + return self:gslot("AppSizeChanged", function() -- 25 + self.camera.zoom = View.size.width / DesignWidth -- 26 + end) -- 26 + end, -- 21 + buildBackground = function(self) -- 28 + local terrainDef -- 29 + do -- 29 + local _with_0 = BodyDef() -- 29 + _with_0.type = "Static" -- 30 + _with_0:attachPolygon(Vec2(2048, 1004 - 994), 4096, 10) -- 31 + _with_0:attachPolygon(Vec2(2048, 1004), 4096, 10) -- 32 + _with_0:attachPolygon(Vec2(-5, 1004 - 512), 10, 1024) -- 33 + _with_0:attachPolygon(Vec2(4101, 1004 - 512), 10, 1024) -- 34 + terrainDef = _with_0 -- 29 + end -- 29 + self:addChild((function() -- 36 + local _with_0 = Body(terrainDef, self, Vec2.zero) -- 36 + _with_0.order = LayerBackground -- 37 + _with_0.group = GroupTerrain -- 38 + return _with_0 -- 36 + end)()) -- 36 + self:addChild((function() -- 40 + local _with_0 = Sprite("Model/items.clip|background") -- 40 + _with_0.order = LayerBackground -- 41 + _with_0.anchor = Vec2.zero -- 42 + _with_0.scaleX = 4146 / _with_0.width -- 43 + _with_0.scaleY = 1600 / _with_0.height -- 44 + _with_0.y = -50 -- 45 + return _with_0 -- 40 + end)()) -- 40 + return self:addChild((function() -- 47 + local _with_0 = Node() -- 47 + _with_0.order = LayerBackground -- 48 + _with_0.y = -44 -- 49 + for i = 0, 32 do -- 50 + _with_0:addChild((function() -- 51 + local _with_1 = Sprite("Model/misc.clip|floor") -- 51 + _with_1.scaleX = 8 -- 52 + _with_1.scaleY = 8 -- 53 + _with_1.x = i * 128 -- 54 + _with_1.filter = "Point" -- 55 + return _with_1 -- 51 + end)()) -- 51 + end -- 55 + return _with_0 -- 47 + end)()) -- 55 + end, -- 28 + buildGameReadme = function(self) -- 57 + local pos = self.camera.position -- 58 + local readmeDef -- 59 + do -- 59 + local _with_0 = BodyDef() -- 59 + _with_0.type = "Static" -- 60 + _with_0:attachPolygon(Vec2(pos.x, 1004 - 994), 1024, 10) -- 61 + _with_0:attachPolygon(Vec2(pos.x, 1004), 1024, 10) -- 62 + _with_0:attachPolygon(Vec2(pos.x - 512, 1004 - 512), 10, 1024) -- 63 + _with_0:attachPolygon(Vec2(pos.x + 512, 1004 - 512), 10, 1024) -- 64 + readmeDef = _with_0 -- 59 + end -- 59 + local readme -- 66 + do -- 66 + local _with_0 = Body(readmeDef, self, Vec2.zero) -- 66 + _with_0.order = LayerBackground -- 67 + _with_0.group = GroupDisplay -- 68 + _with_0:addTo(self) -- 69 + _with_0:gslot("PlayerSelect", function() -- 70 + _with_0.children:each(function(child) -- 71 + local _with_1 = Visual("Particle/heart.par") -- 72 + _with_1.position = child:convertToWorldSpace(Vec2.zero) -- 73 + _with_1:autoRemove() -- 74 + _with_1:start() -- 75 + return _with_1 -- 72 + end) -- 71 + return _with_0:removeFromParent() -- 76 + end) -- 70 + readme = _with_0 -- 66 + end -- 66 + local _list_0 = { -- 79 + { -- 79 + "war", -- 79 + Vec2(pos.x - 512 + 369, 1004 - 413 - 200) -- 79 + }, -- 79 + { -- 80 + "use", -- 80 + Vec2(pos.x - 512 + 459, 1004 - 575 - 200) -- 80 + }, -- 80 + { -- 81 + "key", -- 81 + Vec2(pos.x - 512 + 521, 1004 - 499 - 200) -- 81 + }, -- 81 + { -- 82 + "loli", -- 82 + Vec2(pos.x - 512 + 655, 1004 - 423 - 200) -- 82 + }, -- 82 + { -- 83 + "select", -- 83 + Vec2(pos.x - 512 + 709, 1004 - 509 - 200) -- 83 + }, -- 83 + { -- 84 + "mosic", -- 84 + Vec2(pos.x - 512 + 599, 1004 - 339 - 200) -- 84 + }, -- 84 + { -- 85 + "breakblocks", -- 85 + Vec2(pos.x - 512 + 578, 1004 - 626 - 200) -- 85 + }, -- 85 + { -- 86 + "search", -- 86 + Vec2(pos.x - 512 + 746, 1004 - 604 - 200) -- 86 + }, -- 86 + { -- 87 + "quit", -- 87 + Vec2(pos.x - 512 + 363, 1004 - 566 - 200) -- 87 + }, -- 87 + { -- 88 + "pushSwitch", -- 88 + Vec2(pos.x - 512 + 494, 1004 - 631 - 200) -- 88 + }, -- 88 + { -- 89 + "attack", -- 89 + Vec2(pos.x - 512 + 630, 1004 - 631 - 200) -- 89 + } -- 89 + } -- 78 + for _index_0 = 1, #_list_0 do -- 90 + local item = _list_0[_index_0] -- 78 + local sp -- 91 + do -- 91 + local _with_0 = Sprite("Model/misc.clip|" .. tostring(item[1])) -- 91 + _with_0.scaleX = 2 -- 92 + _with_0.scaleY = 2 -- 93 + _with_0.filter = "Point" -- 94 + sp = _with_0 -- 91 + end -- 91 + local rectDef -- 95 + do -- 95 + local _with_0 = BodyDef() -- 95 + _with_0.linearAcceleration = Vec2(0, -10) -- 96 + _with_0.type = "Dynamic" -- 97 + _with_0:attachPolygon(sp.width * 2, sp.height * 2, 1, 0, 1) -- 98 + rectDef = _with_0 -- 95 + end -- 95 + do -- 99 + local _with_0 = Body(rectDef, self, item[2]) -- 99 + _with_0.order = LayerBackground -- 100 + _with_0.group = GroupDisplay -- 101 + _with_0:addChild(sp) -- 102 + _with_0:addTo(readme) -- 103 + end -- 99 + end -- 103 + end, -- 57 + buildCastles = function(self) -- 105 + local Block -- 106 + Block = function(block, group, look, x, y) -- 106 + return Entity({ -- 108 + block = "Block" .. tostring(block), -- 108 + group = group, -- 109 + layer = LayerBlock, -- 110 + look = look, -- 111 + position = Vec2(x, y) -- 112 + }) -- 112 + end -- 106 + Block("C", GroupPlayerBlock, "gray", 419, 1004 - 280) -- 115 + Block("A", GroupPlayerBlock, "green", 239, 1004 - 190) -- 116 + Block("A", GroupPlayerBlock, "green", 419, 1004 - 190) -- 117 + Block("A", GroupPlayerBlock, "green", 599, 1004 - 190) -- 118 + Block("C", GroupPlayerBlock, "gray", 419, 1004 - 580) -- 119 + Block("B", GroupPlayerBlock, "", 291, 1004 - 430) -- 120 + Block("B", GroupPlayerBlock, "", 416, 1004 - 430) -- 121 + Block("B", GroupPlayerBlock, "", 540, 1004 - 430) -- 122 + Block("A", GroupPlayerBlock, "gray", 239, 1004 - 670) -- 123 + Block("A", GroupPlayerBlock, "gray", 599, 1004 - 670) -- 124 + Block("A", GroupPlayerBlock, "blue", 239, 1004 - 760) -- 125 + Block("A", GroupPlayerBlock, "blue", 599, 1004 - 760) -- 126 + Block("A", GroupPlayerBlock, "red", 239, 1004 - 850) -- 127 + Block("A", GroupPlayerBlock, "red", 599, 1004 - 850) -- 128 + Block("C", GroupPlayerBlock, "jade", 419, 1004 - 940) -- 129 + Block("C", GroupPlayerBlock, "jade", 1074, 1004 - 552) -- 132 + Block("C", GroupPlayerBlock, "jade", 1074, 1004 - 731) -- 133 + Block("A", GroupPlayerBlock, "blue", 894, 1004 - 463) -- 134 + Block("A", GroupPlayerBlock, "blue", 1075, 1004 - 463) -- 135 + Block("A", GroupPlayerBlock, "blue", 1254, 1004 - 463) -- 136 + Block("A", GroupPlayerBlock, "green", 956, 1004 - 642) -- 137 + Block("A", GroupPlayerBlock, "green", 1194, 1004 - 642) -- 138 + Block("B", GroupPlayerBlock, "", 893, 1004 - 881) -- 139 + Block("B", GroupPlayerBlock, "", 1254, 1004 - 881) -- 140 + Block("C", GroupEnemyBlock, "gray", 3674, 1004 - 281) -- 143 + Block("A", GroupEnemyBlock, "green", 3494, 1004 - 191) -- 144 + Block("A", GroupEnemyBlock, "green", 3674, 1004 - 191) -- 145 + Block("A", GroupEnemyBlock, "green", 3854, 1004 - 191) -- 146 + Block("C", GroupEnemyBlock, "gray", 3674, 1004 - 581) -- 147 + Block("B", GroupEnemyBlock, "", 3546, 1004 - 431) -- 148 + Block("B", GroupEnemyBlock, "", 3671, 1004 - 431) -- 149 + Block("B", GroupEnemyBlock, "", 3795, 1004 - 431) -- 150 + Block("A", GroupEnemyBlock, "gray", 3494, 1004 - 671) -- 151 + Block("A", GroupEnemyBlock, "gray", 3854, 1004 - 671) -- 152 + Block("A", GroupEnemyBlock, "blue", 3494, 1004 - 761) -- 153 + Block("A", GroupEnemyBlock, "blue", 3854, 1004 - 761) -- 154 + Block("A", GroupEnemyBlock, "red", 3494, 1004 - 851) -- 155 + Block("A", GroupEnemyBlock, "red", 3854, 1004 - 851) -- 156 + Block("C", GroupEnemyBlock, "jade", 3674, 1004 - 941) -- 157 + Block("C", GroupEnemyBlock, "jade", 3024, 1004 - 552) -- 160 + Block("C", GroupEnemyBlock, "jade", 3024, 1004 - 731) -- 161 + Block("A", GroupEnemyBlock, "blue", 2844, 1004 - 463) -- 162 + Block("A", GroupEnemyBlock, "blue", 3025, 1004 - 463) -- 163 + Block("A", GroupEnemyBlock, "blue", 3204, 1004 - 463) -- 164 + Block("A", GroupEnemyBlock, "green", 2906, 1004 - 642) -- 165 + Block("A", GroupEnemyBlock, "green", 3144, 1004 - 642) -- 166 + Block("B", GroupEnemyBlock, "", 2843, 1004 - 881) -- 167 + Block("B", GroupEnemyBlock, "", 3204, 1004 - 881) -- 168 + local playerBlockHP = 0 -- 170 + local enemyBlockHP = 0 -- 171 + do -- 172 + local _with_0 = Group({ -- 172 + "block" -- 172 + }) -- 172 + _with_0:each(function(self) -- 173 + local _exp_0 = self.group -- 173 + if GroupPlayerBlock == _exp_0 then -- 174 + playerBlockHP = playerBlockHP + Store[self.block].hp -- 175 + elseif GroupEnemyBlock == _exp_0 then -- 176 + enemyBlockHP = enemyBlockHP + Store[self.block].hp -- 177 + end -- 177 + end) -- 173 + end -- 172 + emit("BlockValue", GroupPlayer, playerBlockHP) -- 178 + return emit("BlockValue", GroupEnemy, enemyBlockHP) -- 179 + end, -- 105 + buildSwitches = function(self) -- 181 + local Switch -- 182 + Switch = function(switchType, group, look, x, y) -- 182 + return Entity({ -- 184 + switch = switchType, -- 184 + group = group, -- 185 + look = look, -- 186 + layer = LayerSwitch, -- 187 + position = Vec2(x, y) -- 188 + }) -- 188 + end -- 182 + Switch("Switch", GroupPlayer, "normal", 777, 1004 - 923) -- 189 + Switch("SwitchG", GroupPlayer, "gold", 116, 1004 - 923) -- 190 + Switch("Switch", GroupEnemy, "normal", 3331, 1004 - 923) -- 191 + return Switch("SwitchG", GroupEnemy, "gold", 3977, 1004 - 923) -- 192 + end, -- 181 + addBunnySwither = function(self, group) -- 194 + local switchGExist = false -- 195 + local switchNExist = false -- 196 + local bunnySwitchers = Group({ -- 197 + "bunny", -- 197 + "targetSwitch" -- 197 + }) -- 197 + bunnySwitchers:each(function(switcher) -- 198 + if switcher.group == group then -- 199 + local _exp_0 = switcher.targetSwitch -- 200 + if "SwitchG" == _exp_0 then -- 201 + switchGExist = true -- 201 + elseif "Switch" == _exp_0 then -- 202 + switchNExist = true -- 202 + end -- 202 + end -- 199 + end) -- 198 + if not switchGExist then -- 203 + Entity((function() -- 204 + local _with_0 = { } -- 204 + _with_0.group = group -- 205 + if GroupPlayer == group then -- 207 + _with_0.bunny, _with_0.faceRight, _with_0.position = "BunnyG", false, Vec2(216, 500) -- 207 + elseif GroupEnemy == group then -- 208 + _with_0.bunny, _with_0.faceRight, _with_0.position = "BunnyP", true, Vec2(3877, 500) -- 208 + end -- 208 + _with_0.AI = "BunnySwitcherAI" -- 209 + _with_0.layer = LayerBunny -- 210 + _with_0.targetSwitch = "SwitchG" -- 211 + return _with_0 -- 204 + end)()) -- 204 + end -- 203 + if not switchNExist then -- 212 + return Entity((function() -- 213 + local _with_0 = { } -- 213 + _with_0.group = group -- 214 + if GroupPlayer == group then -- 216 + _with_0.bunny, _with_0.faceRight, _with_0.position = "BunnyG", true, Vec2(677, 500) -- 216 + elseif GroupEnemy == group then -- 217 + _with_0.bunny, _with_0.faceRight, _with_0.position = "BunnyP", false, Vec2(3431, 500) -- 217 + end -- 217 + _with_0.AI = "BunnySwitcherAI" -- 218 + _with_0.layer = LayerBunny -- 219 + _with_0.targetSwitch = "Switch" -- 220 + return _with_0 -- 213 + end)()) -- 220 + end -- 212 + end, -- 194 + clearScene = function(self) -- 222 + self:removeLayer(LayerBlock) -- 223 + self:removeLayer(LayerBunny) -- 224 + self:removeLayer(LayerPlayerHero) -- 225 + return self:removeLayer(LayerEnemyHero) -- 226 + end -- 222 +}) -- 20 +_module_0 = GameWorld() -- 228 +return _module_0 -- 228 diff --git a/Assets/Script/Game/Loli War/UI/Control/ButtonGlow.lua b/Assets/Script/Game/Loli War/UI/Control/ButtonGlow.lua new file mode 100644 index 000000000..74b2ddc0f --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/Control/ButtonGlow.lua @@ -0,0 +1,43 @@ +-- [yue]: Script/Game/Loli War/UI/Control/ButtonGlow.yue +local Class = dora.Class -- 1 +local _module_0 = dora.Platformer -- 1 +local Visual = _module_0.Visual -- 1 +local Director = dora.Director -- 1 +local Audio = dora.Audio -- 1 +local loop = dora.loop -- 1 +local sleep = dora.sleep -- 1 +local _module_0 = nil -- 1 +local ButtonGlow = require("UI.View.ButtonGlow") -- 2 +_module_0 = Class(ButtonGlow, { -- 5 + __init = function(self) -- 5 + return self:slot("Tapped", function(touch) -- 6 + local _with_0 = Visual("Particle/select.par") -- 7 + _with_0.position = self:convertToWorldSpace(touch.location) -- 8 + _with_0:addTo(Director.ui) -- 9 + _with_0:autoRemove() -- 10 + _with_0:start() -- 11 + return _with_0 -- 7 + end) -- 11 + end, -- 5 + glow = function(self) -- 13 + if not self.scheduled then -- 14 + Audio:play("Audio/select.wav") -- 15 + return self:schedule(loop(function() -- 16 + self.up.visible = false -- 17 + self.down.visible = true -- 18 + sleep(0.5) -- 19 + self.up.visible = true -- 20 + self.down.visible = false -- 21 + return sleep(0.5) -- 22 + end)) -- 22 + end -- 14 + end, -- 13 + stopGlow = function(self) -- 24 + if self.scheduled then -- 25 + self:unschedule() -- 26 + self.up.visible = true -- 27 + self.down.visible = false -- 28 + end -- 25 + end -- 24 +}) -- 4 +return _module_0 -- 28 diff --git a/Assets/Script/Game/Loli War/UI/Control/Digit.lua b/Assets/Script/Game/Loli War/UI/Control/Digit.lua new file mode 100644 index 000000000..5f5468bba --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/Control/Digit.lua @@ -0,0 +1,36 @@ +-- [yue]: Script/Game/Loli War/UI/Control/Digit.yue +local Class = dora.Class -- 1 +local property = dora.property -- 1 +local math = _G.math -- 1 +local Sprite = dora.Sprite -- 1 +local tostring = _G.tostring -- 1 +local Vec2 = dora.Vec2 -- 1 +local _module_0 = nil -- 1 +local Digit = require("UI.View.Digit") -- 2 +_module_0 = Class(Digit, { -- 5 + __init = function(self) -- 5 + self._value = 99 -- 6 + self.maxValue = 99 -- 7 + end, -- 5 + value = property((function(self) -- 9 + return self._value -- 9 + end), function(self, value) -- 10 + self._value = math.max(math.min(self.maxValue, value), 0) -- 11 + self:removeAllChildren() -- 12 + local two = math.floor(self._value / 10) -- 13 + if two > 0 then -- 14 + do -- 15 + local _with_0 = Sprite("Model/misc.clip|" .. tostring(two)) -- 15 + _with_0.anchor = Vec2(0, 0.5) -- 16 + _with_0:addTo(self) -- 17 + end -- 15 + end -- 14 + local one = math.floor(self._value % 10) -- 18 + local _with_0 = Sprite("Model/misc.clip|" .. tostring(one)) -- 19 + _with_0.x = 6 -- 20 + _with_0.anchor = Vec2(0, 0.5) -- 21 + _with_0:addTo(self) -- 22 + return _with_0 -- 19 + end) -- 9 +}) -- 4 +return _module_0 -- 22 diff --git a/Assets/Script/Game/Loli War/UI/Control/HPWheel.lua b/Assets/Script/Game/Loli War/UI/Control/HPWheel.lua new file mode 100644 index 000000000..5050ec083 --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/Control/HPWheel.lua @@ -0,0 +1,84 @@ +-- [yue]: Script/Game/Loli War/UI/Control/HPWheel.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Class = dora.Class -- 1 +local math = _G.math -- 1 +local tostring = _G.tostring -- 1 +local ScaleX = dora.ScaleX -- 1 +local string = _G.string -- 1 +local table = _G.table -- 1 +local ipairs = _G.ipairs -- 1 +local X = dora.X -- 1 +local _module_0 = nil -- 1 +local HPWheel = require("UI.View.HPWheel") -- 2 +local EPHint = require("UI.View.EPHint") -- 3 +local GroupPlayer, GroupEnemy, GroupPlayerBlock, GroupEnemyBlock, MaxEP, MaxHP -- 5 +do -- 5 + local _obj_0 = Data.store -- 12 + GroupPlayer, GroupEnemy, GroupPlayerBlock, GroupEnemyBlock, MaxEP, MaxHP = _obj_0.GroupPlayer, _obj_0.GroupEnemy, _obj_0.GroupPlayerBlock, _obj_0.GroupEnemyBlock, _obj_0.MaxEP, _obj_0.MaxHP -- 5 +end -- 12 +_module_0 = Class(HPWheel, { -- 15 + __init = function(self) -- 15 + self.ep = MaxEP -- 16 + self.hp = MaxHP -- 17 + self.hints = { } -- 18 + self.hpShow:slot("AnimationEnd", function(name) -- 20 + if name == "hit" then -- 21 + return self.hpShow:play("idle", true) -- 21 + end -- 21 + end) -- 20 + self:gslot("HPChange", function(group, value) -- 23 + if group == GroupPlayer then -- 24 + local newHP = math.max(self.hp + value, 0) -- 25 + self.hp = math.floor(math.max(math.min(MaxHP, newHP), 0) + 0.5) -- 26 + self.hpShow.look = tostring(self.hp) -- 27 + if value < 0 then -- 28 + return self.hpShow:play("hit") -- 28 + end -- 28 + end -- 24 + end) -- 23 + self:gslot("EPChange", function(group, value) -- 30 + if group == GroupPlayer then -- 31 + if 1 == value or (-1) == value or (-2) == value or 6 == value then -- 33 + self.ep = math.floor(math.max(math.min(MaxEP, self.ep + value), 0) + 0.5) -- 34 + self.fill:perform(ScaleX(0.2, self.fill.scaleX, self.ep / MaxEP)) -- 35 + local hint -- 36 + do -- 36 + local _with_0 = EPHint({ -- 36 + index = #self.hints + 1, -- 36 + clip = string.format("%+d", value) -- 36 + }) -- 36 + _with_0.index = #self.hints + 1 -- 37 + _with_0:slot("DisplayEnd", function() -- 38 + local index = hint.index -- 39 + hint:removeFromParent() -- 40 + table.remove(self.hints, index) -- 41 + for i, v in ipairs(self.hints) do -- 42 + v:runAction(X(0.2, v.x, 55 + 25 * (i - 1))) -- 43 + v.index = i -- 44 + end -- 44 + end) -- 38 + hint = _with_0 -- 36 + end -- 36 + table.insert(self.hints, hint) -- 45 + return self.energy:addChild(hint) -- 46 + end -- 46 + end -- 31 + end) -- 30 + self:gslot("BlockValue", function(group, value) -- 48 + if GroupPlayer == group then -- 50 + self.playerBlocks.value = value -- 51 + elseif GroupEnemy == group then -- 52 + self.enemyBlocks.value = value -- 53 + end -- 53 + end) -- 48 + return self:gslot("BlockChange", function(group, value) -- 55 + if GroupPlayer == group then -- 57 + self.playerBlocks.value = math.max(self.playerBlocks.value + value, 0) -- 58 + elseif GroupEnemy == group then -- 59 + self.enemyBlocks.value = math.max(self.enemyBlocks.value + value, 0) -- 60 + end -- 60 + end) -- 60 + end -- 15 +}) -- 14 +return _module_0 -- 60 diff --git a/Assets/Script/Game/Loli War/UI/Control/StartPanel.lua b/Assets/Script/Game/Loli War/UI/Control/StartPanel.lua new file mode 100644 index 000000000..0a32cd5fe --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/Control/StartPanel.lua @@ -0,0 +1,65 @@ +-- [yue]: Script/Game/Loli War/UI/Control/StartPanel.yue +local Class = dora.Class -- 1 +local Audio = dora.Audio -- 1 +local emit = dora.emit -- 1 +local App = dora.App -- 1 +local nvg = dora.nvg -- 1 +local Vec2 = dora.Vec2 -- 1 +local ipairs = _G.ipairs -- 1 +local Rect = dora.Rect -- 1 +local _module_0 = nil -- 1 +local StartPanel = require("UI.View.StartPanel") -- 2 +_module_0 = Class(StartPanel, { -- 5 + __init = function(self) -- 5 + local buttons = { -- 6 + self.fButton, -- 6 + self.vButton, -- 6 + self.dButton -- 6 + } -- 6 + for _index_0 = 1, #buttons do -- 7 + local button = buttons[_index_0] -- 7 + button:slot("Tapped", function() -- 8 + Audio:play("Audio/choose.wav") -- 9 + for _index_1 = 1, #buttons do -- 10 + local btn = buttons[_index_1] -- 10 + btn.touchEnabled = false -- 11 + end -- 11 + return emit("PlayerSelect", (function() -- 12 + if self.fButton == button then -- 13 + return "Flandre" -- 13 + elseif self.vButton == button then -- 14 + return "Villy" -- 14 + elseif self.dButton == button then -- 15 + return "Dorothy" -- 15 + end -- 15 + end)()) -- 15 + end) -- 8 + end -- 15 + self.node:schedule(function() -- 16 + local bw, bh -- 17 + do -- 17 + local _obj_0 = App.bufferSize -- 17 + bw, bh = _obj_0.width, _obj_0.height -- 17 + end -- 17 + local vw, vh -- 18 + do -- 18 + local _obj_0 = App.visualSize -- 18 + vw, vh = _obj_0.width, _obj_0.height -- 18 + end -- 18 + local pos = nvg.TouchPos() * (bw / vw) -- 19 + pos = Vec2(pos.x - bw / 2, bh / 2 - pos.y) -- 20 + for _, button in ipairs(buttons) do -- 21 + local localPos = button:convertToNodeSpace(pos) -- 22 + if Rect(Vec2.zero, button.size):containsPoint(localPos) then -- 23 + button:glow() -- 24 + else -- 26 + button:stopGlow() -- 26 + end -- 23 + end -- 26 + end) -- 16 + return self.node:slot("PanelHide", function() -- 27 + return self:removeFromParent() -- 27 + end) -- 27 + end -- 5 +}) -- 4 +return _module_0 -- 27 diff --git a/Assets/Script/Game/Loli War/UI/View/Button.lua b/Assets/Script/Game/Loli War/UI/View/Button.lua new file mode 100644 index 000000000..471826d1f --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/Button.lua @@ -0,0 +1,34 @@ +-- [xml]: Script/Game/Loli War/UI/View/Button.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 1 +node1.anchor = Vec2(0,0) -- 1 +node1.x = x or 0 -- 1 +node1.y = y or 0 -- 1 +node1.size = Size(width,height) -- 1 +node1.touchEnabled = true -- 1 +local up = Sprite('Model/items.clip|'..imageUp) -- 2 +up.x = node1.width*0.5 -- 2 +up.y = node1.height*0.5 -- 2 +node1:addChild(up) -- 2 +local down = Sprite('Model/items.clip|'..imageDown) -- 3 +down.x = node1.width*0.5 -- 3 +down.y = node1.height*0.5 -- 3 +down.visible = false -- 3 +node1:addChild(down) -- 3 +local hide = Hide() -- 5 +local show = Show() -- 6 +node1:slot("TapBegan",function() -- 8 +up:perform(hide) -- 8 +end) -- 8 +node1:slot("TapBegan",function() -- 9 +down:perform(show) -- 9 +end) -- 9 +node1:slot("TapEnded",function() -- 10 +up:perform(show) -- 10 +end) -- 10 +node1:slot("TapEnded",function() -- 11 +down:perform(hide) -- 11 +end) -- 11 +return node1 -- 11 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/ButtonGlow.lua b/Assets/Script/Game/Loli War/UI/View/ButtonGlow.lua new file mode 100644 index 000000000..fcd7845be --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/ButtonGlow.lua @@ -0,0 +1,21 @@ +-- [xml]: Script/Game/Loli War/UI/View/ButtonGlow.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 1 +node1.x = x or 0 -- 1 +node1.y = y or 0 -- 1 +node1.size = Size(width,height) -- 1 +node1.touchEnabled = true -- 1 +local up = Sprite('Model/misc.clip|'..normal) -- 2 +up.x = node1.width*0.5 -- 2 +up.y = node1.height*0.5 -- 2 +node1:addChild(up) -- 2 +node1.up = up -- 2 +local down = Sprite('Model/misc.clip|'..glow) -- 3 +down.x = node1.width*0.5 -- 3 +down.y = node1.height*0.5 -- 3 +down.visible = false -- 3 +node1:addChild(down) -- 3 +node1.down = down -- 3 +return node1 -- 3 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/Digit.lua b/Assets/Script/Game/Loli War/UI/View/Digit.lua new file mode 100644 index 000000000..2e65d1ffa --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/Digit.lua @@ -0,0 +1,16 @@ +-- [xml]: Script/Game/Loli War/UI/View/Digit.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 1 +node1.x = x -- 1 +node1.y = y -- 1 +local sprite1 = Sprite("Model/misc.clip|9") -- 2 +sprite1.anchor = Vec2(0,sprite1.anchor.y) -- 2 +sprite1.x = 0 -- 2 +node1:addChild(sprite1) -- 2 +local sprite2 = Sprite("Model/misc.clip|9") -- 3 +sprite2.anchor = Vec2(0,sprite2.anchor.y) -- 3 +sprite2.x = 6 -- 3 +node1:addChild(sprite2) -- 3 +return node1 -- 3 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/EPHint.lua b/Assets/Script/Game/Loli War/UI/View/EPHint.lua new file mode 100644 index 000000000..3357a0e1a --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/EPHint.lua @@ -0,0 +1,15 @@ +-- [xml]: Script/Game/Loli War/UI/View/EPHint.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 1 +node1.x = 55+(index-1)*25 -- 1 +node1.visible = false -- 1 +local sprite1 = Sprite('Model/misc.clip|'..clip) -- 2 +sprite1.anchor = Vec2(0,sprite1.anchor.y) -- 2 +node1:addChild(sprite1) -- 2 +local show = Action(Sequence(Show(),Spawn(Opacity(0.5,0,1),Sequence(Scale(0.3,0,1.5,Ease.OutQuad),Scale(0.2,1.5,1,Ease.InQuad))),Delay(0.8),Opacity(0.3,1,0),Hide(),Event("DisplayEnd"))) -- 4 +node1:slot("Enter",function() -- 19 +node1:perform(show) -- 19 +end) -- 19 +return node1 -- 19 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/HPWheel.lua b/Assets/Script/Game/Loli War/UI/View/HPWheel.lua new file mode 100644 index 000000000..57ff3ea24 --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/HPWheel.lua @@ -0,0 +1,44 @@ +-- [xml]: Script/Game/Loli War/UI/View/HPWheel.xml +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local Digit = require("UI.Control.Digit") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local item1 = AlignNode{alignOffset = Vec2(75,75), vAlign = "Top", hAlign = "Left"} -- 5 +local node1 = Node() -- 6 +node1.scaleX = 1 -- 6 +node1.scaleY = 1 -- 6 +item1:addChild(node1) -- 6 +local hpShow = Playable("model:Model/hpshow") -- 7 +hpShow.scaleX = 2 -- 7 +hpShow.scaleY = 2 -- 7 +hpShow.look = "8" -- 7 +hpShow:play("idle",true) -- 7 +node1:addChild(hpShow) -- 7 +item1.hpShow = hpShow -- 7 +local energy = Node() -- 8 +energy.x = 75 -- 8 +energy.y = 20 -- 8 +energy.scaleX = 2 -- 8 +energy.scaleY = 2 -- 8 +node1:addChild(energy) -- 8 +item1.energy = energy -- 8 +local sprite1 = Sprite("Model/misc.clip|enegyframe") -- 9 +sprite1.anchor = Vec2(0,sprite1.anchor.y) -- 9 +energy:addChild(sprite1) -- 9 +local fill = Sprite("Model/misc.clip|enegyfill") -- 10 +fill.anchor = Vec2(0,fill.anchor.y) -- 10 +fill.x = 1 -- 10 +energy:addChild(fill) -- 10 +item1.fill = fill -- 10 +local sprite2 = Sprite("Model/misc.clip|vs") -- 11 +sprite2.anchor = Vec2(0,sprite2.anchor.y) -- 11 +sprite2.y = -20 -- 11 +energy:addChild(sprite2) -- 11 +local playerBlocks = Digit{y = -20, x = 20} -- 12 +energy:addChild(playerBlocks) -- 12 +item1.playerBlocks = playerBlocks -- 12 +local enemyBlocks = Digit{y = -20, x = 37} -- 13 +energy:addChild(enemyBlocks) -- 13 +item1.enemyBlocks = enemyBlocks -- 13 +return item1 -- 13 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/LeftTouchPad.lua b/Assets/Script/Game/Loli War/UI/View/LeftTouchPad.lua new file mode 100644 index 000000000..83af0dfb2 --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/LeftTouchPad.lua @@ -0,0 +1,30 @@ +-- [xml]: Script/Game/Loli War/UI/View/LeftTouchPad.xml +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local Button = require("UI.View.Button") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local pad = AlignNode{alignOffset = Vec2(20,60), vAlign = "Bottom", hAlign = "Left"} -- 5 +local menu1 = Menu() -- 6 +menu1.anchor = Vec2(0,0) -- 6 +menu1.scaleX = 2 * App.devicePixelRatio -- 6 +menu1.scaleY = 2 * App.devicePixelRatio -- 6 +menu1.size = Size(114,52) -- 6 +pad:addChild(menu1) -- 6 +local item1 = Button{height = 52, width = 52, imageDown = 'keyleft_down', imageUp = 'keyleft_up'} -- 7 +menu1:addChild(item1) -- 7 +item1:slot("TapBegan",function() -- 8 +pad:emit("KeyLeftDown") -- 8 +end) -- 8 +item1:slot("TapEnded",function() -- 9 +pad:emit("KeyLeftUp") -- 9 +end) -- 9 +local item2 = Button{width = 52, height = 52, x = 62, imageDown = 'keyright_down', imageUp = 'keyright_up'} -- 11 +menu1:addChild(item2) -- 11 +item2:slot("TapBegan",function() -- 12 +pad:emit("KeyRightDown") -- 12 +end) -- 12 +item2:slot("TapEnded",function() -- 13 +pad:emit("KeyRightUp") -- 13 +end) -- 13 +return pad -- 13 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/RestartPad.lua b/Assets/Script/Game/Loli War/UI/View/RestartPad.lua new file mode 100644 index 000000000..dc912e7b3 --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/RestartPad.lua @@ -0,0 +1,20 @@ +-- [xml]: Script/Game/Loli War/UI/View/RestartPad.xml +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local Button = require("UI.View.Button") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local pad = AlignNode{alignOffset = Vec2(20,20), vAlign = "Top", hAlign = "Right"} -- 5 +local menu = Menu() -- 6 +menu.anchor = Vec2(1,1) -- 6 +menu.scaleX = 2 -- 6 +menu.scaleY = 2 -- 6 +menu.size = Size(52,52) -- 6 +pad:addChild(menu) -- 6 +local item1 = Button{height = 52, width = 52, imageDown = 'esc_down', imageUp = 'esc_up'} -- 7 +menu:addChild(item1) -- 7 +item1:slot("Tapped",function() -- 8 +pad:emit("Tapped") -- 10 +Audio:play("Audio/choose.wav") -- 11 +end) -- 11 +return pad -- 11 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/RightTouchPad.lua b/Assets/Script/Game/Loli War/UI/View/RightTouchPad.lua new file mode 100644 index 000000000..3642fdb2a --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/RightTouchPad.lua @@ -0,0 +1,30 @@ +-- [xml]: Script/Game/Loli War/UI/View/RightTouchPad.xml +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local Button = require("UI.View.Button") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local pad = AlignNode{alignOffset = Vec2(20,60), vAlign = "Bottom", hAlign = "Right"} -- 5 +local menu1 = Menu() -- 6 +menu1.anchor = Vec2(1,0) -- 6 +menu1.scaleX = 2 * App.devicePixelRatio -- 6 +menu1.scaleY = 2 * App.devicePixelRatio -- 6 +menu1.size = Size(114,52) -- 6 +pad:addChild(menu1) -- 6 +local item1 = Button{height = 52, width = 52, imageDown = 'keyf_down', imageUp = 'keyf_up'} -- 7 +menu1:addChild(item1) -- 7 +item1:slot("TapBegan",function() -- 8 +pad:emit("KeyFDown") -- 8 +end) -- 8 +item1:slot("TapEnded",function() -- 9 +pad:emit("KeyFUp") -- 9 +end) -- 9 +local item2 = Button{width = 52, height = 52, x = 62, imageDown = 'keyup_down', imageUp = 'keyup_up'} -- 11 +menu1:addChild(item2) -- 11 +item2:slot("TapBegan",function() -- 12 +pad:emit("KeyUpDown") -- 12 +end) -- 12 +item2:slot("TapEnded",function() -- 13 +pad:emit("KeyUpUp") -- 13 +end) -- 13 +return pad -- 13 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/UI/View/StartPanel.lua b/Assets/Script/Game/Loli War/UI/View/StartPanel.lua new file mode 100644 index 000000000..5774657f4 --- /dev/null +++ b/Assets/Script/Game/Loli War/UI/View/StartPanel.lua @@ -0,0 +1,46 @@ +-- [xml]: Script/Game/Loli War/UI/View/StartPanel.xml +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local ButtonGlow = require("UI.Control.ButtonGlow") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local item1 = AlignNode{hAlign = 'Center', vAlign = 'Center'} -- 5 +local node1 = Node() -- 6 +node1.scaleX = 2 * App.devicePixelRatio -- 6 +node1.scaleY = 2 * App.devicePixelRatio -- 6 +item1:addChild(node1) -- 6 +local node = Node() -- 7 +node.touchEnabled = true -- 7 +node.swallowTouches = true -- 7 +node1:addChild(node) -- 7 +item1.node = node -- 7 +local sprite1 = Sprite("Model/misc.clip|startboard") -- 8 +node:addChild(sprite1) -- 8 +local scaleOut = Action(Sequence(Spawn(Scale(1,1,0.3,Ease.InBack),Opacity(1,1,0)),Event("PanelHide"))) -- 10 +local fButton = ButtonGlow{height = 167, width = 118, y = sprite1.height*0.5, x = sprite1.width/3-30, glow = 'weaponfl', normal = 'weaponf'} -- 18 +sprite1:addChild(fButton) -- 18 +item1.fButton = fButton -- 18 +fButton:slot("Tapped",function() -- 19 +node:perform(scaleOut) -- 19 +end) -- 19 +local vButton = ButtonGlow{height = 68, width = 82, y = sprite1.height*0.5, x = sprite1.width*0.5, glow = 'weaponvl', normal = 'weaponv'} -- 21 +sprite1:addChild(vButton) -- 21 +item1.vButton = vButton -- 21 +vButton:slot("Tapped",function() -- 22 +node:perform(scaleOut) -- 22 +end) -- 22 +local dButton = ButtonGlow{height = 97, width = 57, y = sprite1.height*0.5, x = sprite1.width-sprite1.width/3+30, glow = 'weapondl', normal = 'weapond'} -- 24 +sprite1:addChild(dButton) -- 24 +item1.dButton = dButton -- 24 +dButton:slot("Tapped",function() -- 25 +node:perform(scaleOut) -- 25 +end) -- 25 +local scaleIn = Action(Scale(1,0,1,Ease.OutBack)) -- 31 +local fadeIn = Sequence(Hide(),Delay(1),Show(),Opacity(0.5,0,1)) -- 32 +item1:slot("Enter",function() -- 39 +node:perform(scaleIn) -- 39 +fButton:perform(fadeIn) -- 41 +vButton:perform(fadeIn) -- 42 +dButton:perform(fadeIn) -- 43 +end) -- 43 +return item1 -- 43 +end \ No newline at end of file diff --git a/Assets/Script/Game/Loli War/Unit.lua b/Assets/Script/Game/Loli War/Unit.lua new file mode 100644 index 000000000..3b9bdd807 --- /dev/null +++ b/Assets/Script/Game/Loli War/Unit.lua @@ -0,0 +1,393 @@ +-- [yue]: Script/Game/Loli War/Unit.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Dictionary = dora.Dictionary -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local TargetAllow = _module_0.TargetAllow -- 1 +local Array = dora.Array -- 1 +local BodyDef = dora.BodyDef -- 1 +local Store = Data.store -- 3 +do -- 5 + local _with_0 = Dictionary() -- 5 + _with_0.linearAcceleration = Vec2(0, -10) -- 6 + _with_0.bodyType = "Dynamic" -- 7 + _with_0.scale = 2 -- 8 + _with_0.density = 1.0 -- 9 + _with_0.friction = 0.6 -- 10 + _with_0.restitution = 0.0 -- 11 + _with_0.playable = "model:Model/flandre" -- 12 + _with_0.size = Size(84, 186) -- 13 + _with_0.tag = "Hero" -- 14 + _with_0.sensity = 0 -- 15 + _with_0.move = 200 -- 16 + _with_0.jump = 780 -- 17 + _with_0.detectDistance = 300 -- 18 + _with_0.hp = 8.0 -- 19 + _with_0.attackBase = 1.0 -- 20 + _with_0.attackSpeed = 1.0 -- 21 + _with_0.attackDelay = 20.0 * 1.0 / 30.0 -- 22 + _with_0.attackEffectDelay = 0.0 -- 23 + _with_0.attackRange = Size(260 + 84 / 2, 200) -- 24 + _with_0.attackPower = Vec2(400, 400) -- 25 + _with_0.attackBonus = 0 -- 26 + _with_0.attackFactor = 1.0 -- 27 + _with_0.attackTarget = "Multi" -- 28 + do -- 29 + local conf -- 30 + do -- 30 + local _with_1 = TargetAllow() -- 30 + _with_1.terrainAllowed = false -- 31 + _with_1:allow("Enemy", true) -- 32 + conf = _with_1 -- 30 + end -- 30 + _with_0.targetAllow = conf:toValue() -- 33 + end -- 33 + _with_0.damageType = 0 -- 34 + _with_0.defenceType = 0 -- 35 + _with_0.bulletType = "" -- 36 + _with_0.attackEffect = "" -- 37 + _with_0.hitEffect = "Particle/blood.par" -- 38 + _with_0.sndAttack = "Audio/f_att.wav" -- 39 + _with_0.sndFallen = "" -- 40 + _with_0.decisionTree = "" -- 41 + _with_0.usePreciseHit = true -- 42 + _with_0.actions = Array({ -- 44 + "walk", -- 44 + "turn", -- 45 + "meleeAttack", -- 46 + "idle", -- 47 + "cancel", -- 48 + "jump", -- 49 + "hit", -- 50 + "fall", -- 51 + "fallOff", -- 52 + "pushSwitch", -- 53 + "strike", -- 54 + "wait" -- 55 + }) -- 43 + Store["Flandre"] = _with_0 -- 5 +end -- 5 +do -- 58 + local _with_0 = Dictionary() -- 58 + _with_0.linearAcceleration = Vec2(0, -10) -- 59 + _with_0.bodyType = "Dynamic" -- 60 + _with_0.scale = 2 -- 61 + _with_0.density = 1.0 -- 62 + _with_0.friction = 0.4 -- 63 + _with_0.restitution = 0.0 -- 64 + _with_0.playable = "model:Model/dorothy" -- 65 + _with_0.size = Size(84, 170) -- 66 + _with_0.tag = "Hero" -- 67 + _with_0.sensity = 0 -- 68 + _with_0.move = 150 -- 69 + _with_0.jump = 600 -- 70 + _with_0.detectDistance = 300 -- 71 + _with_0.hp = 8.0 -- 72 + _with_0.attackBase = 2.0 -- 73 + _with_0.attackSpeed = 1.0 -- 74 + _with_0.attackDelay = 18.0 * 1.0 / 30.0 -- 75 + _with_0.attackEffectDelay = 0.0 -- 76 + _with_0.attackRange = Size(500 + 84 / 2, 100) -- 77 + _with_0.attackPower = Vec2(300, 300) -- 78 + _with_0.attackBonus = 0 -- 79 + _with_0.attackFactor = 1.0 -- 80 + _with_0.attackTarget = "Single" -- 81 + do -- 82 + local conf -- 83 + do -- 83 + local _with_1 = TargetAllow() -- 83 + _with_1.terrainAllowed = true -- 84 + _with_1:allow("Enemy", true) -- 85 + conf = _with_1 -- 83 + end -- 83 + _with_0.targetAllow = conf:toValue() -- 86 + end -- 86 + _with_0.damageType = 0 -- 87 + _with_0.defenceType = 0 -- 88 + _with_0.bulletType = "Arrow" -- 89 + _with_0.attackEffect = "" -- 90 + _with_0.hitEffect = "Particle/blood.par" -- 91 + _with_0.sndAttack = "Audio/d_att.wav" -- 92 + _with_0.sndFallen = "" -- 93 + _with_0.decisionTree = "HeroAI" -- 94 + _with_0.usePreciseHit = true -- 95 + _with_0.actions = Array({ -- 97 + "walk", -- 97 + "turn", -- 98 + "rangeAttack", -- 99 + "idle", -- 100 + "cancel", -- 101 + "jump", -- 102 + "hit", -- 103 + "fall", -- 104 + "fallOff", -- 105 + "pushSwitch", -- 106 + "strike", -- 107 + "wait" -- 108 + }) -- 96 + Store["Dorothy"] = _with_0 -- 58 +end -- 58 +do -- 111 + local _with_0 = Dictionary() -- 111 + _with_0.linearAcceleration = Vec2(0, -10) -- 112 + _with_0.bodyType = "Dynamic" -- 113 + _with_0.scale = 2 -- 114 + _with_0.density = 1.0 -- 115 + _with_0.friction = 0.4 -- 116 + _with_0.restitution = 0.0 -- 117 + _with_0.playable = "model:Model/villy" -- 118 + _with_0.size = Size(84, 186) -- 119 + _with_0.tag = "Hero" -- 120 + _with_0.sensity = 0 -- 121 + _with_0.move = 240 -- 122 + _with_0.jump = 600 -- 123 + _with_0.detectDistance = 300 -- 124 + _with_0.hp = 8.0 -- 125 + _with_0.attackBase = 1.0 -- 126 + _with_0.attackSpeed = 1.0 -- 127 + _with_0.attackDelay = 0.0 -- 128 + _with_0.attackEffectDelay = 0.0 -- 129 + _with_0.attackRange = Size(300 + 84 / 2, 100) -- 130 + _with_0.attackPower = Vec2(200, 300) -- 131 + _with_0.attackBonus = 0 -- 132 + _with_0.attackFactor = 1.0 -- 133 + _with_0.attackTarget = "Single" -- 134 + do -- 135 + local conf -- 136 + do -- 136 + local _with_1 = TargetAllow() -- 136 + _with_1.terrainAllowed = true -- 137 + _with_1:allow("Enemy", true) -- 138 + conf = _with_1 -- 136 + end -- 136 + _with_0.targetAllow = conf:toValue() -- 139 + end -- 139 + _with_0.damageType = 0 -- 140 + _with_0.defenceType = 0 -- 141 + _with_0.bulletType = "Bubble" -- 142 + _with_0.attackEffect = "" -- 143 + _with_0.hitEffect = "Particle/blood.par" -- 144 + _with_0.sndAttack = "Audio/v_att.wav" -- 145 + _with_0.sndFallen = "" -- 146 + _with_0.decisionTree = "" -- 147 + _with_0.usePreciseHit = true -- 148 + _with_0.actions = Array({ -- 150 + "walk", -- 150 + "turn", -- 151 + "villyAttack", -- 152 + "idle", -- 153 + "cancel", -- 154 + "jump", -- 155 + "hit", -- 156 + "fall", -- 157 + "fallOff", -- 158 + "pushSwitch", -- 159 + "strike", -- 160 + "wait" -- 161 + }) -- 149 + Store["Villy"] = _with_0 -- 111 +end -- 111 +do -- 164 + local _with_0 = Dictionary() -- 164 + _with_0.linearAcceleration = Vec2(0, -10) -- 165 + _with_0.bodyType = "Dynamic" -- 166 + _with_0.scale = 2 -- 167 + _with_0.density = 1.0 -- 168 + _with_0.friction = 0.4 -- 169 + _with_0.restitution = 0.0 -- 170 + _with_0.playable = "model:Model/bunnyp" -- 171 + _with_0.size = Size(132, 128) -- 172 + _with_0.tag = "Bunny" -- 173 + _with_0.sensity = 0 -- 174 + _with_0.move = 150 -- 175 + _with_0.jump = 600 -- 176 + _with_0.detectDistance = 0 -- 177 + _with_0.hp = 3.0 -- 178 + _with_0.attackBase = 1.0 -- 179 + _with_0.attackSpeed = 1.0 -- 180 + _with_0.attackDelay = 20.0 * 1.0 / 60.0 -- 181 + _with_0.attackEffectDelay = 0.0 -- 182 + _with_0.attackRange = Size(60 + 132 / 2, 80) -- 183 + _with_0.attackPower = Vec2(400, 400) -- 184 + _with_0.attackBonus = 0 -- 185 + _with_0.attackFactor = 1.0 -- 186 + _with_0.attackTarget = "Single" -- 187 + do -- 188 + local conf -- 189 + do -- 189 + local _with_1 = TargetAllow() -- 189 + _with_1.terrainAllowed = false -- 190 + _with_1:allow("Enemy", true) -- 191 + conf = _with_1 -- 189 + end -- 189 + _with_0.targetAllow = conf:toValue() -- 192 + end -- 192 + _with_0.damageType = 0 -- 193 + _with_0.defenceType = 0 -- 194 + _with_0.bulletType = "" -- 195 + _with_0.attackEffect = "" -- 196 + _with_0.hitEffect = "Particle/blood.par" -- 197 + _with_0.sndAttack = "Audio/b_att.wav" -- 198 + _with_0.sndFallen = "" -- 199 + _with_0.decisionTree = "" -- 200 + _with_0.usePreciseHit = true -- 201 + _with_0.actions = Array({ -- 203 + "walk", -- 203 + "turn", -- 204 + "meleeAttack", -- 205 + "idle", -- 206 + "cancel", -- 207 + "jump", -- 208 + "hit", -- 209 + "fall", -- 210 + "fallOff", -- 211 + "pushSwitch", -- 212 + "strike", -- 213 + "wait" -- 214 + }) -- 202 + Store["BunnyP"] = _with_0 -- 164 +end -- 164 +do -- 217 + local _with_0 = Dictionary() -- 217 + _with_0.linearAcceleration = Vec2(0, -10) -- 218 + _with_0.bodyType = "Dynamic" -- 219 + _with_0.scale = 2 -- 220 + _with_0.density = 1.0 -- 221 + _with_0.friction = 0.4 -- 222 + _with_0.restitution = 0.0 -- 223 + _with_0.playable = "model:Model/bunnyg" -- 224 + _with_0.size = Size(132, 128) -- 225 + _with_0.tag = "Bunny" -- 226 + _with_0.sensity = 0 -- 227 + _with_0.move = 150 -- 228 + _with_0.jump = 600 -- 229 + _with_0.detectDistance = 0 -- 230 + _with_0.hp = 3.0 -- 231 + _with_0.attackBase = 1.0 -- 232 + _with_0.attackSpeed = 1.0 -- 233 + _with_0.attackDelay = 20.0 * 1.0 / 60.0 -- 234 + _with_0.attackEffectDelay = 0.0 -- 235 + _with_0.attackRange = Size(60 + 132 / 2, 80) -- 236 + _with_0.attackPower = Vec2(400, 400) -- 237 + _with_0.attackBonus = 0 -- 238 + _with_0.attackFactor = 1.0 -- 239 + _with_0.attackTarget = "Single" -- 240 + do -- 241 + local conf -- 242 + do -- 242 + local _with_1 = TargetAllow() -- 242 + _with_1.terrainAllowed = false -- 243 + _with_1:allow("Enemy", true) -- 244 + conf = _with_1 -- 242 + end -- 242 + _with_0.targetAllow = conf:toValue() -- 245 + end -- 245 + _with_0.damageType = 0 -- 246 + _with_0.defenceType = 0 -- 247 + _with_0.bulletType = "" -- 248 + _with_0.attackEffect = "" -- 249 + _with_0.hitEffect = "Particle/blood.par" -- 250 + _with_0.sndAttack = "Audio/b_att.wav" -- 251 + _with_0.sndFallen = "" -- 252 + _with_0.decisionTree = "" -- 253 + _with_0.usePreciseHit = true -- 254 + _with_0.actions = Array({ -- 256 + "walk", -- 256 + "turn", -- 257 + "meleeAttack", -- 258 + "idle", -- 259 + "cancel", -- 260 + "jump", -- 261 + "hit", -- 262 + "fall", -- 263 + "fallOff", -- 264 + "pushSwitch", -- 265 + "strike", -- 266 + "wait" -- 267 + }) -- 255 + Store["BunnyG"] = _with_0 -- 217 +end -- 217 +do -- 270 + local _with_0 = Dictionary() -- 270 + _with_0.scale = 2 -- 271 + _with_0.playable = "model:Model/block1" -- 272 + do -- 273 + local _with_1 = BodyDef() -- 273 + _with_1.linearAcceleration = Vec2(0, -10) -- 274 + _with_1.type = "Dynamic" -- 275 + _with_1:attachPolygon(90, 90, 1.0, 0.4, 0.0) -- 276 + _with_0.bodyDef = _with_1 -- 273 + end -- 273 + _with_0.tag = "Block" -- 277 + _with_0.hp = 2.0 -- 278 + _with_0.hitEffect = "Particle/heart.par" -- 279 + _with_0.actions = Array({ -- 280 + "hit" -- 280 + }) -- 280 + Store["BlockA"] = _with_0 -- 270 +end -- 270 +do -- 282 + local _with_0 = Dictionary() -- 282 + _with_0.scale = 2 -- 283 + _with_0.playable = "model:Model/block2" -- 284 + do -- 285 + local _with_1 = BodyDef() -- 285 + _with_1.linearAcceleration = Vec2(0, -10) -- 286 + _with_1.type = "Dynamic" -- 287 + _with_1:attachPolygon(90, 210, 0.5, 0.4, 0.0) -- 288 + _with_0.bodyDef = _with_1 -- 285 + end -- 285 + _with_0.tag = "Block" -- 289 + _with_0.hp = 3.0 -- 290 + _with_0.hitEffect = "Particle/heart.par" -- 291 + _with_0.actions = Array({ -- 292 + "hit" -- 292 + }) -- 292 + Store["BlockB"] = _with_0 -- 282 +end -- 282 +do -- 294 + local _with_0 = Dictionary() -- 294 + _with_0.scale = 2 -- 295 + _with_0.playable = "model:Model/block3" -- 296 + do -- 297 + local _with_1 = BodyDef() -- 297 + _with_1.linearAcceleration = Vec2(0, -10) -- 298 + _with_1.type = "Dynamic" -- 299 + _with_1:attachPolygon(450, 90, 0.5, 0.4, 0.0) -- 300 + _with_0.bodyDef = _with_1 -- 297 + end -- 297 + _with_0.tag = "Block" -- 301 + _with_0.hp = 4.0 -- 302 + _with_0.hitEffect = "Particle/heart.par" -- 303 + _with_0.actions = Array({ -- 304 + "hit" -- 304 + }) -- 304 + Store["BlockC"] = _with_0 -- 294 +end -- 294 +do -- 306 + local _with_0 = Dictionary() -- 306 + _with_0.bodyType = "Static" -- 307 + _with_0.playable = "model:Model/switch" -- 308 + _with_0.attackRange = Size(80, 126) -- 309 + _with_0.tag = "Switch" -- 310 + _with_0.decisionTree = "SwitchAI" -- 311 + _with_0.actions = Array({ -- 312 + "waitUser", -- 312 + "pushed" -- 312 + }) -- 312 + Store["Switch"] = _with_0 -- 306 +end -- 306 +do -- 314 + local _with_0 = Dictionary() -- 314 + _with_0.bodyType = "Static" -- 315 + _with_0.playable = "model:Model/switch" -- 316 + _with_0.attackRange = Size(80, 126) -- 317 + _with_0.tag = "Switch" -- 318 + _with_0.decisionTree = "SwitchAI" -- 319 + _with_0.actions = Array({ -- 320 + "waitUser", -- 320 + "pushed" -- 320 + }) -- 320 + Store["SwitchG"] = _with_0 -- 314 +end -- 314 diff --git a/Assets/Script/Game/Loli War/init.lua b/Assets/Script/Game/Loli War/init.lua new file mode 100644 index 000000000..e31092fa2 --- /dev/null +++ b/Assets/Script/Game/Loli War/init.lua @@ -0,0 +1,23 @@ +-- [yue]: Script/Game/Loli War/init.yue +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +do -- 3 + local scriptPath = Path:getScriptPath(...) -- 3 + if scriptPath then -- 3 + Content:insertSearchPath(1, scriptPath) -- 4 + local _list_0 = { -- 6 + "Constant", -- 6 + "Bullet", -- 7 + "Unit", -- 8 + "AI", -- 9 + "Action", -- 10 + "Logic", -- 11 + "Control", -- 12 + "Scene" -- 13 + } -- 5 + for _index_0 = 1, #_list_0 do -- 14 + local mod = _list_0[_index_0] -- 5 + require(Path(scriptPath, mod)) -- 5 + end -- 5 + end -- 3 +end -- 3 diff --git a/Assets/Script/Game/Touch the Sky/init.lua b/Assets/Script/Game/Touch the Sky/init.lua new file mode 100644 index 000000000..a86a6e9cf --- /dev/null +++ b/Assets/Script/Game/Touch the Sky/init.lua @@ -0,0 +1,788 @@ +-- [yue]: Script/Game/Touch the Sky/init.yue +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +local Vec2 = dora.Vec2 -- 1 +local Director = dora.Director -- 1 +local View = dora.View -- 1 +local Node = dora.Node -- 1 +local Audio = dora.Audio -- 1 +local PhysicsWorld = dora.PhysicsWorld -- 1 +local BodyDef = dora.BodyDef -- 1 +local Color = dora.Color -- 1 +local DrawNode = dora.DrawNode -- 1 +local Camera2D = dora.Camera2D -- 1 +local Body = dora.Body -- 1 +local Sprite = dora.Sprite -- 1 +local math = _G.math -- 1 +local tostring = _G.tostring -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local threadLoop = dora.threadLoop -- 1 +local App = dora.App -- 1 +local _module_0 = dora.ImGui -- 1 +local SetNextWindowBgAlpha = _module_0.SetNextWindowBgAlpha -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local Text = _module_0.Text -- 1 +local Separator = _module_0.Separator -- 1 +local TextWrapped = _module_0.TextWrapped -- 1 +local Checkbox = _module_0.Checkbox -- 1 +local scriptPath = Path:getScriptPath(...) -- 4 +Content:insertSearchPath(1, scriptPath) -- 5 +local gravity = Vec2(0, -10) -- 7 +local updateViewScale -- 9 +updateViewScale = function() -- 9 + Director.currentCamera.zoom = View.size.width / 1620 -- 10 +end -- 9 +local root -- 11 +do -- 11 + local _with_0 = Node() -- 11 + _with_0:gslot("AppSizeChanged", updateViewScale) -- 12 + root = _with_0 -- 11 +end -- 11 +local ui -- 14 +do -- 14 + local _with_0 = Node() -- 14 + _with_0:addTo(Director.ui) -- 15 + ui = _with_0 -- 14 +end -- 14 +local heartNode = nil -- 16 +local restartButton = nil -- 17 +Audio:playStream("sfx/bgm.ogg", true, 0.2) -- 18 +local world -- 20 +do -- 20 + local _with_0 = PhysicsWorld() -- 20 + _with_0:setShouldContact(0, 1, true) -- 21 + _with_0.showDebug = false -- 22 + _with_0:addTo(root) -- 23 + world = _with_0 -- 20 +end -- 20 +local restartGame = nil -- 25 +local cube = nil -- 26 +local touchTheSky = false -- 27 +local isInvincible = false -- 28 +local hardMode = false -- 29 +local getCubeDef -- 31 +getCubeDef = function(width, height) -- 31 + local _with_0 = BodyDef() -- 31 + _with_0.type = "Dynamic" -- 32 + _with_0.linearAcceleration = gravity -- 33 + _with_0.angularDamping = 1.8 -- 34 + _with_0:attachPolygon(width, height, 0.4, 0.4, 0.4) -- 36 + return _with_0 -- 31 +end -- 31 +local getBlockDef -- 38 +getBlockDef = function(width, height) -- 38 + local _with_0 = BodyDef() -- 38 + _with_0.type = "Static" -- 39 + _with_0.linearAcceleration = gravity -- 40 + _with_0:attachPolygon(width, height, 0.4, 0.4, 0.4) -- 41 + return _with_0 -- 38 +end -- 38 +local colorWhite = Color(0xccffffff) -- 43 +local colorRed = Color(0xaae65100) -- 44 +local colorBlue = Color(0xaa00b0ff) -- 45 +local borderHeight = 19000 -- 47 +local borderWidth = 1600 -- 48 +local borderPos = Vec2(0, 0) -- 49 +local springForce = 1000 -- 51 +local heart = 3 -- 52 +local cubeInitPos = Vec2(0, 100) -- 53 +local borderDef -- 55 +do -- 55 + local _with_0 = BodyDef() -- 55 + _with_0.type = "Static" -- 56 + _with_0:attachPolygon(borderPos, borderWidth, 10, 0, 1, 1, 0) -- 57 + _with_0:attachPolygon(Vec2(borderPos.x + borderWidth / 2, borderPos.y + borderHeight / 2), 10, borderHeight, 0, 1, 1, 0) -- 58 + _with_0:attachPolygon(Vec2(borderPos.x - borderWidth / 2, borderPos.y + borderHeight / 2), 10, borderHeight, 0, 1, 1, 0) -- 59 + borderDef = _with_0 -- 55 +end -- 55 +local destinationDef -- 61 +do -- 61 + local _with_0 = BodyDef() -- 61 + _with_0:attachPolygonSensor(1, borderWidth, 10) -- 62 + destinationDef = _with_0 -- 61 +end -- 61 +local blockLevel = { -- 64 + 300, -- 64 + 5200, -- 64 + 9800, -- 64 + 14400 -- 64 +} -- 64 +local blocks1 = { -- 67 + { -- 67 + 200, -- 67 + 200, -- 67 + Vec2(-700, 0), -- 67 + 2 -- 67 + }, -- 67 + { -- 68 + 1000, -- 68 + 200, -- 68 + Vec2(300, 400), -- 68 + 0 -- 68 + }, -- 68 + { -- 69 + 200, -- 69 + 200, -- 69 + Vec2(500, 800), -- 69 + 1 -- 69 + }, -- 69 + { -- 70 + 600, -- 70 + 200, -- 70 + Vec2(-500, 1000), -- 70 + 2 -- 70 + }, -- 70 + { -- 71 + 200, -- 71 + 200, -- 71 + Vec2(-300, 1400), -- 71 + 0 -- 71 + }, -- 71 + { -- 72 + 200, -- 72 + 200, -- 72 + Vec2(-100, 1600), -- 72 + 0 -- 72 + }, -- 72 + { -- 73 + 200, -- 73 + 200, -- 73 + Vec2(-300, 2000), -- 73 + 2 -- 73 + }, -- 73 + { -- 74 + 200, -- 74 + 200, -- 74 + Vec2(-500, 2200), -- 74 + 2 -- 74 + }, -- 74 + { -- 75 + 200, -- 75 + 200, -- 75 + Vec2(500, 2200), -- 75 + 0 -- 75 + }, -- 75 + { -- 76 + 200, -- 76 + 200, -- 76 + Vec2(300, 2400), -- 76 + 0 -- 76 + }, -- 76 + { -- 77 + 200, -- 77 + 200, -- 77 + Vec2(500, 2600), -- 77 + 1 -- 77 + }, -- 77 + { -- 78 + 200, -- 78 + 200, -- 78 + Vec2(-500, 2800), -- 78 + 0 -- 78 + }, -- 78 + { -- 79 + 200, -- 79 + 200, -- 79 + Vec2(-300, 3000), -- 79 + 0 -- 79 + }, -- 79 + { -- 80 + 200, -- 80 + 400, -- 80 + Vec2(700, 3100), -- 80 + 0 -- 80 + }, -- 80 + { -- 81 + 1200, -- 81 + 200, -- 81 + Vec2(-200, 3600), -- 81 + 2 -- 81 + } -- 81 +} -- 66 +local blocks2 = { -- 85 + { -- 85 + 200, -- 85 + 1000, -- 85 + Vec2(-300, 0), -- 85 + 2 -- 85 + }, -- 85 + { -- 86 + 200, -- 86 + 200, -- 86 + Vec2(300, -200), -- 86 + 1 -- 86 + }, -- 86 + { -- 87 + 200, -- 87 + 1000, -- 87 + Vec2(300, 800), -- 87 + 2 -- 87 + }, -- 87 + { -- 88 + 200, -- 88 + 200, -- 88 + Vec2(300, 1400), -- 88 + 1 -- 88 + }, -- 88 + { -- 89 + 200, -- 89 + 1600, -- 89 + Vec2(-300, 1500), -- 89 + 2 -- 89 + }, -- 89 + { -- 90 + 200, -- 90 + 200, -- 90 + Vec2(-500, 2400), -- 90 + 1 -- 90 + }, -- 90 + { -- 91 + 200, -- 91 + 1200, -- 91 + Vec2(500, 2500), -- 91 + 0 -- 91 + }, -- 91 + { -- 92 + 200, -- 92 + 200, -- 92 + Vec2(-700, 3000), -- 92 + 2 -- 92 + }, -- 92 + { -- 93 + 200, -- 93 + 200, -- 93 + Vec2(-500, 3200), -- 93 + 2 -- 93 + }, -- 93 + { -- 94 + 200, -- 94 + 200, -- 94 + Vec2(-300, 3400), -- 94 + 2 -- 94 + }, -- 94 + { -- 95 + 800, -- 95 + 200, -- 95 + Vec2(400, 4000), -- 95 + 0 -- 95 + } -- 95 +} -- 84 +local blocks3 = { -- 99 + { -- 99 + 200, -- 99 + 4000, -- 99 + Vec2(-700, 1900), -- 99 + 2 -- 99 + }, -- 99 + { -- 100 + 200, -- 100 + 200, -- 100 + Vec2(-100, 400), -- 100 + 2 -- 100 + }, -- 100 + { -- 101 + 200, -- 101 + 200, -- 101 + Vec2(100, 600), -- 101 + 1 -- 101 + }, -- 101 + { -- 102 + 200, -- 102 + 200, -- 102 + Vec2(300, 1200), -- 102 + 1 -- 102 + }, -- 102 + { -- 103 + 200, -- 103 + 200, -- 103 + Vec2(100, 1400), -- 103 + 1 -- 103 + }, -- 103 + { -- 104 + 200, -- 104 + 200, -- 104 + Vec2(300, 1800), -- 104 + 1 -- 104 + }, -- 104 + { -- 105 + 400, -- 105 + 200, -- 105 + Vec2(600, 2000), -- 105 + 0 -- 105 + }, -- 105 + { -- 106 + 200, -- 106 + 200, -- 106 + Vec2(100, 3000), -- 106 + 1 -- 106 + }, -- 106 + { -- 107 + 200, -- 107 + 200, -- 107 + Vec2(300, 3200), -- 107 + 1 -- 107 + }, -- 107 + { -- 108 + 200, -- 108 + 200, -- 108 + Vec2(300, 3400), -- 108 + 2 -- 108 + }, -- 108 + { -- 109 + 200, -- 109 + 200, -- 109 + Vec2(100, 3600), -- 109 + 1 -- 109 + }, -- 109 + { -- 110 + 200, -- 110 + 200, -- 110 + Vec2(-100, 3800), -- 110 + 1 -- 110 + }, -- 110 + { -- 111 + 400, -- 111 + 200, -- 111 + Vec2(0, 4000), -- 111 + 0 -- 111 + } -- 111 +} -- 98 +local blocks4 = { -- 115 + { -- 115 + 200, -- 115 + 200, -- 115 + Vec2(-300, 0), -- 115 + 1 -- 115 + }, -- 115 + { -- 116 + 200, -- 116 + 200, -- 116 + Vec2(300, 0), -- 116 + 1 -- 116 + }, -- 116 + { -- 117 + 200, -- 117 + 200, -- 117 + Vec2(-500, 600), -- 117 + 1 -- 117 + }, -- 117 + { -- 118 + 200, -- 118 + 200, -- 118 + Vec2(100, 600), -- 118 + 1 -- 118 + }, -- 118 + { -- 119 + 200, -- 119 + 200, -- 119 + Vec2(700, 600), -- 119 + 1 -- 119 + }, -- 119 + { -- 120 + 600, -- 120 + 200, -- 120 + Vec2(0, 1200), -- 120 + 0 -- 120 + }, -- 120 + { -- 121 + 200, -- 121 + 600, -- 121 + Vec2(700, 1400), -- 121 + 2 -- 121 + }, -- 121 + { -- 122 + 200, -- 122 + 1000, -- 122 + Vec2(-700, 1800), -- 122 + 2 -- 122 + }, -- 122 + { -- 123 + 200, -- 123 + 600, -- 123 + Vec2(-100, 2200), -- 123 + 2 -- 123 + }, -- 123 + { -- 124 + 200, -- 124 + 600, -- 124 + Vec2(500, 2400), -- 124 + 1 -- 124 + }, -- 124 + { -- 125 + 200, -- 125 + 200, -- 125 + Vec2(100, 2800), -- 125 + 2 -- 125 + }, -- 125 + { -- 126 + 200, -- 126 + 200, -- 126 + Vec2(300, 3000), -- 126 + 2 -- 126 + }, -- 126 + { -- 127 + 200, -- 127 + 200, -- 127 + Vec2(-300, 3400), -- 127 + 2 -- 127 + }, -- 127 + { -- 128 + 200, -- 128 + 1200, -- 128 + Vec2(500, 3700), -- 128 + 2 -- 128 + }, -- 128 + { -- 129 + 200, -- 129 + 800, -- 129 + Vec2(-500, 3900), -- 129 + 2 -- 129 + } -- 129 +} -- 114 +local blockTypes = { -- 133 + blocks1, -- 133 + blocks2, -- 134 + blocks3, -- 135 + blocks4 -- 136 +} -- 132 +local blockBodies = { } -- 138 +local ropeNode -- 140 +do -- 140 + local _with_0 = DrawNode() -- 140 + _with_0:addTo(root) -- 141 + ropeNode = _with_0 -- 140 +end -- 140 +local isGrabbing = false -- 142 +local isGrabbed = false -- 143 +local grabBlock = Node() -- 144 +local emitRope -- 146 +emitRope = function(cubeBody, endPoint) -- 146 + local startPoint = cubeBody.position -- 147 + local isBlock = false -- 148 + local isSelf = true -- 149 + local grabPoint = endPoint -- 150 + Audio:play("sfx/slime_touch.wav") -- 151 + world:raycast(startPoint, endPoint, false, function(_body, point) -- 152 + if isBlock then -- 153 + isGrabbed = true -- 154 + grabPoint = point -- 155 + isBlock = false -- 156 + end -- 153 + if isSelf then -- 157 + isSelf = false -- 158 + isBlock = true -- 159 + end -- 157 + end) -- 152 + ropeNode:schedule(function() -- 161 + ropeNode:clear() -- 162 + if not hardMode or isGrabbed then -- 163 + return ropeNode:drawSegment(cubeBody.position, grabPoint, 10, Color(0xaaffffff)) -- 170 + end -- 163 + end) -- 161 + return ropeNode -- 160 +end -- 146 +local getGrabForce -- 172 +getGrabForce = function(body, target) -- 172 + local prePos = body.position -- 173 + local force = target - prePos -- 174 + force = force:normalize() * 30 -- 175 + return force -- 176 +end -- 172 +local camera = Camera2D() -- 178 +Director:pushCamera(camera) -- 179 +updateViewScale() -- 180 +local cameraFollow -- 182 +cameraFollow = function(body) -- 182 + local _with_0 = Node() -- 183 + _with_0:schedule(function() -- 184 + camera.position = Vec2(0, (body.position.y - camera.position.y) * 0.1 + camera.position.y + 30) -- 185 + end) -- 184 + return _with_0 -- 183 +end -- 182 +local addCube -- 187 +addCube = function() -- 187 + do -- 188 + local _with_0 = Node() -- 188 + _with_0:addTo(world) -- 189 + cube = _with_0 -- 188 + end -- 188 + local scale = 0.5 -- 190 + local cubebody -- 191 + do -- 191 + local _with_0 = Body(getCubeDef(256 * scale, 256 * scale), world, cubeInitPos) -- 191 + _with_0.receivingContact = true -- 192 + _with_0.tag = "cubeBody" -- 193 + _with_0.group = 0 -- 194 + _with_0.angularRate = 0 -- 195 + _with_0.velocity = Vec2.zero -- 196 + _with_0:addChild((function() -- 197 + local _with_1 = Sprite("Image/cube.png") -- 197 + _with_1.scaleX = scale -- 198 + _with_1.scaleY = scale -- 198 + return _with_1 -- 197 + end)()) -- 197 + _with_0:addTo(cube) -- 199 + cubebody = _with_0 -- 191 + end -- 191 + return cameraFollow(cubebody) -- 200 +end -- 187 +local addHeartUI -- 202 +addHeartUI = function() -- 202 + do -- 203 + local _with_0 = Node() -- 203 + _with_0:addChild((function() -- 204 + local _with_1 = Sprite("Image/heart_3.png") -- 204 + _with_1.tag = "heartSprite" -- 205 + _with_1.y = View.size.height / 2 - 100 -- 206 + _with_1.scaleX = 6 -- 207 + _with_1.scaleY = 6 -- 207 + return _with_1 -- 204 + end)()) -- 204 + heartNode = _with_0 -- 203 + end -- 203 + return ui:addChild(heartNode) -- 208 +end -- 202 +local loseHeart -- 210 +loseHeart = function() -- 210 + heart = heart - 1 -- 211 + heartNode:removeAllChildren() -- 212 + local heartSprite -- 213 + if 0 <= heart and heart <= 3 then -- 213 + heartSprite = Sprite("Image/heart_" .. tostring(math.tointeger(heart)) .. ".png") -- 214 + else -- 216 + heartSprite = Sprite("Image/heart_0.png") -- 216 + end -- 213 + return heartNode:addChild((function() -- 217 + heartSprite.tag = "heartSprite" -- 218 + heartSprite.y = View.size.height / 2 - 100 -- 219 + heartSprite.scaleX = 6 -- 220 + heartSprite.scaleY = 6 -- 220 + return heartSprite -- 217 + end)()) -- 220 +end -- 210 +local arriveDest -- 222 +arriveDest = function() -- 222 + touchTheSky = true -- 223 + Audio:play("sfx/sky3.mp3") -- 224 + Audio:playStream("sfx/victory.ogg", true, 0.2) -- 225 + local body = cube:getChildByTag("cubeBody") -- 226 + body:applyLinearImpulse(Vec2(0, 1500), body.position) -- 227 + do -- 228 + local _with_0 = Node() -- 228 + _with_0:addChild((function() -- 229 + local _with_1 = Sprite("Image/restart.png") -- 229 + _with_1.scaleX = 2 -- 230 + _with_1.scaleY = 2 -- 230 + _with_1.touchEnabled = true -- 231 + _with_1:slot("TapBegan", function() -- 232 + restartButton:removeFromParent() -- 233 + return restartGame() -- 234 + end) -- 232 + return _with_1 -- 229 + end)()) -- 229 + _with_0:addTo(ui) -- 235 + restartButton = _with_0 -- 228 + end -- 228 +end -- 222 +local buildBlocks -- 237 +buildBlocks = function(index) -- 237 + local blocks = blockTypes[index] -- 238 + for _index_0 = 1, #blocks do -- 239 + local block = blocks[_index_0] -- 239 + local width, height, pos, blockType = block[1], block[2], block[3], block[4] -- 240 + pos = pos + Vec2(0, blockLevel[index]) -- 241 + do -- 242 + local _with_0 = Body(getBlockDef(width, height), world, pos) -- 242 + _with_0.group = 1 -- 243 + local blockColor = Color(0xffffffff) -- 244 + _with_0:attachSensor(1, BodyDef:polygon(width + 15, height + 15)) -- 245 + if 0 == blockType then -- 246 + _with_0:slot("BodyEnter", function() -- 247 + return Audio:play("sfx/strike.wav") -- 248 + end) -- 247 + blockColor = colorWhite -- 249 + elseif 1 == blockType then -- 250 + _with_0:slot("BodyEnter", function(body) -- 251 + if not isInvincible then -- 252 + loseHeart() -- 253 + body:applyLinearImpulse(Vec2(math.random(-1000, 1000), math.random(-1000, -500)), body.position) -- 254 + Audio:play("sfx/explode2.wav") -- 256 + end -- 252 + _with_0:schedule(once(function() -- 257 + isInvincible = true -- 258 + sleep(1) -- 259 + isInvincible = false -- 260 + end)) -- 257 + if heart <= 0 then -- 261 + isGrabbing = false -- 262 + isGrabbed = false -- 263 + grabBlock:unschedule() -- 264 + ropeNode:clear() -- 265 + ropeNode:unschedule() -- 266 + Audio:play("sfx/game_over.wav") -- 267 + return _with_0:schedule(once(function() -- 268 + sleep(0.5) -- 269 + return restartGame() -- 270 + end)) -- 270 + end -- 261 + end) -- 251 + blockColor = colorRed -- 271 + _with_0:addChild((function() -- 272 + local _with_1 = Sprite("Image/red.png") -- 272 + _with_1.scaleX = 0.15 -- 273 + _with_1.scaleY = 0.15 -- 273 + return _with_1 -- 272 + end)()) -- 272 + elseif 2 == blockType then -- 274 + blockColor = colorBlue -- 275 + _with_0:addChild((function() -- 276 + local _with_1 = Sprite("Image/spring.png") -- 276 + _with_1.scaleX = 0.15 -- 277 + _with_1.scaleY = 0.15 -- 277 + return _with_1 -- 276 + end)()) -- 276 + local implulseAvailable = true -- 278 + _with_0:slot("BodyEnter", function(body) -- 279 + if not implulseAvailable then -- 280 + return -- 280 + end -- 280 + Audio:play("sfx/rebound.wav") -- 281 + body:applyLinearImpulse(Vec2(0, springForce), body.position) -- 282 + implulseAvailable = false -- 283 + return _with_0:schedule(once(function() -- 284 + sleep(0.2) -- 285 + implulseAvailable = true -- 286 + end)) -- 286 + end) -- 279 + end -- 286 + _with_0:addChild((function() -- 287 + local _with_1 = DrawNode() -- 287 + local verts = { -- 289 + Vec2(-width / 2, height / 2), -- 289 + Vec2(width / 2, height / 2), -- 290 + Vec2(width / 2, -height / 2), -- 291 + Vec2(-width / 2, -height / 2) -- 292 + } -- 288 + _with_1:drawPolygon(verts, blockColor) -- 294 + return _with_1 -- 287 + end)()) -- 287 + _with_0:addTo(world) -- 295 + blockBodies[#blockBodies + 1] = _with_0 -- 242 + end -- 242 + end -- 295 +end -- 237 +local buildWorld -- 297 +buildWorld = function() -- 297 + buildBlocks(1) -- 298 + buildBlocks(2) -- 299 + buildBlocks(3) -- 300 + buildBlocks(4) -- 301 + do -- 302 + local _with_0 = Body(destinationDef, world, Vec2(borderPos.x, borderPos.y + borderHeight)) -- 302 + _with_0.group = 1 -- 303 + _with_0:addChild((function() -- 304 + local _with_1 = Sprite("Image/sky.png") -- 304 + _with_1.x = 90 -- 305 + _with_1.y = 100 -- 306 + _with_1.scaleX = 2.7 -- 307 + _with_1.scaleY = 2.7 -- 307 + return _with_1 -- 304 + end)()) -- 304 + _with_0:slot("BodyEnter", function() -- 308 + if not touchTheSky then -- 309 + return arriveDest() -- 309 + end -- 309 + end) -- 308 + _with_0:addTo(world) -- 310 + end -- 302 + do -- 311 + local _with_0 = Body(borderDef, world, Vec2.zero) -- 311 + _with_0.group = 1 -- 312 + _with_0:addChild((function() -- 313 + local _with_1 = DrawNode() -- 313 + _with_1:drawSegment(Vec2(-borderWidth / 2, 0), Vec2(borderWidth / 2, 0), 10, colorWhite) -- 314 + _with_1:drawSegment(Vec2(-borderWidth / 2, 0), Vec2(-borderWidth / 2, borderHeight), 10, colorWhite) -- 315 + _with_1:drawSegment(Vec2(borderWidth / 2, 0), Vec2(borderWidth / 2, borderHeight), 10, colorWhite) -- 316 + return _with_1 -- 313 + end)()) -- 313 + _with_0:addTo(world) -- 317 + end -- 311 + local _with_0 = Node() -- 318 + _with_0.touchEnabled = true -- 319 + _with_0:slot("TapBegan", function(touch) -- 320 + isGrabbing = true -- 321 + if not touch.first then -- 322 + return -- 322 + end -- 322 + local location = touch.location -- 323 + local body = cube:getChildByTag("cubeBody") -- 324 + emitRope(body, location) -- 325 + if not hardMode or isGrabbed then -- 326 + grabBlock:schedule(function() -- 328 + return body:applyLinearImpulse(getGrabForce(body, location), body.position) -- 329 + end) -- 328 + return grabBlock -- 327 + end -- 326 + end) -- 320 + _with_0:slot("TapEnded", function() -- 330 + isGrabbing = false -- 331 + isGrabbed = false -- 332 + grabBlock:unschedule() -- 333 + ropeNode:clear() -- 334 + return ropeNode:unschedule() -- 335 + end) -- 330 + _with_0:addTo(root) -- 336 + return _with_0 -- 318 +end -- 297 +addCube() -- 338 +buildWorld() -- 339 +addHeartUI() -- 340 +restartGame = function() -- 342 + if touchTheSky then -- 343 + Audio:playStream("sfx/bgm.ogg", true, 0.2) -- 344 + end -- 343 + heart = 3 -- 345 + touchTheSky = false -- 346 + local body = cube:getChildByTag("cubeBody") -- 347 + body.position = Vec2.zero -- 348 + body.angularRate = 0 -- 349 + ui:removeFromParent() -- 350 + do -- 351 + local _with_0 = Node() -- 351 + _with_0:addTo(Director.ui) -- 352 + heartNode:removeFromParent() -- 353 + ui = _with_0 -- 351 + end -- 351 + return addHeartUI() -- 354 +end -- 342 +local windowFlags = { -- 357 + "NoDecoration", -- 357 + "NoSavedSettings", -- 358 + "NoFocusOnAppearing", -- 359 + "NoNav", -- 360 + "NoMove" -- 361 +} -- 356 +return threadLoop(function() -- 362 + local width, height -- 363 + do -- 363 + local _obj_0 = App.visualSize -- 363 + width, height = _obj_0.width, _obj_0.height -- 363 + end -- 363 + SetNextWindowBgAlpha(0.35) -- 364 + SetNextWindowPos(Vec2(width - 140, height - 170), "Always", Vec2.zero) -- 365 + SetNextWindowSize(Vec2(140, 0), "Always") -- 366 + return Begin("Touch The Sky", windowFlags, function() -- 367 + Text("Touch The Sky") -- 368 + Separator() -- 369 + TextWrapped("Click to grab!") -- 370 + do -- 371 + local changed, isHardMode = Checkbox("Hard Mode", hardMode) -- 371 + if changed then -- 371 + hardMode = isHardMode -- 372 + end -- 371 + end -- 371 + end) -- 372 +end) -- 372 diff --git a/Assets/Script/Game/Zombie Escape/AI.lua b/Assets/Script/Game/Zombie Escape/AI.lua new file mode 100644 index 000000000..989bab0a0 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/AI.lua @@ -0,0 +1,311 @@ +-- [yue]: Script/Game/Zombie Escape/AI.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local _module_1 = dora.Platformer.Decision -- 1 +local Sel = _module_1.Sel -- 1 +local Seq = _module_1.Seq -- 1 +local Con = _module_1.Con -- 1 +local Unit = _module_0.Unit -- 1 +local Vec2 = dora.Vec2 -- 1 +local Act = _module_1.Act -- 1 +local Reject = _module_1.Reject -- 1 +local math = _G.math -- 1 +local Behave = _module_1.Behave -- 1 +local AI = _module_1.AI -- 1 +local App = dora.App -- 1 +local Accept = _module_1.Accept -- 1 +local Group = dora.Group -- 1 +local BT = require("Platformer").Behavior -- 5 +local Store = Data.store -- 7 +local rangeAttack = Sel({ -- 10 + Seq({ -- 11 + Con("attack path blocked", function(self) -- 11 + local sensor = self:getSensorByTag(Unit.AttackSensorTag) -- 12 + if sensor.sensedBodies:each(function(body) -- 13 + return body.group == Data.groupTerrain and (self.x > body.x) ~= self.faceRight and body.tag == "Obstacle" -- 16 + end) then -- 13 + local faceObstacle = true -- 17 + local start = self.position -- 18 + local stop = Vec2(start.x + (self.faceRight and 1 or -1) * self.unitDef.attackRange.width, start.y) -- 19 + Store.world:raycast(start, stop, true, function(b) -- 20 + if b.group == Data.groupDetection then -- 21 + return false -- 21 + end -- 21 + if Data:isEnemy(self, b) then -- 22 + faceObstacle = false -- 22 + end -- 22 + return true -- 23 + end) -- 20 + return faceObstacle -- 24 + else -- 25 + return false -- 25 + end -- 13 + end), -- 11 + Act("jump"), -- 26 + Reject() -- 27 + }), -- 10 + Act("rangeAttack") -- 29 +}) -- 9 +local walk = Sel({ -- 33 + Seq({ -- 34 + Con("obstacles ahead", function(self) -- 34 + local start = self.position -- 35 + local stop = Vec2(start.x + (self.faceRight and 140 or -140), start.y) -- 36 + return Store.world:raycast(start, stop, false, function(b, p) -- 37 + if b.group == Data.groupTerrain and b.tag == "Obstacle" then -- 38 + self.data.obstacleDistance = math.abs(p.x - start.x) -- 39 + return true -- 40 + else -- 41 + return false -- 41 + end -- 38 + end) -- 41 + end), -- 34 + Sel({ -- 43 + Seq({ -- 44 + Con("obstacle distance <= 80", function(self) -- 44 + return self.data.obstacleDistance <= 80 -- 44 + end), -- 44 + Behave("backJump", BT.Seq({ -- 46 + BT.Act("turn"), -- 46 + BT.Countdown(0.3, BT.Act("walk")), -- 47 + BT.Act("turn"), -- 48 + BT.Countdown(0.1, BT.Act("walk")), -- 49 + BT.Act("jump") -- 50 + })) -- 45 + }), -- 43 + Seq({ -- 54 + Con("has forward speed", function(self) -- 54 + return math.abs(self.velocityX) > 0 -- 54 + end), -- 54 + Act("jump") -- 55 + }) -- 53 + }) -- 42 + }), -- 33 + Act("walk") -- 59 +}) -- 32 +local fightDecision = Seq({ -- 63 + Con("see enemy", function(self) -- 63 + return (AI:getNearestUnit("Enemy") ~= nil) -- 63 + end), -- 63 + Sel({ -- 65 + Seq({ -- 66 + Con("need evade", function(self) -- 66 + if not self:getAction("rangeAttack" or not self.onSurface) then -- 67 + return false -- 67 + end -- 67 + local evadeLeftEnemy = false -- 68 + local evadeRightEnemy = false -- 69 + local sensor = self:getSensorByTag(Unit.AttackSensorTag) -- 70 + sensor.sensedBodies:each(function(body) -- 71 + if Data:isEnemy(self, body) then -- 72 + local distance = math.abs(self.x - body.x) -- 73 + if distance < 80 then -- 74 + evadeRightEnemy = false -- 75 + evadeLeftEnemy = false -- 76 + return true -- 77 + elseif distance < 200 then -- 78 + if body.x > self.x then -- 79 + evadeRightEnemy = true -- 79 + end -- 79 + if body.x <= self.x then -- 80 + evadeLeftEnemy = true -- 80 + end -- 80 + end -- 74 + end -- 72 + end) -- 71 + local needEvade = not (evadeLeftEnemy == evadeRightEnemy) and math.abs(self.x) < 1000 -- 81 + if needEvade then -- 82 + self.data.evadeRight = evadeRightEnemy -- 82 + end -- 82 + return needEvade -- 83 + end), -- 66 + Sel({ -- 85 + Seq({ -- 86 + Con("face enemy", function(self) -- 86 + return self.data.evadeRight == self.faceRight -- 86 + end), -- 86 + Act("turn"), -- 87 + walk -- 88 + }), -- 85 + walk -- 90 + }) -- 84 + }), -- 65 + Seq({ -- 94 + Con("not facing nearest enemy", function(self) -- 94 + local enemy = AI:getNearestUnit("Enemy") -- 95 + return (self.x > enemy.x) == self.faceRight -- 96 + end), -- 94 + Act("turn") -- 97 + }), -- 93 + Seq({ -- 100 + Con("enemy in attack range", function(self) -- 100 + local enemy = AI:getNearestUnit("Enemy") -- 101 + local attackUnits = AI:getUnitsInAttackRange() -- 102 + return attackUnits and attackUnits:contains(enemy) or false -- 103 + end), -- 100 + Sel({ -- 105 + rangeAttack, -- 105 + Act("meleeAttack") -- 106 + }) -- 104 + }), -- 99 + Seq({ -- 110 + Con("wanna jump", function(self) -- 110 + return App.rand % 5 == 0 -- 110 + end), -- 110 + Act("jump") -- 111 + }), -- 109 + walk -- 113 + }) -- 64 +}) -- 62 +Store["AI_Zombie"] = Sel({ -- 118 + Seq({ -- 119 + Con("is dead", function(self) -- 119 + return self.entity.hp <= 0 -- 119 + end), -- 119 + Accept() -- 120 + }), -- 118 + Seq({ -- 123 + Con("not entered", function(self) -- 123 + return not self.data.entered -- 123 + end), -- 123 + Act("groundEntrance") -- 124 + }), -- 122 + fightDecision, -- 126 + Seq({ -- 128 + Con("need stop", function(self) -- 128 + return not self:isDoing("idle") -- 128 + end), -- 128 + Act("cancel"), -- 129 + Act("idle") -- 130 + }) -- 127 +}) -- 117 +local playerGroup = Group({ -- 134 + "player" -- 134 +}) -- 134 +Store["AI_KidFollow"] = Sel({ -- 137 + Seq({ -- 138 + Con("is dead", function(self) -- 138 + return self.entity.hp <= 0 -- 138 + end), -- 138 + Accept() -- 139 + }), -- 137 + fightDecision, -- 141 + Seq({ -- 143 + Con("is falling", function(self) -- 143 + return not self.onSurface -- 143 + end), -- 143 + Act("fallOff") -- 144 + }), -- 142 + Seq({ -- 147 + Con("follow target is away", function(self) -- 147 + do -- 148 + local target = playerGroup:find(function(e) -- 148 + return e.unit ~= self -- 148 + end) -- 148 + if target then -- 148 + self.data.followTarget = target.unit -- 149 + return math.abs(self.x - target.unit.x) > 50 -- 150 + else -- 151 + return false -- 151 + end -- 148 + end -- 148 + end), -- 147 + Sel({ -- 153 + Seq({ -- 154 + Con("not facing target", function(self) -- 154 + return (self.x > self.data.followTarget.x) == self.faceRight -- 154 + end), -- 154 + Act("turn") -- 155 + }), -- 153 + Accept() -- 157 + }), -- 152 + walk -- 159 + }), -- 146 + Seq({ -- 162 + Con("need stop", function(self) -- 162 + return not self:isDoing("idle") -- 162 + end), -- 162 + Act("cancel"), -- 163 + Act("idle") -- 164 + }) -- 161 +}) -- 136 +Store["AI_KidSearch"] = Sel({ -- 169 + Seq({ -- 170 + Con("is dead", function(self) -- 170 + return self.entity.hp <= 0 -- 170 + end), -- 170 + Accept() -- 171 + }), -- 169 + fightDecision, -- 173 + Seq({ -- 175 + Con("is falling", function(self) -- 175 + return not self.onSurface -- 175 + end), -- 175 + Act("fallOff") -- 176 + }), -- 174 + Seq({ -- 179 + Con("reach search limit", function(self) -- 179 + return math.abs(self.x) > 1150 and ((self.x > 0) == self.faceRight) -- 179 + end), -- 179 + Act("turn") -- 180 + }), -- 178 + Seq({ -- 183 + Con("continue search", function() -- 183 + return true -- 183 + end), -- 183 + walk -- 184 + }) -- 182 +}) -- 168 +Store["AI_PlayerControl"] = Sel({ -- 189 + Seq({ -- 190 + Con("is dead", function(self) -- 190 + return self.entity.hp <= 0 -- 190 + end), -- 190 + Accept() -- 191 + }), -- 189 + Seq({ -- 194 + Seq({ -- 195 + Con("move key down", function(self) -- 195 + return not (self.data.keyLeft and self.data.keyRight) and ((self.data.keyLeft and self.faceRight) or (self.data.keyRight and not self.faceRight)) -- 200 + end), -- 195 + Act("turn") -- 201 + }), -- 194 + Reject() -- 203 + }), -- 193 + Seq({ -- 206 + Con("attack key down", function(self) -- 206 + return self.data.keyShoot -- 206 + end), -- 206 + Sel({ -- 208 + Act("meleeAttack"), -- 208 + Act("rangeAttack") -- 209 + }) -- 207 + }), -- 205 + Sel({ -- 213 + Seq({ -- 214 + Con("is falling", function(self) -- 214 + return not self.onSurface -- 214 + end), -- 214 + Act("fallOff") -- 215 + }), -- 213 + Seq({ -- 218 + Con("jump key down", function(self) -- 218 + return self.data.keyUp -- 218 + end), -- 218 + Act("jump") -- 219 + }) -- 217 + }), -- 212 + Seq({ -- 223 + Con("move key down", function(self) -- 223 + return self.data.keyLeft or self.data.keyRight -- 223 + end), -- 223 + Act("walk") -- 224 + }), -- 222 + Seq({ -- 227 + Con("need stop", function(self) -- 227 + return not self:isDoing("idle") -- 227 + end), -- 227 + Act("cancel"), -- 228 + Act("idle") -- 229 + }) -- 226 +}) -- 188 diff --git a/Assets/Script/Game/Zombie Escape/Action.lua b/Assets/Script/Game/Zombie Escape/Action.lua new file mode 100644 index 000000000..4a54300fb --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Action.lua @@ -0,0 +1,69 @@ +-- [yue]: Script/Game/Zombie Escape/Action.yue +local _module_0 = dora.Platformer -- 1 +local UnitAction = _module_0.UnitAction -- 1 +local groundEntranceEnd -- 3 +groundEntranceEnd = function(name, playable) -- 3 + if not (name == "groundEntrance") then -- 4 + return -- 4 + end -- 4 + return playable.parent:stop() -- 5 +end -- 3 +UnitAction:add("groundEntrance", { -- 8 + priority = 6, -- 8 + reaction = -1, -- 9 + recovery = 0, -- 10 + queued = true, -- 11 + create = function(self) -- 12 + self.data.lastGroup = self.group -- 13 + self.group = 0 -- 14 + do -- 15 + local _with_0 = self.playable -- 15 + _with_0.speed = 1 -- 16 + _with_0:slot("AnimationEnd", groundEntranceEnd) -- 17 + _with_0:play("groundEntrance") -- 18 + end -- 15 + return function() -- 19 + return false -- 19 + end -- 19 + end, -- 12 + stop = function(self) -- 20 + self.playable:slot("AnimationEnd"):remove(groundEntranceEnd) -- 21 + self.group = self.data.lastGroup -- 22 + self.data.lastGroup = nil -- 23 + self.data.entered = true -- 24 + end -- 20 +}) -- 7 +return UnitAction:add("fallOff", { -- 27 + priority = 1, -- 27 + reaction = 1, -- 28 + recovery = 0, -- 29 + available = function(self) -- 30 + return not self.onSurface -- 30 + end, -- 30 + create = function(self) -- 31 + if self.velocityY <= 0 then -- 32 + self.data.fallDown = true -- 33 + do -- 34 + local _with_0 = self.playable -- 34 + _with_0.speed = 1 -- 35 + _with_0:play("fallOff") -- 36 + end -- 34 + else -- 37 + self.data.fallDown = false -- 37 + end -- 32 + return function(self, action) -- 38 + if self.onSurface then -- 39 + return true -- 39 + end -- 39 + if not self.data.fallDown and self.playable.current ~= "fallOff" and self.velocityY <= 0 then -- 40 + self.data.fallDown = true -- 43 + do -- 44 + local _with_0 = self.playable -- 44 + _with_0.speed = 1 -- 45 + _with_0:play("fallOff") -- 46 + end -- 44 + end -- 40 + return false -- 47 + end -- 47 + end -- 31 +}) -- 47 diff --git a/Assets/Script/Game/Zombie Escape/Body.lua b/Assets/Script/Game/Zombie Escape/Body.lua new file mode 100644 index 000000000..74ab287f3 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Body.lua @@ -0,0 +1,25 @@ +-- [yue]: Script/Game/Zombie Escape/Body.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local BodyDef = dora.BodyDef -- 1 +local Vec2 = dora.Vec2 -- 1 +local Store = Data.store -- 3 +do -- 5 + local _with_0 = BodyDef() -- 5 + _with_0.type = "Static" -- 6 + _with_0:attachPolygon(100, 60, 1, 1, 0) -- 7 + Store["Body_ObstacleS"] = _with_0 -- 5 +end -- 5 +do -- 9 + local _with_0 = BodyDef() -- 9 + _with_0.type = "Static" -- 10 + _with_0:attachPolygon(260, 60, 1, 1, 0) -- 11 + Store["Body_ObstacleM"] = _with_0 -- 9 +end -- 9 +do -- 13 + local _with_0 = BodyDef() -- 13 + _with_0.type = "Dynamic" -- 14 + _with_0.linearAcceleration = Vec2(0, -10) -- 15 + _with_0:attachDisk(40, 1, 0.6, 0.4) -- 16 + Store["Body_ObstacleC"] = _with_0 -- 13 +end -- 13 diff --git a/Assets/Script/Game/Zombie Escape/Bullet.lua b/Assets/Script/Game/Zombie Escape/Bullet.lua new file mode 100644 index 000000000..ab1ba2fb6 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Bullet.lua @@ -0,0 +1,52 @@ +-- [yue]: Script/Game/Zombie Escape/Bullet.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local BulletDef = _module_0.BulletDef -- 1 +local Vec2 = dora.Vec2 -- 1 +local Face = _module_0.Face -- 1 +local Rectangle = require("UI.View.Shape.Rectangle") -- 2 +local Star = require("UI.View.Shape.Star") -- 3 +local Store = Data.store -- 5 +do -- 7 + local _with_0 = BulletDef() -- 7 + _with_0.tag = "" -- 8 + _with_0.endEffect = "" -- 9 + _with_0.lifeTime = 1 -- 10 + _with_0.damageRadius = 0 -- 11 + _with_0.highSpeedFix = false -- 12 + _with_0.gravity = Vec2.zero -- 13 + _with_0.face = Face(function() -- 14 + return Rectangle({ -- 15 + width = 6, -- 15 + height = 6, -- 16 + borderColor = 0xffff0088, -- 17 + fillColor = 0x66ff0088, -- 18 + fillOrder = 1, -- 19 + lineOrder = 2 -- 20 + }) -- 21 + end) -- 14 + _with_0:setAsCircle(6) -- 22 + _with_0:setVelocity(0, 600) -- 23 + Store["Bullet_KidM"] = _with_0 -- 7 +end -- 7 +do -- 25 + local _with_0 = BulletDef() -- 25 + _with_0.tag = "" -- 26 + _with_0.endEffect = "" -- 27 + _with_0.lifeTime = 5 -- 28 + _with_0.damageRadius = 0 -- 29 + _with_0.highSpeedFix = false -- 30 + _with_0.gravity = Vec2(0, -10) -- 31 + _with_0.face = Face(function() -- 32 + return Star({ -- 33 + size = 15, -- 33 + borderColor = 0xffff0088, -- 34 + fillColor = 0x66ff0088, -- 35 + fillOrder = 1, -- 36 + lineOrder = 2 -- 37 + }) -- 38 + end) -- 32 + _with_0:setAsCircle(10) -- 39 + _with_0:setVelocity(60, 600) -- 40 + Store["Bullet_KidW"] = _with_0 -- 25 +end -- 25 diff --git a/Assets/Script/Game/Zombie Escape/Constant.lua b/Assets/Script/Game/Zombie Escape/Constant.lua new file mode 100644 index 000000000..2ccfeb9c3 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Constant.lua @@ -0,0 +1,13 @@ +-- [yue]: Script/Game/Zombie Escape/Constant.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local _with_0 = Data.store -- 3 +_with_0.PlayerLayer = 2 -- 4 +_with_0.ZombieLayer = 1 -- 5 +_with_0.TerrainLayer = 0 -- 6 +_with_0.PlayerGroup = 1 -- 8 +_with_0.ZombieGroup = 2 -- 9 +Data:setRelation(_with_0.PlayerGroup, _with_0.ZombieGroup, "Enemy") -- 11 +_with_0.MaxZombies = 50 -- 13 +_with_0.ZombieWaveDelay = 0 -- 14 +return _with_0 -- 3 diff --git a/Assets/Script/Game/Zombie Escape/Control.lua b/Assets/Script/Game/Zombie Escape/Control.lua new file mode 100644 index 000000000..09b7c445d --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Control.lua @@ -0,0 +1,146 @@ +-- [yue]: Script/Game/Zombie Escape/Control.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Group = dora.Group -- 1 +local App = dora.App -- 1 +local Menu = dora.Menu -- 1 +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local Director = dora.Director -- 1 +local Keyboard = dora.Keyboard -- 1 +local Node = dora.Node -- 1 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 2 +local Rectangle = require("UI.View.Shape.Rectangle") -- 3 +local Circle = require("UI.View.Shape.Circle") -- 4 +local Star = require("UI.View.Shape.Star") -- 5 +local CircleButton = require("UI.Control.Basic.CircleButton") -- 6 +local Store = Data.store -- 8 +Store.controlPlayer = "KidW" -- 10 +local playerGroup = Group({ -- 11 + "player" -- 11 +}) -- 11 +local updatePlayerControl -- 12 +updatePlayerControl = function(key, flag) -- 12 + do -- 13 + local player = playerGroup:find(function(self) -- 13 + return self.unit.tag == Store.controlPlayer -- 13 + end) -- 13 + if player then -- 13 + player.unit.data[key] = flag -- 14 + end -- 13 + end -- 13 +end -- 12 +local uiScale = App.devicePixelRatio -- 15 +do -- 17 + local _with_0 = AlignNode({ -- 17 + isRoot = true -- 17 + }) -- 17 + _with_0.visible = false -- 18 + _with_0:addChild((function() -- 19 + local _with_1 = AlignNode() -- 19 + _with_1.hAlign = "Left" -- 20 + _with_1.vAlign = "Bottom" -- 21 + _with_1:addChild((function() -- 22 + local _with_2 = Menu() -- 22 + _with_2:addChild((function() -- 23 + local _with_3 = CircleButton({ -- 24 + text = "Left", -- 24 + x = 20 * uiScale, -- 25 + y = 60 * uiScale, -- 26 + radius = 30 * uiScale, -- 27 + fontSize = math.floor(18 * uiScale) -- 28 + }) -- 23 + _with_3.anchor = Vec2.zero -- 30 + _with_3:slot("TapBegan", function() -- 31 + return updatePlayerControl("keyLeft", true) -- 31 + end) -- 31 + _with_3:slot("TapEnded", function() -- 32 + return updatePlayerControl("keyLeft", false) -- 32 + end) -- 32 + return _with_3 -- 23 + end)()) -- 23 + _with_2:addChild((function() -- 33 + local _with_3 = CircleButton({ -- 34 + text = "Right", -- 34 + x = 90 * uiScale, -- 35 + y = 60 * uiScale, -- 36 + radius = 30 * uiScale, -- 37 + fontSize = math.floor(18 * uiScale) -- 38 + }) -- 33 + _with_3.anchor = Vec2.zero -- 40 + _with_3:slot("TapBegan", function() -- 41 + return updatePlayerControl("keyRight", true) -- 41 + end) -- 41 + _with_3:slot("TapEnded", function() -- 42 + return updatePlayerControl("keyRight", false) -- 42 + end) -- 42 + return _with_3 -- 33 + end)()) -- 33 + return _with_2 -- 22 + end)()) -- 22 + return _with_1 -- 19 + end)()) -- 19 + _with_0:addChild((function() -- 43 + local _with_1 = AlignNode() -- 43 + _with_1.hAlign = "Right" -- 44 + _with_1.vAlign = "Bottom" -- 45 + _with_1:addChild((function() -- 46 + local _with_2 = Menu() -- 46 + _with_2:addChild((function() -- 47 + local _with_3 = CircleButton({ -- 48 + text = "Jump", -- 48 + x = -80 * uiScale, -- 49 + y = 60 * uiScale, -- 50 + radius = 30 * uiScale, -- 51 + fontSize = math.floor(18 * uiScale) -- 52 + }) -- 47 + _with_3.anchor = Vec2.zero -- 54 + _with_3:slot("TapBegan", function() -- 55 + return updatePlayerControl("keyUp", true) -- 55 + end) -- 55 + _with_3:slot("TapEnded", function() -- 56 + return updatePlayerControl("keyUp", false) -- 56 + end) -- 56 + return _with_3 -- 47 + end)()) -- 47 + _with_2:addChild((function() -- 57 + local _with_3 = CircleButton({ -- 58 + text = "Shoot", -- 58 + x = -150 * uiScale, -- 59 + y = 60 * uiScale, -- 60 + radius = 30 * uiScale, -- 61 + fontSize = math.floor(18 * uiScale) -- 62 + }) -- 57 + _with_3.anchor = Vec2.zero -- 64 + _with_3:slot("TapBegan", function() -- 65 + return updatePlayerControl("keyShoot", true) -- 65 + end) -- 65 + _with_3:slot("TapEnded", function() -- 66 + return updatePlayerControl("keyShoot", false) -- 66 + end) -- 66 + return _with_3 -- 57 + end)()) -- 57 + return _with_2 -- 46 + end)()) -- 46 + return _with_1 -- 43 + end)()) -- 43 + _with_0:addTo((function() -- 67 + local _with_1 = Director.ui -- 67 + _with_1.renderGroup = true -- 68 + return _with_1 -- 67 + end)()) -- 67 +end -- 17 +Store.keyboardEnabled = false -- 70 +local keyboardControl -- 71 +keyboardControl = function() -- 71 + if not Store.keyboardEnabled then -- 72 + return -- 72 + end -- 72 + updatePlayerControl("keyLeft", Keyboard:isKeyPressed("A")) -- 73 + updatePlayerControl("keyRight", Keyboard:isKeyPressed("D")) -- 74 + updatePlayerControl("keyUp", Keyboard:isKeyPressed("K")) -- 75 + return updatePlayerControl("keyShoot", Keyboard:isKeyPressed("J")) -- 76 +end -- 71 +local _with_0 = Node() -- 78 +_with_0:schedule(keyboardControl) -- 79 +return _with_0 -- 78 diff --git a/Assets/Script/Game/Zombie Escape/Debug.lua b/Assets/Script/Game/Zombie Escape/Debug.lua new file mode 100644 index 000000000..94bc853e1 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Debug.lua @@ -0,0 +1,306 @@ +-- [yue]: Script/Game/Zombie Escape/Debug.yue +local _module_1 = dora.Platformer -- 1 +local Data = _module_1.Data -- 1 +local Group = dora.Group -- 1 +local App = dora.App -- 1 +local _module_0 = dora.ImGui -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local Vec2 = dora.Vec2 -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local TextWrapped = _module_0.TextWrapped -- 1 +local tostring = _G.tostring -- 1 +local SameLine = _module_0.SameLine -- 1 +local Button = _module_0.Button -- 1 +local Rect = dora.Rect -- 1 +local Size = dora.Size -- 1 +local Entity = dora.Entity -- 1 +local DragFloat = _module_0.DragFloat -- 1 +local Checkbox = _module_0.Checkbox -- 1 +local Separator = _module_0.Separator -- 1 +local RadioButton = _module_0.RadioButton -- 1 +local Director = dora.Director -- 1 +local table = _G.table -- 1 +local Text = _module_0.Text -- 1 +local Observer = dora.Observer -- 1 +local Star = require("UI.View.Shape.Star") -- 2 +local Store = Data.store -- 4 +local world, ZombieLayer, PlayerGroup = Store.world, Store.ZombieLayer, Store.PlayerGroup -- 5 +local playerGroup = Group({ -- 11 + "player", -- 11 + "unit" -- 11 +}) -- 11 +local zombieGroup = Group({ -- 12 + "zombie", -- 12 + "unit" -- 12 +}) -- 12 +local userControl = false -- 13 +local playerChoice = 1 -- 14 +local controlChoice -- 15 +do -- 15 + local _exp_0 = App.platform -- 15 + if "iOS" == _exp_0 or "Android" == _exp_0 then -- 16 + controlChoice = 0 -- 16 + else -- 17 + controlChoice = 1 -- 17 + end -- 17 +end -- 17 +local camZoom = world.camera.zoom -- 18 +local decisions = { } -- 19 +local showDecisionTrace = false -- 20 +local lastDecisionTree = "" -- 21 +world:schedule(function() -- 22 + local width, height -- 23 + do -- 23 + local _obj_0 = App.visualSize -- 23 + width, height = _obj_0.width, _obj_0.height -- 23 + end -- 23 + SetNextWindowPos(Vec2(width - 250, 10), "FirstUseEver") -- 24 + SetNextWindowSize(Vec2(240, userControl and 500 or 300)) -- 25 + Begin("Zombie Game Demo", { -- 26 + "NoResize", -- 26 + "NoSavedSettings" -- 26 + }, function() -- 26 + TextWrapped("Zombie Killed: " .. tostring(Store.zombieKilled)) -- 27 + SameLine() -- 28 + if Button("Army") then -- 29 + for i = 0, 10 do -- 30 + local available = false -- 31 + local pos = Vec2.zero -- 32 + while not available do -- 33 + pos = Vec2(App.rand % 2400 - 1200, -430) -- 34 + available = not world:query(Rect(pos, Size(5, 5)), function(self) -- 35 + return self.group == Data.groupTerrain -- 35 + end) -- 35 + end -- 35 + Entity({ -- 37 + unitDef = "Unit_Zombie" .. tostring(App.rand % 2 + 1), -- 37 + order = ZombieLayer, -- 38 + position = pos, -- 39 + group = PlayerGroup, -- 40 + faceRight = App.rand % 2 == 0, -- 41 + stared = true -- 42 + }) -- 36 + end -- 42 + end -- 29 + local changed -- 43 + changed, camZoom = DragFloat("Zoom", camZoom, 0.01, 0.5, 2, "%.2f") -- 43 + if changed then -- 44 + world.camera.zoom = camZoom -- 44 + end -- 44 + playerGroup:each(function(self) -- 45 + return TextWrapped(tostring(self.unit.tag) .. " HP: " .. tostring(self.hp)) -- 45 + end) -- 45 + local result -- 46 + changed, result = Checkbox("Physics Debug", world.showDebug) -- 46 + if changed then -- 47 + world.showDebug = result -- 47 + end -- 47 + changed, showDecisionTrace = Checkbox("AI Debug", showDecisionTrace) -- 48 + if changed then -- 49 + playerGroup:each(function(self) -- 50 + self.unit.receivingDecisionTrace = showDecisionTrace -- 50 + end) -- 50 + end -- 49 + changed, userControl = Checkbox("Take Control", userControl) -- 51 + if userControl then -- 52 + if Store.controlPlayer == "Zombie" and not playerGroup:each(function(self) -- 54 + if self.unit.tag == "Zombie" then -- 55 + if self.hp <= 0 then -- 56 + self.player = nil -- 57 + self.unit.children.last:removeFromParent() -- 58 + self.unit.decisionTree = "" -- 59 + self.unit.tag = "ZombieDead" -- 60 + return false -- 61 + else -- 62 + return true -- 62 + end -- 56 + end -- 55 + return false -- 63 + end) then -- 53 + zombieGroup:each(function(self) -- 64 + if self.hp <= 0 then -- 65 + return false -- 65 + end -- 65 + self.player = true -- 66 + self.zombie = nil -- 67 + do -- 68 + local _with_0 = self.unit -- 68 + _with_0.tag = "Zombie" -- 69 + _with_0.group = PlayerGroup -- 70 + _with_0.decisionTree = "AI_PlayerControl" -- 71 + _with_0.sensity = 0 -- 72 + _with_0:addChild(Star({ -- 74 + y = 100, -- 74 + size = 18, -- 75 + borderColor = 0xffff8800, -- 76 + fillColor = 0x66ff8800, -- 77 + fillOrder = 1, -- 78 + lineOrder = 2 -- 79 + })) -- 73 + world.camera.followTarget = _with_0 -- 68 + end -- 68 + return true -- 81 + end) -- 64 + end -- 53 + Separator() -- 82 + local pressedA, choice = RadioButton("Male", playerChoice, 0) -- 83 + if pressedA then -- 84 + playerChoice = choice -- 84 + end -- 84 + local pressedB -- 85 + pressedB, choice = RadioButton("Female", playerChoice, 1) -- 85 + if pressedB then -- 86 + playerChoice = choice -- 86 + end -- 86 + local pressedC -- 87 + pressedC, choice = RadioButton("Zombie", playerChoice, 2) -- 87 + if pressedC then -- 88 + playerChoice = choice -- 88 + end -- 88 + if pressedA or pressedB or pressedC or changed then -- 89 + if 0 == playerChoice then -- 91 + Store.controlPlayer = "KidM" -- 91 + elseif 1 == playerChoice then -- 92 + Store.controlPlayer = "KidW" -- 92 + elseif 2 == playerChoice then -- 93 + Store.controlPlayer = "Zombie" -- 93 + end -- 93 + if Store.controlPlayer == "Zombie" and not playerGroup:each(function(self) -- 95 + return self.unit.tag == "Zombie" -- 95 + end) then -- 94 + zombieGroup:each(function(self) -- 96 + self.player = true -- 97 + self.zombie = nil -- 98 + do -- 99 + local _with_0 = self.unit -- 99 + _with_0.tag = "Zombie" -- 100 + _with_0.group = PlayerGroup -- 101 + _with_0:addChild(Star({ -- 103 + y = 100, -- 103 + size = 18, -- 104 + borderColor = 0xffff8800, -- 105 + fillColor = 0x66ff8800, -- 106 + fillOrder = 1, -- 107 + lineOrder = 2 -- 108 + })) -- 102 + end -- 99 + return true -- 110 + end) -- 96 + end -- 94 + playerGroup:each(function(self) -- 111 + if self.unit.tag == Store.controlPlayer then -- 112 + self.unit.decisionTree = "AI_PlayerControl" -- 113 + self.unit.sensity = 0 -- 114 + world.camera.followTarget = self.unit -- 115 + else -- 117 + do -- 117 + local _exp_0 = self.unit.tag -- 117 + if "KidM" == _exp_0 then -- 118 + self.unit.decisionTree = "AI_KidFollow" -- 118 + elseif "KidW" == _exp_0 then -- 119 + self.unit.decisionTree = "AI_KidSearch" -- 119 + elseif "Zombie" == _exp_0 then -- 120 + self.unit.decisionTree = "AI_Zombie" -- 120 + end -- 120 + end -- 120 + self.unit.sensity = 0.1 -- 121 + end -- 112 + end) -- 111 + end -- 89 + if changed then -- 122 + Store.keyboardEnabled = controlChoice == 1 -- 123 + Director.ui.children.first.visible = controlChoice == 0 -- 124 + end -- 122 + Separator() -- 125 + TextWrapped((function() -- 126 + if controlChoice == 1 then -- 126 + return "Keyboard: Left(A), Right(D), Shoot(J), Jump(K)" -- 127 + else -- 128 + return "TouchPad: Use buttons in lower screen to control unit." -- 128 + end -- 126 + end)()) -- 126 + Separator() -- 129 + pressedA, choice = RadioButton("TouchPad", controlChoice, 0) -- 130 + if pressedA then -- 131 + controlChoice = choice -- 132 + Store.keyboardEnabled = false -- 133 + Director.ui:eachChild(function(self) -- 134 + self.visible = true -- 134 + end) -- 134 + end -- 131 + pressedB, choice = RadioButton("Keyboard", controlChoice, 1) -- 135 + if pressedB then -- 136 + controlChoice = choice -- 137 + Store.keyboardEnabled = true -- 138 + Director.ui.children.first.visible = false -- 139 + end -- 136 + elseif changed then -- 140 + playerGroup:each(function(self) -- 141 + do -- 142 + local _exp_0 = self.unit.tag -- 142 + if "KidM" == _exp_0 then -- 143 + self.unit.decisionTree = "AI_KidFollow" -- 143 + elseif "KidW" == _exp_0 then -- 144 + self.unit.decisionTree = "AI_KidSearch" -- 144 + elseif "Zombie" == _exp_0 then -- 145 + self.unit.decisionTree = "AI_Zombie" -- 145 + end -- 145 + end -- 145 + self.unit.sensity = 0.1 -- 146 + end) -- 141 + Store.keyboardEnabled = false -- 147 + Director.ui.children.first.visible = false -- 148 + end -- 52 + end) -- 26 + local target = world.camera.followTarget -- 150 + if target then -- 151 + local player = target.entity -- 152 + local decisionTrace = player.decisionTrace -- 153 + local lastDecision = decisions[#decisions] -- 154 + if lastDecision ~= decisionTrace then -- 155 + decisions[#decisions + 1] = decisionTrace -- 156 + end -- 155 + if #decisions > 5 then -- 157 + table.remove(decisions, 1) -- 157 + end -- 157 + lastDecisionTree = target.decisionTree -- 158 + end -- 151 + if showDecisionTrace then -- 160 + SetNextWindowPos(Vec2(width / 2 - 200, 10), "FirstUseEver") -- 161 + SetNextWindowSize(Vec2(400, 160), "FirstUseEver") -- 162 + return Begin("Decision Trace (" .. tostring(lastDecisionTree) .. ")", { -- 163 + "NoSavedSettings" -- 163 + }, function() -- 163 + return Text(table.concat(decisions, "\n")) -- 164 + end) -- 164 + end -- 160 +end) -- 22 +do -- 166 + local _with_0 = Observer("Add", { -- 166 + "group", -- 166 + "unit", -- 166 + "player", -- 166 + "stared" -- 166 + }) -- 166 + _with_0:watch(function(self, group, unit) -- 167 + unit:addChild(Star({ -- 169 + y = 100, -- 169 + size = 18, -- 170 + borderColor = 0xff66ccff, -- 171 + fillColor = 0x6666ccff, -- 172 + fillOrder = 1, -- 173 + lineOrder = 2 -- 174 + })) -- 168 + return false -- 175 + end) -- 167 +end -- 166 +local _with_0 = Observer("Add", { -- 177 + "unit", -- 177 + "player" -- 177 +}) -- 177 +_with_0:watch(function(self, unit) -- 178 + unit.receivingDecisionTrace = true -- 178 + return false -- 178 +end) -- 178 +return _with_0 -- 177 diff --git a/Assets/Script/Game/Zombie Escape/Logic.lua b/Assets/Script/Game/Zombie Escape/Logic.lua new file mode 100644 index 000000000..75e5e419e --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Logic.lua @@ -0,0 +1,202 @@ +-- [yue]: Script/Game/Zombie Escape/Logic.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Observer = dora.Observer -- 1 +local Color3 = dora.Color3 -- 1 +local Body = dora.Body -- 1 +local type = _G.type -- 1 +local Color = dora.Color -- 1 +local Unit = _module_0.Unit -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local Opacity = dora.Opacity -- 1 +local Ease = dora.Ease -- 1 +local Group = dora.Group -- 1 +local threadLoop = dora.threadLoop -- 1 +local Vec2 = dora.Vec2 -- 1 +local App = dora.App -- 1 +local Rect = dora.Rect -- 1 +local Size = dora.Size -- 1 +local Entity = dora.Entity -- 1 +local tostring = _G.tostring -- 1 +local math = _G.math -- 1 +local Rectangle = require("UI.View.Shape.Rectangle") -- 2 +local Circle = require("UI.View.Shape.Circle") -- 3 +local Star = require("UI.View.Shape.Star") -- 4 +local Store = Data.store -- 6 +do -- 8 + local _with_0 = Observer("Add", { -- 8 + "obstacleDef", -- 8 + "size", -- 8 + "position", -- 8 + "color" -- 8 + }) -- 8 + _with_0:watch(function(self, obstacleDef, size, position, color) -- 9 + local world, TerrainLayer = Store.world, Store.TerrainLayer -- 10 + color = Color3(color) -- 11 + do -- 12 + local _with_1 = Body(Store[obstacleDef], world, position) -- 12 + _with_1.tag = "Obstacle" -- 13 + _with_1.order = TerrainLayer -- 14 + _with_1.group = Data.groupTerrain -- 15 + if "number" == type(size) then -- 16 + _with_1:addChild(Circle({ -- 18 + radius = size, -- 18 + fillColor = Color(color, 0x66):toARGB(), -- 19 + borderColor = Color(color, 0xff):toARGB(), -- 20 + fillOrder = 1, -- 21 + lineOrder = 2 -- 22 + })) -- 17 + _with_1:addChild(Star({ -- 25 + size = 20, -- 25 + borderColor = 0xffffffff, -- 26 + fillColor = 0x66ffffff, -- 27 + fillOrder = 1, -- 28 + lineOrder = 2 -- 29 + })) -- 24 + else -- 32 + _with_1:addChild(Rectangle({ -- 33 + width = size.width, -- 33 + height = size.height, -- 34 + fillColor = Color(color, 0x66):toARGB(), -- 35 + borderColor = Color(color, 0xff):toARGB(), -- 36 + fillOrder = 1, -- 37 + lineOrder = 2 -- 38 + })) -- 32 + end -- 16 + _with_1:addTo(world) -- 40 + end -- 12 + self:destroy() -- 41 + return false -- 41 + end) -- 9 +end -- 8 +local mutables = { -- 44 + "hp", -- 44 + "moveSpeed", -- 45 + "move", -- 46 + "jump", -- 47 + "targetAllow", -- 48 + "attackPower", -- 49 + "attackSpeed" -- 50 +} -- 43 +do -- 52 + local _with_0 = Observer("Add", { -- 52 + "unitDef", -- 52 + "position", -- 52 + "order", -- 52 + "group", -- 52 + "faceRight" -- 52 + }) -- 52 + _with_0:watch(function(self, unitDef, position, order, group, faceRight) -- 53 + local world = Store.world -- 54 + local def = Store[unitDef] -- 55 + for _index_0 = 1, #mutables do -- 56 + local var = mutables[_index_0] -- 56 + self[var] = def[var] -- 57 + end -- 57 + local unit -- 58 + do -- 58 + local _with_1 = Unit(def, world, self, position) -- 58 + _with_1.group = group -- 59 + _with_1.order = order -- 60 + _with_1.faceRight = faceRight -- 61 + _with_1:addTo(world) -- 62 + _with_1:eachAction(function(self) -- 63 + self.recovery = 0 -- 63 + end) -- 63 + do -- 64 + local _with_2 = _with_1.playable -- 64 + _with_2:eachNode(function(sp) -- 65 + sp.filter = "Point" -- 65 + end) -- 65 + if self.zombie then -- 66 + _with_2:play("groundEntrance") -- 66 + end -- 66 + end -- 64 + unit = _with_1 -- 58 + end -- 58 + if self.player and unit.decisionTree == "AI_KidSearch" then -- 67 + world.camera.followTarget = unit -- 67 + end -- 67 + return false -- 67 + end) -- 53 +end -- 52 +do -- 69 + local _with_0 = Observer("Change", { -- 69 + "hp", -- 69 + "unit" -- 69 + }) -- 69 + _with_0:watch(function(self, hp, unit) -- 70 + local lastHp = self.oldValues.hp -- 71 + if hp < lastHp then -- 72 + if hp > 0 then -- 73 + unit:start("hit") -- 74 + else -- 76 + unit:start("hit") -- 76 + unit:start("fall") -- 77 + unit.group = Data.groupHide -- 78 + unit:schedule(once(function() -- 79 + sleep(5) -- 80 + unit:runAction(Opacity(0.5, 1, 0, Ease.OutQuad)) -- 81 + sleep(0.5) -- 82 + if Store.world.camera.followTarget == unit then -- 83 + do -- 84 + local player = Group({ -- 84 + "player", -- 84 + "unit" -- 84 + }):find(function(self) -- 84 + return self.player -- 84 + end) -- 84 + if player then -- 84 + Store.world.camera.followTarget = player.unit -- 85 + end -- 84 + end -- 84 + end -- 83 + return unit:removeFromParent() -- 86 + end)) -- 79 + end -- 73 + end -- 72 + return false -- 86 + end) -- 70 +end -- 69 +Store.zombieKilled = 0 -- 88 +do -- 89 + local _with_0 = Observer("Change", { -- 89 + "hp", -- 89 + "zombie" -- 89 + }) -- 89 + _with_0:watch(function(self, hp) -- 90 + if hp <= 0 then -- 91 + Store.zombieKilled = Store.zombieKilled + 1 -- 91 + end -- 91 + return false -- 91 + end) -- 90 +end -- 89 +local zombieGroup = Group({ -- 93 + "zombie" -- 93 +}) -- 93 +return threadLoop(function() -- 94 + local ZombieLayer, ZombieGroup, MaxZombies, ZombieWaveDelay, world = Store.ZombieLayer, Store.ZombieGroup, Store.MaxZombies, Store.ZombieWaveDelay, Store.world -- 95 + if zombieGroup.count < MaxZombies then -- 102 + for i = zombieGroup.count + 1, MaxZombies do -- 103 + local available = false -- 104 + local pos = Vec2.zero -- 105 + while not available do -- 106 + pos = Vec2(App.rand % 2400 - 1200, -430) -- 107 + available = not world:query(Rect(pos, Size(5, 5)), function(self) -- 108 + return self.group == Data.groupTerrain -- 108 + end) -- 108 + end -- 108 + Entity({ -- 110 + unitDef = "Unit_Zombie" .. tostring(math.floor(App.rand % 2 + 1)), -- 110 + order = ZombieLayer, -- 111 + position = pos, -- 112 + group = ZombieGroup, -- 113 + faceRight = App.rand % 2 == 0, -- 114 + zombie = true -- 115 + }) -- 109 + sleep(0.1 * App.rand % 5) -- 116 + end -- 116 + end -- 102 + return sleep(ZombieWaveDelay) -- 117 +end) -- 117 diff --git a/Assets/Script/Game/Zombie Escape/Scene.lua b/Assets/Script/Game/Zombie Escape/Scene.lua new file mode 100644 index 000000000..3cfa71659 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Scene.lua @@ -0,0 +1,117 @@ +-- [yue]: Script/Game/Zombie Escape/Scene.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local PlatformWorld = _module_0.PlatformWorld -- 1 +local Vec2 = dora.Vec2 -- 1 +local View = dora.View -- 1 +local BodyDef = dora.BodyDef -- 1 +local Color = dora.Color -- 1 +local App = dora.App -- 1 +local Body = dora.Body -- 1 +local Entity = dora.Entity -- 1 +local Size = dora.Size -- 1 +local Rectangle = require("UI.View.Shape.Rectangle") -- 2 +local Store = Data.store -- 4 +local PlayerLayer, PlayerGroup, ZombieLayer, TerrainLayer = Store.PlayerLayer, Store.PlayerGroup, Store.ZombieLayer, Store.TerrainLayer -- 5 +local DesignWidth = 1280 -- 12 +local world -- 14 +do -- 14 + local _with_0 = PlatformWorld() -- 14 + _with_0:getLayer(PlayerLayer).renderGroup = true -- 15 + _with_0:getLayer(ZombieLayer).renderGroup = true -- 16 + _with_0:getLayer(TerrainLayer).renderGroup = true -- 17 + _with_0.camera.followRatio = Vec2(0.01, 0.01) -- 18 + _with_0.camera.zoom = View.size.width / DesignWidth -- 19 + _with_0:gslot("AppSizeChanged", function() -- 20 + _with_0.camera.zoom = View.size.width / DesignWidth -- 21 + end) -- 20 + world = _with_0 -- 14 +end -- 14 +Store.world = world -- 22 +local terrainDef -- 24 +do -- 24 + local _with_0 = BodyDef() -- 24 + _with_0.type = "Static" -- 25 + _with_0:attachPolygon(Vec2(0, -500), 2500, 10, 0, 1, 1, 0) -- 26 + _with_0:attachPolygon(Vec2(0, 500), 2500, 10, 0, 1, 1, 0) -- 27 + _with_0:attachPolygon(Vec2(1250, 0), 10, 1000, 0, 1, 1, 0) -- 28 + _with_0:attachPolygon(Vec2(-1250, 0), 10, 1000, 0, 1, 1, 0) -- 29 + terrainDef = _with_0 -- 24 +end -- 24 +local fillColor = Color(App.themeColor:toColor3(), 0x66):toARGB() -- 31 +local borderColor = App.themeColor:toARGB() -- 32 +do -- 34 + local _with_0 = Body(terrainDef, world, Vec2.zero) -- 34 + _with_0.order = TerrainLayer -- 35 + _with_0.group = Data.groupTerrain -- 36 + _with_0:addChild(Rectangle({ -- 38 + y = -500, -- 38 + width = 2500, -- 39 + height = 10, -- 40 + fillColor = fillColor, -- 41 + borderColor = borderColor, -- 42 + fillOrder = 1, -- 43 + lineOrder = 2 -- 44 + })) -- 37 + _with_0:addChild(Rectangle({ -- 47 + x = 1250, -- 47 + y = 0, -- 48 + width = 10, -- 49 + height = 1000, -- 50 + fillColor = fillColor, -- 51 + borderColor = borderColor, -- 52 + fillOrder = 1, -- 53 + lineOrder = 2 -- 54 + })) -- 46 + _with_0:addChild(Rectangle({ -- 57 + x = -1250, -- 57 + y = 0, -- 58 + width = 10, -- 59 + height = 1000, -- 60 + fillColor = fillColor, -- 61 + borderColor = borderColor, -- 62 + fillOrder = 1, -- 63 + lineOrder = 2 -- 64 + })) -- 56 + _with_0:addTo(world) -- 66 +end -- 34 +Entity({ -- 69 + obstacleDef = "Body_ObstacleS", -- 69 + size = Size(100, 60), -- 70 + position = Vec2(100, -464), -- 71 + color = borderColor -- 72 +}) -- 68 +Entity({ -- 75 + obstacleDef = "Body_ObstacleM", -- 75 + size = Size(260, 60), -- 76 + position = Vec2(-400, -464), -- 77 + color = borderColor -- 78 +}) -- 74 +Entity({ -- 81 + obstacleDef = "Body_ObstacleS", -- 81 + size = Size(100, 60), -- 82 + position = Vec2(-400, -404), -- 83 + color = borderColor -- 84 +}) -- 80 +Entity({ -- 87 + obstacleDef = "Body_ObstacleC", -- 87 + size = 40, -- 88 + position = Vec2(400, -464), -- 89 + color = 0xff6666 -- 90 +}) -- 86 +Entity({ -- 93 + unitDef = "Unit_KidM", -- 93 + order = PlayerLayer, -- 94 + position = Vec2(-50, -430), -- 95 + group = PlayerGroup, -- 96 + faceRight = false, -- 97 + player = true -- 98 +}) -- 92 +return Entity({ -- 101 + unitDef = "Unit_KidW", -- 101 + order = PlayerLayer, -- 102 + position = Vec2(0, -430), -- 103 + group = PlayerGroup, -- 104 + faceRight = true, -- 105 + player = true -- 106 +}) -- 106 diff --git a/Assets/Script/Game/Zombie Escape/Unit.lua b/Assets/Script/Game/Zombie Escape/Unit.lua new file mode 100644 index 000000000..ce3cefb4e --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/Unit.lua @@ -0,0 +1,231 @@ +-- [yue]: Script/Game/Zombie Escape/Unit.yue +local _module_0 = dora.Platformer -- 1 +local Data = _module_0.Data -- 1 +local Dictionary = dora.Dictionary -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local TargetAllow = _module_0.TargetAllow -- 1 +local Array = dora.Array -- 1 +local Store = Data.store -- 3 +do -- 5 + local _with_0 = Dictionary() -- 5 + _with_0.linearAcceleration = Vec2(0, -10) -- 6 + _with_0.bodyType = "Dynamic" -- 7 + _with_0.scale = 5 -- 8 + _with_0.density = 1.0 -- 9 + _with_0.friction = 1.0 -- 10 + _with_0.restitution = 0.0 -- 11 + _with_0.playable = "model:Model/KidW" -- 12 + _with_0.size = Size(30, 110) -- 13 + _with_0.tag = "KidW" -- 14 + _with_0.sensity = 0.1 -- 15 + _with_0.move = 250 -- 16 + _with_0.moveSpeed = 1.0 -- 17 + _with_0.jump = 500 -- 18 + _with_0.detectDistance = 350 -- 19 + _with_0.hp = 5.0 -- 20 + _with_0.attackBase = 2.5 -- 21 + _with_0.attackDelay = 0.1 -- 22 + _with_0.attackEffectDelay = 0.1 -- 23 + _with_0.attackRange = Size(350, 150) -- 24 + _with_0.attackPower = Vec2(100, 100) -- 25 + _with_0.attackTarget = "Single" -- 26 + do -- 27 + local conf -- 28 + do -- 28 + local _with_1 = TargetAllow() -- 28 + _with_1.terrainAllowed = true -- 29 + _with_1:allow("Enemy", true) -- 30 + conf = _with_1 -- 28 + end -- 28 + _with_0.targetAllow = conf:toValue() -- 31 + end -- 31 + _with_0.damageType = 0 -- 32 + _with_0.defenceType = 0 -- 33 + _with_0.bulletType = "Bullet_KidW" -- 34 + _with_0.attackEffect = "" -- 35 + _with_0.hitEffect = "" -- 36 + _with_0.sndAttack = "" -- 37 + _with_0.sndFallen = "" -- 38 + _with_0.decisionTree = "AI_KidSearch" -- 39 + _with_0.usePreciseHit = false -- 40 + _with_0.actions = Array({ -- 42 + "walk", -- 42 + "turn", -- 43 + "rangeAttack", -- 44 + "idle", -- 45 + "cancel", -- 46 + "jump", -- 47 + "hit", -- 48 + "fall", -- 49 + "fallOff" -- 50 + }) -- 41 + Store["Unit_KidW"] = _with_0 -- 5 +end -- 5 +do -- 52 + local _with_0 = Dictionary() -- 52 + _with_0.linearAcceleration = Vec2(0, -10) -- 53 + _with_0.bodyType = "Dynamic" -- 54 + _with_0.scale = 5 -- 55 + _with_0.density = 1.0 -- 56 + _with_0.friction = 1.0 -- 57 + _with_0.restitution = 0.0 -- 58 + _with_0.playable = "model:Model/KidM" -- 59 + _with_0.size = Size(30, 110) -- 60 + _with_0.tag = "KidM" -- 61 + _with_0.sensity = 0.1 -- 62 + _with_0.move = 250 -- 63 + _with_0.moveSpeed = 1.0 -- 64 + _with_0.jump = 500 -- 65 + _with_0.detectDistance = 500 -- 66 + _with_0.hp = 5.0 -- 67 + _with_0.attackBase = 1.0 -- 68 + _with_0.attackDelay = 0.1 -- 69 + _with_0.attackEffectDelay = 0.1 -- 70 + _with_0.attackRange = Size(400, 100) -- 71 + _with_0.attackPower = Vec2(100, 0) -- 72 + _with_0.attackTarget = "Single" -- 73 + do -- 74 + local conf -- 75 + do -- 75 + local _with_1 = TargetAllow() -- 75 + _with_1.terrainAllowed = true -- 76 + _with_1:allow("Enemy", true) -- 77 + conf = _with_1 -- 75 + end -- 75 + _with_0.targetAllow = conf:toValue() -- 78 + end -- 78 + _with_0.damageType = 0 -- 79 + _with_0.defenceType = 0 -- 80 + _with_0.bulletType = "Bullet_KidM" -- 81 + _with_0.attackEffect = "" -- 82 + _with_0.hitEffect = "" -- 83 + _with_0.sndAttack = "" -- 84 + _with_0.sndFallen = "" -- 85 + _with_0.decisionTree = "AI_KidFollow" -- 86 + _with_0.usePreciseHit = false -- 87 + _with_0.actions = Array({ -- 89 + "walk", -- 89 + "turn", -- 90 + "rangeAttack", -- 91 + "idle", -- 92 + "cancel", -- 93 + "jump", -- 94 + "hit", -- 95 + "fall", -- 96 + "fallOff" -- 97 + }) -- 88 + Store["Unit_KidM"] = _with_0 -- 52 +end -- 52 +do -- 99 + local _with_0 = Dictionary() -- 99 + _with_0.linearAcceleration = Vec2(0, -10) -- 100 + _with_0.bodyType = "Dynamic" -- 101 + _with_0.scale = 5 -- 102 + _with_0.density = 1.0 -- 103 + _with_0.friction = 1.0 -- 104 + _with_0.restitution = 0.0 -- 105 + _with_0.playable = "model:Model/Zombie1" -- 106 + _with_0.size = Size(40, 110) -- 107 + _with_0.tag = "Zombie1" -- 108 + _with_0.sensity = 0.2 -- 109 + _with_0.move = 120 -- 110 + _with_0.moveSpeed = 1.0 -- 111 + _with_0.jump = 600 -- 112 + _with_0.detectDistance = 600 -- 113 + _with_0.hp = 5.0 -- 114 + _with_0.attackBase = 1 -- 115 + _with_0.attackDelay = 0.25 -- 116 + _with_0.attackEffectDelay = 0.1 -- 117 + _with_0.attackRange = Size(80, 50) -- 118 + _with_0.attackPower = Vec2(150, 100) -- 119 + _with_0.attackTarget = "Single" -- 120 + do -- 121 + local conf -- 122 + do -- 122 + local _with_1 = TargetAllow() -- 122 + _with_1.terrainAllowed = true -- 123 + _with_1:allow("Enemy", true) -- 124 + conf = _with_1 -- 122 + end -- 122 + _with_0.targetAllow = conf:toValue() -- 125 + end -- 125 + _with_0.damageType = 0 -- 126 + _with_0.defenceType = 0 -- 127 + _with_0.bulletType = "" -- 128 + _with_0.attackEffect = "" -- 129 + _with_0.hitEffect = "" -- 130 + _with_0.sndAttack = "" -- 131 + _with_0.sndFallen = "" -- 132 + _with_0.decisionTree = "AI_Zombie" -- 133 + _with_0.usePreciseHit = false -- 134 + _with_0.actions = Array({ -- 136 + "walk", -- 136 + "turn", -- 137 + "meleeAttack", -- 138 + "idle", -- 139 + "cancel", -- 140 + "jump", -- 141 + "hit", -- 142 + "fall", -- 143 + "groundEntrance", -- 144 + "fallOff" -- 145 + }) -- 135 + Store["Unit_Zombie1"] = _with_0 -- 99 +end -- 99 +do -- 147 + local _with_0 = Dictionary() -- 147 + _with_0.linearAcceleration = Vec2(0, -10) -- 148 + _with_0.bodyType = "Dynamic" -- 149 + _with_0.scale = 5 -- 150 + _with_0.density = 1.0 -- 151 + _with_0.friction = 1.0 -- 152 + _with_0.restitution = 0.0 -- 153 + _with_0.playable = "model:Model/Zombie2" -- 154 + _with_0.size = Size(40, 110) -- 155 + _with_0.tag = "Zombie2" -- 156 + _with_0.sensity = 0.2 -- 157 + _with_0.move = 60 -- 158 + _with_0.moveSpeed = 1.0 -- 159 + _with_0.jump = 500 -- 160 + _with_0.detectDistance = 600 -- 161 + _with_0.hp = 5.0 -- 162 + _with_0.attackBase = 1 -- 163 + _with_0.attackDelay = 0.4 -- 164 + _with_0.attackEffectDelay = 0.1 -- 165 + _with_0.attackRange = Size(150, 80) -- 166 + _with_0.attackPower = Vec2(150, 100) -- 167 + _with_0.attackTarget = "Multi" -- 168 + do -- 169 + local conf -- 170 + do -- 170 + local _with_1 = TargetAllow() -- 170 + _with_1.terrainAllowed = true -- 171 + _with_1:allow("Enemy", true) -- 172 + conf = _with_1 -- 170 + end -- 170 + _with_0.targetAllow = conf:toValue() -- 173 + end -- 173 + _with_0.damageType = 0 -- 174 + _with_0.defenceType = 0 -- 175 + _with_0.bulletType = "" -- 176 + _with_0.attackEffect = "" -- 177 + _with_0.hitEffect = "" -- 178 + _with_0.sndAttack = "" -- 179 + _with_0.sndFallen = "" -- 180 + _with_0.decisionTree = "AI_Zombie" -- 181 + _with_0.usePreciseHit = false -- 182 + _with_0.actions = Array({ -- 184 + "walk", -- 184 + "turn", -- 185 + "meleeAttack", -- 186 + "idle", -- 187 + "cancel", -- 188 + "jump", -- 189 + "hit", -- 190 + "fall", -- 191 + "groundEntrance", -- 192 + "fallOff" -- 193 + }) -- 183 + Store["Unit_Zombie2"] = _with_0 -- 147 +end -- 147 diff --git a/Assets/Script/Game/Zombie Escape/init.lua b/Assets/Script/Game/Zombie Escape/init.lua new file mode 100644 index 000000000..f265cb1f1 --- /dev/null +++ b/Assets/Script/Game/Zombie Escape/init.lua @@ -0,0 +1,25 @@ +-- [yue]: Script/Game/Zombie Escape/init.yue +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +do -- 4 + local scriptPath = Path:getScriptPath(...) -- 4 + if scriptPath then -- 4 + Content:insertSearchPath(1, scriptPath) -- 5 + local _list_0 = { -- 7 + "Constant", -- 7 + "Unit", -- 8 + "Body", -- 9 + "Bullet", -- 10 + "Action", -- 11 + "AI", -- 12 + "Logic", -- 13 + "Control", -- 14 + "Scene", -- 15 + "Debug" -- 16 + } -- 6 + for _index_0 = 1, #_list_0 do -- 17 + local mod = _list_0[_index_0] -- 6 + require(Path(scriptPath, mod)) -- 6 + end -- 6 + end -- 4 +end -- 4 diff --git a/Assets/Script/Lib/BodyEx.lua b/Assets/Script/Lib/BodyEx.lua new file mode 100644 index 000000000..118ac4584 --- /dev/null +++ b/Assets/Script/Lib/BodyEx.lua @@ -0,0 +1,229 @@ +-- [yue]: Script/Lib/BodyEx.yue +local BodyDef = dora.BodyDef -- 1 +local JointDef = dora.JointDef -- 1 +local Dictionary = dora.Dictionary -- 1 +local Node = dora.Node -- 1 +local Vec2 = dora.Vec2 -- 1 +local tolua = dora.tolua -- 1 +local Body = dora.Body -- 1 +local Playable = dora.Playable -- 1 +local Sprite = dora.Sprite -- 1 +local Joint = dora.Joint -- 1 +local _module_0 = nil -- 1 +local Struct = require("Utils").Struct -- 2 +Struct.Array() -- 4 +Struct.Phyx.Rect("name", "type", "position", "angle", "center", "size", "density", "friction", "restitution", "linearDamping", "angularDamping", "fixedRotation", "linearAcceleration", "bullet", "sensor", "sensorTag", "subShapes", "face", "facePos") -- 6 +Struct.Phyx.Disk("name", "type", "position", "angle", "center", "radius", "density", "friction", "restitution", "linearDamping", "angularDamping", "fixedRotation", "linearAcceleration", "bullet", "sensor", "sensorTag", "subShapes", "face", "facePos") -- 27 +Struct.Phyx.Poly("name", "type", "position", "angle", "vertices", "density", "friction", "restitution", "linearDamping", "angularDamping", "fixedRotation", "linearAcceleration", "bullet", "sensor", "sensorTag", "subShapes", "face", "facePos") -- 48 +Struct.Phyx.Chain("name", "type", "position", "angle", "vertices", "friction", "restitution", "linearDamping", "angularDamping", "fixedRotation", "linearAcceleration", "bullet", "subShapes", "face", "facePos") -- 68 +Struct.Phyx.SubRect("center", "angle", "size", "density", "friction", "restitution", "sensor", "sensorTag") -- 85 +Struct.Phyx.SubDisk("center", "radius", "density", "friction", "restitution", "sensor", "sensorTag") -- 95 +Struct.Phyx.SubPoly("vertices", "density", "friction", "restitution", "sensor", "sensorTag") -- 104 +Struct.Phyx.SubChain("vertices", "friction", "restitution") -- 112 +Struct.Phyx.Distance("name", "collision", "bodyA", "bodyB", "anchorA", "anchorB", "frequency", "damping") -- 117 +Struct.Phyx.Friction("name", "collision", "bodyA", "bodyB", "worldPos", "maxForce", "maxTorque") -- 127 +Struct.Phyx.Gear("name", "collision", "jointA", "jointB", "ratio") -- 136 +Struct.Phyx.Spring("name", "collision", "bodyA", "bodyB", "linearOffset", "angularOffset", "maxForce", "maxTorque", "correctionFactor") -- 143 +Struct.Phyx.Prismatic("name", "collision", "bodyA", "bodyB", "worldPos", "axis", "lowerTranslation", "upperTranslation", "maxMotorForce", "motorSpeed") -- 154 +Struct.Phyx.Pulley("name", "collision", "bodyA", "bodyB", "anchorA", "anchorB", "groundAnchorA", "groundAnchorB", "ratio") -- 166 +Struct.Phyx.Revolute("name", "collision", "bodyA", "bodyB", "worldPos", "lowerAngle", "upperAngle", "maxMotorTorque", "motorSpeed") -- 177 +Struct.Phyx.Rope("name", "collision", "bodyA", "bodyB", "anchorA", "anchorB", "maxLength") -- 188 +Struct.Phyx.Weld("name", "collision", "bodyA", "bodyB", "worldPos", "frequency", "damping") -- 197 +Struct.Phyx.Wheel("name", "collision", "bodyA", "bodyB", "worldPos", "axis", "maxMotorTorque", "motorSpeed", "frequency", "damping") -- 206 +local loadFuncs = nil -- 219 +local loadData -- 220 +loadData = function(data, item) -- 220 + return loadFuncs[data[1]](data, item) -- 221 +end -- 220 +local toDef -- 223 +toDef = function(data) -- 223 + local _with_0 = BodyDef() -- 223 + _with_0.type = data.type -- 224 + _with_0.bullet = data.bullet -- 225 + _with_0.linearAcceleration = data.linearAcceleration -- 226 + _with_0.fixedRotation = data.fixedRotation -- 227 + _with_0.linearDamping = data.linearDamping -- 228 + _with_0.angularDamping = data.angularDamping -- 229 + _with_0.position = data.position -- 230 + _with_0.angle = data.angle -- 231 + _with_0.face = data.face -- 232 + _with_0.facePos = data.facePos -- 233 + return _with_0 -- 223 +end -- 223 +loadFuncs = { -- 236 + ["Array"] = function(data, itemDict) -- 236 + for i = 1, data:count() do -- 237 + loadData(data:get(i), itemDict) -- 238 + end -- 238 + end, -- 236 + ["Phyx.Rect"] = function(data, itemDict) -- 240 + local bodyDef = toDef(data) -- 241 + local width, height -- 242 + do -- 242 + local _obj_0 = data.size -- 242 + width, height = _obj_0.width, _obj_0.height -- 242 + end -- 242 + if data.sensor then -- 243 + bodyDef:attachPolygonSensor(data.sensorTag, data.center, width, height) -- 244 + else -- 246 + bodyDef:attachPolygon(data.center, width, height, 0, data.density, data.friction, data.restitution) -- 246 + end -- 243 + do -- 250 + local subShapes = data.subShapes -- 250 + if subShapes then -- 250 + for i = 1, subShapes:count() do -- 251 + loadData(subShapes:get(i), bodyDef) -- 252 + end -- 252 + end -- 250 + end -- 250 + itemDict[data.name] = bodyDef -- 253 + end, -- 240 + ["Phyx.Disk"] = function(data, itemDict) -- 255 + local bodyDef = toDef(data) -- 256 + if data.sensor then -- 257 + bodyDef:attachDiskSensor(data.sensorTag, data.center, data.radius) -- 258 + else -- 260 + bodyDef:attachDisk(data.center, data.radius, data.density, data.friction, data.restitution) -- 260 + end -- 257 + do -- 264 + local subShapes = data.subShapes -- 264 + if subShapes then -- 264 + for i = 1, subShapes:count() do -- 265 + loadData(subShapes:get(i), bodyDef) -- 266 + end -- 266 + end -- 264 + end -- 264 + itemDict[data.name] = bodyDef -- 267 + end, -- 255 + ["Phyx.Poly"] = function(data, itemDict) -- 269 + local bodyDef = toDef(data) -- 270 + if data.sensor then -- 271 + bodyDef:attachPolygonSensor(data.sensorTag, data.vertices:toArray()) -- 272 + else -- 274 + bodyDef:attachPolygon(data.vertices:toArray(), data.density, data.friction, data.restitution) -- 274 + end -- 271 + do -- 278 + local subShapes = data.subShapes -- 278 + if subShapes then -- 278 + for i = 1, subShapes:count() do -- 279 + loadData(subShapes:get(i), bodyDef) -- 280 + end -- 280 + end -- 278 + end -- 278 + itemDict[data.name] = bodyDef -- 281 + end, -- 269 + ["Phyx.Chain"] = function(data, itemDict) -- 283 + local bodyDef = toDef(data) -- 284 + bodyDef:attachChain(data.vertices:toArray(), data.friction, data.restitution) -- 285 + do -- 286 + local subShapes = data.subShapes -- 286 + if subShapes then -- 286 + for i = 1, subShapes:count() do -- 287 + loadData(subShapes:get(i), bodyDef) -- 288 + end -- 288 + end -- 286 + end -- 286 + itemDict[data.name] = bodyDef -- 289 + end, -- 283 + ["Phyx.SubRect"] = function(data, bodyDef) -- 291 + local width, height -- 292 + do -- 292 + local _obj_0 = data.size -- 292 + width, height = _obj_0.width, _obj_0.height -- 292 + end -- 292 + if data.sensor then -- 293 + return bodyDef:attachPolygonSensor(data.sensorTag, data.center, width, height) -- 294 + else -- 296 + return bodyDef:attachPolygon(data.center, width, height, data.angle, data.density, data.friction, data.restitution) -- 299 + end -- 293 + end, -- 291 + ["Phyx.SubDisk"] = function(data, bodyDef) -- 301 + if data.sensor then -- 302 + return bodyDef:attachDiskSensor(data.sensorTag, data.center, data.radius) -- 303 + else -- 305 + return bodyDef:attachDisk(data.center, data.radius, data.density, data.friction, data.restitution) -- 308 + end -- 302 + end, -- 301 + ["Phyx.SubPoly"] = function(data, bodyDef) -- 310 + if data.sensor then -- 311 + return bodyDef:attachPolygonSensor(data.sensorTag, data.vertices:toArray()) -- 312 + else -- 314 + return bodyDef:attachPolygon(data.vertices:toArray(), data.density, data.friction, data.restitution) -- 317 + end -- 311 + end, -- 310 + ["Phyx.SubChain"] = function(data, bodyDef) -- 319 + return bodyDef:attachChain(data.vertices:toArray(), data.friction, data.restitution) -- 320 + end, -- 319 + ["Phyx.Distance"] = function(data, itemDict) -- 322 + itemDict[data.name] = JointDef:distance(data.collision, data.bodyA, data.bodyB, data.anchorA, data.anchorB, data.frequency, data.damping) -- 323 + end, -- 322 + ["Phyx.Friction"] = function(data, itemDict) -- 332 + itemDict[data.name] = JointDef:friction(data.collision, data.bodyA, data.bodyB, data.worldPos, data.maxForce, data.maxTorque) -- 333 + end, -- 332 + ["Phyx.Gear"] = function(data, itemDict) -- 342 + itemDict[data.name] = JointDef:gear(data.collision, data.jointA, data.jointB, data.ratio) -- 343 + end, -- 342 + ["Phyx.Spring"] = function(data, itemDict) -- 350 + itemDict[data.name] = JointDef:spring(data.collision, data.bodyA, data.bodyB, data.linearOffset, data.angularOffset, data.maxForce, data.maxTorque, data.correctionFactor) -- 351 + end, -- 350 + ["Phyx.Prismatic"] = function(data, itemDict) -- 362 + itemDict[data.name] = JointDef:prismatic(data.collision, data.bodyA, data.bodyB, data.worldPos, data.axis, data.lowerTranslation, data.upperTranslation, data.maxMotorForce, data.motorSpeed) -- 363 + end, -- 362 + ["Phyx.Pulley"] = function(data, itemDict) -- 375 + itemDict[data.name] = JointDef:pulley(data.collision, data.bodyA, data.bodyB, data.anchorA, data.anchorB, data.groundAnchorA, data.groundAnchorB, data.ratio) -- 376 + end, -- 375 + ["Phyx.Revolute"] = function(data, itemDict) -- 387 + itemDict[data.name] = JointDef:revolute(data.collision, data.bodyA, data.bodyB, data.worldPos, data.lowerAngle, data.upperAngle, data.maxMotorTorque, data.motorSpeed) -- 388 + end, -- 387 + ["Phyx.Rope"] = function(data, itemDict) -- 399 + itemDict[data.name] = JointDef:rope(data.collision, data.bodyA, data.bodyB, data.anchorA, data.anchorB, data.maxLength) -- 400 + end, -- 399 + ["Phyx.Weld"] = function(data, itemDict) -- 409 + itemDict[data.name] = JointDef:weld(data.collision, data.bodyA, data.bodyB, data.worldPos, data.frequency, data.damping) -- 410 + end, -- 409 + ["Phyx.Wheel"] = function(data, itemDict) -- 419 + itemDict[data.name] = JointDef:wheel(data.collision, data.bodyA, data.bodyB, data.worldPos, data.axis, data.maxMotorTorque, data.motorSpeed, data.frequency, data.damping) -- 420 + end -- 419 +} -- 235 +_module_0 = function(bodyData, world, pos, angle) -- 432 + local itemDict = Dictionary() -- 433 + loadData(Struct:load(bodyData), itemDict) -- 434 + local root = Node() -- 435 + local items = root.data -- 436 + local center = Vec2.zero -- 437 + itemDict:each(function(itemDef, key) -- 438 + if "BodyDef" == tolua.type(itemDef) then -- 439 + local body = Body(itemDef, world, pos, angle) -- 440 + body.owner = root -- 441 + root:addChild(body) -- 442 + local faceStr = itemDef.face -- 443 + if faceStr ~= "" then -- 444 + local face -- 445 + if faceStr:match(":") then -- 445 + face = Playable(faceStr) -- 446 + else -- 448 + face = Sprite(faceStr) -- 448 + end -- 445 + if face then -- 449 + face.position = itemDef.facePos -- 450 + body:addChild(face) -- 451 + end -- 449 + end -- 444 + items[key] = body -- 452 + else -- 454 + if center then -- 454 + itemDef.center = center -- 455 + itemDef.position = pos -- 456 + itemDef.angle = angle -- 457 + end -- 454 + do -- 458 + local joint = Joint(itemDef, items) -- 458 + if joint then -- 458 + items[key] = joint -- 459 + end -- 458 + end -- 458 + end -- 439 + end) -- 438 + return root -- 460 +end -- 432 +return _module_0 -- 460 diff --git a/Assets/Script/Lib/Config.lua b/Assets/Script/Lib/Config.lua new file mode 100644 index 000000000..cdaccbc65 --- /dev/null +++ b/Assets/Script/Lib/Config.lua @@ -0,0 +1,123 @@ +local Struct = require("Utils").Struct +local DB = require("DB") +local thread = require("thread") + + + + + + +return function(schema, ...) + schema = schema or "" + local Config = + schema == "" and + Struct.Config(...) or + Struct[schema].Config(...) + local tableName = schema == "" and "Config" or schema .. ".Config" + local conf = Config() + local oldValues = {} + local insertValues = {} + local updateValues = {} + local deleteValues = {} + local loaded = false + local function notify(event, key, value) + assert(loaded, "Config should be loaded before updating") + if event == "Modified" then + if oldValues[key] == nil then + insertValues[key] = value + elseif value == nil then + deleteValues[#deleteValues + 1] = key + elseif oldValues[key] ~= value then + updateValues[key] = value + end + oldValues[key] = value + elseif event == "Updated" then + local iValues = {} + for k, v in pairs(insertValues) do + local num = false + local str = false + if type(v) == "number" then + num = v + elseif type(v) == "string" then + str = v + end + iValues[#iValues + 1] = { k, num, str } + end + insertValues = {} + local uValues = {} + for k, v in pairs(updateValues) do + local num = false + local str = false + if type(v) == "number" then + num = v + elseif type(v) == "string" then + str = v + end + uValues[#uValues + 1] = { num, str, k } + end + updateValues = {} + local dValues = {} + for i = 1, #deleteValues do + dValues[#dValues + 1] = { deleteValues[i] } + end + deleteValues = {} + thread(function() + if #iValues > 0 then + DB:insertAsync(tableName, iValues) + end + if #uValues > 0 then + DB:execAsync("update " .. tableName .. " set value_num = ?, value_str = ? where name = ?", uValues) + end + if #dValues > 0 then + DB:execAsync("delete from " .. tableName .. " where name = ?", dValues) + end + end) + end + end + + if not DB:exist("Config", schema) then + DB:exec([[ + CREATE TABLE ]] .. tableName .. [[( + name TEXT(90) NOT NULL, --配置项名称 + value_num REAL(24,6), --配置项数值 + value_str TEXT(255), --配置项文本 + PRIMARY KEY (name) + ); --通用配置表 + ]]) + end + + local function initConfig(self, rows) + local mt = getmetatable(self) + local fields = {} + for i = 1, #mt do + local fieldName = mt[i] + fields[fieldName] = true + end + for i = 1, #rows do + local key = rows[i][1] + if fields[key] then + local value = rows[i][2] or rows[i][3] + oldValues[key] = value + self[key] = value + else + print("Config key \"" .. key .. "\" is no longer exist") + end + end + end + + rawset(conf, "loadAsync", function(self) + local rows = DB:queryAsync("select name, value_num, value_str from " .. tableName) + loaded = true + initConfig(self, rows) + end) + + rawset(conf, "load", function(self) + local rows = DB:query("select name, value_num, value_str from " .. tableName) + loaded = true + initConfig(self, rows) + end) + + rawset(conf, "__notify", notify) + + return conf +end \ No newline at end of file diff --git a/Assets/Script/Lib/Dora/en/TouchType.lua b/Assets/Script/Lib/Dora/en/TouchType.lua new file mode 100644 index 000000000..30051380e --- /dev/null +++ b/Assets/Script/Lib/Dora/en/TouchType.lua @@ -0,0 +1,32 @@ +local Object = require("Object").Type +local Vec2 = require("Vec2").Type + + +local Touch = {} + + + + + + + + + + + + + + + + + + + + + + + + + + +return Touch \ No newline at end of file diff --git a/Assets/Script/Lib/Dora/en/lua.d.tl b/Assets/Script/Lib/Dora/en/lua.d.tl index ebe0b6766..4be655e03 100644 --- a/Assets/Script/Lib/Dora/en/lua.d.tl +++ b/Assets/Script/Lib/Dora/en/lua.d.tl @@ -935,11 +935,6 @@ local record lua -- @return (any, boolean) The user value and a boolean status. getuservalue: function(u: Userdata, n: number): any, boolean - -- Sets a new limit for the C stack. - -- @param limit (number) The new C stack limit. - -- @return (boolean | number) False if invalid, or the old limit. - setcstacklimit: function(limit: number): boolean | number - -- Sets a debug hook function. -- @param thread (thread) [optional] The thread to operate over. -- @param hook (function) The hook function. diff --git a/Assets/Script/Lib/Dora/zh-Hans/TouchType.lua b/Assets/Script/Lib/Dora/zh-Hans/TouchType.lua new file mode 100644 index 000000000..30051380e --- /dev/null +++ b/Assets/Script/Lib/Dora/zh-Hans/TouchType.lua @@ -0,0 +1,32 @@ +local Object = require("Object").Type +local Vec2 = require("Vec2").Type + + +local Touch = {} + + + + + + + + + + + + + + + + + + + + + + + + + + +return Touch \ No newline at end of file diff --git a/Assets/Script/Lib/Dora/zh-Hans/lua.d.tl b/Assets/Script/Lib/Dora/zh-Hans/lua.d.tl index e06210397..a051ca417 100644 --- a/Assets/Script/Lib/Dora/zh-Hans/lua.d.tl +++ b/Assets/Script/Lib/Dora/zh-Hans/lua.d.tl @@ -934,11 +934,6 @@ local record lua -- @return (any, boolean) 用户值和一个布尔状态。 getuservalue: function(u: Userdata, n: number): any, boolean - -- 为C栈设置新限制。 - -- @param limit (number) 新C栈限制。 - -- @return (boolean | number) 如果无效则为false,或旧限制。 - setcstacklimit: function(limit: number): boolean | number - -- 设置调试钩子函数。 -- @param thread (thread) [可选] 要操作的线程。 -- @param hook (function) 钩子函数。 diff --git a/Assets/Script/Lib/UI/Control/Basic/AlignNode.lua b/Assets/Script/Lib/UI/Control/Basic/AlignNode.lua new file mode 100644 index 000000000..b0c87aedc --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/AlignNode.lua @@ -0,0 +1,134 @@ +-- [yue]: Script/Lib/UI/Control/Basic/AlignNode.yue +local Class = dora.Class -- 1 +local Node = dora.Node -- 1 +local Vec2 = dora.Vec2 -- 1 +local App = dora.App -- 1 +local View = dora.View -- 1 +local load = _G.load -- 1 +local tostring = _G.tostring -- 1 +local _module_0 = nil -- 1 +_module_0 = Class(Node, { -- 4 + __init = function(self, args) -- 4 + local isRoot, inUI, hAlign, vAlign, alignOffset, alignWidth, alignHeight -- 5 + do -- 5 + local _obj_0 = args or { } -- 13 + isRoot, inUI, hAlign, vAlign, alignOffset, alignWidth, alignHeight = _obj_0.isRoot, _obj_0.inUI, _obj_0.hAlign, _obj_0.vAlign, _obj_0.alignOffset, _obj_0.alignWidth, _obj_0.alignHeight -- 5 + if isRoot == nil then -- 6 + isRoot = false -- 6 + end -- 6 + if inUI == nil then -- 7 + inUI = true -- 7 + end -- 7 + if hAlign == nil then -- 8 + hAlign = "Center" -- 8 + end -- 8 + if vAlign == nil then -- 9 + vAlign = "Center" -- 9 + end -- 9 + if alignOffset == nil then -- 10 + alignOffset = Vec2.zero -- 10 + end -- 10 + end -- 13 + self.inUI = inUI -- 14 + self._isRoot = isRoot -- 15 + if self._isRoot then -- 16 + local viewSize = inUI and App.bufferSize or View.size -- 17 + self.size = viewSize -- 18 + self._viewSize = viewSize -- 19 + self:gslot("AppSizeChanged", function() -- 20 + viewSize = self.inUI and App.bufferSize or View.size -- 21 + if self._viewSize ~= viewSize then -- 22 + self._viewSize = viewSize -- 23 + self.size = viewSize -- 24 + local width, height = viewSize.width, viewSize.height -- 25 + self:emit("AlignLayout", width, height) -- 26 + return self:eachChild(function(child) -- 27 + return child:emit("AlignLayout", width, height) -- 28 + end) -- 28 + end -- 22 + end) -- 20 + return self:slot("Enter", function() -- 29 + local width, height -- 30 + do -- 30 + local _obj_0 = self.inUI and App.bufferSize or View.size -- 30 + width, height = _obj_0.width, _obj_0.height -- 30 + end -- 30 + self:emit("AlignLayout", width, height) -- 31 + return self:eachChild(function(child) -- 32 + return child:emit("AlignLayout", width, height) -- 33 + end) -- 33 + end) -- 33 + else -- 35 + self.hAlign = hAlign -- 35 + self.vAlign = vAlign -- 36 + self.alignOffset = alignOffset -- 37 + self.alignWidth = alignWidth -- 38 + self.alignHeight = alignHeight -- 39 + return self:slot("AlignLayout", function(w, h) -- 40 + local env = { -- 41 + w = w, -- 41 + h = h -- 41 + } -- 41 + local oldSize = self.size -- 42 + if self.alignWidth then -- 43 + local widthFunc = load("local _ENV = " .. "Dora(...)\nreturn " .. tostring(self.alignWidth)) -- 44 + self.width = widthFunc(env) -- 45 + end -- 43 + if self.alignHeight then -- 46 + local heightFunc = load("local _ENV = " .. "Dora(...)\nreturn " .. tostring(self.alignHeight)) -- 47 + self.height = heightFunc(env) -- 48 + end -- 46 + do -- 49 + local _exp_0 = self.hAlign -- 49 + if "Left" == _exp_0 then -- 50 + self.x = self.width / 2 + self.alignOffset.x -- 50 + elseif "Center" == _exp_0 then -- 51 + self.x = w / 2 + self.alignOffset.x -- 51 + elseif "Right" == _exp_0 then -- 52 + self.x = w - self.width / 2 - self.alignOffset.x -- 52 + end -- 52 + end -- 52 + do -- 53 + local _exp_0 = self.vAlign -- 53 + if "Bottom" == _exp_0 then -- 54 + self.y = self.height / 2 + self.alignOffset.y -- 54 + elseif "Center" == _exp_0 then -- 55 + self.y = h / 2 + self.alignOffset.y -- 55 + elseif "Top" == _exp_0 then -- 56 + self.y = h - self.height / 2 - self.alignOffset.y -- 56 + end -- 56 + end -- 56 + local newSize = self.size -- 57 + if oldSize ~= newSize then -- 58 + local width, height = newSize.width, newSize.height -- 59 + return self:eachChild(function(child) -- 60 + return child:emit("AlignLayout", width, height) -- 61 + end) -- 61 + end -- 58 + end) -- 61 + end -- 16 + end, -- 4 + alignLayout = function(self) -- 63 + if self._isRoot then -- 64 + local width, height -- 65 + do -- 65 + local _obj_0 = self.inUI and App.bufferSize or View.size -- 65 + width, height = _obj_0.width, _obj_0.height -- 65 + end -- 65 + self:emit("AlignLayout", width, height) -- 66 + return self:eachChild(function(child) -- 67 + return child:emit("AlignLayout", width, height) -- 68 + end) -- 68 + else -- 70 + local width, height -- 70 + do -- 70 + local _obj_0 = self.size -- 70 + width, height = _obj_0.width, _obj_0.height -- 70 + end -- 70 + return self:eachChild(function(child) -- 71 + return child:emit("AlignLayout", width, height) -- 72 + end) -- 72 + end -- 64 + end -- 63 +}) -- 3 +return _module_0 -- 72 diff --git a/Assets/Script/Lib/UI/Control/Basic/Button.lua b/Assets/Script/Lib/UI/Control/Basic/Button.lua new file mode 100644 index 000000000..800da37e9 --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/Button.lua @@ -0,0 +1,36 @@ +-- [yue]: Script/Lib/UI/Control/Basic/Button.yue +local Class = dora.Class -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local property = dora.property -- 1 +local _module_0 = nil -- 1 +local Button = require("UI.View.Control.Basic.Button") -- 2 +_module_0 = Class(Button, { -- 9 + __init = function(self, args) -- 9 + if self.label then -- 10 + self._text = self.label.text -- 10 + end -- 10 + self:slot("TapFilter", function(touch) -- 11 + if not touch.first then -- 12 + touch.enabled = false -- 12 + end -- 12 + end) -- 11 + return self:slot("Tapped", function() -- 13 + local enabled = self.touchEnabled -- 14 + self.touchEnabled = false -- 15 + return self:schedule(once(function() -- 16 + sleep() -- 17 + self.touchEnabled = enabled -- 18 + end)) -- 18 + end) -- 18 + end, -- 9 + text = property((function(self) -- 20 + return self._text -- 20 + end), function(self, value) -- 21 + self._text = value -- 22 + if self.label then -- 23 + self.label.text = value -- 23 + end -- 23 + end) -- 20 +}) -- 8 +return _module_0 -- 23 diff --git a/Assets/Script/Lib/UI/Control/Basic/CircleButton.lua b/Assets/Script/Lib/UI/Control/Basic/CircleButton.lua new file mode 100644 index 000000000..35b0ecb08 --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/CircleButton.lua @@ -0,0 +1,36 @@ +-- [yue]: Script/Lib/UI/Control/Basic/CircleButton.yue +local Class = dora.Class -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local property = dora.property -- 1 +local _module_0 = nil -- 1 +local CircleButton = require("UI.View.Control.Basic.CircleButton") -- 2 +_module_0 = Class(CircleButton, { -- 9 + __init = function(self, args) -- 9 + if self.label then -- 10 + self._text = self.label.text -- 10 + end -- 10 + self:slot("TapFilter", function(touch) -- 11 + if not touch.first then -- 12 + touch.enabled = false -- 12 + end -- 12 + end) -- 11 + return self:slot("Tapped", function() -- 13 + local enabled = self.touchEnabled -- 14 + self.touchEnabled = false -- 15 + return self:schedule(once(function() -- 16 + sleep() -- 17 + self.touchEnabled = enabled -- 18 + end)) -- 18 + end) -- 18 + end, -- 9 + text = property((function(self) -- 20 + return self._text -- 20 + end), function(self, value) -- 21 + self._text = value -- 22 + if self.label then -- 23 + self.label.text = value -- 23 + end -- 23 + end) -- 20 +}) -- 8 +return _module_0 -- 23 diff --git a/Assets/Script/Lib/UI/Control/Basic/FixedLabel.lua b/Assets/Script/Lib/UI/Control/Basic/FixedLabel.lua new file mode 100644 index 000000000..d9f39774b --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/FixedLabel.lua @@ -0,0 +1,99 @@ +-- [yue]: Script/Lib/UI/Control/Basic/FixedLabel.yue +local Class = dora.Class -- 1 +local Node = dora.Node -- 1 +local Color = dora.Color -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local Label = dora.Label -- 1 +local property = dora.property -- 1 +local utf8 = require("utf-8") -- 2 +return Class(Node, { -- 5 + __init = function(self, args) -- 5 + local x, y, width, height, text, fontName, fontSize, textAlign, color3, renderOrder = args.x, args.y, args.width, args.height, args.text, args.fontName, args.fontSize, args.textAlign, args.color3, args.renderOrder -- 6 + if x == nil then -- 7 + x = 0 -- 7 + end -- 7 + if y == nil then -- 8 + y = 0 -- 8 + end -- 8 + if width == nil then -- 9 + width = 0 -- 9 + end -- 9 + if height == nil then -- 10 + height = 0 -- 10 + end -- 10 + if text == nil then -- 11 + text = "" -- 11 + end -- 11 + if fontName == nil then -- 12 + fontName = "sarasa-mono-sc-regular" -- 12 + end -- 12 + if fontSize == nil then -- 13 + fontSize = 25 -- 13 + end -- 13 + if textAlign == nil then -- 14 + textAlign = "Left" -- 14 + end -- 14 + if color3 == nil then -- 15 + color3 = Color(0xffffff) -- 15 + end -- 15 + if renderOrder == nil then -- 16 + renderOrder = 0 -- 16 + end -- 16 + self.position = Vec2(x, y) -- 18 + self.size = Size(width, height) -- 19 + local label -- 20 + do -- 20 + local _with_0 = Label(fontName, fontSize) -- 20 + _with_0.batched = false -- 21 + _with_0.alignment = textAlign -- 22 + _with_0.renderOrder = renderOrder -- 23 + _with_0.textWidth = width + 5 -- 24 + if "Center" == textAlign then -- 26 + _with_0.position = Vec2(0.5, 0.5) * self.size -- 27 + elseif "Left" == textAlign then -- 28 + _with_0.y = height / 2 -- 29 + _with_0.anchor = Vec2(0, 0.5) -- 30 + elseif "Right" == textAlign then -- 31 + _with_0.x = width -- 32 + _with_0.y = height / 2 -- 33 + _with_0.anchor = Vec2(1, 0.5) -- 34 + end -- 34 + label = _with_0 -- 20 + end -- 20 + self:addChild(label) -- 35 + self._label = label -- 36 + self.text = text -- 37 + end, -- 5 + text = property((function(self) -- 39 + return self._text -- 39 + end), function(self, value) -- 40 + self._text = value -- 41 + self._label.text = value -- 42 + local width, height = self.width, self.height -- 43 + local charCount = self._label.characterCount -- 44 + if charCount > 0 then -- 45 + local char = self._label:getCharacter(1) -- 46 + if not char then -- 47 + return -- 47 + end -- 47 + local left = char.x - char.width / 2 -- 48 + local top = char.y + char.height / 2 -- 49 + for i = 2, charCount do -- 50 + char = self._label:getCharacter(i) -- 51 + if not (char and char.visible) then -- 52 + goto _continue_0 -- 52 + end -- 52 + local right = char.x + char.width / 2 -- 53 + local bottom = char.y - char.height / 2 -- 54 + if (right - left) > width or (top - bottom) > height then -- 55 + local displayText = utf8.sub(value, 1, i - 4) -- 56 + displayText = displayText .. "..." -- 57 + self._label.text = displayText -- 58 + break -- 59 + end -- 55 + ::_continue_0:: -- 51 + end -- 59 + end -- 45 + end) -- 39 +}) -- 59 diff --git a/Assets/Script/Lib/UI/Control/Basic/Ruler.lua b/Assets/Script/Lib/UI/Control/Basic/Ruler.lua new file mode 100644 index 000000000..0e4092654 --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/Ruler.lua @@ -0,0 +1,344 @@ +-- [yue]: Script/Lib/UI/Control/Basic/Ruler.yue +local Class = dora.Class -- 1 +local View = dora.View -- 1 +local math = _G.math -- 1 +local Label = dora.Label -- 1 +local string = _G.string -- 1 +local Vec2 = dora.Vec2 -- 1 +local tostring = _G.tostring -- 1 +local table = _G.table -- 1 +local tonumber = _G.tonumber -- 1 +local Color = dora.Color -- 1 +local ScaleX = dora.ScaleX -- 1 +local Ease = dora.Ease -- 1 +local once = dora.once -- 1 +local cycle = dora.cycle -- 1 +local App = dora.App -- 1 +local Node = dora.Node -- 1 +local Size = dora.Size -- 1 +local pairs = _G.pairs -- 1 +local Spawn = dora.Spawn -- 1 +local Y = dora.Y -- 1 +local Opacity = dora.Opacity -- 1 +local Sequence = dora.Sequence -- 1 +local Hide = dora.Hide -- 1 +local _module_0 = nil -- 1 +local Ruler = require("UI.View.Control.Basic.Ruler") -- 2 +local Round = require("Utils").Round -- 3 +_module_0 = Class(Ruler, { -- 6 + __init = function(self, args) -- 6 + local y, width, height, fontName, fontSize, fixed = args.y, args.width, args.height, args.fontName, args.fontSize, args.fixed -- 7 + local viewSize = View.size -- 8 + local halfW = width / 2 -- 9 + local halfH = height / 2 -- 10 + local interval = 10 -- 11 + local indent = 100 -- 12 + if fontSize == nil then -- 13 + fontSize = 12 -- 13 + end -- 13 + local vsCache = { } -- 14 + self.endPosY = y -- 15 + if fixed ~= nil then -- 16 + self.isFixed = fixed -- 16 + else -- 16 + self.isFixed = true -- 16 + end -- 16 + local labels = { } -- 18 + local labelList = { } -- 19 + local len = nil -- 20 + do -- 21 + local posX = self.intervalNode.anchor.x * width -- 22 + local center = Round(posX / 100) -- 23 + len = Round((posX + halfW) / 100 - center) -- 24 + len = 1 + math.max((center - Round((posX - halfW) / 100)), len) -- 25 + for i = center - len, center + len do -- 26 + local pos = i * 100 -- 27 + local label -- 28 + do -- 28 + local _with_0 = Label(fontName, fontSize) -- 28 + _with_0.text = string.format("%.0f", pos / 100 * indent) -- 29 + _with_0.scaleX = 1 / self.intervalNode.scaleX -- 30 + _with_0.position = Vec2(pos, halfH - 18 - fontSize) -- 31 + _with_0.tag = tostring(pos) -- 32 + label = _with_0 -- 28 + end -- 28 + self.intervalNode:addChild(label) -- 33 + labels[pos] = label -- 34 + table.insert(labelList, label) -- 35 + end -- 35 + end -- 35 + local moveLabel -- 37 + moveLabel = function(label, pos) -- 37 + labels[tonumber(label.tag)] = nil -- 38 + label.text = string.format("%.0f", pos / 100 * indent) -- 40 + label.scaleX = 1 / self.intervalNode.scaleX -- 41 + label.position = Vec2(pos, halfH - 18 - fontSize) -- 42 + label.tag = tostring(pos) -- 43 + labels[pos] = label -- 39 + end -- 37 + local updateLabels -- 45 + updateLabels = function() -- 45 + local posX = self.intervalNode.anchor.x * width -- 46 + local center = math.floor(posX / 100) -- 47 + local right = center + len -- 48 + local left = center - len -- 49 + local insertPos = 1 -- 50 + for i = left, right do -- 51 + local pos = i * 100 -- 52 + if labels[pos] then -- 53 + break -- 54 + else -- 56 + local label = table.remove(labelList) -- 56 + table.insert(labelList, insertPos, label) -- 57 + insertPos = insertPos + 1 -- 58 + moveLabel(label, pos) -- 59 + end -- 53 + end -- 59 + insertPos = #labelList -- 60 + for i = right, left, -1 do -- 61 + local pos = i * 100 -- 62 + if labels[pos] then -- 63 + break -- 64 + else -- 66 + local label = table.remove(labelList, 1) -- 66 + table.insert(labelList, insertPos, label) -- 67 + insertPos = insertPos - 1 -- 68 + moveLabel(label, pos) -- 69 + end -- 63 + end -- 69 + local scale = self.intervalNode.scaleX -- 71 + local current = Round(self.intervalNode.anchor.x * width / interval) -- 72 + local delta = 1 + math.ceil(halfW / scale / interval) -- 73 + local max = current + delta -- 74 + local min = current - delta -- 75 + local count = 1 -- 76 + local vs = { } -- 77 + for i = min, max do -- 78 + posX = i * interval -- 79 + local v = vsCache[count] -- 80 + if v then -- 81 + v = Vec2(posX, halfH) -- 81 + else -- 83 + v = Vec2(posX, halfH) -- 83 + vsCache[count] = v -- 84 + end -- 81 + vs[count] = v -- 85 + count = count + 1 -- 86 + v = vsCache[count] -- 87 + if v then -- 88 + v = Vec2(posX, halfH - (i % 10 == 0 and fontSize + 6 or fontSize - 2)) -- 88 + else -- 90 + v = Vec2(posX, halfH - (i % 10 == 0 and fontSize + 6 or fontSize - 2)) -- 90 + vsCache[count] = v -- 91 + end -- 88 + vs[count] = v -- 92 + count = count + 1 -- 93 + v = vsCache[count] -- 94 + if v then -- 95 + v = Vec2(posX, halfH) -- 95 + else -- 97 + v = Vec2(posX, halfH) -- 97 + vsCache[count] = v -- 98 + end -- 95 + vs[count] = v -- 99 + count = count + 1 -- 100 + end -- 100 + return self.intervalNode:set(vs, Color(0xffffffff)) -- 101 + end -- 45 + local updateIntervalTextScale -- 103 + updateIntervalTextScale = function(scale) -- 103 + return self.intervalNode:eachChild(function(child) -- 104 + child.scaleX = scale -- 105 + end) -- 105 + end -- 103 + self.makeScale = function(self, scale) -- 107 + scale = math.min(scale, 5) -- 108 + self.intervalNode.scaleX = scale -- 109 + updateIntervalTextScale(1 / scale) -- 111 + return updateLabels() -- 112 + end -- 107 + self.makeScaleTo = function(self, scale) -- 114 + do -- 115 + local _with_0 = self.intervalNode -- 115 + _with_0:perform(ScaleX(0.5, self.intervalNode.scaleX, scale, Ease.OutQuad)) -- 116 + _with_0:schedule(once(function() -- 118 + return cycle(0.5, function() -- 118 + return updateIntervalTextScale(1 / _with_0.scaleX) -- 118 + end) -- 118 + end)) -- 118 + end -- 115 + return updateLabels() -- 119 + end -- 114 + local _value = 0 -- 121 + local _max = 0 -- 122 + local _min = 0 -- 123 + do -- 125 + local _exp_0 = App.platform -- 125 + if "macOS" == _exp_0 or "Windows" == _exp_0 or "Linux" == _exp_0 then -- 126 + self:addChild((function() -- 127 + local _with_0 = Node() -- 127 + _with_0.size = Size(width, height) -- 128 + _with_0.touchEnabled = true -- 129 + _with_0.swallowMouseWheel = true -- 130 + _with_0:slot("MouseWheel", function(delta) -- 131 + local newVal = self:getValue() + delta.y * indent / 10 -- 132 + return self:setValue(_min < _max and math.min(math.max(_min, newVal), _max) or newVal) -- 133 + end) -- 131 + return _with_0 -- 127 + end)()) -- 127 + end -- 133 + end -- 133 + self.setIndent = function(self, ind) -- 135 + indent = ind -- 136 + for i, label in pairs(labels) do -- 137 + label.text = string.format("%.0f", ind * i / 100) -- 138 + end -- 138 + end -- 135 + self.getIndent = function(self) -- 139 + return indent -- 139 + end -- 139 + self.lastValue = nil -- 141 + self.setValue = function(self, v) -- 142 + _value = v -- 143 + local val = _min < _max and math.min(math.max(_value, _min), _max) or _value -- 144 + val = self.isFixed and Round(val / (indent / 10)) * (indent / 10) or val -- 145 + if val == -0 then -- 146 + val = 0 -- 146 + end -- 146 + if self.lastValue ~= val then -- 147 + self.lastValue = val -- 148 + self:emit("Changed", val) -- 149 + end -- 147 + local posX = v * 10 * interval / indent -- 150 + self.intervalNode.anchor = Vec2(posX / width, 0) -- 151 + return updateLabels() -- 152 + end -- 142 + self.getValue = function(self) -- 154 + return _value -- 154 + end -- 154 + self.getPos = function(self) -- 155 + return _value * 10 * interval / indent -- 155 + end -- 155 + self.setLimit = function(self, min, max) -- 157 + _max = max -- 158 + _min = min -- 159 + end -- 157 + local time = 0 -- 161 + local startPos = 0 -- 162 + local updateReset -- 163 + updateReset = function(deltaTime) -- 163 + if _min >= _max then -- 164 + return -- 164 + end -- 164 + local scale = self.intervalNode.scaleX -- 165 + time = time + deltaTime -- 166 + local t = time / 1 -- 167 + if scale < 1 then -- 168 + t = t / 0.1 -- 168 + end -- 168 + t = math.min(1, t) -- 169 + local yVal = nil -- 170 + if startPos < _min then -- 171 + yVal = startPos + (_min - startPos) * Ease:func(scale < 1 and Ease.Linear or Ease.OutElastic, t) -- 172 + elseif startPos > _max then -- 173 + yVal = startPos + (_max - startPos) * Ease:func(scale < 1 and Ease.Linear or Ease.OutElastic, t) -- 174 + end -- 171 + self:setValue(((yVal and yVal or 0) - _value) / scale + _value) -- 175 + if t == 1.0 then -- 176 + return self:unschedule() -- 176 + end -- 176 + end -- 163 + local isReseting -- 178 + isReseting = function() -- 178 + return _min < _max and (_value > _max or _value < _min) -- 179 + end -- 178 + local startReset -- 181 + startReset = function() -- 181 + startPos = _value -- 182 + time = 0 -- 183 + return self:schedule(updateReset) -- 184 + end -- 181 + local _v = 0 -- 186 + local _s = 0 -- 187 + local updateSpeed -- 188 + updateSpeed = function(deltaTime) -- 188 + if _s == 0 then -- 189 + return -- 189 + end -- 189 + _v = _s / deltaTime -- 190 + _s = 0 -- 191 + end -- 188 + local updatePos -- 193 + updatePos = function(deltaTime) -- 193 + local val = viewSize.height * 2 -- 194 + local a = _v > 0 and -val or val -- 195 + local yR = _v > 0 -- 196 + _v = _v + a * deltaTime -- 197 + if (_v < 0) == yR then -- 198 + _v = 0 -- 199 + a = 0 -- 200 + end -- 198 + local ds = _v * deltaTime + a * (0.5 * deltaTime * deltaTime) -- 201 + local newValue = _value - ds * indent / (interval * 10) -- 202 + self:setValue((newValue - _value) / self.intervalNode.scaleY + _value) -- 203 + if _v == 0 or isReseting() then -- 204 + if isReseting() then -- 205 + return startReset() -- 205 + else -- 206 + return self:unschedule() -- 206 + end -- 205 + end -- 204 + end -- 193 + self:slot("TapFilter", function(touch) -- 208 + if not touch.first then -- 209 + touch.enabled = false -- 209 + end -- 209 + end) -- 208 + self:slot("TapBegan", function() -- 211 + _s = 0 -- 212 + _v = 0 -- 213 + return self:schedule(updateSpeed) -- 214 + end) -- 211 + self:slot("TapMoved", function(touch) -- 216 + local deltaX = touch.delta.x -- 217 + local v = _value - deltaX * indent / (interval * 10) -- 218 + local padding = 0.5 * indent -- 219 + if _max > _min then -- 220 + local d = 1 -- 221 + if v > _max then -- 222 + d = (v - _max) * 3 / padding -- 223 + elseif v < _min then -- 224 + d = (_min - v) * 3 / padding -- 225 + end -- 222 + v = _value + (v - _value) / (d < 1 and 1 or d * d) -- 226 + end -- 220 + self:setValue((v - _value) / self.intervalNode.scaleX + _value) -- 227 + _s = _s + deltaX -- 228 + end) -- 216 + return self:slot("TapEnded", function() -- 230 + if isReseting() then -- 231 + return startReset() -- 232 + elseif _v ~= 0 then -- 233 + return self:schedule(updatePos) -- 234 + end -- 231 + end) -- 234 + end, -- 6 + show = function(self, default, min, max, ind, callback) -- 236 + self:setLimit(min, max) -- 237 + self:setIndent(ind) -- 238 + self:slot("Changed"):set(callback) -- 239 + self.lastValue = nil -- 240 + self:setValue(default) -- 241 + self.visible = true -- 242 + return self:perform(Spawn(Y(0.5, self.endPosY + 30, self.endPosY, Ease.OutBack), Opacity(0.3, self.opacity, 1))) -- 246 + end, -- 236 + hide = function(self) -- 248 + if not self.visible then -- 249 + return -- 249 + end -- 249 + self:slot("Changed", nil) -- 250 + self:unschedule() -- 251 + return self:perform(Sequence(Spawn(Y(0.5, self.y, self.endPosY + 30, Ease.InBack), Opacity(0.5, self.opacity, 0)), Hide())) -- 258 + end -- 248 +}) -- 5 +return _module_0 -- 258 diff --git a/Assets/Script/Lib/UI/Control/Basic/ScrollArea.lua b/Assets/Script/Lib/UI/Control/Basic/ScrollArea.lua new file mode 100644 index 000000000..235580529 --- /dev/null +++ b/Assets/Script/Lib/UI/Control/Basic/ScrollArea.lua @@ -0,0 +1,456 @@ +-- [yue]: Script/Lib/UI/Control/Basic/ScrollArea.yue +local Class = dora.Class -- 1 +local App = dora.App -- 1 +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local View = dora.View -- 1 +local Size = dora.Size -- 1 +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local Ease = dora.Ease -- 1 +local Rect = dora.Rect -- 1 +local Sequence = dora.Sequence -- 1 +local Show = dora.Show -- 1 +local Opacity = dora.Opacity -- 1 +local Delay = dora.Delay -- 1 +local Hide = dora.Hide -- 1 +local Action = dora.Action -- 1 +local property = dora.property -- 1 +local cycle = dora.cycle -- 1 +local _module_0 = nil -- 1 +local ScrollArea = require("UI.View.Control.Basic.ScrollArea") -- 2 +local SolidRect = require("UI.View.Shape.SolidRect") -- 3 +_module_0 = Class(ScrollArea, { -- 16 + __init = function(self, args) -- 16 + local selfX, selfY, width, height, viewWidth, viewHeight, scrollBar, scrollBarColor3, clipping = args.x, args.y, args.width, args.height, args.viewWidth, args.viewHeight, args.scrollBar, args.scrollBarColor3, args.clipping -- 17 + if selfX == nil then -- 18 + selfX = 0 -- 18 + end -- 18 + if selfY == nil then -- 19 + selfY = 0 -- 19 + end -- 19 + if width == nil then -- 20 + width = 0 -- 20 + end -- 20 + if height == nil then -- 21 + height = 0 -- 21 + end -- 21 + if viewWidth == nil then -- 22 + viewWidth = width -- 22 + end -- 22 + if viewHeight == nil then -- 23 + viewHeight = height -- 23 + end -- 23 + if scrollBar == nil then -- 24 + scrollBar = true -- 24 + end -- 24 + if scrollBarColor3 == nil then -- 25 + scrollBarColor3 = App.themeColor:toARGB() -- 25 + end -- 25 + if clipping == nil then -- 26 + clipping = true -- 26 + end -- 26 + self.x = selfX -- 28 + self.y = selfY -- 29 + viewWidth = math.max(viewWidth, width) -- 30 + viewHeight = math.max(viewHeight, height) -- 31 + local screenSize = (Vec2(View.size)).length -- 32 + local moveY = viewHeight - height -- 33 + local moveX = viewWidth - width -- 34 + local deltaX, deltaY = 0, 0 -- 35 + local paddingX, paddingY = args.paddingX, args.paddingY -- 36 + if paddingX == nil then -- 36 + paddingX = 200 -- 36 + end -- 36 + if paddingY == nil then -- 36 + paddingY = 200 -- 36 + end -- 36 + local posX, posY = 0, 0 -- 37 + local timePassed = 0 -- 38 + local S = Vec2.zero -- 39 + local V = Vec2.zero -- 40 + local deltaMoveLength = 0 -- 41 + self.contentSize = Size(width, height) -- 42 + self:setupMenuScroll(self.view) -- 43 + self.view:slot("Tapped", function() -- 45 + local enabled = self.view.touchEnabled -- 46 + self.view.touchEnabled = false -- 47 + return self.view:schedule(once(function() -- 48 + sleep() -- 49 + self.view.touchEnabled = enabled -- 50 + end)) -- 50 + end) -- 45 + local updateReset -- 52 + updateReset = function(deltaTime) -- 52 + local x, y -- 53 + timePassed = timePassed + deltaTime -- 54 + local t = math.min(timePassed * 4, 1) -- 55 + do -- 56 + local _with_0 = Ease -- 56 + if posX < -moveX then -- 57 + local tmp = deltaX -- 58 + deltaX = posX - (moveX + posX) * _with_0:func(_with_0.OutQuad, t) -- 59 + x = deltaX - tmp -- 60 + elseif posX > 0 then -- 61 + local tmp = deltaX -- 62 + deltaX = posX - posX * _with_0:func(_with_0.OutQuad, t) -- 63 + x = deltaX - tmp -- 64 + end -- 57 + if posY < 0 then -- 65 + local tmp = deltaY -- 66 + deltaY = posY - posY * _with_0:func(_with_0.OutQuad, t) -- 67 + y = deltaY - tmp -- 68 + elseif posY > moveY then -- 69 + local tmp = deltaY -- 70 + deltaY = posY + (moveY - posY) * _with_0:func(_with_0.OutQuad, t) -- 71 + y = deltaY - tmp -- 72 + end -- 65 + end -- 56 + x = x or 0 -- 73 + y = y or 0 -- 74 + self:emit("Scrolled", Vec2(x, y)) -- 75 + if t == 1 then -- 76 + self:unschedule() -- 77 + self:emit("ScrollEnd") -- 78 + end -- 76 + return false -- 79 + end -- 52 + local isReseting -- 81 + isReseting = function() -- 81 + return not self.dragging and (deltaX > 0 or deltaX < -moveX or deltaY > moveY or deltaY < 0) -- 82 + end -- 81 + local startReset -- 84 + startReset = function() -- 84 + posX = deltaX -- 85 + posY = deltaY -- 86 + timePassed = 0 -- 87 + return self:schedule(updateReset) -- 88 + end -- 84 + local setOffset -- 90 + setOffset = function(delta, touching) -- 90 + local dPosX = delta.x -- 91 + local dPosY = delta.y -- 92 + local newPosX = deltaX + dPosX -- 93 + local newPosY = deltaY + dPosY -- 94 + newPosX = math.min(newPosX, paddingX) -- 96 + newPosX = math.max(newPosX, -moveX - paddingX) -- 97 + newPosY = math.max(newPosY, -paddingY) -- 98 + newPosY = math.min(newPosY, moveY + paddingY) -- 99 + dPosX = newPosX - deltaX -- 100 + dPosY = newPosY - deltaY -- 101 + if touching then -- 103 + local lenY -- 104 + if newPosY < 0 then -- 104 + lenY = (0 - newPosY) / paddingY -- 105 + elseif newPosY > moveY then -- 106 + lenY = (newPosY - moveY) / paddingY -- 107 + else -- 108 + lenY = 0 -- 108 + end -- 104 + local lenX -- 109 + if newPosX > 0 then -- 109 + lenX = (newPosX - 0) / paddingX -- 110 + elseif newPosX < -moveX then -- 111 + lenX = (-moveX - newPosX) / paddingX -- 112 + else -- 113 + lenX = 0 -- 113 + end -- 109 + if lenY > 0 then -- 115 + local v = lenY * 3 -- 116 + dPosY = dPosY / math.max(v * v, 1) -- 117 + end -- 115 + if lenX > 0 then -- 118 + local v = lenX * 3 -- 119 + dPosX = dPosX / math.max(v * v, 1) -- 120 + end -- 118 + end -- 103 + deltaX = deltaX + dPosX -- 122 + deltaY = deltaY + dPosY -- 123 + self:emit("Scrolled", Vec2(dPosX, dPosY)) -- 125 + if not touching and (newPosY < -paddingY * 0.5 or newPosY > moveY + paddingY * 0.5 or newPosX > paddingX * 0.5 or newPosX < -moveX - paddingX * 0.5) then -- 127 + return startReset() -- 131 + end -- 127 + end -- 90 + local accel = screenSize * 2 -- 133 + local updateSpeed -- 134 + updateSpeed = function(dt) -- 134 + V = S / dt -- 135 + if V.length > accel then -- 136 + V = V:normalize() -- 137 + V = V * accel -- 138 + end -- 136 + S = Vec2.zero -- 139 + return false -- 140 + end -- 134 + local updatePos -- 142 + updatePos = function(dt) -- 142 + local dir = Vec2(V.x, V.y) -- 143 + dir = dir:normalize() -- 144 + local A = dir * -accel -- 145 + local incX = V.x > 0 -- 146 + local incY = V.y > 0 -- 147 + V = V + A * dt * 0.5 -- 148 + local decX = V.x < 0 -- 149 + local decY = V.y < 0 -- 150 + if incX == decX and incY == decY then -- 151 + if isReseting() then -- 152 + startReset() -- 153 + else -- 155 + self:unschedule() -- 155 + self:emit("ScrollEnd") -- 156 + end -- 152 + else -- 158 + local dS = V * dt -- 158 + setOffset(dS, false) -- 159 + end -- 151 + return false -- 160 + end -- 142 + self:slot("TapFilter", function(touch) -- 162 + if not touch.first or not Rect(-width / 2, -height / 2, width, height):containsPoint(touch.location) then -- 163 + touch.enabled = false -- 164 + end -- 163 + end) -- 162 + self:slot("TapBegan", function(touch) -- 166 + deltaMoveLength = 0 -- 167 + S = Vec2.zero -- 168 + V = Vec2.zero -- 169 + self:schedule(updateSpeed) -- 170 + return self:emit("ScrollTouchBegan") -- 171 + end) -- 166 + self:slot("TapEnded", function() -- 173 + if not self.dragging then -- 174 + self:emit("NoneScrollTapped") -- 175 + end -- 174 + self.dragging = false -- 176 + if isReseting() then -- 177 + startReset() -- 178 + elseif V ~= Vec2.zero and deltaMoveLength > 10 then -- 179 + self:schedule(updatePos) -- 180 + else -- 182 + self:emit("ScrollEnd") -- 182 + end -- 177 + return self:emit("ScrollTouchEnded") -- 183 + end) -- 173 + self:slot("TapMoved", function(touch) -- 185 + local lastMoveLength = deltaMoveLength -- 186 + S = touch.delta -- 187 + deltaMoveLength = deltaMoveLength + S.length -- 188 + if deltaMoveLength > 10 then -- 189 + setOffset(S, true) -- 190 + if lastMoveLength <= 10 then -- 191 + self.dragging = true -- 192 + return self:emit("ScrollStart") -- 193 + end -- 191 + end -- 189 + end) -- 185 + self.area:slot("MouseWheel", function(delta) -- 195 + local px, py = paddingX, paddingY -- 196 + paddingX, paddingY = 0, 0 -- 197 + setOffset(delta * -20) -- 198 + paddingX, paddingY = px, py -- 199 + end) -- 195 + if scrollBar then -- 201 + local getScrollBarX -- 202 + getScrollBarX = function() -- 202 + if self.barX then -- 203 + return self.barX -- 203 + end -- 203 + local barX = SolidRect({ -- 205 + width = 50, -- 205 + height = 10, -- 206 + color = 0x66000000 + scrollBarColor3 -- 207 + }) -- 204 + local barBgX = SolidRect({ -- 209 + width = self.area.width, -- 209 + height = 10, -- 210 + color = 0x22000000 + scrollBarColor3 -- 211 + }) -- 208 + barBgX:addChild(barX) -- 212 + self.area:addChild(barBgX) -- 213 + self.barX = barX -- 214 + self.barBgX = barBgX -- 215 + return barX -- 216 + end -- 202 + local getScrollBarY -- 217 + getScrollBarY = function() -- 217 + if self.barY then -- 218 + return self.barY -- 218 + end -- 218 + local barY = SolidRect({ -- 220 + width = 10, -- 220 + height = 50, -- 221 + color = 0x66000000 + scrollBarColor3 -- 222 + }) -- 219 + local barBgY = SolidRect({ -- 224 + width = 10, -- 224 + height = self.area.height, -- 225 + color = 0x22000000 + scrollBarColor3 -- 226 + }) -- 223 + barBgY.x = self.area.width - 10 -- 227 + barBgY:addChild(barY) -- 228 + self.area:addChild(barBgY) -- 229 + self.barY = barY -- 230 + self.barBgY = barBgY -- 231 + return barY -- 232 + end -- 217 + local fadeSeq = Sequence(Show(), Opacity(0, 1, 1), Delay(1), Opacity(0.4, 1, 0, Ease.OutQuad), Hide()) -- 233 + local fadeBarX = Action(fadeSeq) -- 240 + local fadeBarY = Action(fadeSeq) -- 241 + self:slot("Scrolled", function(delta) -- 242 + if delta.x ~= 0 then -- 243 + local barX = getScrollBarX() -- 244 + barX.x = (self.area.width - 50) * math.max(math.min(-self.offset.x / (viewWidth - width), 1), 0) -- 245 + self.barBgX:perform(fadeBarX) -- 246 + end -- 243 + if delta.y ~= 0 then -- 247 + local barY = getScrollBarY() -- 248 + barY.y = (self.area.height - 50) * math.max(math.min(1 - self.offset.y / (viewHeight - height), 1), 0) -- 249 + return self.barBgY:perform(fadeBarY) -- 250 + end -- 247 + end) -- 242 + end -- 201 + self:slot("Enter", function() -- 252 + return self:emit("Scrolled", Vec2.zero) -- 252 + end) -- 252 + self.scroll = function(self, delta) -- 254 + if delta then -- 255 + deltaX = deltaX + delta.x -- 256 + deltaY = deltaY + delta.y -- 257 + self:emit("Scrolled", Vec2(delta.x, delta.y)) -- 258 + end -- 255 + if isReseting() then -- 259 + return startReset() -- 259 + end -- 259 + end -- 254 + self.scrollTo = function(self, offset) -- 261 + local delta = offset - Vec2(deltaX, deltaY) -- 262 + deltaX = offset.x -- 263 + deltaY = offset.y -- 264 + return self:emit("Scrolled", delta) -- 265 + end -- 261 + self.updatePadding = function(self, padX, padY) -- 267 + paddingX = padX -- 268 + paddingY = padY -- 269 + return self:scroll(Vec2.zero) -- 270 + end -- 267 + self.getPadding = function() -- 272 + return Vec2(paddingX, paddingY) -- 272 + end -- 272 + self.getViewSize = function() -- 273 + return Size(viewWidth, viewHeight) -- 273 + end -- 273 + self.getTotalDelta = function() -- 274 + return Vec2(deltaX, deltaY) -- 274 + end -- 274 + self.resetSize = function(self, w, h, viewW, viewH) -- 275 + local offset = self.offset -- 276 + self.offset = Vec2.zero -- 277 + width, height = w, h -- 278 + viewWidth = math.max(viewW or w, w) -- 279 + viewHeight = math.max(viewH or h, h) -- 280 + moveY = viewHeight - height -- 281 + moveX = viewWidth - width -- 282 + local size = Size(w, h) -- 283 + self.contentSize = size -- 284 + self.area.size = size -- 285 + self.view.size = size -- 286 + if clipping then -- 287 + self.area.stencil = SolidRect({ -- 287 + width = w, -- 287 + height = h -- 287 + }) -- 287 + end -- 287 + self.offset = offset -- 288 + if self.barBgX then -- 289 + self.area:removeChild(self.barBgX) -- 290 + self.barBgX = nil -- 291 + self.barX = nil -- 292 + end -- 289 + if self.barBgY then -- 293 + self.area:removeChild(self.barBgY) -- 294 + self.barBgY = nil -- 295 + self.barY = nil -- 296 + end -- 293 + end -- 275 + end, -- 16 + offset = property(function(self) -- 298 + return self:getTotalDelta() -- 298 + end, function(self, offset) -- 299 + return self:scroll(offset - self:getTotalDelta()) -- 299 + end), -- 298 + viewSize = property(function(self) -- 301 + return self:getViewSize() -- 301 + end, function(self, size) -- 302 + return self:resetSize(self.contentSize.width, self.contentSize.height, size.width, size.height) -- 308 + end), -- 301 + padding = property(function(self) -- 310 + return self:getPadding() -- 310 + end, function(self, padding) -- 311 + return self:updatePadding(padding.x, padding.y) -- 311 + end), -- 310 + setupMenuScroll = function(self, menu) -- 313 + self:slot("Scrolled", function(delta) -- 314 + return menu:moveAndCullItems(delta) -- 315 + end) -- 314 + local menuEnabled = true -- 316 + self:slot("ScrollStart", function() -- 317 + menuEnabled = menu.enabled -- 318 + menu.enabled = false -- 319 + end) -- 317 + return self:slot("ScrollTouchEnded", function() -- 320 + if not menu.enabled then -- 321 + menu.enabled = menuEnabled -- 321 + end -- 321 + end) -- 321 + end, -- 313 + adjustSizeWithAlign = function(self, alignMode, padding, size, viewSize) -- 323 + if alignMode == nil then -- 323 + alignMode = "Auto" -- 323 + end -- 323 + if padding == nil then -- 323 + padding = 10 -- 323 + end -- 323 + if size == nil then -- 323 + size = self.area.size -- 323 + end -- 323 + viewSize = viewSize or size -- 325 + local offset = self.offset -- 326 + self.offset = Vec2.zero -- 327 + if "Auto" == alignMode then -- 329 + viewSize = self.view:alignItems(Size(viewSize.width, size.height), padding) -- 330 + elseif "Vertical" == alignMode then -- 331 + viewSize = self.view:alignItemsVertically(size, padding) -- 332 + elseif "Horizontal" == alignMode then -- 333 + viewSize = self.view:alignItemsHorizontally(size, padding) -- 334 + end -- 334 + self:resetSize(size.width, size.height, viewSize.width, viewSize.height) -- 335 + self.offset = offset -- 341 + end, -- 323 + scrollToPosY = function(self, posY, time) -- 343 + if time == nil then -- 343 + time = 0.3 -- 343 + end -- 343 + local height = self.contentSize.height -- 344 + local offset = self.offset -- 345 + local viewHeight = self.viewSize.height -- 346 + local deltaY = height / 2 - posY -- 347 + local startY = offset.y -- 348 + local endY = startY + deltaY -- 349 + if viewHeight <= height then -- 350 + endY = 0 -- 351 + else -- 353 + endY = math.max(endY, 0) -- 353 + endY = math.min(endY, viewHeight - height) -- 354 + end -- 350 + return self:schedule(once(function() -- 355 + local changeY = endY - startY -- 356 + cycle(time, function(progress) -- 357 + offset = Vec2(offset.x, startY + changeY * Ease:func(Ease.OutQuad, progress)) -- 358 + return self:scrollTo(offset) -- 359 + end) -- 357 + offset = Vec2(offset.x, endY) -- 360 + return self:scrollTo(offset) -- 361 + end)) -- 361 + end -- 343 +}) -- 15 +return _module_0 -- 361 diff --git a/Assets/Script/Lib/UI/View/Control/Basic/Button.lua b/Assets/Script/Lib/UI/View/Control/Basic/Button.lua new file mode 100644 index 000000000..c977914d2 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Control/Basic/Button.lua @@ -0,0 +1,40 @@ +-- [xml]: Script/Lib/UI/View/Control/Basic/Button.xml +local ButtonBase = require("UI.View.Control.Basic.ButtonBase") -- 3 +local SolidRect = require("UI.View.Shape.SolidRect") -- 4 +local LineRect = require("UI.View.Shape.LineRect") -- 5 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local item1 = ButtonBase{tag = tag, width = width, y = y, height = height, x = x} -- 7 +local face = item1.face -- 8 +local show = Action(Spawn(Show(),Opacity(0,0.6,0.6))) -- 10 +item1.show = show -- 10 +local hide = Action(Sequence(Show(),Opacity(0.8,0.6,0),Hide())) -- 14 +item1.hide = hide -- 14 +local node1 = Node() -- 21 +node1.passColor3 = false -- 21 +face:addChild(node1) -- 21 +local item2 = SolidRect{renderOrder = 1, color = backColor or 0x66000000, height = height, width = width} -- 22 +node1:addChild(item2) -- 22 +if text then -- 25 +local label = Label(fontName or 'sarasa-mono-sc-regular',fontSize or 18) -- 26 +label.x = face.width*0.5 -- 26 +label.y = face.height*0.5 -- 26 +label.renderOrder = 2 -- 26 +label.alignment = "Center" -- 26 +label.text = text -- 26 +face:addChild(label) -- 26 +item1.label = label -- 26 +end -- 27 +local item3 = LineRect{renderOrder = 3, color = 0xffffffff, height = height, width = width} -- 29 +face:addChild(item3) -- 29 +local light = SolidRect{renderOrder = 4, color = App.themeColor:toARGB(), height = height, width = width} -- 30 +face:addChild(light) -- 30 +light.visible = false -- 31 +item1:slot("TapBegan",function() -- 34 +light:perform(show) -- 34 +end) -- 34 +item1:slot("TapEnded",function() -- 35 +light:perform(hide) -- 35 +end) -- 35 +return item1 -- 35 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Control/Basic/ButtonBase.lua b/Assets/Script/Lib/UI/View/Control/Basic/ButtonBase.lua new file mode 100644 index 000000000..f1ecbac29 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Control/Basic/ButtonBase.lua @@ -0,0 +1,22 @@ +-- [xml]: Script/Lib/UI/View/Control/Basic/ButtonBase.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 3 +node1.x = x or 0 -- 3 +node1.y = y or 0 -- 3 +node1.color3 = Color3(App.themeColor:toARGB()) -- 3 +node1.tag = tag or '' -- 3 +node1.size = Size(width,height) -- 3 +node1.touchEnabled = true -- 3 +local scaleAction = Action(Sequence(Scale(0.08,1.0,0.3),Scale(0.3,0.3,1,Ease.OutBack))) -- 6 +local face = Node() -- 13 +face.x = node1.width*0.5 -- 13 +face.y = node1.height*0.5 -- 13 +face.size = Size(width,height) -- 13 +node1:addChild(face) -- 13 +node1.face = face -- 13 +node1:slot("Tapped",function() -- 16 +face:perform(scaleAction) -- 16 +end) -- 16 +return node1 -- 16 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Control/Basic/CircleButton.lua b/Assets/Script/Lib/UI/View/Control/Basic/CircleButton.lua new file mode 100644 index 000000000..35e420f18 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Control/Basic/CircleButton.lua @@ -0,0 +1,38 @@ +-- [xml]: Script/Lib/UI/View/Control/Basic/CircleButton.xml +local ButtonBase = require("UI.View.Control.Basic.ButtonBase") -- 3 +local SolidCircle = require("UI.View.Shape.SolidCircle") -- 4 +local LineCircle = require("UI.View.Shape.LineCircle") -- 5 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local item1 = ButtonBase{width = radius*2, y = y, height = radius*2, x = x} -- 7 +local face = item1.face -- 8 +local show = Action(Spawn(Show(),Opacity(0,0.6,0.6))) -- 10 +local hide = Action(Sequence(Show(),Opacity(0.8,0.6,0),Hide())) -- 14 +local node1 = Node() -- 21 +node1.passColor3 = false -- 21 +face:addChild(node1) -- 21 +local item2 = SolidCircle{color = backColor or 0x88000000, renderOrder = 1, radius = radius} -- 22 +node1:addChild(item2) -- 22 +if text then -- 25 +local label = Label(fontName or 'sarasa-mono-sc-regular',fontSize or 18) -- 26 +label.x = face.width*0.5 -- 26 +label.y = face.height*0.5 -- 26 +label.renderOrder = 2 -- 26 +label.alignment = "Center" -- 26 +label.text = text -- 26 +face:addChild(label) -- 26 +item1.label = label -- 26 +end -- 27 +local item3 = LineCircle{color = 0xffffffff, renderOrder = 3, radius = radius} -- 29 +face:addChild(item3) -- 29 +local light = SolidCircle{color = App.themeColor:toARGB(), renderOrder = 4, radius = radius} -- 30 +face:addChild(light) -- 30 +light.visible = false -- 31 +item1:slot("TapBegan",function() -- 34 +light:perform(show) -- 34 +end) -- 34 +item1:slot("TapEnded",function() -- 35 +light:perform(hide) -- 35 +end) -- 35 +return item1 -- 35 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Control/Basic/Ruler.lua b/Assets/Script/Lib/UI/View/Control/Basic/Ruler.lua new file mode 100644 index 000000000..597f29d9c --- /dev/null +++ b/Assets/Script/Lib/UI/View/Control/Basic/Ruler.lua @@ -0,0 +1,30 @@ +-- [xml]: Script/Lib/UI/View/Control/Basic/Ruler.xml +local LineRect = require("UI.View.Shape.LineRect") -- 3 +local SolidRect = require("UI.View.Shape.SolidRect") -- 4 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local ruler = Node() -- 6 +ruler.x = x or 0 -- 6 +ruler.y = y or 0 -- 6 +ruler.opacity = 0 -- 6 +ruler.visible = false -- 6 +ruler.touchEnabled = true -- 6 +ruler.swallowTouches = true -- 6 +local item1 = SolidRect{color = 0xcc000000, width = width, y = -height/2, height = height, x = -width/2} -- 7 +ruler:addChild(item1) -- 7 +local clipNode1 = ClipNode() -- 9 +ruler:addChild(clipNode1) -- 9 +local item2 = SolidRect{width = width, y = -height/2, height = height, x = -width/2} -- 11 +clipNode1.stencil = item2 -- 11 +local intervalNode = Line() -- 13 +intervalNode.size = Size(width,height) -- 13 +clipNode1:addChild(intervalNode) -- 13 +ruler.intervalNode = intervalNode -- 13 +intervalNode:set({},Color(0xffffffff)) -- 13 +local border = LineRect{width = width, y = -height/2, height = height, x = -width/2} -- 16 +ruler:addChild(border) -- 16 +local cursor = Line() -- 18 +ruler:addChild(cursor) -- 18 +cursor:set({Vec2(0,-height/2),Vec2(0,height/2)},Color(0xffffffff)) -- 18 +return ruler -- 20 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Control/Basic/ScrollArea.lua b/Assets/Script/Lib/UI/View/Control/Basic/ScrollArea.lua new file mode 100644 index 000000000..703f80a7b --- /dev/null +++ b/Assets/Script/Lib/UI/View/Control/Basic/ScrollArea.lua @@ -0,0 +1,41 @@ +-- [xml]: Script/Lib/UI/View/Control/Basic/ScrollArea.xml +local SolidRect = require("UI.View.Shape.SolidRect") -- 3 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local panel = Node() -- 5 +panel.x = x or 0 -- 5 +panel.y = y or 0 -- 5 +panel.visible = visible == nil or visible -- 5 +panel.touchEnabled = true -- 5 +panel.swallowTouches = true -- 5 +if clipping or clipping == nil then -- 6 +local area = ClipNode() -- 7 +area.size = Size(width or 0,height or 0) -- 7 +area.touchEnabled = true -- 7 +area.swallowMouseWheel = true -- 7 +panel:addChild(area) -- 7 +panel.area = area -- 7 +local item1 = SolidRect{height = height or 0, width = width or 0} -- 9 +area.stencil = item1 -- 9 +local view = Menu() -- 11 +view.anchor = Vec2(0,0) -- 11 +view.size = Size(width or 0,height or 0) -- 11 +view.renderGroup = true -- 11 +area:addChild(view) -- 11 +panel.view = view -- 11 +else -- 13 +local area = Node() -- 14 +area.size = Size(width or 0,height or 0) -- 14 +area.touchEnabled = true -- 14 +area.swallowMouseWheel = true -- 14 +panel:addChild(area) -- 14 +panel.area = area -- 14 +local view = Menu() -- 15 +view.anchor = Vec2(0,0) -- 15 +view.size = Size(width or 0,height or 0) -- 15 +view.renderGroup = true -- 15 +area:addChild(view) -- 15 +panel.view = view -- 15 +end -- 17 +return panel -- 17 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/Camera.lua b/Assets/Script/Lib/UI/View/Shape/Camera.lua new file mode 100644 index 000000000..5a14740ef --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/Camera.lua @@ -0,0 +1,29 @@ +-- [xml]: Script/Lib/UI/View/Shape/Camera.xml +local LineCircle = require("UI.View.Shape.LineCircle") -- 3 +local LineRect = require("UI.View.Shape.LineRect") -- 4 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 6 +node1.x = x or 0 -- 6 +node1.y = y or 0 -- 6 +node1.scaleX = scale or 1 -- 6 +node1.scaleY = scale or 1 -- 6 +local drawNode1 = DrawNode() -- 7 +drawNode1.opacity = 0.5 -- 7 +drawNode1.renderOrder = fillOrder or 0 -- 7 +node1:addChild(drawNode1) -- 7 +drawNode1:drawPolygon({Vec2(-20,-10),Vec2(20,-10),Vec2(20,10),Vec2(-20,10)},Color(color),0,Color()) -- 8 +drawNode1:drawPolygon({Vec2(20,3),Vec2(32,10),Vec2(32,-10),Vec2(20,-3)},Color(color),0,Color()) -- 14 +drawNode1:drawDot(Vec2(-11,20),10,Color(color)) -- 20 +drawNode1:drawDot(Vec2(11,20),10,Color(color)) -- 21 +local item1 = LineRect{color = color, width = 40, y = -10, lineOrder = lineOrder or 0, height = 20, x = -20} -- 23 +node1:addChild(item1) -- 23 +local item2 = LineCircle{lineOrder = lineOrder or 0, color = color, radius = 10, y = 10, x = -21} -- 24 +node1:addChild(item2) -- 24 +local item3 = LineCircle{lineOrder = lineOrder or 0, color = color, radius = 10, y = 10, x = 1} -- 25 +node1:addChild(item3) -- 25 +local line1 = Line() -- 26 +node1:addChild(line1) -- 26 +line1:set({Vec2(20,3),Vec2(32,10),Vec2(32,-10),Vec2(20,-3)},Color(0xffffffff)) -- 26 +return node1 -- 30 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/Circle.lua b/Assets/Script/Lib/UI/View/Shape/Circle.lua new file mode 100644 index 000000000..23f9931cf --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/Circle.lua @@ -0,0 +1,19 @@ +-- [xml]: Script/Lib/UI/View/Shape/Circle.xml +local SolidCircle = require("UI.View.Shape.SolidCircle") -- 3 +local LineCircle = require("UI.View.Shape.LineCircle") -- 4 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 6 +node1.x = x or 0 -- 6 +node1.y = y or 0 -- 6 +node1.size = Size(radius * 2,radius * 2) -- 6 +if fillColor then -- 7 +local item1 = SolidCircle{color = fillColor, renderOrder = fillOrder or 0, radius = radius} -- 8 +node1:addChild(item1) -- 8 +end -- 9 +if borderColor then -- 11 +local item2 = LineCircle{color = borderColor, renderOrder = lineOrder or 0, radius = radius} -- 12 +node1:addChild(item2) -- 12 +end -- 13 +return node1 -- 13 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/LineCircle.lua b/Assets/Script/Lib/UI/View/Shape/LineCircle.lua new file mode 100644 index 000000000..58b72a088 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/LineCircle.lua @@ -0,0 +1,27 @@ +-- [yue]: Script/Lib/UI/View/Shape/LineCircle.yue +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local Line = dora.Line -- 1 +local Color = dora.Color -- 1 +local _module_0 = nil -- 1 +local num = 20 -- 3 +local newP -- 5 +newP = function(index, radius) -- 5 + local angle = 2 * math.pi * index / num -- 6 + return Vec2(radius * math.cos(angle), radius * math.sin(angle)) + Vec2(radius, radius) -- 7 +end -- 5 +_module_0 = function(args) -- 9 + local _with_0 = Line((function() -- 10 + local _accum_0 = { } -- 10 + local _len_0 = 1 -- 10 + for index = 0, num do -- 10 + _accum_0[_len_0] = newP(index, args.radius) -- 10 + _len_0 = _len_0 + 1 -- 10 + end -- 10 + return _accum_0 -- 10 + end)(), args.color and Color(args.color or 0xffffffff)) -- 10 + _with_0.position = Vec2(args.x or 0, args.y or 0) -- 11 + _with_0.renderOrder = args.renderOrder or 0 -- 12 + return _with_0 -- 10 +end -- 9 +return _module_0 -- 12 diff --git a/Assets/Script/Lib/UI/View/Shape/LineRect.lua b/Assets/Script/Lib/UI/View/Shape/LineRect.lua new file mode 100644 index 000000000..5ca731e94 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/LineRect.lua @@ -0,0 +1,12 @@ +-- [xml]: Script/Lib/UI/View/Shape/LineRect.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local line1 = Line() -- 3 +line1.x = x or 0 -- 3 +line1.y = y or 0 -- 3 +line1.color3 = Color3(color or 0xffffff) -- 3 +line1.opacity = color and Color(color).opacity or 1 -- 3 +line1.renderOrder = renderOrder or 0 -- 3 +line1:set({Vec2(0,0),Vec2(width,0),Vec2(width,height),Vec2(0,height),Vec2(0,0)},Color(0xffffffff)) -- 3 +return line1 -- 8 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/Rectangle.lua b/Assets/Script/Lib/UI/View/Shape/Rectangle.lua new file mode 100644 index 000000000..2b58d037a --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/Rectangle.lua @@ -0,0 +1,19 @@ +-- [xml]: Script/Lib/UI/View/Shape/Rectangle.xml +local SolidRect = require("UI.View.Shape.SolidRect") -- 3 +local LineRect = require("UI.View.Shape.LineRect") -- 4 +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local node1 = Node() -- 6 +node1.x = x or 0 -- 6 +node1.y = y or 0 -- 6 +node1.size = Size(width,height) -- 6 +if fillColor then -- 7 +local item1 = SolidRect{renderOrder = fillOrder or 0, color = fillColor, height = height, width = width} -- 8 +node1:addChild(item1) -- 8 +end -- 9 +if borderColor then -- 11 +local item2 = LineRect{renderOrder = lineOrder or 0, color = borderColor, height = height, width = width} -- 12 +node1:addChild(item2) -- 12 +end -- 13 +return node1 -- 13 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/SolidCircle.lua b/Assets/Script/Lib/UI/View/Shape/SolidCircle.lua new file mode 100644 index 000000000..a5b760893 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/SolidCircle.lua @@ -0,0 +1,10 @@ +-- [xml]: Script/Lib/UI/View/Shape/SolidCircle.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local drawNode1 = DrawNode() -- 3 +drawNode1.x = x or 0 -- 3 +drawNode1.y = y or 0 -- 3 +drawNode1.renderOrder = renderOrder or 0 -- 3 +drawNode1:drawDot(Vec2(radius,radius),radius,Color(color or 0xffffffff)) -- 4 +return drawNode1 -- 4 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/SolidRect.lua b/Assets/Script/Lib/UI/View/Shape/SolidRect.lua new file mode 100644 index 000000000..f0763ba67 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/SolidRect.lua @@ -0,0 +1,10 @@ +-- [xml]: Script/Lib/UI/View/Shape/SolidRect.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local drawNode1 = DrawNode() -- 3 +drawNode1.x = x or 0 -- 3 +drawNode1.y = y or 0 -- 3 +drawNode1.renderOrder = renderOrder or 0 -- 3 +drawNode1:drawPolygon({Vec2(0,0),Vec2(width,0),Vec2(width,height),Vec2(0,height)},Color(color or 0xffffffff),0,Color()) -- 4 +return drawNode1 -- 8 +end \ No newline at end of file diff --git a/Assets/Script/Lib/UI/View/Shape/Star.lua b/Assets/Script/Lib/UI/View/Shape/Star.lua new file mode 100644 index 000000000..7de9775b7 --- /dev/null +++ b/Assets/Script/Lib/UI/View/Shape/Star.lua @@ -0,0 +1,53 @@ +-- [yue]: Script/Lib/UI/View/Shape/Star.yue +local math = _G.math -- 1 +local Vec2 = dora.Vec2 -- 1 +local Node = dora.Node -- 1 +local DrawNode = dora.DrawNode -- 1 +local Color = dora.Color -- 1 +local Line = dora.Line -- 1 +local _module_0 = nil -- 1 +local StarVertices -- 3 +StarVertices = function(radius, line) -- 3 + if line == nil then -- 3 + line = true -- 3 + end -- 3 + local a = math.rad(36) -- 4 + local c = math.rad(72) -- 5 + local f = math.sin(a) * math.tan(c) + math.cos(a) -- 6 + local R = radius -- 7 + local r = R / f -- 8 + local _accum_0 = { } -- 9 + local _len_0 = 1 -- 9 + for i = 9 + (line and 1 or 0), 0, -1 do -- 9 + local angle = i * a -- 10 + local cr = i % 2 == 1 and r or R -- 11 + _accum_0[_len_0] = Vec2(cr * math.sin(angle), cr * math.cos(angle)) -- 12 + _len_0 = _len_0 + 1 -- 12 + end -- 12 + return _accum_0 -- 12 +end -- 3 +_module_0 = function(args) -- 14 + local _with_0 = Node() -- 15 + _with_0.position = Vec2(args.x or 0, args.y or 0) -- 16 + if args.fillColor then -- 17 + _with_0:addChild((function() -- 18 + local _with_1 = DrawNode() -- 18 + _with_1:drawPolygon(StarVertices(args.size, false), Color(args.fillColor)) -- 19 + if args.fillOrder then -- 20 + _with_1.renderOrder = args.fillOrder -- 20 + end -- 20 + return _with_1 -- 18 + end)()) -- 18 + end -- 17 + if args.borderColor then -- 21 + _with_0:addChild((function() -- 22 + local _with_1 = Line(StarVertices(args.size), Color(args.borderColor)) -- 22 + if args.lineOrder then -- 23 + _with_1.renderOrder = args.lineOrder -- 23 + end -- 23 + return _with_1 -- 22 + end)()) -- 22 + end -- 21 + return _with_0 -- 15 +end -- 14 +return _module_0 -- 23 diff --git a/Assets/Script/Lib/Utils.lua b/Assets/Script/Lib/Utils.lua new file mode 100644 index 000000000..180cd29b2 --- /dev/null +++ b/Assets/Script/Lib/Utils.lua @@ -0,0 +1,552 @@ +-- [yue]: Script/Lib/Utils.yue +local table = _G.table -- 1 +local math = _G.math -- 1 +local thread = dora.thread -- 1 +local getmetatable = _G.getmetatable -- 1 +local error = _G.error -- 1 +local ipairs = _G.ipairs -- 1 +local pairs = _G.pairs -- 1 +local select = _G.select -- 1 +local assert = _G.assert -- 1 +local load = _G.load -- 1 +local Vec2 = dora.Vec2 -- 1 +local string = _G.string -- 1 +local _module_0 = { } -- 1 +local insert, remove, concat, sort = table.insert, table.remove, table.concat, table.sort -- 2 +local floor, ceil = math.floor, math.ceil -- 3 +local type, tostring, setmetatable, table, rawset, rawget = _G.type, _G.tostring, _G.setmetatable, _G.table, _G.rawset, _G.rawget -- 4 +local StructUpdated -- 6 +StructUpdated = function(self) -- 6 + local update = rawget(self, "__update") -- 7 + if not update then -- 8 + return rawset(self, "__update", thread(function() -- 9 + local notify = rawget(self, "__notify") -- 10 + notify("Updated") -- 11 + return rawset(self, "__update", nil) -- 12 + end)) -- 12 + end -- 8 +end -- 6 +local StructToString -- 13 +StructToString = function(self) -- 13 + local structDef = getmetatable(self) -- 14 + local content = { } -- 15 + local ordered = true -- 16 + local count -- 17 + if #structDef == 0 then -- 17 + count = #self -- 17 + else -- 17 + count = #structDef + 1 -- 17 + end -- 17 + for i = 1, count do -- 18 + local value = self[i] -- 19 + if value == nil then -- 20 + ordered = false -- 21 + goto _continue_0 -- 22 + else -- 24 + value = (type(value) == 'string' and "\"" .. tostring(value) .. "\"" or tostring(value)) -- 24 + if ordered then -- 25 + content[#content + 1] = value -- 26 + else -- 28 + content[#content + 1] = "[" .. tostring(i) .. "]=" .. tostring(value) -- 28 + end -- 25 + end -- 20 + ::_continue_0:: -- 19 + end -- 28 + return "{" .. (concat(content, ',')) .. "}" -- 29 +end -- 13 +local StructDefMeta = { -- 31 + set = function(self, index, item) -- 31 + index = index + 1 -- 32 + if 1 <= index and index <= #self then -- 33 + self[index] = item -- 34 + local notify = rawget(self, "__notify") -- 35 + if notify then -- 36 + notify("Changed", index - 1, item) -- 37 + return StructUpdated(self) -- 38 + end -- 36 + else -- 40 + return error("Access index out of range.") -- 40 + end -- 33 + end, -- 31 + get = function(self, index) -- 41 + if 1 <= index and index < #self then -- 42 + return self[index + 1] -- 43 + else -- 45 + return nil -- 45 + end -- 42 + end, -- 41 + insert = function(self, argA, argB) -- 46 + local item, index -- 47 + if argB then -- 48 + item = argB -- 49 + index = argA + 1 -- 50 + if index > #self then -- 51 + index = #self + 1 -- 52 + elseif index < 1 then -- 53 + index = 1 -- 54 + end -- 51 + else -- 56 + item = argA -- 56 + index = #self + 1 -- 57 + end -- 48 + insert(self, index, item) -- 58 + local notify = rawget(self, "__notify") -- 59 + if notify then -- 60 + notify("Added", index - 1, item) -- 61 + StructUpdated(self) -- 62 + end -- 60 + return item -- 63 + end, -- 46 + remove = function(self, arg) -- 64 + local item, index -- 65 + for i = 2, #self do -- 66 + if self[i] == arg then -- 67 + item = arg -- 68 + index = i -- 69 + remove(self, index) -- 70 + break -- 71 + end -- 67 + end -- 71 + if index then -- 72 + local notify = rawget(self, "__notify") -- 73 + if notify then -- 74 + notify("Removed", index - 1, item) -- 75 + StructUpdated(self) -- 76 + end -- 74 + end -- 72 + return item -- 77 + end, -- 64 + removeAt = function(self, index) -- 78 + local length = #self -- 79 + local item -- 80 + if index then -- 80 + if 0 < index and index < length then -- 81 + index = index + 1 -- 82 + item = remove(self, index) -- 83 + else -- 85 + item = nil -- 85 + end -- 81 + else -- 87 + if length > 1 then -- 87 + index = length -- 88 + item = remove(self, index) -- 89 + else -- 91 + item = nil -- 91 + end -- 87 + end -- 80 + if item then -- 92 + local notify = rawget(self, "__notify") -- 93 + if notify then -- 94 + notify("Removed", index - 1, item) -- 95 + StructUpdated(self) -- 96 + end -- 94 + end -- 92 + return item -- 97 + end, -- 78 + clear = function(self) -- 98 + local notify = rawget(self, "__notify") -- 99 + for index = #self, 2, -1 do -- 100 + local item = remove(self) -- 101 + if notify then -- 102 + notify("Removed", index - 1, item) -- 103 + StructUpdated(self) -- 104 + end -- 102 + end -- 104 + end, -- 98 + each = function(self, handler) -- 105 + for index = 2, #self do -- 106 + if true == handler(self[index], index - 1) then -- 107 + return true -- 108 + end -- 107 + end -- 108 + return false -- 109 + end, -- 105 + eachAttr = function(self, handler) -- 110 + for i, v in ipairs(getmetatable(self)) do -- 111 + handler(v, self[i + 1]) -- 112 + end -- 112 + end, -- 110 + contains = function(self, item) -- 113 + for index = 2, #self do -- 114 + if item == self[index] then -- 115 + return true -- 116 + end -- 115 + end -- 116 + return false -- 117 + end, -- 113 + toArray = function(self) -- 118 + local _accum_0 = { } -- 118 + local _len_0 = 1 -- 118 + local _list_0 = self -- 118 + for _index_0 = 2, #_list_0 do -- 118 + local item = _list_0[_index_0] -- 118 + _accum_0[_len_0] = item -- 118 + _len_0 = _len_0 + 1 -- 118 + end -- 118 + return _accum_0 -- 118 + end, -- 118 + count = function(self) -- 119 + return #self - 1 -- 119 + end, -- 119 + sort = function(self, comparer) -- 120 + local arr = self:toArray() -- 121 + sort(arr, comparer) -- 122 + for i = 1, #arr do -- 123 + self:set(i, arr[i]) -- 124 + end -- 124 + end, -- 120 + __tostring = function(self) -- 125 + local content = { } -- 126 + for k, v in pairs(self) do -- 127 + if "number" == type(v) then -- 128 + content[v - 1] = k -- 129 + end -- 128 + end -- 129 + if #content > 1 then -- 130 + return concat({ -- 131 + "Struct.", -- 131 + self.__name, -- 131 + "{\"", -- 131 + concat(content, "\",\""), -- 131 + "\"}" -- 131 + }) -- 131 + else -- 133 + return "Struct." .. tostring(self.__name) .. "()" -- 133 + end -- 130 + end, -- 125 + __call = function(self, data) -- 134 + local item = { -- 135 + self.__name -- 135 + } -- 135 + if data then -- 136 + for k, v in pairs(data) do -- 137 + local key = self[k] -- 138 + if key then -- 139 + item[key] = v -- 140 + elseif type(k) == "number" then -- 141 + item[k + 1] = v -- 142 + else -- 144 + error("Initialize to an invalid field named \"" .. tostring(k) .. "\" for \"" .. tostring(self) .. "\".") -- 144 + end -- 139 + end -- 144 + end -- 136 + setmetatable(item, self) -- 145 + return item -- 146 + end -- 134 +} -- 30 +local StructDefs = { } -- 148 +local StructHelper = { -- 150 + __call = function(self, ...) -- 150 + local structName = self.path .. self.name -- 151 + local tupleDef -- 152 + tupleDef = setmetatable({ -- 154 + __name = structName, -- 154 + __index = function(self, key) -- 155 + local item = tupleDef[key] -- 156 + if item then -- 157 + return rawget(self, item) -- 158 + else -- 160 + return StructDefMeta[key] -- 160 + end -- 157 + end, -- 155 + __newindex = function(self, key, value) -- 161 + local index = tupleDef[key] -- 162 + if index then -- 163 + local oldValue = rawget(self, index) -- 164 + if oldValue == value then -- 165 + return -- 165 + end -- 165 + rawset(self, index, value) -- 166 + local notify = rawget(self, "__notify") -- 167 + if notify then -- 168 + notify("Modified", key, value) -- 169 + return StructUpdated(self) -- 170 + end -- 168 + elseif "number" == type(key) then -- 171 + return rawset(self, key, value) -- 172 + elseif key ~= "__notify" then -- 173 + return error("Access invalid key \"" .. tostring(key) .. "\" for " .. tostring(tupleDef)) -- 174 + elseif value then -- 175 + rawset(self, "__notify", value) -- 176 + if #tupleDef == 0 then -- 177 + for i = 2, #self do -- 178 + value("Added", i - 1, self[i]) -- 179 + end -- 179 + else -- 181 + for _index_0 = 1, #tupleDef do -- 181 + local key = tupleDef[_index_0] -- 181 + value("Modified", key, self[key]) -- 182 + end -- 182 + end -- 177 + return StructUpdated(self) -- 183 + end -- 163 + end, -- 161 + __tostring = StructToString -- 184 + }, StructDefMeta) -- 153 + local count = select("#", ...) -- 186 + if count > 0 then -- 187 + local arg = select(1, ...) -- 188 + if "table" == type(arg) then -- 189 + for i, name in ipairs(arg) do -- 190 + tupleDef[i] = name -- 191 + tupleDef[name] = i + 1 -- 192 + end -- 192 + else -- 194 + for i = 1, count do -- 194 + local name = select(i, ...) -- 195 + tupleDef[i] = name -- 196 + tupleDef[name] = i + 1 -- 197 + end -- 197 + end -- 189 + end -- 187 + StructDefs[structName] = tupleDef -- 198 + return tupleDef -- 199 + end, -- 150 + __index = function(self, key) -- 200 + self.path = self.path .. self.name -- 201 + self.path = self.path .. "." -- 202 + self.name = key -- 203 + return self -- 204 + end, -- 200 + __tostring = function(self) -- 205 + local content = { } -- 206 + local path = self.path .. self.name .. "." -- 207 + local i = 1 -- 208 + for k, v in pairs(StructDefs) do -- 209 + if k:find(path, 1, true) then -- 210 + content[i] = tostring(v) -- 211 + i = i + 1 -- 212 + end -- 210 + end -- 212 + return concat(content, "\n") -- 213 + end -- 205 +} -- 149 +setmetatable(StructHelper, StructHelper) -- 215 +local Struct -- 216 +local StructLoad -- 217 +StructLoad = function(data) -- 217 + if "table" == type(data) then -- 218 + local mt = StructDefs[data[1]] -- 219 + assert(mt, "Struct started with \"" .. tostring(data[1]) .. "\" is not defined.") -- 220 + setmetatable(data, mt) -- 221 + for _index_0 = 1, #data do -- 222 + local item = data[_index_0] -- 222 + StructLoad(item) -- 223 + end -- 223 + end -- 218 +end -- 217 +Struct = setmetatable({ -- 225 + load = function(self, ...) -- 225 + local count = select("#", ...) -- 226 + if count > 1 then -- 227 + local name = select(1, ...) -- 228 + local data = select(2, ...) -- 229 + insert(data, 1, name) -- 230 + StructLoad(data) -- 231 + return data -- 232 + else -- 234 + local arg = select(1, ...) -- 234 + local data -- 235 + do -- 235 + local _exp_0 = type(arg) -- 235 + if "string" == _exp_0 then -- 236 + if arg:sub(1, 6) ~= "return" then -- 237 + arg = "return " .. arg -- 238 + end -- 237 + data = (load(arg))() -- 239 + elseif "table" == _exp_0 then -- 240 + data = arg -- 241 + end -- 241 + end -- 241 + StructLoad(data) -- 242 + return data -- 243 + end -- 227 + end, -- 225 + clear = function(self) -- 244 + StructDefs = { } -- 245 + end, -- 244 + has = function(self, name) -- 246 + return (StructDefs[name] ~= nil) -- 246 + end -- 246 +}, { -- 248 + __index = function(self, name) -- 248 + local def = StructDefs[name] -- 249 + if not def then -- 250 + StructHelper.name = name -- 251 + StructHelper.path = "" -- 252 + def = StructHelper -- 253 + end -- 250 + return def -- 254 + end, -- 248 + __tostring = function(self) -- 255 + return concat((function() -- 256 + local _accum_0 = { } -- 256 + local _len_0 = 1 -- 256 + for _, v in pairs(StructDefs) do -- 256 + _accum_0[_len_0] = tostring(v) -- 256 + _len_0 = _len_0 + 1 -- 256 + end -- 256 + return _accum_0 -- 256 + end)(), "\n") -- 256 + end -- 255 +}) -- 224 +_module_0["Struct"] = Struct -- 257 +local Set -- 259 +Set = function(list) -- 259 + local _tbl_0 = { } -- 259 + for _index_0 = 1, #list do -- 259 + local item = list[_index_0] -- 259 + _tbl_0[item] = true -- 259 + end -- 259 + return _tbl_0 -- 259 +end -- 259 +_module_0["Set"] = Set -- 259 +local CompareTable -- 261 +CompareTable = function(olds, news) -- 261 + local itemsToDel = { } -- 262 + local itemSet = Set(news) -- 263 + for _index_0 = 1, #olds do -- 264 + local item = olds[_index_0] -- 264 + if not itemSet[item] then -- 265 + itemsToDel[#itemsToDel + 1] = item -- 266 + end -- 265 + end -- 266 + local itemsToAdd = { } -- 267 + itemSet = Set(olds) -- 268 + for _index_0 = 1, #news do -- 269 + local item = news[_index_0] -- 269 + if not itemSet[item] then -- 270 + itemsToAdd[#itemsToAdd + 1] = item -- 271 + end -- 270 + end -- 271 + return itemsToAdd, itemsToDel -- 272 +end -- 261 +_module_0["CompareTable"] = CompareTable -- 272 +local Round -- 274 +Round = function(val) -- 274 + if type(val) == "number" then -- 275 + return val > 0 and floor(val + 0.5) or ceil(val - 0.5) -- 276 + else -- 278 + return Vec2(val.x > 0 and floor(val.x + 0.5) or ceil(val.x - 0.5), val.y > 0 and floor(val.y + 0.5) or ceil(val.y - 0.5)) -- 281 + end -- 275 +end -- 274 +_module_0["Round"] = Round -- 281 +local IsValidPath -- 283 +IsValidPath = function(filename) -- 283 + return not filename:match("[\\/|:*?<>\"]") -- 283 +end -- 283 +_module_0["IsValidPath"] = IsValidPath -- 283 +local allowedUseOfGlobals = Set({ -- 286 + "Dora", -- 286 + "dora", -- 287 + "require", -- 288 + "_G" -- 289 +}) -- 285 +local LintYueGlobals -- 291 +LintYueGlobals = function(luaCodes, globals, globalInLocal) -- 291 + if globalInLocal == nil then -- 291 + globalInLocal = true -- 291 + end -- 291 + local errors = { } -- 292 + local requireModules = { } -- 293 + luaCodes = luaCodes:gsub("^local _module_[^\r\n]*[^\r\n]+", "") -- 294 + local importCodes = luaCodes:match("^%s*local%s*_ENV%s*=%s*Dora%(([^%)]-)%)") -- 295 + local importItems -- 296 + if importCodes then -- 296 + do -- 297 + local _accum_0 = { } -- 297 + local _len_0 = 1 -- 297 + for item in importCodes:gmatch("%s*([^,\n\r]+)%s*") do -- 297 + local getImport = load("return " .. tostring(item)) -- 298 + local importItem -- 299 + if getImport ~= nil then -- 299 + importItem = getImport() -- 299 + end -- 299 + if not importItem or "table" ~= type(importItem) then -- 300 + goto _continue_0 -- 300 + end -- 300 + _accum_0[_len_0] = { -- 301 + importItem, -- 301 + item -- 301 + } -- 301 + _len_0 = _len_0 + 1 -- 301 + ::_continue_0:: -- 298 + end -- 301 + importItems = _accum_0 -- 297 + end -- 301 + else -- 302 + importItems = { } -- 302 + end -- 296 + local importSet = { } -- 303 + for _index_0 = 1, #globals do -- 304 + local globalVar = globals[_index_0] -- 304 + local name, line, col = globalVar[1], globalVar[2], globalVar[3] -- 305 + if allowedUseOfGlobals[name] then -- 306 + goto _continue_1 -- 306 + end -- 306 + if _G[name] then -- 307 + if globalInLocal then -- 308 + requireModules[#requireModules + 1] = "local " .. tostring(name) .. " = _G." .. tostring(name) .. " -- 1" -- 309 + end -- 308 + goto _continue_1 -- 310 + end -- 307 + local findModule = false -- 311 + if importCodes then -- 312 + if dora[name] then -- 313 + requireModules[#requireModules + 1] = "local " .. tostring(name) .. " = dora." .. tostring(name) .. " -- 1" -- 314 + findModule = true -- 315 + else -- 317 + for i, _des_0 in ipairs(importItems) do -- 317 + local mod, modName = _des_0[1], _des_0[2] -- 317 + if (mod[name] ~= nil) then -- 318 + local moduleName = "_module_" .. tostring(i - 1) -- 319 + if not importSet[mod] then -- 320 + importSet[mod] = true -- 321 + requireModules[#requireModules + 1] = "local " .. tostring(moduleName) .. " = " .. tostring(modName) .. " -- 1" -- 322 + end -- 320 + requireModules[#requireModules + 1] = "local " .. tostring(name) .. " = " .. tostring(moduleName) .. "." .. tostring(name) .. " -- 1" -- 323 + findModule = true -- 324 + break -- 325 + end -- 318 + end -- 325 + end -- 313 + end -- 312 + if not findModule then -- 326 + errors[#errors + 1] = globalVar -- 327 + end -- 326 + ::_continue_1:: -- 305 + end -- 327 + if #errors > 0 then -- 328 + return false, errors -- 329 + else -- 331 + return true, table.concat(requireModules, "\n") -- 331 + end -- 328 +end -- 291 +_module_0["LintYueGlobals"] = LintYueGlobals -- 331 +local GSplit -- 333 +GSplit = function(text, pattern, plain) -- 333 + local splitStart, length = 1, #text -- 334 + return function() -- 335 + if splitStart then -- 336 + local sepStart, sepEnd = string.find(text, pattern, splitStart, plain) -- 337 + local ret -- 338 + if not sepStart then -- 339 + ret = string.sub(text, splitStart) -- 340 + splitStart = nil -- 341 + elseif sepEnd < sepStart then -- 342 + ret = string.sub(text, splitStart, sepStart) -- 343 + if sepStart < length then -- 344 + splitStart = sepStart + 1 -- 345 + else -- 347 + splitStart = nil -- 347 + end -- 344 + else -- 349 + ret = sepStart > splitStart and string.sub(text, splitStart, sepStart - 1) or '' -- 349 + splitStart = sepEnd + 1 -- 350 + end -- 339 + return ret -- 351 + end -- 336 + end -- 351 +end -- 333 +_module_0["GSplit"] = GSplit -- 351 +return _module_0 -- 351 diff --git a/Assets/Script/Lib/WebServer.lua b/Assets/Script/Lib/WebServer.lua new file mode 100644 index 000000000..afe3daab5 --- /dev/null +++ b/Assets/Script/Lib/WebServer.lua @@ -0,0 +1,2048 @@ +-- [yue]: Script/Lib/WebServer.yue +local HttpServer = dora.HttpServer -- 1 +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +local yue = dora.yue -- 1 +local tostring = _G.tostring -- 1 +local load = _G.load -- 1 +local tonumber = _G.tonumber -- 1 +local teal = dora.teal -- 1 +local type = _G.type -- 1 +local xml = dora.xml -- 1 +local table = _G.table -- 1 +local ipairs = _G.ipairs -- 1 +local pairs = _G.pairs -- 1 +local App = dora.App -- 1 +local wait = dora.wait -- 1 +local emit = dora.emit -- 1 +local thread = dora.thread -- 1 +local print = _G.print -- 1 +local _module_0 = nil -- 1 +HttpServer:stop() -- 3 +HttpServer.wwwPath = Path(Content.writablePath, ".www") -- 5 +local LintYueGlobals -- 7 +do -- 7 + local _obj_0 = require("Utils") -- 7 + LintYueGlobals = _obj_0.LintYueGlobals -- 7 +end -- 7 +local getProjectDirFromFile -- 9 +getProjectDirFromFile = function(file) -- 9 + local writablePath = Content.writablePath -- 10 + if writablePath ~= file:sub(1, #writablePath) then -- 11 + return nil -- 11 + end -- 11 + local current = Path:getRelative(file, writablePath) -- 12 + repeat -- 13 + current = Path:getPath(current) -- 14 + if current == "" then -- 15 + break -- 15 + end -- 15 + local _list_0 = Content:getFiles(Path(writablePath, current)) -- 16 + for _index_0 = 1, #_list_0 do -- 16 + local f = _list_0[_index_0] -- 16 + if Path:getName(f):lower() == "init" then -- 17 + return Path(current, Path:getPath(f)) -- 18 + end -- 17 + end -- 18 + until false -- 19 + return nil -- 20 +end -- 9 +local getSearchPath -- 22 +getSearchPath = function(file) -- 22 + do -- 23 + local dir = getProjectDirFromFile(file) -- 23 + if dir then -- 23 + return Path(dir, "Script", "?.lua") .. ";" .. Path(dir, "?.lua") -- 24 + end -- 23 + end -- 23 + return "" -- 24 +end -- 22 +local getSearchFolders -- 26 +getSearchFolders = function(file) -- 26 + do -- 27 + local dir = getProjectDirFromFile(file) -- 27 + if dir then -- 27 + return { -- 29 + Path(dir, "Script"), -- 29 + dir -- 30 + } -- 30 + end -- 27 + end -- 27 + return { } -- 26 +end -- 26 +local disabledCheckForLua = { -- 33 + "incompatible number of returns", -- 33 + "unknown variable", -- 34 + "cannot index key", -- 35 + "module not found", -- 36 + "don't know how to resolve a dynamic require", -- 37 + "ContainerItem", -- 38 + "cannot resolve a type", -- 39 + "invalid key", -- 40 + "inconsistent index type", -- 41 + "cannot use operator '#'", -- 42 + "attempting ipairs loop", -- 43 + "expects record or nominal", -- 44 + "variable is not being assigned a value", -- 45 + "", -- 46 + "", -- 47 + "using the '#' operator on this map will always return 0", -- 48 + "can't match a record to a map with non%-string keys" -- 49 +} -- 32 +local yueCheck -- 51 +yueCheck = function(file, content) -- 51 + local searchPath = getSearchPath(file) -- 52 + local checkResult, luaCodes = yue.checkAsync(content, searchPath) -- 53 + local info = { } -- 54 + local globals = { } -- 55 + for _index_0 = 1, #checkResult do -- 56 + local _des_0 = checkResult[_index_0] -- 56 + local t, msg, line, col = _des_0[1], _des_0[2], _des_0[3], _des_0[4] -- 56 + if "error" == t then -- 57 + info[#info + 1] = { -- 58 + "syntax", -- 58 + file, -- 58 + line, -- 58 + col, -- 58 + msg -- 58 + } -- 58 + elseif "global" == t then -- 59 + globals[#globals + 1] = { -- 60 + msg, -- 60 + line, -- 60 + col -- 60 + } -- 60 + end -- 60 + end -- 60 + if luaCodes then -- 61 + local success, lintResult = LintYueGlobals(luaCodes, globals, false) -- 62 + if success then -- 63 + luaCodes = luaCodes:gsub("%s*local%s*_ENV%s*=%s*Dora%([^%)]-%)[^\n\r]+[\n\r%s]*", "\n") -- 64 + if not (lintResult == "") then -- 65 + lintResult = lintResult .. "\n" -- 65 + end -- 65 + luaCodes = "-- [yue]: " .. tostring(file) .. "\n" .. tostring(lintResult) .. luaCodes -- 66 + else -- 67 + for _index_0 = 1, #lintResult do -- 67 + local _des_0 = lintResult[_index_0] -- 67 + local name, line, col = _des_0[1], _des_0[2], _des_0[3] -- 67 + info[#info + 1] = { -- 68 + "syntax", -- 68 + file, -- 68 + line, -- 68 + col, -- 68 + "invalid global variable" -- 68 + } -- 68 + end -- 68 + end -- 63 + end -- 61 + return luaCodes, info -- 69 +end -- 51 +local luaCheck -- 71 +luaCheck = function(file, content) -- 71 + local res, err = load(content, "check") -- 72 + if not res then -- 73 + local line, msg = err:match(".*:(%d+):%s*(.*)") -- 74 + return { -- 75 + success = false, -- 75 + info = { -- 75 + { -- 75 + "syntax", -- 75 + file, -- 75 + tonumber(line), -- 75 + 0, -- 75 + msg -- 75 + } -- 75 + } -- 75 + } -- 75 + end -- 73 + local success, info = teal.checkAsync(content, file, true, "") -- 76 + if info then -- 77 + do -- 78 + local _accum_0 = { } -- 78 + local _len_0 = 1 -- 78 + for _index_0 = 1, #info do -- 78 + local item = info[_index_0] -- 78 + local useCheck = true -- 79 + for _index_1 = 1, #disabledCheckForLua do -- 80 + local check = disabledCheckForLua[_index_1] -- 80 + if not item[5]:match("unused") and item[5]:match(check) then -- 81 + useCheck = false -- 82 + end -- 81 + end -- 82 + if not useCheck then -- 83 + goto _continue_0 -- 83 + end -- 83 + do -- 84 + local _exp_0 = item[1] -- 84 + if "type" == _exp_0 then -- 85 + item[1] = "warning" -- 86 + elseif "parsing" == _exp_0 or "syntax" == _exp_0 then -- 87 + goto _continue_0 -- 88 + end -- 88 + end -- 88 + _accum_0[_len_0] = item -- 89 + _len_0 = _len_0 + 1 -- 89 + ::_continue_0:: -- 79 + end -- 89 + info = _accum_0 -- 78 + end -- 89 + if #info == 0 then -- 90 + info = nil -- 91 + success = true -- 92 + end -- 90 + end -- 77 + return { -- 93 + success = success, -- 93 + info = info -- 93 + } -- 93 +end -- 71 +local luaCheckWithLineInfo -- 95 +luaCheckWithLineInfo = function(file, luaCodes) -- 95 + local res = luaCheck(file, luaCodes) -- 96 + local info = { } -- 97 + if not res.success then -- 98 + local current = 1 -- 99 + local lastLine = 1 -- 100 + local lineMap = { } -- 101 + for lineCode in luaCodes:gmatch("([^\r\n]*)\r?\n?") do -- 102 + local num = lineCode:match("--%s*(%d+)%s*$") -- 103 + if num then -- 104 + lastLine = tonumber(num) -- 105 + end -- 104 + lineMap[current] = lastLine -- 106 + current = current + 1 -- 107 + end -- 107 + local _list_0 = res.info -- 108 + for _index_0 = 1, #_list_0 do -- 108 + local item = _list_0[_index_0] -- 108 + item[3] = lineMap[item[3]] or 0 -- 109 + item[4] = 0 -- 110 + info[#info + 1] = item -- 111 + end -- 111 + return false, info -- 112 + end -- 98 + return true, info -- 113 +end -- 95 +local getCompiledYueLine -- 115 +getCompiledYueLine = function(content, line, row, file) -- 115 + local luaCodes, info = yueCheck(file, content) -- 116 + if not luaCodes then -- 117 + return nil -- 117 + end -- 117 + local current = 1 -- 118 + local lastLine = 1 -- 119 + local targetLine = nil -- 120 + local targetRow = nil -- 121 + local lineMap = { } -- 122 + for lineCode in luaCodes:gmatch("([^\r\n]*)\r?\n?") do -- 123 + local num = lineCode:match("--%s*(%d+)%s*$") -- 124 + if num then -- 125 + lastLine = tonumber(num) -- 125 + end -- 125 + lineMap[current] = lastLine -- 126 + if row == lastLine and not targetLine then -- 127 + targetRow = current -- 128 + targetLine = line:gsub("::", "\\"):gsub(":", "="):gsub("\\", ":"):match("[%w_%.:]+$") -- 129 + if targetLine then -- 130 + break -- 130 + end -- 130 + end -- 127 + current = current + 1 -- 131 + end -- 131 + if targetLine and targetRow then -- 132 + return luaCodes, targetLine, targetRow, lineMap -- 133 + else -- 135 + return nil -- 135 + end -- 132 +end -- 115 +HttpServer:postSchedule("/check", function(req) -- 137 + do -- 138 + local _type_0 = type(req) -- 138 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 138 + if _tab_0 then -- 138 + local file -- 138 + do -- 138 + local _obj_0 = req.body -- 138 + local _type_1 = type(_obj_0) -- 138 + if "table" == _type_1 or "userdata" == _type_1 then -- 138 + file = _obj_0.file -- 138 + end -- 168 + end -- 168 + local content -- 138 + do -- 138 + local _obj_0 = req.body -- 138 + local _type_1 = type(_obj_0) -- 138 + if "table" == _type_1 or "userdata" == _type_1 then -- 138 + content = _obj_0.content -- 138 + end -- 168 + end -- 168 + if file ~= nil and content ~= nil then -- 138 + local ext = Path:getExt(file) -- 139 + if "tl" == ext then -- 140 + local searchPath = getSearchPath(file) -- 141 + local success, info = teal.checkAsync(content, file, false, searchPath) -- 142 + return { -- 143 + success = success, -- 143 + info = info -- 143 + } -- 143 + elseif "lua" == ext then -- 144 + return luaCheck(file, content) -- 145 + elseif "yue" == ext then -- 146 + local luaCodes, info = yueCheck(file, content) -- 147 + local success = false -- 148 + if luaCodes then -- 149 + local luaSuccess, luaInfo = luaCheckWithLineInfo(file, luaCodes) -- 150 + do -- 151 + local _tab_1 = { } -- 151 + local _idx_0 = #_tab_1 + 1 -- 151 + for _index_0 = 1, #info do -- 151 + local _value_0 = info[_index_0] -- 151 + _tab_1[_idx_0] = _value_0 -- 151 + _idx_0 = _idx_0 + 1 -- 151 + end -- 151 + local _idx_1 = #_tab_1 + 1 -- 151 + for _index_0 = 1, #luaInfo do -- 151 + local _value_0 = luaInfo[_index_0] -- 151 + _tab_1[_idx_1] = _value_0 -- 151 + _idx_1 = _idx_1 + 1 -- 151 + end -- 151 + info = _tab_1 -- 151 + end -- 151 + success = success and luaSuccess -- 152 + end -- 149 + if #info > 0 then -- 153 + return { -- 154 + success = success, -- 154 + info = info -- 154 + } -- 154 + else -- 156 + return { -- 156 + success = success -- 156 + } -- 156 + end -- 153 + elseif "xml" == ext then -- 157 + local success, result = xml.check(content) -- 158 + if success then -- 159 + local info -- 160 + success, info = luaCheckWithLineInfo(file, result) -- 160 + if #info > 0 then -- 161 + return { -- 162 + success = success, -- 162 + info = info -- 162 + } -- 162 + else -- 164 + return { -- 164 + success = success -- 164 + } -- 164 + end -- 161 + else -- 166 + local info -- 166 + do -- 166 + local _accum_0 = { } -- 166 + local _len_0 = 1 -- 166 + for _index_0 = 1, #result do -- 166 + local _des_0 = result[_index_0] -- 166 + local row, err = _des_0[1], _des_0[2] -- 166 + _accum_0[_len_0] = { -- 167 + "syntax", -- 167 + file, -- 167 + row, -- 167 + 0, -- 167 + err -- 167 + } -- 167 + _len_0 = _len_0 + 1 -- 167 + end -- 167 + info = _accum_0 -- 166 + end -- 167 + return { -- 168 + success = false, -- 168 + info = info -- 168 + } -- 168 + end -- 159 + end -- 168 + end -- 138 + end -- 168 + end -- 168 + return { -- 137 + success = true -- 137 + } -- 168 +end) -- 137 +local updateInferedDesc -- 170 +updateInferedDesc = function(infered) -- 170 + if not infered.key or infered.key == "" or infered.desc:match("^polymorphic function %(with types ") then -- 171 + return -- 171 + end -- 171 + local key, row = infered.key, infered.row -- 172 + do -- 173 + local codes = Content:loadAsync(key) -- 173 + if codes then -- 173 + local comments = { } -- 174 + local line = 0 -- 175 + local skipping = false -- 176 + for lineCode in codes:gmatch("([^\r\n]*)\r?\n?") do -- 177 + line = line + 1 -- 178 + if line >= row then -- 179 + break -- 179 + end -- 179 + if lineCode:match("^%s*%-%- @") then -- 180 + skipping = true -- 181 + goto _continue_0 -- 182 + end -- 180 + do -- 183 + local result = lineCode:match("^%s*%-%- (.+)") -- 183 + if result then -- 183 + if not skipping then -- 184 + comments[#comments + 1] = result -- 184 + end -- 184 + elseif #comments > 0 then -- 185 + comments = { } -- 186 + skipping = false -- 187 + end -- 183 + end -- 183 + ::_continue_0:: -- 178 + end -- 187 + infered.doc = table.concat(comments, "\n") -- 188 + end -- 173 + end -- 173 +end -- 170 +HttpServer:postSchedule("/infer", function(req) -- 190 + do -- 191 + local _type_0 = type(req) -- 191 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 191 + if _tab_0 then -- 191 + local lang -- 191 + do -- 191 + local _obj_0 = req.body -- 191 + local _type_1 = type(_obj_0) -- 191 + if "table" == _type_1 or "userdata" == _type_1 then -- 191 + lang = _obj_0.lang -- 191 + end -- 208 + end -- 208 + local file -- 191 + do -- 191 + local _obj_0 = req.body -- 191 + local _type_1 = type(_obj_0) -- 191 + if "table" == _type_1 or "userdata" == _type_1 then -- 191 + file = _obj_0.file -- 191 + end -- 208 + end -- 208 + local content -- 191 + do -- 191 + local _obj_0 = req.body -- 191 + local _type_1 = type(_obj_0) -- 191 + if "table" == _type_1 or "userdata" == _type_1 then -- 191 + content = _obj_0.content -- 191 + end -- 208 + end -- 208 + local line -- 191 + do -- 191 + local _obj_0 = req.body -- 191 + local _type_1 = type(_obj_0) -- 191 + if "table" == _type_1 or "userdata" == _type_1 then -- 191 + line = _obj_0.line -- 191 + end -- 208 + end -- 208 + local row -- 191 + do -- 191 + local _obj_0 = req.body -- 191 + local _type_1 = type(_obj_0) -- 191 + if "table" == _type_1 or "userdata" == _type_1 then -- 191 + row = _obj_0.row -- 191 + end -- 208 + end -- 208 + if lang ~= nil and file ~= nil and content ~= nil and line ~= nil and row ~= nil then -- 191 + local searchPath = getSearchPath(file) -- 192 + if "tl" == lang or "lua" == lang then -- 193 + local infered = teal.inferAsync(content, line, row, searchPath) -- 194 + if (infered ~= nil) then -- 195 + updateInferedDesc(infered) -- 196 + return { -- 197 + success = true, -- 197 + infered = infered -- 197 + } -- 197 + end -- 195 + elseif "yue" == lang then -- 198 + local luaCodes, targetLine, targetRow, lineMap = getCompiledYueLine(content, line, row, file) -- 199 + if not luaCodes then -- 200 + return { -- 200 + success = false -- 200 + } -- 200 + end -- 200 + local infered = teal.inferAsync(luaCodes, targetLine, targetRow, searchPath) -- 201 + if (infered ~= nil) then -- 202 + local col -- 203 + file, row, col = infered.file, infered.row, infered.col -- 203 + if file == "" and row > 0 and col > 0 then -- 204 + infered.row = lineMap[row] or 0 -- 205 + infered.col = 0 -- 206 + end -- 204 + updateInferedDesc(infered) -- 207 + return { -- 208 + success = true, -- 208 + infered = infered -- 208 + } -- 208 + end -- 202 + end -- 208 + end -- 191 + end -- 208 + end -- 208 + return { -- 190 + success = false -- 190 + } -- 208 +end) -- 190 +local getParamDocs -- 210 +getParamDocs = function(signatures) -- 210 + do -- 211 + local codes = Content:loadAsync(signatures[1].file) -- 211 + if codes then -- 211 + local comments = { } -- 212 + local params = { } -- 213 + local line = 0 -- 214 + local docs = { } -- 215 + local returnType = nil -- 216 + for lineCode in codes:gmatch("([^\r\n]*)\r?\n?") do -- 217 + line = line + 1 -- 218 + local needBreak = true -- 219 + for i, _des_0 in ipairs(signatures) do -- 220 + local row = _des_0.row -- 220 + if line >= row and not (docs[i] ~= nil) then -- 221 + if #comments > 0 or #params > 0 or returnType then -- 222 + docs[i] = { -- 224 + doc = table.concat(comments, " \n"), -- 224 + returnType = returnType -- 225 + } -- 223 + if #params > 0 then -- 227 + docs[i].params = params -- 227 + end -- 227 + else -- 229 + docs[i] = false -- 229 + end -- 222 + end -- 221 + if not docs[i] then -- 230 + needBreak = false -- 230 + end -- 230 + end -- 230 + if needBreak then -- 231 + break -- 231 + end -- 231 + do -- 232 + local result = lineCode:match("%s*%-%- (.+)") -- 232 + if result then -- 232 + local name, typ, desc = result:match("^@param%s*([%w_]+)%s*%(([^%)]-)%)%s*(.+)") -- 233 + if not name then -- 234 + name, typ, desc = result:match("^@param%s*(%.%.%.)%s*%(([^%)]-)%)%s*(.+)") -- 235 + end -- 234 + if name then -- 236 + local pname = name -- 237 + if desc:match("%[optional%]") or desc:match("%[可选%]") then -- 238 + pname = pname .. "?" -- 238 + end -- 238 + params[#params + 1] = { -- 240 + name = tostring(pname) .. ": " .. tostring(typ), -- 240 + desc = "**" .. tostring(name) .. "**: " .. tostring(desc) -- 241 + } -- 239 + else -- 244 + typ = result:match("^@return%s*%(([^%)]-)%)") -- 244 + if typ then -- 244 + if returnType then -- 245 + returnType = returnType .. ", " .. typ -- 246 + else -- 248 + returnType = typ -- 248 + end -- 245 + result = result:gsub("@return", "**return:**") -- 249 + end -- 244 + comments[#comments + 1] = result -- 250 + end -- 236 + elseif #comments > 0 then -- 251 + comments = { } -- 252 + params = { } -- 253 + returnType = nil -- 254 + end -- 232 + end -- 232 + end -- 254 + local results = { } -- 255 + for _index_0 = 1, #docs do -- 256 + local doc = docs[_index_0] -- 256 + if not doc then -- 257 + goto _continue_0 -- 257 + end -- 257 + if doc.params then -- 258 + doc.desc = "function(" .. tostring(table.concat((function() -- 259 + local _accum_0 = { } -- 259 + local _len_0 = 1 -- 259 + local _list_0 = doc.params -- 259 + for _index_1 = 1, #_list_0 do -- 259 + local param = _list_0[_index_1] -- 259 + _accum_0[_len_0] = param.name -- 259 + _len_0 = _len_0 + 1 -- 259 + end -- 259 + return _accum_0 -- 259 + end)(), ', ')) .. ")" -- 259 + else -- 261 + doc.desc = "function()" -- 261 + end -- 258 + if doc.returnType then -- 262 + doc.desc = doc.desc .. ": " .. tostring(doc.returnType) -- 263 + doc.returnType = nil -- 264 + end -- 262 + results[#results + 1] = doc -- 265 + ::_continue_0:: -- 257 + end -- 265 + if #results > 0 then -- 266 + return results -- 266 + else -- 266 + return nil -- 266 + end -- 266 + end -- 211 + end -- 211 + return nil -- 266 +end -- 210 +HttpServer:postSchedule("/signature", function(req) -- 268 + do -- 269 + local _type_0 = type(req) -- 269 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 269 + if _tab_0 then -- 269 + local lang -- 269 + do -- 269 + local _obj_0 = req.body -- 269 + local _type_1 = type(_obj_0) -- 269 + if "table" == _type_1 or "userdata" == _type_1 then -- 269 + lang = _obj_0.lang -- 269 + end -- 286 + end -- 286 + local file -- 269 + do -- 269 + local _obj_0 = req.body -- 269 + local _type_1 = type(_obj_0) -- 269 + if "table" == _type_1 or "userdata" == _type_1 then -- 269 + file = _obj_0.file -- 269 + end -- 286 + end -- 286 + local content -- 269 + do -- 269 + local _obj_0 = req.body -- 269 + local _type_1 = type(_obj_0) -- 269 + if "table" == _type_1 or "userdata" == _type_1 then -- 269 + content = _obj_0.content -- 269 + end -- 286 + end -- 286 + local line -- 269 + do -- 269 + local _obj_0 = req.body -- 269 + local _type_1 = type(_obj_0) -- 269 + if "table" == _type_1 or "userdata" == _type_1 then -- 269 + line = _obj_0.line -- 269 + end -- 286 + end -- 286 + local row -- 269 + do -- 269 + local _obj_0 = req.body -- 269 + local _type_1 = type(_obj_0) -- 269 + if "table" == _type_1 or "userdata" == _type_1 then -- 269 + row = _obj_0.row -- 269 + end -- 286 + end -- 286 + if lang ~= nil and file ~= nil and content ~= nil and line ~= nil and row ~= nil then -- 269 + local searchPath = getSearchPath(file) -- 270 + if "tl" == lang or "lua" == lang then -- 271 + do -- 272 + local signatures = teal.getSignatureAsync(content, line, row, searchPath) -- 272 + if signatures then -- 272 + signatures = getParamDocs(signatures) -- 273 + if signatures then -- 273 + return { -- 274 + success = true, -- 274 + signatures = signatures -- 274 + } -- 274 + end -- 273 + end -- 272 + end -- 272 + elseif "yue" == lang then -- 275 + local luaCodes, targetLine, targetRow, lineMap = getCompiledYueLine(content, line, row, file) -- 276 + if not luaCodes then -- 277 + return { -- 277 + success = false -- 277 + } -- 277 + end -- 277 + do -- 278 + local chainOp, chainCall = line:match("[^%w_]([%.\\])([^%.\\]+)$") -- 278 + if chainOp then -- 278 + local withVar = luaCodes:match("([%w_]+)%.___DUMMY_CALL___%(%)") -- 279 + targetLine = withVar .. (chainOp == '\\' and ':' or '.') .. chainCall -- 280 + end -- 278 + end -- 278 + do -- 281 + local signatures = teal.getSignatureAsync(luaCodes, targetLine, targetRow, searchPath) -- 281 + if signatures then -- 281 + signatures = getParamDocs(signatures) -- 282 + if signatures then -- 282 + return { -- 283 + success = true, -- 283 + signatures = signatures -- 283 + } -- 283 + end -- 282 + else -- 284 + signatures = teal.getSignatureAsync(luaCodes, "dora." .. tostring(targetLine), targetRow, searchPath) -- 284 + if signatures then -- 284 + signatures = getParamDocs(signatures) -- 285 + if signatures then -- 285 + return { -- 286 + success = true, -- 286 + signatures = signatures -- 286 + } -- 286 + end -- 285 + end -- 284 + end -- 281 + end -- 281 + end -- 286 + end -- 269 + end -- 286 + end -- 286 + return { -- 268 + success = false -- 268 + } -- 286 +end) -- 268 +local luaKeywords = { -- 289 + 'and', -- 289 + 'break', -- 290 + 'do', -- 291 + 'else', -- 292 + 'elseif', -- 293 + 'end', -- 294 + 'false', -- 295 + 'for', -- 296 + 'function', -- 297 + 'goto', -- 298 + 'if', -- 299 + 'in', -- 300 + 'local', -- 301 + 'nil', -- 302 + 'not', -- 303 + 'or', -- 304 + 'repeat', -- 305 + 'return', -- 306 + 'then', -- 307 + 'true', -- 308 + 'until', -- 309 + 'while' -- 310 +} -- 288 +local tealKeywords = { -- 314 + 'record', -- 314 + 'as', -- 315 + 'is', -- 316 + 'type', -- 317 + 'embed', -- 318 + 'enum', -- 319 + 'global', -- 320 + 'any', -- 321 + 'boolean', -- 322 + 'integer', -- 323 + 'number', -- 324 + 'string', -- 325 + 'thread' -- 326 +} -- 313 +local yueKeywords = { -- 330 + "and", -- 330 + "break", -- 331 + "do", -- 332 + "else", -- 333 + "elseif", -- 334 + "false", -- 335 + "for", -- 336 + "goto", -- 337 + "if", -- 338 + "in", -- 339 + "local", -- 340 + "nil", -- 341 + "not", -- 342 + "or", -- 343 + "repeat", -- 344 + "return", -- 345 + "then", -- 346 + "true", -- 347 + "until", -- 348 + "while", -- 349 + "as", -- 350 + "class", -- 351 + "continue", -- 352 + "export", -- 353 + "extends", -- 354 + "from", -- 355 + "global", -- 356 + "import", -- 357 + "macro", -- 358 + "switch", -- 359 + "try", -- 360 + "unless", -- 361 + "using", -- 362 + "when", -- 363 + "with" -- 364 +} -- 329 +HttpServer:postSchedule("/complete", function(req) -- 367 + do -- 368 + local _type_0 = type(req) -- 368 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 368 + if _tab_0 then -- 368 + local lang -- 368 + do -- 368 + local _obj_0 = req.body -- 368 + local _type_1 = type(_obj_0) -- 368 + if "table" == _type_1 or "userdata" == _type_1 then -- 368 + lang = _obj_0.lang -- 368 + end -- 475 + end -- 475 + local file -- 368 + do -- 368 + local _obj_0 = req.body -- 368 + local _type_1 = type(_obj_0) -- 368 + if "table" == _type_1 or "userdata" == _type_1 then -- 368 + file = _obj_0.file -- 368 + end -- 475 + end -- 475 + local content -- 368 + do -- 368 + local _obj_0 = req.body -- 368 + local _type_1 = type(_obj_0) -- 368 + if "table" == _type_1 or "userdata" == _type_1 then -- 368 + content = _obj_0.content -- 368 + end -- 475 + end -- 475 + local line -- 368 + do -- 368 + local _obj_0 = req.body -- 368 + local _type_1 = type(_obj_0) -- 368 + if "table" == _type_1 or "userdata" == _type_1 then -- 368 + line = _obj_0.line -- 368 + end -- 475 + end -- 475 + local row -- 368 + do -- 368 + local _obj_0 = req.body -- 368 + local _type_1 = type(_obj_0) -- 368 + if "table" == _type_1 or "userdata" == _type_1 then -- 368 + row = _obj_0.row -- 368 + end -- 475 + end -- 475 + if lang ~= nil and file ~= nil and content ~= nil and line ~= nil and row ~= nil then -- 368 + local searchPath = getSearchPath(file) -- 369 + repeat -- 370 + local item = line:match("require%s*%(%s*['\"]([%w%d-_%./ ]*)$") -- 371 + if lang == "yue" then -- 372 + if not item then -- 373 + item = line:match("require%s*['\"]([%w%d-_%./ ]*)$") -- 373 + end -- 373 + if not item then -- 374 + item = line:match("import%s*['\"]([%w%d-_%.]*)$") -- 374 + end -- 374 + end -- 372 + local searchType = nil -- 375 + if not item then -- 376 + item = line:match("Sprite%s*%(%s*['\"]([%w%d-_/ ]*)$") -- 377 + if lang == "yue" then -- 378 + item = line:match("Sprite%s*['\"]([%w%d-_/ ]*)$") -- 379 + end -- 378 + if (item ~= nil) then -- 380 + searchType = "Image" -- 380 + end -- 380 + end -- 376 + if not item then -- 381 + item = line:match("Label%s*%(%s*['\"]([%w%d-_/ ]*)$") -- 382 + if lang == "yue" then -- 383 + item = line:match("Label%s*['\"]([%w%d-_/ ]*)$") -- 384 + end -- 383 + if (item ~= nil) then -- 385 + searchType = "Font" -- 385 + end -- 385 + end -- 381 + if not item then -- 386 + break -- 386 + end -- 386 + local searchPaths = Content.searchPaths -- 387 + local _list_0 = getSearchFolders(file) -- 388 + for _index_0 = 1, #_list_0 do -- 388 + local folder = _list_0[_index_0] -- 388 + searchPaths[#searchPaths + 1] = folder -- 389 + end -- 389 + if searchType then -- 390 + searchPaths[#searchPaths + 1] = Content.assetPath -- 390 + end -- 390 + local tokens -- 391 + do -- 391 + local _accum_0 = { } -- 391 + local _len_0 = 1 -- 391 + for mod in item:gmatch("([%w%d-_ ]+)[%./]") do -- 391 + _accum_0[_len_0] = mod -- 391 + _len_0 = _len_0 + 1 -- 391 + end -- 391 + tokens = _accum_0 -- 391 + end -- 391 + local suggestions = { } -- 392 + for _index_0 = 1, #searchPaths do -- 393 + local path = searchPaths[_index_0] -- 393 + local sPath = Path(path, table.unpack(tokens)) -- 394 + if not Content:exist(sPath) then -- 395 + goto _continue_0 -- 395 + end -- 395 + if searchType == "Font" then -- 396 + local fontPath = Path(sPath, "Font") -- 397 + if Content:exist(fontPath) then -- 398 + local _list_1 = Content:getFiles(fontPath) -- 399 + for _index_1 = 1, #_list_1 do -- 399 + local f = _list_1[_index_1] -- 399 + if (function() -- 400 + local _val_0 = Path:getExt(f) -- 400 + return "ttf" == _val_0 or "otf" == _val_0 -- 400 + end)() then -- 400 + if "." == f:sub(1, 1) then -- 401 + goto _continue_1 -- 401 + end -- 401 + suggestions[#suggestions + 1] = { -- 402 + Path:getName(f), -- 402 + "font", -- 402 + "field" -- 402 + } -- 402 + end -- 400 + ::_continue_1:: -- 400 + end -- 402 + end -- 398 + end -- 396 + local _list_1 = Content:getFiles(sPath) -- 403 + for _index_1 = 1, #_list_1 do -- 403 + local f = _list_1[_index_1] -- 403 + if "Image" == searchType then -- 404 + do -- 405 + local _exp_0 = Path:getExt(f) -- 405 + if "clip" == _exp_0 or "jpg" == _exp_0 or "png" == _exp_0 or "dds" == _exp_0 or "pvr" == _exp_0 or "ktx" == _exp_0 then -- 405 + if "." == f:sub(1, 1) then -- 406 + goto _continue_2 -- 406 + end -- 406 + suggestions[#suggestions + 1] = { -- 407 + f, -- 407 + "image", -- 407 + "field" -- 407 + } -- 407 + end -- 407 + end -- 407 + goto _continue_2 -- 408 + elseif "Font" == searchType then -- 409 + do -- 410 + local _exp_0 = Path:getExt(f) -- 410 + if "ttf" == _exp_0 or "otf" == _exp_0 then -- 410 + if "." == f:sub(1, 1) then -- 411 + goto _continue_2 -- 411 + end -- 411 + suggestions[#suggestions + 1] = { -- 412 + f, -- 412 + "font", -- 412 + "field" -- 412 + } -- 412 + end -- 412 + end -- 412 + goto _continue_2 -- 413 + end -- 413 + do -- 414 + local _exp_0 = Path:getExt(f) -- 414 + if "lua" == _exp_0 or "tl" == _exp_0 or "yue" == _exp_0 or "xml" == _exp_0 then -- 414 + local name = Path:getName(f) -- 415 + if "d" == Path:getExt(name) then -- 416 + goto _continue_2 -- 416 + end -- 416 + if "." == name:sub(1, 1) then -- 417 + goto _continue_2 -- 417 + end -- 417 + suggestions[#suggestions + 1] = { -- 418 + name, -- 418 + "module", -- 418 + "field" -- 418 + } -- 418 + end -- 418 + end -- 418 + ::_continue_2:: -- 404 + end -- 418 + local _list_2 = Content:getDirs(sPath) -- 419 + for _index_1 = 1, #_list_2 do -- 419 + local dir = _list_2[_index_1] -- 419 + if "." == dir:sub(1, 1) then -- 420 + goto _continue_3 -- 420 + end -- 420 + suggestions[#suggestions + 1] = { -- 421 + dir, -- 421 + "folder", -- 421 + "variable" -- 421 + } -- 421 + ::_continue_3:: -- 420 + end -- 421 + ::_continue_0:: -- 394 + end -- 421 + if item == "" and not searchType then -- 422 + local _list_1 = teal.completeAsync("", "dora.", 1, searchPath) -- 423 + for _index_0 = 1, #_list_1 do -- 423 + local _des_0 = _list_1[_index_0] -- 423 + local name = _des_0[1] -- 423 + suggestions[#suggestions + 1] = { -- 424 + name, -- 424 + "dora module", -- 424 + "function" -- 424 + } -- 424 + end -- 424 + end -- 422 + if #suggestions > 0 then -- 425 + do -- 426 + local _accum_0 = { } -- 426 + local _len_0 = 1 -- 426 + for _, v in pairs((function() -- 426 + local _tbl_0 = { } -- 426 + for _index_0 = 1, #suggestions do -- 426 + local item = suggestions[_index_0] -- 426 + _tbl_0[item[1] .. item[2]] = item -- 426 + end -- 426 + return _tbl_0 -- 426 + end)()) do -- 426 + _accum_0[_len_0] = v -- 426 + _len_0 = _len_0 + 1 -- 426 + end -- 426 + suggestions = _accum_0 -- 426 + end -- 426 + return { -- 427 + success = true, -- 427 + suggestions = suggestions -- 427 + } -- 427 + else -- 429 + return { -- 429 + success = false -- 429 + } -- 429 + end -- 425 + until true -- 430 + if "tl" == lang or "lua" == lang then -- 431 + local suggestions = teal.completeAsync(content, line, row, searchPath) -- 432 + if not line:match("[%.:][%w_]+[%.:]?$") and not line:match("[%w_]+[%.:]$") then -- 433 + local checkSet -- 434 + do -- 434 + local _tbl_0 = { } -- 434 + for _index_0 = 1, #suggestions do -- 434 + local _des_0 = suggestions[_index_0] -- 434 + local name = _des_0[1] -- 434 + _tbl_0[name] = true -- 434 + end -- 434 + checkSet = _tbl_0 -- 434 + end -- 434 + local _list_0 = teal.completeAsync("", "dora.", 1, searchPath) -- 435 + for _index_0 = 1, #_list_0 do -- 435 + local item = _list_0[_index_0] -- 435 + if not checkSet[item[1]] then -- 436 + suggestions[#suggestions + 1] = item -- 436 + end -- 436 + end -- 436 + for _index_0 = 1, #luaKeywords do -- 437 + local word = luaKeywords[_index_0] -- 437 + suggestions[#suggestions + 1] = { -- 438 + word, -- 438 + "keyword", -- 438 + "keyword" -- 438 + } -- 438 + end -- 438 + if lang == "tl" then -- 439 + for _index_0 = 1, #tealKeywords do -- 440 + local word = tealKeywords[_index_0] -- 440 + suggestions[#suggestions + 1] = { -- 441 + word, -- 441 + "keyword", -- 441 + "keyword" -- 441 + } -- 441 + end -- 441 + end -- 439 + end -- 433 + if #suggestions > 0 then -- 442 + return { -- 443 + success = true, -- 443 + suggestions = suggestions -- 443 + } -- 443 + end -- 442 + elseif "yue" == lang then -- 444 + local suggestions = { } -- 445 + local gotGlobals = false -- 446 + do -- 447 + local luaCodes, targetLine, targetRow = getCompiledYueLine(content, line, row, file) -- 447 + if luaCodes then -- 447 + gotGlobals = true -- 448 + do -- 449 + local chainOp = line:match("[^%w_]([%.\\])$") -- 449 + if chainOp then -- 449 + local withVar = luaCodes:match("([%w_]+)%.___DUMMY_CALL___%(%)") -- 450 + if not withVar then -- 451 + return { -- 451 + success = false -- 451 + } -- 451 + end -- 451 + targetLine = tostring(withVar) .. tostring(chainOp == '\\' and ':' or '.') -- 452 + elseif line:match("^([%.\\])$") then -- 453 + return { -- 454 + success = false -- 454 + } -- 454 + end -- 449 + end -- 449 + local _list_0 = teal.completeAsync(luaCodes, targetLine, targetRow, searchPath) -- 455 + for _index_0 = 1, #_list_0 do -- 455 + local item = _list_0[_index_0] -- 455 + suggestions[#suggestions + 1] = item -- 455 + end -- 455 + if #suggestions == 0 then -- 456 + local _list_1 = teal.completeAsync(luaCodes, "dora." .. tostring(targetLine), targetRow, searchPath) -- 457 + for _index_0 = 1, #_list_1 do -- 457 + local item = _list_1[_index_0] -- 457 + suggestions[#suggestions + 1] = item -- 457 + end -- 457 + end -- 456 + end -- 447 + end -- 447 + if not line:match("[%.:\\][%w_]+[%.\\]?$") and not line:match("[%.\\]$") then -- 458 + local checkSet -- 459 + do -- 459 + local _tbl_0 = { } -- 459 + for _index_0 = 1, #suggestions do -- 459 + local _des_0 = suggestions[_index_0] -- 459 + local name = _des_0[1] -- 459 + _tbl_0[name] = true -- 459 + end -- 459 + checkSet = _tbl_0 -- 459 + end -- 459 + local _list_0 = teal.completeAsync("", "dora.", 1, searchPath) -- 460 + for _index_0 = 1, #_list_0 do -- 460 + local item = _list_0[_index_0] -- 460 + if not checkSet[item[1]] then -- 461 + suggestions[#suggestions + 1] = item -- 461 + end -- 461 + end -- 461 + if not gotGlobals then -- 462 + local _list_1 = teal.completeAsync("", "x", 1, searchPath) -- 463 + for _index_0 = 1, #_list_1 do -- 463 + local item = _list_1[_index_0] -- 463 + if not checkSet[item[1]] then -- 464 + suggestions[#suggestions + 1] = item -- 464 + end -- 464 + end -- 464 + end -- 462 + for _index_0 = 1, #yueKeywords do -- 465 + local word = yueKeywords[_index_0] -- 465 + if not checkSet[word] then -- 466 + suggestions[#suggestions + 1] = { -- 467 + word, -- 467 + "keyword", -- 467 + "keyword" -- 467 + } -- 467 + end -- 466 + end -- 467 + end -- 458 + if #suggestions > 0 then -- 468 + return { -- 469 + success = true, -- 469 + suggestions = suggestions -- 469 + } -- 469 + end -- 468 + elseif "xml" == lang then -- 470 + local items = xml.complete(content) -- 471 + if #items > 0 then -- 472 + local suggestions -- 473 + do -- 473 + local _accum_0 = { } -- 473 + local _len_0 = 1 -- 473 + for _index_0 = 1, #items do -- 473 + local _des_0 = items[_index_0] -- 473 + local label, insertText = _des_0[1], _des_0[2] -- 473 + _accum_0[_len_0] = { -- 474 + label, -- 474 + insertText, -- 474 + "field" -- 474 + } -- 474 + _len_0 = _len_0 + 1 -- 474 + end -- 474 + suggestions = _accum_0 -- 473 + end -- 474 + return { -- 475 + success = true, -- 475 + suggestions = suggestions -- 475 + } -- 475 + end -- 472 + end -- 475 + end -- 368 + end -- 475 + end -- 475 + return { -- 367 + success = false -- 367 + } -- 475 +end) -- 367 +HttpServer:upload("/upload", function(req, filename) -- 479 + do -- 480 + local _type_0 = type(req) -- 480 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 480 + if _tab_0 then -- 480 + local path -- 480 + do -- 480 + local _obj_0 = req.params -- 480 + local _type_1 = type(_obj_0) -- 480 + if "table" == _type_1 or "userdata" == _type_1 then -- 480 + path = _obj_0.path -- 480 + end -- 486 + end -- 486 + if path ~= nil then -- 480 + local uploadPath = Path(Content.writablePath, ".upload") -- 481 + if not Content:exist(uploadPath) then -- 482 + Content:mkdir(uploadPath) -- 483 + end -- 482 + local targetPath = Path(uploadPath, filename) -- 484 + Content:mkdir(Path:getPath(targetPath)) -- 485 + return targetPath -- 486 + end -- 480 + end -- 486 + end -- 486 + return nil -- 486 +end, function(req, file) -- 487 + do -- 488 + local _type_0 = type(req) -- 488 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 488 + if _tab_0 then -- 488 + local path -- 488 + do -- 488 + local _obj_0 = req.params -- 488 + local _type_1 = type(_obj_0) -- 488 + if "table" == _type_1 or "userdata" == _type_1 then -- 488 + path = _obj_0.path -- 488 + end -- 493 + end -- 493 + if path ~= nil then -- 488 + local uploadPath = Path(Content.writablePath, ".upload") -- 489 + local targetPath = Path(path, Path:getRelative(file, uploadPath)) -- 490 + Content:mkdir(Path:getPath(targetPath)) -- 491 + if Content:move(file, targetPath) then -- 492 + return true -- 493 + end -- 492 + end -- 488 + end -- 493 + end -- 493 + return false -- 493 +end) -- 477 +HttpServer:post("/list", function(req) -- 496 + do -- 497 + local _type_0 = type(req) -- 497 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 497 + if _tab_0 then -- 497 + local path -- 497 + do -- 497 + local _obj_0 = req.body -- 497 + local _type_1 = type(_obj_0) -- 497 + if "table" == _type_1 or "userdata" == _type_1 then -- 497 + path = _obj_0.path -- 497 + end -- 519 + end -- 519 + if path ~= nil then -- 497 + if Content:exist(path) then -- 498 + local files = { } -- 499 + local visitAssets -- 500 + visitAssets = function(path, folder) -- 500 + local dirs = Content:getDirs(path) -- 501 + for _index_0 = 1, #dirs do -- 502 + local dir = dirs[_index_0] -- 502 + if dir:match("^%.") then -- 503 + goto _continue_0 -- 503 + end -- 503 + local current -- 504 + if folder == "" then -- 504 + current = dir -- 505 + else -- 507 + current = Path(folder, dir) -- 507 + end -- 504 + files[#files + 1] = current -- 508 + visitAssets(Path(path, dir), current) -- 509 + ::_continue_0:: -- 503 + end -- 509 + local fs = Content:getFiles(path) -- 510 + for _index_0 = 1, #fs do -- 511 + local f = fs[_index_0] -- 511 + if f:match("^%.") then -- 512 + goto _continue_1 -- 512 + end -- 512 + if folder == "" then -- 513 + files[#files + 1] = f -- 514 + else -- 516 + files[#files + 1] = Path(folder, f) -- 516 + end -- 513 + ::_continue_1:: -- 512 + end -- 516 + end -- 500 + visitAssets(path, "") -- 517 + if #files == 0 then -- 518 + files = nil -- 518 + end -- 518 + return { -- 519 + success = true, -- 519 + files = files -- 519 + } -- 519 + end -- 498 + end -- 497 + end -- 519 + end -- 519 + return { -- 496 + success = false -- 496 + } -- 519 +end) -- 496 +HttpServer:post("/info", function() -- 521 + return { -- 522 + platform = App.platform, -- 522 + locale = App.locale, -- 523 + version = App.version -- 524 + } -- 524 +end) -- 521 +HttpServer:post("/new", function(req) -- 526 + do -- 527 + local _type_0 = type(req) -- 527 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 527 + if _tab_0 then -- 527 + local path -- 527 + do -- 527 + local _obj_0 = req.body -- 527 + local _type_1 = type(_obj_0) -- 527 + if "table" == _type_1 or "userdata" == _type_1 then -- 527 + path = _obj_0.path -- 527 + end -- 539 + end -- 539 + local content -- 527 + do -- 527 + local _obj_0 = req.body -- 527 + local _type_1 = type(_obj_0) -- 527 + if "table" == _type_1 or "userdata" == _type_1 then -- 527 + content = _obj_0.content -- 527 + end -- 539 + end -- 539 + if path ~= nil and content ~= nil then -- 527 + if not Content:exist(path) then -- 528 + local parent = Path:getPath(path) -- 529 + local files = Content:getFiles(parent) -- 530 + local name = Path:getName(path):lower() -- 531 + for _index_0 = 1, #files do -- 532 + local file = files[_index_0] -- 532 + if name == Path:getName(file):lower() then -- 533 + return { -- 534 + success = false -- 534 + } -- 534 + end -- 533 + end -- 534 + if "" == Path:getExt(path) then -- 535 + if Content:mkdir(path) then -- 536 + return { -- 537 + success = true -- 537 + } -- 537 + end -- 536 + elseif Content:save(path, content) then -- 538 + return { -- 539 + success = true -- 539 + } -- 539 + end -- 535 + end -- 528 + end -- 527 + end -- 539 + end -- 539 + return { -- 526 + success = false -- 526 + } -- 539 +end) -- 526 +HttpServer:post("/delete", function(req) -- 541 + do -- 542 + local _type_0 = type(req) -- 542 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 542 + if _tab_0 then -- 542 + local path -- 542 + do -- 542 + local _obj_0 = req.body -- 542 + local _type_1 = type(_obj_0) -- 542 + if "table" == _type_1 or "userdata" == _type_1 then -- 542 + path = _obj_0.path -- 542 + end -- 555 + end -- 555 + if path ~= nil then -- 542 + if Content:exist(path) then -- 543 + local parent = Path:getPath(path) -- 544 + local files = Content:getFiles(parent) -- 545 + local name = Path:getName(path):lower() -- 546 + local ext = Path:getExt(path) -- 547 + for _index_0 = 1, #files do -- 548 + local file = files[_index_0] -- 548 + if name == Path:getName(file):lower() then -- 549 + do -- 550 + local _exp_0 = Path:getExt(file) -- 550 + if "tl" == _exp_0 then -- 550 + if ("vs" == ext) then -- 550 + Content:remove(Path(parent, file)) -- 551 + end -- 550 + elseif "lua" == _exp_0 then -- 552 + if ("tl" == ext or "yue" == ext or "ts" == ext or "tsx" == ext or "vs" == ext or "xml" == ext) then -- 552 + Content:remove(Path(parent, file)) -- 553 + end -- 552 + end -- 553 + end -- 553 + end -- 549 + end -- 553 + if Content:remove(path) then -- 554 + return { -- 555 + success = true -- 555 + } -- 555 + end -- 554 + end -- 543 + end -- 542 + end -- 555 + end -- 555 + return { -- 541 + success = false -- 541 + } -- 555 +end) -- 541 +HttpServer:post("/rename", function(req) -- 557 + do -- 558 + local _type_0 = type(req) -- 558 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 558 + if _tab_0 then -- 558 + local old -- 558 + do -- 558 + local _obj_0 = req.body -- 558 + local _type_1 = type(_obj_0) -- 558 + if "table" == _type_1 or "userdata" == _type_1 then -- 558 + old = _obj_0.old -- 558 + end -- 580 + end -- 580 + local new -- 558 + do -- 558 + local _obj_0 = req.body -- 558 + local _type_1 = type(_obj_0) -- 558 + if "table" == _type_1 or "userdata" == _type_1 then -- 558 + new = _obj_0.new -- 558 + end -- 580 + end -- 580 + if old ~= nil and new ~= nil then -- 558 + if Content:exist(old) and not Content:exist(new) then -- 559 + local parent = Path:getPath(new) -- 560 + local files = Content:getFiles(parent) -- 561 + local name = Path:getName(new):lower() -- 562 + for _index_0 = 1, #files do -- 563 + local file = files[_index_0] -- 563 + if name == Path:getName(file):lower() then -- 564 + return { -- 565 + success = false -- 565 + } -- 565 + end -- 564 + end -- 565 + if Content:move(old, new) then -- 566 + local newParent = Path:getPath(new) -- 567 + parent = Path:getPath(old) -- 568 + files = Content:getFiles(parent) -- 569 + local newName = Path:getName(new) -- 570 + local oldName = Path:getName(old) -- 571 + name = oldName:lower() -- 572 + local ext = Path:getExt(old) -- 573 + for _index_0 = 1, #files do -- 574 + local file = files[_index_0] -- 574 + if name == Path:getName(file):lower() then -- 575 + do -- 576 + local _exp_0 = Path:getExt(file) -- 576 + if "tl" == _exp_0 then -- 576 + if ("vs" == ext) then -- 576 + Content:move(Path(parent, file), Path(newParent, newName .. ".tl")) -- 577 + end -- 576 + elseif "lua" == _exp_0 then -- 578 + if ("tl" == ext or "yue" == ext or "ts" == ext or "tsx" == ext or "vs" == ext or "xml" == ext) then -- 578 + Content:move(Path(parent, file), Path(newParent, newName .. ".lua")) -- 579 + end -- 578 + end -- 579 + end -- 579 + end -- 575 + end -- 579 + return { -- 580 + success = true -- 580 + } -- 580 + end -- 566 + end -- 559 + end -- 558 + end -- 580 + end -- 580 + return { -- 557 + success = false -- 557 + } -- 580 +end) -- 557 +HttpServer:postSchedule("/read", function(req) -- 582 + do -- 583 + local _type_0 = type(req) -- 583 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 583 + if _tab_0 then -- 583 + local path -- 583 + do -- 583 + local _obj_0 = req.body -- 583 + local _type_1 = type(_obj_0) -- 583 + if "table" == _type_1 or "userdata" == _type_1 then -- 583 + path = _obj_0.path -- 583 + end -- 586 + end -- 586 + if path ~= nil then -- 583 + if Content:exist(path) then -- 584 + do -- 585 + local content = Content:loadAsync(path) -- 585 + if content then -- 585 + return { -- 586 + content = content, -- 586 + success = true -- 586 + } -- 586 + end -- 585 + end -- 585 + end -- 584 + end -- 583 + end -- 586 + end -- 586 + return { -- 582 + success = false -- 582 + } -- 586 +end) -- 582 +local compileFileAsync -- 588 +compileFileAsync = function(inputFile, sourceCodes) -- 588 + local file = Path:getFilename(inputFile) -- 589 + local searchPath -- 590 + do -- 590 + local dir = getProjectDirFromFile(inputFile) -- 590 + if dir then -- 590 + file = Path:getRelative(inputFile, Path(Content.writablePath, dir)) -- 591 + searchPath = Path(dir, "Script", "?.lua") .. ";" .. Path(dir, "?.lua") -- 592 + else -- 593 + searchPath = "" -- 593 + end -- 590 + end -- 590 + local outputFile = Path:replaceExt(inputFile, "lua") -- 594 + local yueext = yue.options.extension -- 595 + local resultCodes = nil -- 596 + do -- 597 + local _exp_0 = Path:getExt(inputFile) -- 597 + if yueext == _exp_0 then -- 597 + yue.compile(inputFile, outputFile, searchPath, function(codes, err, globals) -- 598 + if not codes then -- 599 + return -- 599 + end -- 599 + local success, result = LintYueGlobals(codes, globals) -- 600 + if not success then -- 601 + return -- 601 + end -- 601 + codes = codes:gsub("%s*local%s*_ENV%s*=%s*Dora%([^%)]-%)[^\n\r]+[\n\r%s]*", "\n") -- 602 + codes = codes:gsub("^\n*", "") -- 603 + if not (result == "") then -- 604 + result = result .. "\n" -- 604 + end -- 604 + resultCodes = "-- [yue]: " .. tostring(file) .. "\n" .. tostring(result) .. tostring(codes) -- 605 + return resultCodes -- 606 + end, function(success) -- 598 + if not success then -- 607 + Content:remove(outputFile) -- 608 + resultCodes = false -- 609 + end -- 607 + end) -- 598 + elseif "tl" == _exp_0 then -- 610 + do -- 611 + local codes = teal.toluaAsync(sourceCodes, file, searchPath) -- 611 + if codes then -- 611 + resultCodes = codes -- 612 + Content:saveAsync(outputFile, codes) -- 613 + else -- 615 + Content:remove(outputFile) -- 615 + resultCodes = false -- 616 + end -- 611 + end -- 611 + elseif "xml" == _exp_0 then -- 617 + do -- 618 + local codes = xml.tolua(sourceCodes) -- 618 + if codes then -- 618 + resultCodes = "-- [xml]: " .. tostring(file) .. "\n" .. tostring(codes) -- 619 + Content:saveAsync(outputFile, resultCodes) -- 620 + else -- 622 + Content:remove(outputFile) -- 622 + resultCodes = false -- 623 + end -- 618 + end -- 618 + end -- 623 + end -- 623 + wait(function() -- 624 + return resultCodes ~= nil -- 624 + end) -- 624 + if resultCodes then -- 625 + return resultCodes -- 625 + end -- 625 + return nil -- 625 +end -- 588 +HttpServer:postSchedule("/write", function(req) -- 627 + do -- 628 + local _type_0 = type(req) -- 628 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 628 + if _tab_0 then -- 628 + local path -- 628 + do -- 628 + local _obj_0 = req.body -- 628 + local _type_1 = type(_obj_0) -- 628 + if "table" == _type_1 or "userdata" == _type_1 then -- 628 + path = _obj_0.path -- 628 + end -- 634 + end -- 634 + local content -- 628 + do -- 628 + local _obj_0 = req.body -- 628 + local _type_1 = type(_obj_0) -- 628 + if "table" == _type_1 or "userdata" == _type_1 then -- 628 + content = _obj_0.content -- 628 + end -- 634 + end -- 634 + if path ~= nil and content ~= nil then -- 628 + if Content:saveAsync(path, content) then -- 629 + do -- 630 + local _exp_0 = Path:getExt(path) -- 630 + if "tl" == _exp_0 or "yue" == _exp_0 or "xml" == _exp_0 then -- 630 + if '' == Path:getExt(Path:getName(path)) then -- 631 + local resultCodes = compileFileAsync(path, content) -- 632 + return { -- 633 + success = true, -- 633 + resultCodes = resultCodes -- 633 + } -- 633 + end -- 631 + end -- 633 + end -- 633 + return { -- 634 + success = true -- 634 + } -- 634 + end -- 629 + end -- 628 + end -- 634 + end -- 634 + return { -- 627 + success = false -- 627 + } -- 634 +end) -- 627 +local extentionLevels = { -- 637 + vs = 2, -- 637 + ts = 1, -- 638 + tsx = 1, -- 639 + tl = 1, -- 640 + yue = 1, -- 641 + xml = 1, -- 642 + lua = 0 -- 643 +} -- 636 +HttpServer:post("/assets", function() -- 645 + local visitAssets -- 646 + visitAssets = function(path, root) -- 646 + local children = nil -- 647 + local dirs = Content:getDirs(path) -- 648 + for _index_0 = 1, #dirs do -- 649 + local dir = dirs[_index_0] -- 649 + if root then -- 650 + if ".upload" == dir or ".download" == dir or ".www" == dir or ".build" == dir or ".git" == dir then -- 650 + goto _continue_0 -- 651 + end -- 651 + elseif dir == ".git" then -- 652 + goto _continue_0 -- 653 + end -- 650 + if not children then -- 654 + children = { } -- 654 + end -- 654 + children[#children + 1] = visitAssets(Path(path, dir)) -- 655 + ::_continue_0:: -- 650 + end -- 655 + local files = Content:getFiles(path) -- 656 + local names = { } -- 657 + for _index_0 = 1, #files do -- 658 + local file = files[_index_0] -- 658 + if file:match("^%.") then -- 659 + goto _continue_1 -- 659 + end -- 659 + local name = Path:getName(file) -- 660 + do -- 661 + local ext = names[name] -- 661 + if ext then -- 661 + local lv1 -- 662 + do -- 662 + local _exp_0 = extentionLevels[ext] -- 662 + if _exp_0 ~= nil then -- 662 + lv1 = _exp_0 -- 662 + else -- 662 + lv1 = -1 -- 662 + end -- 662 + end -- 662 + ext = Path:getExt(file) -- 663 + local lv2 -- 664 + do -- 664 + local _exp_0 = extentionLevels[ext] -- 664 + if _exp_0 ~= nil then -- 664 + lv2 = _exp_0 -- 664 + else -- 664 + lv2 = -1 -- 664 + end -- 664 + end -- 664 + if lv2 > lv1 then -- 665 + names[name] = ext -- 665 + end -- 665 + else -- 667 + ext = Path:getExt(file) -- 667 + if not extentionLevels[ext] then -- 668 + names[file] = "" -- 669 + else -- 671 + names[name] = ext -- 671 + end -- 668 + end -- 661 + end -- 661 + ::_continue_1:: -- 659 + end -- 671 + do -- 672 + local _accum_0 = { } -- 672 + local _len_0 = 1 -- 672 + for name, ext in pairs(names) do -- 672 + _accum_0[_len_0] = ext == '' and name or name .. '.' .. ext -- 672 + _len_0 = _len_0 + 1 -- 672 + end -- 672 + files = _accum_0 -- 672 + end -- 672 + for _index_0 = 1, #files do -- 673 + local file = files[_index_0] -- 673 + if not children then -- 674 + children = { } -- 674 + end -- 674 + children[#children + 1] = { -- 676 + key = Path(path, file), -- 676 + dir = false, -- 677 + title = file -- 678 + } -- 675 + end -- 679 + if children then -- 680 + table.sort(children, function(a, b) -- 681 + if a.dir == b.dir then -- 682 + return a.title < b.title -- 683 + else -- 685 + return a.dir -- 685 + end -- 682 + end) -- 681 + end -- 680 + local title = Path:getFilename(path) -- 686 + if title == "" then -- 687 + return children -- 688 + else -- 690 + return { -- 691 + key = path, -- 691 + dir = true, -- 692 + title = title, -- 693 + children = children -- 694 + } -- 695 + end -- 687 + end -- 646 + local zh = (App.locale:match("^zh") ~= nil) -- 696 + return { -- 698 + key = Content.writablePath, -- 698 + dir = true, -- 699 + title = "Assets", -- 700 + children = (function() -- 702 + local _tab_0 = { -- 702 + { -- 703 + key = Path(Content.assetPath), -- 703 + dir = true, -- 704 + title = zh and "内置资源" or "Built-in", -- 705 + children = { -- 707 + (function() -- 707 + local _with_0 = visitAssets(Path(Content.assetPath, "Doc", zh and "zh-Hans" or "en")) -- 707 + _with_0.title = zh and "说明文档" or "Readme" -- 708 + return _with_0 -- 707 + end)(), -- 707 + (function() -- 709 + local _with_0 = visitAssets(Path(Content.assetPath, "Script", "Lib", "Dora", zh and "zh-Hans" or "en")) -- 709 + _with_0.title = zh and "接口文档" or "API Doc" -- 710 + return _with_0 -- 709 + end)(), -- 709 + (function() -- 711 + local _with_0 = visitAssets(Path(Content.assetPath, "Script", "Example")) -- 711 + _with_0.title = zh and "代码示例" or "Example" -- 712 + return _with_0 -- 711 + end)(), -- 711 + (function() -- 713 + local _with_0 = visitAssets(Path(Content.assetPath, "Script", "Test")) -- 713 + _with_0.title = zh and "功能测试" or "Test" -- 714 + return _with_0 -- 713 + end)(), -- 713 + visitAssets(Path(Content.assetPath, "Image")), -- 715 + visitAssets(Path(Content.assetPath, "Spine")), -- 716 + visitAssets(Path(Content.assetPath, "Font")) -- 717 + } -- 706 + } -- 702 + } -- 720 + local _obj_0 = visitAssets(Content.writablePath, true) -- 720 + local _idx_0 = #_tab_0 + 1 -- 720 + for _index_0 = 1, #_obj_0 do -- 720 + local _value_0 = _obj_0[_index_0] -- 720 + _tab_0[_idx_0] = _value_0 -- 720 + _idx_0 = _idx_0 + 1 -- 720 + end -- 720 + return _tab_0 -- 719 + end)() -- 701 + } -- 722 +end) -- 645 +HttpServer:postSchedule("/run", function(req) -- 724 + do -- 725 + local _type_0 = type(req) -- 725 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 725 + if _tab_0 then -- 725 + local file -- 725 + do -- 725 + local _obj_0 = req.body -- 725 + local _type_1 = type(_obj_0) -- 725 + if "table" == _type_1 or "userdata" == _type_1 then -- 725 + file = _obj_0.file -- 725 + end -- 737 + end -- 737 + local asProj -- 725 + do -- 725 + local _obj_0 = req.body -- 725 + local _type_1 = type(_obj_0) -- 725 + if "table" == _type_1 or "userdata" == _type_1 then -- 725 + asProj = _obj_0.asProj -- 725 + end -- 737 + end -- 737 + if file ~= nil and asProj ~= nil then -- 725 + local Entry = require("Dev.Entry") -- 726 + if asProj then -- 727 + do -- 728 + local proj = getProjectDirFromFile(file) -- 728 + if proj then -- 728 + Entry.allClear() -- 729 + local target = Path(proj, "init") -- 730 + local success, err = Entry.enterEntryAsync({ -- 731 + "Project", -- 731 + target -- 731 + }) -- 731 + target = Path:getName(Path:getPath(target)) -- 732 + return { -- 733 + success = success, -- 733 + target = target, -- 733 + err = err -- 733 + } -- 733 + end -- 728 + end -- 728 + end -- 727 + Entry.allClear() -- 734 + file = Path:replaceExt(file, "") -- 735 + local success, err = Entry.enterEntryAsync({ -- 736 + Path:getName(file), -- 736 + file -- 736 + }) -- 736 + return { -- 737 + success = success, -- 737 + err = err -- 737 + } -- 737 + end -- 725 + end -- 737 + end -- 737 + return { -- 724 + success = false -- 724 + } -- 737 +end) -- 724 +HttpServer:postSchedule("/stop", function() -- 739 + local Entry = require("Dev.Entry") -- 740 + return { -- 741 + success = Entry.stop() -- 741 + } -- 741 +end) -- 739 +HttpServer:postSchedule("/zip", function(req) -- 743 + do -- 744 + local _type_0 = type(req) -- 744 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 744 + if _tab_0 then -- 744 + local path -- 744 + do -- 744 + local _obj_0 = req.body -- 744 + local _type_1 = type(_obj_0) -- 744 + if "table" == _type_1 or "userdata" == _type_1 then -- 744 + path = _obj_0.path -- 744 + end -- 747 + end -- 747 + local zipFile -- 744 + do -- 744 + local _obj_0 = req.body -- 744 + local _type_1 = type(_obj_0) -- 744 + if "table" == _type_1 or "userdata" == _type_1 then -- 744 + zipFile = _obj_0.zipFile -- 744 + end -- 747 + end -- 747 + if path ~= nil and zipFile ~= nil then -- 744 + Content:mkdir(Path:getPath(zipFile)) -- 745 + return { -- 746 + success = Content:zipAsync(path, zipFile, function(file) -- 746 + return not (file:match('^%.') or file:match("[\\/]%.")) -- 747 + end) -- 746 + } -- 747 + end -- 744 + end -- 747 + end -- 747 + return { -- 743 + success = false -- 743 + } -- 747 +end) -- 743 +HttpServer:postSchedule("/unzip", function(req) -- 749 + do -- 750 + local _type_0 = type(req) -- 750 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 750 + if _tab_0 then -- 750 + local zipFile -- 750 + do -- 750 + local _obj_0 = req.body -- 750 + local _type_1 = type(_obj_0) -- 750 + if "table" == _type_1 or "userdata" == _type_1 then -- 750 + zipFile = _obj_0.zipFile -- 750 + end -- 752 + end -- 752 + local path -- 750 + do -- 750 + local _obj_0 = req.body -- 750 + local _type_1 = type(_obj_0) -- 750 + if "table" == _type_1 or "userdata" == _type_1 then -- 750 + path = _obj_0.path -- 750 + end -- 752 + end -- 752 + if zipFile ~= nil and path ~= nil then -- 750 + return { -- 751 + success = Content:unzipAsync(zipFile, path, function(file) -- 751 + return not (file:match('^%.') or file:match("[\\/]%.") or file:match("__MACOSX")) -- 752 + end) -- 751 + } -- 752 + end -- 750 + end -- 752 + end -- 752 + return { -- 753 + success = false -- 753 + } -- 753 +end) -- 749 +HttpServer:post("/editingInfo", function(req) -- 755 + local Entry = require("Dev.Entry") -- 756 + local config = Entry.getConfig() -- 757 + do -- 758 + local _type_0 = type(req) -- 758 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 758 + local _match_0 = false -- 758 + if _tab_0 then -- 758 + local editingInfo -- 758 + do -- 758 + local _obj_0 = req.body -- 758 + local _type_1 = type(_obj_0) -- 758 + if "table" == _type_1 or "userdata" == _type_1 then -- 758 + editingInfo = _obj_0.editingInfo -- 758 + end -- 760 + end -- 760 + if editingInfo ~= nil then -- 758 + _match_0 = true -- 758 + config.editingInfo = editingInfo -- 759 + return { -- 760 + success = true -- 760 + } -- 760 + end -- 758 + end -- 758 + if not _match_0 then -- 758 + if not (config.editingInfo ~= nil) then -- 762 + local json = require("json") -- 763 + local folder -- 764 + if App.locale:match('^zh') then -- 764 + folder = 'zh-Hans' -- 764 + else -- 764 + folder = 'en' -- 764 + end -- 764 + config.editingInfo = json.dump({ -- 766 + index = 0, -- 766 + files = { -- 768 + { -- 769 + key = Path(Content.assetPath, 'Doc', folder, 'welcome.md'), -- 769 + title = "welcome.md" -- 770 + } -- 768 + } -- 767 + }) -- 765 + end -- 762 + return { -- 774 + success = true, -- 774 + editingInfo = config.editingInfo -- 774 + } -- 774 + end -- 774 + end -- 774 +end) -- 755 +HttpServer:post("/command", function(req) -- 776 + do -- 777 + local _type_0 = type(req) -- 777 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 777 + if _tab_0 then -- 777 + local code -- 777 + do -- 777 + local _obj_0 = req.body -- 777 + local _type_1 = type(_obj_0) -- 777 + if "table" == _type_1 or "userdata" == _type_1 then -- 777 + code = _obj_0.code -- 777 + end -- 779 + end -- 779 + if code ~= nil then -- 777 + emit("AppCommand", code) -- 778 + return { -- 779 + success = true -- 779 + } -- 779 + end -- 777 + end -- 779 + end -- 779 + return { -- 776 + success = false -- 776 + } -- 779 +end) -- 776 +HttpServer:post("/exist", function(req) -- 781 + do -- 782 + local _type_0 = type(req) -- 782 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 782 + if _tab_0 then -- 782 + local file -- 782 + do -- 782 + local _obj_0 = req.body -- 782 + local _type_1 = type(_obj_0) -- 782 + if "table" == _type_1 or "userdata" == _type_1 then -- 782 + file = _obj_0.file -- 782 + end -- 783 + end -- 783 + if file ~= nil then -- 782 + return { -- 783 + success = Content:exist(file) -- 783 + } -- 783 + end -- 782 + end -- 783 + end -- 783 + return { -- 781 + success = false -- 781 + } -- 783 +end) -- 781 +local status = { -- 785 + url = nil -- 785 +} -- 785 +_module_0 = status -- 786 +thread(function() -- 788 + local doraWeb = Path(Content.assetPath, "www", "index.html") -- 789 + local doraReady = Path(Content.writablePath, ".www", "dora-ready") -- 790 + if Content:exist(doraWeb) then -- 791 + local needReload -- 792 + if Content:exist(doraReady) then -- 792 + needReload = App.version ~= Content:load(doraReady) -- 793 + else -- 794 + needReload = true -- 794 + end -- 792 + if needReload then -- 795 + Content:remove(Path(Content.writablePath, ".www")) -- 796 + Content:copyAsync(Path(Content.assetPath, "www"), Path(Content.writablePath, ".www")) -- 797 + Content:save(doraReady, App.version) -- 801 + print("Dora Dora is ready!") -- 802 + end -- 795 + if HttpServer:start(8866) then -- 803 + local localIP = HttpServer.localIP -- 804 + if localIP == "" then -- 805 + localIP = "localhost" -- 805 + end -- 805 + status.url = "http://" .. tostring(localIP) .. ":8866" -- 806 + return HttpServer:startWS(8868) -- 807 + else -- 809 + status.url = nil -- 809 + return print("8866 Port not available!") -- 810 + end -- 803 + end -- 791 +end) -- 788 +return _module_0 -- 810 diff --git a/Assets/Script/Lib/YarnRunner.lua b/Assets/Script/Lib/YarnRunner.lua new file mode 100644 index 000000000..3279ae7a6 --- /dev/null +++ b/Assets/Script/Lib/YarnRunner.lua @@ -0,0 +1,299 @@ +-- [yue]: Script/Lib/YarnRunner.yue +local tonumber = _G.tonumber -- 1 +local tostring = _G.tostring -- 1 +local coroutine = _G.coroutine -- 1 +local load = _G.load -- 1 +local error = _G.error -- 1 +local pairs = _G.pairs -- 1 +local setmetatable = _G.setmetatable -- 1 +local getmetatable = _G.getmetatable -- 1 +local math = _G.math -- 1 +local _module_0 = nil -- 1 +local yarncompile = require("yarncompile") -- 1 +local Content = require("Content") -- 2 +local json = require("json") -- 3 +local rewriteError -- 5 +rewriteError = function(err, luaCode, title) -- 5 + local line, msg = err:match(".*:(%d+):%s*(.*)") -- 6 + line = tonumber(line) -- 7 + local current = 1 -- 8 + local lastLine = 1 -- 9 + local lineMap = { } -- 10 + for lineCode in luaCode:gmatch("([^\r\n]*)\r?\n?") do -- 11 + local num = lineCode:match("--%s*(%d+)%s*$") -- 12 + if num then -- 13 + lastLine = tonumber(num) -- 13 + end -- 13 + lineMap[current] = lastLine -- 14 + current = current + 1 -- 15 + end -- 15 + line = lineMap[line] or line -- 16 + return tostring(title) .. ":" .. tostring(line) .. ": " .. tostring(msg) -- 17 +end -- 5 +local YarnRunner -- 19 +do -- 19 + local _class_0 -- 19 + local _base_0 = { -- 19 + gotoStory = function(self, title) -- 20 + local storyFunc = self.funcs[title] -- 21 + if not storyFunc then -- 22 + local yarnCode = self.codes[title] -- 23 + local luaCode, err = yarncompile(yarnCode) -- 24 + if not luaCode then -- 25 + if self.startTitle then -- 26 + return false, tostring(title) .. ":" .. tostring(err) -- 27 + else -- 29 + coroutine.yield("Error", tostring(title) .. ":" .. tostring(err)) -- 29 + return -- 30 + end -- 26 + end -- 25 + self.codes[title] = luaCode -- 31 + local luaFunc -- 32 + luaFunc, err = load(luaCode, title) -- 32 + if not luaFunc then -- 33 + err = rewriteError(err, luaCode, title) -- 34 + if self.startTitle then -- 35 + return false, err -- 36 + else -- 38 + coroutine.yield("Error", err) -- 38 + return -- 39 + end -- 35 + end -- 33 + storyFunc = luaFunc() -- 40 + self.funcs[title] = storyFunc -- 41 + end -- 22 + local visitedCount -- 42 + do -- 42 + local _exp_0 = self.visited[title] -- 42 + if _exp_0 ~= nil then -- 42 + visitedCount = _exp_0 -- 42 + else -- 42 + visitedCount = 0 -- 42 + end -- 42 + end -- 42 + self.visited[title] = 1 + visitedCount -- 43 + do -- 44 + local _obj_0 = self.stories -- 44 + _obj_0[#_obj_0 + 1] = { -- 44 + title, -- 44 + coroutine.create(function() -- 44 + return storyFunc(title, self.state, self.command, self.yarn, (function() -- 45 + local _base_1 = self -- 45 + local _fn_0 = _base_1.gotoStory -- 45 + return _fn_0 and function(...) -- 45 + return _fn_0(_base_1, ...) -- 45 + end -- 45 + end)()) -- 45 + end) -- 44 + } -- 44 + end -- 46 + return true -- 47 + end, -- 90 + advance = function(self, choice) -- 90 + if self.startTitle then -- 91 + local success, err = self:gotoStory(self.startTitle) -- 92 + self.startTitle = nil -- 93 + if not success then -- 94 + return "Error", err -- 94 + end -- 94 + end -- 91 + if choice then -- 95 + if not self.option then -- 96 + return "Error", "there is no option to choose" -- 97 + end -- 96 + local title, branches -- 98 + do -- 98 + local _obj_0 = self.option -- 98 + title, branches = _obj_0.title, _obj_0.branches -- 98 + end -- 98 + if not (1 <= choice and choice <= #branches) then -- 99 + return "Error", "choice " .. tostring(choice) .. " is out of range" -- 100 + end -- 99 + local optionBranch = branches[choice] -- 101 + self.option = nil -- 102 + do -- 103 + local _obj_0 = self.stories -- 103 + _obj_0[#_obj_0 + 1] = { -- 103 + title, -- 103 + coroutine.create(optionBranch) -- 103 + } -- 103 + end -- 103 + elseif self.option then -- 104 + return "Error", "required a choice to continue" -- 105 + end -- 95 + local title -- 106 + local success, resultType, body, branches -- 107 + do -- 107 + local storyItem = self.stories[#self.stories] -- 107 + if storyItem then -- 107 + local story -- 108 + title, story = storyItem[1], storyItem[2] -- 108 + success, resultType, body, branches = coroutine.resume(story) -- 109 + end -- 107 + end -- 107 + if not success and #self.stories > 0 then -- 110 + self.stories = { } -- 111 + local err = rewriteError(resultType, self.codes[title], title) -- 112 + return "Error", err -- 113 + end -- 110 + if not resultType then -- 114 + if #self.stories > 0 then -- 115 + self.stories[#self.stories] = nil -- 116 + return self:advance() -- 117 + end -- 115 + end -- 114 + if "Dialog" == resultType then -- 119 + return "Text", body -- 120 + elseif "Option" == resultType then -- 121 + self.option = { -- 122 + title = title, -- 122 + branches = branches -- 122 + } -- 122 + return "Option", body -- 123 + elseif "Goto" == resultType then -- 124 + return self:advance() -- 125 + elseif "Command" == resultType then -- 126 + return "Command", body -- 127 + elseif "Error" == resultType or "Stop" == resultType then -- 128 + self.stories = { } -- 129 + return "Error", body -- 130 + else -- 132 + return nil, "end of the story" -- 132 + end -- 132 + end -- 19 + } -- 19 + if _base_0.__index == nil then -- 19 + _base_0.__index = _base_0 -- 19 + end -- 132 + _class_0 = setmetatable({ -- 19 + __init = function(self, filename, startTitle, state, command, testing) -- 49 + if state == nil then -- 49 + state = { } -- 49 + end -- 49 + if command == nil then -- 49 + command = { } -- 49 + end -- 49 + if testing == nil then -- 49 + testing = false -- 49 + end -- 49 + local jsonCode = Content:load(filename) -- 50 + if not jsonCode then -- 51 + error("failed to read yarn file \"" .. tostring(filename) .. "\"") -- 51 + end -- 51 + local jsonObject = json.load(jsonCode) -- 52 + if not jsonObject then -- 53 + error("failed to load yarn json code") -- 53 + end -- 53 + self.codes = { } -- 55 + self.funcs = { } -- 56 + self.state = state -- 57 + do -- 59 + local _tab_0 = { -- 59 + stop = function() -- 59 + return coroutine.yield("Stop") -- 59 + end -- 59 + } -- 60 + local _idx_0 = 1 -- 60 + for _key_0, _value_0 in pairs(command) do -- 60 + if _idx_0 == _key_0 then -- 60 + _tab_0[#_tab_0 + 1] = _value_0 -- 60 + _idx_0 = _idx_0 + 1 -- 60 + else -- 60 + _tab_0[_key_0] = _value_0 -- 60 + end -- 60 + end -- 60 + self.command = _tab_0 -- 59 + end -- 59 + setmetatable(self.command, getmetatable(command)) -- 62 + self.stories = { } -- 63 + self.visited = { } -- 64 + self.yarn = { -- 66 + dice = function(num) -- 66 + return math.random(num) -- 66 + end, -- 66 + random = function() -- 67 + return math.random() -- 67 + end, -- 67 + random_range = function(start, stop) -- 68 + return math.random(start, stop) -- 68 + end, -- 68 + visited = function(name) -- 69 + return (self.visited[name] ~= nil) -- 69 + end, -- 69 + visited_count = function(name) -- 70 + local _exp_0 = self.visited[name] -- 70 + if _exp_0 ~= nil then -- 70 + return _exp_0 -- 70 + else -- 70 + return 0 -- 70 + end -- 70 + end -- 70 + } -- 65 + self.startTitle = startTitle -- 72 + if testing then -- 73 + do -- 74 + local variables -- 74 + do -- 74 + local _obj_0 = jsonObject.header -- 74 + if _obj_0 ~= nil then -- 74 + do -- 74 + local _obj_1 = _obj_0.pluginStorage -- 74 + if _obj_1 ~= nil then -- 74 + do -- 74 + local _obj_2 = _obj_1.Runner -- 74 + if _obj_2 ~= nil then -- 74 + variables = _obj_2.variables -- 74 + end -- 74 + end -- 74 + end -- 74 + end -- 74 + end -- 74 + end -- 74 + if variables then -- 74 + for _index_0 = 1, #variables do -- 75 + local _des_0 = variables[_index_0] -- 75 + local key, value = _des_0.key, _des_0.value -- 75 + if "true" == value then -- 76 + state[key] = true -- 77 + elseif "false" == value then -- 78 + state[key] = false -- 79 + else -- 81 + do -- 81 + local num = tonumber(value) -- 81 + if num then -- 81 + state[key] = num -- 82 + else -- 84 + state[key] = value -- 84 + end -- 81 + end -- 81 + end -- 84 + end -- 84 + end -- 74 + end -- 74 + end -- 73 + do -- 85 + local nodes = jsonObject.nodes -- 85 + if nodes then -- 85 + for _index_0 = 1, #nodes do -- 86 + local node = nodes[_index_0] -- 86 + local title, body = node.title, node.body -- 87 + self.codes[title] = body -- 88 + end -- 88 + end -- 85 + end -- 85 + end, -- 19 + __base = _base_0, -- 19 + __name = "YarnRunner" -- 19 + }, { -- 19 + __index = _base_0, -- 19 + __call = function(cls, ...) -- 19 + local _self_0 = setmetatable({ }, _base_0) -- 19 + cls.__init(_self_0, ...) -- 19 + return _self_0 -- 19 + end -- 19 + }) -- 19 + _base_0.__class = _class_0 -- 19 + YarnRunner = _class_0 -- 19 + _module_0 = _class_0 -- 19 +end -- 132 +return _module_0 -- 132 diff --git a/Assets/Script/Test/BodyTL.lua b/Assets/Script/Test/BodyTL.lua new file mode 100644 index 000000000..e09392a93 --- /dev/null +++ b/Assets/Script/Test/BodyTL.lua @@ -0,0 +1,76 @@ +local Vec2 = require("Vec2") +local BodyDef = require("BodyDef") +local Body = require("Body") +local PhysicsWorld = require("PhysicsWorld") +local threadLoop = require("threadLoop") + +local gravity = Vec2(0, -10) +local groupZero = 0 +local groupOne = 1 +local groupTwo = 2 + +local terrainDef = BodyDef() +terrainDef.type = "Static" +terrainDef:attachPolygon(800, 10, 1, 0.8, 0.2) + +local polygonDef = BodyDef() +polygonDef.type = "Dynamic" +polygonDef.linearAcceleration = gravity +polygonDef:attachPolygon({ + Vec2(60, 0), + Vec2(30, -30), + Vec2(-30, -30), + Vec2(-60, 0), + Vec2(-30, 30), + Vec2(30, 30), +}, 1, 0.4, 0.4) + +local diskDef = BodyDef() +diskDef.type = "Dynamic" +diskDef.linearAcceleration = gravity +diskDef:attachDisk(60, 1, 0.4, 0.4) + +local world = PhysicsWorld() +world.y = -200 +world:setShouldContact(groupZero, groupOne, false) +world:setShouldContact(groupZero, groupTwo, true) +world:setShouldContact(groupOne, groupTwo, true) +world.showDebug = true + +local body = Body(terrainDef, world, Vec2.zero) +body.group = groupTwo +world:addChild(body) + +body = Body(polygonDef, world, Vec2(0, 500), 15) +body.group = groupOne +world:addChild(body) + +body = Body(diskDef, world, Vec2(50, 800)) +body.group = groupZero +body.angularRate = 90 +world:addChild(body) + + + +local ImGui = require("ImGui") +local App = require("App") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Body", windowFlags, function() + ImGui.Text("Body") + ImGui.Separator() + ImGui.TextWrapped("Basic usage to create physics bodies!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/CameraTL.lua b/Assets/Script/Test/CameraTL.lua new file mode 100644 index 000000000..c4c08f979 --- /dev/null +++ b/Assets/Script/Test/CameraTL.lua @@ -0,0 +1,75 @@ +local Director = require("Director") +local Node = require("Node") +local Model = require("Model") +local once = require("once") +local cycle = require("cycle") +local Sprite = require("Sprite") +local Vec2 = require("Vec2") +local Ease = require("Ease") +local Camera2D = require("Camera2D") +local threadLoop = require("threadLoop") + +local node = Node() + +local model = Model("Model/xiaoli.model") +model.look = "happy" +model:play("idle", true) +node:addChild(model) + +local sprite = Sprite("Image/logo.png") +if sprite == nil then + return +end +sprite.scaleX = 0.4 +sprite.scaleY = 0.4 +sprite.position = Vec2(200, -100) +sprite.angleY = 45 +sprite.z = -300 +node:addChild(sprite) + +node:schedule(once(function() + local camera = Director.currentCamera + cycle(1.5, function(dt) + camera.position = Vec2(200 * Ease:func(Ease.InOutQuad, dt), 0) + end) + cycle(0.1, function(dt) + camera.rotation = 25 * Ease:func(Ease.OutSine, dt) + end) + cycle(0.2, function(dt) + camera.rotation = 25 - 50 * Ease:func(Ease.InOutQuad, dt) + end) + cycle(0.1, function(dt) + camera.rotation = -25 + 25 * Ease:func(Ease.OutSine, dt) + end) + cycle(1.5, function(dt) + camera.position = Vec2(200 * Ease:func(Ease.InOutQuad, 1 - dt), 0) + end) + local zoom = camera.zoom + cycle(2.5, function(dt) + camera.zoom = zoom + Ease:func(Ease.InOutQuad, dt) + end) +end)) + + + +local App = require("App") +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Camera", windowFlags, function() + ImGui.Text("Camera") + ImGui.Separator() + ImGui.TextWrapped("View camera motions, use 3D camera as default!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/ClipNodeTL.lua b/Assets/Script/Test/ClipNodeTL.lua new file mode 100644 index 000000000..e6bd91b91 --- /dev/null +++ b/Assets/Script/Test/ClipNodeTL.lua @@ -0,0 +1,123 @@ +local Vec2 = require("Vec2") +local DrawNode = require("DrawNode") +local Model = require("Model") +local Sequence = require("Sequence") +local X = require("X") +local Event = require("Event") +local Node = require("Node") +local ClipNode = require("ClipNode") +local Line = require("Line") +local App = require("App") +local threadLoop = require("threadLoop") +local Action = require("Action") + +local function StarVertices(radius, line) + local a = math.rad(36) + local c = math.rad(72) + local f = math.sin(a) * math.tan(c) + math.cos(a) + local R = radius + local r = R / f + local vecs = {} + local count = 1 + for i = 9, line and -1 or 0, -1 do + local angle = i * a + local cr = i % 2 == 1 and r or R + vecs[count] = Vec2(cr * math.sin(angle), cr * math.cos(angle)) + count = count + 1 + end + return vecs +end + +local maskA = DrawNode() +maskA:drawPolygon(StarVertices(160, false)) + +local targetA = Model("Model/xiaoli.model") +targetA.look = "happy" +targetA.fliped = true +targetA:play("walk", true) +targetA:runAction( +Sequence( +X(1.5, -200, 200), +Event("Turn"), +X(1.5, 200, -200), +Event("Turn"))) + + +targetA:slot("ActionEnd", function(action) + targetA:runAction(action) +end) +targetA:slot("Turn", function() + targetA.fliped = not targetA.fliped +end) + +local exampleA = Node() +local clipNodeA = ClipNode(maskA) +clipNodeA:addChild(targetA) +clipNodeA.inverted = true +exampleA:addChild(clipNodeA) + +local frame = Line(StarVertices(160, true), App.themeColor) +frame.visible = false +exampleA:addChild(frame) +exampleA.visible = false + +local maskB = Model("Model/xiaoli.model") +maskB.look = "happy" +maskB.fliped = true +maskB:play("walk", true) + +local targetB = DrawNode() +targetB:drawPolygon(StarVertices(160, false)) +targetB:runAction( +Sequence( +X(1.5, -200, 200), +X(1.5, 200, -200))) + + +targetB:slot("ActionEnd", function(action) + targetB:runAction(action) +end) + +local exampleB = Node() +local clipNodeB = ClipNode(maskB) +clipNodeB:addChild(targetB) +clipNodeB.inverted = true +clipNodeB.alphaThreshold = 0.3 +exampleB:addChild(clipNodeB) + + + +local ImGui = require("ImGui") + +local inverted = true +local withAlphaThreshold = true +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Clip Node", windowFlags, function() + ImGui.Text("Clip Node") + ImGui.Separator() + ImGui.TextWrapped("Render children nodes with mask!") + local changed = false + changed, inverted = ImGui.Checkbox("Inverted", inverted) + if changed then + clipNodeA.inverted = inverted + clipNodeB.inverted = inverted + frame.visible = not inverted + end + changed, withAlphaThreshold = ImGui.Checkbox("With alphaThreshold", withAlphaThreshold) + if changed then + exampleB.visible = withAlphaThreshold + exampleA.visible = not withAlphaThreshold + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/ContactTL.lua b/Assets/Script/Test/ContactTL.lua new file mode 100644 index 000000000..1db36fafe --- /dev/null +++ b/Assets/Script/Test/ContactTL.lua @@ -0,0 +1,88 @@ +local Vec2 = require("Vec2") +local PhysicsWorld = require("PhysicsWorld") +local Label = require("Label") +local BodyDef = require("BodyDef") +local Body = require("Body") +local Line = require("Line") +local App = require("App") +local threadLoop = require("threadLoop") + +local gravity = Vec2(0, -10) + +local world = PhysicsWorld() +world:setShouldContact(0, 0, true) +world.showDebug = true + +local label = Label("sarasa-mono-sc-regular", 30) +label:addTo(world) + +local terrainDef = BodyDef() +local count = 50 +local radius = 300 +local vertices = {} +local index = 1 +for i = 1, count + 1 do + local angle = 2 * math.pi * i / count + vertices[index] = Vec2(radius * math.cos(angle), radius * math.sin(angle)) + index = index + 1 +end +terrainDef:attachChain(vertices, 0.4, 0) +terrainDef:attachDisk(Vec2(0, -270), 30, 1, 0, 1.0) +terrainDef:attachPolygon(Vec2(0, 80), 120, 30, 0, 1, 0, 1.0) + +local terrain = Body(terrainDef, world) +terrain:addTo(world) + +local drawNode = Line({ + Vec2(-20, 0), + Vec2(20, 0), + Vec2.zero, + Vec2(0, -20), + Vec2(0, 20), +}, App.themeColor) +drawNode:addTo(world) + +local diskDef = BodyDef() +diskDef.type = "Dynamic" +diskDef.linearAcceleration = gravity +diskDef:attachDisk(20, 5, 0.8, 1) + +local disk = Body(diskDef, world, Vec2(100, 200)) +disk:addTo(world) +disk.angularRate = -1800 +disk.receivingContact = true +disk:slot("ContactStart", function(_, point) + drawNode.position = point + label.text = string.format("Contact: [%.0f,%.0f]", point.x, point.y) +end) + + + +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +local receivingContact = disk.receivingContact +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Contact", windowFlags, function() + ImGui.Text("Contact") + ImGui.Separator() + ImGui.TextWrapped("Receive events when physics bodies contact.") + local changed = false + changed, receivingContact = ImGui.Checkbox("Receiving Contact", receivingContact) + if changed then + disk.receivingContact = receivingContact + label.text = "" + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/DragonBonesTL.lua b/Assets/Script/Test/DragonBonesTL.lua new file mode 100644 index 000000000..b5c82a7f2 --- /dev/null +++ b/Assets/Script/Test/DragonBonesTL.lua @@ -0,0 +1,90 @@ +local DragonBone = require("DragonBone") +local Label = require("Label") +local App = require("App") +local Sequence = require("Sequence") +local Spawn = require("Spawn") +local Scale = require("Scale") +local Ease = require("Ease") +local Delay = require("Delay") +local Opacity = require("Opacity") +local Event = require("Event") +local Vec2 = require("Vec2") +local threadLoop = require("threadLoop") + + +local boneStr = "DragonBones/NewDragon" +local animations = DragonBone:getAnimations(boneStr) +local looks = DragonBone:getLooks(boneStr) + +p(animations, looks) + +local bone = DragonBone(boneStr) +if bone == nil then + return +end +bone.look = looks[1] +bone:play(animations[1], true) +bone:slot("AnimationEnd", function(name) + print(name .. " end!") +end) + +bone.y = -200 +bone.touchEnabled = true +bone:slot("TapBegan", function(touch) + local loc = touch.location + local x, y = loc.x, loc.y + local name = bone:containsPoint(x, y) + if not (name == nil) then + local label = Label("sarasa-mono-sc-regular", 30) + label.text = name + label.color = App.themeColor + label.position = Vec2(x, y) + label.order = 100 + label:perform( + Sequence( + Spawn( + Scale(1, 0, 2, Ease.OutQuad), + Sequence( + Delay(0.5), + Opacity(0.5, 1, 0))), + + + Event("Stop"))) + + + label:slot("Stop", function() + label:removeFromParent() + end) + bone:addChild(label) + end +end) + + + +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +local showDebug = bone.showDebug +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("DragonBones", windowFlags, function() + ImGui.Text("DragonBones") + ImGui.Separator() + ImGui.TextWrapped("Basic usage to create dragonBones! Tap it for a hit test.") + local changed = false + changed, showDebug = ImGui.Checkbox("BoundingBox", showDebug) + if changed then + bone.showDebug = showDebug + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/DrawNodeTL.lua b/Assets/Script/Test/DrawNodeTL.lua new file mode 100644 index 000000000..96c3cd9fc --- /dev/null +++ b/Assets/Script/Test/DrawNodeTL.lua @@ -0,0 +1,118 @@ +local Vec2 = require("Vec2") +local threadLoop = require("threadLoop") +local Node = require("Node") +local DrawNode = require("DrawNode") +local Color = require("Color") +local App = require("App") +local Line = require("Line") + +local function CircleVertices(radius, verts) + if verts == nil then + verts = 20 + end + local function newV(index, r) + local angle = 2 * math.pi * index / verts + return Vec2(r * math.cos(angle), radius * math.sin(angle)) + Vec2(r, radius) + end + local vs = {} + local count = 1 + for index = 0, verts do + vs[count] = newV(index, radius) + count = count + 1 + end + return vs +end + +local function StarVertices(radius) + local a = math.rad(36) + local c = math.rad(72) + local f = math.sin(a) * math.tan(c) + math.cos(a) + local R = radius + local r = R / f + local vs = {} + local count = 1 + for i = 9, 0, -1 do + local angle = i * a + local cr = i % 2 == 1 and r or R + vs[count] = Vec2(cr * math.sin(angle), cr * math.cos(angle)) + count = count + 1 + end + return vs +end + +local node = Node() + +local star = DrawNode() +star.position = Vec2(200, 200) +star:drawPolygon(StarVertices(60), Color(0x80ff0080), 1, Color(0xffff0080)) +star:addTo(node) + +local themeColor = App.themeColor + +local circle = Line(CircleVertices(60), themeColor) +circle.position = Vec2(-200, 200) +circle:addTo(node) + +local camera = Node() +camera.color = themeColor +camera.scaleX = 2 +camera.scaleY = 2 +camera:addTo(node) + +local cameraFill = DrawNode() +cameraFill.opacity = 0.5 +cameraFill:drawPolygon({ + Vec2(-20, -10), + Vec2(20, -10), + Vec2(20, 10), + Vec2(-20, 10), +}) +cameraFill:drawPolygon({ + Vec2(20, 3), + Vec2(32, 10), + Vec2(32, -10), + Vec2(20, -3), +}) +cameraFill:drawDot(Vec2(-11, 20), 10) +cameraFill:drawDot(Vec2(11, 20), 10) +cameraFill:addTo(camera) + +local cameraLine = Line(CircleVertices(10)) +cameraLine.position = Vec2(-21, 10) +cameraLine:addTo(camera) + +cameraLine = Line(CircleVertices(10)) +cameraLine.position = Vec2(1, 10) +cameraLine:addTo(camera) + +cameraLine = Line({ + Vec2(20, 3), + Vec2(32, 10), + Vec2(32, -10), + Vec2(20, -3), +}) +cameraLine:addTo(camera) + + + +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Draw Node", windowFlags, function() + ImGui.Text("Draw Node") + ImGui.Separator() + ImGui.TextWrapped("Draw shapes and lines!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/Entity MoveTL.lua b/Assets/Script/Test/Entity MoveTL.lua new file mode 100644 index 000000000..18907ed05 --- /dev/null +++ b/Assets/Script/Test/Entity MoveTL.lua @@ -0,0 +1,173 @@ +local Group = require("Group") +local Observer = require("Observer") +local Scale = require("Scale") +local Ease = require("Ease") +local Roll = require("Roll") +local Entity = require("Entity") +local Sprite = require("Sprite") +local Vec2 = require("Vec2") +local Node = require("Node") +local Sequence = require("Sequence") +local Event = require("Event") + +local tolua = require("tolua") + +local sceneGroup = Group({ "scene" }) +local positionGroup = Group({ "position" }) + + +local function toNode(item) + return tolua.cast(item, "Node") +end + +Observer("Add", { "scene" }):watch(function(_entity, scene) + scene.touchEnabled = true + scene:slot("TapEnded", function(touch) + local location = touch.location + positionGroup:each(function(entity) + entity.target = location + return false + end) + end) +end) + +Observer("Add", { "image" }):watch(function(self, image) + sceneGroup:each(function(e) + local scene = toNode(e.scene) + if not (scene == nil) then + local sprite = Sprite(image) + if sprite == nil then + return false + end + sprite:addTo(scene) + sprite:runAction(Scale(0.5, 0, 0.5, Ease.OutBack)) + self.sprite = sprite + return true + end + end) +end) + +Observer("Remove", { "sprite" }):watch(function(self) + local sprite = toNode(self.oldValues.sprite) + if not (sprite == nil) then + sprite:removeFromParent() + end +end) + +Observer("Remove", { "target" }):watch(function(self) + print("remove target from entity " .. tostring(self.index)) +end) + +Group({ "position", "direction", "speed", "target" }):watch( +function(self, position, _direction, speed, target) + if target == position then + return + end + local dir = target - position + dir = dir:normalize() + local angle = math.deg(math.atan(dir.x, dir.y)) + local newPos = position + dir * speed + newPos = newPos:clamp(position, target) + self.position = newPos + self.direction = angle + if newPos == target then + self.target = nil + end +end) + +Observer("AddOrChange", { "position", "direction", "sprite" }):watch( +function(self, position, direction, sprite) + sprite.position = position + local lastDirection = self.oldValues.direction or sprite.angle + if type(lastDirection) == "number" then + if math.abs(direction - lastDirection) > 1 then + sprite:runAction(Roll(0.3, lastDirection, direction)) + end + end +end) + + + + + + + + +Entity({ scene = Node() }) + +do + local def = { + image = "Image/logo.png", + position = Vec2.zero, + direction = 45.0, + speed = 4.0, + } + Entity(def) +end + +do + local def = { + image = "Image/logo.png", + position = Vec2(-100, 200), + direction = 90.0, + speed = 10.0, + } + Entity(def) +end + + + +local App = require("App") +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +Observer("Add", { "scene" }):watch(function(entity) + local scene = toNode(entity.scene) + if not (scene == nil) then + scene:schedule(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("ECS System", windowFlags, function() + ImGui.Text("ECS System") + ImGui.Separator() + ImGui.TextWrapped("Tap any place to move entities.") + if ImGui.Button("Create Random Entity") then + local def = { + image = "Image/logo.png", + position = Vec2(6 * math.random(1, 100), 6 * math.random(1, 100)), + direction = 1.0 * math.random(0, 360), + speed = 1.0 * math.random(1, 20), + } + Entity(def) + end + if ImGui.Button("Destroy An Entity") then + Group({ "sprite", "position" }):each(function(e) + e.position = nil + local sprite = toNode(e.sprite) + if not (sprite == nil) then + sprite:runAction( + Sequence( + Scale(0.5, 0.5, 0, Ease.InBack), + Event("Destroy"))) + + + sprite:slot("Destroy", function() + e:destroy() + end) + end + return true + end) + end + end) + end) + end +end) \ No newline at end of file diff --git a/Assets/Script/Test/EntitySystem.lua b/Assets/Script/Test/EntitySystem.lua new file mode 100644 index 000000000..189923c0a --- /dev/null +++ b/Assets/Script/Test/EntitySystem.lua @@ -0,0 +1,91 @@ +-- [yue]: Script/Test/EntitySystem.yue +local Group = dora.Group -- 1 +local Observer = dora.Observer -- 1 +local Entity = dora.Entity -- 1 +local print = _G.print -- 1 +local assert = _G.assert -- 1 +local tostring = _G.tostring -- 1 +local hpGroup = Group({ -- 3 + "hp" -- 3 +}) -- 3 +local spGroup = Group({ -- 4 + "sp" -- 4 +}) -- 4 +local observer = Observer("Change", { -- 6 + "hp", -- 6 + "mp" -- 6 +}) -- 6 +local observer1 = Observer("Remove", { -- 7 + "hp", -- 7 + "sp" -- 7 +}) -- 7 +Entity({ -- 10 + hp = 100, -- 10 + mp = 998, -- 11 + id = 0 -- 12 +}) -- 9 +local entity0 = hpGroup:find(function(self) -- 14 + return self.id == 0 -- 14 +end) -- 14 +Entity({ -- 17 + hp = 119, -- 17 + sp = 233, -- 18 + id = 1 -- 19 +}) -- 16 +local entity1 = hpGroup:find(function(self) -- 21 + return self.id == 1 -- 21 +end) -- 21 +print("-- {hp} group") -- 23 +hpGroup:each(function(self) -- 24 + return print("entity", self.id) -- 25 +end) -- 24 +print("-- {sp} group") -- 27 +spGroup:each(function(self) -- 28 + return print("entity", self.id) -- 29 +end) -- 28 +print("-- {hp mp} observer") -- 31 +entity0.hp = entity0.hp + 20 -- 32 +entity0.hp = entity0.hp - 34 -- 33 +entity1.hp = entity1.hp - 1 -- 35 +entity1.hp = entity1.hp - 99 -- 36 +observer:watch(function(self, hp, mp) -- 39 + do -- 40 + local oldHP = self.oldValues.hp -- 40 + if oldHP then -- 40 + assert(self.oldValues.hp == 100) -- 41 + assert(hp == 86) -- 42 + print("hp change [from " .. tostring(oldHP) .. " to " .. tostring(hp) .. "]: entity " .. tostring(self.id)) -- 43 + end -- 40 + end -- 40 + do -- 44 + local oldMP = self.oldValues.mp -- 44 + if oldMP then -- 44 + return print("mp change [from " .. tostring(oldMP) .. " to " .. tostring(mp) .. "]: entity " .. tostring(self.id)) -- 45 + end -- 44 + end -- 44 +end) -- 38 +observer1:watch(function(self, hp, sp) -- 48 + if hp == nil then -- 49 + assert(self.oldValues.hp == 119) -- 50 + print("hp removed from entity " .. tostring(self.id) .. ", old value: " .. tostring(self.oldValues.hp)) -- 51 + end -- 49 + if sp == nil then -- 52 + return print("sp removed from entity " .. tostring(self.id) .. ", old value: " .. tostring(self.oldValues.sp)) -- 53 + end -- 52 +end) -- 47 +print("-- {hp} group") -- 55 +hpGroup:each(function(self) -- 56 + return print("entity", self.id, self.hp) -- 57 +end) -- 56 +print("remove hp from entity", entity1.id) -- 59 +entity1.hp = nil -- 60 +print("-- {hp} group") -- 62 +hpGroup:each(function(self) -- 63 + return print("entity", self.index, self.hp) -- 64 +end) -- 63 +assert(hpGroup.count == 1) -- 66 +print("-- {sp} group") -- 68 +spGroup:each(function(self) -- 69 + return print("entity", self.index, self.sp) -- 70 +end) -- 69 +return assert(spGroup.count == 1) -- 72 diff --git a/Assets/Script/Test/ExcelTest.lua b/Assets/Script/Test/ExcelTest.lua new file mode 100644 index 000000000..a8d7babbb --- /dev/null +++ b/Assets/Script/Test/ExcelTest.lua @@ -0,0 +1,545 @@ +local Platformer = require("Platformer") +local Data = Platformer.Data + +local Rectangle = require("UI.View.Shape.Rectangle") +local Vec2 = require("Vec2") +local Rect = require("Rect") +local BodyDef = require("BodyDef") +local Body = require("Body") +local Director = require("Director") +local App = require("App") +local Color = require("Color") +local View = require("View") + +local TerrainLayer = 0 +local PlayerLayer = 1 +local ItemLayer = 2 + +local PlayerGroup = Data.groupFirstPlayer +local ItemGroup = Data.groupFirstPlayer + 1 +local TerrainGroup = Data.groupTerrain + +Data:setShouldContact(PlayerGroup, ItemGroup, true) + +local themeColor = App.themeColor +local fillColor = Color(themeColor:toColor3(), 0x66):toARGB() +local borderColor = themeColor:toARGB() +local DesignWidth = 1500 + +local world = Platformer.PlatformWorld() +world.camera.boundary = Rect(-1250, -500, 2500, 1000) +world.camera.followRatio = Vec2(0.02, 0.02) +world.camera.zoom = View.size.width / DesignWidth +world:gslot("AppSizeChanged", function() + world.camera.zoom = View.size.width / DesignWidth +end) + +local terrainDef = BodyDef() +terrainDef.type = "Static" +terrainDef:attachPolygon(Vec2(0, -500), 2500, 10, 0, 1, 1, 0) +terrainDef:attachPolygon(Vec2(0, 500), 2500, 10, 0, 1, 1, 0) +terrainDef:attachPolygon(Vec2(1250, 0), 10, 1000, 0, 1, 1, 0) +terrainDef:attachPolygon(Vec2(-1250, 0), 10, 1000, 0, 1, 1, 0) + +local terrain = Body(terrainDef, world, Vec2.zero) +terrain.order = TerrainLayer +terrain.group = TerrainGroup +terrain:addChild(Rectangle({ + y = -500, + width = 2500, + height = 10, + fillColor = fillColor, + borderColor = borderColor, + fillOrder = 1, + lineOrder = 2, +})) +terrain:addChild(Rectangle({ + x = 1250, + y = 0, + width = 10, + height = 1000, + fillColor = fillColor, + borderColor = borderColor, + fillOrder = 1, + lineOrder = 2, +})) +terrain:addChild(Rectangle({ + x = -1250, + y = 0, + width = 10, + height = 1000, + fillColor = fillColor, + borderColor = borderColor, + fillOrder = 1, + lineOrder = 2, +})) +world:addChild(terrain) + +local once = require("once") +local loop = require("loop") +local sleep = require("sleep") + +local UnitAction = Platformer.UnitAction + + +UnitAction:add("idle", { + priority = 1, + reaction = 2.0, + recovery = 0.2, + available = function(self) + return self.onSurface + end, + create = function(self) + + + + local playable = self.playable + playable.speed = 1.0 + playable:play("idle", true) + local playIdleSpecial = loop(function() + sleep(3) + sleep(playable:play("idle1")) + playable:play("idle", true) + end) + self.data.playIdleSpecial = playIdleSpecial + return function(owner) + coroutine.resume(playIdleSpecial) + return not owner.onSurface + end + end, +}) + +UnitAction:add("move", { + priority = 1, + reaction = 2.0, + recovery = 0.2, + available = function(self) + return self.onSurface + end, + create = function(self) + + + + local playable = self.playable + playable.speed = 1 + playable:play("fmove", true) + return function(self, action) + local elapsedTime = action.elapsedTime + local recovery = action.recovery * 2 + local move = self.unitDef.move + local moveSpeed = 1.0 + if elapsedTime < recovery then + moveSpeed = math.min(elapsedTime / recovery, 1.0) + end + self.velocityX = moveSpeed * (self.faceRight and move or -move) + return not self.onSurface + end + end, +}) + +UnitAction:add("jump", { + priority = 3, + reaction = 2.0, + recovery = 0.1, + queued = true, + available = function(self) + return self.onSurface + end, + create = function(self) + + + + local jump = self.unitDef.jump + self.velocityY = jump + return once(function() + local playable = self.playable + playable.speed = 1 + sleep(playable:play("jump", false)) + end) + end, +}) + +UnitAction:add("fallOff", { + priority = 2, + reaction = -1, + recovery = 0.3, + available = function(self) + return not self.onSurface + end, + create = function(self) + + + + if self.playable.current ~= "jumping" then + local playable = self.playable + playable.speed = 1 + playable:play("jumping", true) + end + return loop(function(self) + if self.onSurface then + local playable = self.playable + playable.speed = 1 + sleep(playable:play("landing", false)) + return true + else + return false + end + end) + end, +}) + +local Decision = Platformer.Decision +local Sel = Decision.Sel +local Seq = Decision.Seq +local Con = Decision.Con +local Act = Decision.Act + +Data.store["AI:playerControl"] = Sel({ + Seq({ + Con("fmove key down", function(self) + return not (self.entity.keyLeft and self.entity.keyRight) and + ( + (self.entity.keyLeft and self.faceRight) or + (self.entity.keyRight and not self.faceRight)) + + end), + Act("turn"), + }), + Seq({ + Con("is falling", function(self) + return not self.onSurface + end), + Act("fallOff"), + }), + Seq({ + Con("jump key down", function(self) + return self.entity.keyJump + end), + Act("jump"), + }), + Seq({ + Con("fmove key down", function(self) + return (self.entity.keyLeft or self.entity.keyRight) + end), + Act("move"), + }), + Act("idle"), +}) + +local Dictionary = require("Dictionary") +local Size = require("Size") +local Array = require("Array") + +local unitDef = Dictionary() +unitDef.linearAcceleration = Vec2(0, -15) +unitDef.bodyType = "Dynamic" +unitDef.scale = 1.0 +unitDef.density = 1.0 +unitDef.friction = 1.0 +unitDef.restitution = 0.0 +unitDef.playable = "spine:Spine/moling" +unitDef.defaultFaceRight = true +unitDef.size = Size(60, 300) +unitDef.sensity = 0 +unitDef.move = 300 +unitDef.jump = 1000 +unitDef.detectDistance = 350 +unitDef.hp = 5.0 +unitDef.tag = "player" +unitDef.decisionTree = "AI:playerControl" +unitDef.usePreciseHit = false +unitDef.actions = Array({ + "idle", + "turn", + "move", + "jump", + "fallOff", + "cancel", +}) + +local Observer = require("Observer") +local Sprite = require("Sprite") +local Spawn = require("Spawn") +local AngleY = require("AngleY") +local Sequence = require("Sequence") +local Y = require("Y") +local Scale = require("Scale") +local Opacity = require("Opacity") +local Ease = require("Ease") +local tolua = require("tolua") +local Unit = Platformer.Unit +local Entity = require("Entity") + +Observer("Add", { "player" }):watch(function(self) + local unit = Unit(unitDef, world, self, Vec2(300, -350)) + unit.order = PlayerLayer + unit.group = PlayerGroup + unit.playable.position = Vec2(0, -150) + unit.playable:play("idle", true) + world:addChild(unit) + world.camera.followTarget = unit +end) + +Observer("Add", { "x", "icon" }):watch(function(self, x, icon) + local sprite = Sprite(icon) + if sprite == nil then + return + end + + sprite:schedule(loop(function() + sleep(sprite:runAction(Spawn( + AngleY(5, 0, 360), + Sequence( + Y(2.5, 0, 40, Ease.OutQuad), + Y(2.5, 40, 0, Ease.InQuad))))) + + + end)) + + local bodyDef = BodyDef() + bodyDef.type = "Dynamic" + bodyDef.linearAcceleration = Vec2(0, -10) + bodyDef:attachPolygon(sprite.width * 0.5, sprite.height) + bodyDef:attachPolygonSensor(0, sprite.width, sprite.height) + + local body = Body(bodyDef, world, Vec2(x, 0)) + body.order = ItemLayer + body.group = ItemGroup + body:addChild(sprite) + + body:slot("BodyEnter", function(item) + if tolua.type(item) == "Platformer::Unit" then + self.picked = true + body.group = Data.groupHide + body:schedule(once(function() + sleep(sprite:runAction(Spawn( + Scale(0.2, 1, 1.3, Ease.OutBack), + Opacity(0.2, 1, 0)))) + + self.body = nil + end)) + end + end) + + world:addChild(body) + self.body = body +end) + +Observer("Remove", { "body" }):watch(function(self) + (self.oldValues.body):removeFromParent() +end) + +local Content = require("Content") +local Group = require("Group") +local Utils = require("Utils") +local Struct = Utils.Struct + + + + + + + + + + + +local function loadExcel() + local xlsx = Content:loadExcel("Data/items.xlsx", { "items" }) + if not (xlsx == nil) then + local its = xlsx["items"] + local names = its[2] + table.remove(names, 1) + if not Struct:has("Item") then + Struct.Item(names) + end + Group({ "item" }):each(function(e) + e:destroy() + end) + for i = 3, #its do + local st = Struct:load(its[i]) + local item = { + name = st.Name, + no = st.No, + x = st.X, + num = st.Num, + icon = st.Icon, + desc = st.Desc, + item = true, + } + Entity(item) + end + end +end + +local ImGui = require("ImGui") +local AlignNode = require("UI.Control.Basic.AlignNode") +local CircleButton = require("UI.Control.Basic.CircleButton") +local Menu = require("Menu") +local Keyboard = require("Keyboard") + +local keyboardEnabled = true + +local playerGroup = Group({ "player" }) +local function updatePlayerControl(key, flag, vpad) + if keyboardEnabled and vpad then + keyboardEnabled = false + end + playerGroup:each(function(self) + self[key] = flag + end) +end + +local uiScale = App.devicePixelRatio +local alignNode = AlignNode({ + isRoot = true, + inUI = true, +}) +Director.ui:addChild(alignNode) + +local leftAlign = AlignNode({ + hAlign = "Left", + vAlign = "Bottom", +}) +alignNode:addChild(leftAlign) + +local leftMenu = Menu() +leftAlign:addChild(leftMenu) + +local leftButton = CircleButton({ + text = "左(a)", + x = 20 * uiScale, + y = 60 * uiScale, + radius = 30 * uiScale, + fontSize = math.floor(18 * uiScale), +}) +leftButton.anchor = Vec2.zero +leftButton:slot("TapBegan", function() + updatePlayerControl("keyLeft", true, true) +end) +leftButton:slot("TapEnded", function() + updatePlayerControl("keyLeft", false, true) +end) +leftMenu:addChild(leftButton) + +local rightButton = CircleButton({ + text = "右(d)", + x = 90 * uiScale, + y = 60 * uiScale, + radius = 30 * uiScale, + fontSize = math.floor(18 * uiScale), +}) +rightButton.anchor = Vec2.zero +rightButton:slot("TapBegan", function() + updatePlayerControl("keyRight", true, true) +end) +rightButton:slot("TapEnded", function() + updatePlayerControl("keyRight", false, true) +end) +leftMenu:addChild(rightButton) + +local rightAlign = AlignNode({ + hAlign = "Right", + vAlign = "Bottom", +}) +alignNode:addChild(rightAlign) + +local rightMenu = Menu() +rightAlign:addChild(rightMenu) + +local jumpButton = CircleButton({ + text = "跳(j)", + x = -80 * uiScale, + y = 60 * uiScale, + radius = 30 * uiScale, + fontSize = math.floor(18 * uiScale), +}) +jumpButton.anchor = Vec2.zero +jumpButton:slot("TapBegan", function() + updatePlayerControl("keyJump", true, true) +end) +jumpButton:slot("TapEnded", function() + updatePlayerControl("keyJump", false, true) +end) +rightMenu:addChild(jumpButton) + +alignNode:alignLayout() + +alignNode:schedule(function() + local keyA = Keyboard:isKeyPressed("A") + local keyD = Keyboard:isKeyPressed("D") + local keyJ = Keyboard:isKeyPressed("J") + if keyD or keyD or keyJ then + keyboardEnabled = true + end + if not keyboardEnabled then + return false + end + updatePlayerControl("keyLeft", keyA, false) + updatePlayerControl("keyRight", keyD, false) + updatePlayerControl("keyJump", keyJ, false) + return false +end) + +local pickedItemGroup = Group({ "picked" }) +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +Director.ui:schedule(function() + local size = App.visualSize + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(size.width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(100, 300), "FirstUseEver") + ImGui.Begin("BackPack", windowFlags, function() + if ImGui.Button("重新加载Excel") then + loadExcel() + end + ImGui.Separator() + ImGui.Dummy(Vec2(100, 10)) + ImGui.Text("背包") + ImGui.Separator() + ImGui.Columns(3, false) + pickedItemGroup:each(function(e) + local item = e + if item.num > 0 then + if ImGui.ImageButton("item" .. tostring(item.no), item.icon, Vec2(50, 50)) then + item.num = item.num - 1 + local sprite = Sprite(item.icon) + if sprite == nil then + return + end + sprite.scaleX = 0.5 + sprite.scaleY = 0.5 + sprite:perform(Spawn( + Opacity(1, 1, 0), + Y(1, 150, 250))) + + local player = playerGroup:find(function() return true end) + local unit = player.unit + unit:addChild(sprite) + end + if ImGui.IsItemHovered() then + ImGui.BeginTooltip(function() + ImGui.Text(item.name) + ImGui.TextColored(themeColor, "数量:") + ImGui.SameLine() + ImGui.Text(tostring(item.num)) + ImGui.TextColored(themeColor, "描述:") + ImGui.SameLine() + ImGui.Text(tostring(item.desc)) + end) + end + ImGui.NextColumn() + end + end) + end) + return false +end) + +Entity({ player = true }) +loadExcel() \ No newline at end of file diff --git a/Assets/Script/Test/FixedLabel.lua b/Assets/Script/Test/FixedLabel.lua new file mode 100644 index 000000000..766d99639 --- /dev/null +++ b/Assets/Script/Test/FixedLabel.lua @@ -0,0 +1,43 @@ +-- [yue]: Script/Test/FixedLabel.yue +local once = dora.once -- 1 +local sleep = dora.sleep -- 1 +local Node = dora.Node -- 1 +local LineRect = require("UI.View.Shape.LineRect") -- 2 +local FixedLabel = require("UI.Control.Basic.FixedLabel") -- 3 +local utf8 = require("utf-8") -- 4 +local createLabel -- 6 +createLabel = function(textAlign) -- 6 + local _with_0 = FixedLabel({ -- 7 + text = "", -- 7 + width = 100, -- 7 + height = 30, -- 7 + textAlign = textAlign -- 7 + }) -- 7 + _with_0:addChild(LineRect({ -- 8 + width = 100, -- 8 + height = 30, -- 8 + color = 0xffff0080 -- 8 + })) -- 8 + local text = "1.23456壹贰叁肆伍陆柒玐玖" -- 9 + local textLen = utf8.len(text) -- 10 + _with_0:schedule(once(function() -- 11 + for i = 1, textLen do -- 12 + _with_0.text = utf8.sub(text, 1, i) -- 13 + sleep(0.3) -- 14 + end -- 14 + end)) -- 11 + return _with_0 -- 7 +end -- 6 +local _with_0 = Node() -- 16 +_with_0:addChild(createLabel("Center")) -- 17 +_with_0:addChild((function() -- 18 + local _with_1 = createLabel("Left") -- 18 + _with_1.y = 40 -- 19 + return _with_1 -- 18 +end)()) -- 18 +_with_0:addChild((function() -- 20 + local _with_1 = createLabel("Right") -- 20 + _with_1.y = -40 -- 21 + return _with_1 -- 20 +end)()) -- 20 +return _with_0 -- 16 diff --git a/Assets/Script/Test/GestureTL.lua b/Assets/Script/Test/GestureTL.lua new file mode 100644 index 000000000..3ef1038fb --- /dev/null +++ b/Assets/Script/Test/GestureTL.lua @@ -0,0 +1,49 @@ +local nvg = require("nvg") +local Sprite = require("Sprite") +local Vec2 = require("Vec2") +local View = require("View") +local threadLoop = require("threadLoop") +local Node = require("Node") + +local texture = nvg.GetDoraSSR() +local sprite = Sprite(texture) +local length = Vec2(View.size).length +local width, height = sprite.width, sprite.height +local size = Vec2(width, height).length +local scaledSize = size + +local node = Node() +node:addChild(sprite) +node.touchEnabled = true +node:slot("Gesture", function(center, _numFingers, deltaDist, deltaAngle) + sprite.position = center + sprite.angle = sprite.angle + deltaAngle + scaledSize = scaledSize + (deltaDist * length) + sprite.scaleX = scaledSize / size + sprite.scaleY = scaledSize / size +end) + + + +local App = require("App") +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local w = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(w - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Gesture", windowFlags, function() + ImGui.Text("Gesture") + ImGui.Separator() + ImGui.TextWrapped("Interact with multi-touches!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/Hello WorldTL.lua b/Assets/Script/Test/Hello WorldTL.lua new file mode 100644 index 000000000..c98ba0856 --- /dev/null +++ b/Assets/Script/Test/Hello WorldTL.lua @@ -0,0 +1,48 @@ +local threadLoop = require("threadLoop") +local Node = require("Node") +local once = require("once") +local sleep = require("sleep") + +local node = Node() +node:slot("Enter", function() + print("on enter event") +end) +node:slot("Exit", function() + print("on exit event") +end) +node:slot("Cleanup", function() + print("on node destoyed event") +end) +node:schedule(once(function() + for i = 5, 1, -1 do + print(i) + sleep(1) + end + print("Hello World!") +end)) + + + +local ImGui = require("ImGui") +local Vec2 = require("Vec2") +local App = require("App") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local size = App.visualSize + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(size.width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Hello World", windowFlags, function() + ImGui.Text("Hello World") + ImGui.Separator() + ImGui.TextWrapped("Basic Dora schedule and signal function usage. Written in Teal. View outputs in log window!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/ImGui.lua b/Assets/Script/Test/ImGui.lua new file mode 100644 index 000000000..09d604c18 --- /dev/null +++ b/Assets/Script/Test/ImGui.lua @@ -0,0 +1,4 @@ +-- [yue]: Script/Test/ImGui.yue +local threadLoop = dora.threadLoop -- 1 +local ImGui = dora.ImGui -- 1 +return threadLoop(ImGui.ShowDemoWindow) -- 3 diff --git a/Assets/Script/Test/LabelTL.lua b/Assets/Script/Test/LabelTL.lua new file mode 100644 index 000000000..cbb24ccef --- /dev/null +++ b/Assets/Script/Test/LabelTL.lua @@ -0,0 +1,65 @@ +local threadLoop = require("threadLoop") +local Node = require("Node") +local Label = require("Label") +local Sequence = require("Sequence") +local Delay = require("Delay") +local Scale = require("Scale") +local App = require("App") +local Vec2 = require("Vec2") +local Opacity = require("Opacity") + +local node = Node() + +local label = Label("sarasa-mono-sc-regular", 40) +label.batched = false +label.text = "你好,Dora SSR!" +label:addTo(node) +for i = 1, label.characterCount do + local char = label:getCharacter(i) + if not (char == nil) then + char:runAction( + Sequence( + Delay(i / 5), + Scale(0.2, 1, 2), + Scale(0.2, 2, 1))) + + + end +end + +label = Label("sarasa-mono-sc-regular", 30) +label.text = "-- from Jin." +label.color = App.themeColor +label.opacity = 0 +label.position = Vec2(120, -70) +label:runAction( +Sequence( +Delay(2), +Opacity(0.2, 0, 1))) + + +label:addTo(node) + + + +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Label", windowFlags, function() + ImGui.Text("Label") + ImGui.Separator() + ImGui.TextWrapped("Render labels with unbatched and batched methods!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/ModelTL.lua b/Assets/Script/Test/ModelTL.lua new file mode 100644 index 000000000..b277121a8 --- /dev/null +++ b/Assets/Script/Test/ModelTL.lua @@ -0,0 +1,70 @@ +local Model = require("Model") +local threadLoop = require("threadLoop") + +local modelFile = "Model/xiaoli.model" +local model = Model(modelFile) +model.recovery = 0.2 +model.look = "happy" +model:play("walk", true) +model:slot("AnimationEnd", function(name) + print(name, "end") +end) + + + +local App = require("App") +local ImGui = require("ImGui") +local Vec2 = require("Vec2") + +local looks = Model:getLooks(modelFile) +if #looks == 0 then + looks[#looks + 1] = "" +end +local animations = Model:getAnimations(modelFile) +if #animations == 0 then + animations[#animations + 1] = "" +end +local currentLook = #looks +local currentAnim = #animations +local loop = true +local windowFlags = { + "NoResize", + "NoSavedSettings", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowPos(Vec2(width - 250, 10), "FirstUseEver") + ImGui.SetNextWindowSize(Vec2(240, 325), "FirstUseEver") + ImGui.Begin("Model", windowFlags, function() + local changed = false + changed, currentLook = ImGui.Combo("Look", currentLook, looks) + if changed then + model.look = looks[currentLook] + end + changed, currentAnim = ImGui.Combo("Anim", currentAnim, animations) + if changed then + model:play(animations[currentAnim], loop) + end + changed, loop = ImGui.Checkbox("Loop", loop) + if changed then + model:play(animations[currentAnim], loop) + end + ImGui.SameLine() + changed, model.reversed = ImGui.Checkbox("Reversed", model.reversed) + if changed then + model:play(animations[currentAnim], loop) + end + ImGui.PushItemWidth(-70, function() + local _ + _, model.speed = ImGui.DragFloat("Speed", model.speed, 0.01, 0, 10, "%.2f") + _, model.recovery = ImGui.DragFloat("Recovery", model.recovery, 0.01, 0, 10, "%.2f") + end) + local scale = model.scaleX + local _ + _, scale = ImGui.DragFloat("Scale", scale, 0.01, 0.5, 2, "%.2f") + model.scaleX, model.scaleY = scale, scale + if ImGui.Button("Play", Vec2(140, 30)) then + model:play(animations[currentAnim], loop) + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/NestedClipping.lua b/Assets/Script/Test/NestedClipping.lua new file mode 100644 index 000000000..e511cbf56 --- /dev/null +++ b/Assets/Script/Test/NestedClipping.lua @@ -0,0 +1,17 @@ +-- [xml]: Script/Test/NestedClipping.xml +return function(args) -- 1 +local _ENV = Dora(args) -- 1 +local clipNode1 = ClipNode() -- 2 +local sprite1 = Sprite("Image/logo.png") -- 4 +clipNode1.stencil = sprite1 -- 4 +local clipNode2 = ClipNode() -- 7 +clipNode2.x = 400 -- 7 +clipNode1:addChild(clipNode2) -- 7 +local sprite2 = Sprite("Image/logo.png") -- 9 +sprite2.scaleX = 0.5 -- 9 +sprite2.scaleY = 0.5 -- 9 +clipNode2.stencil = sprite2 -- 9 +local sprite3 = Sprite("Image/logo.png") -- 11 +clipNode2:addChild(sprite3) -- 11 +return clipNode1 -- 11 +end \ No newline at end of file diff --git a/Assets/Script/Test/RenderGroupTL.lua b/Assets/Script/Test/RenderGroupTL.lua new file mode 100644 index 000000000..5695693b4 --- /dev/null +++ b/Assets/Script/Test/RenderGroupTL.lua @@ -0,0 +1,93 @@ +local Node = require("Node") +local Vec2 = require("Vec2") +local Sprite = require("Sprite") +local DrawNode = require("DrawNode") +local Color = require("Color") +local App = require("App") +local Line = require("Line") +local Angle = require("Angle") +local Size = require("Size") +local threadLoop = require("threadLoop") +local Action = require("Action") + +local function Item() + local node = Node() + node.width = 144 + node.height = 144 + node.anchor = Vec2.zero + + local sprite = Sprite("Image/logo.png") + if sprite == nil then + return node + end + sprite.scaleX = 0.1 + sprite.scaleY = 0.1 + sprite.renderOrder = 1 + sprite:addTo(node) + + local drawNode = DrawNode() + drawNode:drawPolygon({ + Vec2(-60, -60), + Vec2(60, -60), + Vec2(60, 60), + Vec2(-60, 60), + }, Color(App.themeColor:toColor3(), 0x30)) + drawNode.renderOrder = 2 + drawNode.angle = 45 + drawNode:addTo(node) + + local line = Line({ + Vec2(-60, -60), + Vec2(60, -60), + Vec2(60, 60), + Vec2(-60, 60), + Vec2(-60, -60), + }, Color(0xffff0080)) + line.renderOrder = 3 + line.angle = 45 + line:addTo(node) + + node:runAction(Angle(5, 0, 360)) + node:slot("ActionEnd", function(action) + node:runAction(action) + end) + return node +end + +local currentEntry = Node() +currentEntry.renderGroup = true +currentEntry.size = Size(750, 750) +for _i = 1, 16 do + currentEntry:addChild(Item()) +end +currentEntry:alignItems() + + + +local ImGui = require("ImGui") + +local renderGroup = currentEntry.renderGroup +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Render Group", windowFlags, function() + ImGui.Text("Render Group") + ImGui.Separator() + ImGui.TextWrapped("When render group is enabled, the nodes in the sub render tree will be grouped by \"renderOrder\" property, and get rendered in ascending order!\nNotice the draw call changes in stats window.") + local changed = true + changed, renderGroup = ImGui.Checkbox("Grouped", renderGroup) + if changed then + currentEntry.renderGroup = renderGroup + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/RenderTargetTL.lua b/Assets/Script/Test/RenderTargetTL.lua new file mode 100644 index 000000000..56abdab2c --- /dev/null +++ b/Assets/Script/Test/RenderTargetTL.lua @@ -0,0 +1,88 @@ +local Node = require("Node") +local Spine = require("Spine") +local Sequence = require("Sequence") +local X = require("X") +local Event = require("Event") +local RenderTarget = require("RenderTarget") +local Color = require("Color") +local App = require("App") +local Sprite = require("Sprite") +local Line = require("Line") +local Vec2 = require("Vec2") +local threadLoop = require("threadLoop") +local Action = require("Action") + +local root = Node() + +local node = Node() +node:addTo(root, 1) + +local spine = Spine("Spine/moling") +if spine == nil then + return +end +spine.y = -200 +spine.scaleX = 1.2 +spine.scaleY = 1.2 +spine.fliped = false +spine:play("fmove", true) +spine:runAction( +Sequence( +X(2, -150, 250), +Event("Turn"), +X(2, 250, -150), +Event("Turn"))) + + +spine:slot("ActionEnd", function(action) + spine:runAction(action) +end) +spine:slot("Turn", function() + spine.fliped = not spine.fliped +end) +spine:addTo(node) + +local renderTarget = RenderTarget(300, 400) +renderTarget:renderWithClear(Color(0xff8a8a8a)) + +local surface = Sprite(renderTarget.texture) +surface.z = 300 +surface.angleY = 25 +surface:addChild(Line({ + Vec2.zero, + Vec2(300, 0), + Vec2(300, 400), + Vec2(0, 400), + Vec2.zero, +}, App.themeColor)) +surface:schedule(function() + node.y = 200 + renderTarget:renderWithClear(node, Color(0xff8a8a8a)) + node.y = 0 + return false +end) +surface:addTo(root) + + + +local ImGui = require("ImGui") + +local windowFlags = { + "NoDecoration", + "AlwaysAutoResize", + "NoSavedSettings", + "NoFocusOnAppearing", + "NoNav", + "NoMove", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowBgAlpha(0.35) + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 0), "FirstUseEver") + ImGui.Begin("Render Target", windowFlags, function() + ImGui.Text("Render Target") + ImGui.Separator() + ImGui.TextWrapped("Use render target node as a mirror!") + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/Ruler.lua b/Assets/Script/Test/Ruler.lua new file mode 100644 index 000000000..0e1f00f03 --- /dev/null +++ b/Assets/Script/Test/Ruler.lua @@ -0,0 +1,30 @@ +-- [yue]: Script/Test/Ruler.yue +local print = _G.print -- 1 +local Ruler = require("UI.Control.Basic.Ruler") -- 2 +local CircleButton = require("UI.Control.Basic.CircleButton") -- 3 +local ruler = Ruler({ -- 6 + x = 0, -- 6 + y = 0, -- 7 + width = 600, -- 8 + height = 150, -- 9 + fontName = "sarasa-mono-sc-regular", -- 10 + fontSize = 30 -- 11 +}) -- 5 +local _with_0 = CircleButton({ -- 15 + text = "显示", -- 15 + y = -200, -- 16 + radius = 60, -- 17 + fontSize = 40 -- 18 +}) -- 14 +_with_0:slot("Tapped", function() -- 20 + if _with_0.text == "显示" then -- 21 + _with_0.text = "隐藏" -- 22 + return ruler:show(0, 0, 100, 10, function(value) -- 23 + return print(value) -- 24 + end) -- 24 + else -- 26 + _with_0.text = "显示" -- 26 + return ruler:hide() -- 27 + end -- 21 +end) -- 20 +return _with_0 -- 14 diff --git a/Assets/Script/Test/SQLiteTL.lua b/Assets/Script/Test/SQLiteTL.lua new file mode 100644 index 000000000..0c1bc529d --- /dev/null +++ b/Assets/Script/Test/SQLiteTL.lua @@ -0,0 +1,47 @@ +local DB = require("DB") +local thread = require("thread") + +local sqls = { + "DROP TABLE IF EXISTS test", + "CREATE TABLE test (id INTEGER PRIMARY KEY, value TEXT)", + { + "INSERT INTO test VALUES(?, ?)", + { + { false, "hello" }, + { false, "world" }, + { false, "ok" }, + }, + }, +} + +local result = DB:transaction(sqls) +print(result and "Success" or "Failure") +print(DB:exist("test")) + +p(DB:query("SELECT * FROM test", true)) + +print("row changed:", DB:exec("DELETE FROM test WHERE id > 1")) +print("row changed:", DB:exec("UPDATE test SET value = ? WHERE id = 1", { "hello world!" })) + +thread(function() + print("insert async") + local data = {} + local count = 1 + for k in pairs(_G) do + data[count] = { false, k } + count = count + 1 + end + p(DB:insertAsync("test", data)) + print("query async...") + local items = DB:queryAsync("SELECT value FROM test WHERE value NOT LIKE 'hello%' ORDER BY value ASC") + local rows = {} + count = 1 + for i = 1, #items do + local item = items[i] + rows[count] = item[1] + count = count + 1 + end + p(rows) +end) + +print("OK") \ No newline at end of file diff --git a/Assets/Script/Test/SpriteTL.lua b/Assets/Script/Test/SpriteTL.lua new file mode 100644 index 000000000..47b4527b1 --- /dev/null +++ b/Assets/Script/Test/SpriteTL.lua @@ -0,0 +1,111 @@ +local Sprite = require("Sprite") + + +local sprite = Sprite("Image/logo.png") +if sprite == nil then + return +end +sprite.scaleX = 0.5 +sprite.scaleY = 0.5 +sprite.touchEnabled = true +sprite:slot("TapMoved", function(touch) + if not touch.first then + return + end + sprite.position = sprite.position + touch.delta +end) + +local ImGui = require("ImGui") +local Vec2 = require("Vec2") +local Size = require("Size") +local App = require("App") +local threadLoop = require("threadLoop") + +local windowFlags = { + "NoResize", + "NoSavedSettings", +} +threadLoop(function() + local width = App.visualSize.width + ImGui.SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) + ImGui.SetNextWindowSize(Vec2(240, 520), "FirstUseEver") + ImGui.Begin("Sprite", windowFlags, function() + ImGui.BeginChild("SpriteSetting", Vec2(-1, -40), function() + if sprite == nil then + return + end + local changed = false + local z = sprite.z + changed, z = ImGui.DragFloat("Z", z, 1, -1000, 1000, "%.2f") + if changed then + sprite.z = z + end + local anchor = sprite.anchor + local x, y = anchor.x, anchor.y + changed, x, y = ImGui.DragFloat2("Anchor", x, y, 0.01, 0, 1, "%.2f") + if changed then + sprite.anchor = Vec2(x, y) + end + local size = sprite.size + local spriteW, height = size.width, size.height + changed, spriteW, height = ImGui.DragFloat2("Size", spriteW, height, 0.1, 0, 1000, "%.f") + if changed then + sprite.size = Size(spriteW, height) + end + local scaleX, scaleY = sprite.scaleX, sprite.scaleY + changed, scaleX, scaleY = ImGui.DragFloat2("Scale", scaleX, scaleY, 0.01, -2, 2, "%.2f") + if changed then + sprite.scaleX, sprite.scaleY = scaleX, scaleY + end + ImGui.PushItemWidth(-60, function() + local angle = sprite.angle + changed, angle = ImGui.DragInt("Angle", math.floor(angle), 1, -360, 360) + if changed then + sprite.angle = angle + end + end) + ImGui.PushItemWidth(-60, function() + local angleX = sprite.angleX + changed, angleX = ImGui.DragInt("AngleX", math.floor(angleX), 1, -360, 360) + if changed then + sprite.angleX = angleX + end + end) + ImGui.PushItemWidth(-60, function() + local angleY = sprite.angleY + changed, angleY = ImGui.DragInt("AngleY", math.floor(angleY), 1, -360, 360) + if changed then + sprite.angleY = angleY + end + end) + local skewX, skewY = sprite.skewX, sprite.skewY + changed, skewX, skewY = ImGui.DragInt2("Skew", math.floor(skewX), math.floor(skewY), 1, -360, 360) + if changed then + sprite.skewX, sprite.skewY = skewX, skewY + end + ImGui.PushItemWidth(-70, function() + local opacity = sprite.opacity + changed, opacity = ImGui.DragFloat("Opacity", opacity, 0.01, 0, 1, "%.2f") + if changed then + sprite.opacity = opacity + end + end) + ImGui.PushItemWidth(-1, function() + local color3 = sprite.color3 + ImGui.SetColorEditOptions("RGB") + if ImGui.ColorEdit3("", color3) then + sprite.color3 = color3 + end + end) + end) + if ImGui.Button("Reset", Vec2(140, 30)) then + if sprite == nil then + return + end + local parent = sprite.parent + parent:removeChild(sprite) + sprite = Sprite("Image/logo.png") + parent:addChild(sprite) + end + end) +end) \ No newline at end of file diff --git a/Assets/Script/Test/Struct.lua b/Assets/Script/Test/Struct.lua new file mode 100644 index 000000000..410f8eb69 --- /dev/null +++ b/Assets/Script/Test/Struct.lua @@ -0,0 +1,58 @@ +-- [yue]: Script/Test/Struct.yue +local print = _G.print -- 1 +local tostring = _G.tostring -- 1 +local Struct = require("Utils").Struct -- 1 +local Unit = Struct.My.Name.Space.Unit("name", "group", "tag", "actions") -- 4 +local Action = Struct.Action("name", "id") -- 5 +local Array = Struct.Array() -- 6 +local unit = Unit({ -- 10 + name = "abc", -- 10 + group = 123, -- 11 + tag = "tagX", -- 12 + actions = Array({ -- 14 + Action({ -- 14 + name = "walk", -- 14 + id = "a1" -- 14 + }), -- 14 + Action({ -- 15 + name = "run", -- 15 + id = "a2" -- 15 + }), -- 15 + Action({ -- 16 + name = "sleep", -- 16 + id = "a3" -- 16 + }) -- 16 + }) -- 13 +}) -- 9 +unit.__notify = function(event, key, value) -- 19 + if "Modified" == event then -- 21 + return print("Value of name \"" .. tostring(key) .. "\" changed to " .. tostring(value) .. ".") -- 22 + elseif "Updated" == event then -- 23 + return print("Values updated.") -- 24 + end -- 24 +end -- 19 +unit.actions.__notify = function(event, index, item) -- 27 + if "Added" == event then -- 29 + return print("Add item " .. tostring(item) .. " at index " .. tostring(index) .. ".") -- 30 + elseif "Removed" == event then -- 31 + return print("Remove item " .. tostring(item) .. " at index " .. tostring(index) .. ".") -- 32 + elseif "Changed" == event then -- 33 + return print("Change item to " .. tostring(item) .. " at index " .. tostring(index) .. ".") -- 34 + elseif "Updated" == event then -- 35 + return print("Items updated.") -- 36 + end -- 36 +end -- 27 +unit.name = "pig" -- 38 +unit.actions:insert(Action({ -- 39 + name = "idle", -- 39 + id = "a4" -- 39 +})) -- 39 +unit.actions:removeAt(1) -- 40 +local structStr = tostring(unit) -- 42 +print(structStr) -- 43 +local loadedUnit = Struct:load(structStr) -- 45 +for i = 1, loadedUnit.actions:count() do -- 46 + print(loadedUnit.actions:get(i)) -- 47 +end -- 47 +print(Struct) -- 49 +return Struct:clear() -- 52 diff --git a/Assets/Script/Test/Text.lua b/Assets/Script/Test/Text.lua new file mode 100644 index 000000000..5dc83a08c --- /dev/null +++ b/Assets/Script/Test/Text.lua @@ -0,0 +1,145 @@ +-- [yue]: Script/Test/Text.yue +local View = dora.View -- 1 +local math = _G.math -- 1 +local App = dora.App -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local Label = dora.Label -- 1 +local LineRect = require("UI.View.Shape.LineRect") -- 2 +local ScrollArea = require("UI.Control.Basic.ScrollArea") -- 3 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 4 +local viewWidth, viewHeight -- 6 +do -- 6 + local _obj_0 = View.size -- 6 + viewWidth, viewHeight = _obj_0.width, _obj_0.height -- 6 +end -- 6 +local width, height = viewWidth - 200, viewHeight - 20 -- 8 +local fontSize = math.floor(20 * App.devicePixelRatio) -- 10 +local _with_0 = AlignNode({ -- 12 + isRoot = true, -- 12 + inUI = false -- 12 +}) -- 12 +_with_0:addChild((function() -- 13 + local _with_1 = AlignNode({ -- 13 + alignWidth = "w", -- 13 + alignHeight = "h" -- 13 + }) -- 13 + _with_1:addChild((function() -- 14 + local _with_2 = ScrollArea({ -- 15 + width = width, -- 15 + height = height, -- 16 + paddingX = 0, -- 17 + paddingY = 50, -- 18 + viewWidth = height, -- 19 + viewHeight = height -- 20 + }) -- 14 + _with_2.border = LineRect({ -- 22 + width = width, -- 22 + height = height, -- 22 + color = 0xffffffff -- 22 + }) -- 22 + _with_2.area:addChild(_with_2.border) -- 23 + _with_2:slot("AlignLayout", function(w, h) -- 24 + _with_2.position = Vec2(w / 2, h / 2) -- 25 + w = w - 200 -- 26 + h = h - 20 -- 27 + _with_2.view.children.first.textWidth = w - fontSize -- 28 + _with_2:adjustSizeWithAlign("Auto", 10, Size(w, h)) -- 29 + _with_2.area:removeChild(_with_2.border) -- 30 + _with_2.border = LineRect({ -- 31 + width = w, -- 31 + height = h, -- 31 + color = 0xffffffff -- 31 + }) -- 31 + return _with_2.area:addChild(_with_2.border) -- 32 + end) -- 24 + _with_2.view:addChild((function() -- 33 + local _with_3 = Label("sarasa-mono-sc-regular", fontSize) -- 33 + _with_3.alignment = "Left" -- 34 + _with_3.textWidth = width - fontSize -- 35 + _with_3.text = [[# 贡献指南 + +  非常感谢您对Dora SSR的兴趣和支持!我们欢迎任何人为这个项目做出贡献。以下是一些建议和指南,以帮助您开始参与项目的贡献。 + +
+ +## 报告问题 + +  如果您在使用Dora SSR时发现了问题,请在GitHub仓库中提交一个Issue。在提交Issue之前,请确保: + +1. 查看已有的Issue,避免重复报告。 + +2. 详细描述问题,包括预期行为和实际发生的行为。 + +3. 如果可能,请提供一个简单的重现问题的示例代码。 + +
+ +## 功能建议 + +  我们非常欢迎您对Dora SSR提出新功能的建议。在提交功能建议之前,请确保: + +1. 您的建议与Dora SSR的目标和愿景保持一致。 + +2. 提供足够的细节,以便我们理解您的需求和建议的实现方式。 + +
+ +## 代码贡献 + +  如果您想要为Dora SSR贡献代码,请遵循以下步骤: + +1. **Fork 项目**: + + Fork 项目仓库。 + +2. **创建分支**: + + 在本地创建一个新的分支,以便进行更改。 + +3. **从源代码构建**: + + 通过参考[从源代码构建的文档](https://dora-ssr.net/zh-Hans/docs/tutorial/dev-configuration),熟悉项目从源代码编译的过程。 + +4. **进行编码**: + + 编写代码并确保代码符合项目的编码规范。 + + **编码风格指南:** + + - 我们希望遵循同一种编码书写的风格,以保持项目的一致性和可读性。我们的风格定义在位于[此处](Tools/Format/.clang-format)的 clang-format 配置文件中。 + - 提交代码之前,请确保对代码运行 clang-format 对代码做格式化的处理。 + +5. **提交和推送**: + + 提交更改并将它们推送到您的 fork 仓库。 + +6. **提交 Pull Request**: + + 创建一个 Pull Request,详细描述你的更改及其理由。 + +
+ +## 贡献文档 + +  文档对于开源项目的成功至关重要。如果您发现了文档中的错误,或者您认为可以改进文档,请提交Issue或Pull Request。 + +
+ +## 社区支持 + +  您可以通过回答问题、提供教程和示例项目等方式为社区提供支持。如果您有任何可以帮助其他开发者的资源,请分享在社区相关的论坛和聊天室中。 + +
+ +------ + +  希望这个贡献指南能帮助您开始参与Dora SSR项目。再次感谢您的支持,期待您的贡献!]] -- 36 + return _with_3 -- 33 + end)()) -- 33 + _with_2:adjustSizeWithAlign() -- 114 + return _with_2 -- 14 + end)()) -- 14 + return _with_1 -- 13 +end)()) -- 13 +return _with_0 -- 12 diff --git a/Assets/Script/Test/TextInput.lua b/Assets/Script/Test/TextInput.lua new file mode 100644 index 000000000..a29e8456b --- /dev/null +++ b/Assets/Script/Test/TextInput.lua @@ -0,0 +1,187 @@ +-- [yue]: Script/Test/TextInput.yue +local Class = dora.Class -- 1 +local Label = dora.Label -- 1 +local Vec2 = dora.Vec2 -- 1 +local Line = dora.Line -- 1 +local Color = dora.Color -- 1 +local loop = dora.loop -- 1 +local sleep = dora.sleep -- 1 +local math = _G.math -- 1 +local ClipNode = dora.ClipNode -- 1 +local Size = dora.Size -- 1 +local Keyboard = dora.Keyboard -- 1 +local App = dora.App -- 1 +local Node = dora.Node -- 1 +local property = dora.property -- 1 +local LineRect = require("UI.View.Shape.LineRect") -- 2 +local SolidRect = require("UI.View.Shape.SolidRect") -- 3 +local utf8 = require("utf-8") -- 4 +local TextInput = Class((function(args) -- 6 + local fontName, fontSize, width, height, hint, text = args.fontName, args.fontSize, args.width, args.height, args.hint, args.text -- 7 + if hint == nil then -- 12 + hint = "" -- 12 + end -- 12 + if text == nil then -- 13 + text = hint -- 13 + end -- 13 + local label -- 16 + do -- 16 + local _with_0 = Label(fontName, fontSize) -- 16 + _with_0.batched = false -- 17 + _with_0.text = text -- 18 + _with_0.y = height / 2 - fontSize / 2 -- 19 + _with_0.anchor = Vec2.zero -- 20 + _with_0.alignment = "Left" -- 21 + label = _with_0 -- 16 + end -- 16 + local cursor = Line({ -- 23 + Vec2.zero, -- 23 + Vec2(0, fontSize + 2) -- 23 + }, Color(0xffffffff)) -- 23 + local blink -- 24 + blink = function() -- 24 + return loop(function() -- 24 + cursor.visible = true -- 25 + sleep(0.5) -- 26 + cursor.visible = false -- 27 + return sleep(0.5) -- 28 + end) -- 28 + end -- 24 + cursor.y = label.y -- 30 + cursor.visible = false -- 31 + local updateText -- 33 + updateText = function(text) -- 33 + label.text = text -- 34 + local offsetX = math.max(label.width + 3 - width, 0) -- 35 + label.x = -offsetX -- 36 + cursor.x = label.width - offsetX - 10 -- 37 + return cursor:schedule(blink()) -- 38 + end -- 33 + local node -- 40 + do -- 40 + local _with_0 = ClipNode(SolidRect({ -- 40 + width = width, -- 40 + height = height -- 40 + })) -- 40 + local textEditing = "" -- 41 + local textDisplay = "" -- 42 + _with_0.size = Size(width, height) -- 44 + _with_0.position = Vec2(width, height) / 2 -- 45 + _with_0.hint = hint -- 46 + _with_0:addChild(label) -- 47 + _with_0:addChild(cursor) -- 48 + local updateIMEPos -- 50 + updateIMEPos = function() -- 50 + return _with_0:convertToWindowSpace(Vec2(-label.x + label.width, 0), function(pos) -- 51 + return Keyboard:updateIMEPosHint(pos) -- 52 + end) -- 51 + end -- 50 + local startEditing -- 53 + startEditing = function() -- 53 + if not _with_0.imeAttached then -- 54 + _with_0:attachIME() -- 55 + return updateIMEPos() -- 56 + end -- 54 + end -- 53 + _with_0.updateDisplayText = function(self, text) -- 57 + textDisplay = text -- 58 + label.text = text -- 59 + end -- 57 + _with_0.imeAttached = false -- 61 + _with_0:slot("AttachIME", function() -- 62 + _with_0.imeAttached = true -- 63 + _with_0.keyboardEnabled = true -- 64 + return updateText(textDisplay) -- 65 + end) -- 62 + _with_0:slot("DetachIME", function() -- 67 + _with_0.imeAttached = false -- 68 + _with_0.keyboardEnabled = false -- 69 + cursor.visible = false -- 70 + cursor:unschedule() -- 71 + textEditing = "" -- 72 + label.x = 0 -- 73 + if textDisplay == "" then -- 74 + label.text = _with_0.hint -- 74 + end -- 74 + end) -- 67 + _with_0.touchEnabled = true -- 76 + _with_0:slot("Tapped", function(touch) -- 77 + if touch.first then -- 77 + return startEditing() -- 77 + end -- 77 + end) -- 77 + _with_0:slot("KeyPressed", function(key) -- 79 + if App.platform == "Android" and utf8.len(textEditing) == 1 then -- 80 + if key == "BackSpace" then -- 81 + textEditing = "" -- 81 + end -- 81 + else -- 83 + if textEditing ~= "" then -- 83 + return -- 83 + end -- 83 + end -- 80 + if "BackSpace" == key then -- 85 + if #textDisplay > 0 then -- 86 + textDisplay = utf8.sub(textDisplay, 1, -2) -- 87 + return updateText(textDisplay) -- 88 + end -- 86 + elseif "Return" == key then -- 89 + return _with_0:detachIME() -- 90 + end -- 90 + end) -- 79 + _with_0:slot("TextInput", function(text) -- 92 + textDisplay = utf8.sub(textDisplay, 1, -1 - utf8.len(textEditing)) .. text -- 93 + textEditing = "" -- 94 + updateText(textDisplay) -- 95 + return updateIMEPos() -- 96 + end) -- 92 + _with_0:slot("TextEditing", function(text, start) -- 98 + textDisplay = utf8.sub(textDisplay, 1, -1 - utf8.len(textEditing)) .. text -- 99 + textEditing = text -- 100 + label.text = textDisplay -- 101 + local offsetX = math.max(label.width + 3 - width, 0) -- 102 + label.x = -offsetX -- 103 + local charSprite = label:getCharacter(utf8.len(textDisplay) - utf8.len(textEditing) + start) -- 104 + if charSprite then -- 105 + cursor.x = charSprite.x + charSprite.width / 2 - offsetX + 1 -- 106 + cursor:schedule(blink()) -- 107 + else -- 109 + updateText(textDisplay) -- 109 + end -- 105 + return updateIMEPos() -- 110 + end) -- 98 + node = _with_0 -- 40 + end -- 40 + local _with_0 = Node() -- 112 + _with_0.content = node -- 113 + _with_0.cursor = cursor -- 114 + _with_0.label = label -- 115 + _with_0.size = Size(width, height) -- 116 + _with_0:addChild(node) -- 117 + return _with_0 -- 112 +end), { -- 119 + text = property((function(self) -- 119 + return self.label.text -- 119 + end), function(self, value) -- 120 + if self.content.imeAttached then -- 121 + self.content:detachIME() -- 121 + end -- 121 + return self.content:updateDisplayText(value) -- 122 + end) -- 119 +}) -- 6 +local _with_0 = TextInput({ -- 126 + hint = "点这里进行输入", -- 126 + width = 300, -- 127 + height = 60, -- 128 + fontName = "sarasa-mono-sc-regular", -- 129 + fontSize = 40 -- 130 +}) -- 125 +local themeColor = App.themeColor:toARGB() -- 132 +_with_0.label.color = Color(0xffff0080) -- 135 +_with_0:addChild(LineRect({ -- 137 + x = -2, -- 137 + width = _with_0.width + 4, -- 138 + height = _with_0.height, -- 139 + color = themeColor -- 140 +})) -- 136 +return _with_0 -- 125 diff --git a/Assets/Script/Test/UI.lua b/Assets/Script/Test/UI.lua new file mode 100644 index 000000000..6e012cc9c --- /dev/null +++ b/Assets/Script/Test/UI.lua @@ -0,0 +1,103 @@ +-- [yue]: Script/Test/UI.yue +local tostring = _G.tostring -- 1 +local print = _G.print -- 1 +local Size = dora.Size -- 1 +local Director = dora.Director -- 1 +local Vec2 = dora.Vec2 -- 1 +local Button = require("UI.Control.Basic.Button") -- 2 +local LineRect = require("UI.View.Shape.LineRect") -- 3 +local ScrollArea = require("UI.Control.Basic.ScrollArea") -- 4 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 5 +local Panel -- 7 +Panel = function(width, height, viewWidth, viewHeight) -- 7 + local _with_0 = ScrollArea({ -- 9 + width = width, -- 9 + height = height, -- 10 + paddingX = 50, -- 11 + paddingY = 50, -- 12 + viewWidth = viewWidth, -- 13 + viewHeight = viewHeight -- 14 + }) -- 8 + _with_0.border = LineRect({ -- 16 + width = width, -- 16 + height = height, -- 16 + color = 0xffffffff -- 16 + }) -- 16 + _with_0.area:addChild(_with_0.border) -- 17 + for i = 1, 50 do -- 18 + _with_0.view:addChild((function() -- 19 + local _with_1 = Button({ -- 20 + text = "点击\n按钮" .. tostring(i), -- 20 + width = 60, -- 21 + height = 60, -- 22 + fontName = "sarasa-mono-sc-regular", -- 23 + fontSize = 16 -- 24 + }) -- 19 + _with_1:slot("Tapped", function() -- 26 + return print("clicked " .. tostring(i)) -- 26 + end) -- 26 + return _with_1 -- 19 + end)()) -- 19 + end -- 26 + _with_0.view:alignItems(Size(viewWidth, height)) -- 27 + return _with_0 -- 8 +end -- 7 +return Director.ui:addChild((function() -- 29 + local _with_0 = AlignNode({ -- 29 + isRoot = true, -- 29 + inUI = true -- 29 + }) -- 29 + _with_0:addChild((function() -- 30 + local _with_1 = AlignNode() -- 30 + _with_1.hAlign = "Left" -- 31 + _with_1.vAlign = "Top" -- 32 + _with_1.alignWidth = "200" -- 33 + _with_1.alignHeight = "h-20" -- 34 + _with_1.alignOffset = Vec2(10, 10) -- 35 + _with_1:addChild((function() -- 36 + local _with_2 = Panel(200, 300, 430, 640) -- 36 + _with_2.position = Vec2(100, 150) -- 37 + _with_2:slot("AlignLayout", function(w, h) -- 38 + _with_2:adjustSizeWithAlign("Auto", 10, Size(w, h), Size(400, h)) -- 39 + _with_2.position = Vec2(w / 2, h / 2) -- 40 + _with_2.area:removeChild(_with_2.border) -- 41 + _with_2.border = LineRect({ -- 42 + width = w, -- 42 + height = h, -- 42 + color = 0xffffffff -- 42 + }) -- 42 + return _with_2.area:addChild(_with_2.border) -- 43 + end) -- 38 + return _with_2 -- 36 + end)()) -- 36 + return _with_1 -- 30 + end)()) -- 30 + _with_0:addChild((function() -- 44 + local _with_1 = AlignNode() -- 44 + _with_1.size = Size(300, 300) -- 45 + _with_1.hAlign = "Center" -- 46 + _with_1.vAlign = "Center" -- 47 + _with_1.alignOffset = Vec2.zero -- 48 + _with_1:addChild((function() -- 49 + local _with_2 = Panel(300, 300, 430, 640) -- 49 + _with_2.position = Vec2(150, 150) -- 50 + return _with_2 -- 49 + end)()) -- 49 + return _with_1 -- 44 + end)()) -- 44 + _with_0:addChild((function() -- 51 + local _with_1 = AlignNode() -- 51 + _with_1.size = Size(150, 200) -- 52 + _with_1.hAlign = "Right" -- 53 + _with_1.vAlign = "Bottom" -- 54 + _with_1.alignOffset = Vec2(10, 10) -- 55 + _with_1:addChild((function() -- 56 + local _with_2 = Panel(150, 200, 430, 640) -- 56 + _with_2.position = Vec2(75, 100) -- 57 + return _with_2 -- 56 + end)()) -- 56 + return _with_1 -- 51 + end)()) -- 51 + _with_0:alignLayout() -- 58 + return _with_0 -- 29 +end)()) -- 58 diff --git a/Assets/Script/Test/Yarn.lua b/Assets/Script/Test/Yarn.lua new file mode 100644 index 000000000..b8351adaa --- /dev/null +++ b/Assets/Script/Test/Yarn.lua @@ -0,0 +1,258 @@ +-- [yue]: Script/Test/Yarn.yue +local Path = dora.Path -- 1 +local Content = dora.Content -- 1 +local View = dora.View -- 1 +local math = _G.math -- 1 +local App = dora.App -- 1 +local Vec2 = dora.Vec2 -- 1 +local Size = dora.Size -- 1 +local Label = dora.Label -- 1 +local setmetatable = _G.setmetatable -- 1 +local table = _G.table -- 1 +local tostring = _G.tostring -- 1 +local select = _G.select -- 1 +local coroutine = _G.coroutine -- 1 +local Menu = dora.Menu -- 1 +local type = _G.type -- 1 +local ipairs = _G.ipairs -- 1 +local thread = dora.thread -- 1 +local threadLoop = dora.threadLoop -- 1 +local _module_0 = dora.ImGui -- 1 +local SetNextWindowPos = _module_0.SetNextWindowPos -- 1 +local SetNextWindowSize = _module_0.SetNextWindowSize -- 1 +local Begin = _module_0.Begin -- 1 +local Text = _module_0.Text -- 1 +local Separator = _module_0.Separator -- 1 +local Combo = _module_0.Combo -- 1 +local pairs = _G.pairs -- 1 +local CircleButton = require("UI.Control.Basic.CircleButton") -- 2 +local YarnRunner = require("YarnRunner") -- 3 +local LineRect = require("UI.View.Shape.LineRect") -- 4 +local ScrollArea = require("UI.Control.Basic.ScrollArea") -- 5 +local AlignNode = require("UI.Control.Basic.AlignNode") -- 6 +local path = Path:getScriptPath(...) -- 8 +Content:insertSearchPath(1, path) -- 9 +local viewWidth, viewHeight -- 11 +do -- 11 + local _obj_0 = View.size -- 11 + viewWidth, viewHeight = _obj_0.width, _obj_0.height -- 11 +end -- 11 +local width, height = viewWidth - 200, viewHeight - 20 -- 13 +local fontSize = math.floor(20 * App.devicePixelRatio) -- 15 +local texts = { } -- 17 +local root, label, scroll, control -- 19 +do -- 21 + local _with_0 = AlignNode({ -- 21 + isRoot = true, -- 21 + inUI = false -- 21 + }) -- 21 + _with_0:addChild((function() -- 22 + root = AlignNode({ -- 22 + alignWidth = "w", -- 22 + alignHeight = "h" -- 22 + }) -- 22 + root:addChild((function() -- 23 + scroll = ScrollArea({ -- 24 + width = width, -- 24 + height = height, -- 25 + paddingX = 0, -- 26 + paddingY = 50, -- 27 + viewWidth = height, -- 28 + viewHeight = height -- 29 + }) -- 23 + scroll.border = LineRect({ -- 31 + width = width, -- 31 + height = height, -- 31 + color = 0xffffffff -- 31 + }) -- 31 + scroll.area:addChild(scroll.border) -- 32 + scroll:slot("AlignLayout", function(w, h) -- 33 + scroll.position = Vec2(w / 2, h / 2) -- 34 + w = w - 200 -- 35 + h = h - 20 -- 36 + scroll.view.children.first.textWidth = w - fontSize -- 37 + scroll:adjustSizeWithAlign("Auto", 10, Size(w, h)) -- 38 + scroll.area:removeChild(scroll.border) -- 39 + scroll.border = LineRect({ -- 40 + width = w, -- 40 + height = h, -- 40 + color = 0xffffffff -- 40 + }) -- 40 + return scroll.area:addChild(scroll.border) -- 41 + end) -- 33 + scroll.view:addChild((function() -- 42 + label = Label("sarasa-mono-sc-regular", fontSize) -- 42 + label.alignment = "Left" -- 43 + label.textWidth = width - fontSize -- 44 + label.text = "" -- 45 + return label -- 42 + end)()) -- 42 + return scroll -- 23 + end)()) -- 23 + return root -- 22 + end)()) -- 22 + _with_0:addChild((function() -- 46 + control = AlignNode({ -- 46 + hAlign = "Center", -- 46 + vAlign = "Bottom" -- 46 + }) -- 46 + control.alignOffset = Vec2(0, 200) -- 47 + return control -- 46 + end)()) -- 46 + _with_0:alignLayout() -- 48 +end -- 21 +local commands = setmetatable({ }, { -- 50 + __index = function(self, name) -- 50 + return function(...) -- 50 + local msg = "[command]: " .. name .. " " .. table.concat((function(...) -- 51 + local _accum_0 = { } -- 51 + local _len_0 = 1 -- 51 + for i = 1, select('#', ...) do -- 51 + _accum_0[_len_0] = tostring(select(i, ...)) -- 51 + _len_0 = _len_0 + 1 -- 51 + end -- 51 + return _accum_0 -- 51 + end)(...), ", ") -- 51 + return coroutine.yield("Command", msg) -- 52 + end -- 52 + end -- 50 +}) -- 50 +local runner = YarnRunner("tutorial.yarn", "Start", { }, commands, true) -- 54 +local menu -- 56 +do -- 56 + local _with_0 = Menu() -- 56 + _with_0:addTo(control) -- 57 + menu = _with_0 -- 56 +end -- 56 +local advance -- 59 +local setButtons -- 61 +setButtons = function(options) -- 61 + menu:removeAllChildren() -- 62 + local buttons -- 63 + if options ~= nil then -- 63 + buttons = options -- 63 + else -- 63 + buttons = 1 -- 63 + end -- 63 + menu.size = Size(140 * buttons, 140) -- 65 + for i = 1, buttons do -- 66 + menu:addChild((function() -- 67 + local _with_0 = CircleButton({ -- 68 + text = options and tostring(i) or "Next", -- 68 + radius = 60, -- 69 + fontSize = 40 -- 70 + }) -- 67 + _with_0:slot("Tapped", function() -- 72 + if options then -- 73 + return advance(i) -- 74 + else -- 76 + return advance() -- 76 + end -- 73 + end) -- 72 + return _with_0 -- 67 + end)()) -- 67 + end -- 76 + menu:alignItems() -- 77 + return menu -- 64 +end -- 61 +advance = function(option) -- 79 + local action, result = runner:advance(option) -- 80 + if "Text" == action then -- 81 + local charName = "" -- 82 + if result.marks then -- 83 + local _list_0 = result.marks -- 84 + for _index_0 = 1, #_list_0 do -- 84 + local mark = _list_0[_index_0] -- 84 + do -- 85 + local _type_0 = type(mark) -- 85 + local _tab_0 = "table" == _type_0 or "userdata" == _type_0 -- 85 + if _tab_0 then -- 85 + local attr = mark.name -- 85 + local name -- 85 + do -- 85 + local _obj_0 = mark.attrs -- 85 + local _type_1 = type(_obj_0) -- 85 + if "table" == _type_1 or "userdata" == _type_1 then -- 85 + name = _obj_0.name -- 85 + end -- 86 + end -- 86 + if attr ~= nil and name ~= nil then -- 85 + if attr == "char" then -- 86 + charName = tostring(name) .. ": " -- 86 + end -- 86 + end -- 85 + end -- 86 + end -- 86 + end -- 86 + end -- 83 + texts[#texts + 1] = charName .. result.text -- 87 + if result.optionsFollowed then -- 88 + advance() -- 89 + else -- 91 + setButtons() -- 91 + end -- 88 + elseif "Option" == action then -- 92 + for i, op in ipairs(result) do -- 93 + texts[#texts + 1] = "[" .. tostring(i) .. "]: " .. tostring(op.text) -- 94 + end -- 94 + setButtons(#result) -- 95 + elseif "Command" == action then -- 96 + texts[#texts + 1] = result -- 97 + setButtons() -- 98 + else -- 100 + menu:removeAllChildren() -- 100 + texts[#texts + 1] = result -- 101 + end -- 101 + label.text = table.concat(texts, "\n") -- 102 + root:alignLayout() -- 103 + return thread(function() -- 104 + return scroll:scrollToPosY(label.y - label.height / 2) -- 104 + end) -- 104 +end -- 79 +advance() -- 106 +local testFiles = { -- 108 + "tutorial.yarn" -- 108 +} -- 108 +local files = { -- 109 + "tutorial.yarn" -- 109 +} -- 109 +local _list_0 = Content:getAllFiles(Content.writablePath) -- 110 +for _index_0 = 1, #_list_0 do -- 110 + local file = _list_0[_index_0] -- 110 + if "yarn" ~= Path:getExt(file) then -- 111 + goto _continue_0 -- 111 + end -- 111 + testFiles[#testFiles + 1] = Path(Content.writablePath, file) -- 112 + files[#files + 1] = Path:getFilename(file) -- 113 + ::_continue_0:: -- 111 +end -- 113 +local currentFile = 1 -- 115 +local windowFlags = { -- 117 + "NoDecoration", -- 117 + "NoSavedSettings", -- 118 + "NoFocusOnAppearing", -- 119 + "NoNav", -- 120 + "NoMove" -- 121 +} -- 116 +return threadLoop(function() -- 122 + local width -- 123 + width = App.visualSize.width -- 123 + SetNextWindowPos(Vec2(width - 10, 10), "Always", Vec2(1, 0)) -- 124 + SetNextWindowSize(Vec2(200, 0), "Always") -- 125 + return Begin("Yarn Test", windowFlags, function() -- 126 + Text("Yarn Tester") -- 127 + Separator() -- 128 + local changed -- 129 + changed, currentFile = Combo("File", currentFile, files) -- 129 + if changed then -- 130 + runner = YarnRunner(testFiles[currentFile], "Start", { }, commands, true) -- 131 + texts = { } -- 132 + advance() -- 133 + end -- 130 + Text("Variables") -- 134 + Separator() -- 135 + for k, v in pairs(runner.state) do -- 136 + Text(tostring(k) .. ": " .. tostring(v)) -- 137 + end -- 137 + end) -- 137 +end) -- 137 diff --git a/Assets/Script/init.lua b/Assets/Script/init.lua new file mode 100644 index 000000000..6c07316c5 --- /dev/null +++ b/Assets/Script/init.lua @@ -0,0 +1,14 @@ +-- [yue]: Script/init.yue +local Content = dora.Content -- 1 +local Path = dora.Path -- 1 +local App = dora.App -- 1 +do -- 3 + local _with_0 = Content -- 3 + _with_0.searchPaths = { -- 5 + _with_0.writablePath, -- 5 + Path(_with_0.assetPath, "Script"), -- 6 + Path(_with_0.assetPath, "Script", "Lib"), -- 7 + Path(_with_0.assetPath, "Script", "Lib", "Dora", App.locale:match("^zh") and "zh-Hans" or "en") -- 8 + } -- 4 +end -- 3 +return require("Dev.Entry") -- 10 diff --git a/Source/3rdParty/Lua/lapi.c b/Source/3rdParty/Lua/lapi.c index 74dcd6f17..8c735005b 100644 --- a/Source/3rdParty/Lua/lapi.c +++ b/Source/3rdParty/Lua/lapi.c @@ -139,7 +139,7 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); - api_checknelems(from, n); + api_checkpop(from, n); api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); from->top.p -= n; @@ -205,7 +205,6 @@ LUA_API void lua_settop (lua_State *L, int idx) { api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } - api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); newtop = L->top.p + diff; if (diff < 0 && L->tbclist.p >= newtop) { lua_assert(hastocloseCfunc(ci->nresults)); @@ -253,6 +252,7 @@ LUA_API void lua_rotate (lua_State *L, int idx, int n) { lua_lock(L); t = L->top.p - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ + api_check(L, L->tbclist.p < p, "moving a to-be-closed slot"); api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ reverse(L, p, m); /* reverse the prefix with length 'n' */ @@ -345,9 +345,9 @@ LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { LUA_API void lua_arith (lua_State *L, int op) { lua_lock(L); if (op != LUA_OPUNM && op != LUA_OPBNOT) - api_checknelems(L, 2); /* all other operations expect two operands */ + api_checkpop(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ - api_checknelems(L, 1); + api_checkpop(L, 1); setobjs2s(L, L->top.p, L->top.p - 1); api_incr_top(L); } @@ -620,17 +620,18 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { api_incr_top(L); } else { + int i; CClosure *cl; - api_checknelems(L, n); + api_checkpop(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; - L->top.p -= n; - while (n--) { - setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); + for (i = 0; i < n; i++) { + setobj2n(L, &cl->upvalue[i], s2v(L->top.p - n + i)); /* does not need barrier because closure is white */ lua_assert(iswhite(cl)); } + L->top.p -= n; setclCvalue(L, s2v(L->top.p), cl); api_incr_top(L); luaC_checkGC(L); @@ -718,6 +719,7 @@ LUA_API int lua_gettable (lua_State *L, int idx) { int hres; TValue *t; lua_lock(L); + api_checkpop(L, 1); t = index2value(L, idx); luaV_fastget(t, s2v(L->top.p - 1), s2v(L->top.p - 1), luaH_get, hres); if (hres != HOK) @@ -768,13 +770,13 @@ l_sinline Table *gettable (lua_State *L, int idx) { LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; - int hres; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); - hres = luaH_get(t, s2v(L->top.p - 1), s2v(L->top.p - 1)); - L->top.p--; /* remove key */ - return finishrawget(L, hres); + if (luaH_get(t, s2v(L->top.p - 1), s2v(L->top.p - 1)) != HOK) + setnilvalue(s2v(L->top.p - 1)); + lua_unlock(L); + return ttype(s2v(L->top.p - 1)); } @@ -798,7 +800,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { } -LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { +LUA_API void lua_createtable (lua_State *L, unsigned narray, unsigned nrec) { Table *t; lua_lock(L); t = luaH_new(L); @@ -868,7 +870,7 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { static void auxsetstr (lua_State *L, const TValue *t, const char *k) { int hres; TString *str = luaS_new(L, k); - api_checknelems(L, 1); + api_checkpop(L, 1); luaV_fastset(t, str, s2v(L->top.p - 1), hres, luaH_psetstr); if (hres == HOK) { luaV_finishfastset(L, t, s2v(L->top.p - 1)); @@ -896,7 +898,7 @@ LUA_API void lua_settable (lua_State *L, int idx) { TValue *t; int hres; lua_lock(L); - api_checknelems(L, 2); + api_checkpop(L, 2); t = index2value(L, idx); luaV_fastset(t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres, luaH_pset); if (hres == HOK) { @@ -919,7 +921,7 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { TValue *t; int hres; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = index2value(L, idx); luaV_fastseti(t, n, s2v(L->top.p - 1), hres); if (hres == HOK) @@ -937,7 +939,7 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { Table *t; lua_lock(L); - api_checknelems(L, n); + api_checkpop(L, n); t = gettable(L, idx); luaH_set(L, t, key, s2v(L->top.p - 1)); invalidateTMcache(t); @@ -962,7 +964,7 @@ LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); luaH_setint(L, t, n, s2v(L->top.p - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); @@ -975,7 +977,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); obj = index2value(L, objindex); if (ttisnil(s2v(L->top.p - 1))) mt = NULL; @@ -1032,7 +1034,7 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) @@ -1065,7 +1067,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); - api_checknelems(L, nargs+1); + api_checkpop(L, nargs + 1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); func = L->top.p - (nargs+1); @@ -1106,7 +1108,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); - api_checknelems(L, nargs+1); + api_checkpop(L, nargs + 1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); if (errfunc == 0) @@ -1175,7 +1177,7 @@ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { ptrdiff_t otop = savestack(L, L->top.p); /* original top */ TValue *f = s2v(L->top.p - 1); /* function to be dumped */ lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); api_check(L, isLfunction(f), "Lua function expected"); status = luaU_dump(L, clLvalue(f)->p, writer, data, strip); L->top.p = restorestack(L, otop); /* restore top */ @@ -1255,7 +1257,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { int param = va_arg(argp, int); int value = va_arg(argp, int); api_check(L, 0 <= param && param < LUA_GCPN, "invalid parameter"); - res = luaO_applyparam(g->gcparams[param], 100); + res = cast_int(luaO_applyparam(g->gcparams[param], 100)); if (value >= 0) g->gcparams[param] = luaO_codeparam(value); break; @@ -1278,7 +1280,7 @@ LUA_API int lua_error (lua_State *L) { TValue *errobj; lua_lock(L); errobj = s2v(L->top.p - 1); - api_checknelems(L, 1); + api_checkpop(L, 1); /* error object is the memory error message? */ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) luaM_error(L); /* raise a memory error */ @@ -1293,7 +1295,7 @@ LUA_API int lua_next (lua_State *L, int idx) { Table *t; int more; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); more = luaH_next(L, t, L->top.p - 1); if (more) diff --git a/Source/3rdParty/Lua/lapi.h b/Source/3rdParty/Lua/lapi.h index 438456480..757bf3d2e 100644 --- a/Source/3rdParty/Lua/lapi.h +++ b/Source/3rdParty/Lua/lapi.h @@ -29,8 +29,18 @@ /* Ensure the stack has at least 'n' elements */ #define api_checknelems(L,n) \ - api_check(L, (n) < (L->top.p - L->ci->func.p), \ - "not enough elements in the stack") + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") + + +/* Ensure the stack has at least 'n' elements to be popped. (Some +** functions only update a slot after checking it for popping, but that +** is only an optimization for a pop followed by a push.) +*/ +#define api_checkpop(L,n) \ + api_check(L, (n) < L->top.p - L->ci->func.p && \ + L->tbclist.p < L->top.p - (n), \ + "not enough free elements in the stack") /* diff --git a/Source/3rdParty/Lua/lauxlib.c b/Source/3rdParty/Lua/lauxlib.c index f48060ed9..634c85cd2 100644 --- a/Source/3rdParty/Lua/lauxlib.c +++ b/Source/3rdParty/Lua/lauxlib.c @@ -1131,8 +1131,11 @@ static void warnfon (void *ud, const char *message, int tocont) { /* Size for the buffer in int's, rounded up */ #define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int)) - -#define addbuff(b,v) (memcpy(b, &(v), sizeof(v)), b += sizeof(v)) +/* +** Copy the contents of variable 'v' into the buffer pointed by 'b'. +** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.) +*/ +#define addbuff(b,v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v)) static unsigned int luai_makeseed (void) { @@ -1146,7 +1149,7 @@ static unsigned int luai_makeseed (void) { /* fill (rare but possible) remain of the buffer with zeros */ memset(b, 0, sizeof(buff) - BUFSEEDB); res = buff[0]; - for (i = 0; i < BUFSEED; i++) + for (i = 1; i < BUFSEED; i++) res ^= (res >> 3) + (res << 7) + buff[i]; return res; } diff --git a/Source/3rdParty/Lua/ldblib.c b/Source/3rdParty/Lua/ldblib.c index 6dcbaa982..2c9413847 100644 --- a/Source/3rdParty/Lua/ldblib.c +++ b/Source/3rdParty/Lua/ldblib.c @@ -446,14 +446,6 @@ static int db_traceback (lua_State *L) { } -static int db_setcstacklimit (lua_State *L) { - int limit = (int)luaL_checkinteger(L, 1); - int res = lua_setcstacklimit(L, limit); - lua_pushinteger(L, res); - return 1; -} - - static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getuservalue", db_getuservalue}, @@ -471,7 +463,6 @@ static const luaL_Reg dblib[] = { {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_traceback}, - {"setcstacklimit", db_setcstacklimit}, {NULL, NULL} }; diff --git a/Source/3rdParty/Lua/ldebug.c b/Source/3rdParty/Lua/ldebug.c index aa3277cb9..daa979afe 100644 --- a/Source/3rdParty/Lua/ldebug.c +++ b/Source/3rdParty/Lua/ldebug.c @@ -245,6 +245,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { + api_checkpop(L, 1); setobjs2s(L, pos, L->top.p - 1); L->top.p--; /* pop value */ } diff --git a/Source/3rdParty/Lua/ldo.c b/Source/3rdParty/Lua/ldo.c index 05b14ec8a..699a9d2a7 100644 --- a/Source/3rdParty/Lua/ldo.c +++ b/Source/3rdParty/Lua/ldo.c @@ -767,6 +767,7 @@ static CallInfo *findpcall (lua_State *L) { ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { + api_checkpop(L, narg); L->top.p -= narg; /* remove args from the stack */ setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ api_incr_top(L); @@ -849,7 +850,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, return resume_error(L, "C stack overflow", nargs); L->nCcalls++; luai_userstateresume(L, nargs); - api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + api_checkpop(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); /* continue running after recoverable errors */ status = precover(L, status); @@ -878,7 +879,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, luai_userstateyield(L, nresults); lua_lock(L); ci = L->ci; - api_checknelems(L, nresults); + api_checkpop(L, nresults); if (l_unlikely(!yieldable(L))) { if (L != G(L)->mainthread) luaG_runerror(L, "attempt to yield across a C-call boundary"); diff --git a/Source/3rdParty/Lua/ldump.c b/Source/3rdParty/Lua/ldump.c index b31e7bc79..34cfb5761 100644 --- a/Source/3rdParty/Lua/ldump.c +++ b/Source/3rdParty/Lua/ldump.c @@ -30,7 +30,7 @@ typedef struct { int strip; int status; Table *h; /* table to track saved strings */ - lua_Integer nstr; /* counter to number saved strings */ + lua_Unsigned nstr; /* counter to number saved strings */ } DumpState; @@ -83,26 +83,27 @@ static void dumpByte (DumpState *D, int y) { /* -** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" -** rounds up the division.) +** size for 'dumpVarint' buffer: each byte can store up to 7 bits. +** (The "+6" rounds up the division.) */ -#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) +#define DIBS ((sizeof(varint_t) * CHAR_BIT + 6) / 7) -static void dumpSize (DumpState *D, size_t x) { +/* +** Dumps an unsigned integer using the MSB Varint encoding +*/ +static void dumpVarint (DumpState *D, varint_t x) { lu_byte buff[DIBS]; - int n = 0; - do { - buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ - x >>= 7; - } while (x != 0); - buff[DIBS - 1] |= 0x80; /* mark last byte */ + int n = 1; + buff[DIBS - 1] = x & 0x7f; /* fill least-significant byte */ + while ((x >>= 7) != 0) /* fill other bytes in reverse order */ + buff[DIBS - (++n)] = (x & 0x7f) | 0x80; dumpVector(D, buff + DIBS - n, n); } static void dumpInt (DumpState *D, int x) { lua_assert(x >= 0); - dumpSize(D, x); + dumpVarint(D, x); } @@ -125,22 +126,22 @@ static void dumpInteger (DumpState *D, lua_Integer x) { */ static void dumpString (DumpState *D, TString *ts) { if (ts == NULL) - dumpSize(D, 0); + dumpVarint(D, 0); else { TValue idx; if (luaH_getstr(D->h, ts, &idx) == HOK) { /* string already saved? */ - dumpSize(D, 1); /* reuse a saved string */ - dumpInt(D, ivalue(&idx)); /* index of saved string */ + dumpVarint(D, 1); /* reuse a saved string */ + dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */ } else { /* must write and save the string */ TValue key, value; /* to save the string in the hash */ size_t size; const char *s = getlstr(ts, size); - dumpSize(D, size + 2); + dumpVarint(D, size + 2); dumpVector(D, s, size + 1); /* include ending '\0' */ D->nstr++; /* one more saved string */ setsvalue(D->L, &key, ts); /* the string is the key */ - setivalue(&value, D->nstr); /* its index is the value */ + setivalue(&value, l_castU2S(D->nstr)); /* its index is the value */ luaH_set(D->L, D->h, &key, &value); /* h[ts] = nstr */ /* integer value does not need barrier */ } diff --git a/Source/3rdParty/Lua/linit.c b/Source/3rdParty/Lua/linit.c index 675fb65fb..140f6d759 100644 --- a/Source/3rdParty/Lua/linit.c +++ b/Source/3rdParty/Lua/linit.c @@ -21,12 +21,12 @@ /* -** Standard Libraries +** Standard Libraries. (Must be listed in the same ORDER of their +** respective constants LUA_K.) */ static const luaL_Reg stdlibs[] = { {LUA_GNAME, luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, - {LUA_COLIBNAME, luaopen_coroutine}, {LUA_DBLIBNAME, luaopen_debug}, {LUA_IOLIBNAME, luaopen_io}, @@ -35,30 +35,28 @@ static const luaL_Reg stdlibs[] = { {LUA_STRLIBNAME, luaopen_string}, {LUA_TABLIBNAME, luaopen_table}, {LUA_UTF8LIBNAME, luaopen_utf8}, - {NULL, NULL} }; /* -** require selected standard libraries and add the others to the -** preload table. +** require and preload selected standard libraries */ -LUALIB_API void luaL_openselectedlibs (lua_State *L, int what) { - int mask = 1; +LUALIB_API void luaL_openselectedlibs (lua_State *L, int load, int preload) { + int mask; const luaL_Reg *lib; luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); - for (lib = stdlibs; lib->func; (lib++, mask <<= 1)) { - if (what & mask) { /* selected? */ + for (lib = stdlibs, mask = 1; lib->name != NULL; lib++, mask <<= 1) { + if (load & mask) { /* selected? */ luaL_requiref(L, lib->name, lib->func, 1); /* require library */ lua_pop(L, 1); /* remove result from the stack */ } - else { /* add library to PRELOAD table */ + else if (preload & mask) { /* selected? */ lua_pushcfunction(L, lib->func); - lua_setfield(L, -2, lib->name); + lua_setfield(L, -2, lib->name); /* add library to PRELOAD table */ } } lua_assert((mask >> 1) == LUA_UTF8LIBK); - lua_pop(L, 1); // remove PRELOAD table + lua_pop(L, 1); /* remove PRELOAD table */ } diff --git a/Source/3rdParty/Lua/lmathlib.c b/Source/3rdParty/Lua/lmathlib.c index c0a75f065..c1041f37b 100644 --- a/Source/3rdParty/Lua/lmathlib.c +++ b/Source/3rdParty/Lua/lmathlib.c @@ -352,7 +352,7 @@ static lua_Number I2d (Rand64 x) { SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG); lua_Number res = (lua_Number)(sx) * scaleFIG; if (sx < 0) - res += 1.0; /* correct the two's complement if negative */ + res += l_mathop(1.0); /* correct the two's complement if negative */ lua_assert(0 <= res && res < 1); return res; } diff --git a/Source/3rdParty/Lua/lstate.c b/Source/3rdParty/Lua/lstate.c index 9a61cd6d5..2ae76d8c8 100644 --- a/Source/3rdParty/Lua/lstate.c +++ b/Source/3rdParty/Lua/lstate.c @@ -66,12 +66,6 @@ void luaE_setdebt (global_State *g, l_obj debt) { } -LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { - UNUSED(L); UNUSED(limit); - return LUAI_MAXCCALLS; /* warning?? */ -} - - CallInfo *luaE_extendCI (lua_State *L) { CallInfo *ci; lua_assert(L->ci->next == NULL); diff --git a/Source/3rdParty/Lua/ltable.c b/Source/3rdParty/Lua/ltable.c index 353e567b0..cb7eb6488 100644 --- a/Source/3rdParty/Lua/ltable.c +++ b/Source/3rdParty/Lua/ltable.c @@ -40,39 +40,48 @@ /* -** Only tables with hash parts larget than LIMFORLAST has a 'lastfree' -** field that optimizes finding a free slot. Smaller tables do a +** Only tables with hash parts larger than 2^LIMFORLAST has a 'lastfree' +** field that optimizes finding a free slot. That field is stored just +** before the array of nodes, in the same block. Smaller tables do a ** complete search when looking for a free slot. */ -#define LLIMFORLAST 2 /* log2 of LIMTFORLAST */ -#define LIMFORLAST twoto(LLIMFORLAST) +#define LIMFORLAST 2 /* log2 of real limit */ /* -** Union to store an int field ensuring that what follows it in -** memory is properly aligned to store a TValue. +** The union 'Limbox' stores 'lastfree' and ensures that what follows it +** is properly aligned to store a Node. */ +typedef struct { Node *dummy; Node follows_pNode; } Limbox_aux; + typedef union { - int lastfree; - char padding[offsetof(struct { int i; TValue v; }, v)]; + Node *lastfree; + char padding[offsetof(Limbox_aux, follows_pNode)]; } Limbox; -#define haslastfree(t) ((t)->lsizenode > LLIMFORLAST) -#define getlastfree(t) (&((cast(Limbox *, (t)->node) - 1)->lastfree)) +#define haslastfree(t) ((t)->lsizenode > LIMFORLAST) +#define getlastfree(t) ((cast(Limbox *, (t)->node) - 1)->lastfree) /* -** MAXABITS is the largest integer such that MAXASIZE fits in an +** MAXABITS is the largest integer such that 2^MAXABITS fits in an ** unsigned int. */ #define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) +/* +** MAXASIZEB is the maximum number of elements in the array part such +** that the size of the array fits in 'size_t'. +*/ +#define MAXASIZEB ((MAX_SIZET/sizeof(ArrayCell)) * NM) + + /* ** MAXASIZE is the maximum size of the array part. It is the minimum -** between 2^MAXABITS and the maximum size that, measured in bytes, -** fits in a 'size_t'. +** between 2^MAXABITS and MAXASIZEB. */ -#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) +#define MAXASIZE \ + (((1u << MAXABITS) < MAXASIZEB) ? (1u << MAXABITS) : cast_uint(MAXASIZEB)) /* ** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a @@ -586,13 +595,13 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) luaG_runerror(L, "table overflow"); size = twoto(lsize); - if (lsize <= LLIMFORLAST) /* no 'lastfree' field? */ + if (lsize <= LIMFORLAST) /* no 'lastfree' field? */ t->node = luaM_newvector(L, size, Node); else { size_t bsize = size * sizeof(Node) + sizeof(Limbox); char *node = luaM_newblock(L, bsize); t->node = cast(Node *, node + sizeof(Limbox)); - *getlastfree(t) = size; /* all positions are free */ + getlastfree(t) = gnode(t, size); /* all positions are free */ } t->lsizenode = cast_byte(lsize); setnodummy(t); @@ -663,6 +672,8 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, Table newt; /* to keep the new hash part */ unsigned int oldasize = setlimittosize(t); ArrayCell *newarray; + if (newasize > MAXASIZE) + luaG_runerror(L, "table overflow"); /* create new hash part with appropriate size into 'newt' */ newt.flags = 0; setnodevector(L, &newt, nhsize); @@ -767,8 +778,8 @@ void luaH_free (lua_State *L, Table *t) { static Node *getfreepos (Table *t) { if (haslastfree(t)) { /* does it have 'lastfree' information? */ /* look for a spot before 'lastfree', updating 'lastfree' */ - while (*getlastfree(t) > 0) { - Node *free = gnode(t, --(*getlastfree(t))); + while (getlastfree(t) > t->node) { + Node *free = --getlastfree(t); if (keyisnil(free)) return free; } @@ -984,7 +995,8 @@ static int finishnodeset (Table *t, const TValue *slot, TValue *val) { } else if (isabstkey(slot)) return HNOTFOUND; /* no slot with that key */ - else return (cast(Node*, slot) - t->node) + HFIRSTNODE; /* node encoded */ + else /* return node encoded */ + return cast_int((cast(Node*, slot) - t->node)) + HFIRSTNODE; } diff --git a/Source/3rdParty/Lua/ltablib.c b/Source/3rdParty/Lua/ltablib.c index c8838963d..2ba31a4fd 100644 --- a/Source/3rdParty/Lua/ltablib.c +++ b/Source/3rdParty/Lua/ltablib.c @@ -59,8 +59,10 @@ static void checktab (lua_State *L, int arg, int what) { static int tcreate (lua_State *L) { - int sizeseq = (int)luaL_checkinteger(L, 1); - int sizerest = (int)luaL_optinteger(L, 2, 0); + lua_Unsigned sizeseq = (lua_Unsigned)luaL_checkinteger(L, 1); + lua_Unsigned sizerest = (lua_Unsigned)luaL_optinteger(L, 2, 0); + luaL_argcheck(L, sizeseq <= UINT_MAX, 1, "out of range"); + luaL_argcheck(L, sizerest <= UINT_MAX, 2, "out of range"); lua_createtable(L, sizeseq, sizerest); return 1; } diff --git a/Source/3rdParty/Lua/lua.h b/Source/3rdParty/Lua/lua.h index b656760dc..bdf06c0ae 100644 --- a/Source/3rdParty/Lua/lua.h +++ b/Source/3rdParty/Lua/lua.h @@ -270,7 +270,7 @@ LUA_API int (lua_rawget) (lua_State *L, int idx); LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); -LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void (lua_createtable) (lua_State *L, unsigned narr, unsigned nrec); LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); @@ -492,7 +492,6 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L); LUA_API int (lua_gethookmask) (lua_State *L); LUA_API int (lua_gethookcount) (lua_State *L); -LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit); struct lua_Debug { int event; diff --git a/Source/3rdParty/Lua/lualib.h b/Source/3rdParty/Lua/lualib.h index e124cf1b8..068f60ab3 100644 --- a/Source/3rdParty/Lua/lualib.h +++ b/Source/3rdParty/Lua/lualib.h @@ -14,11 +14,11 @@ /* version suffix for environment variable names */ #define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR -#define LUA_GK 1 +#define LUA_GLIBK 1 LUAMOD_API int (luaopen_base) (lua_State *L); #define LUA_LOADLIBNAME "package" -#define LUA_LOADLIBK (LUA_GK << 1) +#define LUA_LOADLIBK (LUA_GLIBK << 1) LUAMOD_API int (luaopen_package) (lua_State *L); @@ -56,10 +56,10 @@ LUAMOD_API int (luaopen_utf8) (lua_State *L); /* open selected libraries */ -LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int what); +LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload); /* open all libraries */ -#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0) +#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0) #endif diff --git a/Source/3rdParty/Lua/lundump.c b/Source/3rdParty/Lua/lundump.c index b33258b03..d485f266c 100644 --- a/Source/3rdParty/Lua/lundump.c +++ b/Source/3rdParty/Lua/lundump.c @@ -37,7 +37,7 @@ typedef struct { const char *name; Table *h; /* list for string reuse */ size_t offset; /* current position relative to beginning of dump */ - lua_Integer nstr; /* number of strings in the list */ + lua_Unsigned nstr; /* number of strings in the list */ lu_byte fixed; /* dump is fixed in memory */ } LoadState; @@ -71,10 +71,9 @@ static void loadAlign (LoadState *S, int align) { } -#define getaddr(S,n,t) cast(t *, getaddr_(S,n,sizeof(t))) +#define getaddr(S,n,t) cast(t *, getaddr_(S,(n) * sizeof(t))) -static const void *getaddr_ (LoadState *S, int n, size_t sz) { - size_t size = n * sz; +static const void *getaddr_ (LoadState *S, size_t size) { const void *block = luaZ_getaddr(S->Z, size); S->offset += size; if (block == NULL) @@ -95,8 +94,8 @@ static lu_byte loadByte (LoadState *S) { } -static size_t loadUnsigned (LoadState *S, size_t limit) { - size_t x = 0; +static varint_t loadVarint (LoadState *S, varint_t limit) { + varint_t x = 0; int b; limit >>= 7; do { @@ -104,18 +103,18 @@ static size_t loadUnsigned (LoadState *S, size_t limit) { if (x >= limit) error(S, "integer overflow"); x = (x << 7) | (b & 0x7f); - } while ((b & 0x80) == 0); + } while ((b & 0x80) != 0); return x; } static size_t loadSize (LoadState *S) { - return loadUnsigned(S, MAX_SIZET); + return cast_sizet(loadVarint(S, MAX_SIZET)); } static int loadInt (LoadState *S) { - return cast_int(loadUnsigned(S, INT_MAX)); + return cast_int(loadVarint(S, INT_MAX)); } @@ -149,9 +148,10 @@ static void loadString (LoadState *S, Proto *p, TString **sl) { return; } else if (size == 1) { /* previously saved string? */ - int idx = loadInt(S); /* get its index */ + /* get its index */ + lua_Unsigned idx = cast(lua_Unsigned, loadVarint(S, LUA_MAXUNSIGNED)); TValue stv; - luaH_getint(S->h, idx, &stv); + luaH_getint(S->h, l_castU2S(idx), &stv); /* get its value */ *sl = ts = tsvalue(&stv); luaC_objbarrier(L, p, ts); return; /* do not save it again */ @@ -175,7 +175,7 @@ static void loadString (LoadState *S, Proto *p, TString **sl) { /* add string to list of saved strings */ S->nstr++; setsvalue(L, &sv, ts); - luaH_setint(L, S->h, S->nstr, &sv); + luaH_setint(L, S->h, l_castU2S(S->nstr), &sv); luaC_objbarrierback(L, obj2gco(S->h), ts); } diff --git a/Source/3rdParty/Lua/lundump.h b/Source/3rdParty/Lua/lundump.h index b10307e49..ff66d2e78 100644 --- a/Source/3rdParty/Lua/lundump.h +++ b/Source/3rdParty/Lua/lundump.h @@ -7,6 +7,8 @@ #ifndef lundump_h #define lundump_h +#include + #include "llimits.h" #include "lobject.h" #include "lzio.h" @@ -25,6 +27,19 @@ #define LUAC_FORMAT 0 /* this is the official format */ + +/* +** Type to handle MSB Varint encoding: Try to get the largest unsigned +** integer available. (It was enough to be the largest between size_t and +** lua_Integer, but the C89 preprocessor knows nothing about size_t.) +*/ +#if !defined(LUA_USE_C89) && defined(LLONG_MAX) +typedef unsigned long long varint_t; +#else +typedef unsigned long varint_t; +#endif + + /* load one chunk; from lundump.c */ LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, int fixed); diff --git a/Source/3rdParty/yuescript/yue_compiler.cpp b/Source/3rdParty/yuescript/yue_compiler.cpp index 8583f02ac..c9917337d 100644 --- a/Source/3rdParty/yuescript/yue_compiler.cpp +++ b/Source/3rdParty/yuescript/yue_compiler.cpp @@ -9033,7 +9033,7 @@ class YueCompilerImpl { } #else // YUE_NO_MACRO if (importAllMacro) { - throw CompileError("macro feature not supported"sv, import->target); + throw CompileError("macro feature not supported"sv, importNode->target); } #endif // YUE_NO_MACRO if (newTab->items.empty()) { diff --git a/Source/Common/Utils.cpp b/Source/Common/Utils.cpp index 4317959cc..98c9f2885 100644 --- a/Source/Common/Utils.cpp +++ b/Source/Common/Utils.cpp @@ -76,7 +76,11 @@ std::string Path::concat(const std::list& paths) { fs::path path = paths.front().toString(); for (auto it = ++paths.begin(); it != paths.end(); ++it) { if (it->empty()) continue; - path /= it->toString(); + if (path.empty()) { + path = it->toString(); + } else { + path /= it->toString(); + } } return path.string(); } diff --git a/Source/GUI/ImGuiDora.cpp b/Source/GUI/ImGuiDora.cpp index fe7fa57ee..f2b698f35 100644 --- a/Source/GUI/ImGuiDora.cpp +++ b/Source/GUI/ImGuiDora.cpp @@ -500,8 +500,9 @@ ImGuiDora::ImGuiDora() e->get(name, msg, level, cost); if (name == "Loader"_slice) { const auto& assetPath = SharedContent.getAssetPath(); - if (Slice(msg).left(assetPath.size()) == assetPath) { - msg = Path::concat({"assets"_slice, msg.substr(assetPath.size())}); + auto relative = Path::getRelative(msg, assetPath); + if (!relative.empty() && !relative.starts_with(".."sv)) { + msg = Path::concat({"assets"_slice, relative}); } if (level == 0) _loaderTotalTime += cost; _loaderCosts.push_front({s_cast(_loaderCosts.size()),