JSO compiler; packer; update CDB to 0.80; assets now packed into binary

This commit is contained in:
2023-09-18 12:36:07 +00:00
parent 97483d4ce5
commit c9c05d931d
68 changed files with 1306 additions and 347 deletions

View File

@@ -646,6 +646,11 @@ static inline HMM_Vec4 HMM_SubV4(HMM_Vec4 Left, HMM_Vec4 Right) {
return Result;
}
static inline HMM_Vec2 HMM_ScaleV2(HMM_Vec2 v, double s)
{
return HMM_V2(v.X*s, v.Y*s);
}
static inline HMM_Vec2 HMM_MulV2(HMM_Vec2 Left, HMM_Vec2 Right) {
HMM_Vec2 Result;

View File

@@ -1099,6 +1099,14 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 135:
ret = float2js(cam_zoom());
break;
case 136:
ret = v22js(world2screen(js2hmmv2(argv[1])));
break;
case 137:
ret = v22js(screen2world(js2hmmv2(argv[1])));
break;
}
if (str)

View File

@@ -382,9 +382,6 @@ void call_input_down(int *key) {
/* This is called once every frame - or more if we want it more! */
void input_poll(double wait) {
mouse_delta = cpvzero;
mousewheel = cpvzero;
for (int i = 0; i < arrlen(downkeys); i++)
call_input_down(&downkeys[i]);
}

View File

@@ -15,6 +15,10 @@
#include "stb_ds.h"
#include "resources.h"
#include "yugine.h"
#include "sokol/sokol_app.h"
#define SOKOL_GLUE_IMPL
#include "sokol/sokol_glue.h"
#include "crt.sglsl.h"
#include "box.sglsl.h"
@@ -209,6 +213,8 @@ void render_init() {
mainwin.height = sapp_height();
sg_setup(&(sg_desc){
.context = sapp_sgcontext(),
.mtl_force_managed_storage_mode = 1,
.logger = {
.func = sg_logging,
.user_data = NULL,
@@ -372,9 +378,23 @@ cpVect cam_pos() {
static float zoom = 1.f;
float cam_zoom() { return zoom; }
void add_zoom(float val) { zoom = val; }
HMM_Vec2 world2screen(HMM_Vec2 pos)
{
pos = HMM_SubV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
pos = HMM_ScaleV2(pos, 1.0/zoom);
pos = HMM_AddV2(pos, HMM_V2(mainwin.width/2.0, mainwin.height/2.0));
return pos;
}
HMM_Vec2 screen2world(HMM_Vec2 pos)
{
pos = HMM_AddV2(pos, HMM_V2(mainwin.width/2.0, mainwin.height/2.0));
pos = HMM_MulV2(pos, HMM_V2(zoom,zoom));
return HMM_AddV2(pos, HMM_V2(cam_pos().x, cam_pos().y));
}
HMM_Mat4 projection = {0.f};
HMM_Mat4 hudproj = {0.f};

View File

@@ -9,6 +9,7 @@
#define SOKOL_GLCORE33
#define SOKOL_WIN32_FORCE_MAIN
#elif __APPLE__
// #define SOKOL_METAL
#define SOKOL_GLCORE33
#endif
@@ -77,6 +78,8 @@ void set_cam_body(cpBody *body);
cpVect cam_pos();
float cam_zoom();
void add_zoom(float val);
HMM_Vec2 world2screen(HMM_Vec2 pos);
HMM_Vec2 screen2world(HMM_Vec2 pos);
sg_shader sg_compile_shader(const char *v, const char *f, sg_shader_desc *d);

View File

@@ -22,6 +22,8 @@
#include "stb_ds.h"
#include "core.cdb.h"
char *DATA_PATH = NULL;
char *PREF_PATH = NULL;
@@ -36,8 +38,8 @@ char pathbuf[MAXPATH + 1];
const char *DB_NAME = "test.db";
static struct cdb corecdb;
static struct cdb game_cdb;
static int loaded_cdb = 0;
void resources_init() {
DATA_PATH = malloc(MAXPATH);
@@ -47,10 +49,9 @@ void resources_init() {
if (!PREF_PATH)
PREF_PATH = strdup("./tmp/");
int fd;
fd = open("test.cdb", O_RDONLY);
int fd = open("test.cdb", O_RDONLY);
cdb_init(&game_cdb, fd);
loaded_cdb = 1;
cdb_initf(&corecdb, core_cdb, core_cdb_len);
}
char *get_filename_from_path(char *path, int extension) {
@@ -86,6 +87,21 @@ FILE *res_open(char *path, const char *tag) {
return f;
}
char *seprint(char *fmt, ...)
{
va_list args;
va_start (args, fmt);
char test[128];
int len = vsnprintf(test, 128, fmt, args);
if (len > 128) {
char test = malloc(len+1);
vsnprintf(test, len+1, fmt, args);
return strdup(test);
}
return strdup(test);
}
static char *ext_paths = NULL;
#ifndef __EMSCRIPTEN__
@@ -163,17 +179,23 @@ char *make_path(const char *file) {
return pathbuf;
}
unsigned char *slurp_file(const char *filename, size_t *size)
void *cdb_slurp(struct cdb *cdb, const char *file, size_t *size)
{
if (cdb_find(&game_cdb, filename, strlen(filename))) {
unsigned vlen, vpos;
vpos = cdb_datapos(&game_cdb);
vlen = cdb_datalen(&game_cdb);
vpos = cdb_datapos(cdb);
vlen = cdb_datalen(cdb);
char *data = malloc(vlen);
cdb_read(&game_cdb, data, vlen, vpos);
cdb_read(cdb, data, vlen, vpos);
if (size) *size = vlen;
return data;
}
}
unsigned char *slurp_file(const char *filename, size_t *size)
{
if (cdb_find(&game_cdb, filename, strlen(filename)))
return cdb_slurp(&game_cdb, filename, size);
else if (cdb_find(&corecdb, filename, strlen(filename)))
return cdb_slurp(&corecdb, filename, size);
FILE *f;
@@ -197,7 +219,7 @@ unsigned char *slurp_file(const char *filename, size_t *size)
char *slurp_text(const char *filename, size_t *size)
{
size_t len;
char *str = slurp_file(filename, &len);
unsigned char *str = slurp_file(filename, &len);
if (!str) return NULL;
char *retstr = malloc(len+1);
memcpy(retstr, str, len);

View File

@@ -19,6 +19,8 @@ unsigned char *slurp_file(const char *filename, size_t *size);
char *slurp_text(const char *filename, size_t *size);
int slurp_write(const char *txt, const char *filename);
char *seprint(char *fmt, ...);
void pack_engine(const char *fname);
#endif

View File

@@ -45,6 +45,9 @@ void script_startup() {
for (int i = 0; i < 100; i++)
num_cache[i] = int2js(i);
script_dofile("scripts/engine.js");
// jso_file("scripts/engine.js");
}
JSValue num_cache[100] = {0};
@@ -95,13 +98,13 @@ void script_evalf(const char *format, ...)
JS_FreeValue(js,obj);
}
uint8_t *compile_script(const char *file) {
size_t len;
const char *script = slurp_text(file, &len);
JSValue obj = JS_Eval(js, script, len, file, JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAGS);
uint8_t *compile_script(const char *file, size_t *len) {
const char *script = slurp_text(file, len);
JSValue obj = JS_Eval(js, script, *len, file, JS_EVAL_FLAG_COMPILE_ONLY | JS_EVAL_TYPE_GLOBAL | JS_EVAL_FLAGS);
free(script);
size_t out_len;
uint8_t *out;
return JS_WriteObject(js, &out_len, obj, JS_WRITE_OBJ_BYTECODE);
uint8_t *out = JS_WriteObject(js, &out_len, obj, JS_WRITE_OBJ_BYTECODE);
return out;
}
struct callee stacktrace_callee;
@@ -127,6 +130,25 @@ int script_dofile(const char *file) {
return file_mod_secs(file);
}
JSValue script_runjso(const uint8_t *buf, size_t len)
{
JSValue obj = JS_ReadObject(js, buf, len, JS_EVAL_FLAGS);
JSValue ret = JS_EvalFunction(js, obj);
js_print_exception(ret);
return ret;
}
time_t jso_file(const char *file)
{
size_t len;
uint8_t *byte = compile_script(file, &len);
JSValue obj = JS_ReadObject(js, byte, len, JS_READ_OBJ_BYTECODE);
JSValue ret = JS_EvalFunction(js, obj);
js_print_exception(ret);
free(byte);
return file_mod_secs(file);
}
JSValue script_runfile(const char *file)
{
size_t len;

View File

@@ -20,6 +20,7 @@ void script_startup();
void script_run(const char *script, const char *file);
void script_evalf(const char *format, ...);
int script_dofile(const char *file);
time_t jso_file(const char *file);
JSValue script_runfile(const char *file);
void script_update(double dt);
void script_draw();
@@ -57,6 +58,6 @@ void call_physics(double dt);
void register_draw(struct callee c);
void call_draw();
uint8_t *compile_script(const char *file);
uint8_t *compile_script(const char *file, size_t *len);
#endif

View File

@@ -292,13 +292,13 @@ static void _sg_metal_encode_texture_pixels(int x, int y, int w, int h, bool ori
[cmd_buffer waitUntilCompleted];
MTLRegion mtl_region = MTLRegionMake2D(0, 0, w, h);
void* temp_pixels = (void*)SOKOL_MALLOC(w * 4 * h);
void* temp_pixels = malloc(w * 4 * h);
SOKOL_ASSERT(temp_pixels);
[mtl_dst_texture getBytes:temp_pixels bytesPerRow:w * 4 fromRegion:mtl_region mipmapLevel:0];
// int res = SDL_ConvertPixels(w, h, _sg_metal_texture_format_to_sdl_pixel_format(mtl_dst_texture_desc.pixelFormat), temp_pixels, w * 4, SDL_PIXELFORMAT_RGBA32, pixels, w * 4);
SOKOL_FREE(temp_pixels);
SOKOL_ASSERT(res == 0);
_SOKOL_UNUSED(res);
free(temp_pixels);
// SOKOL_ASSERT(res == 0);
// _SOKOL_UNUSED(res);
}
static void _sg_metal_query_image_pixels(_sg_image_t* img, void* pixels) {

View File

@@ -1,184 +0,0 @@
2006-06-29 Michael Tokarev <mjt@corpit.ru>
* see debian/changelog file for further changes.
2005-04-18 Michael Tokarev <mjt@corpit.ru>
* move cdb_make_find.c content into cdb_make_put.c
* introduce CDB_PUT_REPLACE0 - zerofill old duplicates
* allow usage of cdb.h in C++
2005-04-11 Michael Tokarev <mjt@corpit.ru>
* do not autogenerate files (cdb.h.in, cdb.3.in etc), but
use real files instead (only substituted VERSION and NAME)
* finally fixed the `!fputs()' condition to be `fputs() < 0'
as it should be in cdb.c (misbehaves on *bsd)
* kill cdbi_t usage in cdb_int.h
* export _cdb_make_fullwrite() (was ewrite()) and _cdb_make_flush()
and use them in cdb_make.c as appropriate
* almost completely rewrite _cdb_make_find() and friends:
- _cdb_make_find() now accepts new parameter, remove (bool), which,
if true, indicates all found records should be deleted from the
database.
- Call _cdb_make_find() with remove=0 from cdb_make_exists()
- Call _cdb_make_find() with appropriate arguments from
cdb_make_put(), and simplify the latter greatly (was too clumsy
anyway)
* rename `flags' parameter in cdb_make_put() to be `mode' which is
more appropriate
* change #if conditional in nss_cdb.c
from __GLIBC__ to __GNU_LIBRARY__
2003-11-04 Michael Tokarev <mjt@corpit.ru>
* added cdb_get() routine: tinycdb officially uses mmap.
* added cdb_{get,read}{data,key}() macros to read and get
current data and key.
* fixed bug in cdb_seek() - incorrect wrap, sometimes
cdb_seek()+cdb_bread() may return EIO instead of finding
correct record.
* added some tweaks to Makefile to build position-independent
libcdb_pic.a and shared libcdb.so libraries. Note that
using libcdb as shared library is probably not a good idea,
due to tiny size of the library.
* added initial nss_cdb module. Still not well-tested.
Probably will not build on non-GNU system.
* adjusted tests.{ok,sh} for latest cdb utility modifications
(-a mode in query by default)
* Victor Porton (porton at ex-code.com) provided a patch
to allow tinycdb to be built on win32 platform (cdb_init.c).
Completely untested.
2003-08-13 Michael Tokarev <mjt@corpit.ru>
* s/cdbi_t/unsigned/g. No need to keep this type.
* changed usage of cdb_findnext(): one need to pass
pointer to cdb structure to cdb_findnext() now,
and should use cdb_datapos(struct cdb_find *)
instead of cdb_datapos(struct cdb *)
* added cdb_seqinit() and cdb_seqnext() routines for sequential
record enumeration
* addded cdb_dend to the cdb structure: end of data
position. Use that in cdb_seq*().
* more strict checking: ensure data is within data section,
and hash tables are within hash section of a file.
* cdb_make.c (cdb_make_start): zerofill cdb_make structure
to shut valgrind up (writing uninitialized data to file)
* cdb.c (cmode): always open file in RDWR mode to allow
duplicate key detection
2002-12-08 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.73
* de-Debianization. Oh well... ;)
* no code changes, just like in 0.72
2002-10-13 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.72
* cleaned up debian packaging and made it actually work
* no code changes
2002-07-22 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.71
* rearranged object files to not depend on ranlib on
systems that requires it (i.e. OpenBSD)
* use ranlib but mark it's possible error as non-fatal
2001-12-10 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.7a
* converted to CVS, added two missing #include <stdlib.h> for
malloc declaration and spec target to the Makefile
2001-10-14 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.7
* added cdb_seek() and cdb_bread() routines as found
in freecdb/cdb-0.64
2001-07-26 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.6
* added another option, CDB_PUT_WARN, to cdb_make_put's flags
(to allow adding unconditionally but still warn about dups),
now cdb_make_put seems to be logically complete.
* added and documented -r and -u options for cdb(1) command,
and made them consistent with -w and -e also.
* reorganized cdb(1) manpage and added changes made to cdb
command.
* added version references to manpages (and make them autogenerated
to simplify maintenance).
* added cdb(5) manpage describing CDB file format.
2001-07-25 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.5
* added missing #include <sys/types.h> in cdb_init.c, thanks to
ppetru@ppetru.net (Petru Paler)
* removed usage of pread() in cdb_make_find() and friends,
suggested by Liviu Daia <Liviu.Daia@imar.ro>
* autogenerate tinycdb.spec file from template and debian/changelog
* autogenerate cdb.h from cdb.h.in (substituting version)
2001-06-29 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.4
* added cdb_make_put() routine to conditionnaly add a record
* split cdb library to more files (finer granularity)
* added cdb_findinit() and cdb_findnext() routines
* renamed cdbtool to cdb
* simplified cdb utility (dropped various format spec, changed
options parsing) and a manpage
* added small note and copyright to every file in package
* added some testsuite (make test)
2001-05-27 Michael Tokarev <mjt+cdb@corpit.ru>
* version 0.3
* Initial Release.

View File

@@ -1,88 +0,0 @@
# tinycdb.spec: tinycdb RPM spec file.
#
# This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
# Public domain.
Summary: A package for maintenance of constant databases
Name: tinycdb
Version: 0.78
Release: 1
Source: ftp://ftp.corpit.ru/pub/tinycdb/tinycdb_%version.tar.gz
License: Public Domain
Group: System Environment/Libraries
Prefix: %{_prefix}
BuildRoot: %{_tmppath}/%{name}-root
Summary: TinyCDB - a Constant DataBase
%description
tinycdb is a small, fast and reliable utility set and subroutine
library for creating and reading constant databases. The database
structure is tuned for fast reading:
+ Successful lookups take normally just two disk accesses.
+ Unsuccessful lookups take only one disk access.
+ Small disk space and memory size requirements; a database
uses 2048 bytes for the header and 24 bytes plus size of
(key,value) per record.
+ Maximum database size is 4GB; individual record size is not
otherwise limited.
+ Portable file format.
+ Fast creation of new databases.
+ No locking, updates are atomical.
This package contains both the utility and the development
files, together with nss_cdb module.
%package devel
Summary: Development files for the tinycdb library.
Group: System Environment/Libraries
Requires: %name = %version-%release
Summary: Development files for tinycdb
%description devel
tinycdb is a small, fast and reliable utility set and subroutine
library for creating and reading constant databases.
This package contains tinycdb development libraries and header files.
%prep
%setup -q
%build
make CFLAGS="$RPM_OPT_FLAGS" \
staticlib sharedlib cdb-shared nss \
sysconfdir=/etc
%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT
%makeinstall DESTDIR=$RPM_BUILD_ROOT \
libdir=%_libdir bindir=%_bindir mandir=%_mandir \
syslibdir=/%_lib sysconfdir=/etc \
includedir=%_includedir \
install-all install-nss install-piclib install-sharedlib \
INSTALLPROG=cdb-shared CP="cp -p"
%files
%defattr(-,root,root)
%_bindir/*
%_mandir/man1/*
%_mandir/man5/*
%_libdir/libcdb.so.*
/%_lib/libnss_cdb*
/etc/cdb-Makefile
%doc ChangeLog NEWS debian/changelog
%files devel
%defattr(-,root,root)
%_libdir/libcdb.a
%_libdir/libcdb_pic.a
%_libdir/libcdb.so
%_mandir/man3/*
%_includedir/*
%clean
rm -rf $RPM_BUILD_ROOT
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
%changelog

View File

@@ -0,0 +1,175 @@
#! /usr/bin/make -rf
# Makefile: make file for tinycdb package
#
# This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
# Public domain.
VERSION = 0.80
prefix=/usr/local
exec_prefix=$(prefix)
bindir=$(exec_prefix)/bin
libdir=$(exec_prefix)/lib
syslibdir=$(libdir)
sysconfdir=/etc
includedir=$(prefix)/include
mandir=$(prefix)/man
NSSCDB_DIR = $(sysconfdir)
DESTDIR=
CC = cc
CFLAGS = -O
CDEFS = -D_FILE_OFFSET_BITS=64
LD = $(CC)
LDFLAGS =
AR = ar
ARFLAGS = rv
RANLIB = ranlib
NSS_CDB = libnss_cdb.so.2
LIBBASE = libcdb
LIB = $(LIBBASE).a
PICLIB = $(LIBBASE)_pic.a
SHAREDLIB = $(LIBBASE).so.1
SOLIB = $(LIBBASE).so
CDB_USELIB = $(LIB)
NSS_USELIB = $(PICLIB)
LIBMAP = $(LIBBASE).map
INSTALLPROG = cdb
# The following assumes GNU CC/LD -
# used for building shared libraries only
CFLAGS_PIC = -fPIC
LDFLAGS_SHARED = -shared
LDFLAGS_SONAME = -Wl,--soname=
LDFLAGS_VSCRIPT = -Wl,--version-script=
CP = cp
LIB_SRCS = cdb_init.c cdb_find.c cdb_findnext.c cdb_seq.c cdb_seek.c \
cdb_pack.c cdb_unpack.c \
cdb_make_add.c cdb_make_put.c cdb_make.c cdb_hash.c
NSS_SRCS = nss_cdb.c nss_cdb-passwd.c nss_cdb-group.c nss_cdb-spwd.c
NSSMAP = nss_cdb.map
DISTFILES = Makefile cdb.h cdb_int.h $(LIB_SRCS) cdb.c \
$(NSS_SRCS) nss_cdb.h nss_cdb-Makefile \
cdb.3 cdb.1 cdb.5 \
tinycdb.spec tests.sh tests.ok \
$(LIBMAP) $(NSSMAP) \
NEWS
all: static
static: staticlib cdb
staticlib: $(LIB)
nss: $(NSS_CDB)
piclib: $(PICLIB)
sharedlib: $(SHAREDLIB)
shared: sharedlib cdb-shared
LIB_OBJS = $(LIB_SRCS:.c=.o)
LIB_OBJS_PIC = $(LIB_SRCS:.c=.lo)
NSS_OBJS = $(NSS_SRCS:.c=.lo)
$(LIB): $(LIB_OBJS)
-rm -f $@
$(AR) $(ARFLAGS) $@ $(LIB_OBJS)
-$(RANLIB) $@
$(PICLIB): $(LIB_OBJS_PIC)
-rm -f $@
$(AR) $(ARFLAGS) $@ $(LIB_OBJS_PIC)
-$(RANLIB) $@
$(SHAREDLIB): $(LIB_OBJS_PIC) $(LIBMAP)
-rm -f $(SOLIB)
ln -s $@ $(SOLIB)
$(LD) $(LDFLAGS) $(LDFLAGS_SHARED) -o $@ \
$(LDFLAGS_SONAME)$(SHAREDLIB) $(LDFLAGS_VSCRIPT)$(LIBMAP) \
$(LIB_OBJS_PIC)
cdb: cdb.o $(CDB_USELIB)
$(LD) $(LDFLAGS) -o $@ cdb.o $(CDB_USELIB)
cdb-shared: cdb.o $(SHAREDLIB)
$(LD) $(LDFLAGS) -o $@ cdb.o $(SHAREDLIB)
$(NSS_CDB): $(NSS_OBJS) $(NSS_USELIB) $(NSSMAP)
$(LD) $(LDFLAGS) $(LDFLAGS_SHARED) -o $@ \
$(LDFLAGS_SONAME)$@ $(LDFLAGS_VSCRIPT)$(NSSMAP) \
$(NSS_OBJS) $(NSS_USELIB)
.SUFFIXES:
.SUFFIXES: .c .o .lo
.c.o:
$(CC) $(CFLAGS) $(CDEFS) -c $<
.c.lo:
$(CC) $(CFLAGS) $(CDEFS) $(CFLAGS_PIC) -c -o $@ -DNSSCDB_DIR=\"$(NSSCDB_DIR)\" $<
cdb.o: cdb.h
$(LIB_OBJS) $(LIB_OBJS_PIC): cdb_int.h cdb.h
$(NSS_OBJS): nss_cdb.h cdb.h
clean:
-rm -f *.o *.lo core *~ tests.out tests-shared.ok
realclean distclean: clean
-rm -f $(LIBBASE)[._][aps]* $(NSS_CDB)* cdb cdb-shared
test tests check: cdb
sh ./tests.sh ./cdb > tests.out 2>&1
diff tests.ok tests.out
@echo All tests passed
test-shared tests-shared check-shared: cdb-shared
sed 's/^cdb: /cdb-shared: /' <tests.ok >tests-shared.ok
LD_LIBRARY_PATH=. sh ./tests.sh ./cdb-shared > tests.out 2>&1
diff tests-shared.ok tests.out
rm -f tests-shared.ok
@echo All tests passed
do_install = \
while [ "$$1" ] ; do \
if [ .$$4 = .- ]; then f=$$1; else f=$$4; fi; \
d=$(DESTDIR)$$3 ; echo installing $$1 to $$d/$$f; \
[ -d $$d ] || mkdir -p $$d || exit 1 ; \
$(CP) $$1 $$d/$$f || exit 1; \
chmod 0$$2 $$d/$$f || exit 1; \
shift 4; \
done
install-all: all $(INSTALLPROG)
set -- \
cdb.h 644 $(includedir) - \
cdb.3 644 $(mandir)/man3 - \
cdb.1 644 $(mandir)/man1 - \
cdb.5 644 $(mandir)/man5 - \
$(INSTALLPROG) 755 $(bindir) cdb \
libcdb.a 644 $(libdir) - \
; \
$(do_install)
install-nss: nss
@set -- $(NSS_CDB) 644 $(syslibdir) - \
nss_cdb-Makefile 644 $(sysconfdir) cdb-Makefile ; \
$(do_install)
install-sharedlib: sharedlib
@set -- $(SHAREDLIB) 644 $(libdir) - ; \
$(do_install) ; \
ln -sf $(SHAREDLIB) $(DESTDIR)$(libdir)/$(LIBBASE).so
install-piclib: piclib
@set -- $(PICLIB) 644 $(libdir) - ; \
$(do_install)
install: install-all
DNAME = tinycdb-$(VERSION)
dist: ../$(DNAME).tar.gz
../$(DNAME).tar.gz: $(DISTFILES)
mkdir $(DNAME)
ln $(DISTFILES) $(DNAME)/
rm -f $@
tar cfz $@ $(DNAME)
rm -fr $(DNAME)
.PHONY: all clean realclean dist spec
.PHONY: test tests check test-shared tests-shared check-shared
.PHONY: static staticlib shared sharedlib nss piclib
.PHONY: install install-all install-sharedlib install-piclib install-nss

View File

@@ -1,5 +1,35 @@
User-visible news. Latest at the top.
tinycdb-0.80 2023-09-17
- bugfix: cdb utility: check return value from close() too,
when creating the cdb file
- portability: cdb utility: ignore SIGXFSZ signal if defined,
to be able to clean up when exceeding file limit
- robustness: let cdb_make_start to lseek() to the zero position
of file. This ensures the file is seekable right at the start
instead of at the very end, too
- robustness: cdb utility: remove temporary file in case create
operation fails
- portability use SEE_SET macro instead of 0 for lseek()
- split out cdb_pack() into its own .c file
tinycdb-0.79 2023-09-17
- bugfix: call fsync() at the end of cdb_make_finish()
to ensure data does not stay in some kernel buffer
- bugfix: clean tests.out in `make distclean' too
- `make dist' makes tarball in the parent dir
- stop shipping debian/* - it is not a native debian package
tinycdb-0.78 2012-05-11
- bugfix release:

View File

@@ -1,11 +1,11 @@
/* cdb.h: public cdb include file
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/
#ifndef TINYCDB_VERSION
#define TINYCDB_VERSION 0.78
#define TINYCDB_VERSION 0.80
#ifdef __cplusplus
extern "C" {
@@ -37,6 +37,7 @@ struct cdb {
#define cdb_fileno(c) ((c)->cdb_fd)
int cdb_init(struct cdb *cdbp, int fd);
int cdb_initf(struct cdb *cdbp, void *p, unsigned len);
void cdb_free(struct cdb *cdbp);
int cdb_read(const struct cdb *cdbp,

View File

@@ -1,6 +1,6 @@
/* cdb_find.c: cdb_find routine
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_findnext.c: sequential cdb_find routines
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_hash.c: cdb hashing routine
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_init.c: cdb_init, cdb_free and cdb_read routines
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/
@@ -16,6 +16,27 @@
#include <sys/stat.h>
#include "cdb_int.h"
int cdb_initf(struct cdb *cdbp, void *p, unsigned len)
{
/* get file size */
if (len < 0)
return -1;
cdbp->cdb_fd = -1;
cdbp->cdb_fsize = len;
cdbp->cdb_mem = p;
cdbp->cdb_vpos = cdbp->cdb_vlen = 0;
cdbp->cdb_kpos = cdbp->cdb_klen = 0;
unsigned dend;
unsigned fsize = len;
dend = cdb_unpack(p);
if (dend < 2048) dend = 2048;
else if (dend >= fsize) dend = fsize;
cdbp->cdb_dend = dend;
return 0;
}
int
cdb_init(struct cdb *cdbp, int fd)
{

View File

@@ -1,6 +1,6 @@
/* cdb_int.h: internal cdb library declarations
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_make.c: basic cdb creation routines
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/
@@ -9,15 +9,6 @@
#include <string.h>
#include "cdb_int.h"
void
cdb_pack(unsigned num, unsigned char buf[4])
{
buf[0] = num & 255; num >>= 8;
buf[1] = num & 255; num >>= 8;
buf[2] = num & 255;
buf[3] = num >> 8;
}
int
cdb_make_start(struct cdb_make *cdbmp, int fd)
{
@@ -25,7 +16,7 @@ cdb_make_start(struct cdb_make *cdbmp, int fd)
cdbmp->cdb_fd = fd;
cdbmp->cdb_dpos = 2048;
cdbmp->cdb_bpos = cdbmp->cdb_buf + 2048;
return 0;
return lseek(fd, 0, SEEK_SET);
}
int internal_function
@@ -152,11 +143,11 @@ cdb_make_finish_internal(struct cdb_make *cdbmp)
cdb_pack(hpos[t], p + (t << 3));
cdb_pack(hcnt[t], p + (t << 3) + 4);
}
if (lseek(cdbmp->cdb_fd, 0, 0) != 0 ||
if (lseek(cdbmp->cdb_fd, 0, SEEK_SET) != 0 ||
_cdb_make_fullwrite(cdbmp->cdb_fd, p, 2048) != 0)
return -1;
return 0;
return fsync(cdbmp->cdb_fd);
}
static void

View File

@@ -1,6 +1,6 @@
/* cdb_make_add.c: basic cdb_make_add routine
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_make_put.c: "advanced" cdb_make_put routine
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -0,0 +1,16 @@
/* cdb_pack.c: pack a 32bit integer (to network byte order)
*
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/
#include "cdb.h"
void
cdb_pack(unsigned num, unsigned char buf[4])
{
buf[0] = num & 255; num >>= 8;
buf[1] = num & 255; num >>= 8;
buf[2] = num & 255;
buf[3] = num >> 8;
}

View File

@@ -1,6 +1,6 @@
/* cdb_seek.c: old interface for reading cdb file
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_seq.c: sequential record retrieval routines
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -1,6 +1,6 @@
/* cdb_unpack.c: unpack 32bit integer
/* cdb_unpack.c: unpack a 32bit integer from network byte order
*
* This file is a part of tinycdb package by Michael Tokarev, mjt@corpit.ru.
* This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
* Public domain.
*/

View File

@@ -0,0 +1,83 @@
Create simple db
0
checksum may fail if no md5sum program
97549c2e76e2d446430a392d77ed1bcb
Dump simple db
+3,4:one->here
+1,1:a->b
+1,3:b->abc
+3,4:one->also
0
Stats for simple db
number of records: 4
key min/avg/max length: 1/2/3
val min/avg/max length: 1/3/4
hash tables/entries/collisions: 3/8/1
hash table min/avg/max length: 2/3/4
hash table distances:
d0: 3 75%
d1: 1 25%
d2: 0 0%
d3: 0 0%
d4: 0 0%
d5: 0 0%
d6: 0 0%
d7: 0 0%
d8: 0 0%
d9: 0 0%
>9: 0 0%
0
Query simple db (two records match)
herealso
0
Query for non-existed key
100
Doing 600 repeated records
0
checksum may fail if no md5sum program
412a0b7578efca528bf8398c8811caf4
cdb stats should show 601 record
number of records: 601
key min/avg/max length: 1/1/1
val min/avg/max length: 3/3/5
hash tables/entries/collisions: 2/1202/599
hash table min/avg/max length: 2/601/1200
hash table distances:
d0: 2 0%
d1: 1 0%
d2: 1 0%
d3: 1 0%
d4: 1 0%
d5: 1 0%
d6: 1 0%
d7: 1 0%
d8: 1 0%
d9: 1 0%
>9: 590 98%
0
Querying key
other
0
Dumping and re-creating db
0
0
Handling large key size
cdb: (stdin): bad format
2
Handling large value size
cdb: (stdin): bad format
2
Handling invalid input format (short file)
cdb: unable to read: short file
2
Creating db with eol in key and value
0
checksum may fail if no md5sum program
1d444fe759c26d36f500d01c41cfda40
Querying key-value with eol
b
0
Handling file size limits
cdb: cdb_make_put: File too large
111

143
source/engine/thirdparty/tinycdb/tests.sh vendored Executable file
View File

@@ -0,0 +1,143 @@
#! /bin/sh
# tests.sh: This script will run tests for cdb.
# Execute with ./tests.sh ./cdb
# (first arg if present gives path to cdb tool to use, default is `cdb').
#
# This file is a part of tinycdb package by Michael Tokarev, mjt+cdb@corpit.ru.
# Public domain.
case "$1" in
"") cdb=cdb ;;
*) cdb="$1" ;;
esac
do_csum() {
echo checksum may fail if no md5sum program
md5sum $1 | sed -e 's|[ ].*||' -e 'y|[ABCDEF]|[abcdef]|'
}
rm -f 1.cdb 1a.cdb
echo Create simple db
echo "+3,4:one->here
+1,1:a->b
+1,3:b->abc
+3,4:one->also
" | $cdb -c 1.cdb
echo $?
do_csum 1.cdb
echo Dump simple db
$cdb -d 1.cdb
echo $?
echo Stats for simple db
$cdb -s 1.cdb
echo $?
echo "Query simple db (two records match)"
$cdb -q 1.cdb one
echo "
$?"
echo Query for non-existed key
$cdb -q 1.cdb none
echo $?
echo Doing 600 repeated records
(
for i in 0 1 2 3 4 5 ; do
for j in 0 1 2 3 4 5 6 7 8 9 ; do
for k in 0 1 2 3 4 5 6 7 8 9 ; do
echo "+1,3:a->$i$j$k"
done
done
done
echo "+1,5:b->other"
echo
) | $cdb -c 1.cdb
echo $?
do_csum 1.cdb
echo cdb stats should show 601 record
$cdb -s 1.cdb
echo $?
echo Querying key
$cdb -q 1.cdb b
echo "
"$?
echo Dumping and re-creating db
$cdb -d 1.cdb | $cdb -c 1a.cdb
echo $?
cmp 1.cdb 1a.cdb
$cdb -d -m 1.cdb | $cdb -c -m 1a.cdb
echo $?
cmp 1.cdb 1a.cdb
echo Handling large key size
echo "+123456789012,1:" | $cdb -c 1.cdb
echo $?
echo Handling large value size
echo "+1,123456789012:" | $cdb -c 1.cdb
echo $?
echo "Handling invalid input format (short file)"
echo "+10,10:" | $cdb -c 1.cdb
echo $?
echo Creating db with eol in key and value
echo "+2,2:a
->b
" | $cdb -c 1.cdb
echo $?
do_csum 1.cdb
echo Querying key-value with eol
$cdb -q 1.cdb "a
"
echo $?
echo Handling file size limits
(
ulimit -f 4
trap '' 25
(
for i in 0 1 2 3 4 5 6 7 8 9 ; do
for j in 0 1 2 3 4 5 6 7 8 9 ; do
for k in 0 1 2 3 4 5 6 7 8 9 ; do
echo "+4,4:k$i$j$k->v$i$j$k"
done
done
done
echo
) | $cdb -c 1.cdb
echo $?
)
if false ; then # does not work for now, bugs in libc
echo Handling oom condition
(
for i0 in 0 1 2 3 4 5 6 7 8 9 ; do
for i1 in 0 1 2 3 4 5 6 7 8 9 ; do
for i2 in 0 1 2 3 4 5 6 7 8 9 ; do
for i3 in 0 1 2 3 4 5 6 7 8 9 ; do
for i4 in 0 1 2 3 4 5 6 7 8 9 ; do
echo "+5,0:$i0$i1$i2$i3$i4->"
done
done
done
done
done
echo
) | (ulimit -v 1900; $cdb -c 1.cdb)
echo $?
fi
rm -rf 1.cdb 1a.cdb 1.cdb.tmp
exit 0

View File

@@ -8,6 +8,8 @@
#include "sound.h"
#include "resources.h"
#include <stdio.h>
#include "datastream.h"
#include "timer.h"
@@ -42,6 +44,7 @@
#include "sokol/sokol_app.h"
#include "sokol/sokol_audio.h"
#include "sokol/sokol_time.h"
#include "sokol/sokol_args.h"
#define STB_DS_IMPLEMENTATION
#include <stb_ds.h>
@@ -152,7 +155,7 @@ int frame_fps() {
return 1.0/sapp_frame_duration();
}
static double low_fps = 1/24.0; /* Chosen because of apple's 24 hz mode */
static double low_fps = 1/24.0;
static double low_fps_c = 0.0;
void c_frame()
@@ -355,8 +358,6 @@ sapp_desc sokol_main(int argc, char **argv) {
phys2d_init();
script_startup();
script_dofile("scripts/engine.js");
int argsize = 0;
for (int i = 0; i < argc; i++) {

View File

@@ -1,747 +0,0 @@
/* Prototypes out an object and extends with values */
function clone(proto, binds) {
var c = Object.create(proto);
complete_assign(c, binds);
return c;
};
/* Prototypes out an object and assigns values */
function copy(proto, binds) {
var c = Object.create(proto);
Object.assign(c, binds);
return c;
};
/* OBJECT DEFININTIONS */
Object.defineProperty(Object.prototype, 'getOwnPropertyDescriptors', {
value: function() {
var obj = {};
for (var key in this) {
obj[key] = Object.getOwnPropertyDescriptor(this, key);
}
return obj;
}
});
Object.defineProperty(Object.prototype, 'obscure', {
value: function(name) {
Object.defineProperty(this, name, { enumerable: false });
}
});
Object.defineProperty(Object.prototype, 'hasOwn', {
value: function(x) { return this.hasOwnProperty(x); }
});
Object.defineProperty(Object.prototype, 'array', {
value: function() {
var a = [];
for (var key in this)
a.push(this[key]);
return a;
},
});
Object.defineProperty(Object.prototype, 'defn', {
value: function(name, val) {
Object.defineProperty(this, name, { value:val, writable:true, configurable:true });
}
});
Object.defineProperty(Object.prototype, 'nulldef', {
value: function(name, val) {
if (!this.hasOwnProperty(name)) this[name] = val;
}
});
/*Object.defineProperty(Object.prototype, 'writable', {
value: function(name) {
return Object.getPropertyDescriptor(this, name).writable;
}
});
*/
Object.defineProperty(Object.prototype, 'prop_obj', {
value: function() { return JSON.parse(JSON.stringify(this)); }
});
/* defc 'define constant'. Defines a value that is not writable. */
Object.defineProperty(Object.prototype, 'defc', {
value: function(name, val) {
Object.defineProperty(this,name, {
value: val,
writable:false,
enumerable:true,
configurable:false,
});
}
});
Object.defineProperty(Object.prototype, 'stick', {
value: function(prop) {
Object.defineProperty(this, prop, {writable:false});
}
});
Object.defineProperty(Object.prototype, 'harden', {
value: function(prop) {
Object.defineProperty(this, prop, {writable:false, configurable:false, enumerable: false});
}
});
Object.defineProperty(Object.prototype, 'deflock', {
value: function(prop) {
Object.defineProperty(this,prop, {configurable:false});
}
});
Object.defineProperty(Object.prototype, 'forEach', {
value: function(fn) {
for (var key in this)
fn(this[key]);
}
});
Object.defineProperty(Object.prototype, 'empty', {
get: function() {
return Object.keys(this).empty;
},
});
Object.defineProperty(Object.prototype, 'nth', {
value: function(x) {
if (this.empty || x >= Object.keys(this).length) return null;
return this[Object.keys(this)[x]];
},
});
Object.defineProperty(Object.prototype, 'findIndex', {
value: function(x) {
var i = 0;
for (var key in this) {
if (this[key] === x) return i;
i++;
}
return -1;
}
});
/* STRING DEFS */
Object.defineProperty(String.prototype, 'next', {
value: function(char, from) {
if (!Array.isArray(char))
char = [char];
if (from > this.length-1)
return -1;
else if (!from)
from = 0;
var find = this.slice(from).search(char[0]);
if (find === -1)
return -1;
else
return from + find;
var i = 0;
var c = this.charAt(from+i);
while (!char.includes(c)) {
i++;
if (from+i >this.length-1) return -1;
c = this.charAt(from+i);
}
return from+i;
}
});
Object.defineProperty(String.prototype, 'prev', {
value: function(char, from, count) {
if (from > this.length-1)
return -1;
else if (!from)
from = this.length-1;
if (!count) count = 0;
var find = this.slice(0,from).lastIndexOf(char);
while (count > 1) {
find = this.slice(0,find).lastIndexOf(char);
count--;
}
if (find === -1)
return 0;
else
return find;
}
});
Object.defineProperty(String.prototype, 'shift', {
value: function(n) {
if (n === 0) return this.slice();
if (n > 0)
return this.slice(n);
if (n < 0)
return this.slice(0, this.length+n);
}
});
Object.defineProperty(String.prototype, 'ext', {
value: function() {
var idx = this.lastIndexOf('.');
if (idx === -1) return undefined;
return this.slice(idx);
}
});
Object.defineProperty(String.prototype, 'set_ext', {
value: function(val) {
return this.name() + val;
}
});
Object.defineProperty(String.prototype, 'name', {
value: function() {
var s = this.lastIndexOf('/');
var e = this.lastIndexOf('.');
if (e === -1) e = this.length;
return this.slice(s+1,e);
}
});
Object.defineProperty(String.prototype, 'base', {
value: function() {
return this.slice(this.lastIndexOf('/')+1);
}
});
Object.defineProperty(String.prototype, 'dir', {
value: function() {
var e = this.lastIndexOf('/');
return this.slice(0, e);
}
});
/* ARRAY DEFS */
Object.defineProperty(Array.prototype, 'copy', {
value: function() {
var c = [];
this.forEach(function(x, i) {
c[i] = deep_copy(x);
});
return c;
}
});
Object.defineProperty(Array.prototype, 'rotate', {
value: function(a) {
return Vector.rotate(this, a);
}
});
Object.defineProperty(Array.prototype, '$add', {
value: function(b) {
for (var i = 0; i < this.length; i++) {
this[i] += b[i];
}
}});
function setelem(n) {
return {
get: function() { return this[n]; },
set: function(x) { this[n] = x; }
}
};
function arrsetelem(str, n)
{
Object.defineProperty(Array.prototype, str, setelem(n));
}
Object.defineProperty(Array.prototype, 'x', setelem(0));
Object.defineProperty(Array.prototype, 'y', setelem(1));
Object.defineProperty(Array.prototype, 'z', setelem(2));
Object.defineProperty(Array.prototype, 'w', setelem(3));
arrsetelem('r', 0);
arrsetelem('g', 1);
arrsetelem('b', 2);
arrsetelem('a', 3);
Object.defineProperty(Array.prototype, 'add', {
value: function(b) {
var c = [];
for (var i = 0; i < this.length; i++) { c[i] = this[i] + b[i]; }
return c;
}});
Object.defineProperty(Array.prototype, 'newfirst', {
value: function(i) {
var c = this.slice();
if (i >= c.length) return c;
do {
c.push(c.shift());
i--;
} while (i > 0);
return c;
}
});
Object.defineProperty(Array.prototype, 'doubleup', {
value: function(n) {
var c = [];
this.forEach(function(x) {
for (var i = 0; i < n; i++)
c.push(x);
});
return c;
}
});
Object.defineProperty(Array.prototype, 'sub', {
value: function(b) {
var c = [];
for (var i = 0; i < this.length; i++) { c[i] = this[i] - b[i]; }
return c;
}});
Object.defineProperty(Array.prototype, 'mult', {
value: function(arr) {
var c = [];
for (var i = 0; i < this.length; i++) { c[i] = this[i] * arr[i]; }
return c;
}});
Object.defineProperty(Array.prototype, 'apply', {
value: function(fn) {
this.forEach(function(x) { x[fn].apply(x); });
}
});
Object.defineProperty(Array.prototype, 'scale', {
value: function(s) {
if (Array.isArray(s)) {
var c = this.slice();
c.forEach(function(x,i) { c[i] = x * s[i]; });
return c;
}
return this.map(function(x) { return x*s; });
}});
Object.defineProperty(Array.prototype, 'equal', {
value: function(b) {
if (this.length !== b.length) return false;
if (b == null) return false;
if (this === b) return true;
return JSON.stringify(this) === JSON.stringify(b);
for (var i = 0; i < this.length; i++) {
if (!this[i] === b[i])
return false;
}
return true;
}});
function add(x,y) { return x+y; };
function mult(x,y) { return x*y; };
Object.defineProperty(Array.prototype, 'mapc', {
value: function(fn, arr) {
return this.map(function(x, i) {
return fn(x, arr[i]);
});
}
});
Object.defineProperty(Array.prototype, 'remove', {
value: function(b) {
var idx = this.indexOf(b);
if (idx === -1) return false;
this.splice(idx, 1);
return true;
}});
Object.defineProperty(Array.prototype, 'set', {
value: function(b) {
if (this.length !== b.length) return;
b.forEach(function(val, i) { this[i] = val; }, this);
}
});
Object.defineProperty(Array.prototype, 'flat', {
value: function() {
return [].concat.apply([],this);
}
});
Object.defineProperty(Array.prototype, 'any', {
value: function(fn) {
var ev = this.every(function(x) {
return !fn(x);
});
return !ev;
}
});
/* Return true if array contains x */
/*Object.defineProperty(Array.prototype, 'includes', {
value: function(x) {
return this.some(e => e === x);
}});
*/
Object.defineProperty(Array.prototype, 'empty', {
get: function() { return this.length === 0; },
});
Object.defineProperty(Array.prototype, 'push_unique', {
value: function(x) {
if (!this.includes(x)) this.push(x);
}});
Object.defineProperty(Array.prototype, 'unique', {
value: function() {
var c = [];
this.forEach(function(x) { c.push_unique(x); });
return c;
}
});
Object.defineProperty(Array.prototype, 'findIndex', {
value: function(fn) {
var idx = -1;
this.every(function(x, i) {
if (fn(x)) {
idx = i;
return false;
}
return true;
});
return idx;
}});
Object.defineProperty(Array.prototype, 'find', {
value: function(fn) {
var ret;
this.every(function(x) {
if (fn(x)) {
ret = x;
return false;
}
return true;
});
return ret;
}});
Object.defineProperty(Array.prototype, 'last', {
get: function() { return this[this.length-1]; },
});
Object.defineProperty(Array.prototype, 'at', {
value: function(x) {
return x < 0 ? this[this.length+x] : this[x];
}});
Object.defineProperty(Array.prototype, 'wrapped', {
value: function(x) {
var c = this.slice(0, this.length);
for (var i = 0; i < x; i++)
c.push(this[i]);
return c;
}});
Object.defineProperty(Array.prototype, 'wrap_idx', {
value: function(x) {
while (x >= this.length) {
x -= this.length;
}
return x;
}
});
Object.defineProperty(Array.prototype, 'mirrored', {
value: function(x) {
var c = this.slice(0);
if (c.length <= 1) return c;
for (var i = c.length-2; i >= 0; i--)
c.push(c[i]);
return c;
}
});
Object.defineProperty(Array.prototype, 'lerp', {
value: function(to, t) {
var c = [];
this.forEach(function(x,i) {
c[i] = (to[i] - x) * t + x;
});
return c;
}
});
Math.lerp = function(s,f,t) { return (f-s)*t + s; };
Object.defineProperty(Object.prototype, 'lerp',{
value: function(to, t) {
var self = this;
var obj = {};
Object.keys(self).forEach(function(key) {
obj[key] = self[key].lerp(to[key],t);
});
return obj;
}});
/* MATH EXTENSIONS */
Object.defineProperty(Number.prototype, 'lerp', {
value: function(to, t) {
var s = this;
return (to - this) * t + this;
}
});
Math.clamp = function (x, l, h) { return x > h ? h : x < l ? l : x; }
Math.random_range = function(min,max) { return Math.random() * (max-min) + min; };
Math.snap = function(val, grid) {
if (!grid || grid === 1) return Math.round(val);
var rem = val%grid;
var d = val - rem;
var i = Math.round(rem/grid)*grid;
return d+i;
}
Math.angledist = function (a1, a2) {
var dist = a2 - a1;
var wrap = dist >= 0 ? dist+360 : dist-360;
wrap %= 360;
if (Math.abs(dist) < Math.abs(wrap))
return dist;
return wrap;
};
Math.angledist.doc = "Find the shortest angle between two angles.";
Math.deg2rad = function(deg) { return deg * 0.0174533; };
Math.rad2deg = function(rad) { return rad / 0.0174533; };
Math.randomint = function(max) { return Math.clamp(Math.floor(Math.random() * max), 0, max-1); };
/* BOUNDINGBOXES */
function cwh2bb(c, wh) {
return {
t: c.y+wh.y/2,
b: c.y-wh.y/2,
l: c.x-wh.x/2,
r: c.x+wh.x/2
};
};
function points2bb(points) {
var b= {t:0,b:0,l:0,r:0};
points.forEach(function(x) {
if (x.y > b.t) b.t = x.y;
if (x.y < b.b) b.b = x.y;
if (x.x > b.r) b.r = x.x;
if (x.x < b.l) b.l = x.x;
});
return b;
};
function bb2points(bb)
{
return [
[bb.l,bb.t],
[bb.r,bb.t],
[bb.r,bb.b],
[bb.l,bb.b]
];
}
function points2cwh(start,end)
{
var c = [];
c[0] = (end[0] - start[0]) / 2;
c[0] += start[0];
c[1] = (end[1] - start[1]) / 2;
c[1] += start[1];
var wh = [];
wh[0] = Math.abs(end[0] - start[0]);
wh[1] = Math.abs(end[1] - start[1]);
return {c: c, wh: wh};
}
function bb2cwh(bb) {
if (!bb) return undefined;
var cwh = {};
var w = bb.r - bb.l;
var h = bb.t - bb.b;
cwh.wh = [w, h];
cwh.c = [bb.l + w/2, bb.b + h/2];
return cwh;
};
function pointinbb(bb, p)
{
if (bb.t < p.y || bb.b > p.y || bb.l > p.x || bb.r < p.x)
return false;
return true;
}
function movebb(bb, pos) {
var newbb = Object.assign({}, bb);
newbb.t += pos.y;
newbb.b += pos.y;
newbb.l += pos.x;
newbb.r += pos.x;
return newbb;
};
function bb_expand(oldbb, x) {
if (!oldbb || !x) return;
var bb = {};
Object.assign(bb, oldbb);
if (bb.t < x.t) bb.t = x.t;
if (bb.r < x.r) bb.r = x.r;
if (bb.b > x.b) bb.b = x.b;
if (bb.l > x.l) bb.l = x.l;
return bb;
};
function bb_from_objects(objs) {
var bb = objs[0].boundingbox;
objs.forEach(function(obj) { bb = bb_expand(bb, obj.boundingbox); });
return bb;
};
/* VECTORS */
var Vector = {
x: 0,
y: 0,
length(v) {
var sum = v.reduce(function(acc, val) { return acc + val**2; }, 0);
return Math.sqrt(sum);
},
norm(v) {
var len = Vector.length(v);
return [v.x/len, v.y/len];
},
make(x, y) {
var vec = Object.create(this, {x:x, y:y});
},
project(a, b) {
return cmd(85, a, b);
},
dot(a, b) {
},
random() {
var vec = [Math.random()-0.5, Math.random()-0.5];
return Vector.norm(vec);
},
angle(v) {
return Math.atan2(v.y, v.x);
},
rotate(v,angle) {
var r = Vector.length(v);
var p = Vector.angle(v) + angle;
return [r*Math.cos(p), r*Math.sin(p)];
},
equal(v1, v2, tol) {
if (!tol)
return v1.equal(v2);
var eql = true;
var c = v1.sub(v2);
c.forEach(function(x) {
if (!eql) return;
if (Math.abs(x) > tol)
eql = false;
});
return eql;
},
};
/* POINT ASSISTANCE */
function points2cm(points)
{
var x = 0;
var y = 0;
var n = points.length;
points.forEach(function(p) {
x = x + p[0];
y = y + p[1];
});
return [x/n,y/n];
};
function sortpointsccw(points)
{
var cm = points2cm(points);
var cmpoints = points.map(function(x) { return x.sub(cm); });
var ccw = cmpoints.sort(function(a,b) {
aatan = Math.atan2(a.y, a.x);
batan = Math.atan2(b.y, b.x);
return aatan - batan;
});
return ccw.map(function(x) { return x.add(cm); });
}

View File

@@ -1,699 +0,0 @@
var component = {
toString() {
if ('gameobject' in this)
return this.name + " on " + this.gameobject;
else
return this.name;
},
name: "component",
component: true,
enabled: true,
enable() { this.enabled = true; },
disable() { this.enabled = false; },
make(go) { },
kill() { Log.info("Kill not created for this component yet"); },
gui() { },
gizmo() { },
prepare_center() {},
finish_center() {},
clone(spec) {
return clone(this, spec);
},
};
var sprite = clone(component, {
name: "sprite",
path: "",
layer: 0,
pos: [0,0],
get visible() { return this.enabled; },
set visible(x) { this.enabled = x; },
asset(str) { this.path = str; this.sync();},
angle: 0,
rect: {s0:0, s1: 1, t0: 0, t1: 1},
dimensions() { return cmd(64,this.path); },
width() { return cmd(64,this.path).x; },
height() { return cmd(64,this.path).y; },
make(go) {
var sprite = Object.create(this);
var id = make_sprite(go,this.path,this.pos);
complete_assign(sprite, {
get enabled() { return cmd(114,id); },
set enabled(x) { cmd(20,id,x); },
set color(x) { cmd(96,id,x); },
get color() {return undefined; },
get pos() { return cmd(111, id); },
set pos(x) { cmd(37,id,x); },
set layer(x) { cmd(60, id, x); },
get layer() { return undefined; },
boundingbox() {
var dim = this.dimensions();
dim = dim.scale(this.gameobject.scale);
var realpos = this.pos.copy();
realpos.x = realpos.x * dim.x + (dim.x/2);
realpos.y = realpos.y * dim.y + (dim.y/2);
return cwh2bb(realpos,dim);
},
sync() {
if (this.path)
cmd(12,id,this.path,this.rect);
},
kill() { cmd(9,id); },
});
sprite.layer = 1;
return sprite;
},
POS_MID: [-0.5, -0.5],
});
sprite.inputs = {};
sprite.inputs.kp9 = function() { this.pos = [0,0]; };
sprite.inputs.kp8 = function() { this.pos = [-0.5, 0]; };
sprite.inputs.kp7 = function() { this.pos = [-1,0]; };
sprite.inputs.kp6 = function() { this.pos = [0,-0.5]; };
sprite.inputs.kp5 = function() { this.pos = [-0.5,-0.5]; };
sprite.inputs.kp4 = function() { this.pos = [-1,-0.5]; };
sprite.inputs.kp3 = function() { this.pos = [0, -1]; };
sprite.inputs.kp2 = function() { this.pos = [-0.5,-1]; };
sprite.inputs.kp1 = function() { this.pos = [-1,-1]; };
/* Container to play sprites and anim2ds */
var char2d = clone(sprite, {
clone(anims) {
var char = clone(this);
char.anims = anims;
return char;
},
name: "char 2d",
frame2rect(frames, frame) {
var rect = {s0:0,s1:1,t0:0,t1:1};
var frameslice = 1/frames;
rect.s0 = frameslice*frame;
rect.s1 = frameslice*(frame+1);
return rect;
},
make(go) {
var char = clone(this, {
get enabled() { return cmd(114,this.id); },
set enabled(x) { cmd(20,this.id,x); },
set color(x) { cmd(96,this.id,x); },
get pos() { return cmd(111, this.id); },
set pos(x) { cmd(37,this.id,x); },
set layer(x) { cmd(60, this.id, x); },
get layer() { return this.gameobject.draw_layer; },
boundingbox() {
var dim = cmd(64,this.path);
dim = dim.scale(this.gameobject.scale);
dim.x *= 1/6;
var realpos = [0,0];
// var realpos = this.pos.slice();
// realpos.x = realpos.x * dim.x + (dim.x/2);
// realpos.y = realpos.y * dim.y + (dim.y/2);
return cwh2bb(realpos,dim);
},
sync() {
if (this.path)
cmd(12,this.id,this.path,this.rect);
},
kill() { cmd(9,this.id); },
});
char.curplaying = char.anims.array()[0];
char.obscure('curplaying');
char.id = make_sprite(go, char.curplaying.path, this.pos);
char.obscure('id');
char.frame = 0;
char.timer = timer.make(char.advance.bind(char), 1/char.curplaying.fps);
char.timer.loop = true;
char.obscure('timer');
// char.obscure('rect');
char.rect = {};
char.setsprite();
return char;
},
frame: 0,
play(name) {
if (!(name in this.anims)) {
Log.info("Can't find an animation named " + name);
return;
}
if (this.curplaying === this.anims[name]) {
this.timer.start();
return;
}
this.curplaying = this.anims[name];
this.timer.time = 1/this.curplaying.fps;
this.timer.start();
this.frame = 0;
this.setsprite();
},
setsprite() {
this.path = this.curplaying.path;
this.rect = this.frame2rect(this.curplaying.frames, this.frame);
cmd(12, this.id, this.path, this.rect);
},
advance() {
this.frame = (this.frame + 1) % this.curplaying.frames;
this.setsprite();
if (this.frame === 0 && !this.curplaying.loop)
this.timer.pause();
},
devance() {
this.frame = (this.frame - 1);
if (this.frame === -1) this.frame = this.curplaying.frames-1;
this.setsprite();
},
setframe(frame) {
this.frame = frame;
this.setsprite();
},
pause() {
this.timer.pause();
},
stop() {
this.setframe(0);
this.timer.stop();
},
kill() {
this.timer.kill();
cmd(9, this.id);
},
});
/* Returns points specifying this geometry, with ccw */
var Geometry = {
box(w, h) {
w /= 2;
h /= 2;
var points = [
[w,h],
[-w,h],
[-w,-h],
[w,-h]
];
return points;
}
};
/* For all colliders, "shape" is a pointer to a phys2d_shape, "id" is a pointer to the shape data */
var collider2d = clone(component, {
name: "collider 2d",
sensor: false,
kill() {}, /* No killing is necessary - it is done through the gameobject's kill */
register_hit(fn, obj) {
register_collide(1, fn, obj, this.gameobject.body, this.shape);
},
make_fns: {
set sensor(x) { cmd(18,this.shape,x); },
get sensor() { return cmd(21,this.shape); },
set enabled(x) { cmd(22,this.shape,x); },
get enabled() { return cmd(23,this.shape); }
},
});
collider2d.inputs = {};
collider2d.inputs['M-s'] = function() { this.sensor = !this.sensor; }
collider2d.inputs['M-s'].doc = "Toggle if this collider is a sensor.";
collider2d.inputs['M-t'] = function() { this.enabled = !this.enabled; }
collider2d.inputs['M-t'].doc = "Toggle if this collider is enabled.";
var polygon2d = clone(collider2d, {
name: "polygon 2d",
points: [],
flipx: false,
flipy: false,
clone(spec) {
var obj = Object.create(this);
obj.points = this.points.copy();
Object.assign(obj, spec);
return obj;
},
make(go) {
var poly = Object.create(this);
Object.assign(poly, make_poly2d(go, this.points));
complete_assign(poly, this.make_fns);
complete_assign(poly, {
boundingbox() {
return points2bb(this.spoints);
},
sync() { cmd_poly2d(0, this.id, this.spoints); }
});
poly.defn('points', this.points.copy());
Object.defineProperty(poly, 'id', {enumerable:false});
Object.defineProperty(poly, 'shape', {enumerable:false});
poly.sync();
return poly;
},
/* EDITOR */
get spoints() {
var spoints = this.points.slice();
if (this.flipx) {
spoints.forEach(function(x) {
var newpoint = x.slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
});
}
if (this.flipy) {
spoints.forEach(function(x) {
var newpoint = x.slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
});
}
return spoints;
},
gizmo() {
if (!this.hasOwn('points')) this.points = this.__proto__.points.copy();
this.spoints.forEach(function(x) {
Debug.point(world2screen(this.gameobject.this2world(x)), 3, Color.green);
}, this);
this.points.forEach(function(x, i) {
Debug.numbered_point(this.gameobject.this2world(x), i);
}, this);
},
pick(pos) {
return Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
},
query() {
return cmd(80, this.shape);
},
});
polygon2d.inputs = {};
polygon2d.inputs.post = function() { this.sync(); };
polygon2d.inputs.f10 = function() {
this.points = sortpointsccw(this.points);
};
polygon2d.inputs.f10.doc = "Sort all points to be CCW order.";
polygon2d.inputs['C-lm'] = function() {
this.points.push(this.gameobject.world2this(Mouse.worldpos));
};
polygon2d.inputs['C-lm'].doc = "Add a point to location of mouse.";
polygon2d.inputs['S-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.points.map(p => this.gameobject.this2world(p)), 25);
if (idx === -1) return;
this.points.splice(idx, 1);
};
polygon2d.inputs['S-lm'].doc = "Remove point under mouse.";
polygon2d.inputs['C-b'] = function() {
this.points = this.spoints;
this.flipx = false;
this.flipy = false;
};
polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
var bucket = clone(collider2d, {
name: "bucket",
clone(spec) {
var obj = Object.create(this);
obj.cpoints = this.cpoints.copy();
dainty_assign(obj, spec);
return obj;
},
cpoints:[],
degrees:2,
dimensions:2,
/* open: 0
clamped: 1
beziers: 2
looped: 3
*/
type: 3,
typeid: {
open: 0,
clamped: 1,
beziers: 2,
looped: 3
},
flipx: false,
flipy: false,
hollow: false,
hollowt: 0,
get spoints() {
var spoints = this.cpoints.slice();
if (this.flipx) {
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.x = -newpoint.x;
spoints.push(newpoint);
}
}
if (this.flipy) {
for (var i = spoints.length-1; i >= 0; i--) {
var newpoint = spoints[i].slice();
newpoint.y = -newpoint.y;
spoints.push(newpoint);
}
}
return spoints;
if (this.hollow) {
var hpoints = [];
var inflatep = inflate_cpv(spoints, spoints.length, this.hollowt);
inflatep[0].slice().reverse().forEach(function(x) { hpoints.push(x); });
inflatep[1].forEach(function(x) { hpoints.push(x); });
return hpoints;
}
return spoints;
},
sample(n) {
var spoints = this.spoints;
this.degrees = Math.clamp(this.degrees, 1, spoints.length-1);
if (spoints.length === 2)
return spoints;
if (spoints.length < 2)
return [];
if (this.degrees < 2) {
if (this.type === 3)
return spoints.wrapped(1);
return spoints;
}
/*
order = degrees+1
knots = spoints.length + order
assert knots%order != 0
*/
if (this.type === bucket.typeid.looped)
return spline_cmd(0, this.degrees, this.dimensions, 0, spoints.wrapped(this.degrees), n);
return spline_cmd(0, this.degrees, this.dimensions, this.type, spoints, n);
},
samples: 10,
points:[],
thickness:0, /* Number of pixels out the edge is */
make(go) {
var edge = Object.create(this);
Object.assign(edge, make_edge2d(go, this.points, this.thickness));
complete_assign(edge, {
set thickness(x) {
cmd_edge2d(1,this.id,x);
},
get thickness() { return cmd(112,this.id); },
boundingbox() {
return points2bb(this.points.map(x => x.scale(this.gameobject.scale)));
},
sync() {
var sensor = this.sensor;
this.points = this.sample(this.samples);
cmd_edge2d(0,this.id,this.points);
this.sensor = sensor;
},
});
complete_assign(edge, this.make_fns);
Object.defineProperty(edge, 'id', {enumerable:false});
Object.defineProperty(edge, 'shape', {enumerable:false});
edge.defn('points', []);
return edge;
},
/* EDITOR */
gizmo() {
if (!this.hasOwn('cpoints')) this.cpoints = this.__proto__.cpoints.copy();
this.spoints.forEach(function(x) {
Debug.point(world2screen(this.gameobject.this2world(x)), 3, Color.green);
}, this);
this.cpoints.forEach(function(x, i) {
Debug.numbered_point(this.gameobject.this2world(x), i);
}, this);
},
finish_center(change) {
this.cpoints = this.cpoints.map(function(x) { return x.sub(change); });
},
pick(pos) { return Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints); },
});
bucket.inputs = {};
bucket.inputs.h = function() { this.hollow = !this.hollow; };
bucket.inputs.h.doc = "Toggle hollow.";
bucket.inputs['C-g'] = function() {
this.hollowt--;
if (this.hollowt < 0) this.hollowt = 0;
};
bucket.inputs['C-g'].doc = "Thin the hollow thickness.";
bucket.inputs['C-f'] = function() { this.hollowt++; };
bucket.inputs['C-f'].doc = "Increase the hollow thickness.";
bucket.inputs['M-v'] = function() { this.thickness--; };
bucket.inputs['M-v'].doc = "Decrease spline thickness.";
bucket.inputs['M-v'].rep = true;
bucket.inputs['C-b'] = function() {
this.cpoints = this.spoints;
this.flipx = false;
this.flipy = false;
};
bucket.inputs['C-b'].doc = "Freeze mirroring,";
bucket.inputs['M-b'] = function() { this.thickness++; };
bucket.inputs['M-b'].doc = "Increase spline thickness.";
bucket.inputs['M-b'].rep = true;
bucket.inputs['C-plus'] = function() { this.degrees++; };
bucket.inputs['C-plus'].doc = "Increase the degrees of this spline.";
bucket.inputs['C-plus'].rep = true;
bucket.inputs.plus = function() { this.samples++; };
bucket.inputs.plus.doc = "Increase the number of samples of this spline.";
bucket.inputs.plus.rep = true;
bucket.inputs.minus = function() {
this.samples--;
if (this.samples < 1) this.samples = 1;
};
bucket.inputs.minus.doc = "Decrease the number of samples on this spline.";
bucket.inputs.minus.rep = true;
bucket.inputs['C-minus'] = function() { this.degrees--; };
bucket.inputs['C-minus'].doc = "Decrease the number of degrees of this spline.";
bucket.inputs['C-minus'].rep = true;
bucket.inputs['C-r'] = function() { this.cpoints = this.cpoints.reverse(); };
bucket.inputs['C-r'].doc = "Reverse the order of the spline's points.";
bucket.inputs['C-l'] = function() { this.type = 3; };
bucket.inputs['C-l'].doc = "Set type of spline to clamped.";
bucket.inputs['C-c'] = function() { this.type = 1; };
bucket.inputs['C-c'].doc = "Set type of spline to closed.";
bucket.inputs['C-o'] = function() { this.type = 0; };
bucket.inputs['C-o'].doc = "Set spline to open.";
bucket.inputs['C-M-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(this.gameobject.world2this,this.gameobject), 25);
if (idx === -1) return;
this.cpoints = this.cpoints.newfirst(idx);
};
bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.";
bucket.inputs['C-lm'] = function() {
var idx = 0;
if (this.cpoints.length >= 2) {
idx = cmd(59, screen2world(Mouse.pos).sub(this.gameobject.pos), this.cpoints, 1000);
if (idx === -1) return;
}
if (idx === this.cpoints.length)
this.cpoints.push(this.gameobject.world2this(screen2world(Mouse.pos)));
else
this.cpoints.splice(idx, 0, this.gameobject.world2this(screen2world(Mouse.pos)));
};
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['S-lm'] = function() {
var idx = grab_from_points(screen2world(Mouse.pos), this.cpoints.map(function(x) {return x.add(this.gameobject.pos); }, this), 25);
if (idx === -1) return;
this.cpoints.splice(idx, 1);
};
bucket.inputs['S-lm'].doc = "Remove point from the spline.";
bucket.inputs.lb = function() {
var np = [];
this.cpoints.forEach(function(c) {
np.push(Vector.rotate(c, Math.deg2rad(-1)));
});
this.cpoints = np;
};
bucket.inputs.lb.doc = "Rotate the points CCW.";
bucket.inputs.lb.rep = true;
bucket.inputs.rb = function() {
var np = [];
this.cpoints.forEach(function(c) {
np.push(Vector.rotate(c, Math.deg2rad(1)));
});
this.cpoints = np;
};
bucket.inputs.rb.doc = "Rotate the points CW.";
bucket.inputs.rb.rep = true;
var circle2d = clone(collider2d, {
name: "circle 2d",
radius: 10,
offset: [0,0],
get scale() { return this.radius; },
set scale(x) { this.radius = x; },
get pos() { return this.offset; },
set pos(x) { this.offset = x; },
make(go) {
var circle = clone(this);
var circ = make_circle2d(go, circle.radius, circle.offset);
Object.assign(circle, circ);
Object.defineProperty(circle, 'id', {enumerable:false});
Object.defineProperty(circle, 'shape', {enumerable:false});
complete_assign(circle, {
set radius(x) { cmd_circle2d(0,this.id,x); },
get radius() { return cmd_circle2d(2,this.id); },
set offset(x) { cmd_circle2d(1,this.id,x); },
get offset() { return cmd_circle2d(3,this.id); },
boundingbox() {
var diameter = this.radius*2*this.gameobject.scale;
return cwh2bb(this.offset.scale(this.gameobject.scale), [this.radius,this.radius]);
},
});
complete_assign(circle, this.make_fns);
return circle;
},
gui() {
Nuke.newline();
Nuke.label("circle2d");
this.radius = Nuke.pprop("Radius", this.radius);
this.offset = Nuke.pprop("offset", this.offset);
},
});
/* ASSETS */
var Texture = {
mipmaps(path, x) {
cmd(94, path, x);
},
sprite(path, x) {
cmd(95, path, x);
},
};
var Resources = {
load(path) {
if (path in this)
return this[path];
var src = {};
this[path] = src;
src.path = path;
if (!IO.exists(`${path}.asset`))
return this[path];
var data = JSON.parse(IO.slurp(`${path}.asset`));
Object.assign(src,data);
return this[path];
},
};

View File

@@ -1,336 +0,0 @@
var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) {
var idx = grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return null;
return points[idx];
},
};
var Shape = {
circle(pos, radius, color) {
cmd(115, pos, radius, color);
},
};
var Debug = {
draw_grid(width, span, color) {
color = color ? color : Color.green;
cmd(47, width, span, color);
},
point(pos, size, color) {
color = color ? color : Color.blue;
Shape.circle(pos, size, color);
// cmd(51, pos, size,color);
},
arrow(start, end, color, capsize) {
color = color ? color : Color.red;
if (!capsize)
capsize = 4;
cmd(81, start, end, color, capsize);
},
poly(points, color) {
cmd_points(0,points,color);
},
boundingbox(bb, color) {
color ??= Color.white;
cmd_points(0, bb2points(bb), color);
},
box(pos, wh, color) {
color ??= Color.white;
cmd(53, pos, wh, color);
},
numbered_point(pos, n) {
Debug.point(world2screen(pos), 3);
gui_text(n, world2screen(pos).add([0,4]), 1);
},
phys_drawing: false,
draw_phys(on) {
this.phys_drawing = on;
cmd(4, this.phys_drawing);
},
draw_obj_phys(obj) {
cmd(82, obj.body);
},
register_call(fn, obj) {
Register.debug.register(fn,obj);
},
line(points, color, type, thickness) {
thickness ??= 1;
if (!type)
type = 0;
if (!color)
color = Color.white;
switch (type) {
case 0:
cmd(83, points, color, thickness);
}
},
draw_bb: false,
draw_gizmos: false,
draw_names: false,
draw() {
if (this.draw_bb)
Game.objects.forEach(function(x) { Debug.boundingbox(x.boundingbox(), [255,255,255,10]); });
if (Game.paused()) gui_text("PAUSED", [0,0],1);
if (this.draw_gizmos)
Game.objects.forEach(function(x) {
if (!x.icon) return;
gui_img(x.icon, world2screen(x.pos));
});
if (this.draw_names)
Game.objects.forEach(function(x) {
GUI.text(x.fullpath(), world2screen(x.pos).add([0,32]), 1, [84,110,255]);
});
if (Debug.Options.gif.rec) {
gui_text("REC", [0,40], 1);
gui_text(Time.seconds_to_timecode(Time.time - Debug.Options.gif.start_time, Debug.Options.gif.fps), [0,30], 1);
}
gui_text(Game.playing() ? "PLAYING"
: Game.stepping() ?
"STEP" :
Game.paused() ?
"PAUSED; EDITING" :
"EDIT", [0, 0], 1);
},
};
Debug.Options = { };
Debug.Options.Color = {
set trigger(x) { cmd(17,x); },
set debug(x) { cmd(16, x); },
};
var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) {
var idx = grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return null;
return points[idx];
},
};
var Profile = {
tick_now() { return cmd(127); },
ns(ticks) { return cmd(128, ticks); },
us(ticks) { return cmd(129, ticks); },
ms(ticks) { return cmd(130, ticks); },
cpu(fn, times, q) {
times ??= 1;
q ??= "ns";
var start = Profile.tick_now();
for (var i = 0; i < times; i++)
fn();
var elapsed = Profile.tick_now() - start;
Log.say(`Profiled in ${Profile[q](elapsed)/times} avg ${q}.`);
},
get fps() { return sys_cmd(8); },
};
var Nuke = {
newline(cols) { nuke(3, cols ? cols : 1); },
newrow(height) { nuke(13,height); },
wins: {},
curwin:"",
prop(str, v) {
var ret = nuke(2, str, v);
if (Number.isFinite(ret)) return ret;
return 0;
},
treeid: 0,
tree(str) { var on = nuke(11, str, this.treeid); this.treeid++; return on; },
tree_pop() { nuke(12);},
prop_num(str, num) { return nuke(2, str, num, -1e10, 1e10, 0.01); },
prop_bool(str, val) { return nuke(4, str, val); },
checkbox(val) { return nuke(4,"",val); },
label(str) { nuke(5, str); },
textbox(str) { return nuke(7, str); },
scrolltext(str) { nuke(14,str); },
defaultrect: { x:10, y:10, w:400, h:600 },
window(name) {
this.curwin = name;
var rect;
if (name in this.wins)
rect = this.wins[name];
else
rect = { x:10, y:10, w:400, h:600 };
nuke(0, name, rect);
},
button(name) { return nuke(6, name); },
radio(name, val, cmp) { return nuke(9, name, val, cmp); },
img(path) { nuke(8, path); },
end() {
this.wins[this.curwin] = nuke(10);
this.treeid = 0;
nuke(1);
},
pprop(str, p, nonew) {
switch(typeof p) {
case 'number':
if (!nonew) Nuke.newline();
return Nuke.prop_num(str, p);
break;
case 'boolean':
if (!nonew) Nuke.newline();
return Nuke.prop_bool(str, p);
case 'object':
if (Array.isArray(p)) {
var arr = [];
Nuke.newline(p.length+1);
Nuke.label(str);
arr[0] = Nuke.pprop("#x", p[0], true);
arr[1] = Nuke.pprop("#y", p[1], true);
return arr;
} else {
if (!nonew)Nuke.newline(2);
Nuke.label(str);
Nuke.label(p);
}
break;
case 'string':
if (!nonew) Nuke.newline();
Nuke.label(str);
return Nuke.textbox(p);
default:
if (!nonew) Nuke.newline(2);
Nuke.label(str);
Nuke.label(p);
}
},
};
Object.defineProperty(Nuke, "curwin", {enumerable:false});
Object.defineProperty(Nuke, "defaultrect", {enumerable:false});
/* These controls are available during editing, and during play of debug builds */
var DebugControls = {};
DebugControls.inputs = {};
DebugControls.inputs.f1 = function () { Debug.draw_phys(!Debug.phys_drawing); };
DebugControls.inputs.f1.doc = "Draw physics debugging aids.";
DebugControls.inputs.f3 = function() { Debug.draw_bb = !Debug.draw_bb; };
DebugControls.inputs.f3.doc = "Toggle drawing bounding boxes.";
DebugControls.inputs.f4 = function() {
Debug.draw_names = !Debug.draw_names;
Debug.draw_gizmos = !Debug.draw_gizmos;
};
DebugControls.inputs.f4.doc = "Toggle drawing gizmos and names of objects.";
Debug.Options.gif = {
w: 640, /* Max width */
h: 480, /* Max height */
stretch: false, /* True if you want to stretch */
cpf: 4,
depth: 16,
file: "out.gif",
rec: false,
secs: 6,
start_time: 0,
fps: 0,
start() {
var w = this.w;
var h = this.h;
if (!this.stretch) {
var win = Window.height / Window.width;
var gif = h/w;
if (gif > win)
h = w * win;
else
w = h / win;
}
cmd(131, w, h, this.cpf, this.depth);
this.rec = true;
this.fps = (1/this.cpf)*100;
this.start_time = Time.time;
timer.oneshot(this.stop.bind(this), this.secs, this, true);
},
stop() {
if (!this.rec) return;
cmd(132, this.file);
this.rec = false;
},
};
DebugControls.inputs.f8 = Debug.Options.gif.start.bind(Debug.Options.gif);
DebugControls.inputs.f9 = Debug.Options.gif.stop.bind(Debug.Options.gif);
DebugControls.inputs.f10 = function() { Time.timescale = 0.1; };
DebugControls.inputs.f10.doc = "Toggle timescale to 1/10.";
DebugControls.inputs.f10.released = function () { Time.timescale = 1.0; };
DebugControls.inputs.f12 = function() { GUI.defaults.debug = !GUI.defaults.debug; Log.warn("GUI toggle debug");};
DebugControls.inputs.f12.doc = "Toggle drawing GUI debugging aids.";
DebugControls.inputs['M-1'] = Render.normal;
Render.normal.doc = "Render mode for enabling all shaders and lighting effects.";
DebugControls.inputs['M-2'] = Render.wireframe;
Render.wireframe.doc = "Render mode to see wireframes of all models.";
DebugControls.inputs['C-M-f'] = function() {};
DebugControls.inputs['C-M-f'].doc = "Enter camera fly mode.";
var Time = {
set timescale(x) { cmd(3, x); },
get timescale() { return cmd(121); },
set updateMS(x) { cmd(6, x); },
set physMS(x) { cmd(7, x); },
set renderMS(x) { cmd(5, x); },
get time() { return cmd(133); },
seconds_to_timecode(secs, fps)
{
var s = Math.trunc(secs);
secs -= s;
var f = Math.trunc(fps * secs);
return `${s}:${f}`;
},
pause() {
Time.timescale = 0;
},
play() {
if (!Time.stash) {
Log.warn("Tried to resume time without calling Time.pause first.");
return;
}
Time.timescale = Time.stash;
},
};
Player.players[0].control(DebugControls);
Register.gui.register(Debug.draw, Debug);

View File

@@ -1,150 +0,0 @@
function deep_copy(from) {
if (typeof from !== 'object')
return from;
if (Array.isArray(from)) {
var c = [];
from.forEach(function(x,i) { c[i] = deep_copy(x); });
return c;
}
var obj = {};
for (var key in from)
obj[key] = deep_copy(from[key]);
return obj;
};
var walk_up_get_prop = function(obj, prop, endobj) {
var props = [];
var cur = obj;
while (cur !== Object.prototype) {
if (cur.hasOwn(prop))
props.push(cur[prop]);
cur = cur.__proto__;
}
return props;
};
function complete_assign(target, source) {
var descriptors = {};
var assigns = {};
if (typeof source === 'undefined') return target;
Object.keys(source).forEach(function (k) {
var desc = Object.getOwnPropertyDescriptor(source, k);
if (desc.value) {
if (typeof desc.value === 'object' && desc.value.hasOwn('value'))
descriptors[k] = desc.value;
else
assigns[k] = desc.value;
} else
descriptors[k] = desc;
});
Object.defineProperties(target, descriptors);
Object.assign(target, assigns);
return target;
};
/* Assigns properties from source to target, only if they exist in target */
function dainty_assign(target, source)
{
for (var key in source) {
if (typeof source[key] === 'function') {
target[key] = source[key];
continue;
}
if (!Object.hasOwn(target, key)) continue;
if (!Object.getOwnPropertyDescriptor(target, key).writable) continue;
if (Array.isArray(target[key]))
target[key] = source[key];
else if (typeof target[key] === 'object')
dainty_assign(target[key], source[key]);
else {
target[key] = source[key];
}
}
};
/* Deeply remove source keys from target, not removing objects */
function unmerge(target, source) {
for (var key in source) {
if (typeof source[key] === 'object' && !Array.isArray(source[key]))
unmerge(target[key], source[key]);
else
delete target[key];
}
};
/* Deeply merge two objects, not clobbering objects on target with objects on source */
function deep_merge(target, source)
{
Log.warn("Doing a deep merge ...");
for (var key in source) {
if (typeof source[key] === 'object' && !Array.isArray(source[key])) {
Log.warn(`Deeper merge on ${key}`);
deep_merge(target[key], source[key]);
}
else {
Log.warn(`Setting key ${key}`);
target[key] = source[key];
}
}
};
function equal(x,y) {
if (typeof x === 'object')
for (var key in x)
return equal(x[key],y[key]);
return x === y;
};
function diffassign(target, from) {
if (from.empty) return;
for (var e in from) {
if (typeof from[e] === 'object') {
if (!target.hasOwnProperty(e))
target[e] = from[e];
else
diffassign(target[e], from[e]);
} else {
if (from[e] === "DELETE") {
delete target[e];
} else {
target[e] = from[e];
}
}
}
};
function diff(from, to) {
var obj = {};
for (var e in to) {
if (typeof to[e] === 'object' && from.hasOwnProperty(e)) {
obj[e] = diff(from[e], to[e]);
if (obj[e].empty)
delete obj[e];
} else {
if (from[e] !== to[e])
obj[e] = to[e];
}
}
for (var e in from) {
if (!to.hasOwnProperty(e))
obj[e] = "DELETE";
}
return obj;
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,631 +0,0 @@
var files = {};
function load(file) {
var modtime = cmd(0, file);
if (modtime === 0) {
Log.stack();
return false;
}
files[file] = modtime;
}
load("scripts/std.js");
function initialize()
{
if (!Game.edit)
run("scripts/play.js");
else
run("scripts/editor.js");
}
function run(file)
{
var modtime = cmd(119, file);
if (modtime === 0) {
Log.stack();
return false;
}
files[file] = modtime;
return cmd(117, file);
}
load("scripts/base.js");
load("scripts/diff.js");
Log.level = 1;
var Color = {
white: [255,255,255,255],
black: [0,0,0,255],
blue: [84,110,255,255],
green: [120,255,10,255],
yellow: [251,255,43,255],
red: [255,36,20,255],
teal: [96, 252, 237,255],
gray: [181, 181,181,255],
cyan: [0,255,255],
};
function bb2wh(bb) {
return [bb.r-bb.l, bb.t-bb.b];
};
load("scripts/gui.js");
var timer = {
make(fn, secs,obj,loop,app) {
app ??= false;
if (secs === 0) {
fn.call(obj);
return;
}
var t = clone(this);
t.callback = fn;
var guardfn = function() {
if (typeof t.callback === 'function')
t.callback();
else
Log.warn("Timer trying to execute without a function.");
};
t.id = make_timer(guardfn, secs, app);
return t;
},
oneshot(fn, secs,obj, app) {
app ??= false;
var t = this.make(fn, secs, obj, 0, app);
t.start();
},
get remain() { return cmd(32, this.id); },
get on() { return cmd(33, this.id); },
get loop() { return cmd(34, this.id); },
set loop(x) { cmd(35, this.id, x); },
start() { cmd(26, this.id); },
stop() { cmd(25, this.id); },
pause() { cmd(24, this.id); },
kill() { cmd(27, this.id); },
set time(x) { cmd(28, this.id, x); },
get time() { return cmd(29, this.id); },
get pct() { return this.remain / this.time; },
};
var animation = {
time: 0,
loop: false,
playtime: 0,
playing: false,
keyframes: [],
create() {
var anim = Object.create(animation);
Register.update.register(anim.update, anim);
return anim;
},
start() {
this.playing = true;
this.time = this.keyframes.last[1];
this.playtime = 0;
},
interval(a, b, t) {
return (t - a) / (b - a);
},
near_val(t) {
for (var i = 0; i < this.keyframes.length-1; i++) {
if (t > this.keyframes[i+1][1]) continue;
return this.interval(this.keyframes[i][1], this.keyframes[i+1][1], t) >= 0.5 ? this.keyframes[i+1][0] : this.keyframes[i][0];
}
return this.keyframes.last[0];
},
lerp_val(t) {
for (var i = 0; i < this.keyframes.length-1; i++) {
if (t > this.keyframes[i+1][1]) continue;
var intv = this.interval(this.keyframes[i][1], this.keyframes[i+1][1], t);
return ((1 - intv) * this.keyframes[i][0]) + (intv * this.keyframes[i+1][0]);
}
return this.keyframes.last[0];
},
cubic_val(t) {
},
mirror() {
if (this.keyframes.length <= 1) return;
for (var i = this.keyframes.length-1; i >= 1; i--) {
this.keyframes.push(this.keyframes[i-1]);
this.keyframes.last[1] = this.keyframes[i][1] + (this.keyframes[i][1] - this.keyframes[i-1][1]);
}
},
update(dt) {
if (!this.playing) return;
this.playtime += dt;
if (this.playtime >= this.time) {
if (this.loop)
this.playtime = 0;
else {
this.playing = false;
return;
}
}
this.fn(this.lerp_val(this.playtime));
},
};
var Render = {
normal() {
cmd(67);
},
wireframe() {
cmd(68);
},
};
load("scripts/physics.js");
load("scripts/input.js");
load("scripts/sound.js");
function screen2world(screenpos) { return Game.camera.view2world(screenpos); }
function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
var Register = {
inloop: false,
loopcbs: [],
finloop() {
this.loopcbs.forEach(x => x());
this.loopcbs = [];
},
wraploop(loop) {
this.inloop = true;
loop();
this.inloop = false;
this.finloop();
},
kbm_input(mode, btn, state, ...args) {
if (state === 'released') {
btn = btn.split('-').last;
}
switch(mode) {
case "emacs":
Player.players[0].raw_input(btn, state, ...args);
break;
case "mouse":
Player.players[0].mouse_input(btn, state, ...args);
break;
case "char":
Player.players[0].char_input(btn);
break;
};
},
gamepad_playermap: [],
gamepad_input(pad, btn, state, ...args) {
var player = this.gamepad_playermap[pad];
if (!player) return;
var statestr = Input.state2str(state);
var rawfn = `gamepad_${btn}_${statestr}`;
player.input(rawfn, ...args);
Action.actions.forEach(x => {
if (x.inputs.includes(btn))
player.input(`action_${x.name}_${statestr}`, ...args);
});
},
unregister_obj(obj) {
Register.registries.forEach(function(x) {
x.unregister_obj(obj);
});
Player.uncontrol(obj);
},
endofloop(fn) {
if (!this.inloop)
fn();
else {
this.loopcbs.push(fn);
}
},
clear() {
Register.registries.forEach(function(n) {
n.entries = [];
});
},
registries: [],
add_cb(idx, name) {
var entries = [];
var n = {};
n.register = function(fn, obj) {
if (!obj) {
Log.error("Refusing to register a function without a destroying object.");
return;
}
entries.push({
fn: fn,
obj: obj
});
}
n.unregister = function(fn) {
entries = entries.filter(function(e) { return e.fn !== f; });
}
n.unregister_obj = function(obj) {
entries = entries.filter(function(e) { return e.obj !== obj; });
}
n.broadcast = function(...args) {
entries.forEach(x => x.fn.call(x.obj, ...args));
}
n.clear = function() {
entries = [];
}
register(idx, n.broadcast, n);
Register[name] = n;
Register.registries.push(n);
return n;
},
};
Register.add_cb(0, "update").doc = "Called once per frame.";
Register.add_cb(1, "physupdate");
Register.add_cb(2, "gui");
Register.add_cb(3, "nk_gui");
Register.add_cb(6, "debug");
register(7, Register.kbm_input, Register);
Register.add_cb(8, "gamepad_input");
Register.add_cb(10, "draw");
register(9, Log.stack, this);
Register.gamepad_playermap[0] = Player.players[0];
Player.players[0].control(GUI);
var Signal = {
signals: [],
obj_begin(fn, obj, go) {
this.signals.push([fn, obj]);
register_collide(0, fn, obj, go.body);
},
obj_separate(fn, obj, go) {
this.signals.push([fn,obj]);
register_collide(3,fn,obj,go.body);
},
clera_obj(obj) {
this.signals.filter(function(x) { return x[1] !== obj; });
},
c:{},
register(name, fn) {
if (!this.c[name])
this.c[name] = [];
this.c[name].push(fn);
},
call(name, ...args) {
if (this.c[name])
this.c[name].forEach(function(fn) { fn.call(this, ...args); });
},
};
var Window = {
set width(w) { cmd(125, w); },
set height(h) { cmd(126, h); },
get width() { return cmd(48); },
get height() { return cmd(49); },
get dimensions() { return [this.width, this.height]; },
set name(str) { cmd(134, str); },
};
Window.icon = function(path) { cmd(90, path); };
Window.icon.doc = "Set the icon of the window using the PNG image at path.";
function reloadfiles() {
Object.keys(files).forEach(function (x) { load(x); });
}
load("scripts/debug.js");
/*
function Color(from) {
var color = Object.create(Array);
Object.defineProperty(color, 'r', setelem(0));
Object.defineProperty(color, 'g', setelem(1));
Object.defineProperty(color, 'b', setelem(2));
Object.defineProperty(color, 'a', setelem(3));
color.a = color.g = color.b = color.a = 1;
Object.assign(color, from);
return color;
};
*/
load("scripts/components.js");
function replacer_empty_nil(key, val) {
if (typeof val === 'object' && JSON.stringify(val) === '{}')
return undefined;
// if (typeof val === 'number')
// return parseFloat(val.toFixed(4));
return val;
};
function clean_object(obj) {
Object.keys(obj).forEach(function(x) {
if (!(x in obj.__proto__)) return;
switch(typeof obj[x]) {
case 'object':
if (Array.isArray(obj[x])) {
if (obj[x].equal(obj.__proto__[x])) {
delete obj[x];
}
} else
clean_object(obj[x]);
break;
case 'function':
return;
default:
if (obj[x] === obj.__proto__[x])
delete obj[x];
break;
}
});
};
function find_com(objects)
{
if (!objects || objects.length === 0)
return [0,0];
var com = [0,0];
com[0] = objects.reduce(function(acc, val) {
return acc + val.pos[0];
}, 0);
com[0] /= objects.length;
com[1] = objects.reduce(function(acc, val) {
return acc + val.pos[1];
}, 0);
com[1] /= objects.length;
return com;
};
var Game = {
objects: [],
resolution: [1200,720],
name: "Untitled",
edit: true,
register_obj(obj) {
this.objects[obj.body] = obj;
},
/* Returns an object given an id */
object(id) {
return this.objects[id];
},
/* Returns a list of objects by name */
find(name) {
},
/* Return a list of objects derived from a specific prototype */
find_proto(proto) {
},
/* List of all objects spawned that have a specific tag */
find_tag(tag) {
},
groupify(objects, spec) {
var newgroup = {
locked: true,
breakable: true,
objs: objects,
// get pos() { return find_com(objects); },
// set pos(x) { this.objs.forEach(function(obj) { obj.pos = x; }) },
};
Object.assign(newgroup, spec);
objects.forEach(function(x) {
x.defn('group', newgroup);
});
var bb = bb_from_objects(newgroup.objs);
newgroup.startbb = bb2cwh(bb);
newgroup.bboffset = newgroup.startbb.c.sub(newgroup.objs[0].pos);
newgroup.boundingbox = function() {
newgroup.startbb.c = newgroup.objs[0].pos.add(newgroup.bboffset);
return cwh2bb(newgroup.startbb.c, newgroup.startbb.wh);
};
if (newgroup.file)
newgroup.color = [120,255,10];
return newgroup;
},
quit()
{
sys_cmd(0);
},
pause()
{
sys_cmd(3);
},
stop()
{
Game.pause();
/* And return to editor .. */
Log.warn("Stopping not implemented. Paused, and go to editor.");
},
step()
{
sys_cmd(4);
},
render() { sys_cmd(10); },
playing() { return sys_cmd(5); },
paused() { return sys_cmd(6); },
stepping() {
return cmd(79); },
play()
{
sys_cmd(1);
},
get dt() {
return cmd(63);
},
wait_fns: [],
wait_exec(fn) {
if (!phys_stepping())
fn();
else
this.wait_fns.push(fn);
},
exec() {
this.wait_fns.forEach(function(x) { x(); });
this.wait_fns = [];
},
};
Register.update.register(Game.exec, Game);
//load("scripts/level.js");
load("scripts/entity.js");
var World = Object.create(gameobject);
var Primum = World;
gameobject.make_parentable(Primum);
Primum.tag = "PRIMUM";
Primum.selectable = false;
Primum.ur = { tag: "Primum" };
/* Reparent this object to a new one */
World.reparent = function(parent) { Log.warn("Cannot reparent the Primum."); }
World.unparent = function() { Log.warn("The Primum has no parent, always."); }
World.fullpath = function() { return World.name; };
/* Load configs */
function load_configs(file) {
Log.info(`Loading config file ${file}.`);
var configs = JSON.parse(IO.slurp(file));
for (var key in configs) {
if (typeof globalThis[key] !== "object") continue;
Object.assign(globalThis[key], configs[key]);
}
Collision.sync();
Game.objects.forEach(function(x) { x.sync(); });
if (!local_conf.mouse) {
Log.info("disabling mouse features");
Mouse.disabled = function() {};
Mouse.hidden = function() {};
};
};
var local_conf = {
mouse: true,
};
if (IO.exists("game.config"))
load_configs("game.config");
/* Save configs */
function save_configs() {
Log.info("saving configs");
var configs = {};
configs.editor_config = editor_config;
configs.Nuke = Nuke;
configs.local_conf = local_conf;
IO.slurpwrite(JSON.stringify(configs, null, 1), "editor.config");
save_game_configs();
};
function save_game_configs() {
var configs = {};
configs.physics = physics;
configs.Collision = Collision;
Log.info(configs);
IO.slurpwrite(JSON.stringify(configs,null,1), "game.config");
Collision.sync();
Game.objects.forEach(function(x) { x.sync(); });
};
load("scripts/physics.js");
Game.view_camera = function(cam)
{
Game.camera = cam;
cmd(61, Game.camera.body);
}
Game.view_camera(Primum.spawn(ur.camera2d));
Window.name = "Primum Machinam (V0.1)";
Window.width = 1280;
Window.height = 720;

View File

@@ -1,576 +0,0 @@
function grab_from_points(pos, points, slop) {
var shortest = slop;
var idx = -1;
points.forEach(function(x,i) {
if (Vector.length(pos.sub(x)) < shortest) {
shortest = Vector.length(pos.sub(x));
idx = i;
}
});
return idx;
};
var gameobject = {
scale: 1.0,
save: true,
selectable: true,
spawn(ur) {
if (typeof ur === 'string')
ur = prototypes.get_ur(ur);
return ur.type.make(this);
},
clone(name, ext) {
var obj = Object.create(this);
complete_assign(obj, ext);
return obj;
},
layer: 0, /* Collision layer; should probably have been called "mask" */
layer_nuke() {
Nuke.label("Collision layer");
Nuke.newline(Collision.num);
for (var i = 0; i < Collision.num; i++)
this.layer = Nuke.radio(i, this.layer, i);
},
draw_layer: 1,
draw_layer_nuke() {
Nuke.label("Draw layer");
Nuke.newline(5);
for (var i = 0; i < 5; i++)
this.draw_layer = Nuke.radio(i, this.draw_layer, i);
},
ed_locked: false,
_visible: true,
get visible(){ return this._visible; },
set visible(x) {
this._visible = x;
for (var key in this.components) {
if ('visible' in this.components[key]) {
this.components[key].visible = x;
}
}
},
mass: 1,
phys: 2,
phys_nuke() {
Nuke.newline(1);
Nuke.label("phys");
Nuke.newline(3);
this.phys = Nuke.radio("dynamic", this.phys, 0);
this.phys = Nuke.radio("kinematic", this.phys, 1);
this.phys = Nuke.radio("static", this.phys, 2);
},
friction: 0,
elasticity: 0,
flipx: false,
flipy: false,
set_center(pos) {
var change = pos.sub(this.pos);
this.pos = pos;
for (var key in this.components) {
this.components[key].finish_center(change);
}
},
varname: "",
pos: [0,0],
set relpos(x) {
if (!this.level) {
this.pos = x;
return;
}
this.pos = Vector.rotate(x, Math.deg2rad(this.level.angle)).add(this.level.pos);
},
get relpos() {
if (!this.level) return this.pos;
var offset = this.pos.sub(this.level.pos);
return Vector.rotate(offset, -Math.deg2rad(this.level.angle));
},
angle: 0,
get relangle() {
if (!this.level) return this.angle;
return this.angle - this.level.angle;
},
velocity: [0,0],
angularvelocity: 0,
gizmo: "", /* Path to an image to draw for this gameobject */
/* Bounding box of the object in world dimensions */
boundingbox() {
var boxes = [];
boxes.push({t:0, r:0,b:0,l:0});
for (var key in this.components) {
if ('boundingbox' in this.components[key])
boxes.push(this.components[key].boundingbox());
}
if (boxes.empty) return cwh2bb([0,0], [0,0]);
var bb = boxes[0];
boxes.forEach(function(x) {
bb = bb_expand(bb, x);
});
var cwh = bb2cwh(bb);
if (!bb) return;
if (this.flipx) cwh.c.x *= -1;
if (this.flipy) cwh.c.y *= -1;
cwh.c = cwh.c.add(this.pos);
bb = cwh2bb(cwh.c, cwh.wh);
return bb ? bb : cwh2bb([0,0], [0,0]);
},
width() {
var bb = this.boundingbox();
return bb.r - bb.l;
},
height() {
var bb = this.boundingbox();
return bb.t-bb.b;
},
stop() {},
/* Make a unique object the same as its prototype */
revert() {
// unmerge(this, this.prop_obj());
},
gui() {
var go_guis = walk_up_get_prop(this, 'go_gui');
Nuke.newline();
go_guis.forEach(function(x) { x.call(this); }, this);
for (var key in this) {
if (typeof this[key] === 'object' && 'gui' in this[key]) this[key].gui();
}
},
check_registers(obj) {
Register.unregister_obj(this);
if (typeof obj.update === 'function')
Register.update.register(obj.update, obj);
if (typeof obj.physupdate === 'function')
Register.physupdate.register(obj.physupdate, obj);
if (typeof obj.collide === 'function')
obj.register_hit(obj.collide, obj);
if (typeof obj.separate === 'function')
obj.register_separate(obj.separate, obj);
if (typeof obj.draw === 'function')
Register.draw.register(obj.draw,obj);
if (typeof obj.debug === 'function')
Register.debug.register(obj.debug, obj);
obj.components.forEach(function(x) {
if (typeof x.collide === 'function')
register_collide(1, x.collide, x, obj.body, x.shape);
});
},
instances: [],
make(level) {
level ??= Primum;
var obj = Object.create(this);
this.instances.push(obj);
obj.toString = function() {
if (obj.ur)
return obj.ur.tag;
return "NO UR"};
obj.defn('body', make_gameobject(this.scale,
this.phys,
this.mass,
this.friction,
this.elasticity) );
obj.defn('components', {});
Game.register_obj(obj);
cmd(113, obj.body, obj);
/* Now that it's concrete in the engine, these functions update to return engine data */
complete_assign(obj, {
set scale(x) { cmd(36, this.body, x); },
get scale() { return cmd(103, this.body); },
get flipx() { return cmd(104,this.body); },
set flipx(x) { cmd(55, this.body, x); },
get flipy() { return cmd(105,this.body); },
set flipy(x) { cmd(56, this.body, x); },
get angle() { return Math.rad2deg(q_body(2,this.body))%360; },
set angle(x) { set_body(0,this.body, Math.deg2rad(x)); },
set pos(x) {
var diff = x.sub(this.pos);
this.objects.forEach(function(x) { x.pos = x.pos.add(diff); });
set_body(2,this.body,x); },
get pos() { return q_body(1,this.body); },
get elasticity() { return cmd(107,this.body); },
set elasticity(x) { cmd(106,this.body,x); },
get friction() { return cmd(109,this.body); },
set friction(x) { cmd(108,this.body,x); },
set mass(x) { set_body(7,this.body,x); },
get mass() { return q_body(5, this.body); },
set phys(x) { set_body(1, this.body, x); },
get phys() { return q_body(0,this.body); },
get velocity() { return q_body(3, this.body); },
set velocity(x) { set_body(9, this.body, x); },
get angularvelocity() { return Math.rad2deg(q_body(4, this.body)); },
set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); },
pulse(vec) { set_body(4, this.body, vec);},
push(vec) { set_body(12,this.body,vec);},
world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); },
set layer(x) { cmd(75,this.body,x); },
get layer() { return 0; },
alive() { return this.body >= 0; },
in_air() { return q_body(7, this.body);},
on_ground() { return !this.in_air(); },
disable() { this.components.forEach(function(x) { x.disable(); });},
enable() { this.components.forEach(function(x) { x.enable(); });},
sync() { },
dirty() { return false; },
dup(diff) {
var dup = Primum.spawn(this.ur);
Object.assign(dup, this);
return dup;
},
kill() {
if (this.body === -1) {
Log.warn(`Object is already dead!`);
return;
}
Register.endofloop(() => {
cmd(2, this.body);
delete Game.objects[this.body];
if (this.level)
this.level.unregister(this);
Player.uncontrol(this);
this.instances.remove(this);
Register.unregister_obj(this);
// Signal.clear_obj(this);
this.body = -1;
for (var key in this.components) {
Register.unregister_obj(this.components[key]);
this.components[key].kill();
}
this.objects.forEach(x => x.kill());
this.stop();
});
},
up() { return [0,1].rotate(Math.deg2rad(this.angle));},
down() { return [0,-1].rotate(Math.deg2rad(this.angle));},
right() { return [1,0].rotate(Math.deg2rad(this.angle));},
left() { return [-1,0].rotate(Math.deg2rad(this.angle));},
toJSON() {
var ret = {};
for (var key in this) {
var prop = Object.getOwnPropertyDescriptor(this, key);
if (!prop) continue;
if (prop.get) {
if (prop.get() !== Object.getPrototypeOf(this)[key])
ret[key] = prop.get();
}
else
ret[key] = this[key];
}
return ret;
},
});
for (var prop in obj) {
if (typeof obj[prop] === 'object' && 'make' in obj[prop]) {
obj[prop] = obj[prop].make(obj.body);
obj[prop].defn('gameobject', obj);
obj.components[prop] = obj[prop];
}
};
obj.check_registers(obj);
gameobject.make_parentable(obj);
/* Spawn subobjects defined */
if (obj.$) {
for (var e in obj.$)
obj.$[e] = obj.spawn(prototypes.get_ur(obj.$[e].ur));
}
if (typeof obj.start === 'function') obj.start();
level.add_child(obj);
return obj;
},
register_hit(fn, obj) {
if (!obj)
obj = this;
Signal.obj_begin(fn, obj, this);
},
register_separate(fn, obj) {
if (!obj)
obj = this;
Signal.obj_separate(fn,obj,this);
},
}
gameobject.make_parentable = function(obj) {
var objects = [];
obj.remove_child = function(child) {
objects.remove(child);
}
obj.add_child = function(child) {
child.unparent();
objects.push(child);
child.level = obj;
}
/* Reparent this object to a new one */
obj.reparent = function(parent) {
if (parent === obj.level)
return;
parent.add_child(obj);
obj.level = parent;
}
obj.unparent = function() {
if (!obj.level) return;
obj.level.remove_child(obj);
obj.parent = undefined;
}
obj.objects = objects;
}
/* Default objects */
var prototypes = {};
prototypes.ur = {};
prototypes.load_all = function()
{
if (IO.exists("proto.json"))
prototypes = JSON.parse(IO.slurp("proto.json"));
for (var key in prototypes) {
if (key in gameobjects)
dainty_assign(gameobjects[key], prototypes[key]);
else {
/* Create this gameobject fresh */
Log.info("Making new prototype: " + key + " from " + prototypes[key].from);
var newproto = gameobjects[prototypes[key].from].clone(key);
gameobjects[key] = newproto;
for (var pkey in newproto)
if (typeof newproto[pkey] === 'object' && newproto[pkey] && 'clone' in newproto[pkey])
newproto[pkey] = newproto[pkey].clone();
dainty_assign(gameobjects[key], prototypes[key]);
}
}
}
prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };
prototypes.from_file = function(file)
{
if (!IO.exists(file)) {
Log.error(`File ${file} does not exist.`);
return;
}
var newobj = gameobject.clone(file, {});
var script = IO.slurp(file);
newobj.$ = {};
var json = {};
if (IO.exists(file.name() + ".json")) {
json = JSON.parse(IO.slurp(file.name() + ".json"));
Object.assign(newobj.$, json.$);
delete json.$;
}
compile_env(`var self = this; var $ = self.$; ${script}`, newobj, file);
dainty_assign(newobj, json);
file = file.replaceAll('/', '.');
var path = file.name().split('.');
var nested_access = function(base, names) {
for (var i = 0; i < names.length; i++)
base = base[names[i]] = base[names[i]] || {};
return base;
};
var a = nested_access(ur, path);
a.tag = file.name();
prototypes.list.push(a.tag);
a.type = newobj;
a.instances = [];
newobj.ur = a;
return a;
}
prototypes.from_file.doc = "Create a new ur-type from a given script file.";
prototypes.list = [];
prototypes.from_obj = function(name, obj)
{
var newobj = gameobject.clone(name, obj);
prototypes.ur[name] = {
tag: name,
type: newobj
};
newobj.ur = prototypes.ur[name];
return prototypes.ur[name];
}
prototypes.load_config = function(name)
{
if (!prototypes.ur[name])
prototypes.ur[name] = gameobject.clone(name);
Log.warn(`Made new ur of name ${name}`);
return prototypes.ur[name];
}
prototypes.list_ur = function()
{
var list = [];
function list_obj(obj, prefix)
{
prefix ??= "";
var list = [];
for (var e in obj) {
list.push(prefix + e);
Log.warn("Descending into " + e);
list.concat(list_obj(obj[e], e + "."));
}
return list;
}
return list_obj(ur);
}
prototypes.get_ur = function(name)
{
if (!prototypes.ur[name]) {
if (IO.exists(name + ".js"))
prototypes.from_file(name + ".js");
prototypes.load_config(name);
return prototypes.ur[name];
} else
return prototypes.ur[name];
}
prototypes.from_obj("polygon2d", {
polygon2d: polygon2d.clone(),
});
prototypes.from_obj("edge2d", {
edge2d: bucket.clone(),
});
prototypes.from_obj("sprite", {
sprite: sprite.clone(),
});
prototypes.generate_ur = function(path)
{
var ob = IO.glob("**.js");
ob = ob.filter(function(str) { return !str.startsWith("scripts"); });
ob.forEach(function(name) {
if (name === "game.js") return;
if (name === "play.js") return;
prototypes.from_file(name);
});
}
var ur = prototypes.ur;
prototypes.from_obj("camera2d", {
phys: Physics.kinematic,
speed: 300,
get zoom() { return cmd(135); },
set zoom(x) {
x = Math.clamp(x,0.1,10);
cmd(62, x);
},
speedmult: 1.0,
selectable: false,
view2world(pos) {
pos.y *= -1;
return pos.add([-Window.width,Window.height].scale(0.5)).scale(this.zoom).add(this.pos);
},
world2view(pos) {
return pos.sub(this.pos).scale(1/this.zoom).add(Window.dimensions.scale(0.5));
},
});
prototypes.from_obj("arena", {});

View File

@@ -1,392 +0,0 @@
var GUI = {
text(str, pos, size, color, wrap) {
size = size ? size : 1;
color = color ? color : [255,255,255,255];
wrap = wrap ? wrap : -1;
var bb = cmd(118, str, size, wrap);
var opos = [bb.r, bb.t];
var h = ui_text(str, pos, size, color, wrap);
return bb;
},
text_cursor(str, pos, size, cursor) {
cursor_text(str,pos,size,[255,255,255],cursor);
},
image(path,pos) {
var wh = cmd(64,path);
gui_img(path,pos, [1.0,1.0], 0.0, 0.0, [0.0,0.0], 0.0, Color.black);
return cwh2bb([0,0], wh);
},
image_fn(defn) {
var def = Object.create(this.defaults);
Object.assign(def,defn);
if (!def.path) {
Log.warn("GUI image needs a path.");
def.draw = function(){};
return def;
}
var tex_wh = cmd(64,def.path);
var wh = tex_wh.slice();
if (def.width !== 0)
wh.x = def.width;
if (def.height !== 0)
wh.y = def.height;
wh = wh.scale(def.scale);
var sendscale = [];
sendscale.x = wh.x / tex_wh.x;
sendscale.y = wh.y / tex_wh.y;
def.draw = function(pos) {
def.calc_bb(pos);
gui_img(def.path, pos.sub(def.anchor.scale(wh)), sendscale, def.angle, def.image_repeat, def.image_repeat_offset, def.color);
};
def.calc_bb = function(cursor) {
def.bb = cwh2bb(wh.scale([0.5,0.5]), wh);
def.bb = movebb(def.bb, cursor.sub(wh.scale(def.anchor)));
};
return def;
},
defaults: {
padding:[2,2], /* Each element inset with this padding on all sides */
font: "fonts/LessPerfectDOSVGA.ttf",
font_size: 1,
text_align: "left",
scale: 1,
angle: 0,
anchor: [0,0],
text_shadow: {
pos: [0,0],
color: [255,255,255,255]
},
text_outline: 1, /* outline in pixels */
color: [255,255,255,255],
margin: [5,5], /* Distance between elements for things like columns */
width: 0,
height: 0,
image_repeat: false,
image_repeat_offset: [0,0],
debug: false, /* set to true to draw debug boxes */
},
text_fn(str, defn)
{
var def = Object.create(this.defaults);
Object.assign(def,defn);
def.draw = function(cursor) {
def.calc_bb(cursor);
if (def.debug)
Debug.boundingbox(def.bb, def.debug_colors.bounds);
var old = def;
def = Object.create(def);
/* if (pointinbb(def.bb, Mouse.screenpos)) {
Object.assign(def, def.hovered);
def.calc_bb(cursor);
GUI.selected = def;
def.selected = true;
}
*/
if (def.selected) {
Object.assign(def, def.hovered);
def.calc_bb(cursor);
}
var pos = cursor.sub(bb2wh(def.bb).scale(def.anchor));
ui_text(str, pos, def.font_size, def.color, def.width);
def = old;
};
def.calc_bb = function(cursor) {
var bb = cmd(118, str, def.font_size, def.width);
var wh = bb2wh(bb);
var pos = cursor.sub(wh.scale(def.anchor));
def.bb = movebb(bb,pos);
};
return def;
},
column(defn) {
var def = Object.create(this.defaults);
Object.assign(def,defn);
if (!def.items) {
Log.warn("Columns needs items.");
def.draw = function(){};
return def;
};
def.items.forEach(function(item,idx) {
Object.setPrototypeOf(def.items[idx], def);
if (def.items[idx-1])
def.up = def.items[idx-1];
if (def.items[idx+1])
def.down = def.items[idx+1];
});
def.draw = function(pos) {
def.items.forEach(function(item) {
item.draw.call(this,pos);
var wh = bb2wh(item.bb);
pos.y -= wh.y;
pos.y -= def.padding.x*2;
});
};
return def;
},
input_lmouse_pressed() {
if (GUI.selected)
GUI.selected.action();
},
input_s_pressed() {
if (GUI.selected?.down) {
GUI.selected.selected = false;
GUI.selected = GUI.selected.down;
GUI.selected.selected = true;
}
},
input_w_pressed() {
if (GUI.selected?.up) {
GUI.selected.selected = false;
GUI.selected = GUI.selected.up;
GUI.selected.selected = true;
}
},
input_enter_pressed() {
if (GUI.selected) {
GUI.selected.action();
}
}
};
GUI.defaults.debug_colors = {
bounds: Color.red.slice(),
margin: Color.blue.slice(),
padding: Color.green.slice()
};
Object.values(GUI.defaults.debug_colors).forEach(function(v) { v.a = 100; });
/* Take numbers from 0 to 1 and remap them to easing functions */
var Ease = {
linear(t) { return t; },
in(t) { return t*t; },
out(t) {
var d = 1-t;
return 1 - d*d
},
inout(t) {
var d = -2*t + 2;
return t < 0.5 ? 2 * t * t : 1 - (d * d) / 2;
},
};
function make_easing_fns(num) {
var obj = {};
obj.in = function(t) {
return Math.pow(t,num);
};
obj.out = function(t) {
return 1 - Math.pow(1 - t, num);
};
var mult = Math.pow(2, num-1);
obj.inout = function(t) {
return t < 0.5 ? mult * Math.pow(t, num) : 1 - Math.pow(-2 * t + 2, num) / 2;
};
return obj;
};
Ease.quad = make_easing_fns(2);
Ease.cubic = make_easing_fns(3);
Ease.quart = make_easing_fns(4);
Ease.quint = make_easing_fns(5);
Ease.expo = {
in(t) {
return t === 0 ? 0 : Math.pow(2, 10 * t - 10);
},
out(t) {
return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
},
inout(t) {
return t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ? Math.pow(2, 20 * t - 10) / 2 : (2 - Math.pow(2, -20 * t + 10)) / 2;
}
};
Ease.bounce = {
in(t) {
return 1 - this.out(t - 1);
},
out(t) {
var n1 = 7.5625;
var d1 = 2.75;
if (t < 1 / d1) { return n1 * t * t; }
else if (t < 2 / d1) { return n1 * (t -= 1.5 / d1) * t + 0.75; }
else if (t < 2.5 / d1) { return n1 * (t -= 2.25 / d1) * t + 0.9375; }
else
return n1 * (t -= 2.625 / d1) * t + 0.984375;
},
inout(t) {
return t < 0.5 ? (1 - this.out(1 - 2 * t)) / 2 : (1 + this.out(2 * t - 1)) / 2;
}
};
Ease.sine = {
in(t) { return 1 - Math.cos((t * Math.PI)/2); },
out(t) { return Math.sin((t*Math.PI)/2); },
inout(t) { return -(Math.cos(Math.PI*t) - 1) / 2; }
};
Ease.elastic = {
in(t) {
return t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10*t-10) * Math.sin((t * 10 - 10.75) * this.c4);
},
out(t) {
return t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10*t) * Math.sin((t * 10 - 0.75) * this.c4) + 1;
},
inout(t) {
t === 0 ? 0 : t === 1 ? 1 : t < 0.5 ?
-(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2
: (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * this.c5)) / 2 + 1;
},
};
Ease.elastic.c4 = 2*Math.PI/3;
Ease.elastic.c5 = 2*Math.PI / 4.5;
var Tween = {
default: {
loop: "restart", /* none, restart, yoyo, circle */
time: 1, /* seconds to do */
ease: Ease.linear,
whole: true,
},
start(obj, target, tvals, options)
{
var defn = Object.create(this.default);
Object.assign(defn, options);
if (defn.loop === 'circle')
tvals.push(tvals[0]);
else if (defn.loop === 'yoyo') {
for (var i = tvals.length-2; i >= 0; i--)
tvals.push(tvals[i]);
}
defn.accum = 0;
var slices = tvals.length - 1;
var slicelen = 1 / slices;
defn.fn = function(dt) {
defn.accum += dt;
defn.pct = (defn.accum % defn.time) / defn.time;
var t = defn.whole ? defn.ease(defn.pct) : defn.pct;
var nval = t / slicelen;
var i = Math.trunc(nval);
nval -= i;
if (!defn.whole)
nval = defn.ease(nval);
obj[target] = tvals[i].lerp(tvals[i+1], nval);
};
defn.restart = function() { defn.accum = 0; };
defn.stop = function() { defn.pause(); defn.restart(); };
defn.pause = function() { Register.update.unregister(defn.fn); };
Register.update.register(defn.fn, defn);
return defn;
},
embed(obj, target, tvals, options) {
var defn = Object.create(this.default);
Object.assign(defn, options);
defn.update_vals = function(vals) {
defn.vals = vals;
if (defn.loop === 'circle')
defn.vals.push(defn.vals[0]);
else if (defn.loop === 'yoyo') {
for (var i = defn.vals.length-2; i >= 0; i--)
defn.vals.push(defn.vals[i]);
}
defn.slices = defn.vals.length - 1;
defn.slicelen = 1 / defn.slices;
};
defn.update_vals(tvals);
defn.time_s = Date.now();
Object.defineProperty(obj, target, {
get() {
defn.accum = (Date.now() - defn.time_s)/1000;
defn.pct = (defn.accum % defn.time) / defn.time;
var t = defn.whole ? defn.ease(defn.pct) : defn.pct;
var nval = t / defn.slicelen;
var i = Math.trunc(nval);
nval -= i;
if (!defn.whole)
nval = defn.ease(nval);
return defn.vals[i].lerp(defn.vals[i+1],nval);
},
});
return defn;
},
};

View File

@@ -1,190 +0,0 @@
var Input = {
setgame() { cmd(77); },
setnuke() { cmd(78); },
};
var Mouse = {
get pos() {
return cmd(45);
},
get screenpos() {
var p = this.pos;
p.y = Window.dimensions.y - p.y;
return p;
},
get worldpos() {
return screen2world(cmd(45));
},
disabled() {
cmd(46, 1);
},
hidden() {
cmd(46, 1);
},
normal() {
cmd(46, 0);
},
};
var Keys = {
shift() {
return cmd(50, 340);// || cmd(50, 344);
},
ctrl() {
return cmd(50, 341);// || cmd(50, 344);
},
alt() {
return cmd(50, 342);// || cmd(50, 346);
},
super() {
return cmd(50, 343);// || cmd(50, 347);
},
};
Input.state2str = function(state) {
if (typeof state === 'string') return state;
switch (state) {
case 0:
return "down";
case 1:
return "pressed";
case 2:
return "released";
}
}
Input.print_pawn_kbm = function(pawn) {
if (!('inputs' in pawn)) return;
var str = "";
for (var key in pawn.inputs) {
str += `${key} | ${pawn.inputs[key].doc}\n`;
}
return str;
};
Input.print_md_kbm = function(pawn) {
if (!('inputs' in pawn)) return;
var str = "";
str += "|control|description|\n|---|---|\n";
for (var key in pawn.inputs) {
str += `|${key}|${pawn.inputs[key].doc}|`;
str += "\n";
}
return str;
};
Input.has_bind = function(pawn, bind) {
return (typeof pawn.inputs?.[bind] === 'function');
};
var Action = {
add_new(name) {
var action = Object.create(Action);
action.name = name;
action.inputs = [];
this.actions.push(action);
return action;
},
actions: [],
};
/* May be a human player; may be an AI player */
var Player = {
players: [],
input(fn, ...args) {
this.pawns.forEach(x => x[fn]?.(...args));
},
mouse_input(type, ...args) {
for (var pawn of this.pawns.reverse()) {
if (typeof pawn.inputs?.mouse?.[type] === 'function') {
pawn.inputs.mouse[type].call(pawn,...args);
pawn.inputs.post?.call(pawn);
return;
}
}
},
char_input(c) {
for (var pawn of this.pawns.reverse()) {
if (typeof pawn.inputs?.char === 'function') {
pawn.inputs.char.call(pawn, c);
pawn.inputs.post?.call(pawn);
return;
}
};
},
raw_input(cmd, state, ...args) {
for (var pawn of this.pawns.reverse()) {
if (typeof pawn.inputs?.any === 'function') {
pawn.inputs.any(cmd);
return;
}
if (!pawn.inputs?.[cmd]) continue;
var fn = null;
switch (state) {
case 'pressed':
fn = pawn.inputs[cmd];
break;
case 'rep':
fn = pawn.inputs[cmd].rep ? pawn.inputs[cmd] : null;
break;
case 'released':
fn = pawn.inputs[cmd].released;
break;
case 'down':
fn = pawn.inputs[cmd].down;
}
if (typeof fn === 'function') {
fn.call(pawn, ... args);
pawn.inputs.post?.call(pawn);
}
}
},
uncontrol(pawn) {
this.players.forEach(function(p) {
p.pawns = p.pawns.filter(x => x !== pawn);
});
},
obj_controlled(obj) {
for (var p in Player.players) {
if (p.pawns.contains(obj))
return true;
}
return false;
},
create() {
var n = Object.create(this);
n.pawns = [];
n.gamepads = [];
n.control = function(pawn) { n.pawns.push_unique(pawn); };
n.uncontrol = function(pawn) { n.pawns = n.pawns.filter(x => x !== pawn); };
this.players.push(n);
return n;
},
};
for (var i = 0; i < 4; i++) {
Player.create();
}

View File

@@ -1,14 +0,0 @@
function nogamegui()
{
GUI.column({
items: [
GUI.text_fn("NO GAME LOADED", {font_size: 6}),
GUI.text_fn("No game.js available.")
],
anchor: [0.5,0.5],
}).draw(Window.dimensions.scale(0.5));
}
Register.gui.register(nogamegui);

View File

@@ -1,65 +0,0 @@
var Physics = {
dynamic: 0,
kinematic: 1,
static: 2,
};
var physics = {
set gravity(x) { cmd(8, x); },
get gravity() { return cmd(72); },
set damping(x) { cmd(73,Math.clamp(x,0,1)); },
get damping() { return cmd(74); },
pos_query(pos) {
return cmd(44, pos);
},
/* Returns a list of body ids that a box collides with */
box_query(box) {
var pts = cmd(52,box.pos,box.wh);
return cmd(52, box.pos, box.wh);
},
box_point_query(box, points) {
if (!box || !points)
return [];
return cmd(86, box.pos, box.wh, points, points.length);
},
};
var Collision = {
types: {},
num: 10,
set_collide(a, b, x) {
this.types[a][b] = x;
this.types[b][a] = x;
},
sync() {
for (var i = 0; i < this.num; i++)
cmd(76,i,this.types[i]);
},
types_nuke() {
Nuke.newline(this.num+1);
Nuke.label("");
for (var i = 0; i < this.num; i++) Nuke.label(i);
for (var i = 0; i < this.num; i++) {
Nuke.label(i);
for (var j = 0; j < this.num; j++) {
if (j < i)
Nuke.label("");
else {
this.types[i][j] = Nuke.checkbox(this.types[i][j]);
this.types[j][i] = this.types[i][j];
}
}
}
},
};
for (var i = 0; i < Collision.num; i++) {
Collision.types[i] = [];
for (var j = 0; j < Collision.num; j++)
Collision.types[i][j] = false;
};

View File

@@ -1,6 +0,0 @@
Game.play();
if (!IO.exists("game.js"))
load("scripts/nogame.js");
else
load("game.js");

View File

@@ -1,45 +0,0 @@
var Music = {
play(path) {
Log.info("Playing " + path);
cmd(87,path);
},
stop() {
cmd(89);
},
pause() {
cmd(88);
},
set volume(x) {
},
};
var Sound = {
sounds: [], /* array of loaded sound files */
play(file) {
// var s = Object.create(Sound);
// s.path = file;
// s.play();
this.id = cmd(14,file);
//return s;
},
music(midi, sf) {
cmd(13, midi, sf);
},
musicstop() {
cmd(15);
},
/* Between 0 and 100 */
set volume(x) { cmd(19, x); },
killall() {
Music.stop();
this.musicstop();
/* TODO: Kill all sound effects that may still be running */
},
};

View File

@@ -1,179 +0,0 @@
function compile_env(str, env, file)
{
file ??= "unknown";
return cmd(123, str, env, file);
}
function fcompile_env(file, env)
{
return compile_env(IO.slurp(file), env, file);
}
var Log = {
set level(x) { cmd(92,x); },
get level() { return cmd(93); },
print(msg, lvl) {
var lg;
if (typeof msg === 'object') {
lg = JSON.stringify(msg, null, 2);
} else {
lg = msg;
}
var stack = (new Error()).stack;
var n = stack.next('\n',0)+1;
n = stack.next('\n', n)+1;
var nnn = stack.slice(n);
var fmatch = nnn.match(/\(.*\:/);
var file = fmatch ? fmatch[0].shift(1).shift(-1) : "nofile";
var lmatch = nnn.match(/\:\d*\)/);
var line = lmatch ? lmatch[0].shift(1).shift(-1) : "0";
yughlog(lvl, msg, file, line);
},
info(msg) {
this.print(msg, 0);
},
warn(msg) {
this.print(msg, 1);
},
error(msg) {
this.print(msg, 2);
this.stack(1);
},
critical(msg) {
this.print(msg,3);
this.stack(1);
},
write(msg) {
cmd(91,msg);
},
say(msg) {
cmd(91, `${msg}\n`);
},
stack(skip = 0) {
var stack = (new Error()).stack;
var n = stack.next('\n',0)+1;
for (var i = 0; i < skip; i++)
n = stack.next('\n', n)+1;
this.write(stack.slice(n));
},
};
var IO = {
exists(file) { return cmd(65, file);},
slurp(file) {
if (!this.exists(file)) {
Log.warn(`File ${file} does not exist; can't slurp.`);
return "";
}
return cmd(38,file);
},
slurpwrite(str, file) { return cmd(39, str, file); },
extensions(ext) {
var paths = IO.ls();
paths = paths.filter(function(str) { return str.ext() === ext; });
return paths;
},
ls() { return cmd(66); },
glob(pat) {
var paths = IO.ls();
pat = pat.replaceAll(/([\[\]\(\)\^\$\.\|\+])/g, "\\$1");
pat = pat.replaceAll('**', '.*');
pat = pat.replaceAll(/[^\.]\*/g, '[^\\/]*');
pat = pat.replaceAll('?', '.');
var regex = new RegExp("^"+pat+"$", "");
return paths.filter(str => str.match(regex));
},
};
var Cmdline = {};
Cmdline.cmds = [];
Cmdline.register_cmd = function(flag, fn, doc) {
Cmdline.cmds.push({
flag: flag,
fn: fn,
doc: doc
});
};
function cmd_args(cmdargs)
{
var play = false;
var cmds = cmdargs.split(" ");
for (var i = 1; i < cmds.length; i++) {
if (cmds[i][0] !== '-') {
Log.warn(`Command '${cmds[i]}' should start with a '-'.`);
continue;
}
var c = Cmdline.cmds.find(function(cmd) { return cmd.flag === cmds[i].slice(1); });
if (!c) {
Log.warn(`Command ${cmds[i]} not recognized.`);
continue;
}
var sendstr = [];
var j = i+1;
while (cmds[j] && cmds[j][0] !== '-') {
sendstr.push(cmds[j]);
j++;
}
c.fn(sendstr);
i = j-1;
}
}
Cmdline.register_cmd("p", function() { Game.edit = false; }, "Launch engine in play mode.");
Cmdline.register_cmd("v", function() { Log.say(cmd(120)); Game.quit(); }, "Display engine info.");
Cmdline.register_cmd("c", function() {}, "Redirect logging to console.");
Cmdline.register_cmd("l", function(n) {
Log.level = n;
}, "Set log level.");
Cmdline.register_cmd("h", function(str) {
for (var cmd of Cmdline.cmds) {
Log.say(`-${cmd.flag}: ${cmd.doc}`);
}
Game.quit();
},
"Help.");
Cmdline.register_cmd("b", function(str) {
var packname;
if (str.length === 0)
packname = "test.cdb";
else if (str.length > 1) {
Log.warn("Give me a single filename for the pack.");
Game.quit();
} else
packname = str[0];
Log.warn(`Packing into ${packname}`);
cmd(124, packname);
Game.quit();
}, "Pack the game into the given name.");
Cmdline.register_cmd("e", function(pawn) {
run("scripts/editor.js");
eval(`Log.write(Input.print_md_kbm(${pawn}));`);
Game.quit();
}, "Print input documentation for a given object in a markdown table." );
Cmdline.register_cmd("t", function() {
Log.warn("Testing not implemented yet.");
Game.quit();
}, "Test suite.");

View File

@@ -1,335 +0,0 @@
var texteditor = clone(inputpanel, {
title: "text editor",
_cursor:0, /* Text cursor: [char,line] */
get cursor() { return this._cursor; },
set cursor(x) {
if (x > this.value.length)
x = this.value.length;
if (x < 0)
x = 0;
this._cursor = x;
this.line = this.get_line();
},
submit() {},
line: 0,
killring: [],
undos: [],
startbuffer: "",
savestate() {
this.undos.push(this.value.slice());
},
popstate() {
if (this.undos.length === 0) return;
this.value = this.undos.pop();
this.cursor = this.cursor;
},
copy(start, end) {
return this.value.slice(start,end);
},
delete_line(p) {
var ls = this.line_start(p);
var le = this.line_end(p)+1;
this.cut_span(ls,le);
this.to_line_start();
},
line_blank(p) {
var ls = this.line_start(p);
var le = this.line_end(p);
var line = this.value.slice(ls, le);
if (line.search(/[^\s]/g) === -1)
return true;
else
return false;
},
get_line() {
var line = 0;
for (var i = 0; i < this.cursor; i++)
if (this.value[i] === "\n")
line++;
return line;
},
start() {
this.cursor = 0;
this.startbuffer = this.value.slice();
},
get dirty() {
return this.startbuffer !== this.value;
},
gui() {
GUI.text_cursor(this.value, [100,700],1,this.cursor+1);
GUI.text("C" + this.cursor + ":::L" + this.line + ":::" + (this.dirty ? "DIRTY" : "CLEAN"), [100,100], 1);
},
insert_char(char) {
this.value = this.value.slice(0,this.cursor) + char + this.value.slice(this.cursor);
this.cursor++;
},
input_enter_pressrep() {
var white = this.line_starting_whitespace(this.cursor);
this.insert_char('\n');
for (var i = 0; i < white; i++)
this.insert_char(" ");
},
input_text(char) {
if (Keys.ctrl() || Keys.alt()) return;
this.insert_char(char);
this.keycb();
},
input_backspace_pressrep() {
this.value = this.value.slice(0,this.cursor-1) + this.value.slice(this.cursor);
this.cursor--;
},
line_starting_whitespace(p) {
var white = 0;
var l = this.line_start(p);
while (this.value[l] === " ") {
white++;
l++;
}
return white;
},
cut_span(start, end) {
if (end < start) return;
this.savestate();
var ret = this.value.slice(start,end);
this.value = this.value.slice(0,start) + this.value.slice(end);
if (start > this.cursor)
return ret;
this.cursor -= ret.length;
return ret;
},
next_word(pos) {
var v = this.value.slice(pos+1).search(/[^\w]\w/g);
if (v === -1) return pos;
return pos + v + 2;
},
prev_word(pos) {
while (this.value.slice(pos,pos+2).search(/[^\w]\w/g) === -1 && pos > 0)
pos--;
return pos+1;
},
end_of_word(pos) {
var l = this.value.slice(pos).search(/\w[^\w]/g);
return l+pos;
},
get inset() {
return this.cursor - this.value.prev('\n', this.cursor) - 1;
},
line_start(p) {
return this.value.prev('\n', p)+1;
},
line_end(p) {
return this.value.next('\n', p);
},
next_line(p) {
return this.value.next('\n',p)+1;
},
prev_line(p) {
return this.line_start(this.value.prev('\n', p));
},
to_line_start() {
this.cursor = this.value.prev('\n', this.cursor)+1;
},
to_line_end() {
var p = this.value.next('\n', this.cursor);
if (p === -1)
this.to_file_end();
else
this.cursor = p;
},
line_width(pos) {
var start = this.line_start(pos);
var end = this.line_end(pos);
if (end === -1)
end = this.value.length;
return end-start;
},
to_file_end() { this.cursor = this.value.length; },
to_file_start() { this.cursor = 0; },
desired_inset: 0,
});
texteditor.inputs = {};
texteditor.inputs['C-s'] = function() {
editor.edit_level.script = texteditor.value;
editor.save_current();
texteditor.startbuffer = texteditor.value.slice();
};
texteditor.inputs['C-s'].doc = "Save script to file.";
texteditor.inputs['C-u'] = function() { this.popstate(); };
texteditor.inputs['C-u'].doc = "Undo.";
texteditor.inputs['C-q'] = function() {
var ws = this.prev_word(this.cursor);
var we = this.end_of_word(this.cursor)+1;
var find = this.copy(ws, we);
var obj = editor.edit_level.varname2obj(find);
if (obj) {
editor.unselect();
editor.selectlist.push(obj);
}
};
texteditor.inputs['C-q'].doc = "Select object of selected word.";
texteditor.inputs['C-o'] = function() {
this.insert_char('\n');
this.cursor--;
};
texteditor.inputs['C-o'].doc = "Insert newline.";
texteditor.inputs['C-o'].rep = true;
texteditor.inputs['M-o'] = function() {
while (this.line_blank(this.next_line(this.cursor)))
this.delete_line(this.next_line(this.cursor));
while (this.line_blank(this.prev_line(this.cursor)))
this.delete_line(this.prev_line(this.cursor));
};
texteditor.inputs['M-o'].doc = "Delete surround blank lines.";
texteditor.inputs['C-d'] = function () { this.value = this.value.slice(0,this.cursor) + this.value.slice(this.cursor+1); };
texteditor.inputs['C-d'].doc = "Delete character.";
texteditor.inputs['M-d'] = function() { this.cut_span(this.cursor, this.end_of_word(this.cursor)+1); };
texteditor.inputs['M-d'].doc = "Delete word.";
texteditor.inputs['C-a'] = function() {
this.to_line_start();
this.desired_inset = this.inset;
};
texteditor.inputs['C-a'].doc = "To start of line.";
texteditor.inputs['C-y'] = function() {
if (this.killring.length === 0) return;
this.insert_char(this.killring.pop());
};
texteditor.inputs['C-y'].doc = "Insert from killring.";
texteditor.inputs['C-e'] = function() {
this.to_line_end();
this.desired_inset = this.inset;
};
texteditor.inputs['C-e'].doc = "To line end.";
texteditor.inputs['C-k'] = function() {
if (this.cursor === this.value.length-1) return;
var killamt = this.value.next('\n', this.cursor) - this.cursor;
var killed = this.cut_span(this.cursor-1, this.cursor+killamt);
this.killring.push(killed);
};
texteditor.inputs['C-k'].doc = "Kill from cursor to end of line.";
texteditor.inputs['M-k'] = function() {
var prevn = this.value.prev('\n', this.cursor);
var killamt = this.cursor - prevn;
var killed = this.cut_span(prevn+1, prevn+killamt);
this.killring.push(killed);
this.to_line_start();
};
texteditor.inputs['M-k'].doc = "Kill entire line the cursor is on.";
texteditor.inputs['C-b'] = function() {
this.cursor--;
this.desired_inset = this.inset;
};
texteditor.inputs['C-b'].rep = true;
texteditor.inputs['M-b'] = function() {
this.cursor = this.prev_word(this.cursor-2);
this.desired_inset = this.inset;
};
texteditor.inputs['M-b'].rep = true;
texteditor.inputs['C-f'] = function() {
this.cursor++;
this.desired_inset = this.inset;
};
texteditor.inputs['C-f'].rep = true;
texteditor.inputs['M-f'] = function() {
this.cursor = this.next_word(this.cursor);
this.desired_inset = this.inset;
};
texteditor.inputs['M-f'].rep = true;
texteditor.inputs['C-p'] = function() {
if (this.cursor === 0) return;
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.prev_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-p'].rep = true;
texteditor.inputs['M-p'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.prev_line(this.cursor);
};
texteditor.inputs['M-p'].doc = "Go up to next line with text on it.";
texteditor.inputs['M-p'].rep = true;
texteditor.inputs['C-n'] = function() {
if (this.cursor === this.value.length-1) return;
if (this.value.next('\n', this.cursor) === -1) {
this.to_file_end();
return;
}
this.desired_inset = Math.max(this.desired_inset, this.inset);
this.cursor = this.next_line(this.cursor);
var newlinew = this.line_width(this.cursor);
this.cursor += Math.min(this.desired_inset, newlinew);
};
texteditor.inputs['C-n'].rep = true;
texteditor.inputs['M-n'] = function() {
while (this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
while (!this.line_blank(this.cursor))
this.cursor = this.next_line(this.cursor);
};
texteditor.inputs['M-n'].doc = "Go down to next line with text on it.";
texteditor.inputs['M-n'].rep = true;