commit b5c753ff4de47e077864681b46e93f350cda4f93 Author: Signal Date: Sat Feb 14 12:36:36 2026 -0500 Initial commit. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1625c17 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. \ No newline at end of file diff --git a/game.conf b/game.conf new file mode 100644 index 0000000..aa850be --- /dev/null +++ b/game.conf @@ -0,0 +1,8 @@ +name = firefly +title = Firefly +description = TBD +allowed_mapgens = singlenode +disallowed_mapgens = v5, v7, flat, fractal, valleys, carpathian, v6 +min_minetest_version = 5.10 +disabled_settings = !enable_damage, creative_mode +author = Signal diff --git a/mods/firefly_admin/init.lua b/mods/firefly_admin/init.lua new file mode 100644 index 0000000..14f2227 --- /dev/null +++ b/mods/firefly_admin/init.lua @@ -0,0 +1,7 @@ +local ns = firefly + +-- Determine whether the target player is an admin. +local admin = minetest.settings:get("name") -- Assume this will never be changed live. +function ns.is_admin(name) + return name == admin or name == "singleplayer" and firefly.singleplayer_admin +end diff --git a/mods/firefly_admin/mod.conf b/mods/firefly_admin/mod.conf new file mode 100644 index 0000000..cec7efd --- /dev/null +++ b/mods/firefly_admin/mod.conf @@ -0,0 +1,2 @@ +name = firefly_admin +depends = firefly_base \ No newline at end of file diff --git a/mods/firefly_base/init.lua b/mods/firefly_base/init.lua new file mode 100644 index 0000000..8e6ff04 --- /dev/null +++ b/mods/firefly_base/init.lua @@ -0,0 +1,236 @@ +-- Enable Minetest forever. +minetest = core + +firefly = { + -- Set to `true` to enable testing admin things without having to open a server. + singleplayer_admin = true +} +local ns = firefly + +function include(file) + return dofile(minetest.get_modpath(minetest.get_current_modname()).."/"..file) +end + +function enum(cases) + local out = {} + local i = 0 + for _, x in ipairs(cases) do + out[x] = i + i = i +1 + end + return out +end + +say = minetest.chat_send_all + +function extend(dst, src) + for k, v in pairs(src) do + dst[k] = v + end + return dst +end + +Promise = { + resolve = function(e, ...) + if e.resolved then return end + e.resolved = true + if e._after then e._after(...) end + end, + after = function(e, fn) + e._after = fn + end +} +Promise.__index = Promise +setmetatable(Promise, { + __call = function(_, fn) + local e = {resolved = false} + setmetatable(e, Promise) + fn(function(...) e:resolve(...) end) + return e + end +}) + +EventTarget = { + init = function() + local e = { + listeners = {} + } + return setmetatable(e, EventTarget) + end, + listen = function(e, channel, fn) + if not e.listeners[channel] then e.listeners[channel] = {} end + local l = e.listeners[channel] + l[#l +1] = fn + end, + unlisten = function(e, channel, fn) + if not e.listeners[channel] then return end + local l = e.listeners[channel] + local idx = table.indexof(l, fn) + if idx < 0 then return end + table.remove(l, idx) + end, + dispatch = function(e, channel, ...) + local l = e.listeners[channel] + if not l then return end + for i = 1, #l do + l[i](...) + end + end +} +EventTarget.__index = EventTarget +setmetatable(EventTarget, { + __call = function(_, ...) return EventTarget.init(...) end +}) + +-- HACK: Lookup table for getting a rotation from a +-- facedir (because Minetest doesn't have any way +-- to get this information normally). +local facedir_rotations = { + -- +Y + [0] = vector.new(0, 0, 0), + [1] = vector.new(0, math.pi * 1.5, 0), + [2] = vector.new(0, math.pi * 1.0, 0), + [3] = vector.new(0, math.pi * 0.5, 0), + -- +Z + [4] = vector.new(math.pi * 1.5, 0, 0), + [5] = vector.new(0, math.pi * 1.5, math.pi * 1.5), + [6] = vector.new(math.pi * 0.5, math.pi * 1.0, 0), + [7] = vector.new(0, math.pi * 0.5, math.pi * 0.5), + -- -Z + [8] = vector.new(math.pi * 0.5, 0, 0), + [9] = vector.new(0, math.pi * 1.5, math.pi * 0.5), + [10] = vector.new(math.pi * 1.5, math.pi * 1.0, 0), + [11] = vector.new(0, math.pi * 0.5, math.pi * 1.5), + -- +X + [12] = vector.new(0, 0, math.pi * 0.5), + [13] = vector.new(math.pi * 1.5, math.pi * 1.5, 0), + [14] = vector.new(0, math.pi * 1.0, math.pi * 1.5), + [15] = vector.new(math.pi * 0.5, math.pi * 0.5, 0), + -- -X + [16] = vector.new(0, 0, math.pi * 1.5), + [17] = vector.new(math.pi * 0.5, math.pi * 1.5, 0), + [18] = vector.new(0, math.pi * 1.0, math.pi * 0.5), + [19] = vector.new(math.pi * 1.5, math.pi * 0.5, 0), + -- -Y + [20] = vector.new(0, 0, math.pi * 1.0), + [21] = vector.new(0, math.pi * 0.5, math.pi * 1.0), + [22] = vector.new(0, math.pi * 1.0, math.pi * 1.0), + [23] = vector.new(0, math.pi * 1.5, math.pi * 1.0), +} +function ns.facedir_to_rotation(facedir) + return facedir_rotations[facedir] or minetest.facedir_to_dir(facedir):dir_to_rotation() +end + +function ns.rotate_selectionbox(box, rot) + local a = vector.new(box[1], box[2], box[3]):rotate(rot) + local b = vector.new(box[4], box[5], box[6]):rotate(rot) + return { + a.x, a.y, a.z, + b.x, b.y, b.z + } +end + +function ns.register_item(name, def) + local needs_alias + if not name:find ":" then + def._name = name + name = "firefly:"..name + needs_alias = true + end + + minetest.register_craftitem(":"..name, def) + + if needs_alias then + minetest.register_alias(def._name, name) + end +end + +function ns.solid_color_frames(frames) + local out = "[fill:1x"..#frames..":0,0:#000" + for i, x in ipairs(frames) do + out = out.."^[fill:1x3:0,"..(i -1)..":"..x + end + return out +end + +local digit_widths = { + [0] = 7, + 6, + 7, + 7, + 7, + 7, + 7, + 7, + 7, + 7, +} +function ns.texture_from_number(num) + local w = 0 + local digits = {} + repeat + w = w +1 + digits[w] = num %10 + num = math.floor(num /10) + until num <= 0 + local width = 0 + local out = "" + for i = w, 1, -1 do + out = out..":"..width..",1=firefly_number_"..digits[i]..".png" + width = width +digit_widths[digits[i]] +(i > 1 and 2 or 0) + end + return "[combine:"..width.."x16"..out, width +end + +function ns.read_file(path) + local f = io.open(path) + if not f then return false end + local out = f:read("a") + f:close() + return out +end + +function ns.manhattan_distance(a, b) + return math.abs(b.x -a.x) +math.abs(b.y -a.y) +math.abs(b.z -a.z) +end + +minetest.register_lbm { + name = ":firefly:on_load", + nodenames = {"group:call_on_load"}, + action = function(pos, node) + minetest.registered_nodes[node.name].on_load(pos) + end +} + +ns.timer = EventTarget() + +local last_time = minetest.get_us_time() +minetest.register_globalstep(function() + local time = minetest.get_us_time() + if time -last_time > 1000000 then + ns.timer:dispatch("every_second") + last_time = time + end +end) + +minetest.register_entity(":display", { + initial_properties = { + visual = "sprite", + textures = {"blank.png"}, + pointable = false, + static_save = false + }, + on_activate = function(e) + e.object:set_armor_groups{immortal = 1} + end +}) + + +if minetest.get_modpath("testtools") then + minetest.override_item("testtools:param2tool", { + pointabilities = { + nodes = {["group:everything"] = true}, + objects = {["group:immortal"] = false} + } + }) +end diff --git a/mods/firefly_base/mod.conf b/mods/firefly_base/mod.conf new file mode 100644 index 0000000..b8f20fb --- /dev/null +++ b/mods/firefly_base/mod.conf @@ -0,0 +1 @@ +name = firefly_base \ No newline at end of file diff --git a/mods/firefly_base/textures/firefly_number_0.png b/mods/firefly_base/textures/firefly_number_0.png new file mode 100644 index 0000000..301073d Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_0.png differ diff --git a/mods/firefly_base/textures/firefly_number_1.png b/mods/firefly_base/textures/firefly_number_1.png new file mode 100644 index 0000000..3e5240c Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_1.png differ diff --git a/mods/firefly_base/textures/firefly_number_2.png b/mods/firefly_base/textures/firefly_number_2.png new file mode 100644 index 0000000..e902159 Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_2.png differ diff --git a/mods/firefly_base/textures/firefly_number_3.png b/mods/firefly_base/textures/firefly_number_3.png new file mode 100644 index 0000000..4752c1e Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_3.png differ diff --git a/mods/firefly_base/textures/firefly_number_4.png b/mods/firefly_base/textures/firefly_number_4.png new file mode 100644 index 0000000..a3c6190 Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_4.png differ diff --git a/mods/firefly_base/textures/firefly_number_5.png b/mods/firefly_base/textures/firefly_number_5.png new file mode 100644 index 0000000..e4a0000 Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_5.png differ diff --git a/mods/firefly_base/textures/firefly_number_6.png b/mods/firefly_base/textures/firefly_number_6.png new file mode 100644 index 0000000..d40dc1c Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_6.png differ diff --git a/mods/firefly_base/textures/firefly_number_7.png b/mods/firefly_base/textures/firefly_number_7.png new file mode 100644 index 0000000..49ddb4d Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_7.png differ diff --git a/mods/firefly_base/textures/firefly_number_8.png b/mods/firefly_base/textures/firefly_number_8.png new file mode 100644 index 0000000..ccc4f0a Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_8.png differ diff --git a/mods/firefly_base/textures/firefly_number_9.png b/mods/firefly_base/textures/firefly_number_9.png new file mode 100644 index 0000000..dbd7dec Binary files /dev/null and b/mods/firefly_base/textures/firefly_number_9.png differ diff --git a/mods/firefly_characters/init.lua b/mods/firefly_characters/init.lua new file mode 100644 index 0000000..e69de29 diff --git a/mods/firefly_characters/mod.conf b/mods/firefly_characters/mod.conf new file mode 100644 index 0000000..f43d8cb --- /dev/null +++ b/mods/firefly_characters/mod.conf @@ -0,0 +1,2 @@ +name = firefly_characters +depends = firefly_player \ No newline at end of file diff --git a/mods/firefly_characters/models/firefly_character.gltf b/mods/firefly_characters/models/firefly_character.gltf new file mode 100644 index 0000000..73544ac --- /dev/null +++ b/mods/firefly_characters/models/firefly_character.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[24],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"Head","mesh":0},{"name":"Hat Layer","mesh":1},{"translation":[0,15,0],"name":"Head","children":[0,1]},{"translation":[0,-15,0],"name":"Body","mesh":2},{"translation":[0,-15,0],"name":"Body Layer","mesh":3},{"translation":[0,15,0],"name":"Body","children":[3,4]},{"translation":[-3.125,-13.75,0],"name":"Right Arm Layer","mesh":4},{"translation":[-3.125,-13.75,0],"name":"Right Arm","mesh":5},{"translation":[3.125,13.75,0],"name":"RightArm","children":[6,7]},{"translation":[3.125,-13.75,0],"name":"Left Arm","mesh":6},{"translation":[3.125,-13.75,0],"name":"Left Arm Layer","mesh":7},{"translation":[-3.125,13.75,0],"name":"LeftArm","children":[9,10]},{"name":"Torso","children":[2,5,8,11]},{"translation":[-1.1875,-7.5,0],"name":"Right Leg","mesh":8},{"translation":[-1.1875,-7.5,0],"name":"Right Leg Layer","mesh":9},{"translation":[-1.25,-0.625,0.9375],"name":"Right Leg","mesh":10},{"translation":[0.06250000000000006,-6.875,-0.9375],"name":"RightLegFoot","children":[15]},{"translation":[1.1875,7.5,0],"name":"RightLeg","children":[13,14,16]},{"translation":[1.1875,-7.5,0],"name":"Left Leg","mesh":11},{"translation":[1.1875,-7.5,0],"name":"Left Leg Layer","mesh":12},{"translation":[1.25,-0.625,0.9375],"name":"Left Leg","mesh":13},{"translation":[-0.06250000000000006,-6.875,-0.9375],"name":"LeftLegFoot","children":[20]},{"translation":[-1.1875,7.5,0],"name":"LeftLeg","children":[18,19,21]},{"name":"root","children":[12,17,22]},{"children":[23]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":3360,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3648,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3936,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4128,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":4200,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4488,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4776,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4968,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5040,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5328,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5616,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":5808,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5880,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6168,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6456,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":6648,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":6720,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7008,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7296,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":7488,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":7560,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7848,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8136,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":8328,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":8400,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8688,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8976,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":9168,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":9240,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9528,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9816,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10008,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10080,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10368,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10656,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10848,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10920,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11208,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11496,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":11688,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":11760,"byteLength":20},{"buffer":0,"byteOffset":11780,"byteLength":80},{"buffer":0,"byteOffset":11860,"byteLength":36},{"buffer":0,"byteOffset":11896,"byteLength":108},{"buffer":0,"byteOffset":12004,"byteLength":20},{"buffer":0,"byteOffset":12024,"byteLength":80},{"buffer":0,"byteOffset":12104,"byteLength":36},{"buffer":0,"byteOffset":12140,"byteLength":144},{"buffer":0,"byteOffset":12284,"byteLength":36},{"buffer":0,"byteOffset":12320,"byteLength":144},{"buffer":0,"byteOffset":12464,"byteLength":36},{"buffer":0,"byteOffset":12500,"byteLength":144},{"buffer":0,"byteOffset":12644,"byteLength":36},{"buffer":0,"byteOffset":12680,"byteLength":144},{"buffer":0,"byteOffset":12824,"byteLength":36},{"buffer":0,"byteOffset":12860,"byteLength":144},{"buffer":0,"byteOffset":13004,"byteLength":36},{"buffer":0,"byteOffset":13040,"byteLength":144}],"buffers":[{"byteLength":13184,"uri":"data:application/octet-stream;base64,AAAgQAAAoEAAACBAAAAgQAAAoEAAACDAAAAgQAAAAAAAACBAAAAgQAAAAAAAACDAAAAgwAAAoEAAACDAAAAgwAAAoEAAACBAAAAgwAAAAAAAACDAAAAgwAAAAAAAACBAAAAgwAAAoEAAACDAAAAgQAAAoEAAACDAAAAgwAAAoEAAACBAAAAgQAAAoEAAACBAAAAgwAAAAAAAACBAAAAgQAAAAAAAACBAAAAgwAAAAAAAACDAAAAgQAAAAAAAACDAAAAgwAAAoEAAACBAAAAgQAAAoEAAACBAAAAgwAAAAAAAACBAAAAgQAAAAAAAACBAAAAgQAAAoEAAACDAAAAgwAAAoEAAACDAAAAgQAAAAAAAACDAAAAgwAAAAAAAACDAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAOQBAAD4AgP89AEAAPgAAgDkAwH8+AID/PQDAfz4AIIA+AEAAPgDgvz4AQAA+ACCAPgDAfz4A4L8+AMB/PgDAfz4AgP89AEAAPgCA/z0AwH8+AACAOQBAAD4AAIA5AOC/PgAAgDkAIIA+AACAOQDgvz4AgP89ACCAPgCA/z0AIMA+AEAAPgDg/z4AQAA+ACDAPgDAfz4A4P8+AMB/PgBAAD4AQAA+AMB/PgBAAD4AQAA+AMB/PgDAfz4AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAA0QAAAqkAAADRAAAA0QAAAqkAAADTAAAA0QAAAoL4AADRAAAA0QAAAoL4AADTAAAA0wAAAqkAAADTAAAA0wAAAqkAAADRAAAA0wAAAoL4AADTAAAA0wAAAoL4AADRAAAA0wAAAqkAAADTAAAA0QAAAqkAAADTAAAA0wAAAqkAAADRAAAA0QAAAqkAAADRAAAA0wAAAoL4AADRAAAA0QAAAoL4AADRAAAA0wAAAoL4AADTAAAA0QAAAoL4AADTAAAA0wAAAqkAAADRAAAA0QAAAqkAAADRAAAA0wAAAoL4AADRAAAA0QAAAoL4AADRAAAA0QAAAqkAAADTAAAA0wAAAqkAAADTAAAA0QAAAoL4AADTAAAA0wAAAoL4AADTAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAAPwBAAD4A8B8/AEAAPgAQAD8AwH8+APAfPwDAfz4AEEA/AEAAPgDwXz8AQAA+ABBAPwDAfz4A8F8/AMB/PgDwPz8AgP89ABAgPwCA/z0A8D8/AACAOQAQID8AAIA5APBfPwAAgDkAEEA/AACAOQDwXz8AgP89ABBAPwCA/z0AEGA/AEAAPgDwfz8AQAA+ABBgPwDAfz4A8H8/AMB/PgAQID8AQAA+APA/PwBAAD4AECA/AMB/PgDwPz8AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAgQAAAcEEAAHA/AAAgQAAAcEEAAHC/AAAgQAAA8EAAAHA/AAAgQAAA8EAAAHC/AAAgwAAAcEEAAHC/AAAgwAAAcEEAAHA/AAAgwAAA8EAAAHC/AAAgwAAA8EAAAHA/AAAgwAAAcEEAAHC/AAAgQAAAcEEAAHC/AAAgwAAAcEEAAHA/AAAgQAAAcEEAAHA/AAAgwAAA8EAAAHA/AAAgQAAA8EAAAHA/AAAgwAAA8EAAAHC/AAAgQAAA8EAAAHC/AAAgwAAAcEEAAHA/AAAgQAAAcEEAAHA/AAAgwAAA8EAAAHA/AAAgQAAA8EAAAHA/AAAgQAAAcEEAAHC/AAAgwAAAcEEAAHC/AAAgQAAA8EAAAHC/AAAgwAAA8EAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACIPgAAoD4AAKA+AACgPgAAiD4AAAA/AACgPgAAAD8AAOA+AACgPgAA+D4AAKA+AADgPgAAAD8AAPg+AAAAPwAA4D4AAKA+AACgPgAAoD4AAOA+AACIPgAAoD4AAIg+AAAQPwAAiD4AAOA+AACIPgAAED8AAKA+AADgPgAAoD4AAAA/AACgPgAAID8AAKA+AAAAPwAAAD8AACA/AAAAPwAAoD4AAKA+AADgPgAAoD4AAKA+AAAAPwAA4D4AAAA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAqQACAckEAAIw/AAAqQACAckEAAIy/AAAqQAAA60AAAIw/AAAqQAAA60AAAIy/AAAqwACAckEAAIy/AAAqwACAckEAAIw/AAAqwAAA60AAAIy/AAAqwAAA60AAAIw/AAAqwACAckEAAIy/AAAqQACAckEAAIy/AAAqwACAckEAAIw/AAAqQACAckEAAIw/AAAqwAAA60AAAIw/AAAqQAAA60AAAIw/AAAqwAAA60AAAIy/AAAqQAAA60AAAIy/AAAqwACAckEAAIw/AAAqQACAckEAAIw/AAAqwAAA60AAAIw/AAAqQAAA60AAAIw/AAAqQACAckEAAIy/AAAqwACAckEAAIy/AAAqQAAA60AAAIy/AAAqwAAA60AAAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACIPgAAED8AAKA+AAAQPwAAiD4AAEA/AACgPgAAQD8AAOA+AAAQPwAA+D4AABA/AADgPgAAQD8AAPg+AABAPwAA4D4AABA/AACgPgAAED8AAOA+AAAEPwAAoD4AAAQ/AAAQPwAABD8AAOA+AAAEPwAAED8AABA/AADgPgAAED8AAAA/AAAQPwAAID8AABA/AAAAPwAAQD8AACA/AABAPwAAoD4AABA/AADgPgAAED8AAKA+AABAPwAA4D4AAEA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACRQACAckEAAIw/AACRQACAckEAAIy/AACRQAAA60AAAIw/AACRQAAA60AAAIy/AAAWQACAckEAAIy/AAAWQACAckEAAIw/AAAWQAAA60AAAIy/AAAWQAAA60AAAIw/AAAWQACAckEAAIy/AACRQACAckEAAIy/AAAWQACAckEAAIw/AACRQACAckEAAIw/AAAWQAAA60AAAIw/AACRQAAA60AAAIw/AAAWQAAA60AAAIy/AACRQAAA60AAAIy/AAAWQACAckEAAIw/AACRQACAckEAAIw/AAAWQAAA60AAAIw/AACRQAAA60AAAIw/AACRQACAckEAAIy/AAAWQACAckEAAIy/AACRQAAA60AAAIy/AAAWQAAA60AAAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAkPwAAED8AADA/AAAQPwAAJD8AAEA/AAAwPwAAQD8AAEA/AAAQPwAATD8AABA/AABAPwAAQD8AAEw/AABAPwAAPD8AABA/AAAwPwAAED8AADw/AAAEPwAAMD8AAAQ/AABIPwAABD8AADw/AAAEPwAASD8AABA/AAA8PwAAED8AAEw/AAAQPwAAWD8AABA/AABMPwAAQD8AAFg/AABAPwAAMD8AABA/AAA8PwAAED8AADA/AABAPwAAPD8AAEA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACMQAAAcEEAAHA/AACMQAAAcEEAAHC/AACMQAAA8EAAAHA/AACMQAAA8EAAAHC/AAAgQAAAcEEAAHC/AAAgQAAAcEEAAHA/AAAgQAAA8EAAAHC/AAAgQAAA8EAAAHA/AAAgQAAAcEEAAHC/AACMQAAAcEEAAHC/AAAgQAAAcEEAAHA/AACMQAAAcEEAAHA/AAAgQAAA8EAAAHA/AACMQAAA8EAAAHA/AAAgQAAA8EAAAHC/AACMQAAA8EAAAHC/AAAgQAAAcEEAAHA/AACMQAAAcEEAAHA/AAAgQAAA8EAAAHA/AACMQAAA8EAAAHA/AACMQAAAcEEAAHC/AAAgQAAAcEEAAHC/AACMQAAA8EAAAHC/AAAgQAAA8EAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAkPwAgoD4A8C8/ACCgPgAQJD8A4P8+APAvPwDg/z4AEDw/ACCgPgDwRz8AIKA+ABA8PwDg/z4A8Ec/AOD/PgDwOz8A4J8+ABAwPwDgnz4A8Ds/ACCIPgAQMD8AIIg+APBHPwAgiD4AEDw/ACCIPgDwRz8A4J8+ABA8PwDgnz4AEEg/ACCgPgDwUz8AIKA+ABBIPwDg/z4A8FM/AOD/PgAQMD8AIKA+APA7PwAgoD4AEDA/AOD/PgDwOz8A4P8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAgwAAAcEEAAHA/AAAgwAAAcEEAAHC/AAAgwAAA8EAAAHA/AAAgwAAA8EAAAHC/AACMwAAAcEEAAHC/AACMwAAAcEEAAHA/AACMwAAA8EAAAHC/AACMwAAA8EAAAHA/AACMwAAAcEEAAHC/AAAgwAAAcEEAAHC/AACMwAAAcEEAAHA/AAAgwAAAcEEAAHA/AACMwAAA8EAAAHA/AAAgwAAA8EAAAHA/AACMwAAA8EAAAHC/AAAgwAAA8EAAAHC/AACMwAAAcEEAAHA/AAAgwAAAcEEAAHA/AACMwAAA8EAAAHA/AAAgwAAA8EAAAHA/AAAgwAAAcEEAAHC/AACMwAAAcEEAAHC/AAAgwAAA8EAAAHC/AACMwAAA8EAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAEPwAQUD8A8A8/ABBQPwAQBD8A8H8/APAPPwDwfz8AEBw/ABBQPwDwJz8AEFA/ABAcPwDwfz8A8Cc/APB/PwDwGz8A8E8/ABAQPwDwTz8A8Bs/ABBEPwAQED8AEEQ/APAnPwAQRD8AEBw/ABBEPwDwJz8A8E8/ABAcPwDwTz8AECg/ABBQPwDwMz8AEFA/ABAoPwDwfz8A8DM/APB/PwAQED8AEFA/APAbPwAQUD8AEBA/APB/PwDwGz8A8H8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAWwACAckEAAIw/AAAWwACAckEAAIy/AAAWwAAA60AAAIw/AAAWwAAA60AAAIy/AACRwACAckEAAIy/AACRwACAckEAAIw/AACRwAAA60AAAIy/AACRwAAA60AAAIw/AACRwACAckEAAIy/AAAWwACAckEAAIy/AACRwACAckEAAIw/AAAWwACAckEAAIw/AACRwAAA60AAAIw/AAAWwAAA60AAAIw/AACRwAAA60AAAIy/AAAWwAAA60AAAIy/AACRwACAckEAAIw/AAAWwACAckEAAIw/AACRwAAA60AAAIw/AAAWwAAA60AAAIw/AAAWwACAckEAAIy/AACRwACAckEAAIy/AAAWwAAA60AAAIy/AACRwAAA60AAAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AABAPwAAUD8AAEw/AABQPwAAQD8AAIA/AABMPwAAgD8AAFw/AABQPwAAaD8AAFA/AABcPwAAgD8AAGg/AACAPwAAXD8AAFA/AABQPwAAUD8AAFw/AABEPwAAUD8AAEQ/AABoPwAARD8AAFw/AABEPwAAaD8AAFA/AABcPwAAUD8AAGw/AABQPwAAeD8AAFA/AABsPwAAgD8AAHg/AACAPwAAUD8AAFA/AABcPwAAUD8AAFA/AACAPwAAXD8AAIA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAcQAAA8EAAAHA/AAAcQAAA8EAAAHC/AAAcQAAAAAAAAHA/AAAcQAAAAAAAAHC/AACAvQAA8EAAAHC/AACAvQAA8EAAAHA/AACAvQAAAAAAAHC/AACAvQAAAAAAAHA/AACAvQAA8EAAAHC/AAAcQAAA8EAAAHC/AACAvQAA8EAAAHA/AAAcQAAA8EAAAHA/AACAvQAAAAAAAHA/AAAcQAAAAAAAAHA/AACAvQAAAAAAAHC/AAAcQAAAAAAAAHC/AACAvQAA8EAAAHA/AAAcQAAA8EAAAHA/AACAvQAAAAAAAHA/AAAcQAAAAAAAAHA/AAAcQAAA8EAAAHC/AACAvQAA8EAAAHC/AAAcQAAAAAAAAHC/AACAvQAAAAAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPAAAoD4AAIA9AACgPgAAgDwAAAA/AACAPQAAAD8AAAA+AACgPgAAMD4AAKA+AAAAPgAAAD8AADA+AAAAPwAAAD4AAKA+AACAPQAAoD4AAAA+AACIPgAAgD0AAIg+AABAPgAAiD4AAAA+AACIPgAAQD4AAKA+AAAAPgAAoD4AAEA+AACgPgAAgD4AAKA+AABAPgAAAD8AAIA+AAAAPwAAgD0AAKA+AAAAPgAAoD4AAIA9AAAAPwAAAD4AAAA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAmQAAA9UAAAIw/AAAmQAAA9UAAAIy/AAAmQAAAIL4AAIw/AAAmQAAAIL4AAIy/AABgvgAA9UAAAIy/AABgvgAA9UAAAIw/AABgvgAAIL4AAIy/AABgvgAAIL4AAIw/AABgvgAA9UAAAIy/AAAmQAAA9UAAAIy/AABgvgAA9UAAAIw/AAAmQAAA9UAAAIw/AABgvgAAIL4AAIw/AAAmQAAAIL4AAIw/AABgvgAAIL4AAIy/AAAmQAAAIL4AAIy/AABgvgAA9UAAAIw/AAAmQAAA9UAAAIw/AABgvgAAIL4AAIw/AAAmQAAAIL4AAIw/AAAmQAAA9UAAAIy/AABgvgAA9UAAAIy/AAAmQAAAIL4AAIy/AABgvgAAIL4AAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACCPAAQED8AAH89ABAQPwAAgjwA8D8/AAB/PQDwPz8AQAA+ABAQPwDALz4AEBA/AEAAPgDwPz8AwC8+APA/PwCA/z0A8A8/AICAPQDwDz8AgP89ABAEPwCAgD0AEAQ/AMA/PgAQBD8AQAA+ABAEPwDAPz4A8A8/AEAAPgDwDz8AQDA+ABAQPwDAbz4AEBA/AEAwPgDwPz8AwG8+APA/PwCAgD0AEBA/AID/PQAQED8AgIA9APA/PwCA/z0A8D8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAcQAAAoD8AAHC/AAAcQAAAoD8AADTAAAAcQAAAAAAAAHC/AAAcQAAAAAAAADTAAACAvQAAoD8AADTAAACAvQAAoD8AAHC/AACAvQAAAAAAADTAAACAvQAAAAAAAHC/AACAvQAAoD8AADTAAAAcQAAAoD8AADTAAACAvQAAoD8AAHC/AAAcQAAAoD8AAHC/AACAvQAAAAAAAHC/AAAcQAAAAAAAAHC/AACAvQAAAAAAADTAAAAcQAAAAAAAADTAAACAvQAAoD8AAHC/AAAcQAAAoD8AAHC/AACAvQAAAAAAAHC/AAAcQAAAAAAAAHC/AAAcQAAAoD8AADTAAACAvQAAoD8AADTAAAAcQAAAAAAAADTAAACAvQAAAAAAADTAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABBIPwAQAD8A8FM/ABAAPwAQSD8A8Ac/APBTPwDwBz8AEGQ/ABAAPwDwbz8AEAA/ABBkPwDwBz8A8G8/APAHPwDwYz8A4P8+ABBUPwDg/z4A8GM/ACDoPgAQVD8AIOg+APBzPwAg6D4AEGQ/ACDoPgDwcz8A4P8+ABBkPwDg/z4AEHA/ABAAPwDwfz8AEAA/ABBwPwDwBz8A8H8/APAHPwAQVD8AEAA/APBjPwAQAD8AEFQ/APAHPwDwYz8A8Ac/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACAPQAA8EAAAHA/AACAPQAA8EAAAHC/AACAPQAAAAAAAHA/AACAPQAAAAAAAHC/AAAcwAAA8EAAAHC/AAAcwAAA8EAAAHA/AAAcwAAAAAAAAHC/AAAcwAAAAAAAAHA/AAAcwAAA8EAAAHC/AACAPQAA8EAAAHC/AAAcwAAA8EAAAHA/AACAPQAA8EAAAHA/AAAcwAAAAAAAAHA/AACAPQAAAAAAAHA/AAAcwAAAAAAAAHC/AACAPQAAAAAAAHC/AAAcwAAA8EAAAHA/AACAPQAA8EAAAHA/AAAcwAAAAAAAAHA/AACAPQAAAAAAAHA/AACAPQAA8EAAAHC/AAAcwAAA8EAAAHC/AACAPQAAAAAAAHC/AAAcwAAAAAAAAHC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACIPgAAUD8AAKA+AABQPwAAiD4AAIA/AACgPgAAgD8AAMA+AABQPwAA2D4AAFA/AADAPgAAgD8AANg+AACAPwAAwD4AAFA/AACgPgAAUD8AAMA+AABEPwAAoD4AAEQ/AADgPgAARD8AAMA+AABEPwAA4D4AAFA/AADAPgAAUD8AAOA+AABQPwAAAD8AAFA/AADgPgAAgD8AAAA/AACAPwAAoD4AAFA/AADAPgAAUD8AAKA+AACAPwAAwD4AAIA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgPgAA9UAAAIw/AABgPgAA9UAAAIy/AABgPgAAIL4AAIw/AABgPgAAIL4AAIy/AAAmwAAA9UAAAIy/AAAmwAAA9UAAAIw/AAAmwAAAIL4AAIy/AAAmwAAAIL4AAIw/AAAmwAAA9UAAAIy/AABgPgAA9UAAAIy/AAAmwAAA9UAAAIw/AABgPgAA9UAAAIw/AAAmwAAAIL4AAIw/AABgPgAAIL4AAIw/AAAmwAAAIL4AAIy/AABgPgAAIL4AAIy/AAAmwAAA9UAAAIw/AABgPgAA9UAAAIw/AAAmwAAAIL4AAIw/AABgPgAAIL4AAIw/AABgPgAA9UAAAIy/AAAmwAAA9UAAAIy/AABgPgAAIL4AAIy/AAAmwAAAIL4AAIy/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACCPAAQUD8AAH89ABBQPwAAgjwA8H8/AAB/PQDwfz8AQAA+ABBQPwDALz4AEFA/AEAAPgDwfz8AwC8+APB/PwCA/z0A8E8/AICAPQDwTz8AgP89ABBEPwCAgD0AEEQ/AMA/PgAQRD8AQAA+ABBEPwDAPz4A8E8/AEAAPgDwTz8AQDA+ABBQPwDAbz4AEFA/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"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[2.5,5,2.5],"min":[-2.5,0,-2.5],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.499755859375,0.249755859375],"min":[0.000244140625,0.000244140625],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[2.8125,5.3125,2.8125],"min":[-2.8125,-0.3125,-2.8125],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.999755859375,0.249755859375],"min":[0.500244140625,0.000244140625],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[2.5,15,0.9375],"min":[-2.5,7.5,-0.9375],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.625,0.5],"min":[0.265625,0.265625],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":12,"componentType":5126,"count":24,"max":[2.65625,15.15625,1.09375],"min":[-2.65625,7.34375,-1.09375],"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":14,"componentType":5126,"count":24,"max":[0.625,0.75],"min":[0.265625,0.515625],"type":"VEC2"},{"bufferView":15,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":16,"componentType":5126,"count":24,"max":[4.53125,15.15625,1.09375],"min":[2.34375,7.34375,-1.09375],"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":18,"componentType":5126,"count":24,"max":[0.84375,0.75],"min":[0.640625,0.515625],"type":"VEC2"},{"bufferView":19,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":20,"componentType":5126,"count":24,"max":[4.375,15,0.9375],"min":[2.5,7.5,-0.9375],"type":"VEC3"},{"bufferView":21,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":22,"componentType":5126,"count":24,"max":[0.827880859375,0.499755859375],"min":[0.640869140625,0.265869140625],"type":"VEC2"},{"bufferView":23,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":24,"componentType":5126,"count":24,"max":[-2.5,15,0.9375],"min":[-4.375,7.5,-0.9375],"type":"VEC3"},{"bufferView":25,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":26,"componentType":5126,"count":24,"max":[0.702880859375,0.999755859375],"min":[0.515869140625,0.765869140625],"type":"VEC2"},{"bufferView":27,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":28,"componentType":5126,"count":24,"max":[-2.34375,15.15625,1.09375],"min":[-4.53125,7.34375,-1.09375],"type":"VEC3"},{"bufferView":29,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":30,"componentType":5126,"count":24,"max":[0.96875,1],"min":[0.75,0.765625],"type":"VEC2"},{"bufferView":31,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":32,"componentType":5126,"count":24,"max":[2.4375,7.5,0.9375],"min":[-0.0625,0,-0.9375],"type":"VEC3"},{"bufferView":33,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":34,"componentType":5126,"count":24,"max":[0.25,0.5],"min":[0.015625,0.265625],"type":"VEC2"},{"bufferView":35,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":36,"componentType":5126,"count":24,"max":[2.59375,7.65625,1.09375],"min":[-0.21875,-0.15625,-1.09375],"type":"VEC3"},{"bufferView":37,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":38,"componentType":5126,"count":24,"max":[0.234130859375,0.749755859375],"min":[0.015869140625,0.515869140625],"type":"VEC2"},{"bufferView":39,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":40,"componentType":5126,"count":24,"max":[2.4375,1.25,-0.9375],"min":[-0.0625,0,-2.8125],"type":"VEC3"},{"bufferView":41,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":42,"componentType":5126,"count":24,"max":[0.999755859375,0.531005859375],"min":[0.781494140625,0.453369140625],"type":"VEC2"},{"bufferView":43,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":44,"componentType":5126,"count":24,"max":[0.0625,7.5,0.9375],"min":[-2.4375,0,-0.9375],"type":"VEC3"},{"bufferView":45,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":46,"componentType":5126,"count":24,"max":[0.5,1],"min":[0.265625,0.765625],"type":"VEC2"},{"bufferView":47,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":48,"componentType":5126,"count":24,"max":[0.21875,7.65625,1.09375],"min":[-2.59375,-0.15625,-1.09375],"type":"VEC3"},{"bufferView":49,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":50,"componentType":5126,"count":24,"max":[0.234130859375,0.999755859375],"min":[0.015869140625,0.765869140625],"type":"VEC2"},{"bufferView":51,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":52,"componentType":5126,"count":24,"max":[0.0625,1.25,-0.9375],"min":[-2.4375,0,-2.8125],"type":"VEC3"},{"bufferView":53,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":54,"componentType":5126,"count":24,"max":[0.953125,0.421875],"min":[0.828125,0.3125],"type":"VEC2"},{"bufferView":55,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":56,"componentType":5126,"count":5,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":57,"componentType":5126,"count":5,"max":[0,0,0,1],"min":[0,0,0,1],"type":"VEC4"},{"bufferView":58,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":59,"componentType":5126,"count":9,"max":[0,0,0],"min":[0,-1.25,0],"type":"VEC3"},{"bufferView":60,"componentType":5126,"count":5,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":61,"componentType":5126,"count":5,"max":[0,0,0,1],"min":[0,0,0,1],"type":"VEC4"},{"bufferView":62,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":63,"componentType":5126,"count":9,"max":[0.4617486000061035,0,0,1],"min":[-0.4617486000061035,0,0,0.8870108127593994],"type":"VEC4"},{"bufferView":64,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":65,"componentType":5126,"count":9,"max":[0.4617486000061035,0,0,1],"min":[-0.4617486000061035,0,0,0.8870108127593994],"type":"VEC4"},{"bufferView":66,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":67,"componentType":5126,"count":9,"max":[0.4226182699203491,0,0,1],"min":[-0.4226182699203491,0,0,0.9063078165054321],"type":"VEC4"},{"bufferView":68,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":69,"componentType":5126,"count":9,"max":[0.15212339162826538,0,0,1],"min":[-0.08715574443340302,0,0,0.9883615374565125],"type":"VEC4"},{"bufferView":70,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":71,"componentType":5126,"count":9,"max":[0.4226182699203491,0,0,1],"min":[-0.4226182699203491,0,0,0.9063078165054321],"type":"VEC4"},{"bufferView":72,"componentType":5126,"count":9,"max":[2],"min":[0],"type":"SCALAR"},{"bufferView":73,"componentType":5126,"count":9,"max":[0.15212339162826538,0,0,1],"min":[-0.08715574443340302,0,0,0.9883615374565125],"type":"VEC4"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"artifact_key.png"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"firefly_character.png","uri":"firefly_character.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":12,"NORMAL":13,"TEXCOORD_0":14},"indices":15,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":16,"NORMAL":17,"TEXCOORD_0":18},"indices":19,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":20,"NORMAL":21,"TEXCOORD_0":22},"indices":23,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":24,"NORMAL":25,"TEXCOORD_0":26},"indices":27,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":28,"NORMAL":29,"TEXCOORD_0":30},"indices":31,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":32,"NORMAL":33,"TEXCOORD_0":34},"indices":35,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":36,"NORMAL":37,"TEXCOORD_0":38},"indices":39,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":40,"NORMAL":41,"TEXCOORD_0":42},"indices":43,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":44,"NORMAL":45,"TEXCOORD_0":46},"indices":47,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":48,"NORMAL":49,"TEXCOORD_0":50},"indices":51,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":52,"NORMAL":53,"TEXCOORD_0":54},"indices":55,"material":0}]}],"animations":[{"name":"animation","samplers":[{"input":56,"output":57,"interpolation":"LINEAR"},{"input":58,"output":59,"interpolation":"LINEAR"},{"input":60,"output":61,"interpolation":"LINEAR"},{"input":62,"output":63,"interpolation":"LINEAR"},{"input":64,"output":65,"interpolation":"LINEAR"},{"input":66,"output":67,"interpolation":"LINEAR"},{"input":68,"output":69,"interpolation":"LINEAR"},{"input":70,"output":71,"interpolation":"LINEAR"},{"input":72,"output":73,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":23,"path":"rotation"}},{"sampler":1,"target":{"node":23,"path":"translation"}},{"sampler":2,"target":{"node":5,"path":"rotation"}},{"sampler":3,"target":{"node":8,"path":"rotation"}},{"sampler":4,"target":{"node":11,"path":"rotation"}},{"sampler":5,"target":{"node":17,"path":"rotation"}},{"sampler":6,"target":{"node":16,"path":"rotation"}},{"sampler":7,"target":{"node":22,"path":"rotation"}},{"sampler":8,"target":{"node":21,"path":"rotation"}}]}]} \ No newline at end of file diff --git a/mods/firefly_characters/models/firefly_head.gltf b/mods/firefly_characters/models/firefly_head.gltf new file mode 100644 index 0000000..aac39fd --- /dev/null +++ b/mods/firefly_characters/models/firefly_head.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[2],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[0,-2.5,0],"name":"Head","mesh":0},{"translation":[0,-2.5,0],"name":"Hat Layer","mesh":1},{"children":[0,1]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963}],"buffers":[{"byteLength":1680,"uri":"data:application/octet-stream;base64,AAAgQAAAoEAAACBAAAAgQAAAoEAAACDAAAAgQAAAAAAAACBAAAAgQAAAAAAAACDAAAAgwAAAoEAAACDAAAAgwAAAoEAAACBAAAAgwAAAAAAAACDAAAAgwAAAAAAAACBAAAAgwAAAoEAAACDAAAAgQAAAoEAAACDAAAAgwAAAoEAAACBAAAAgQAAAoEAAACBAAAAgwAAAAAAAACBAAAAgQAAAAAAAACBAAAAgwAAAAAAAACDAAAAgQAAAAAAAACDAAAAgwAAAoEAAACBAAAAgQAAAoEAAACBAAAAgwAAAAAAAACBAAAAgQAAAAAAAACBAAAAgQAAAoEAAACDAAAAgwAAAoEAAACDAAAAgQAAAAAAAACDAAAAgwAAAAAAAACDAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAOQBAAD4AgP89AEAAPgAAgDkAwH8+AID/PQDAfz4AIIA+AEAAPgDgvz4AQAA+ACCAPgDAfz4A4L8+AMB/PgDAfz4AgP89AEAAPgCA/z0AwH8+AACAOQBAAD4AAIA5AOC/PgAAgDkAIIA+AACAOQDgvz4AgP89ACCAPgCA/z0AIMA+AEAAPgDg/z4AQAA+ACDAPgDAfz4A4P8+AMB/PgBAAD4AQAA+AMB/PgBAAD4AQAA+AMB/PgDAfz4AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAA0QAAAqkAAADRAAAA0QAAAqkAAADTAAAA0QAAAoL4AADRAAAA0QAAAoL4AADTAAAA0wAAAqkAAADTAAAA0wAAAqkAAADRAAAA0wAAAoL4AADTAAAA0wAAAoL4AADRAAAA0wAAAqkAAADTAAAA0QAAAqkAAADTAAAA0wAAAqkAAADRAAAA0QAAAqkAAADRAAAA0wAAAoL4AADRAAAA0QAAAoL4AADRAAAA0wAAAoL4AADTAAAA0QAAAoL4AADTAAAA0wAAAqkAAADRAAAA0QAAAqkAAADRAAAA0wAAAoL4AADRAAAA0QAAAoL4AADRAAAA0QAAAqkAAADTAAAA0wAAAqkAAADTAAAA0QAAAoL4AADTAAAA0wAAAoL4AADTAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ABAAPwBAAD4A8B8/AEAAPgAQAD8AwH8+APAfPwDAfz4AEEA/AEAAPgDwXz8AQAA+ABBAPwDAfz4A8F8/AMB/PgDwPz8AgP89ABAgPwCA/z0A8D8/AACAOQAQID8AAIA5APBfPwAAgDkAEEA/AACAOQDwXz8AgP89ABBAPwCA/z0AEGA/AEAAPgDwfz8AQAA+ABBgPwDAfz4A8H8/AMB/PgAQID8AQAA+APA/PwBAAD4AECA/AMB/PgDwPz8AwH8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[2.5,5,2.5],"min":[-2.5,0,-2.5],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.499755859375,0.249755859375],"min":[0.000244140625,0.000244140625],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[2.8125,5.3125,2.8125],"min":[-2.8125,-0.3125,-2.8125],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.999755859375,0.249755859375],"min":[0.500244140625,0.000244140625],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"artifact_key.png"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"firefly_character_2.png","uri":"firefly_character_2.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]}]} \ No newline at end of file diff --git a/mods/firefly_characters/models/firefly_jetpack.gltf b/mods/firefly_characters/models/firefly_jetpack.gltf new file mode 100644 index 0000000..eba8066 --- /dev/null +++ b/mods/firefly_characters/models/firefly_jetpack.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[19],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[0,-0.625,1.875],"name":"cube","mesh":0},{"translation":[-1.875,-0.625,1.875],"name":"cube","mesh":1},{"translation":[3.125,-0.625,1.875],"name":"cube","mesh":2},{"translation":[0,0,1.875],"name":"cube","mesh":3},{"name":"tube","mesh":4},{"name":"fx2a","children":[4]},{"name":"tube","mesh":5},{"name":"fx2b","children":[6]},{"name":"tube","mesh":6},{"name":"fx2c","children":[8]},{"name":"tube","mesh":7},{"translation":[-5,0,0],"name":"fx1a","children":[10]},{"name":"tube","mesh":8},{"translation":[-5,0,0],"name":"fx1b","children":[12]},{"name":"tube","mesh":9},{"translation":[-5,0,0],"name":"fx1c","children":[14]},{"translation":[-2.5,-6.875,1.875],"name":"exhaust1"},{"translation":[2.5,-6.875,1.875],"name":"exhaust2"},{"translation":[0,14.375,-0.3125],"name":"root","children":[0,1,2,3,5,7,9,11,13,15,16,17]},{"children":[18]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":3360,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4128,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4896,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":5408,"byteLength":192,"target":34963},{"buffer":0,"byteOffset":5600,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6368,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7136,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":7648,"byteLength":192,"target":34963},{"buffer":0,"byteOffset":7840,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8608,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9376,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":9888,"byteLength":192,"target":34963},{"buffer":0,"byteOffset":10080,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10848,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11616,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":12128,"byteLength":192,"target":34963},{"buffer":0,"byteOffset":12320,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":13088,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":13856,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":14368,"byteLength":192,"target":34963},{"buffer":0,"byteOffset":14560,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":15328,"byteLength":768,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":16096,"byteLength":512,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":16608,"byteLength":192,"target":34963}],"buffers":[{"byteLength":16800,"uri":"data:application/octet-stream;base64,AADwPwAAAAAAAKA/AADwPwAAAAAAACC/AADwPwAAtMAAAKA/AADwPwAAtMAAACC/AADwvwAAAAAAACC/AADwvwAAAAAAAKA/AADwvwAAtMAAACC/AADwvwAAtMAAAKA/AADwvwAAAAAAACC/AADwPwAAAAAAACC/AADwvwAAAAAAAKA/AADwPwAAAAAAAKA/AADwvwAAtMAAAKA/AADwPwAAtMAAAKA/AADwvwAAtMAAACC/AADwPwAAtMAAACC/AADwvwAAAAAAAKA/AADwPwAAAAAAAKA/AADwvwAAtMAAAKA/AADwPwAAtMAAAKA/AADwPwAAAAAAACC/AADwvwAAAAAAACC/AADwPwAAtMAAACC/AADwvwAAtMAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAkD4AAMA9AACQPgAAAAAAABA/AADAPQAAED8AAMA9AACQPgAAQD4AAJA+AADAPQAAED8AAEA+AAAQPwAAwD4AAMA+AABAPgAAwD4AAMA+AACQPgAAQD4AAJA+AAAQPwAAAAAAAMA+AAAAAAAAED8AAMA9AADAPgAAwD0AAEA+AAAAAAAAwD4AAAAAAABAPgAAkD4AAMA+AACQPgAAAAAAAAAAAABAPgAAAAAAAAAAAACQPgAAQD4AAJA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAAAAAIL8AACA/AAAAAAAAIL8AACC/AAAAAAAAyMAAACA/AAAAAAAAyMAAACC/AACgvwAAIL8AACC/AACgvwAAIL8AACA/AACgvwAAyMAAACC/AACgvwAAyMAAACA/AACgvwAAIL8AACC/AAAAAAAAIL8AACC/AACgvwAAIL8AACA/AAAAAAAAIL8AACA/AACgvwAAyMAAACA/AAAAAAAAyMAAACA/AACgvwAAyMAAACC/AAAAAAAAyMAAACC/AACgvwAAIL8AACA/AAAAAAAAIL8AACA/AACgvwAAyMAAACA/AAAAAAAAyMAAACA/AAAAAAAAIL8AACC/AACgvwAAIL8AACC/AAAAAAAAyMAAACC/AACgvwAAyMAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AABAPgAAwD4AAIA+AADAPgAAQD4AACg/AACAPgAAKD8AAKA+AADAPgAAwD4AAMA+AACgPgAAKD8AAMA+AAAoPwAAED8AAOA+AAAAPwAA4D4AABA/AADAPgAAAD8AAMA+AAAQPwAA4D4AAAA/AADgPgAAED8AAAA/AAAAPwAAAD8AAIA+AADAPgAAoD4AAMA+AACAPgAAKD8AAKA+AAAoPwAAwD4AAMA9AADgPgAAwD0AAMA+AADAPgAA4D4AAMA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAAAAAIL8AACA/AAAAAAAAIL8AACC/AAAAAAAAyMAAACA/AAAAAAAAyMAAACC/AACgvwAAIL8AACC/AACgvwAAIL8AACA/AACgvwAAyMAAACC/AACgvwAAyMAAACA/AACgvwAAIL8AACC/AAAAAAAAIL8AACC/AACgvwAAIL8AACA/AAAAAAAAIL8AACA/AACgvwAAyMAAACA/AAAAAAAAyMAAACA/AACgvwAAyMAAACC/AAAAAAAAyMAAACC/AACgvwAAIL8AACA/AAAAAAAAIL8AACA/AACgvwAAyMAAACA/AAAAAAAAyMAAACA/AAAAAAAAIL8AACC/AACgvwAAIL8AACC/AAAAAAAAyMAAACC/AACgvwAAyMAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AADgPgAAwD0AAAA/AADAPQAA4D4AAMA+AAAAPwAAwD4AAAA/AADAPQAAED8AAMA9AAAAPwAAwD4AABA/AADAPgAAED8AABA/AAAAPwAAED8AABA/AAAAPwAAAD8AAAA/AACAPQAAED8AAAAAAAAQPwAAgD0AACA/AAAAAAAAID8AAOA+AADAPgAAAD8AAMA+AADgPgAAKD8AAAA/AAAoPwAAwD4AAMA+AADgPgAAwD4AAMA+AAAoPwAA4D4AACg/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAgPwAAAAAAACA/AAAgPwAAAAAAAAAAAAAgPwAAIL8AACA/AAAgPwAAIL8AAAAAAAAgvwAAAAAAAAAAAAAgvwAAAAAAACA/AAAgvwAAIL8AAAAAAAAgvwAAIL8AACA/AAAgvwAAAAAAAAAAAAAgPwAAAAAAAAAAAAAgvwAAAAAAACA/AAAgPwAAAAAAACA/AAAgvwAAIL8AACA/AAAgPwAAIL8AACA/AAAgvwAAIL8AAAAAAAAgPwAAIL8AAAAAAAAgvwAAAAAAACA/AAAgPwAAAAAAACA/AAAgvwAAIL8AACA/AAAgPwAAIL8AACA/AAAgPwAAAAAAAAAAAAAgvwAAAAAAAAAAAAAgPwAAIL8AAAAAAAAgvwAAIL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAQPwAAwD0AABg/AADAPQAAED8AAAA+AAAYPwAAAD4AAAA+AAAQPwAAID4AABA/AAAAPgAAGD8AACA+AAAYPwAAAD4AABg/AACAPQAAGD8AAAA+AAAQPwAAgD0AABA/AAAgPwAAgD0AABA/AACAPQAAID8AAMA9AAAQPwAAwD0AABA/AAAAPQAAID8AAAA9AAAQPwAAgD0AACA/AACAPQAAED8AAAAAAAAgPwAAAAAAABA/AAAAPQAAID8AAAA9AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUArsdIQAAAAsGuxyBArsdIQAAAAsGlcJ4/rsdIQAAA8MClcJ4/rsdIQAAA8MCuxyBAYIxyQAAA8MBgjEpAYIxyQAAA8MCAzhU/YIxyQAAAAsGAzhU/YIxyQAAAAsFgjEpAYIxyQAAAAsFgjEpAYIxyQAAAAsGAzhU/rsdIQAAAAsGlcJ4/rsdIQAAAAsGuxyBArsdIQAAA8MCuxyBArsdIQAAA8MClcJ4/YIxyQAAA8MCAzhU/YIxyQAAA8MBgjEpArsdIQAAAAsGlcJ4/pXDuPwAAAsGlcJ4/pXDuPwAA8MClcJ4/rsdIQAAA8MClcJ4/YIxyQAAA8MCAzhU/QOeaPwAA8MCAzhU/QOeaPwAAAsGAzhU/YIxyQAAAAsGAzhU/YIxyQAAAAsGAzhU/QOeaPwAAAsGAzhU/pXDuPwAAAsGlcJ4/rsdIQAAAAsGlcJ4/rsdIQAAA8MClcJ4/pXDuPwAA8MClcJ4/QOeaPwAA8MCAzhU/YIxyQAAA8MCAzhU/pXDuPwAAAsGlcJ4/pXDuPwAAAsGuxyBApXDuPwAA8MCuxyBApXDuPwAA8MClcJ4/QOeaPwAA8MCAzhU/QOeaPwAA8MBgjEpAQOeaPwAAAsFgjEpAQOeaPwAAAsGAzhU/QOeaPwAAAsGAzhU/QOeaPwAAAsFgjEpApXDuPwAAAsGuxyBApXDuPwAAAsGlcJ4/pXDuPwAA8MClcJ4/pXDuPwAA8MCuxyBAQOeaPwAA8MBgjEpAQOeaPwAA8MCAzhU/pXDuPwAAAsGuxyBArsdIQAAAAsGuxyBArsdIQAAA8MCuxyBApXDuPwAA8MCuxyBAQOeaPwAA8MBgjEpAYIxyQAAA8MBgjEpAYIxyQAAAAsFgjEpAQOeaPwAAAsFgjEpAQOeaPwAAAsFgjEpAYIxyQAAAAsFgjEpArsdIQAAAAsGuxyBApXDuPwAAAsGuxyBApXDuPwAA8MCuxyBArsdIQAAA8MCuxyBAYIxyQAAA8MBgjEpAQOeaPwAA8MBgjEpAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/2Rj4pQAAAAAAAIA/2Rj4pQAAAAAAAIA/2Rj4pQAAAAAAAIA/2Rj4pQAAAAAAAIA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAPwAAAADZGHglAACAPwAAAADZGHglAACAPwAAAADZGHglAACAPwAAAADZGHglAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/2Rh4JQAAAAAAAIC/2Rh4JQAAAAAAAIC/2Rh4JQAAAAAAAIC/2Rh4JQAAAAAAAIC/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAD4AAIA/AAAAPgAAgD8AAAAAAAAAAAAAAADqBK0/AAAAAAAAAAAAAAAAAAAAAAAAAD7qBK0/AAAAPqkTND7qBK0/qRM0PgAAAAAAAAAAqBM0PgAAAAB1gpY/AAAAAKgTND4AAAAAdYKWP6kTND7qBK0/qRM0PgAAAAAAAAAAAAAAPgAAgD8AAAA+AACAPwAAAAAAAAAAAAAAAOoErT8AAAAAAAAAAAAAAAAAAAAAAAAAPuoErT8AAAA+6gStPwAAAAAAAAAAAAAAAKkTND6oEzQ+dYKWP6gTND51gpY/AAAAAKkTND4AAAAAAAAAAKgTND7qBK0/qBM0PgAAAAAAAAA+AACAPwAAAD4AAIA/AAAAAAAAAAAAAAAA6gStPwAAAAAAAAAAAAAAAAAAAAAAAAA+6gStPwAAAD4AAAAAAAAAAAAAAADqBK0/qRM0PnWClj+pEzQ+qBM0PqkTND51gpY/qRM0PqgTND4AAAAAAAAAAAAAAADqBK0/AAAAAAAAAD4AAIA/AAAAPgAAgD8AAAAAAAAAAAAAAADqBK0/AAAAAAAAAAAAAAAAAAAAAAAAAD7qBK0/AAAAPgAAAACoEzQ+6gStP6gTND51gpY/AAAAAKkTND4AAAAAqRM0PqgTND51gpY/qBM0PuoErT8AAAAAAAAAAAAAAAAAAAEAAgAAAAIAAwAEAAUABgAEAAYABwAIAAkACgAIAAoACwAMAA0ADgAMAA4ADwAQABEAEgAQABIAEwAUABUAFgAUABYAFwAYABkAGgAYABoAGwAcAB0AHgAcAB4AHwAgACEAIgAgACIAIwAkACUAJgAkACYAJwAoACkAKgAoACoAKwAsAC0ALgAsAC4ALwAwADEAMgAwADIAMwA0ADUANgA0ADYANwA4ADkAOgA4ADoAOwA8AD0APgA8AD4APwBD4z9AAAAVwUTjF0BD4z9AAAAVwXk5sD9D4z9AAAANwXk5sD9D4z9AAAANwUTjF0BgjGBAAAANwWCMOEBgjGBAAAANwYDOXT9gjGBAAAAVwYDOXT9gjGBAAAAVwWCMOEBgjGBAAAAVwWCMOEBgjGBAAAAVwYDOXT9D4z9AAAAVwXk5sD9D4z9AAAAVwUTjF0BD4z9AAAANwUTjF0BD4z9AAAANwXk5sD9gjGBAAAANwYDOXT9gjGBAAAANwWCMOEBD4z9AAAAVwXk5sD+8HABAAAAVwXk5sD+8HABAAAANwXk5sD9D4z9AAAANwXk5sD9gjGBAAAANwYDOXT9A574/AAANwYDOXT9A574/AAAVwYDOXT9gjGBAAAAVwYDOXT9gjGBAAAAVwYDOXT9A574/AAAVwYDOXT+8HABAAAAVwXk5sD9D4z9AAAAVwXk5sD9D4z9AAAANwXk5sD+8HABAAAANwXk5sD9A574/AAANwYDOXT9gjGBAAAANwYDOXT+8HABAAAAVwXk5sD+8HABAAAAVwUTjF0C8HABAAAANwUTjF0C8HABAAAANwXk5sD9A574/AAANwYDOXT9A574/AAANwWCMOEBA574/AAAVwWCMOEBA574/AAAVwYDOXT9A574/AAAVwYDOXT9A574/AAAVwWCMOEC8HABAAAAVwUTjF0C8HABAAAAVwXk5sD+8HABAAAANwXk5sD+8HABAAAANwUTjF0BA574/AAANwWCMOEBA574/AAANwYDOXT+8HABAAAAVwUTjF0BD4z9AAAAVwUTjF0BD4z9AAAANwUTjF0C8HABAAAANwUTjF0BA574/AAANwWCMOEBgjGBAAAANwWCMOEBgjGBAAAAVwWCMOEBA574/AAAVwWCMOEBA574/AAAVwWCMOEBgjGBAAAAVwWCMOEBD4z9AAAAVwUTjF0C8HABAAAAVwUTjF0C8HABAAAANwUTjF0BD4z9AAAANwUTjF0BgjGBAAAANwWCMOEBA574/AAANwWCMOEAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAPgAAgD8AAAA+AACAPwAAAAAAAAAAAAAAAOoErT8AAAAAAAAAAAAAAAAAAAAAAAAAPuoErT8AAAA+qRM0PuoErT+pEzQ+AAAAAAAAAACoEzQ+AAAAAHWClj8AAAAAqBM0PgAAAAB1gpY/qRM0PuoErT+pEzQ+AAAAAAAAAAAAAAA+AACAPwAAAD4AAIA/AAAAAAAAAAAAAAAA6gStPwAAAAAAAAAAAAAAAAAAAAAAAAA+6gStPwAAAD7qBK0/AAAAAAAAAAAAAAAAqRM0PqgTND51gpY/qBM0PnWClj8AAAAAqRM0PgAAAAAAAAAAqBM0PuoErT+oEzQ+AAAAAAAAAD4AAIA/AAAAPgAAgD8AAAAAAAAAAAAAAADqBK0/AAAAAAAAAAAAAAAAAAAAAAAAAD7qBK0/AAAAPgAAAAAAAAAAAAAAAOoErT+pEzQ+dYKWP6kTND6oEzQ+qRM0PnWClj+pEzQ+qBM0PgAAAAAAAAAAAAAAAOoErT8AAAAAAAAAPgAAgD8AAAA+AACAPwAAAAAAAAAAAAAAAOoErT8AAAAAAAAAAAAAAAAAAAAAAAAAPuoErT8AAAA+AAAAAKgTND7qBK0/qBM0PnWClj8AAAAAqRM0PgAAAACpEzQ+qBM0PnWClj+oEzQ+6gStPwAAAAAAAAAAAAAAAAAAAQACAAAAAgADAAQABQAGAAQABgAHAAgACQAKAAgACgALAAwADQAOAAwADgAPABAAEQASABAAEgATABQAFQAWABQAFgAXABgAGQAaABgAGgAbABwAHQAeABwAHgAfACAAIQAiACAAIgAjACQAJQAmACQAJgAnACgAKQAqACgAKgArACwALQAuACwALgAvADAAMQAyADAAMgAzADQANQA2ADQANgA3ADgAOQA6ADgAOgA7ADwAPQA+ADwAPgA/AOoBNkAAACnB6gEOQOoBNkAAACnBLPzDP+oBNkAAACHBLPzDP+oBNkAAACHB6gEOQGCMTEAAACHBYIwkQGCMTEAAACHBQOeWP2CMTEAAACnBQOeWP2CMTEAAACnBYIwkQGCMTEAAACnBYIwkQGCMTEAAACnBQOeWP+oBNkAAACnBLPzDP+oBNkAAACnB6gEOQOoBNkAAACHB6gEOQOoBNkAAACHBLPzDP2CMTEAAACHBQOeWP2CMTEAAACHBYIwkQOoBNkAAACnBLPzDPxb+CUAAACnBLPzDPxb+CUAAACHBLPzDP+oBNkAAACHBLPzDP2CMTEAAACHBQOeWP0Dn5j8AACHBQOeWP0Dn5j8AACnBQOeWP2CMTEAAACnBQOeWP2CMTEAAACnBQOeWP0Dn5j8AACnBQOeWPxb+CUAAACnBLPzDP+oBNkAAACnBLPzDP+oBNkAAACHBLPzDPxb+CUAAACHBLPzDP0Dn5j8AACHBQOeWP2CMTEAAACHBQOeWPxb+CUAAACnBLPzDPxb+CUAAACnB6gEOQBb+CUAAACHB6gEOQBb+CUAAACHBLPzDP0Dn5j8AACHBQOeWP0Dn5j8AACHBYIwkQEDn5j8AACnBYIwkQEDn5j8AACnBQOeWP0Dn5j8AACnBQOeWP0Dn5j8AACnBYIwkQBb+CUAAACnB6gEOQBb+CUAAACnBLPzDPxb+CUAAACHBLPzDPxb+CUAAACHB6gEOQEDn5j8AACHBYIwkQEDn5j8AACHBQOeWPxb+CUAAACnB6gEOQOoBNkAAACnB6gEOQOoBNkAAACHB6gEOQBb+CUAAACHB6gEOQEDn5j8AACHBYIwkQGCMTEAAACHBYIwkQGCMTEAAACnBYIwkQEDn5j8AACnBYIwkQEDn5j8AACnBYIwkQGCMTEAAACnBYIwkQOoBNkAAACnB6gEOQBb+CUAAACnB6gEOQBb+CUAAACHB6gEOQOoBNkAAACHB6gEOQGCMTEAAACHBYIwkQEDn5j8AACHBYIwkQAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAgL8AAAAA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AAAVwYDOXT9gjGBAAAAVwYDOXT9A574/AAAVwYDOXT+8HABAAAAVwXk5sD9D4z9AAAAVwXk5sD9D4z9AAAANwXk5sD+8HABAAAANwXk5sD9A574/AAANwYDOXT9gjGBAAAANwYDOXT+8HABAAAAVwXk5sD+8HABAAAAVwUTjF0C8HABAAAANwUTjF0C8HABAAAANwXk5sD9A574/AAANwYDOXT9A574/AAANwWCMOEBA574/AAAVwWCMOEBA574/AAAVwYDOXT9A574/AAAVwYDOXT9A574/AAAVwWCMOEC8HABAAAAVwUTjF0C8HABAAAAVwXk5sD+8HABAAAANwXk5sD+8HABAAAANwUTjF0BA574/AAANwWCMOEBA574/AAANwYDOXT+8HABAAAAVwUTjF0BD4z9AAAAVwUTjF0BD4z9AAAANwUTjF0C8HABAAAANwUTjF0BA574/AAANwWCMOEBgjGBAAAANwWCMOEBgjGBAAAAVwWCMOEBA574/AAAVwWCMOEBA574/AAAVwWCMOEBgjGBAAAAVwWCMOEBD4z9AAAAVwUTjF0C8HABAAAAVwUTjF0C8HABAAAANwUTjF0BD4z9AAAANwUTjF0BgjGBAAAANwWCMOEBA574/AAANwWCMOEAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/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6gEOQGCMTEAAACHBYIwkQEDn5j8AACHBYIwkQAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAAA+AACAPwAAAD4AAIA/AAAAAAAAAAAAAAAA6gStPwAAAAAAAAAAAAAAAAAAAAAAAAA+6gStPwAAAD6pEzQ+6gStP6kTND4AAAAAAAAAAKgTND4AAAAAdYKWPwAAAACoEzQ+AAAAAHWClj+pEzQ+6gStP6kTND4AAAAAAAAAAAAAAD4AAIA/AAAAPgAAgD8AAAAAAAAAAAAAAADqBK0/AAAAAAAAAAAAAAAAAAAAAAAAAD7qBK0/AAAAPuoErT8AAAAAAAAAAAAAAACpEzQ+qBM0PnWClj+oEzQ+dYKWPwAAAACpEzQ+AAAAAAAAAACoEzQ+6gStP6gTND4AAAAAAAAAPgAAgD8AAAA+AACAPwAAAAAAAAAAAAAAAOoErT8AAAAAAAAAAAAAAAAAAAAAAAAAPuoErT8AAAA+AAAAAAAAAAAAAAAA6gStP6kTND51gpY/qRM0PqgTND6pEzQ+dYKWP6kTND6oEzQ+AAAAAAAAAAAAAAAA6gStPwAAAAAAAAA+AACAPwAAAD4AAIA/AAAAAAAAAAAAAAAA6gStPwAAAAAAAAAAAAAAAAAAAAAAAAA+6gStPwAAAD4AAAAAqBM0PuoErT+oEzQ+dYKWPwAAAACpEzQ+AAAAAKkTND6oEzQ+dYKWP6gTND7qBK0/AAAAAAAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcAGAAZABoAGAAaABsAHAAdAB4AHAAeAB8AIAAhACIAIAAiACMAJAAlACYAJAAmACcAKAApACoAKAAqACsALAAtAC4ALAAuAC8AMAAxADIAMAAyADMANAA1ADYANAA2ADcAOAA5ADoAOAA6ADsAPAA9AD4APAA+AD8A"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1.875,0,1.25],"min":[-1.875,-5.625,-0.625],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.5625,0.5625],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[0,-0.625,0.625],"min":[-1.25,-6.25,-0.625],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.5625,0.65625],"min":[0.1875,0.09375],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[0,-0.625,0.625],"min":[-1.25,-6.25,-0.625],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.5625,0.65625],"min":[0,0.09375],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":12,"componentType":5126,"count":24,"max":[0.625,0,0.625],"min":[-0.625,-0.625,0],"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":14,"componentType":5126,"count":24,"max":[0.625,0.59375],"min":[0.0625,0],"type":"VEC2"},{"bufferView":15,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":16,"componentType":5126,"count":64,"max":[3.7898178100585938,-7.5,3.1648178100585938],"min":[1.2101821899414062,-8.125,0.5851821899414062],"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":18,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":19,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"},{"bufferView":20,"componentType":5126,"count":64,"max":[3.5085678100585938,-8.8125,2.8835678100585938],"min":[1.4914321899414062,-9.3125,0.8664321899414062],"type":"VEC3"},{"bufferView":21,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":22,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":23,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"},{"bufferView":24,"componentType":5126,"count":64,"max":[3.1960678100585938,-10.0625,2.5710678100585938],"min":[1.8039321899414062,-10.5625,1.1789321899414062],"type":"VEC3"},{"bufferView":25,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":26,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":27,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"},{"bufferView":28,"componentType":5126,"count":64,"max":[3.7898178100585938,-7.5,3.1648178100585938],"min":[1.2101821899414062,-8.125,0.5851821899414062],"type":"VEC3"},{"bufferView":29,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":30,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":31,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"},{"bufferView":32,"componentType":5126,"count":64,"max":[3.5085678100585938,-8.8125,2.8835678100585938],"min":[1.4914321899414062,-9.3125,0.8664321899414062],"type":"VEC3"},{"bufferView":33,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":34,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":35,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"},{"bufferView":36,"componentType":5126,"count":64,"max":[3.1960678100585938,-10.0625,2.5710678100585938],"min":[1.8039321899414062,-10.5625,1.1789321899414062],"type":"VEC3"},{"bufferView":37,"componentType":5126,"count":64,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":38,"componentType":5126,"count":64,"max":[1.3517124652862549,1.3517124652862549],"min":[0,0],"type":"VEC2"},{"bufferView":39,"componentType":5123,"count":96,"max":[63],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true},{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":1}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"firefly_jetpack"},{"sampler":1,"source":1,"name":"texture"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071},{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"firefly_jetpack.png","uri":"firefly_jetpack.png"},{"mimeType":"image/png","name":"texture","uri":"texture.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":12,"NORMAL":13,"TEXCOORD_0":14},"indices":15,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":16,"NORMAL":17,"TEXCOORD_0":18},"indices":19,"material":1}]},{"primitives":[{"mode":4,"attributes":{"POSITION":20,"NORMAL":21,"TEXCOORD_0":22},"indices":23,"material":1}]},{"primitives":[{"mode":4,"attributes":{"POSITION":24,"NORMAL":25,"TEXCOORD_0":26},"indices":27,"material":1}]},{"primitives":[{"mode":4,"attributes":{"POSITION":28,"NORMAL":29,"TEXCOORD_0":30},"indices":31,"material":1}]},{"primitives":[{"mode":4,"attributes":{"POSITION":32,"NORMAL":33,"TEXCOORD_0":34},"indices":35,"material":1}]},{"primitives":[{"mode":4,"attributes":{"POSITION":36,"NORMAL":37,"TEXCOORD_0":38},"indices":39,"material":1}]}]} \ No newline at end of file diff --git a/mods/firefly_characters/textures/firefly_character.png b/mods/firefly_characters/textures/firefly_character.png new file mode 100644 index 0000000..59afd08 Binary files /dev/null and b/mods/firefly_characters/textures/firefly_character.png differ diff --git a/mods/firefly_characters/textures/firefly_character_2.png b/mods/firefly_characters/textures/firefly_character_2.png new file mode 100644 index 0000000..d128aa6 Binary files /dev/null and b/mods/firefly_characters/textures/firefly_character_2.png differ diff --git a/mods/firefly_characters/textures/firefly_jetpack.png b/mods/firefly_characters/textures/firefly_jetpack.png new file mode 100644 index 0000000..4500f0f Binary files /dev/null and b/mods/firefly_characters/textures/firefly_jetpack.png differ diff --git a/mods/firefly_font/fonts/change_atlas_cell_size.py b/mods/firefly_font/fonts/change_atlas_cell_size.py new file mode 100644 index 0000000..a05130f --- /dev/null +++ b/mods/firefly_font/fonts/change_atlas_cell_size.py @@ -0,0 +1,61 @@ +from PIL import Image + +def adjust_font_atlas(input_path, output_path, current_width, current_height, new_width, new_height): + """ + Adjusts the cell dimensions of a pixel font atlas sprite sheet by creating larger cells + and placing the original sprites in the top-left corner of each new cell, filling + the extra space with the background color detected from the top-left pixel of the + original image. This allows for manual extension of sprites without repositioning them. + + Args: + input_path (str): Path to the input atlas image. + output_path (str): Path to save the output atlas image. + current_width (int): Current width of each cell (in pixels). + current_height (int): Current height of each cell (in pixels). + new_width (int): New larger width for each cell (in pixels). + new_height (int): New larger height for each cell (in pixels). + """ + if new_width < current_width or new_height < current_height: + raise ValueError("New dimensions must be larger than the current dimensions.") + + img = Image.open(input_path) + width, height = img.size + + if width % current_width != 0 or height % current_height != 0: + raise ValueError("Image dimensions are not multiples of the current cell dimensions.") + + cols = width // current_width + rows = height // current_height + + # Create a new image with the same mode + new_img = Image.new(img.mode, (cols * new_width, rows * new_height)) + + # Detect background color from top-left pixel (assuming it's background) + bg_color = img.getpixel((0, 0)) + + # Fill the new image with the background color + new_img.paste(bg_color, (0, 0, new_img.width, new_img.height)) + + # Copy each original sprite into the top-left of its new larger cell + for r in range(rows): + for c in range(cols): + # Crop the original sprite + left = c * current_width + top = r * current_height + right = left + current_width + bottom = top + current_height + sprite = img.crop((left, top, right, bottom)) + + # Paste into new position + new_left = c * new_width + new_top = r * new_height + new_img.paste(sprite, (new_left, new_top)) + + # Save the new atlas + new_img.save(output_path) + + +# Example usage: +base_path = "/Users/iboettcher/eclipse-workspace/firefly/mods/firefly_font/fonts/" +adjust_font_atlas(f'{base_path}firefly_normal.png', + f'{base_path}firefly_wide.png', 8, 17, 17, 17) diff --git a/mods/firefly_font/fonts/firefly_normal.json b/mods/firefly_font/fonts/firefly_normal.json new file mode 100644 index 0000000..fb2f351 --- /dev/null +++ b/mods/firefly_font/fonts/firefly_normal.json @@ -0,0 +1,20 @@ +{ + "A": { + "width": 7, + "height": 10, + "base": 0, + "advance": 2 + }, + "B": { + "width": 7, + "height": 10, + "base": 0, + "advance": 2 + }, + "C": { + "width": 7, + "height": 10, + "base": 0, + "advance": 2 + } +} \ No newline at end of file diff --git a/mods/firefly_font/fonts/firefly_normal.png b/mods/firefly_font/fonts/firefly_normal.png new file mode 100644 index 0000000..dba7056 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_65.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_65.png new file mode 100644 index 0000000..c44e845 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_65.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_66.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_66.png new file mode 100644 index 0000000..d7cab72 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_66.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_67.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_67.png new file mode 100644 index 0000000..51cd7f5 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_67.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_68.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_68.png new file mode 100644 index 0000000..0f166e9 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_68.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_69.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_69.png new file mode 100644 index 0000000..6cba3c5 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_69.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_70.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_70.png new file mode 100644 index 0000000..4153fba Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_70.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_71.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_71.png new file mode 100644 index 0000000..95b9027 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_71.png differ diff --git a/mods/firefly_font/fonts/firefly_normal/firefly_char_72.png b/mods/firefly_font/fonts/firefly_normal/firefly_char_72.png new file mode 100644 index 0000000..6567fd4 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_normal/firefly_char_72.png differ diff --git a/mods/firefly_font/fonts/firefly_wide.png b/mods/firefly_font/fonts/firefly_wide.png new file mode 100644 index 0000000..a6d6f19 Binary files /dev/null and b/mods/firefly_font/fonts/firefly_wide.png differ diff --git a/mods/firefly_font/fonts/regular.ttf b/mods/firefly_font/fonts/regular.ttf new file mode 100644 index 0000000..9ec2722 Binary files /dev/null and b/mods/firefly_font/fonts/regular.ttf differ diff --git a/mods/firefly_font/init.lua b/mods/firefly_font/init.lua new file mode 100644 index 0000000..99256e8 --- /dev/null +++ b/mods/firefly_font/init.lua @@ -0,0 +1,46 @@ +local ns = firefly +local modname = minetest.get_current_modname() + +ns.fonts = {} + +function ns.rasterize(str, font, max_width) + if not font then font = "firefly_normal" end + local atlas = ns.fonts[font] or minetest.parse_json(ns.read_file(minetest.get_modpath(modname).."/fonts/"..font..".json")) + if not ns.fonts[font] then + ns.fonts[font] = atlas + end + + local newline = string.byte("\n", 1) + + local out = {} + local width = 0 + local height = 0 + local x = 0 + local y = 0 + for i = 0, #str do + local char = str:byte(i) + if not char then goto continue end + if char == newline then + goto continue + end + local glyph = atlas[string.char(char)] + + local num = #out + out[num +1] = ":" + out[num +2] = x + out[num +3] = "," + out[num +4] = y + out[num +5] = "=" + out[num +6] = font + out[num +7] = "/firefly_char_" + out[num +8] = char + out[num +9] = ".png" + + x = x +glyph.width +glyph.advance + if x > width then + width = x + end + ::continue:: + end + return "[combine:"..width.."x"..height..table.concat(out) +end diff --git a/mods/firefly_font/mod.conf b/mods/firefly_font/mod.conf new file mode 100644 index 0000000..9a8e33d --- /dev/null +++ b/mods/firefly_font/mod.conf @@ -0,0 +1,2 @@ +name = firefly_font +depends = firefly_base \ No newline at end of file diff --git a/mods/firefly_hud/init.lua b/mods/firefly_hud/init.lua new file mode 100644 index 0000000..09d5626 --- /dev/null +++ b/mods/firefly_hud/init.lua @@ -0,0 +1,289 @@ + +local ns = firefly + +ns.hud_types = {} +ns.Element = { + animate = function(e, target) + if not e.targets then e.targets = {} end + local time = minetest.get_us_time() + for k, v in pairs(target) do + e.targets[k] = { + ref = { + time = time, + value = e[k] + }, + target = { + time = time +(v.duration or 1) *1000000, + value = v.value + }, + ease_fn = v.ease_fn + } + end + end +} + +local function bezier_ease(t, x1, y1, x2, y2) + if t <= 0 then return 0 end + if t >= 1 then return 1 end + + local low = 0 + local high = 1 + local epsilon = 1e-6 + local iterations = 0 + while (high - low > epsilon) and (iterations < 100) do + local mid = (low + high) / 2 + local x = 3 * mid * (1 - mid) ^ 2 * x1 + 3 * mid ^ 2 * (1 - mid) * x2 + mid ^ 3 + if x < t then + low = mid + else + high = mid + end + iterations = iterations + 1 + end + local u = (low + high) / 2 + + local y = 3 * u * (1 - u) ^ 2 * y1 + 3 * u ^ 2 * (1 - u) * y2 + u ^ 3 + return y +end + +local function interpolate(ref, target, t, x1, y1, x2, y2) + local eased_t = bezier_ease(t, x1 or 0, y1 or 0, x2 or 1, y2 or 1) + return ref + (target - ref) * eased_t +end +ns.interpolate = interpolate + +function ns.register_hud_type(def) + ns.hud_types[def.name] = setmetatable(def, {__index = ns.Element}) +end + +function ns.validate_type(elem, type) + if not ns.hud_types[elem.type] then + warn("Unknown HUD type `"..type.."` for element `"..elem.name.."`; ignoring.") + return false + end + if ns.hud_types[type].required_fields then + for _, field in ipairs(ns.hud_types[type].required_fields) do + if elem[field] == nil then return false end + end + end + return true +end + +function ns.hud_add(m, def) + if not ns.validate_type(def, def.type) then + return false + end + local type = ns.hud_types[def.type] + if type.defaults then + def = extend(table.copy(type.defaults), def) + end + -- Create a random name if none is given, since the + -- assumption is that the user doesn't care about the name. + if not def.name then + def.name = ""..math.random() + end + local el + if m.hud[def.name] then + el = m.hud[def.name] + -- Simply write all modified fields to the existing element. + extend(el, def) + else + el = setmetatable(def, {__index = type}) + m.hud[def.name] = el + el:add(m) + end + return el +end + +function ns.update_poi(m) + for _, x in pairs(m.poi) do + x:remove(m) + end + m.poi = {} + for _, x in ipairs(minetest.find_nodes_in_area(m.pos:offset(-100, -100, -100), m.pos:offset(100,100,100), "group:poi")) do + m.poi[#m.poi +1] = ns.hud_add(m, { + name = "poi:"..x:to_string(), + type = "poi", + world_pos = x, + }) + end +end + +local default_ease_fn = {0,0,1,1} +minetest.register_globalstep(function(dtime) + local time = minetest.get_us_time() + for _, m in pairs(ns.players) do + for k, el in pairs(m.hud) do + if el.remove_after then + el.remove_after = el.remove_after -dtime + if el.remove_after < 0 then + el:remove(m) + m.hud[k] = nil + end + end + + if el.targets and next(el.targets) then + local changes = {} + for key, target in pairs(el.targets) do + local fac = (time -target.ref.time) /(target.target.time -target.ref.time) + local ease_fn = target.ease_fn or default_ease_fn + local value + if el.field_types[key] == "vec2" then + value = { + x = interpolate(target.ref.value.x, target.target.value.x, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]), + y = interpolate(target.ref.value.y, target.target.value.y, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]) + } + if fac >= 1 then + el.targets[key] = nil + end + elseif el.field_types[key] == "color" then + value = { + r = interpolate(target.ref.value.r, target.target.value.r, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]), + g = interpolate(target.ref.value.g, target.target.value.g, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]), + b = interpolate(target.ref.value.b, target.target.value.b, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]), + a = interpolate(target.ref.value.a, target.target.value.a, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]), + } + if value.r == target.target.value.r and value.g == target.target.value.g and value.b == target.target.value.b and value.a == target.target.value.a then + el.targets[key] = nil + end + else + value = interpolate(target.ref.value, target.target.value, fac, ease_fn[1], ease_fn[2], ease_fn[3], ease_fn[4]) + if value == target.target.value then + el.targets[key] = nil + end + end + el[key] = value + -- We could just set this to true, but since we already + -- have a new table, we might as well use it. + changes[key] = value + end + el:update(m, changes) + end + end + +-- for k, el in ipairs(m.poi) do +-- if m.dir:distance(m.pos:direction(el.world_pos)) < 0.05 then +-- el.focused = true +-- el:animate { +-- scale = { +-- value = {x=2,y=2}, +-- duration = 0.2, +-- } +-- } +-- elseif el.focused then +-- el.focused = false +-- el:animate { +-- scale = { +-- value = {x=1,y=1}, +-- duration = 0.2, +-- } +-- } +-- end +-- end + end +end) + +function ns.color_to_number(color) + return tonumber(string.format("0x%.2x%.2x%.2x", color.r, color.g, color.b)) +end + +ns.register_hud_type { + name = "text", + required_fields = {"pos", "text"}, + field_types = { + offset = "vec2", + pos = "vec2", + size = "vec2", + color = "color" + }, + defaults = { + dir = 0, + align = {x=0, y=0}, + offset = {x=0, y=0}, + size = {x=1, y=1}, + color = {r = 0xff, g = 0xff, b = 0xff, a = 0xff} + }, + add = function(e, m) + e._id = m.player:hud_add { + type = "text", + position = e.pos, + direction = e.dir, + alignment = e.align, + offset = e.offset, + scale = {x=100, y=100}, + size = e.size, + text = e.text, + number = ns.color_to_number(e.color) + } + end, + update = function(e, m, changes) + for k, v in pairs(changes) do + if k == "color" then + k = "number" + v = ns.color_to_number(v) + elseif k == "dir" then + k = "direction" + elseif k == "align" then + k = "alignment" + end + m.player:hud_change(e._id, k, v) + end + end, + remove = function(e, m) + m.hud[e.name] = nil + m.player:hud_remove(e._id) + e._id = nil + -- Prevent ongoing animations from attempting to change a nonexistent element. + e.update = function() end + end +} + +ns.register_hud_type { + name = "image", + required_fields = {"pos", "image"}, + field_types = { + offset = "vec2", + scale = "vec2", + pos = "vec2" + }, + defaults = { + dir = 0, + align = {x=0, y=0}, + offset = {x=0, y=0}, + scale = {x=1, y=1}, + opacity = 256 + }, + add = function(e, m) + e._id = m.player:hud_add { + type = "image", + position = e.pos, + direction = e.dir, + alignment = e.align, + offset = e.offset, + scale = e.scale, + text = e.image..string.format("^[opacity:%i", e.opacity) + } + end, + update = function(e, m, changes) + for k, v in pairs(changes) do + if k == "align" then + k = "alignment" + elseif k == "pos" then + k = "position" + elseif k == "image" then + k = "text" + elseif k == "opacity" then + k = "text" + v = e.image..string.format("^[opacity:%i", e.opacity) + end + m.player:hud_change(e._id, k, v) + end + end, + remove = function(e, m) + m.hud[e.name] = nil + m.player:hud_remove(e._id) + e._id = nil + -- Prevent ongoing animations from attempting to change a nonexistent element. + e.update = function() end + end +} diff --git a/mods/firefly_hud/mod.conf b/mods/firefly_hud/mod.conf new file mode 100644 index 0000000..f0efe1b --- /dev/null +++ b/mods/firefly_hud/mod.conf @@ -0,0 +1,2 @@ +name = firefly_hud +depends = firefly_base \ No newline at end of file diff --git a/mods/firefly_hudbars/init.lua b/mods/firefly_hudbars/init.lua new file mode 100644 index 0000000..3ff3a8c --- /dev/null +++ b/mods/firefly_hudbars/init.lua @@ -0,0 +1,132 @@ +local ns = firefly + +Player:listen('init', function(m) + m.hud_bars = {} + m.hud_bars_list = {} +end) + + + +function ns.add_hud_bar(m, name, def) + if m.hud_bars[name] then + ns.remove_hud_bar(m, name) + end + if not def.color then def.color = "#48d" end + + local ypos = #m.hud_bars_list *50 + local pixel_scale = 3 + local hud = { + left = ns.hud_add(m, { + type = "image", + image = "firefly_hud_bar_left"..(def.value == 0 and "_bg" or "")..".png^[multiply:"..def.color, + pos = {x = 0.5, y = 0}, + offset = {x = -400 -pixel_scale, y = ypos +36}, + scale = {x = pixel_scale, y = pixel_scale} + }), + center = ns.hud_add(m, { + type = "image", + image = "firefly_hud_bar_center_bg.png^[multiply:"..def.color, + pos = {x = 0.5, y = 0}, + offset = {x = 0, y = ypos +36}, + scale = {x = 800, y = pixel_scale} + }), + overlay = ns.hud_add(m, { + type = "image", + image = "firefly_hud_bar_center.png^[multiply:"..def.color, + pos = {x = 0.5, y = 0}, + offset = {x = -400, y = ypos +36}, + align = {x = 1, y = 0}, + scale = {x = 800 *(def.value /def.max), y = pixel_scale} + }), + right = ns.hud_add(m, { + type = "image", + image = "firefly_hud_bar_right"..(def.value == def.max and "" or "_bg")..".png^[multiply:"..def.color, + pos = {x = 0.5, y = 0}, + offset = {x = 400 +pixel_scale, y = ypos +36}, + scale = {x = pixel_scale, y = pixel_scale} + }), + title = ns.hud_add(m, { + type = "text", + text = def.title or "", + pos = {x = 0.5, y = 0}, + offset = {x = 0, y = ypos +4}, + align = {x = 0, y = 1}, + color = minetest.colorspec_to_table(def.color) + }) + } + m.hud_bars[name] = { + def = def, + hud = hud, + index = #m.hud_bars_list +1 + } + table.insert(m.hud_bars_list, name) +end + +function ns.change_hud_bar(m, name, def) + local bar = m.hud_bars[name] + if not bar then return end + local last_value = bar.def.value + local last_title = bar.def.title + local last_color = bar.def.color + extend(bar.def, def) + if def.value or def.max then + if last_color ~= bar.def.color then + bar.hud.right:update(m, {text = "firefly_hud_bar_right"..(def.value == def.max and "" or "_bg")..".png^[multiply:"..def.color}) + bar.hud.left:update(m, {text = "firefly_hud_bar_left"..(def.value == 0 and "_bg" or "")..".png^[multiply:"..def.color}) + bar.hud.center:update(m, {text = "firefly_hud_bar_center_bg.png^[multiply:"..def.color}) + bar.hud.overlay:update(m, {text = "firefly_hud_bar_center.png^[multiply:"..def.color}) + bar.hud.title:update(m, {color = minetest.colorspec_to_table(def.color)}) + end + + if bar.def.value == bar.def.max and last_value ~= bar.def.max then + minetest.after(def.ease or 0.3, function() + bar.hud.right:update(m, {text = "firefly_hud_bar_right.png^[multiply:"..bar.def.color}) + end) + elseif last_value == bar.def.max then + bar.hud.right:update(m, {text = "firefly_hud_bar_right_bg.png^[multiply:"..bar.def.color}) + end + + if bar.def.value == 0 and last_value ~= 0 then + minetest.after(def.ease or 0.3, function() + bar.hud.left:update(m, {image = "firefly_hud_bar_left_bg.png^[multiply:"..bar.def.color}) + end) + elseif last_value == 0 then + bar.hud.left:update(m, {image = "firefly_hud_bar_left.png^[multiply:"..bar.def.color}) + end + + if last_title ~= bar.def.title then + bar.hud.title:update(m, {text = bar.def.title}) + end + + bar.hud.overlay:animate { + scale = { + value = {x = 800 *(bar.def.value /bar.def.max), y = 3}, + duration = def.ease or 0.3, + ease_fn = {0.42, 0, 0.58, 1.06} + } + } + end +end + +function ns.remove_hud_bar(m, name) + if not m.hud_bars[name] then return end + for _, x in pairs(m.hud_bars[name].hud) do + x:remove(m) + end + m.hud_bars[name] = nil + table.remove(m.hud_bars_list, table.indexof(m.hud_bars_list, name)) + + -- Reposition all other HUD bars so there are no gaps. + for i = 1, #m.hud_bars_list do + local bar = m.hud_bars[m.hud_bars_list[i]] + if bar.index ~= i then + local dist = i *50 + bar.hud.right:update(m, {offset = {x = bar.hud.right.offset.x, y = dist +36}}) + bar.hud.left:update(m, {offset = {x = bar.hud.left.offset.x, y = dist +36}}) + bar.hud.center:update(m, {offset = {x = bar.hud.center.offset.x, y = dist +36}}) + bar.hud.overlay:update(m, {offset = {x = bar.hud.overlay.offset.x, y = dist +36}}) + bar.hud.title:update(m, {offset = {x = bar.hud.title.offset.x, y = dist +4}}) + end + end +end + diff --git a/mods/firefly_hudbars/mod.conf b/mods/firefly_hudbars/mod.conf new file mode 100644 index 0000000..c899760 --- /dev/null +++ b/mods/firefly_hudbars/mod.conf @@ -0,0 +1,2 @@ +name = firefly_hudbars +depends = firefly_hud, firefly_base \ No newline at end of file diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_center.png b/mods/firefly_hudbars/textures/firefly_hud_bar_center.png new file mode 100644 index 0000000..efc9131 Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_center.png differ diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_center_bg.png b/mods/firefly_hudbars/textures/firefly_hud_bar_center_bg.png new file mode 100644 index 0000000..c9a09af Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_center_bg.png differ diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_left.png b/mods/firefly_hudbars/textures/firefly_hud_bar_left.png new file mode 100644 index 0000000..5bb1bb6 Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_left.png differ diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_left_bg.png b/mods/firefly_hudbars/textures/firefly_hud_bar_left_bg.png new file mode 100644 index 0000000..7139bf6 Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_left_bg.png differ diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_right.png b/mods/firefly_hudbars/textures/firefly_hud_bar_right.png new file mode 100644 index 0000000..4d3a4c7 Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_right.png differ diff --git a/mods/firefly_hudbars/textures/firefly_hud_bar_right_bg.png b/mods/firefly_hudbars/textures/firefly_hud_bar_right_bg.png new file mode 100644 index 0000000..d9c8323 Binary files /dev/null and b/mods/firefly_hudbars/textures/firefly_hud_bar_right_bg.png differ diff --git a/mods/firefly_player/init.lua b/mods/firefly_player/init.lua new file mode 100644 index 0000000..03a3f8d --- /dev/null +++ b/mods/firefly_player/init.lua @@ -0,0 +1,432 @@ +local ns = firefly + +ns.players = {} + + +minetest.register_entity(":firefly:player", { + initial_properties = { + visual = "mesh", + mesh = "firefly_character.gltf", + textures = {"firefly_character.png"}, + backface_culling = false, + pointable = false, + visual_size = vector.new(1,1,1), + collisionbox = {2/3, 1.625, 2/3, -2/3, 0, -2/3}, + selectionbox = {2/3, 1.625, 2/3, -2/3, 0, -2/3}, + static_save = false, + }, + on_deactivate = function(e) + -- Ensure that the player's display will never get lost until the player leaves. + if e.owner and e.owner.player:get_pos() then + e.owner.object = minetest.add_entity(e.owner.pos, "firefly:player") + e.owner.object:set_attach(e.owner.player) + end + end, + on_step = function(e, dtime, movement) + if e.owner then + e.owner.movement = movement + end + end +}) + +function ns.add_jetpack_particles(m) + local def = { + attached = m.jetpack_a, + pos = { + min = vector.new(-0.1,-0.1,-0.1), + max = vector.new(0.1,0.1,0.1) + }, + vel = { + min = vector.new(0, -5, 0), + max = vector.new(0, -1, 0) + }, + animation = { + type = "vertical_frames", + aspect_w = 1, + aspect_h = 1, + length = -1 + }, + texture = ns.solid_color_frames {"#f7d19b", "#f0a951", "#e1820c", "#cd5819", "#5d342c", "#444"}, + time = 0, + amount = 50, + exptime = 0.5, + size_tween = { + {min = 1, max = 2}, + 0.05 + } + + } + m.jetpack_particles_a = minetest.add_particlespawner(table.copy(def)) + def.attached = m.jetpack_b + m.jetpack_particles_b = minetest.add_particlespawner(def) +end + +function ns.remove_jetpack_particles(m) + minetest.delete_particlespawner(m.jetpack_particles_a) + minetest.delete_particlespawner(m.jetpack_particles_b) +end + +function ns.enter_jetpack_mode(m) + m.jetpack_mode = true + m:set_physics_override("jetpack", { + speed = 3, + }) + ns.add_jetpack_particles(m) +end + +function ns.exit_jetpack_mode(m) + m.jetpack_mode = nil + m:set_physics_override("jetpack") + ns.remove_jetpack_particles(m) +end + +local blank_physics_override = { + speed = 1, + speed_walk = 1, + speed_crouch = 1, + speed_fast = 1, + speed_climb = 1, + speed_jump = 1, + jump = 1, + gravity = 1, + liquid_fluidity = 1, + liquid_fluidity_smooth = 1, + liquid_sink = 1, + acceleration_default = 1, + acceleration_air = 1, + acceleration_fast = 1, +} + +Player = { + listeners = {}, + init = function(p) + local m = { + active = true, + player = p, + name = p:get_player_name(), + object = minetest.add_entity(p:get_pos(), "firefly:player"), + hud = {}, + physics_overrides = {}, + pos = p:get_pos() + } + setmetatable(m, {__index = Player}) + m.object:get_luaentity().owner = m + + p:set_properties { + visual = "sprite", + textures = {"blank.png"}, + pointable = false + } + + p:hud_set_flags { + healthbar = false, + breathbar = false, + + } + + m.eye_height = p:get_properties().eye_height + + m.object:set_attach(p) + + m.jetpack = minetest.add_entity(m.pos, "display") + m.jetpack:set_properties { + visual = "mesh", + mesh = "firefly_jetpack.gltf", + textures = {"firefly_jetpack.png", "blank.png"} + } + m.jetpack:set_attach(m.object, "Torso") + m.jetpack_a = minetest.add_entity(m.pos, "display") + m.jetpack_a:set_attach(m.jetpack, "exhaust1") + m.jetpack_b = minetest.add_entity(m.pos, "display") + m.jetpack_b:set_attach(m.jetpack, "exhaust2") + + + m.stats_bg = m.player:hud_add { + type = "image", + text = "firefly_stats_bg.png", + position = {x = 0, y = 1}, + offset = {x = 50, y = -50}, + alignment = {x = 1, y = -1}, + scale = {x = 6, y = 6}, + } + + m.healthbar = m.player:hud_add { + type = "image", + text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#d33", + position = {x = 0, y = 1}, + offset = {x = 50 +(29 *6), y = -50 -(19 *6)}, + alignment = {x = 1, y = -1}, + scale = {x = 6, y = 6}, + } + + m.ammobar = m.player:hud_add { + type = "image", + text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#dc4", + position = {x = 0, y = 1}, + offset = {x = 50 +(29 *6), y = -50 -(13 *6)}, + alignment = {x = 1, y = -1}, + scale = {x = 6, y = 6}, + } + + m.ammobar2 = m.player:hud_add { + type = "image", + text = "[fill:4x32:0,0:#0000^[lowpart:100:firefly_health_bar.png^[transformR270^[multiply:#4dc", + position = {x = 0, y = 1}, + offset = {x = 50 +(29 *6), y = -50 -(7 *6)}, + alignment = {x = 1, y = -1}, + scale = {x = 6, y = 6}, + } + + m:set_hotbar_size(8) + + + m.ctl = {} + m.pitch = 0 + m.yaw = 0 + m.last_jump = 0 + Player:dispatch('init', m) + return m + end, + deinit = function(m) + m.active = false + end, + tick = function(m, dtime) + local p = m.player + local pos = p:get_pos() + m.eye_pos = pos:offset(0, m.eye_height, 0) + local vel = p:get_velocity() + local pitch = p:get_look_vertical() + local yaw = p:get_look_horizontal() + local look_dir = p:get_look_dir() + local ctl = p:get_player_control() + local time = minetest.get_us_time() + + local standing_on = minetest.get_node(pos:offset(0, -0.5, 0)) + + local moving = ctl.up or ctl.left or ctl.down or ctl.right + + -- MARK: Wield callbacks + + local wielded_item = p:get_wielded_item() + local wi_def = wielded_item:get_definition() + if not m.wielded_item or m.wielded_item:get_name() ~= wielded_item:get_name() then + if m.wielded_item then + local old_def = m.wielded_item:get_definition() + if old_def.on_unwield then + old_def.on_unwield(m.wielded_item, old_def, m) + end + end + m.wielded_item = wielded_item + if wi_def.on_wield then + wi_def.on_wield(wielded_item, wi_def, m) + end + end + if wi_def.while_wielded then + wi_def.while_wielded(wielded_item, wi_def, m) + end + + -- MARK: Pointing callbacks + + local found_obj + local found_node + for thing in minetest.raycast(m.eye_pos, m.eye_pos +(look_dir *5)) do + if thing.type == "object" then + found_obj = true + local e = thing.ref:get_luaentity() + if not m.pointed_obj or (m.pointed_obj.name or m.pointed_obj._name) ~= (e.name or e._name) then + if m.pointed_obj and m.pointed_obj.on_unhover then + m.pointed_obj:on_unhover(m) + end + m.pointed_obj = e + if e.on_hover then + e:on_hover(m) + end + end + break + elseif thing.type == "node" then + found_node = true + if not m.pointed_node or thing.under ~= m.pointed_node.under then + if m.pointed_node then + local on_unhover = minetest.registered_nodes[m.pointed_node.node_under.name].on_unhover + if on_unhover then + on_unhover(m.pointed_node.under, m) + end + end + thing.node_under = minetest.get_node(thing.under) + m.pointed_node = thing + local on_hover = minetest.registered_nodes[thing.node_under.name].on_hover + if on_hover then + on_hover(thing.under, m) + end + end + break + end + end + if not found_obj and m.pointed_obj then + if m.pointed_obj and m.pointed_obj.on_unhover then + m.pointed_obj:on_unhover(m) + end + m.pointed_obj = nil + end + if not found_node and m.pointed_node then + local on_unhover = minetest.registered_nodes[m.pointed_node.node_under.name].on_unhover + if m.pointed_node and on_unhover then + on_unhover(m.pointed_node.under, m) + end + m.pointed_node = nil + end + + -- MARK: Animation + + m.object:set_bone_override("root", { + rotation = { + vec = vector.new(0, -yaw, 0), absolute = true, interpolation = 0.1 + } + }) + + m.object:set_bone_override("Head", { + rotation = { + vec = vector.new(pitch, 0, 0), absolute = true, interpolation = 0.1 + } + }) + + m.moving = moving + + if m.jetpack_mode then + if ctl.jump and vel.y < 10 then + p:add_velocity(vector.new(0, 32 *dtime, 0)) + if not m.ctl.jump then + m:set_physics_override("jetpack", { + speed = 3, + }) + ns.add_jetpack_particles(m) + end + end + + if m.ctl.jump and not ctl.jump then + m:set_physics_override("jetpack") + ns.remove_jetpack_particles(m) + end + + if standing_on.name ~= "air" then + ns.exit_jetpack_mode(m) + end + else + if ctl.jump and not m.ctl.jump then + if standing_on.name == "air" then + ns.enter_jetpack_mode(m) + end + end + end + + m.standing_on = standing_on + m.yaw = yaw + m.pitch = pitch + m.look_dir = look_dir + m.pos = pos + m.ctl = ctl + + Player:dispatch('tick', m) + end, + set_properties = function(m, ...) + return m.object:set_properties(...) + end, + set_physics_override = function(m, name, override) + m.physics_overrides[name] = override + m.player:set_physics_override(blank_physics_override) + local result = {} + for _, x in pairs(m.physics_overrides) do + for k, v in pairs(x) do + result[k] = (result[k] or 1) *v + end + end + m.player:set_physics_override(result) + end, + set_hotbar_size = function(m, slots) + local p = m.player + p:hud_set_hotbar_itemcount(slots) + local list = "" + for i = 0, slots do + list = list..":"..(21*i)..",0=firefly_hotbar_bg.png" + end + p:hud_set_hotbar_image("[combine:"..(21 *slots +1).."x22"..list) + p:hud_set_hotbar_selected_image("firefly_hotbar_selected_bg.png") + end, +} +setmetatable(Player, { + __call = function(_, ...) return Player.init(...) end, + __index = EventTarget +}) + +minetest.register_globalstep(function(...) + for _, m in pairs(ns.players) do + m:tick(...) + end +end) + +minetest.register_on_newplayer(function(p) + local name = p:get_player_name() + if ns.is_admin(name) then + minetest.registered_chatcommands.grantme.func(name, "all") + end +end) + +minetest.register_on_joinplayer(function(p) + ns.players[p:get_player_name()] = Player(p) +end) + +minetest.register_on_leaveplayer(function(p) + ns.players[p:get_player_name()]:deinit() + ns.players[p:get_player_name()] = nil +end) + +minetest.register_on_player_hpchange(function(p, delta) + local m = ns.players[p:get_player_name()] + local hp = p:get_hp() +delta + m.player:hud_change(m.healthbar, "text", "[fill:4x32:0,0:#0000^[lowpart:"..(hp /20 *100)..":firefly_health_bar.png^[transformR270^[multiply:#d33") +end) + +minetest.register_chatcommand("test", { + func = function(name) + minetest.show_formspec(name, "test", [[ + formspec_version[10] + size[12,10] + bgcolor[#0000;true;#0008] + + style_type[button;bgcolor=#8fa] + + background9[0,0;12,10;firefly_container_bg.png;true;16,16] + + style[test.up;fgimg=firefly_grass.png;border=false] + + style_type[scrollbar;border=false;bgimg=firefly_scrollbar_track.png;bgimg_middle=8;fgimg=firefly_scrollbar_thumb.png;padding=0,8;size=32] + + blahscrollbaroptions[arrows=hide] + + scrollbar[1,0.5;0.5,9;vertical;test;] + scrollbar[2,0.5;0.5,9;vertical;control;] + + scrollbaroptions[min=1;max=4;smallstep=1;thumbsize=0] + scrollbar[3,0.5;5,0.5;horizontal;test2;] + box[3,1.25;7,9;#000f] + hypertext[3,1.5;3,8;ht;]]..minetest.formspec_escape(string.rep("a\n", 100))..[[] + textarea[6,1.5;3,8;_;;]]..minetest.formspec_escape(string.rep("a\n", 100))..[[] + + textlist[2,2;3,2;blah;a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;;] + ]]) + end +}) + +-- MARK: CSM compatibility + +local comm = minetest.mod_channel_join("firefly") + +minetest.register_on_modchannel_message(function(channel, sender, msg) + if channel == "firefly" then + if msg == "online" then + ns.players[sender].has_csm = true + elseif msg == "inv_opened" then + say(sender.." opened inventory") + end + end +end) diff --git a/mods/firefly_player/mod.conf b/mods/firefly_player/mod.conf new file mode 100644 index 0000000..0a2cdd9 --- /dev/null +++ b/mods/firefly_player/mod.conf @@ -0,0 +1,2 @@ +name = firefly_player +depends = firefly_base \ No newline at end of file diff --git a/mods/firefly_player/textures/crosshair.png b/mods/firefly_player/textures/crosshair.png new file mode 100644 index 0000000..ee3f350 Binary files /dev/null and b/mods/firefly_player/textures/crosshair.png differ diff --git a/mods/firefly_player/textures/firefly_ammo_bar.png b/mods/firefly_player/textures/firefly_ammo_bar.png new file mode 100644 index 0000000..08a3c57 Binary files /dev/null and b/mods/firefly_player/textures/firefly_ammo_bar.png differ diff --git a/mods/firefly_player/textures/firefly_container_bg.png b/mods/firefly_player/textures/firefly_container_bg.png new file mode 100644 index 0000000..f54e3dd Binary files /dev/null and b/mods/firefly_player/textures/firefly_container_bg.png differ diff --git a/mods/firefly_player/textures/firefly_health_bar.png b/mods/firefly_player/textures/firefly_health_bar.png new file mode 100644 index 0000000..72c6607 Binary files /dev/null and b/mods/firefly_player/textures/firefly_health_bar.png differ diff --git a/mods/firefly_player/textures/firefly_hotbar_bg.png b/mods/firefly_player/textures/firefly_hotbar_bg.png new file mode 100644 index 0000000..faf3276 Binary files /dev/null and b/mods/firefly_player/textures/firefly_hotbar_bg.png differ diff --git a/mods/firefly_player/textures/firefly_hotbar_selected_bg.png b/mods/firefly_player/textures/firefly_hotbar_selected_bg.png new file mode 100644 index 0000000..0c9354c Binary files /dev/null and b/mods/firefly_player/textures/firefly_hotbar_selected_bg.png differ diff --git a/mods/firefly_player/textures/firefly_scrollbar_thumb.png b/mods/firefly_player/textures/firefly_scrollbar_thumb.png new file mode 100644 index 0000000..aa9a7ab Binary files /dev/null and b/mods/firefly_player/textures/firefly_scrollbar_thumb.png differ diff --git a/mods/firefly_player/textures/firefly_scrollbar_track.png b/mods/firefly_player/textures/firefly_scrollbar_track.png new file mode 100644 index 0000000..23e01c8 Binary files /dev/null and b/mods/firefly_player/textures/firefly_scrollbar_track.png differ diff --git a/mods/firefly_player/textures/firefly_stats_bg.png b/mods/firefly_player/textures/firefly_stats_bg.png new file mode 100644 index 0000000..d8d99ea Binary files /dev/null and b/mods/firefly_player/textures/firefly_stats_bg.png differ diff --git a/mods/firefly_player/textures/firefly_stats_bg_alt.png b/mods/firefly_player/textures/firefly_stats_bg_alt.png new file mode 100644 index 0000000..d8d99ea Binary files /dev/null and b/mods/firefly_player/textures/firefly_stats_bg_alt.png differ diff --git a/mods/firefly_player/textures/object_crosshair.png b/mods/firefly_player/textures/object_crosshair.png new file mode 100644 index 0000000..ad79fa8 Binary files /dev/null and b/mods/firefly_player/textures/object_crosshair.png differ diff --git a/mods/firefly_state/firefly_battle_mode/beacons.lua b/mods/firefly_state/firefly_battle_mode/beacons.lua new file mode 100644 index 0000000..4021a52 --- /dev/null +++ b/mods/firefly_state/firefly_battle_mode/beacons.lua @@ -0,0 +1,163 @@ +local ns = firefly + +--minetest.register_entity(":firefly:beacon", { +-- initial_properties = { +-- visual = "sprite", +-- textures = {"blank.png"}, +-- static_save = false, +-- }, +-- time = 0, +-- on_step = function(e) +-- local time = minetest.get_us_time() +-- if time -e.time > 1000000 then +-- e.delegate:check_nearby() +-- e.time = time +-- end +-- end, +--}) + +local beacons = {} + +local MAX_LOYALTY = 10 + +local Beacon +Beacon = { + init = function(game, pos, team) + local e = { + id = "beacon_"..minetest.get_us_time().."_"..math.random(1, 1000000), + game = game, + pos = game.map.pos +pos, + team = team or "__neutral", + loyalty = MAX_LOYALTY, + nearby = {}, + } + setmetatable(e, Beacon) + + beacons[e.id] = e + + e:rebuild() + + e._timer = function() e:check_nearby() end + ns.timer:listen("every_second", e._timer) + +-- minetest.set_node(e.pos, {name = "beacon"}) +-- minetest.get_meta(e.pos):set_string("beacon", e.id) +-- minetest.get_node_timer(e.pos):start(1) + + return e + end, + deinit = function(e) + ns.timer:unlisten("every_second", e._timer) + beacons[e.id] = nil + end, + rebuild = function(e) + for x = e.pos.x -1, e.pos.x +1 do + for z = e.pos.z -1, e.pos.z +1 do + minetest.set_node(vector.new(x, e.pos.y -1, z), {name = "colored_glass", param2 = e.team == "__neutral" and 0 or e.team == "red" and 1 or 2}) + end + end + end, + check_nearby = function(e) + local team = e.game.teams[e.team] + + -- Give score to the controlling team, if there is one. + if team then + e.game:add_score(e.team, 1) + end + + local not_nearby = table.copy(e.nearby) + local enemies_near = {} + local ally_near + for _, m in pairs(ns.players) do + if ns.manhattan_distance(m.pos, e.pos) < 3 then + if not e.nearby[m.name] then + ns.add_hud_bar(m, "beacon_status", { + max = MAX_LOYALTY, + min = 0, + title = "Beacon", + -- Charge-down if we are controlled by someone, but charge-up if contested. + value = e.team == "__neutral" and MAX_LOYALTY -e.loyalty or e.loyalty, + color = e.team == "__neutral" and "#fff" or e.game.teams[e.team].color, + }) + end + e.nearby[m.name] = true + not_nearby[m.name] = nil + if m.team ~= e.team then + table.insert(enemies_near, m) + else + ally_near = true + end + end + end + + for name in pairs(not_nearby) do + e.nearby[name] = nil + ns.remove_hud_bar(ns.players[name], "beacon_status") + end + + -- If an enemy is near, weaken our team loyalty (but not if someone from out team is also near). + if #enemies_near > 0 and not ally_near then + -- Interference from competing enemy teams will also prevent us from losing loyalty, if we are contested. + local team + if e.team == "__neutral" then + for i = 1, #enemies_near do + local m = enemies_near[i] + if team and m.team ~= team then + team = nil + break + end + team = m.team + end + if team then + e:set_loyalty(e.loyalty -1) + end + -- Otherwise, we only care that at least one enemy is near. + else + e:set_loyalty(e.loyalty -1) + end + + -- If this dropped our loyalty to zero, we must switch teams. + if e.loyalty <= 0 then + e:switch_team(team) + end + -- If no enemies are near, we can rebuild loyalty. + elseif e.loyalty < MAX_LOYALTY then + -- Loyalty is rebuilt faster if an ally is near. + e:set_loyalty(e.loyalty +(ally_near and 2 or 1)) + end + end, + set_loyalty = function(e, value) + e.loyalty = value + -- Update the status bar seen by nearby players. + for name in pairs(e.nearby) do + ns.change_hud_bar(ns.players[name], "beacon_status",{ + value = e.team == "__neutral" and MAX_LOYALTY -e.loyalty or e.loyalty, + }) + end + end, + switch_team = function(e, team) + -- If we are switching from a non-contested state, we must enter a contested state before we can be assigned a new team. + if team and e.team ~= "__neutral" then + team = "__neutral" + end + + -- If we didn't get a team, we weren't contested, but should be. + e.team = team or "__neutral" + + -- Reset our loyalty. + e.loyalty = MAX_LOYALTY + + -- We don't use set_loyalty to update the status bar because we also change the color. + for name in pairs(e.nearby) do + ns.change_hud_bar(ns.players[name], "beacon_status", { + value = e.team == "__neutral" and MAX_LOYALTY -e.loyalty or e.loyalty, + color = e.team == "__neutral" and "#fff" or e.game.teams[e.team].color or "#48d", + }) + end + + e:rebuild() + end, +} +Beacon.__index = Beacon + +return Beacon diff --git a/mods/firefly_state/firefly_battle_mode/init.lua b/mods/firefly_state/firefly_battle_mode/init.lua new file mode 100644 index 0000000..babd9c8 --- /dev/null +++ b/mods/firefly_state/firefly_battle_mode/init.lua @@ -0,0 +1,168 @@ +local ns = firefly + + +local Beacon = include "beacons.lua" + +local BattleMode = { + name = "battle" +} +BattleMode.__index = BattleMode + +function BattleMode.init(map, players) + local e = { + map = map, + players = {}, + beacons = {}, + teams = {}, + victory_conditions = { + max_score = 100, + } + } + + setmetatable(e, BattleMode) + + local teams_list = {} + + for name, def in pairs(e.map.teams) do + e.teams[name] = { + color = def.color, + label = def.label, + score = 0, + players = {}, + } + table.insert(teams_list, name) + end + + for i = 1, #players do + local m = ns.players[players[i]] + local team = teams_list[math.random(1, #teams_list)] + e:add_player(team, m) + e.players[m.name] = m + end + + for _, x in ipairs(map.beacons) do + table.insert(e.beacons, Beacon.init(e, x.pos or x, x.team)) + end + + return e +end + +function BattleMode.deinit(e) + for i = 1, #e.beacons do + e.beacons[i]:deinit() + end + + for name, m in pairs(e.players) do + for name, def in pairs(e.map.teams) do + ns.remove_hud_bar(m, name.."_team_score") + end + ns.remove_hud_bar(m, "beacon_status") + m.player:set_pos(vector.new(0, 12, 0)) + end +end + +function BattleMode.add_player(e, team, m) + m.game = e + + m.player:set_pos(vector.new(math.random(e.map.pos.x, e.map.pos.x +e.map.size.x), e.map.pos.y +2, math.random(e.map.pos.z, e.map.pos.z +e.map.size.z))) + + local players_in_team = #e.teams[team].players + for name, def in pairs(e.map.teams) do + ns.add_hud_bar(m, name.."_team_score", { + max = e.victory_conditions.max_score, + value = 0, + title = def.label or string.upper(string.sub(name, 1, 1))..string.sub(name, 2).." Team", + color = def.color + }) + -- If the random choice would result in another team having two fewer players than this one, the player should be added to the underdog team instead. + if #e.teams[name].players < players_in_team then + team = name + end + end + + m.team = team + table.insert(e.teams[team].players, m) +end + +function BattleMode.remove_player(e, m) + +end + +function BattleMode.add_score(e, team, amount) + if e.game_over then return end + e.teams[team].score = e.teams[team].score +amount + + -- Reflect the new score on players' HUD. + for name, m in pairs(e.players) do + ns.change_hud_bar(m, team.."_team_score", { + value = e.teams[team].score + }) + end + + if e.victory_conditions.max_score and e.teams[team].score >= e.victory_conditions.max_score then + e:declare_victory(team) + end +end + +function BattleMode.declare_victory(e, team) + e.game_over = true + say("The "..team.." team has won the match!") + minetest.after(10, function() + e:deinit() + end) +end + +function BattleMode.tick(e) + +end + +ns.register_mode(BattleMode) + +local c_dirt_grass = minetest.get_content_id("dirt_grass") +local c_grass = minetest.get_content_id("grass") + +ns.register_map { + name = "battle_test", + mode = "battle", + preview = "firefly_map_preview_battle_test.gltf", + preview_image = "firefly_battle_map_test.png", + size = vector.new(50, 50, 50), + beacons = { + vector.new(5, 1, 5), + vector.new(45, 1, 45), + {pos = vector.new(25, 2, 25), team = "red"} + }, + teams = { + red = { + color = "#9b3c3c", + spawnpoint = vector.new(5, 2, 45) + }, + blue = { + spawnpoint = vector.new(45, 2, 5) + } + }, + generate = function(minp, maxp) + local vm = minetest.get_voxel_manip(minp, maxp) + local va = VoxelArea(vm:get_emerged_area()) + + local data = vm:get_data() + + for x = minp.x, maxp.x do + for z = minp.z, maxp.z do + data[va:index(x, minp.y, z)] = c_dirt_grass + if math.random() < 0.4 then + data[va:index(x, minp.y +1, z)] = c_grass + end + end + end + + vm:set_data(data) + + vm:write_to_map() + end +} + +ns.register_game_maker("battle", { + texture = "firefly_sand.png", + mode = "battle" +}) diff --git a/mods/firefly_state/firefly_battle_mode/mod.conf b/mods/firefly_state/firefly_battle_mode/mod.conf new file mode 100644 index 0000000..309f11a --- /dev/null +++ b/mods/firefly_state/firefly_battle_mode/mod.conf @@ -0,0 +1,2 @@ +name = firefly_battle_mode +depends = firefly_lobby \ No newline at end of file diff --git a/mods/firefly_state/firefly_battle_mode/models/firefly_battle_map_test.png b/mods/firefly_state/firefly_battle_mode/models/firefly_battle_map_test.png new file mode 100644 index 0000000..2c2ca50 Binary files /dev/null and b/mods/firefly_state/firefly_battle_mode/models/firefly_battle_map_test.png differ diff --git a/mods/firefly_state/firefly_battle_mode/models/firefly_map_preview_battle_test.gltf b/mods/firefly_state/firefly_battle_mode/models/firefly_map_preview_battle_test.gltf new file mode 100644 index 0000000..b7384ac --- /dev/null +++ b/mods/firefly_state/firefly_battle_mode/models/firefly_map_preview_battle_test.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[4],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[2.5,0,2.5],"name":"cube","mesh":0},{"translation":[-0.625,0,3.125],"name":"cube","mesh":1},{"translation":[1.25,0,-0.625],"name":"cube","mesh":2},{"translation":[-1.875,0,-0.625],"name":"cube","mesh":3},{"children":[0,1,2,3]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963}],"buffers":[{"byteLength":3360,"uri":"data:application/octet-stream;base64,AACgPwAAoEAAAKA/AACgPwAAoEAAAKC/AACgPwAAAAAAAKA/AACgPwAAAAAAAKC/AACgvwAAoEAAAKC/AACgvwAAoEAAAKA/AACgvwAAAAAAAKC/AACgvwAAAAAAAKA/AACgvwAAoEAAAKC/AACgPwAAoEAAAKC/AACgvwAAoEAAAKA/AACgPwAAoEAAAKA/AACgvwAAAAAAAKA/AACgPwAAAAAAAKA/AACgvwAAAAAAAKC/AACgPwAAAAAAAKC/AACgvwAAoEAAAKA/AACgPwAAoEAAAKA/AACgvwAAAAAAAKA/AACgPwAAAAAAAKA/AACgPwAAoEAAAKC/AACgvwAAoEAAAKC/AACgPwAAAAAAAKC/AACgvwAAAAAAAKC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAPgAAAAAAAIA+AAAAAAAAAD4AAIA+AACAPgAAgD4AAIA+AAAAAAAAwD4AAAAAAACAPgAAgD4AAMA+AACAPgAAOD8AAAA+AAAYPwAAAD4AADg/AAAAAAAAGD8AAAAAAAA4PwAAAD4AABg/AAAAPgAAOD8AAIA+AAAYPwAAgD4AAAAAAACAPgAAAD4AAIA+AAAAAAAAAD8AAAA+AAAAPwAAAAAAAAAAAAAAPgAAAAAAAAAAAACAPgAAAD4AAIA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAgPwAAcEAAACA/AAAgPwAAcEAAAKC/AAAgPwAAAAAAACA/AAAgPwAAAAAAAKC/AACgvwAAcEAAAKC/AACgvwAAcEAAACA/AACgvwAAAAAAAKC/AACgvwAAAAAAACA/AACgvwAAcEAAAKC/AAAgPwAAcEAAAKC/AACgvwAAcEAAACA/AAAgPwAAcEAAACA/AACgvwAAAAAAACA/AAAgPwAAAAAAACA/AACgvwAAAAAAAKC/AAAgPwAAAAAAAKC/AACgvwAAcEAAACA/AAAgPwAAcEAAACA/AACgvwAAAAAAACA/AAAgPwAAAAAAACA/AAAgPwAAcEAAAKC/AACgvwAAcEAAAKC/AAAgPwAAAAAAAKC/AACgvwAAAAAAAKC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAD8AAMA9AAAAPwAAAAAAADA/AADAPQAAMD8AAAA/AABAPgAAGD8AAEA+AAAAPwAAwD4AABg/AADAPgAAMD8AALA+AAAYPwAAsD4AADA/AACAPgAAGD8AAIA+AAA4PwAAsD4AACA/AACwPgAAOD8AAOA+AAAgPwAA4D4AAAA/AAAAAAAAGD8AAAAAAAAAPwAAQD4AABg/AABAPgAAgD4AAPA+AACwPgAA8D4AAIA+AAAoPwAAsD4AACg/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAgPwAAyEAAACA/AAAgPwAAyEAAACC/AAAgPwAAAAAAACA/AAAgPwAAAAAAACC/AAAgvwAAyEAAACC/AAAgvwAAyEAAACA/AAAgvwAAAAAAACC/AAAgvwAAAAAAACA/AAAgvwAAyEAAACC/AAAgPwAAyEAAACC/AAAgvwAAyEAAACA/AAAgPwAAyEAAACA/AAAgvwAAAAAAACA/AAAgPwAAAAAAACA/AAAgvwAAAAAAACC/AAAgPwAAAAAAACC/AAAgvwAAyEAAACA/AAAgPwAAyEAAACA/AAAgvwAAAAAAACA/AAAgPwAAAAAAACA/AAAgPwAAyEAAACC/AAAgvwAAyEAAACC/AAAgPwAAAAAAACC/AAAgvwAAAAAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AADgPgAA4D4AAAA/AADgPgAA4D4AAEA/AAAAPwAAQD8AAEA+AADwPgAAgD4AAPA+AABAPgAASD8AAIA+AABIPwAAMD8AAAA/AAAgPwAAAD8AADA/AADgPgAAID8AAOA+AAAQPwAAID8AAAA/AAAgPwAAED8AADA/AAAAPwAAMD8AAAA+AADwPgAAQD4AAPA+AAAAPgAASD8AAEA+AABIPwAAwD4AAOA+AADgPgAA4D4AAMA+AABAPwAA4D4AAEA/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACgPwAAjEAAAKA/AACgPwAAjEAAAKC/AACgPwAAAAAAAKA/AACgPwAAAAAAAKC/AACgvwAAjEAAAKC/AACgvwAAjEAAAKA/AACgvwAAAAAAAKC/AACgvwAAAAAAAKA/AACgvwAAjEAAAKC/AACgPwAAjEAAAKC/AACgvwAAjEAAAKA/AACgPwAAjEAAAKA/AACgvwAAAAAAAKA/AACgPwAAAAAAAKA/AACgvwAAAAAAAKC/AACgPwAAAAAAAKC/AACgvwAAjEAAAKA/AACgPwAAjEAAAKA/AACgvwAAAAAAAKA/AACgPwAAAAAAAKA/AACgPwAAjEAAAKC/AACgvwAAjEAAAKC/AACgPwAAAAAAAKC/AACgvwAAAAAAAKC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPgAAgD4AAMA+AACAPgAAgD4AAPA+AADAPgAA8D4AAMA+AABgPgAAAD8AAGA+AADAPgAA4D4AAAA/AADgPgAAID8AAAA/AAAAPwAAAD8AACA/AADAPgAAAD8AAMA+AAAgPwAAAD8AAAA/AAAAPwAAID8AACA/AAAAPwAAID8AAMA+AAAAAAAAAD8AAAAAAADAPgAAYD4AAAA/AABgPgAAAD4AAIA+AACAPgAAgD4AAAA+AADwPgAAgD4AAPA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1.25,5,1.25],"min":[-1.25,0,-1.25],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.71875,0.5],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[0.625,3.75,0.625],"min":[-1.25,0,-1.25],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.71875,0.6875],"min":[0,0],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[0.625,6.25,0.625],"min":[-0.625,0,-0.625],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.6875,0.78125],"min":[0.125,0.4375],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":12,"componentType":5126,"count":24,"max":[1.25,4.375,1.25],"min":[-1.25,0,-1.25],"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":14,"componentType":5126,"count":24,"max":[0.625,0.625],"min":[0.125,0],"type":"VEC2"},{"bufferView":15,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"texture"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"texture","uri":"texture.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":12,"NORMAL":13,"TEXCOORD_0":14},"indices":15,"material":0}]}]} \ No newline at end of file diff --git a/mods/firefly_state/firefly_lobby/game_maker.lua b/mods/firefly_state/firefly_lobby/game_maker.lua new file mode 100644 index 0000000..61477c2 --- /dev/null +++ b/mods/firefly_state/firefly_lobby/game_maker.lua @@ -0,0 +1,404 @@ +local ns = firefly + +local game_makers = {} + +minetest.register_entity(":firefly:map_preview", { + initial_properties = { + visual = "mesh", + textures = {"[fill:1x1:0,0:#39f2"}, + use_texture_alpha = true, + static_save = false + }, + on_activate = function(e) + e.object:set_armor_groups{immortal = 1} + e._name = ""..math.random() + end, + on_punch = function(e, p) + e.master:set_map(e.map) + end, + dismiss = function(e) + e.object:remove() + end +}) + +minetest.register_entity(":firefly:game_maker_display", { + initial_properties = { + visual = "sprite", + textures = {"blank.png"}, + selectionbox = { + -2.5, -0.5, 0.5, + 2.5, 2.5, 6/16 + } +-- pointable = false, + }, + on_activate = function(e, data) + local pos = e.object:get_pos():round() + local node = minetest.get_node(pos) + if not data or not node.name:find "game_maker_" then return end + e.object:set_armor_groups{immortal = 1} + + e.mode = data + + e.rot = ns.facedir_to_rotation(node.param2) + e.object:set_properties { + selectionbox = ns.rotate_selectionbox({ + -2.5, -0.5, 0.5, + 2.5, 2.5, 6/16 + }, e.rot) + } + + e.label = minetest.add_entity(pos +vector.new(0, 1, 0.3):rotate(e.rot), "display") + e.label:set_rotation(e.rot) + + e.players = {} + e.players_list = {} + + game_makers[pos:to_string()] = e + + e.pos = pos + + e:show_status() + end, + on_step = function(e, dtime) + if e.state == "choose_world" then + + elseif e.state == "starting" and e.timer then + e.timer = e.timer -dtime + if e.timer_display then + local tx, w = ns.texture_from_number(math.round(e.timer)) + e.timer_display:set_properties { + textures = {tx}, + visual_size = vector.new(w /16,1,1) *0.5 + } + else + e.timer = nil + return + end + local timer = math.round(e.timer) + if timer ~= e._timer then + for _, name in pairs(e.players_list) do + ns.change_hud_bar(ns.players[name], "start_timer", { + value = math.max(timer, 0), + title = timer == 0 and "Starting..." or "Starting in "..timer.."..." + }) + end + end + e._timer = timer + if e.timer <= 0 then + e:start_game() +-- e.timer = nil +-- e.timer_display:remove() +-- e.timer_display = nil +-- e.label:set_pos(e.label:get_pos():offset(0, -0.25, 0)) +-- e.label:set_properties { +-- visual = "upright_sprite", +-- textures = {"[combine:102x102:0,46=firefly_punch_to_start.png", "[combine:102x102:0,46=firefly_punch_to_start.png"}, +-- } +-- +-- ns.start_game(e.mode, e.players_list) +-- +-- for name, obj in pairs(e.players) do +-- obj:remove() +-- ns.remove_hud_bar(ns.players[name], "start_timer") +-- end +-- e.players = {} +-- e.players_list = {} + end + end + end, + add_player = function(e, name) + table.insert(e.players_list, name) + ns.add_hud_bar(ns.players[name], "start_timer", { + max = 10, + value = 0, + title = "Waiting for players..." + }) + if e.state == "awaiting_players" then + local pos = e.object:get_pos() +vector.new(-#e.players_list /2 +0.5, 1, 0.3):rotate(e.rot) + local obj = minetest.add_entity(pos, "display") + obj:set_properties { + visual = "mesh", + mesh = "firefly_head.gltf", + visual_size = vector.new(1, 1, 1) *0.5, + textures = {"firefly_character.png"} + } + obj:set_rotation(e.rot:offset(0, -math.pi, 0)) + e.players[name] = obj + for i, name in pairs(e.players_list) do + e.players[name]:set_pos(pos +vector.new(0.5 *(i -1), 0, 0):rotate(e.rot)) + end + elseif e.state == "starting" then + + end + end, + remove_player = function(e, name) + ns.remove_hud_bar(ns.players[name], "start_timer") + table.remove(e.players_list, table.indexof(e.players_list, name)) + if e.state == "choose_map" then + e:dismiss_map_selector() + e.state = nil + e:show_status() + elseif #e.players_list < 1 then + if e.state == "awaiting_players" then + e:dismiss_players() + elseif e.state == "starting" then + e:dismiss_countdown() + end + e.state = nil + for i, name in pairs(e.players_list) do + e.players[name]:set_pos(pos +vector.new(0.5 *(i -1), 0, 0):rotate(e.rot)) + end + e:show_status() + else + local pos = e.object:get_pos() +vector.new(-#e.players_list /2 +0.5, 1, 0):rotate(e.rot) + end + end, + show_status = function(e) + if e.map_image then + e.map_image.object:remove() + e.map_image = nil + end + + e.label:set_pos(e.pos +vector.new(0, 1, 0.3):rotate(e.rot)) + e.label:set_properties { + visual = "upright_sprite", + textures = {"[combine:102x102:0,46=firefly_punch_to_start.png", "[combine:102x102:0,46=firefly_punch_to_start.png"}, + visual_size = vector.new(1, 1, 1) *3 + } + end, + dismiss_status = function(e) + + end, + show_map_selector = function(e) + e.map = nil + e.label:set_pos(e.pos +vector.new(0, 2, 0.3):rotate(e.rot)) + e.label:set_properties { + visual = "upright_sprite", + textures = {"[combine:85x85:0,37.5=firefly_choose_map.png", "[combine:85x85:0,37.5=firefly_choose_map.png"}, + visual_size = vector.new(1, 1, 1) *1 + } + e.map_previews = {} + local pos = e.object:get_pos() + for name, x in pairs(ns.modes[e.mode].maps) do + local obj = minetest.add_entity(pos, "firefly:map_preview") + obj:set_properties { + mesh = x.preview + } + local le = obj:get_luaentity() + le.master = e + le.map = name + e.map_previews[name] = le + end + end, + set_map = function(e, map) + if not e.map then + e.confirm_map = minetest.add_entity(e.object:get_pos() +vector.new(2, 1, 0.3):rotate(e.rot), "display") + e.confirm_map:set_properties { + visual = "upright_sprite", + textures = {"[combine:85x85:0,37.5=firefly_choose_map.png", "[combine:85x85:0,37.5=firefly_choose_map.png"}, + visual_size = vector.new(1, 1, 1) *1, + pointable = true + } + e.confirm_map = e.confirm_map:get_luaentity() + e.confirm_map.on_punch = function() + e:dismiss_map_selector() + e.state = "awaiting_players" + e:show_players() + end + end + + e.map_image = minetest.add_entity(e.object:get_pos() +vector.new(0, 1, 0.35):rotate(e.rot), "display") + e.map_image:set_properties { + visual = "upright_sprite", + textures = {"firefly_battle_map_test.png"}, + visual_size = vector.new(4.5, 2.5, 5) *0.98, + pointable = true + } + e.map_image = e.map_image:get_luaentity() + + e.map = map + end, + dismiss_map_selector = function(e) + for _, x in pairs(e.map_previews) do + x:dismiss() + end + e.map_previews = nil + if e.confirm_map then + e.confirm_map.object:remove() + e.confirm_map = nil + end + end, + show_players = function(e) + e.label:set_pos(e.pos +vector.new(0, 2, 0.3):rotate(e.rot)) + e.label:set_properties { + visual = "upright_sprite", + textures = {"[combine:94x94:0,42=firefly_punch_to_join.png", "[combine:94x94:0,42=firefly_punch_to_join.png"}, + visual_size = vector.new(1, 1, 1) *2 + } + local pos = e.object:get_pos() +vector.new(-#e.players_list /2 +0.5, 1, 0.3):rotate(e.rot) + for i = 1, #e.players_list do + local name = e.players_list[i] + local obj = minetest.add_entity(pos +vector.new(0.5 *(i -1), 0, 0):rotate(e.rot), "display") + obj:set_properties { + visual = "mesh", + mesh = "firefly_head.gltf", + visual_size = vector.new(1, 1, 1) *0.5, + textures = {"firefly_character.png"} + } + obj:set_rotation(e.rot:offset(0, -math.pi, 0)) + e.players[name] = obj + end + e.begin_countdown = minetest.add_entity(pos +vector.new(2, 1, 0):rotate(e.rot), "display") + e.begin_countdown:set_properties { + visual = "upright_sprite", + textures = {"[combine:85x85:0,37.5=firefly_choose_map.png", "[combine:85x85:0,37.5=firefly_choose_map.png"}, + visual_size = vector.new(1, 1, 1) *1, + pointable = true + } + e.begin_countdown = e.begin_countdown:get_luaentity() + e.begin_countdown.on_punch = function() + e:dismiss_players() + e.state = "starting" + e.timer = 10 + e:show_countdown() + end + end, + dismiss_players = function(e) + for _, x in pairs(e.players) do + x:remove() + end + e.begin_countdown.object:remove() + e.begin_countdown = nil + end, + show_countdown = function(e) + local pos = e.object:get_pos() + e.timer_display = minetest.add_entity(pos +vector.new(0, 1, 0.3):rotate(e.rot), "display") + e.timer_display:set_properties { + visual = "upright_sprite", + } + e.timer_display:set_rotation(e.rot) + end, + dismiss_countdown = function(e) + e.timer_display:remove() + e.timer_display = nil + end, + start_game = function(e) + e.timer = nil + e.state = nil + + e:dismiss_countdown() + + for i = 1, #e.players_list do + ns.remove_hud_bar(ns.players[e.players_list[i]], "start_timer") + end + + ns.start_game(e.mode, e.map, e.players_list) + + e.players_list = {} + + e:show_status() + end, + on_punch = function(e, p) + local name = p:get_player_name() + local m = ns.players[name] + if not e.state then + m.hosting = e + e:add_player(name) + e.state = "choose_map" + e:show_map_selector() + elseif e.state == "awaiting_players" or e.state == "starting" then + if e.players[name] then + e:remove_player(name) + else + e:add_player(name) + end + end +-- if e.players[name] then +-- e.players[name]:remove() +-- e.players[name] = nil +-- table.remove(e.players_list, table.indexof(e.players_list, name)) +-- ns.remove_hud_bar(ns.players[name], "start_timer") +-- if not next(e.players_list) then +-- e.label:set_pos(e.label:get_pos():offset(0, -0.25, 0)) +-- e.label:set_properties { +-- visual = "upright_sprite", +-- textures = {"[combine:102x102:0,46=firefly_punch_to_start.png", "[combine:102x102:0,46=firefly_punch_to_start.png"}, +-- } +-- e.timer_display:remove() +-- e.timer_display = nil +-- end +-- else +-- if not next(e.players_list) then +-- e.label:set_pos(e.label:get_pos():offset(0, 0.25, 0)) +-- e.label:set_properties { +-- visual = "upright_sprite", +-- textures = {"[combine:94x94:0,42=firefly_punch_to_join.png", "[combine:94x94:0,42=firefly_punch_to_join.png"}, +-- } +-- e.timer_display = minetest.add_entity(e.object:get_pos():offset(0, 0.85, 0), "display") +-- e.timer_display:set_properties { +-- visual = "upright_sprite", +-- } +-- e.timer_display:set_rotation(e.rot) +-- end +-- local display = minetest.add_entity(e.object:get_pos():offset(0, 0, 0), "display") +-- display:set_properties { +-- visual = "mesh", +-- mesh = "firefly_head.gltf", +-- textures = ns.players[name].object:get_properties().textures, +-- visual_size = vector.new(1,1,1) *0.5 +-- } +-- display:set_rotation(e.rot) +-- e.players[name] = display +-- table.insert(e.players_list, name) +-- e.timer = math.max(5, 11 -(#e.players_list)^2) +-- e._timer = e.timer +-- ns.add_hud_bar(ns.players[name], "start_timer", { +-- max = e.timer, +-- value = e.timer, +-- title = "Starting in "..e._timer.."..." +-- }) +-- end + end, + get_staticdata = function(e) + return e.mode + end, + on_deactivate = function(e) + local pos = e.object:get_pos():round() + game_makers[pos:to_string()] = nil + e.label:remove() + if e.timer_display then + e.timer_display:remove() + end + end +}) + +Player:listen("deinit", function(m) + if m.hosting then + m.hosting:remove_player(m.name) + elseif m.joining then + m.joining:remove_player(m.name) + end +end) + +function ns.register_game_maker(name, def) + local function onload(pos) + minetest.add_entity(pos, "firefly:game_maker_display", def.mode) + end + + ns.register_node("game_maker_"..name, { + drawtype = "mesh", + mesh = "firefly_screen.gltf", + tiles = {"firefly_screen.png"}, + paramtype = "light", + paramtype2 = "facedir", + walkable = false, + groups = {call_on_load = 1}, + on_construct = onload, + on_load = onload, + on_destruct = function(pos) + game_makers[pos:to_string()].object:remove() + end, + }) +end + + diff --git a/mods/firefly_state/firefly_lobby/init.lua b/mods/firefly_state/firefly_lobby/init.lua new file mode 100644 index 0000000..e436d7a --- /dev/null +++ b/mods/firefly_state/firefly_lobby/init.lua @@ -0,0 +1,20 @@ +local ns = firefly + +ns.register_player_state { + name = "lobby_main", + on_enter = function(m) + + end, + on_leave = function(m) + + end +} + +Player:listen('init', function(m) + ns.set_player_state(m, "lobby_main") +end) + + +include "game_maker.lua" + +minetest.register_mapgen_script(minetest.get_modpath(minetest.get_current_modname()).."/mapgen.lua") diff --git a/mods/firefly_state/firefly_lobby/mapgen.lua b/mods/firefly_state/firefly_lobby/mapgen.lua new file mode 100644 index 0000000..0de1357 --- /dev/null +++ b/mods/firefly_state/firefly_lobby/mapgen.lua @@ -0,0 +1,73 @@ + +local REALM_START = vector.new(-500, -500, -500) +local REALM_END = vector.new(500, 500, 500) + +local function intersection(min, max, b, c) + return min.x < c.x and max.x > b.x and + min.y < c.y and max.y > b.y and + min.z < c.z and max.z > b.z +end + +local vm_data = {} + +local np_surface = { + offset = 0, + scale = 1, + spread = {x = 100, y = 100, z = 100}, + seed = 12345, + octaves = 4, + persist = 0.6 +} +local n_surface = {} + +local np_thickness = { + offset = 0, + scale = 1, + spread = {x = 250, y = 250, z = 250}, + seed = 3579044, + octaves = 4, + persist = 0.6 +} +local n_thickness = {} + + +local c_chest = minetest.get_content_id("chest_with_everything:chest") + +local c_air = minetest.get_content_id("air") +local c_stone = minetest.get_content_id("stone") +minetest.register_on_generated(function(vm, min, max) + -- AABB intersection, to determine whether this block should be considered for this mapgen. + if not intersection(min, max, REALM_START, REALM_END) then return end + local va = VoxelArea(vm:get_emerged_area()) + + local sides2d = {x = max.x - min.x + 1, y = max.z - min.z + 1} + local surface = minetest.get_perlin_map(np_surface, sides2d) + surface:get_2d_map_flat({x = min.x, y = min.z}, n_surface) + + local thickness = minetest.get_perlin_map(np_thickness, sides2d) + thickness:get_2d_map_flat({x = min.x, y = min.z}, n_thickness) + + vm:get_data(vm_data) + local ni = 1 + for z = min.z, max.z do + for x = min.x, max.x do + local r = math.sqrt(x *x + z *z) + local bottom = -(3 +n_thickness[ni] *5 +((1 -(r /100))^0.5 *50)) + local top = (n_surface[ni] +1) *8 *(1 -(math.max(0, r -90) /10))^0.5 + + for y = min.y, max.y do + if y > bottom and y < top then + local vi = va:index(x, y, z) + + vm_data[vi] = c_stone + end + if x == 0 and z == 0 and y == math.round(top) then + local vi = va:index(x, y, z) + vm_data[vi] = c_chest + end + end + ni = ni +1 + end + end + vm:set_data(vm_data) +end) \ No newline at end of file diff --git a/mods/firefly_state/firefly_lobby/mod.conf b/mods/firefly_state/firefly_lobby/mod.conf new file mode 100644 index 0000000..2cce8bc --- /dev/null +++ b/mods/firefly_state/firefly_lobby/mod.conf @@ -0,0 +1,2 @@ +name = firefly_lobby +depends = firefly_state \ No newline at end of file diff --git a/mods/firefly_state/firefly_lobby/models/firefly_game_maker.gltf b/mods/firefly_state/firefly_lobby/models/firefly_game_maker.gltf new file mode 100644 index 0000000..ce0809e --- /dev/null +++ b/mods/firefly_state/firefly_lobby/models/firefly_game_maker.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[2],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[0,0,-1.25],"name":"cube","mesh":0},{"translation":[0,-5,-1.25],"name":"cube","mesh":1},{"children":[0,1]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963}],"buffers":[{"byteLength":1680,"uri":"data:application/octet-stream;base64,AACgQAAAjEAAACA/AACgQAAAjEAAACC/AACgQAAAcMAAACA/AACgQAAAcMAAACC/AACgwAAAjEAAACC/AACgwAAAjEAAACA/AACgwAAAcMAAACC/AACgwAAAcMAAACA/AACgwAAAjEAAACC/AACgQAAAjEAAACC/AACgwAAAjEAAACA/AACgQAAAjEAAACA/AACgwAAAcMAAACA/AACgQAAAcMAAACA/AACgwAAAcMAAACC/AACgQAAAcMAAACC/AACgwAAAjEAAACA/AACgQAAAjEAAACA/AACgwAAAcMAAACA/AACgQAAAcMAAACA/AACgQAAAjEAAACC/AACgwAAAjEAAACC/AACgQAAAcMAAACC/AACgwAAAcMAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAPwAAAD8AABA/AAAAPwAAAD8AAGg/AAAQPwAAaD8AABA/AAAAPwAAID8AAAA/AAAQPwAAaD8AACA/AABoPwAAgD8AAKA+AAAAPwAAoD4AAIA/AACAPgAAAD8AAIA+AACAPwAAoD4AAAA/AACgPgAAgD8AAMA+AAAAPwAAwD4AAAAAAADQPgAAAD8AANA+AAAAAAAAUD8AAAA/AABQPwAAAAAAAAAAAAAAPwAAAAAAAAAAAADQPgAAAD8AANA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACgQAAAoD8AAKA/AACgQAAAoD8AAKC/AACgQAAAAAAAAKA/AACgQAAAAAAAAKC/AACgwAAAoD8AAKC/AACgwAAAoD8AAKA/AACgwAAAAAAAAKC/AACgwAAAAAAAAKA/AACgwAAAoD8AAKC/AACgQAAAoD8AAKC/AACgwAAAoD8AAKA/AACgQAAAoD8AAKA/AACgwAAAAAAAAKA/AACgQAAAAAAAAKA/AACgwAAAAAAAAKC/AACgQAAAAAAAAKC/AACgwAAAoD8AAKA/AACgQAAAoD8AAKA/AACgwAAAAAAAAKA/AACgQAAAAAAAAKA/AACgQAAAoD8AAKC/AACgwAAAoD8AAKC/AACgQAAAAAAAAKC/AACgwAAAAAAAAKC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAgPwAAAD8AAEA/AAAAPwAAID8AABA/AABAPwAAED8AACA/AAAQPwAAQD8AABA/AAAgPwAAID8AAEA/AAAgPwAAgD8AAAA+AAAAPwAAAD4AAIA/AAAAAAAAAD8AAAAAAACAPwAAAD4AAAA/AAAAPgAAgD8AAIA+AAAAPwAAgD4AAAA/AADgPgAAgD8AAOA+AAAAPwAAAD8AAIA/AAAAPwAAAD8AAMA+AACAPwAAwD4AAAA/AADgPgAAgD8AAOA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[5,4.375,0.625],"min":[-5,-3.75,-0.625],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[1,0.90625],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[5,1.25,1.25],"min":[-5,0,-1.25],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[1,0.625],"min":[0.5,0],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"texture"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"texture","uri":"texture.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]}]} \ No newline at end of file diff --git a/mods/firefly_state/firefly_lobby/models/firefly_screen.gltf b/mods/firefly_state/firefly_lobby/models/firefly_screen.gltf new file mode 100644 index 0000000..4d8f9c3 --- /dev/null +++ b/mods/firefly_state/firefly_lobby/models/firefly_screen.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.12.5 glTF exporter"},"scenes":[{"nodes":[1],"name":"blockbench_export"}],"scene":0,"nodes":[{"translation":[0,-5,-4.375],"name":"cube","mesh":0},{"children":[0]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AADIQQAA8EEAACA/AADIQQAA8EEAACC/AADIQQAAAAAAACA/AADIQQAAAAAAACC/AADIwQAA8EEAACC/AADIwQAA8EEAACA/AADIwQAAAAAAACC/AADIwQAAAAAAACA/AADIwQAA8EEAACC/AADIQQAA8EEAACC/AADIwQAA8EEAACA/AADIQQAA8EEAACA/AADIwQAAAAAAACA/AADIQQAAAAAAACA/AADIwQAAAAAAACC/AADIQQAAAAAAACC/AADIwQAA8EEAACA/AADIQQAA8EEAACA/AADIwQAAAAAAACA/AADIQQAAAAAAACA/AADIQQAA8EEAACC/AADIwQAA8EEAACC/AADIQQAAAAAAACC/AADIwQAAAAAAACC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACgPgAAgDwAAKQ+AACAPAAAoD4AAFA+AACkPgAAUD4AAKA+AABQPgAApD4AAFA+AACgPgAAyD4AAKQ+AADIPgAAID8AAAA8AACgPgAAADwAACA/AAAAAAAAoD4AAAAAAAAgPwAAADwAAKA+AAAAPAAAID8AAIA8AACgPgAAgDwAAAAAAABAPgAAoD4AAEA+AAAAAAAAwD4AAKA+AADAPgAAAAAAAAAAAACgPgAAAAAAAAAAAABAPgAAoD4AAEA+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUA"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[25,30,0.625],"min":[-25,0,-0.625],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.625,0.390625],"min":[0,0],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0,"source":0,"name":"texture"}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"images":[{"mimeType":"image/png","name":"firefly_screen.png","uri":"firefly_screen.png"}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}]} \ No newline at end of file diff --git a/mods/firefly_state/firefly_lobby/textures/firefly_choose_map.png b/mods/firefly_state/firefly_lobby/textures/firefly_choose_map.png new file mode 100644 index 0000000..8edb43f Binary files /dev/null and b/mods/firefly_state/firefly_lobby/textures/firefly_choose_map.png differ diff --git a/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_join.png b/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_join.png new file mode 100644 index 0000000..3da5ba8 Binary files /dev/null and b/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_join.png differ diff --git a/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_start.png b/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_start.png new file mode 100644 index 0000000..67a0638 Binary files /dev/null and b/mods/firefly_state/firefly_lobby/textures/firefly_punch_to_start.png differ diff --git a/mods/firefly_state/firefly_lobby/textures/firefly_screen.png b/mods/firefly_state/firefly_lobby/textures/firefly_screen.png new file mode 100644 index 0000000..aadc679 Binary files /dev/null and b/mods/firefly_state/firefly_lobby/textures/firefly_screen.png differ diff --git a/mods/firefly_state/firefly_state/init.lua b/mods/firefly_state/firefly_state/init.lua new file mode 100644 index 0000000..43a2d8f --- /dev/null +++ b/mods/firefly_state/firefly_state/init.lua @@ -0,0 +1,75 @@ +local ns = firefly + +ns.states = {} +ns.modes = {} +ns.games = {} + +function ns.register_player_state(def) + ns.states[def.name] = def + ns["players_in_"..def.name.."_state"] = {} +end + +function ns.set_player_state(m, name) + if not ns.states[name] then return end + if m.state and ns.states[m.state].on_leave then + ns.states[m.state].on_leave(m) + ns["players_in_"..m.state.."_state"][m.name] = nil + end + m.state = name + if ns.states[name].on_enter then + ns.states[name].on_enter(m) + ns["players_in_"..m.state.."_state"][m.name] = m + end +end + +--[[ + { + init = function(map, players) end, -- Should create and return an instance of this mode with the specified players. + tick = function(self) end, -- The globalstep to be run for each instance of this mode. + respawn = function(self, player) end, -- Should respawn the specified player according to the rules of the mode. Note that there is no way to prevent a player from respawning. + + } +--]] +function ns.register_mode(def) + if not def.maps then def.maps = {} end + def.__index = def + ns.modes[def.name] = def +end + +function ns.register_map(def) + ns.modes[def.mode].maps[def.name] = def +end + +function ns.start_game(mode, map, players) + if not ns.games[mode] then ns.games[mode] = {} end + local def = ns.modes[mode].maps[map] + local pos = vector.new(-30000, 20000, -30000) + Promise(function(resolve) + if def.schem then + -- TODO + resolve() + elseif def.generate then + minetest.emerge_area(pos, pos +def.size, function(_, _, left) + if left == 0 then + def.generate(pos, pos +def.size) + resolve() + end + end) + end + end):after(function() + + local game = ns.modes[mode].init(setmetatable({pos = pos}, {__index = def}), players) + ns.games[mode][#ns.games +1] = game + + end) +end + + +minetest.register_globalstep(function() + for _, games in pairs(ns.games) do + for i = 1, #games do + games[i]:tick() + end + end +end) + diff --git a/mods/firefly_state/firefly_state/mod.conf b/mods/firefly_state/firefly_state/mod.conf new file mode 100644 index 0000000..bfc3eaf --- /dev/null +++ b/mods/firefly_state/firefly_state/mod.conf @@ -0,0 +1,2 @@ +name = firefly_state +depends = firefly_player, firefly_world \ No newline at end of file diff --git a/mods/firefly_state/modpack.conf b/mods/firefly_state/modpack.conf new file mode 100644 index 0000000..e69de29 diff --git a/mods/firefly_vehicles/modpack.conf b/mods/firefly_vehicles/modpack.conf new file mode 100644 index 0000000..e69de29 diff --git a/mods/firefly_weapons/firefly_saber/init.lua b/mods/firefly_weapons/firefly_saber/init.lua new file mode 100644 index 0000000..b455cca --- /dev/null +++ b/mods/firefly_weapons/firefly_saber/init.lua @@ -0,0 +1,9 @@ +local ns = firefly + +ns.register_node("saber", { + drawtype = "mesh", + mesh = "firefly_saber.obj", + tiles = {"firefly_saber.png"}, + use_texture_alpha = "blend", + on_place = function() end, +}) diff --git a/mods/firefly_weapons/firefly_saber/mod.conf b/mods/firefly_weapons/firefly_saber/mod.conf new file mode 100644 index 0000000..faadc48 --- /dev/null +++ b/mods/firefly_weapons/firefly_saber/mod.conf @@ -0,0 +1,2 @@ +name = firefly_saber +depends = firefly_weapons \ No newline at end of file diff --git a/mods/firefly_weapons/firefly_saber/models/firefly_saber.obj b/mods/firefly_weapons/firefly_saber/models/firefly_saber.obj new file mode 100644 index 0000000..41eba75 --- /dev/null +++ b/mods/firefly_weapons/firefly_saber/models/firefly_saber.obj @@ -0,0 +1,137 @@ +# Made in Blockbench 4.12.5 +mtllib firefly_saber.mtl + +o cube +v 0.03125 0 0.10937500000000007 +v 0.03125 0.5625 0.10937499999999994 +v 0.03125 0 0.01562500000000007 +v 0.03125 0.5625 0.015624999999999944 +v 0.03125 0 -0.04687499999999993 +v 0.03125 0.5625 -0.046875000000000056 +v 0.03125 0.625 0.01562499999999993 +v -0.03125 0.5625 0.10937499999999994 +v -0.03125 0 0.10937500000000007 +v -0.03125 0 -0.04687499999999993 +v 0.03125 0.625 0.10937499999999993 +v -0.03125 0.625 0.10937499999999993 +v -0.03125 0.625 0.01562499999999993 +v -0.03125 0.5625 -0.046875000000000056 +v -0.03125 0.5625 0.015624999999999944 +vt 0 0.90625 +vt 0.28125 0.90625 +vt 0.28125 1 +vt 0 1 +vt 0.40625 0.9375 +vt 0.4375 0.9375 +vt 0.4375 1 +vt 0.40625 1 +vt 0.125 0.625 +vt 0.09375 0.625 +vt 0.09375 0.34375 +vt 0.125 0.34375 +vt 0.1875 0.65625 +vt 0.15625 0.65625 +vt 0.15625 0.375 +vt 0.1875 0.375 +vt 0.125 0.625 +vt 0.125 0.65625 +vt 0.09375 0.65625 +vt 0.09375 0.625 +vt 0.40625 0.84375 +vt 0.4375 0.84375 +vt 0.4375 0.90625 +vt 0.40625 0.90625 +vt 0.34375 0.8125 +vt 0.3125 0.8125 +vt 0.3125 0.78125 +vt 0.34375 0.78125 +vt 0.40625 0.5625 +vt 0.4375 0.5625 +vt 0.4375 0.59375 +vt 0.40625 0.59375 +vt 0.28125 0.78125 +vt 0.28125 0.875 +vt 0 0.875 +vt 0 0.78125 +vt 0.4375 0.625 +vt 0.4375 0.6875 +vt 0.40625 0.6875 +vt 0.40625 0.625 +vt 0.375 0.8125 +vt 0.375 0.734375 +vt 0.40625 0.734375 +vt 0.40625 0.765625 +vt 0.40625 0.765625 +vt 0.40625 0.8125 +vt 0.375 0.8125 +vn 1 0 0 +vn 1 0 0 +vn 0 2.220446049250313e-16 1 +vn 0 -2.220446049250313e-16 -1 +vn 0 2.220446049250313e-16 1 +vn 0 1 -2.220446049250313e-16 +vn 0 -2.220446049250313e-16 -1 +vn 0 1 -2.220446049250313e-16 +vn -1 0 0 +vn -1 0 0 +vn 0 -1 2.220446049250313e-16 +vn 0 -1 2.220446049250313e-16 +usemtl m_1a866da6-a095-0dfa-2917-4c8c09aff454 +f 5/1/1 6/2/1 2/3/1 1/4/1 +f 4/5/2 7/6/2 11/7/2 2/8/2 +f 2/9/3 8/10/3 9/11/3 1/12/3 +f 5/13/4 10/14/4 14/15/4 6/16/4 +f 2/17/5 11/18/5 12/19/5 8/20/5 +f 7/21/6 13/22/6 12/23/6 11/24/6 +f 4/25/7 15/26/7 13/27/7 7/28/7 +f 6/29/8 14/30/8 15/31/8 4/32/8 +f 10/33/9 9/34/9 8/35/9 14/36/9 +f 15/37/10 8/38/10 12/39/10 13/40/10 +f 9/41/11 10/42/11 5/43/11 3/44/11 +f 3/45/12 1/46/12 9/47/12 +o cube +v 0.046875 -0.3125 0.10937500000000006 +v 0.046875 2.7755575615628914e-17 0.10937499999999999 +v 0.046875 -0.3125 0.015625000000000056 +v 0.046875 0 0.015624999999999986 +v -0.046875 2.7755575615628914e-17 0.10937499999999999 +v -0.046875 -0.3125 0.10937500000000006 +v -0.046875 0 0.015624999999999986 +v -0.046875 -0.3125 0.015625000000000056 +vt 0.21875 0.65625 +vt 0.28125 0.65625 +vt 0.28125 0.59375 +vt 0.21875 0.59375 +vt 0 0.75 +vt 0.15625 0.75 +vt 0.15625 0.6875 +vt 0 0.6875 +vt 0.3125 0.65625 +vt 0.375 0.65625 +vt 0.375 0.59375 +vt 0.3125 0.59375 +vt 0.1875 0.75 +vt 0.34375 0.75 +vt 0.34375 0.6875 +vt 0.1875 0.6875 +vt 0.375 0.84375 +vt 0.3125 0.84375 +vt 0.3125 1 +vt 0.375 1 +vt 0.0625 0.65625 +vt 0 0.65625 +vt 0 0.5 +vt 0.0625 0.5 +vn 0 1 -2.220446049250313e-16 +vn 1 0 0 +vn 0 -1 2.220446049250313e-16 +vn -1 0 0 +vn 0 2.220446049250313e-16 1 +vn 0 -2.220446049250313e-16 -1 +usemtl m_1a866da6-a095-0dfa-2917-4c8c09aff454 +f 19/51/13 22/50/13 20/49/13 17/48/13 +f 18/55/14 19/54/14 17/53/14 16/52/14 +f 23/59/15 18/58/15 16/57/15 21/56/15 +f 22/63/16 23/62/16 21/61/16 20/60/16 +f 21/67/17 16/66/17 17/65/17 20/64/17 +f 22/71/18 19/70/18 18/69/18 23/68/18 \ No newline at end of file diff --git a/mods/firefly_weapons/firefly_saber/textures/firefly_saber.png b/mods/firefly_weapons/firefly_saber/textures/firefly_saber.png new file mode 100644 index 0000000..7e08ad9 Binary files /dev/null and b/mods/firefly_weapons/firefly_saber/textures/firefly_saber.png differ diff --git a/mods/firefly_weapons/firefly_streamer/init.lua b/mods/firefly_weapons/firefly_streamer/init.lua new file mode 100644 index 0000000..51133b6 --- /dev/null +++ b/mods/firefly_weapons/firefly_streamer/init.lua @@ -0,0 +1,9 @@ +local ns = firefly + +ns.register_weapon("streamer", { + inventory_image = "firefly_weapon_streamer.png", + fire_rate = 0.02, + fire = function(m, s) + ns.add_projectile(m.eye_pos +m.look_dir, {}, vector.new(0, 0, 1):rotate(vector.new(-m.pitch +(math.random() -0.5) /10, m.yaw +(math.random() -0.5) /10, 0)) *100, 0) + end +}) diff --git a/mods/firefly_weapons/firefly_streamer/mod.conf b/mods/firefly_weapons/firefly_streamer/mod.conf new file mode 100644 index 0000000..a596af0 --- /dev/null +++ b/mods/firefly_weapons/firefly_streamer/mod.conf @@ -0,0 +1,2 @@ +name = firefly_streamer +depends = firefly_weapons \ No newline at end of file diff --git a/mods/firefly_weapons/firefly_streamer/textures/firefly_weapon_streamer.png b/mods/firefly_weapons/firefly_streamer/textures/firefly_weapon_streamer.png new file mode 100644 index 0000000..18a2876 Binary files /dev/null and b/mods/firefly_weapons/firefly_streamer/textures/firefly_weapon_streamer.png differ diff --git a/mods/firefly_weapons/firefly_weapons/init.lua b/mods/firefly_weapons/firefly_weapons/init.lua new file mode 100644 index 0000000..97fa205 --- /dev/null +++ b/mods/firefly_weapons/firefly_weapons/init.lua @@ -0,0 +1,93 @@ +local ns = firefly + + +local function weapon_wielded(s, def, m) + local time = minetest.get_us_time() + if m.ctl.dig and time -m.last_fire_time >= def.fire_rate *1000000 then + def.fire(m, s) + m.last_fire_time = time + end +end + +local function weapon_wield(s, def, m) + m.last_fire_time = minetest.get_us_time() +end + +minetest.register_entity(":firefly:projectile", { + initial_properties = { + visual = "sprite", + textures = {"[fill:1x1:0,0:#333"}, + visual_size = vector.new(1,1,1) *0.1, + pointable = false, + physical = true, + collide_with_objects = false, + collisionbox = { + -0.1, -0.1, -0.1, + 0.1, 0.1, 0.1 + }, + static_save = false + }, + on_step = function(e, dtime, movement) + if movement and movement.collides then + minetest.add_particlespawner { + pos = { + min = e.object:get_pos():offset(-0.1,-0.1,-0.1), + max = e.object:get_pos():offset(0.1,0.1,0.1) + }, + acc = vector.new(0, -9.81, 0), + time = 0.1, + amount = 5, + exptime = 0.3, + animation = { + type = "vertical_frames", + aspect_w = 1, + aspect_h = 1, + length = -1 + }, + texture = ns.solid_color_frames {"#f7d19b", "#f0a951", "#e1820c", "#cd5819", "#5d342c", "#444"}, + attract = { + kind = "point", + origin = e.object:get_pos(), + strength = { + min = -40, + max = -10 + } + }, + size_tween = { + 1, + 0.02 + }, + collisiondetection = true, + die_on_collision = true, + } + e.object:remove() + end + end +}) + +-- Adds a basic gravity-velocity projectile. Custom behaviors can be created by creating a projectile entity and setting its callbacks to one's own implementations. +function ns.add_projectile(pos, props, vel, gravity) + local e = minetest.add_entity(pos, "firefly:projectile") + e:set_velocity(vel) + e:set_acceleration(vector.new(0, -gravity, 0)) + return e +end + +--[[ + { + fire_rate = 1, -- Numer of times to fire per second when Punch is held. If negative, such automatic refiring + -- is disabled and this value instead indicates the minimum delay required between clicks to fire successfully. + fire = function(m, s) end, -- Called when the weapon should fire. + } +--]] +function ns.register_weapon(name, def) + def.on_wield = weapon_wield + def.while_wielded = weapon_wielded + def.range = 0 + def.pointablilities = { + nodes = { + ["group:everything"] = false + } + } + firefly.register_item(name, def) +end diff --git a/mods/firefly_weapons/firefly_weapons/mod.conf b/mods/firefly_weapons/firefly_weapons/mod.conf new file mode 100644 index 0000000..213ae93 --- /dev/null +++ b/mods/firefly_weapons/firefly_weapons/mod.conf @@ -0,0 +1,2 @@ +name = firefly_weapons +depends = firefly_player \ No newline at end of file diff --git a/mods/firefly_weapons/modpack.conf b/mods/firefly_weapons/modpack.conf new file mode 100644 index 0000000..e69de29 diff --git a/mods/firefly_world/init.lua b/mods/firefly_world/init.lua new file mode 100644 index 0000000..2f812cb --- /dev/null +++ b/mods/firefly_world/init.lua @@ -0,0 +1,298 @@ +local ns = firefly + +function ns.register_node(name, def) + local needs_alias + if not name:find ":" then + def._name = name + name = "firefly:"..name + needs_alias = true + end + + if not def.groups then def.groups = {} end + def.groups.everything = 1 + def.groups.dig_immediate = 3 + + if def._variants then + for i, x in ipairs(def.tiles) do + if not x.name then + def.tiles[i] = {name = x, align_style = "world"} + end + end + for _, x in ipairs(def._variants) do + if x == "slab" then + ns.register_slab(def) + elseif x == "stair" then + ns.register_stair(def) + end + end + end + + minetest.register_node(":"..name, def) + + if needs_alias then + minetest.register_alias(def._name, name) + end +end + +function ns.register_slab(def) + def = table.copy(def) + def._variants = nil + + local paramtype2 = def.paramtype2 == "color" and "colorfacedir" or "facedir" + + ns.register_node(def._name.."_slab", extend(def, { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5} + }, + paramtype = "light", + paramtype2 = paramtype2 + })) +end + +function ns.register_stair(def) + def = table.copy(def) + def._variants = nil + + def.groups[def._name.."_stair"] = 1 + + local paramtype2 = def.paramtype2 == "color" and "colorfacedir" or "facedir" + + ns.register_node(def._name.."_stair", extend(table.copy(def), { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, -0.5, 0, 0.5, 0.5, 0.5}} + }, + paramtype = "light", + paramtype2 = paramtype2 + })) + + ns.register_node(def._name.."_stair_inner", extend(table.copy(def), { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, -0.5, 0, 0.5, 0.5, 0.5}, {-0.5, -0.5, -0.5, 0, 0.5, 0.5}} + }, + paramtype = "light", + paramtype2 = paramtype2 + })) + + ns.register_node(def._name.."_stair_outer", extend(def, { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = {{-0.5, -0.5, -0.5, 0.5, 0, 0.5}, {-0.5, 0, 0.5, 0, 0.5, 0}} + }, + paramtype = "light", + paramtype2 = paramtype2 + })) +end + + + +ns.register_node("dirt", { + tiles = {"firefly_dirt.png"} +}) + +ns.register_node("dirt_grass", { + tiles = {"firefly_grass_top.png", "firefly_dirt.png", "firefly_dirt.png^firefly_grass_side.png"} +}) + +ns.register_node("grass", { + drawtype = "plantlike", + tiles = {"firefly_grass.png"}, + paramtype = "light", + sunlight_propagates = true, + walkable = false, +}) + + +ns.register_node("snow", { + tiles = {"firefly_snow_top.png"} +}) + +ns.register_node("snow_leveled", { + drawtype = "nodebox", + tiles = {"firefly_snow_top.png"}, + node_box = { + type = "leveled", + fixed = { + -0.5, -0.5, -0.5, + 0.5, 6/16, 0.5 + } + }, + paramtype = "light", + paramtype2 = "leveled", + place_param2 = 8 +}) + +ns.register_node("snow_grass", { + drawtype = "plantlike", + tiles = {"firefly_snow_grass.png"}, + paramtype = "light", + sunlight_propagates = true, + walkable = false, +}) + + +ns.register_node("sand", { + tiles = {"firefly_sand.png"} +}) + +ns.register_node("sand_leveled", { + drawtype = "nodebox", + tiles = {"firefly_sand.png"}, + node_box = { + type = "leveled", + fixed = { + -0.5, -0.5, -0.5, + 0.5, 6/16, 0.5 + } + }, + paramtype = "light", + paramtype2 = "leveled", + place_param2 = 64 +}) + +ns.register_node("sandstone", { + tiles = {"firefly_sandstone.png"}, + _variants = {"slab", "stair"}, +}) + +ns.register_node("sandstone_sandy", { + tiles = {"firefly_sand.png", "firefly_sandstone.png", "firefly_sandstone.png^firefly_sand_side_2.png"} +}) + +ns.register_node("sandstone_bricks", { + tiles = {"firefly_sandstone_bricks.png"}, + _variants = {"slab", "stair"}, +}) + +ns.register_node("sandstone_bricks_sandy", { + tiles = {"firefly_sand.png", "firefly_sandstone_bricks.png", "firefly_sandstone_bricks.png^firefly_sand_side.png"} +}) + +ns.register_node("cactus", { + tiles = {"firefly_cactus_top.png", "firefly_cactus_top.png", "firefly_cactus_side.png"}, + paramtype2 = "facedir" +}) + + +ns.register_node("stone", { + tiles = {"firefly_stone.png"}, + _variants = {"slab", "stair"}, +}) + +ns.register_node("stone_bricks", { + tiles = {"firefly_stone_bricks.png"}, + _variants = {"slab", "stair"}, +}) + + +ns.register_node("metal_floor", { + tiles = {"firefly_metal_floor.png"}, + _variants = {"slab", "stair"}, +}) + +ns.register_node("orange_light", { + tiles = {"firefly_orange_light.png"}, + paramtype = "light", + light_source = 14 +}) + +ns.register_node("gray_pillar", { + tiles = {"firefly_gray_pillar_top.png", "firefly_gray_pillar_top.png", "firefly_gray_pillar_side.png"}, + paramtype2 = "facedir", + _variants = {"slab", "stair"}, +}) + +ns.register_node("gray_column", { + tiles = {{name = "firefly_gray_column.png"}}, + paramtype2 = "4dir", + _variants = {"slab", "stair"}, +}) + +ns.register_node("gray_wall", { + tiles = {"firefly_gray_wall.png"}, + _variants = {"slab", "stair"}, +}) + +ns.register_node("gray_tile", { + tiles = {"firefly_gray_tile.png"}, + _variants = {"slab", "stair"}, +}) + + +local brown = "#584e2d" +ns.register_node("brown_wall", { + tiles = {"firefly_wall.png"}, + _variants = {"slab", "stair"}, + color = brown, +}) + +ns.register_node("brown_column", { + tiles = {{name = "firefly_column.png"}}, + paramtype2 = "4dir", + _variants = {"slab", "stair"}, + color = brown, +}) + +ns.register_node("brown_pillar", { + tiles = {"firefly_pillar_top.png", "firefly_pillar_top.png", "firefly_pillar_side.png"}, + paramtype2 = "facedir", + _variants = {"slab", "stair"}, + color = brown, +}) + + +ns.register_node("glass", { + paramtype = "light", + sunlight_propagates = true, + tiles = {{name = "firefly_glass.png", align_style = "world"}}, + use_texture_alpha = "clip", + _variants = {"slab", "stair"}, +}) + +ns.register_node("colored_glass", { + drawtype = "glasslike", + paramtype = "light", + paramtype2 = "color", + palette = "firefly_glass_palette.png", + sunlight_propagates = true, + tiles = {{name = "firefly_colored_glass.png", align_style = "world"}}, + use_texture_alpha = "blend", + _variants = {"slab", "stair"}, +}) + +ns.register_node("steel_grate", { + drawtype = "mesh", + mesh = "firefly_steel_grate.obj", + tiles = {{name = "firefly_steel_grate.png", backface_culling = true}}, + use_texture_alpha = "clip", + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + collision_box = { + type = "fixed", + fixed = { + -0.5, 6/16, -0.5, + 0.5, 0.5, 0.5 + } + }, + selection_box = { + type = "fixed", + fixed = { + -0.5, 6/16, -0.5, + 0.5, 0.5, 0.5 + } + } +}) + +minetest.register_chatcommand("chest", { + privs = {give = true}, + func = function(name) + minetest.registered_chatcommands.giveme.func(name, "chest_with_everything:chest") + end +}) diff --git a/mods/firefly_world/mod.conf b/mods/firefly_world/mod.conf new file mode 100644 index 0000000..bc4bd6e --- /dev/null +++ b/mods/firefly_world/mod.conf @@ -0,0 +1,2 @@ +name = firefly_world +depends = firefly_base \ No newline at end of file diff --git a/mods/firefly_world/models/firefly_steel_grate.obj b/mods/firefly_world/models/firefly_steel_grate.obj new file mode 100644 index 0000000..25fb9b3 --- /dev/null +++ b/mods/firefly_world/models/firefly_steel_grate.obj @@ -0,0 +1,49 @@ +# Made in Blockbench 4.12.5 +mtllib firefly_steel_grate.mtl + +o cube +v 0.5 0.5 0.5 +v 0.5 0.5 -0.5 +v 0.5 0.375 0.5 +v 0.5 0.375 -0.5 +v -0.5 0.5 -0.5 +v -0.5 0.5 0.5 +v -0.5 0.375 -0.5 +v -0.5 0.375 0.5 +vt 0.5 1 +vt 1 1 +vt 1 0.9375 +vt 0.5 0.9375 +vt 0.5 0.9375 +vt 1 0.9375 +vt 1 0.875 +vt 0.5 0.875 +vt 0.5 0.875 +vt 1 0.875 +vt 1 0.8125 +vt 0.5 0.8125 +vt 0.5 0.8125 +vt 1 0.8125 +vt 1 0.75 +vt 0.5 0.75 +vt 0.5 0.5 +vt 0 0.5 +vt 0 1 +vt 0.5 1 +vt 0.5 0.5 +vt 0 0.5 +vt 0 0 +vt 0.5 0 +vn 0 0 -1 +vn 1 0 0 +vn 0 0 1 +vn -1 0 0 +vn 0 1 0 +vn 0 -1 0 +usemtl m_18cc3c99-d6ee-54f4-84b1-03e8119d680b +f 4/4/1 7/3/1 5/2/1 2/1/1 +f 3/8/2 4/7/2 2/6/2 1/5/2 +f 8/12/3 3/11/3 1/10/3 6/9/3 +f 7/16/4 8/15/4 6/14/4 5/13/4 +f 6/20/5 1/19/5 2/18/5 5/17/5 +f 7/24/6 4/23/6 3/22/6 8/21/6 \ No newline at end of file diff --git a/mods/firefly_world/textures/firefly_cactus_side.png b/mods/firefly_world/textures/firefly_cactus_side.png new file mode 100644 index 0000000..eba4fe8 Binary files /dev/null and b/mods/firefly_world/textures/firefly_cactus_side.png differ diff --git a/mods/firefly_world/textures/firefly_cactus_top.png b/mods/firefly_world/textures/firefly_cactus_top.png new file mode 100644 index 0000000..02cd974 Binary files /dev/null and b/mods/firefly_world/textures/firefly_cactus_top.png differ diff --git a/mods/firefly_world/textures/firefly_colored_glass.png b/mods/firefly_world/textures/firefly_colored_glass.png new file mode 100644 index 0000000..c939efc Binary files /dev/null and b/mods/firefly_world/textures/firefly_colored_glass.png differ diff --git a/mods/firefly_world/textures/firefly_column.png b/mods/firefly_world/textures/firefly_column.png new file mode 100644 index 0000000..8e8cc3d Binary files /dev/null and b/mods/firefly_world/textures/firefly_column.png differ diff --git a/mods/firefly_world/textures/firefly_concrete.png b/mods/firefly_world/textures/firefly_concrete.png new file mode 100644 index 0000000..024d154 Binary files /dev/null and b/mods/firefly_world/textures/firefly_concrete.png differ diff --git a/mods/firefly_world/textures/firefly_dirt.png b/mods/firefly_world/textures/firefly_dirt.png new file mode 100644 index 0000000..fe26689 Binary files /dev/null and b/mods/firefly_world/textures/firefly_dirt.png differ diff --git a/mods/firefly_world/textures/firefly_glass.png b/mods/firefly_world/textures/firefly_glass.png new file mode 100644 index 0000000..9c97509 Binary files /dev/null and b/mods/firefly_world/textures/firefly_glass.png differ diff --git a/mods/firefly_world/textures/firefly_glass_palette.png b/mods/firefly_world/textures/firefly_glass_palette.png new file mode 100644 index 0000000..cd7b373 Binary files /dev/null and b/mods/firefly_world/textures/firefly_glass_palette.png differ diff --git a/mods/firefly_world/textures/firefly_grass.png b/mods/firefly_world/textures/firefly_grass.png new file mode 100644 index 0000000..67be125 Binary files /dev/null and b/mods/firefly_world/textures/firefly_grass.png differ diff --git a/mods/firefly_world/textures/firefly_grass_side.png b/mods/firefly_world/textures/firefly_grass_side.png new file mode 100644 index 0000000..048b99d Binary files /dev/null and b/mods/firefly_world/textures/firefly_grass_side.png differ diff --git a/mods/firefly_world/textures/firefly_grass_top.png b/mods/firefly_world/textures/firefly_grass_top.png new file mode 100644 index 0000000..5edb726 Binary files /dev/null and b/mods/firefly_world/textures/firefly_grass_top.png differ diff --git a/mods/firefly_world/textures/firefly_gray_column.png b/mods/firefly_world/textures/firefly_gray_column.png new file mode 100644 index 0000000..6a93f0d Binary files /dev/null and b/mods/firefly_world/textures/firefly_gray_column.png differ diff --git a/mods/firefly_world/textures/firefly_gray_pillar_side.png b/mods/firefly_world/textures/firefly_gray_pillar_side.png new file mode 100644 index 0000000..9785893 Binary files /dev/null and b/mods/firefly_world/textures/firefly_gray_pillar_side.png differ diff --git a/mods/firefly_world/textures/firefly_gray_pillar_top.png b/mods/firefly_world/textures/firefly_gray_pillar_top.png new file mode 100644 index 0000000..39d6480 Binary files /dev/null and b/mods/firefly_world/textures/firefly_gray_pillar_top.png differ diff --git a/mods/firefly_world/textures/firefly_gray_tile.png b/mods/firefly_world/textures/firefly_gray_tile.png new file mode 100644 index 0000000..49d5a56 Binary files /dev/null and b/mods/firefly_world/textures/firefly_gray_tile.png differ diff --git a/mods/firefly_world/textures/firefly_gray_wall.png b/mods/firefly_world/textures/firefly_gray_wall.png new file mode 100644 index 0000000..bb73521 Binary files /dev/null and b/mods/firefly_world/textures/firefly_gray_wall.png differ diff --git a/mods/firefly_world/textures/firefly_metal_floor.png b/mods/firefly_world/textures/firefly_metal_floor.png new file mode 100644 index 0000000..9274b22 Binary files /dev/null and b/mods/firefly_world/textures/firefly_metal_floor.png differ diff --git a/mods/firefly_world/textures/firefly_orange_light.png b/mods/firefly_world/textures/firefly_orange_light.png new file mode 100644 index 0000000..3dd0a93 Binary files /dev/null and b/mods/firefly_world/textures/firefly_orange_light.png differ diff --git a/mods/firefly_world/textures/firefly_pillar_side.png b/mods/firefly_world/textures/firefly_pillar_side.png new file mode 100644 index 0000000..d16e56b Binary files /dev/null and b/mods/firefly_world/textures/firefly_pillar_side.png differ diff --git a/mods/firefly_world/textures/firefly_pillar_top.png b/mods/firefly_world/textures/firefly_pillar_top.png new file mode 100644 index 0000000..f18a805 Binary files /dev/null and b/mods/firefly_world/textures/firefly_pillar_top.png differ diff --git a/mods/firefly_world/textures/firefly_sand.png b/mods/firefly_world/textures/firefly_sand.png new file mode 100644 index 0000000..49f8869 Binary files /dev/null and b/mods/firefly_world/textures/firefly_sand.png differ diff --git a/mods/firefly_world/textures/firefly_sand_side.png b/mods/firefly_world/textures/firefly_sand_side.png new file mode 100644 index 0000000..6721de0 Binary files /dev/null and b/mods/firefly_world/textures/firefly_sand_side.png differ diff --git a/mods/firefly_world/textures/firefly_sand_side_2.png b/mods/firefly_world/textures/firefly_sand_side_2.png new file mode 100644 index 0000000..3f2b783 Binary files /dev/null and b/mods/firefly_world/textures/firefly_sand_side_2.png differ diff --git a/mods/firefly_world/textures/firefly_sandstone.png b/mods/firefly_world/textures/firefly_sandstone.png new file mode 100644 index 0000000..ec75383 Binary files /dev/null and b/mods/firefly_world/textures/firefly_sandstone.png differ diff --git a/mods/firefly_world/textures/firefly_sandstone_bricks.png b/mods/firefly_world/textures/firefly_sandstone_bricks.png new file mode 100644 index 0000000..88ea7ac Binary files /dev/null and b/mods/firefly_world/textures/firefly_sandstone_bricks.png differ diff --git a/mods/firefly_world/textures/firefly_snow_grass.png b/mods/firefly_world/textures/firefly_snow_grass.png new file mode 100644 index 0000000..c162c52 Binary files /dev/null and b/mods/firefly_world/textures/firefly_snow_grass.png differ diff --git a/mods/firefly_world/textures/firefly_snow_top.png b/mods/firefly_world/textures/firefly_snow_top.png new file mode 100644 index 0000000..2d7c923 Binary files /dev/null and b/mods/firefly_world/textures/firefly_snow_top.png differ diff --git a/mods/firefly_world/textures/firefly_steel_grate.png b/mods/firefly_world/textures/firefly_steel_grate.png new file mode 100644 index 0000000..8969854 Binary files /dev/null and b/mods/firefly_world/textures/firefly_steel_grate.png differ diff --git a/mods/firefly_world/textures/firefly_stone.png b/mods/firefly_world/textures/firefly_stone.png new file mode 100644 index 0000000..9f91b93 Binary files /dev/null and b/mods/firefly_world/textures/firefly_stone.png differ diff --git a/mods/firefly_world/textures/firefly_stone_bricks.png b/mods/firefly_world/textures/firefly_stone_bricks.png new file mode 100644 index 0000000..30a4421 Binary files /dev/null and b/mods/firefly_world/textures/firefly_stone_bricks.png differ diff --git a/mods/firefly_world/textures/firefly_wall.png b/mods/firefly_world/textures/firefly_wall.png new file mode 100644 index 0000000..a241db4 Binary files /dev/null and b/mods/firefly_world/textures/firefly_wall.png differ diff --git a/mods/imfs/init.lua b/mods/imfs/init.lua new file mode 100644 index 0000000..b522380 --- /dev/null +++ b/mods/imfs/init.lua @@ -0,0 +1,1501 @@ + +local fe = minetest.formspec_escape +local hte = minetest.hypertext_escape + +local ctx + +local player_contexts = {} +local contexts = {} +local inventories = {} + +local theme = {} + +-- MARK: Helpers + +-- Detects percentage units andconverts them into an appropriate number (based on `ref`). +local function resolve_layout_units(value, ref) + if type(value) == "string" then + local num = tonumber(value) + if num then return num end + + local percent = value:match "(%d*.?%d+)%%" + if ref and percent then + local sign, offset = value:match "%%%s*([+-])%s*(%d*.?%d+)" + return tonumber(percent) /100 *ref +(tonumber(offset or 0) *(sign == "-" and -1 or 1)) + end + + print(string.match("100%", "(%d*.?%d+)%%"), percent, ref) + + error("Malformed layout units: "..value) + else + return value + end +end + +-- The same as resolve_layout_units, but allows (and notifies of) flex units. +local function resolve_flex_layout_units(value, ref) + if type(value) == "string" then + local num = tonumber(value) + if num then return num end + + local percent = value:match "(%d*.?%d+)%%" + if ref and percent then + local sign, offset = value:match "%%%s*([+-])%s*(%d*.?%d+)" + return tonumber(percent) /100 *ref +(tonumber(offset or 0) *(sign == "-" and -1 or 1)) + end + + local flex = value:match "(%d*.?%d+)x" + if flex then + return tonumber(flex), "flex" + end + + error("Malformed layout units: "..value) + else + return value + end +end + +-- This should be a method on any element classes that support inline styling. +local function add_elem_style(e, state, props) + -- Allow omitting the state while keeping the properties the last argument. + if not props then + props = state + state = "default" + end + e._styles[state] = imfs.style(e.__id..":"..state, props, true) + return e +end + +-- This should be a method on any element classes that support tooltips. +local function add_elem_tooltip(e, text, enabled, bgcolor, txtcolor) + -- Permit the user to pass a boolean to disable the tooltip (useful for modal dialogs that should occlude tooltips without more bothersome composition boilerplate than necessary). + if enabled == false then + return + elseif enabled ~= true then + txtcolor = bgcolor + bgcolor = enabled + end + -- For named elements, prefer name-associated tooltips. + if e.__id then + imfs._named_tooltip(e.__id, text, bgcolor, txtcolor) + -- For other elements, if they have a size, use an area tooltip instead. + elseif e.w then + -- We must overload `render()` in order to ensure the tooltip's position matches the parent's exactly in all cases. + local render = e.render + e.render = function(e, x, y, w, h) + local out = render(e, x, y, w, h) + return table.concat{out, imfs.tooltip.init(x or e.x, y or e.y, w or e.w, h or e.h, text, bgcolor, txtcolor, true):render()} + end + end + return e +end + +local name_scope = {"_"} +local nth_id = {0} + +local function scope(name) + table.insert(name_scope, name) + table.insert(nth_id, 0) +end + +local function scope_end() + table.remove(name_scope) + table.remove(nth_id) +end + +local function new_id() + local depth = #nth_id + nth_id[depth] = nth_id[depth] +1 + return table.concat(name_scope, ".").."_"..nth_id[depth] +end + +local function unique_id() + return "_"..minetest.get_us_time().."_"..math.random(1, 100000) +end + +local function string_or(a, b) + return (a ~= "" and a) or b +end + +-- MARK: Data structures + +local observers = {} +local state = { + observers = observers, + get = function(e) + local observer = observers[#observers] + if observer then + observer[e] = true + end + return e._val + end, + set = function(e, val) + if val == nil then error "!" end + e._old_val = e._val + e._val = val + for _, x in pairs(e._getters) do + x:update() + end + end, + __call = function(e, val) + if val == nil then -- Getter + return e:get() + else -- Setter + return e:set(val) + end + end +} +state.__index = state +setmetatable(state, { + __call = function(_, val) + local e = {_getters = {}, _val = val} + setmetatable(e, state) + + return e + end +}) + +local DerivedState = { + get = function(e) + if e._stale then + e:update() + end + return e._val + end, + update = function(e) + for dep in pairs(e._deps) do + dep._getters[e.__id] = nil + e._deps[dep] = nil + end + + local tracker = e._deps + table.insert(observers, tracker) + + local val = e._fn() + + table.remove(observers) + + e._val = val + + for dep in pairs(e._deps) do + dep._getters[e.__id] = e + end + + -- Don't propagate updates on initial computation to avoid stack overflow. + if not e._stale then + for _, x in pairs(e._getters) do + x:update() + end + end + e._stale = false + end, + __call = function(e) + return e:get() + end +} +DerivedState.__index = DerivedState + +function derive(fn) + return setmetatable({_fn = fn, _stale = true, _getters = {}, _deps = {}, __id = minetest.get_us_time()}, DerivedState) +end + +local function get(e, x) + local item = e[x] + local mt = getmetatable(item) + if mt == state or mt == DerivedState then + return fe(tostring(item() or "")) + end + return fe(tostring(item or "")) +end + +local function get_raw(e, x) + local item = e[x] + local mt = getmetatable(item) + if mt == state or mt == DerivedState then + return item() + end + return item +end + +-- MARK: Elements + +local fs_style = { + render = function(e) + local props = {} + for k, v in pairs(e.props) do + props[#props +1] = ";" + props[#props +1] = k + props[#props +1] = "=" + props[#props +1] = tostring(v) + end + return string.format("style[%s%s]", e.name, table.concat(props)) + end +} +fs_style.__index = fs_style +setmetatable(fs_style, { + __call = function(_, name, props, internal) + local e = {name = name, props = props} + setmetatable(e, fs_style) + if not internal then table.insert(ctx, e) end + return e + end +}) + +local fs_tooltip = { + _no_layout = true, + init = function(x, y, w, h, text, bgcolor, txtcolor) + local e = {x = x, y = y, w = w, h = h, text = text, bgcolor = bgcolor or imfs.theme.tooltip_bg or "#444", txtcolor = txtcolor or imfs.theme.tooltip_text or "#aaa"} + setmetatable(e, imfs.tooltip) + return e + end, + render = function(e, x, y, w, h) + if e._name then + return string.format("tooltip[%s;%s;%s;%s]", e._name, get(e, "text"), get(e, "bgcolor"), get(e, "txtcolor")) + else + return string.format( + "tooltip[%f,%f;%f,%f;%s;%s;%s]", + x or get(e, "x"), y or get(e, "y"), + w or get(e, "w"), h or get(e, "h"), + get(e, "text"), + get(e, "bgcolor"), + get(e, "txtcolor") + ) + end + end +} +fs_tooltip.__index = fs_tooltip +setmetatable(fs_tooltip, { + __call = function(_, x, y, w, h, text, bgcolor, txtcolor) + local e = {x = x, y = y, w = w, h = h, text = text, bgcolor = bgcolor or imfs.theme.tooltip_bg or "#444", txtcolor = txtcolor or imfs.theme.tooltip_text or "#aaa"} + setmetatable(e, fs_tooltip) + table.insert(ctx, e) + return e + end +}) + +local function fs_named_tooltip(name, text, bgcolor, txtcolor) + local e = {_name = name, text = text, bgcolor = bgcolor or imfs.theme.tooltip_bg or "#444", txtcolor = txtcolor or imfs.theme.tooltip_text or "#aaa"} + setmetatable(e, fs_tooltip) + table.insert(ctx, e) + return e +end + +local fs_label = { + render = function(e, x, y) + return string.format("label[%f,%f;%s]", x or get(e, "x"), y or get(e, "y"), get(e, "txt")) + end +} +fs_label.__index = fs_label +setmetatable(fs_label, { + __call = function(_, x, y, txt) + local e = {x = x, y = y, txt = txt} + setmetatable(e, fs_label) + table.insert(ctx, e) + return e + end +}) + +local fs_arealabel = { + render = function(e, x, y, w, h) + x = x or get(e, "x") + y = y or get(e, "y") + w = w or get(e, "w") + h = h or get(e, "h") + + if e._scrollable then + return string.format("textarea[%f,%f;%f,%f;;;%s]", x, y, w, h, get(e, "txt")) + else + return string.format("label[%f,%f;%f,%f;%s]", x, y, w, h, get(e, "txt")) + end + end, + scrollable = function(e) + e._scrollable = true + return e + end, + tooltip = add_elem_tooltip, +} +fs_arealabel.__index = fs_arealabel +setmetatable(fs_arealabel, { + __call = function(_, x, y, w, h, txt) + local e = {x = x, y = y, w = w, h = h, txt = txt} + setmetatable(e, fs_arealabel) + table.insert(ctx, e) + return e + end +}) + +local fs_hypertext = { + render = function(e, x, y, w, h) + return string.format("hypertext[%f,%f;%f,%f;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), e.__id, get(e, "txt")) + end, + onaction = function(e, fn) + ctx._events.on_click[e.__id] = fn + return e + end, + tooltip = add_elem_tooltip, +} +fs_hypertext.__index = fs_hypertext +setmetatable(fs_hypertext, { + __call = function(_, x, y, w, h, txt) + local e = {x = x, y = y, w = w, h = h, txt = txt} + e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000) + setmetatable(e, fs_hypertext) + table.insert(ctx, e) + return e + end +}) + +local fs_box = { + render = function(e, x, y, w, h) + return string.format("box[%f,%f;%f,%f;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "bg")) + end, + tooltip = add_elem_tooltip, +} +fs_box.__index = fs_box +setmetatable(fs_box, { + __call = function(_, x, y, w, h, bg) + local e = {x = x, y = y, w = w, h = h, bg = bg} + setmetatable(e, fs_box) + table.insert(ctx, e) + return e + end +}) + +local fs_image = { + render = function(e, x, y, w, h) + if e._anim_frames then + return string.format( + "animated_image[%f,%f;%f,%f;%s;%s;%f;%f;%f;%s]", + x or get(e, "x"), y or get(e, "y"), + w or get(e, "w"), h or get(e, "h"), + e.__id, + get(e, "texture"), + get(e, "_anim_frames"), + get(e, "_anim_duration"), + get(e, "_anim_start"), + get(e, "middle") + ) + else + return string.format("image[%f,%f;%f,%f;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "texture"), get(e, "middle")) + end + end, + animated = function(e, frames, duration, start) + -- We only need an ID if the image is animated and must persist its state. + e.__id = e.__id or new_id() + e._anim_frames = frames + e._anim_duration = duration or 50 + e._anim_start = start or 1 + return e + end, + tooltip = add_elem_tooltip, +} +fs_image.__index = fs_image +setmetatable(fs_image, { + __call = function(_, x, y, w, h, texture, middle) + local e = {x = x, y = y, w = w, h = h, texture = texture, middle = middle or ""} + setmetatable(e, fs_image) + table.insert(ctx, e) + return e + end +}) + +local fs_item_image = { + render = function(e, x, y, w, h) + return string.format("item_image[%f,%f;%f,%f;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "item")) + end, + tooltip = add_elem_tooltip, +} +fs_item_image.__index = fs_item_image +setmetatable(fs_item_image, { + __call = function(_, x, y, w, h, item) + local e = {x = x, y = y, w = w, h = h, item = item} + setmetatable(e, fs_item_image) + table.insert(ctx, e) + return e + end +}) + +local fs_model = { + render = function(e, x, y, w, h) + local out = {} + for _, x in pairs(e._styles) do + out[#out +1] = x:render() + end + + local textures = get_raw(e, "textures") + if type(textures) ~= "string" then + textures = table.concat(textures, ",") + end + + local rx = get_raw(e, "_rotation_x") + local ry = get_raw(e, "_rotation_y") + local rotation = "" + if rx and ry then + rotation = string.format("%s,%s", fe(rx), fe(ry)) + end + + local as = get_raw(e, "_animation_start") + local ae = get_raw(e, "_animation_end") + local animation = "" + if as and ae then + animation = string.format("%s,%s", fe(as), fe(ae)) + end + + out[#out +1] = string.format( + "model[%f,%f;%f,%f;%s;%s;%s;%s;%s;%s;%s;%s]", + x or get(e, "x"), y or get(e, "y"), + w or get(e, "w"), h or get(e, "h"), + e.__id, + get(e, "mesh"), textures, + rotation, get(e, "_continuous"), + get(e, "_mouse_control"), + animation, get(e, "_animation_speed") + ) + return table.concat(out) + end, + rotation = function(e, x, y, continuous) + e._rotation_x = x + e._rotation_y = y or 0 + e._continuous = continuous + return e + end, + mouse_control = function(e, mouse_control) + e._mouse_control = mouse_control ~= false and true or false + return e + end, + animated = function(e, start, end_, speed) + e._animation_start = start + e._animation_end = end_ + e._animation_speed = speed or 1 + return e + end, + style = add_elem_style, + tooltip = add_elem_tooltip, +} +fs_model.__index = fs_model +setmetatable(fs_model, { + __call = function(_, x, y, w, h, mesh, textures) + local e = {x = x, y = y, w = w, h = h, mesh = mesh, textures = textures, _mouse_control = true, _styles = {}} + e.__id = new_id() + setmetatable(e, fs_model) + table.insert(ctx, e) + return e + end +}) + +local fs_button = { + render = function(e, x, y, w, h) + local out = {} + for _, x in pairs(e._styles) do + out[#out +1] = x:render() + end + if e._item then + out[#out +1] = string.format("item_image_button[%f,%f;%f,%f;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "_item"), e.__id, get(e, "label")) + elseif e._image then + if e._image_pressed then + -- We never specify noclip or border here. That's a job for styles. + out[#out +1] = string.format("image_button[%f,%f;%f,%f;%s;%s;%s;;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "_image"), e.__id, get(e, "label"), get(e, "_image_pressed")) + else + out[#out +1] = string.format("image_button%s[%f,%f;%f,%f;%s;%s;%s]", e._exit and "_exit" or "", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "_image"), e.__id, get(e, "label")) + end + else + out[#out +1] = string.format("button%s[%f,%f;%f,%f;%s;%s]", e._exit and "_exit" or "", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), e.__id, get(e, "label")) + end + return table.concat(out) + end, + image = function(e, img, pressed_img) + e._image = img + e._image_pressed = pressed_img + return e + end, + item_image = function(e, item) + e._item = item + return e + end, + exit = function(e) + e._exit = true + return e + end, + onclick = function(e, fn) + ctx._events.on_click[e.__id] = fn + return e + end, + style = add_elem_style, + tooltip = add_elem_tooltip, +} +fs_button.__index = fs_button +setmetatable(fs_button, { + __call = function(_, x, y, w, h, label) + local e = {x = x, y = y, w = w, h = h, label = label, _styles = {}} + e.__id = new_id() + setmetatable(e, fs_button) + table.insert(ctx, e) + return e + end +}) + +local fs_checkbox = { + render = function(e, x, y) + return string.format("checkbox[%f,%f;%s;%s;%s]", x or get(e, "x"), y or get(e, "y"), e.__id, get(e, "label"), get(e, "checked") and "true" or "false") + end, + onchange = function(fn) + ctx._events.on_change[e.__id] = fn + return e + end, + tooltip = add_elem_tooltip, +} +fs_checkbox.__index = fs_checkbox +setmetatable(fs_checkbox, { + __call = function(_, x, y, label, checked) + local e = {x = x, y = y, label = label or "", checked = checked or label or false} + e.__id = new_id() + setmetatable(e, fs_checkbox) + table.insert(ctx, e) + return e + end +}) + +local fs_list = { + render = function(e, x, y, w, h) + w = w or get(e, "w") + h = h or get(e, "h") + + return string.format("list[%s;%s;%f,%f;%d,%d;%s]", get(e, "location"), get(e, "list"), x or get(e, "x"), y or get(e, "y"), w, h, get(e, "start")) + end +} +fs_list.__index = fs_list +setmetatable(fs_list, { + __call = function(_, x, y, w, h, location, list, start) + local e = {x = x, y = y, w = type(w == "string") and w or w +((w -1) /4), h = type(h) == "string" and h or h +((h -1) /4), location = location or "current_player", list = list or "main", start = start or ""} + e.__id = new_id() + setmetatable(e, fs_list) + table.insert(ctx, e) + return e + end +}) + +local fs_inventory = fs_list + +local fs_listring = { + render = function(e) + if e.location then + return string.format("listring[%s;%s]", e.location, e.list) + else + return "listring[]" + end + end, +} +fs_listring.__index = fs_listring +setmetatable(fs_listring, { + __call = function(_, location, list) + local e = location and list and {location = location, list = list} or {} + setmetatable(e, fs_listring) + table.insert(ctx, e) + return e + end +}) + +local fs_field = { + render = function(e, x, y, w, h) + local out = {} + for _, x in pairs(e._styles) do + out[#out +1] = x:render() + end + + x = x or get(e, "x") + y = y or get(e, "y") + w = w or get(e, "w") + h = h or get(e, "h") + + if e._password then + out[#out +1] = string.format("pwdfield[%f,%f;%f,%f;%s;%s]", x, y, w, h, e.__id, get(e, "label")) + + if not e._close_on_enter then + out[#out +1] = string.format("field_close_on_enter[%s;false]", e.__id) + end + else + out[#out +1] = string.format("%s[%f,%f;%f,%f;%s;%s;%s]", e._textarea and "textarea" or "field", x, y, w, h, e.__id, get(e, "label"), get(e, "value")) + + if not e._textarea and not e._close_on_enter then + out[#out +1] = string.format("field_close_on_enter[%s;false]", e.__id) + end + end + + return table.concat(out) + end, + onenter = function(e, fn) + ctx._events.on_enter[e.__id] = fn + return e + end, + onchange = function(e, fn) + ctx._events.on_change[e.__id] = fn + return e + end, + close_on_enter = function(e) + e._close_on_enter = true + return e + end, + multiline = function(e) + e._textarea = true + return e + end, + password = function(e) + e._password = true + return e + end, + style = add_elem_style, + tooltip = add_elem_tooltip, +} +fs_field.__index = fs_field +setmetatable(fs_field, { + __call = function(_, x, y, w, h, label, value) + local e = {x = x, y = y, w = w, h = h, label = value and label or "", value = value or label or "", _styles = {}} + e.__id = new_id() + setmetatable(e, fs_field) + table.insert(ctx, e) + return e + end +}) + +local function fs_textarea(...) + return fs_field(...) + :multiline() +end + +local fs_scrollbar = { + render = function(e, x, y, w, h) + local out = {} + if e._options then + out[#out +1] = "scrollbaroptions[" + local first = true + for k, v in pairs(e._options) do + if not first then + out[#out +1] = ";" + end + out[#out +1] = k + out[#out +1] = "=" + out[#out +1] = v + first = nil + end + out[#out +1] = "]" + end + + out[#out +1] = string.format("scrollbar[%f,%f;%f,%f;%s;%s;%s]", x or get(e, "x"), y or get(e, "y"), w or get(e, "w"), h or get(e, "h"), get(e, "orientation"), e.__id, get(e, "value")) + + return table.concat(out) + end, + options = function(e, props) + e._options = props + return e + end, + onchange = function(e, fn) + ctx._events.on_scrollbar_event[e.__id] = fn + return e + end, + style = add_elem_style, + tooltip = add_elem_tooltip, +} +fs_scrollbar.__index = fs_scrollbar +setmetatable(fs_scrollbar, { + __call = function(_, x, y, w, h, orientation, value) + local e = {x = x, y = y, w = w, h = h, orientation = orientation or "vertical", value = value or "", _styles = {}} + e.__id = "_"..minetest.get_us_time().."_"..math.random(1, 100000) + setmetatable(e, fs_scrollbar) + ctx[#ctx +1] = e + return e + end +}) + +local fs_scroll_container = { + render = function(e, x, y, w, h, orient, fac, pad) + x = x or get(e, "x") + y = y or get(e, "y") + w = w or get(e, "w") + h = h or get(e, "h") + + local out = { + string.format("scroll_container[%f,%f;%f,%f;%s;%s;%s;%s]", + x, y, + w, h, + e.__id, + orient or get(e, "orientation"), + fac or get(e, "factor"), + pad or get(e, "padding") + ) + } + for i = 1, #e do + local c = e[i] + local cx, cy + if c.x then + cx = resolve_layout_units(get_raw(c, "x"), w) + cy = resolve_layout_units(get_raw(c, "y"), h) + end + + local cw, ch + if c.w then + cw = resolve_layout_units(get(c, "w"), w) + ch = resolve_layout_units(get(c, "h"), h) + end + + out[#out +1] = c:render(cx, cy, cw, ch) + end + out[#out +1] = "scroll_container_end[]" + + if e._scrollbar then + out[#out +1] = e._scrollbar:render() + else + local v = e.__fs._ctx.fields[e.__id] + out[#out +1] = string.format("scrollbar[-800,-800;0,0;%s;%s;%s]", get(e, "orientation"), e.__id, v and v:sub(5) or "") + end + + return table.concat(out) + end, + scrollbar = function(e, fn, ...) + -- Make a fake ctx to put the scrollbar in. + ctx = setmetatable({ + __parent = ctx, + _events = setmetatable({}, { + __index = ctx._events, + __newindex = ctx._events + }), + }, { + -- Assignment trap to ensure that the scrollbar gets our ID immediately on construction. + __newindex = function(tbl, key, value) + value.__id = e.__id + return rawset(tbl, key, value) + end + }) + if type(fn) == "function" then + fn() + else + fs_scrollbar(fn, ...) + end + e._scrollbar = ctx[1] + ctx = ctx.__parent + e._scrollbar.__id = e.__id + return e + end, + onscroll = function(e, fn) + if e._scrollbar then + e._scrollbar:onchange(fn) + return e + end + e._scrollbar = fs_scrollbar(-800, -800, 0, 0, e._orientation, e.__id):onchange(fn) + return e + end, + -- A non-transient name is required in order to preserve scroll position upon a rebuild. + named = function(e, name) + e.__id = name + return e + end +} +fs_scroll_container.__index = fs_scroll_container +setmetatable(fs_scroll_container, { + __call = function(_, x, y, w, h, orientation, factor, padding) + local e = { + x = x, + y = y, + w = w, + h = h, + orientation = orientation or "vertical", + factor = factor or "", + padding = padding or "0", + _styles = {}, + __parent = ctx, + _events = setmetatable({}, { + __index = ctx._events, + __newindex = ctx._events + }), + __fs = ctx.__parent or ctx + } + e.__id = new_id() + setmetatable(e, fs_scroll_container) + table.insert(ctx, e) + ctx = e + return e + end +}) + +local function fs_scroll_container_end() + if getmetatable(ctx) ~= fs_scroll_container then + minetest.log("warn", "`scroll_container_end` has no scroll container to end; it will be ignored.") + return + end + ctx = ctx.__parent +end + + +-- MARK: Builtin layouting helpers + +local fs_group = { + render = function(e, x, y, w, h) + x = x or get(e, "x") + y = y or get(e, "y") + w = w or get(e, "w") + h = h or get(e, "h") + + local out = {} + for i = 1, #e do + local c = e[i] + local cx, cy + if c.x then + cx = resolve_layout_units(get_raw(c, "x"), w) +x + cy = resolve_layout_units(get_raw(c, "y"), h) +y + end + + local cw, ch + if c.w then + cw = resolve_layout_units(get(c, "w"), w) + ch = resolve_layout_units(get(c, "h"), h) + end + + out[#out +1] = c:render(cx, cy, cw, ch) + end + + return table.concat(out) + end, +} +fs_group.__index = fs_group +setmetatable(fs_group, { + __call = function(_, x, y, w, h) + local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx} + setmetatable(e, fs_group) + table.insert(ctx, e) + ctx = e + return e + end +}) + +local function fs_group_end() + if getmetatable(ctx) ~= fs_group then + minetest.log("warn", "`group_end` has no group to end; it will be ignored.") + return + end + ctx = ctx.__parent +end + + +local fs_row = { + render = function(e, x, y, w, h) + x = x or get(e, "x") + y = y or get(e, "y") + w = w or get(e, "w") + h = h or get(e, "h") + + local axis = e._direction == "column" and y or x + local axis_size = e._direction == "column" and h or w + + local out = {} + + local total_grow = 0 + local used_space = 0 + local flex_found + + -- Pass 1: Collect total sizing information to allow layout computation. + for i = 1, #e do + local c = e[i] + if not c._no_layout then + local ca, ca_flex = resolve_flex_layout_units(get(c, e._direction == "column" and "h" or "w"), axis_size) + if c.w then + if ca_flex then + flex_found = true + c.__flex = ca + total_grow = total_grow +ca + else + used_space = used_space +(ca or 0) + end + end + end + end + + used_space = used_space +(e._gap *math.max(0, #e -1)) + local grow_basis = total_grow > 0 and (math.max(0, axis_size -used_space) /total_grow) or 0 + + -- If any flex element exists, it will take up all unused space, so the total width is guaranteed to be 100%. + -- Otherwise, we can simply use the size we already calculated for fixed elements. + local total_width = flex_found and axis_size or used_space + + -- Pass 2: Assign element positions based on flex ratios. + local current = 0 + for i = 1, #e do + local c = e[i] + if not c._no_layout then + c.__flex_offset = current + if c.__flex then + c.__flex = c.__flex *grow_basis + current = current +c.__flex +e._gap + else + current = current +resolve_layout_units(get(c, e._direction == "column" and "h" or "w"), axis_size) +e._gap + end + end + end + + -- Pass 3: Justify and render. + local base = axis + if e._align == "center" then + base = axis +((axis_size -total_width) /2) + elseif e._align == "right" then + base = axis +axis_size -total_width + end + + for i = 1, #e do + local c = e[i] + if c._no_layout then + local cx, cy + if c.x then + cx = resolve_layout_units(get_raw(c, "x"), w) +x + cy = resolve_layout_units(get_raw(c, "y"), h) +y + end + + local cw, ch + if c.w then + cw = resolve_layout_units(get(c, "w"), w) + ch = resolve_layout_units(get(c, "h"), h) + end + + out[#out +1] = c:render(cx, cy, cw, ch) + elseif c.w then + if e._direction == "column" then + out[#out +1] = c:render(resolve_layout_units(get(c, "x"), w) +x, base +c.__flex_offset, resolve_layout_units(get(c, "w"), w), c.__flex) + else + out[#out +1] = c:render(base +c.__flex_offset, resolve_layout_units(get(c, "y"), h) +y, c.__flex, resolve_layout_units(get(c, "h"), h)) + end + elseif c.x then + if e._direction == "column" then + out[#out +1] = c:render(resolve_layout_units(get(c, "x"), w) +x, base +c.__flex_offset) + else + out[#out +1] = c:render(base +c.__flex_offset, resolve_layout_units(get(c, "y"), h) +y) + end + end + end + + return table.concat(out) + end, + direction = function(e, dir) + e._direction = dir + return e + end, + gap = function(e, gap) + e._gap = gap + return e + end, + align = function(e, align) + e._align = align + return e + end, +} +fs_row.__index = fs_row +setmetatable(fs_row, { + __call = function(_, x, y, w, h) + local e = {x = x, y = y, w = w, h = h, _gap = 0, __parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx} + setmetatable(e, fs_row) + table.insert(ctx, e) + ctx = e + return e + end +}) + + +local function fs_row_end() + if getmetatable(ctx) ~= fs_row then + minetest.log("warn", "`row_end` has no row to end; it will be ignored.") + return + end + ctx = ctx.__parent +end + +local function fs_column(...) + return fs_row(...) + :direction "column" +end + +local fs_column_end = fs_row_end + +-- MARK: Building + +local Window = { + render = function(e) + local out = { + "formspec_version[10]", + string.format("size[%f,%f]", e.width, e.height) + } + + if e._no_prepend then + out[#out +1] = "no_prepend[]" + end + + if e._position then + out[#out +1] = string.format("position[%f,%f]", e._position.x, e._position.y) + end + + if e._anchor then + out[#out +1] = string.format("anchor[%f,%f]", e._anchor.x, e._anchor.y) + end + + if e._padding then + out[#out +1] = string.format("padding[%f,%f]", e._padding.x, e._padding.y) + end + + if e._modal then + out[#out +1] = "allow_close[false]" + end + + if e._fullscreen then + out[#out +1] = string.format("bgcolor[%s;%s;%s]", get(e, "_fgcolor"), get(e, "_fullscreen"), get(e, "_bgcolor")) + end + + for i = 1, #e do + local c = e[i] + local cx, cy + if c.x then + cx = resolve_layout_units(get(c, "x"), e.width) + cy = resolve_layout_units(get(c, "y"), e.height) + end + + local cw, ch + if c.w then + cw = resolve_layout_units(get(c, "w"), e.width) + ch = resolve_layout_units(get(c, "h"), e.height) + end + + out[#out +1] = c:render(cx, cy, cw, ch) + end + return table.concat(out) + end, + no_prepend = function(e) + e._no_prepend = true + return e + end, + position = function(e, x, y) + e._position = {x = x, y = y} + return e + end, + anchor = function(e, x, y) + e._anchor = {x = x, y = y} + return e + end, + padding = function(e, x, y) + e._padding = {x = x, y = y} + return e + end, + modal = function(e, modal) + e._modal = modal ~= false and true or false + return e + end, + onclose = function(e, fn) + e._onclose = fn + return e + end, + bgcolor = function(e, foreground, fullscreen) + if fullscreen then + if fullscreen == true then + e._fullscreen = "true" + e._bgcolor = foreground + else + e._fullscreen = "both" + e._fgcolor = foreground + e._bgcolor = fullscreen + end + elseif not foreground then + e._fullscreen = "neither" + else + e._fullscreen = "false" + e._fgcolor = foreground + end + return e + end +} +Window.__index = Window + +local function fs_begin(w, h) + ctx = {_events = {on_click = {}, on_change = {}, on_scrollbar_event = {}, on_enter = {}}, width = w or 12, height = h or 10} + setmetatable(ctx, Window) + name_scope = {"_"} + nth_id = {0} + return ctx +end + +local function fs_end() + local _ctx = ctx + ctx = nil + name_scope = nil + nth_id = nil + return _ctx +end + +local Context = { + update = function(e) + -- This is used to prevent changes in state from inside the builder to trigger another rebuild. + if e._ignore then return end + -- `_inert` should be set when many state updates may take place at once, to avoid a rapid succession of rebuilds. + -- After these updates have taken place, the user should call `rebuild()` manually if `_dirty` is true. + if e._inert then + e._dirty = true + else + e:rebuild() + end + end, + rebuild = function(e) + e._dirty = nil + e._ignore = true + e:clear_state_bindings() + + local tracker = e._linked_states + table.insert(observers, tracker) + + local fs = type(e.formspec) == "function" and e.formspec(e.state, function() e:close() end) or e.formspec + + e._window = fs + + table.remove(observers) + + e._ignore = nil + + for x in pairs(e._linked_states) do + x._getters[e.id] = e + end + + fs._ctx = e + + for _, el in ipairs(fs) do + for _, x in pairs(el) do + local mt = getmetatable(x) + if mt == state or mt == DerivedState then + x._getters[e.id] = e + e._linked_states[x] = true + end + end + end + + e._events = fs._events + + local str = fs:render() + if e._mainmenu then + minetest.update_formspec(str) + elseif e._is_inventory then + e._player:set_inventory_formspec(str) + else + minetest.show_formspec(e.target, e.id, str) + end + end, + clear_state_bindings = function(e) + for x in pairs(e._linked_states) do + x._getters[e.id] = nil + end + end, + deinit = function(e) + if contexts[e.id] then + contexts[e.id] = nil + end + if e._window._onclose then + e._window:_onclose() + end + e:clear_state_bindings() + -- Kill our rebuild capability in case any callbacks fire on the way out. + e.rebuild = function() end + end, + close = function(e) + -- Inventories cannot be 'closed', only replaced. + if e._is_inventory then return end + minetest.close_formspec(e.target, e.id) + e:deinit() + end +} +Context.__index = Context + +local function fs_show(target, fs, state) + local id = "form"..unique_id() + local ctx = setmetatable({ + formspec = fs, + fields = {}, + target = type(target) == "string" and target or target:get_player_name(), + id = id, + _linked_states = {}, + state = state or {} + }, Context) + + if player_contexts[ctx.target] then + player_contexts[ctx.target]:deinit() + end + + contexts[id] = ctx + player_contexts[ctx.target] = ctx + + ctx:rebuild() + + return ctx +end + +local function fs_set_inventory(p, fs, state) + local id = "form"..unique_id() + local name = type(p) == "string" and p or p:get_player_name() + local ctx = setmetatable({ + _is_inventory = true, + _player = type(p) == "string" and minetest.get_player_by_name(p) or p, + formspec = fs, + fields = {}, + target = name, + id = id, + _linked_states = {}, + state = state or {} + }, Context) + + inventories[name] = ctx + + ctx:rebuild() + + return ctx +end + +local function fs_remove_inventory(p) + local name = p:get_player_name() + local inv = inventories[name] + if inv then + inv:deinit() + inventories[name] = nil + end +end + +-- MARK: Callback handling + +local function handler(ctx, fields) + ctx._inert = true + + for k, v in pairs(fields) do + if ctx._events.on_click[k] then + ctx._events.on_click[k](v) + elseif ctx._events.on_scrollbar_event[k] then + local ev = minetest.explode_scrollbar_event(v) + ctx._events.on_scrollbar_event[k](ev.type, ev.value) + elseif (not ctx.fields[k] or ctx.fields[k] ~= v) and ctx._events.on_change[k] then + ctx._events.on_change[k](v) + end + end + + if fields.key_enter_field and ctx._events.on_enter[fields.key_enter_field] then + ctx._events.on_enter[fields.key_enter_field](fields[fields.key_enter_field]) + end + + ctx.fields = fields + + ctx._inert = nil + + if fields.quit then + ctx:deinit() + else + if ctx._dirty then + ctx:rebuild() + ctx._dirty = nil + end + end +end + +-- Compatibility with main menu usage. +if minetest.update_formspec then + local mainmenu_context + function fs_show(fs, state) + if mainmenu_context then + mainmenu_context:deinit() + end + + local id = "form"..unique_id() + local ctx = setmetatable({ + _mainmenu = true, + formspec = fs, + fields = {}, + target = "menu", + id = id, + _linked_states = {}, + state = state or {} + }, Context) + + mainmenu_context = ctx + + ctx:rebuild() + + return ctx + end + + fs_set_inventory = nil + + minetest.button_handler = function(fields) + handler(mainmenu_context, fields) + end +-- Normal in-game usage. +else + minetest.register_on_player_receive_fields(function(p, form, fields) + local name = p:get_player_name() + local ctx = contexts[form] + -- If this is an event from a player inventory, check if we have a context managing that inventory. + -- No other special handling is needed, because the context already knows that it's an inventory and will react appropriately. + if form == "" then + ctx = inventories[name] + end + + -- Ensure that a) players can only trigger effects of formspecs that were opened legitimately, and b) only the player who opened it may interact with a given formspec instance. + if ctx and ctx.target == name then + handler(ctx, fields) + end + end) + + -- Remove all contexts tied to a leaving player. + minetest.register_on_leaveplayer(function(p) + fs_remove_inventory(p) + end) +end + + +-- MARK: API exposure + +imfs = { + _contexts = contexts, + _inventories = inventories, + + state = state, + derive = derive, + get_field = get, + begin = fs_begin, + end_ = fs_end, + scope = scope, + scope_end = scope_end, + show = fs_show, + set_inventory = fs_set_inventory, + remove_inventory = fs_remove_inventory, + resolve_layout_units = resolve_layout_units, + resolve_flex_layout_units = resolve_flex_layout_units, + container_start = function() + local container = {__parent = ctx, _events = setmetatable({}, {__index = ctx._events, __newindex = ctx._events}), __fs = ctx.__parent or ctx} + table.insert(ctx, container) + ctx = container + end, + container_end = function() + ctx = ctx.__parent + end, + add_to_context = function(container) + table.insert(ctx, container) + end, + + theme = theme, + + style = fs_style, + tooltip = fs_tooltip, + _named_tooltip = fs_named_tooltip, + label = fs_label, + arealabel = fs_arealabel, + hypertext = fs_hypertext, + box = fs_box, + image = fs_image, + item_image = fs_item_image, + model = fs_model, + button = fs_button, + list = fs_list, + inventory = fs_inventory, + listring = fs_listring, + field = fs_field, + textarea = fs_textarea, + checkbox = fs_checkbox, + scrollbar = fs_scrollbar, + scroll_container = fs_scroll_container, + scroll_container_end = fs_scroll_container_end, + group = fs_group, + group_end = fs_group_end, + row = fs_row, + row_end = fs_row_end, + column = fs_column, + column_end = fs_column_end, + + -- Can be called in a game's base mod to globalize imfs for brevity. + export = function() + for k, v in pairs(imfs) do + local key + if k == "state" or k == "derive" then + key = k + elseif k == "end_" then + key = "fs_end" + else + key = "fs_"..k + end + _G[key] = v + end + end +} + + + +local counter = imfs.state(0) +local str = imfs.state("") +local pressed = imfs.state(false) + +local function fs() + imfs.begin(12, 10) + + imfs.label(1, 2, "Counter: "..counter()) -- We don't need to use a derived state here because the window will detect this dependency. + imfs.label(1, 3, derive(function() return "Length: "..#str() end)) -- However, using a derived state will still work just fine. + + if pressed() then -- Reactive conditional rendering can be achieved using states. + imfs.label(1, 4, "Pressed!!") + end + + imfs.button(4, 0.5, 4, 0.75, "Increment") + :style { + bgcolor = "#aaf" + } + :style("hovered", { + bgcolor = "#faa" + }) + :style("pressed", { + bgcolor = "#afa" + }) + :onclick(function() + counter(counter() +1) + end) + + imfs.field(4, 2, 4, 0.75, "Test", str) + :onchange(function(value) + str(value) + end) + :onenter(function(value) + str(value) + pressed(true) + end) + + local m = imfs.model(1, 1, 3, 5, "firefly_character.gltf", {"firefly_character.png"}) + :rotation(0, 90) + :tooltip "Test" + + imfs.scrollbar(0, 0, 0.5, "100%") + + imfs.scroll_container(4, 3, 8, 7) + :named("test") + + for i = 0, 10 do -- One benefit of functional style over table style is that you can use loops seamlessly. + imfs.label(0, i, i) + end + + imfs.row(0, 1, 8, 4) + + imfs.box(0, 0, "1x", 2, "#f88") -- The "1x" unit for the width marks this element as dynamically resizing. + imfs.box(0, 0, counter() *0.25, 2, "#8f8") + imfs.box(0, 0, 2, 2, "#88f") + + imfs.row_end() + + imfs.column(0, 3, 4, 8) + :align "center" -- Notice that the elements are centered vertically in the column. + + imfs.box(0, 0, 2, counter() *0.25, "#8f8") + imfs.box(0, 0, 2, 2, "#88f") + + imfs.column_end() + + imfs.scroll_container_end() + + return imfs.end_() +end + +minetest.register_chatcommand("demo", { + func = function(name) + imfs.show(name, fs) + end +}) + diff --git a/mods/imfs/mod.conf b/mods/imfs/mod.conf new file mode 100644 index 0000000..45cd0e4 --- /dev/null +++ b/mods/imfs/mod.conf @@ -0,0 +1,2 @@ +name = imfs +decription = Immediate mode formspecs. \ No newline at end of file diff --git a/mods/imhud/init.lua b/mods/imhud/init.lua new file mode 100644 index 0000000..49a26e3 --- /dev/null +++ b/mods/imhud/init.lua @@ -0,0 +1,84 @@ + +local contexts = {} + +imhud = { + _contexts = contexts +} + +local ctx + +-- MARK: Helpers + +local function new_id() + return "_imhud_"..minetest.get_us_time().."_"..math.random(1, 100000) +end + +-- MARK: Elements + +local hud_image = { + add = function() + + end, + update = function() + + end, + remove = function() + + end +} +setmetatable(hud_image, { + __call = function(_, texture) + local e = {text = texture} + setmetatable(e, hud_image) + return e + end +}) + +-- MARK: Building + +local HUD = { + +} +HUD.__index = HUD + +function imhud.begin() + local e = {} + setmetatable(e, HUD) + ctx = e + return e +end + +function imhud.end_() + +end + +local Context = { + +} +Context.__index = Context + +function imhud.add(target, hud, name) + local ctx = { + target = type(target) == "string" and target or target:get_player_name(), + elems = {} + } + setmetatable(ctx, Context) + contexts[name or new_id()] = ctx +end + +function imhud.remove(name) + +end + + + + +local function hud() + +end + +minetest.register_chatcommand("hud", { + func = function(name) + imhud.add() + end +}) diff --git a/mods/imhud/mod.conf b/mods/imhud/mod.conf new file mode 100644 index 0000000..9801daa --- /dev/null +++ b/mods/imhud/mod.conf @@ -0,0 +1,2 @@ +name = imhud +depends = imfs \ No newline at end of file