diff --git a/.gitignore b/.gitignore index 184bbc51..7b2e735f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .obj/ bin/ build/ +*.zip *.o *.a *.d diff --git a/Makefile b/Makefile index a752b87d..ad65ddbe 100755 --- a/Makefile +++ b/Makefile @@ -10,29 +10,18 @@ endif CXX:=$(CC) -# Temp to strip long emcc paths to just emcc -CC := $(notdir $(CC)) - OPT ?= 0 - -INFO := LD = $(CC) STEAM = steam/sdk STEAMAPI = -ifeq ($(CC), emcc) - LDFLAGS += -sUSE_WEBGPU --shell-file shell.html - CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=5MB -sALLOW_MEMORY_GROWTH - OPT = 0 +ifeq ($(CROSS)$(CC), emcc) + LDFLAGS += --shell-file shell.html --closure 1 + CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=1MB -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB NDEBUG = 1 - AR = emar ARCH:= wasm -endif - -CCC != $(CC) -v -ifneq ($(findstring clangcc , $(CCC)),) - LDFLAGS += -Wl,-rpath=./ + OPT=small endif ifdef NEDITOR @@ -68,17 +57,13 @@ ifdef LEAK endif ifeq ($(OPT),small) - CPPFLAGS += -Oz -flto -fno-ident -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections + CPPFLAGS += -Os -flto -fno-ident -fno-asynchronous-unwind-tables -ffunction-sections -fdata-sections LDFLAGS += -flto - ifeq ($(CC), emcc) - LDFLAGS += --closure 1 - endif - INFO :=$(INFO)_small else ifeq ($(OPT), 1) - CPPFLAGS += -O2 -flto + CPPFLAGS += -O3 -flto INFO :=$(INFO)_opt else CPPFLAGS += -O2 @@ -103,11 +88,11 @@ ifeq ($(OS), Windows_NT) # then WINDOWS LDFLAGS += -mwin32 -static CPPFLAGS += -mwin32 LDLIBS += mingw32 kernel32 d3d11 user32 shell32 dxgi gdi32 ws2_32 ole32 winmm setupapi m pthread - PKGCMD = zip -q -r $(MAKEDIR)/$(DISTDIR)/$(DIST) . -x \*.a ./obj/\* ZIP = .zip UNZIP = unzip -o -q $(DISTDIR)/$(DIST) -d $(DESTDIR) INFO :=$(INFO)_win + EXT = .exe else ifeq ($(OS), IOS) CC = /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang SDK = iphoneos @@ -118,7 +103,7 @@ else ifeq ($(OS), IOS) CXXFLAGS += -std=c++11 CFLAGS += -x objective-c INFO :=$(INFO)_ios -else ifeq ($(CC), emcc) # Then WEB +else ifeq ($(OS), wasm) # Then WEB OS := Web LDFLAGS += -sMIN_WEBGL_VERSION=2 -sMAX_WEBGL_VERSION=2 CPPFLAGS += -DNSTEAM @@ -199,20 +184,20 @@ install: $(NAME) $(NAME): $(OBJS) $(DEPS) @echo Linking $(NAME) - $(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@ + $(CROSS)$(LD) $^ $(CPPFLAGS) $(LDFLAGS) -L. $(LDPATHS) $(LDLIBS) -o $@ @echo Finished build %$(INFO).o: %.c @echo Making C object $@ - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + $(CROSS)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ %$(INFO).o: %.cpp @echo Making C++ object $@ - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -fpermissive -c $< -o $@ + $(CROSS)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -fpermissive -c $< -o $@ %$(INFO).o: %.m @echo Making Objective-C object $@ - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + $(CROSS)$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ shaders: $(SHADERS) @echo Making shaders @@ -246,7 +231,7 @@ icon.ico: $(ICON) rm $(ICNNAME) resource.o: resource.rc resource.manifest icon.ico - windres -i $< -o $@ + $(CROSS)windres -i $< -o $@ crossios: make OS=IOS ARCH=arm64 DEBUG=$(DEBUG) OPT=$(OPT) @@ -271,11 +256,14 @@ crossmac: Prosperon.icns mv $(NAME) Prosperon.app/Contents/MacOS/Prosperon cp Info.plist Prosperon.app/Contents cp Prosperon.icns Prosperon.app/Contents/Resources + +crosswin: + make CROSS=x86_64-w64-mingw32- OS=Windows_NT CC=gcc crossweb: - make CC=emcc + make CROSS=em OS=wasm mv $(APP).html index.html - + clean: @echo Cleaning project rm -f source/shaders/*.h core.cdb jso cdb packer TAGS source/engine/core.cdb.h tools/libcdb.a $(APP)* *.icns *.ico diff --git a/scripts/editor.js b/scripts/editor.js index 839ff3cc..9c6234d4 100644 --- a/scripts/editor.js +++ b/scripts/editor.js @@ -1372,129 +1372,6 @@ editor.inputs.s.doc = "Scale selected."; editor.inputs.s.released = function() { this.scalelist = []; }; -var inputpanel = { - title: "untitled", - toString() { return this.title; }, - value: "", - on: false, - pos:[100,window.size.y-50], - wh:[350,600], - anchor: [0,1], - padding:[5,-15], - - gui() { - this.win ??= Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor, padding:this.padding}); - var itms = this.guibody(); - if (!Array.isArray(itms)) itms = [itms]; - if (this.title) - this.win.items = [ - Mum.column({items: [Mum.text({str:this.title}), ...itms ]}) - ]; - else - this.win.items = itms; - - this.win.draw([100, window.size.y-50]); - }, - - guibody() { - return [ - Mum.text({str:this.value, color:Color.green}), - Mum.button({str:"SUBMIT", action:this.submit.bind(this)}) - ]; - }, - - open() { - this.on = true; - this.value = ""; - this.start(); - this.keycb(); - }, - - start() {}, - - close() { - player[0].uncontrol(this); - this.on = false; - if ('on_close' in this) - this.on_close(); - }, - - action() { - - }, - - closeonsubmit: true, - submit() { - if (!this.submit_check()) return; - this.action(); - if (this.closeonsubmit) - this.close(); - }, - - submit_check() { return true; }, - - keycb() {}, - - caret: 0, - - reset_value() { - this.value = ""; - this.caret = 0; - }, - - input_backspace_pressrep() { - this.value = this.value.slice(0,-1); - this.keycb(); - }, -}; - -inputpanel.inputs = {}; -inputpanel.inputs.block = true; - -inputpanel.inputs.post = function() { this.keycb(); } - -inputpanel.inputs.char = function(c) { - this.value = this.value.slice(0,this.caret) + c + this.value.slice(this.caret); - this.caret++; -} -inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); }; -inputpanel.inputs['C-d'].rep = true; -inputpanel.inputs.tab = function() { - this.value = tab_complete(this.value, this.assets); - this.caret = this.value.length; -} -inputpanel.inputs.escape = function() { this.close(); } -inputpanel.inputs['C-b'] = function() { - if (this.caret === 0) return; - this.caret--; -}; -inputpanel.inputs['C-b'].rep = true; -inputpanel.inputs['C-u'] = function() -{ - this.value = this.value.slice(this.caret); - this.caret = 0; -} -inputpanel.inputs['C-f'] = function() { - if (this.caret === this.value.length) return; - this.caret++; -}; -inputpanel.inputs['C-f'].rep = true; -inputpanel.inputs['C-a'] = function() { this.caret = 0; }; -inputpanel.inputs['C-e'] = function() { this.caret = this.value.length; }; -inputpanel.inputs.backspace = function() { - if (this.caret === 0) return; - this.value = this.value.slice(0,this.caret-1) + this.value.slice(this.caret); - this.caret--; -}; -inputpanel.inputs.backspace.rep = true; -inputpanel.inputs.enter = function() { this.submit(); } - -inputpanel.inputs['C-k'] = function() { - this.value = this.value.slice(0,this.caret); -}; - -inputpanel.inputs.lm = function() { gui.controls.check_submit(); } - var replpanel = Object.copy(inputpanel, { title: "", closeonsubmit:false, @@ -1579,11 +1456,11 @@ replpanel.inputs.tab = function() { var comp = ""; if (stub) - comp = tab_complete(stub, keys); + comp = input.tabcomplete(stub, keys); else if (!this.value.includes('.')) - comp = tab_complete(o, keys); + comp = input.tabcomplete(o, keys); else - comp = tab_complete("",keys); + comp = input.tabcomplete("",keys); if (stub) this.value = o + '.' + comp; @@ -1848,33 +1725,6 @@ var quitpanel = Object.copy(inputpanel, { }, }); -var notifypanel = Object.copy(inputpanel, { - title: "notification", - msg: "Refusing to save. File already exists.", - action() { - this.close(); - }, - - guibody() { - return Mum.column({items: [ - Mum.text({str:this.msg}), - Mum.button({str:"OK", action:this.close.bind(this)}) - ]}); - }, -}); - -var gen_notify = function(val, fn) { - var panel = Object.create(notifypanel); - panel.msg = val; - panel.yes = fn; - panel.inputs = {}; - panel.inputs.y = function() { panel.yes(); panel.close(); }; - panel.inputs.y.doc = "Confirm yes."; - panel.inputs.enter = function() { panel.close(); }; - panel.inputs.enter.doc = "Close."; - return panel; -}; - var allfiles = []; allfiles.push(Resources.scripts, Resources.images, Resources.sounds); allfiles = allfiles.flat(); @@ -1904,29 +1754,6 @@ var componentexplorer = Object.copy(inputpanel, { }, }); -function tab_complete(val, list) { - if (!val) return val; - list.dofilter(function(x) { return x.startsWith(val); }); - - if (list.length === 1) { - return list[0]; - } - - var ret = undefined; - var i = val.length; - while (!ret && !Object.empty(list)) { - var char = list[0][i]; - if (!list.every(function(x) { return x[i] === char; })) - ret = list[0].slice(0, i); - else { - i++; - list.dofilter(function(x) { return x.length-1 > i; }); - } - } - - return ret ? ret : val; -} - var entitylistpanel = Object.copy(inputpanel, { title: "Level object list", level: {}, diff --git a/scripts/engine.js b/scripts/engine.js index d914e588..7d4b3b80 100644 --- a/scripts/engine.js +++ b/scripts/engine.js @@ -253,6 +253,9 @@ global.check_registers = function(obj) if (typeof obj.gui === 'function') obj.timers.push(Register.gui.register(obj.gui.bind(obj))); + + if (typeof obj.screengui === 'function') + obj.timers.push(Register.screengui.register(obj.screengui.bind(obj))); for (var k in obj) { if (!k.startswith("on_")) continue; @@ -335,9 +338,12 @@ function process() debug.draw(); // calls needed debugs render.flush(); - render.set_window(); + render.hud_res(window.rendersize); prosperon.gui(); render.flush(); + render.hud_res(window.size); + prosperon.screengui(); + render.flush(); render.end_pass(); } @@ -379,7 +385,7 @@ game.tag_clear_guid = function(guid) game.objects_with_tag = function(tag) { - if (!game.tags[tag]) return; + if (!game.tags[tag]) return []; return Object.values(game.tags[tag]); } @@ -545,6 +551,7 @@ Register.add_cb("physupdate"); Register.add_cb("gui"); Register.add_cb("debug"); Register.add_cb("draw"); +Register.add_cb("screengui"); var Event = { events: {}, @@ -611,6 +618,9 @@ function world_start() { } global.mixin("scripts/physics"); +global.mixin("scripts/widget"); + +globalThis.mum = app.spawn("scripts/mum"); window.title = `Prosperon v${prosperon.version}`; window.size = [500,500]; diff --git a/scripts/input.js b/scripts/input.js index 6fd5c827..91d224b8 100644 --- a/scripts/input.js +++ b/scripts/input.js @@ -245,6 +245,29 @@ input.action = { actions: [], }; +input.tabcomplete = function(val, list) { + if (!val) return val; + list.dofilter(function(x) { return x.startsWith(val); }); + + if (list.length === 1) { + return list[0]; + } + + var ret = undefined; + var i = val.length; + while (!ret && !Object.empty(list)) { + var char = list[0][i]; + if (!list.every(function(x) { return x[i] === char; })) + ret = list[0].slice(0, i); + else { + i++; + list.dofilter(function(x) { return x.length-1 > i; }); + } + } + + return ret ? ret : val; +} + /* May be a human player; may be an AI player */ var Player = { players: [], diff --git a/scripts/mum.js b/scripts/mum.js new file mode 100644 index 00000000..4442773d --- /dev/null +++ b/scripts/mum.js @@ -0,0 +1,21 @@ +var panel; + +self.screengui = function() +{ + if (panel) panel.gui(); +} + +self.prompt = function(msg = "prompt", value = "", list = [], cb = function() {}) +{ + console.info(`creating popup`); + panel = Object.create(listpanel); + panel.title = msg; + panel.value = value; + panel.allassets = list; + panel.action = function() { + cb(panel.value); + panel = undefined; + } + panel.start(); + player[0].control(panel); +} \ No newline at end of file diff --git a/scripts/std.js b/scripts/std.js index a27a51b8..39499825 100644 --- a/scripts/std.js +++ b/scripts/std.js @@ -44,7 +44,7 @@ os.openurl = function(url) { os.system(`open ${url}`); } -var projectfile = ".prosperon/project.json"; +var projectfile = "project.prosperon"; Resources.is_sound = function(path) { var ext = path.ext(); @@ -204,29 +204,31 @@ Cmdline.register_order("edit", function() { }, "Edit the project in this folder. Give it the name of an UR to edit that specific object.", "?UR?"); Cmdline.register_order("init", function() { - say('top of init'); if (io.exists(projectfile)) { say("Already a game here."); return; } - say('top of ls'); + if (!(io.ls().length === 0)) { say("Directory is not empty. Make an empty one and init there."); return; } - say('top of mkdir'); + io.mkdir(".prosperon"); var project = {}; project.version = prosperon.version; project.revision = prosperon.revision; io.slurpwrite(projectfile, json.encode(project)); - }, "Turn the directory into a Prosperon game."); Cmdline.register_order("debug", function() { Cmdline.orders.play([]); }, "Play the game with debugging enabled."); +Cmdline.register_order("web", function() { + Cmdline.orders.play([]); +}, "Play the game in a web browser."); + Cmdline.register_order("play", function(argv) { if (argv[0]) io.chdir(argv[0]); @@ -244,7 +246,6 @@ Cmdline.register_order("play", function(argv) { global.mixin("config.js"); if (project.title) window.title = project.title; - if (window.rendersize.equal([0,0])) window.rendersize = window.size; console.info(`Starting game with window size ${window.size} and render ${window.rendersize}.`); diff --git a/scripts/widget.js b/scripts/widget.js new file mode 100644 index 00000000..8b3ac558 --- /dev/null +++ b/scripts/widget.js @@ -0,0 +1,192 @@ + +var inputpanel = { + title: "untitled", + toString() { return this.title; }, + value: "", + on: false, + pos:[20,window.size.y-20], + wh:[100,100], + anchor: [0,1], + padding:[5,-15], + + gui() { + this.win ??= Mum.window({width:this.wh.x,height:this.wh.y, color:Color.black.alpha(0.1), anchor:this.anchor, padding:this.padding}); + var itms = this.guibody(); + if (!Array.isArray(itms)) itms = [itms]; + if (this.title) + this.win.items = [ + Mum.column({items: [Mum.text({str:this.title}), ...itms ]}) + ]; + else + this.win.items = itms; + + this.win.draw([100, window.size.y-50]); + }, + + guibody() { + return [ + Mum.text({str:this.value, color:Color.green}), + Mum.button({str:"SUBMIT", action:this.submit.bind(this)}) + ]; + }, + + open() { + this.on = true; + this.value = ""; + this.start(); + this.keycb(); + }, + + start() {}, + + close() { + player[0].uncontrol(this); + this.on = false; + if ('on_close' in this) + this.on_close(); + }, + + action() { + + }, + + closeonsubmit: true, + submit() { + if (!this.submit_check()) return; + this.action(); + if (this.closeonsubmit) + this.close(); + }, + + submit_check() { return true; }, + + keycb() {}, + + caret: 0, + + reset_value() { + this.value = ""; + this.caret = 0; + }, + + input_backspace_pressrep() { + this.value = this.value.slice(0,-1); + this.keycb(); + }, +}; + +inputpanel.inputs = {}; +inputpanel.inputs.block = true; + +inputpanel.inputs.post = function() { this.keycb(); } + +inputpanel.inputs.char = function(c) { + this.value = this.value.slice(0,this.caret) + c + this.value.slice(this.caret); + this.caret++; +} +inputpanel.inputs['C-d'] = function() { this.value = this.value.slice(0,this.caret) + this.value.slice(this.caret+1); }; +inputpanel.inputs['C-d'].rep = true; +inputpanel.inputs.tab = function() { + this.value = input.tabcomplete(this.value, this.assets); + this.caret = this.value.length; +} +inputpanel.inputs.escape = function() { this.close(); } +inputpanel.inputs['C-b'] = function() { + if (this.caret === 0) return; + this.caret--; +}; +inputpanel.inputs['C-b'].rep = true; +inputpanel.inputs['C-u'] = function() +{ + this.value = this.value.slice(this.caret); + this.caret = 0; +} +inputpanel.inputs['C-f'] = function() { + if (this.caret === this.value.length) return; + this.caret++; +}; +inputpanel.inputs['C-f'].rep = true; +inputpanel.inputs['C-a'] = function() { this.caret = 0; }; +inputpanel.inputs['C-e'] = function() { this.caret = this.value.length; }; +inputpanel.inputs.backspace = function() { + if (this.caret === 0) return; + this.value = this.value.slice(0,this.caret-1) + this.value.slice(this.caret); + this.caret--; +}; +inputpanel.inputs.backspace.rep = true; +inputpanel.inputs.enter = function() { this.submit(); } + +inputpanel.inputs['C-k'] = function() { + this.value = this.value.slice(0,this.caret); +}; + +inputpanel.inputs.lm = function() { gui.controls.check_submit(); } + +var notifypanel = Object.copy(inputpanel, { + title: "notification", + msg: "Refusing to save. File already exists.", + action() { + this.close(); + }, + + guibody() { + return Mum.column({items: [ + Mum.text({str:this.msg}), + Mum.button({str:"OK", action:this.close.bind(this)}) + ]}); + }, +}); + +var gen_notify = function(val, fn) { + var panel = Object.create(notifypanel); + panel.msg = val; + panel.yes = fn; + panel.inputs = {}; + panel.inputs.y = function() { panel.yes(); panel.close(); }; + panel.inputs.y.doc = "Confirm yes."; + panel.inputs.enter = function() { panel.close(); }; + panel.inputs.enter.doc = "Close."; + return panel; +}; + +var listpanel = Object.copy(inputpanel, { + assets: [], + allassets: [], + mumlist: {}, + + submit_check() { + if (this.assets.length === 0) return false; + + this.value = this.assets[0]; + return true; + }, + + start() { + this.assets = this.allassets.slice(); + this.caret = 0; + this.mumlist = []; + this.assets.forEach(function(x) { + this.mumlist[x] = Mum.text({str:x, action:this.action, color: Color.blue, hovered: {color:Color.red}, selectable:true}); + }, this); + }, + + keycb() { + if(this.value) + this.assets = this.allassets.filter(x => x.startswith(this.value)); + else + this.assets = this.allassets.slice(); + for (var m in this.mumlist) + this.mumlist[m].hide = true; + this.assets.forEach(function(x) { + this.mumlist[x].hide = false; + }, this); + }, + + guibody() { + var a = [Mum.text({str:this.value,color:Color.green, caret:this.caret})]; + var b = a.concat(Object.values(this.mumlist)); + return Mum.column({items:b, offset:[0,-10]}); + }, +}); + +return {inputpanel, gen_notify, notifypanel, listpanel} \ No newline at end of file diff --git a/shell.html b/shell.html new file mode 100644 index 00000000..a5b10d4e --- /dev/null +++ b/shell.html @@ -0,0 +1,62 @@ + + +
+ + +