Jofthomas's picture
Jofthomas HF staff
bulk
ce8b18b
raw
history blame
48.2 kB
// --
// Simple level editer.
//
// TODO:
// -- right now if plaxing a sprite, will place based on selected tiles. So need to clear that when
// loading a sprite
// -- fix hardcoded animations, hack of putting spritesheet into g_ctx etc
// -- create tab that contains all animations for a given json file
// -- add portals to level for character start positions
// -- if you load an animated sprite and then load a level, it just puts the sprite everywhere
//
//
// Done:
// -- fix level load bug where texture doesn't fit (load, mage, serene and then gentle)
// -- write maps with sprites
// - <esc> clear selected_tiles
// - Delete tiles
// - move magic numbers to context / initialization (zIndex, pane size etc.)
// - todo fudge factor on g_ctx.tileset
// - get rid of dangerous CONFIG.tiledim (use g_ctx.tileDim instead)
// - XXX create tilesetpadding for tilesets whos tiles are spaced (e.g. phantasy star II)
// - only use fudge to pick sprites rather than fudge and non
// - use g_ctx for g_ctx.tileset parameters instead of CONFIG (starting with initTilesetConfig)
// - todo print locations on screen
//
//
// Keybindings:
// f - fill level 0 with current tile
// <ctl>-z - undo
// g - overlay 32x32 grid
// s - generate .js file to move over to convex/maps/
// m - place a semi-transparent red mask over all tiles. This helps find invisible tiles
// d - hold while clicking a tile to delete
// p - toggle between 16pixel and 32 pixel.
//
// Known bugs and annoyances
// - if deleting a tile while filter is on, filter isn't refreshed so need to toggle with "m"
// --
import * as PIXI from 'pixi.js'
import { g_ctx } from './lecontext.js' // global context
import * as CONFIG from './leconfig.js'
import * as UNDO from './undo.js'
import * as MAPFILE from './mapfile.js'
import * as UI from './lehtmlui.js'
import { EventSystem } from '@pixi/events';
g_ctx.debug_flag = true;
g_ctx.debug_flag2 = false; // really verbose output
function tileset_index_from_coords(x, y) {
let retme = x + (y*g_ctx.tilesettilew);
console.log("tileset_index_from_coord ",retme, x, y);
return retme;
}
function level_index_from_coords(x, y) {
// place 16px tiles in separate index space
let offset = (g_ctx.tiledimx == 16)? CONFIG.MAXTILEINDEX : 0;
let retme = x + (y*CONFIG.leveltilewidth) + offset;
return retme;
}
function tileset_index_from_px(x, y) {
let coord_x = Math.floor(x / (g_ctx.tiledimx + CONFIG.tilesetpadding));
let coord_y = Math.floor(y / (g_ctx.tiledimx+ CONFIG.tilesetpadding));
console.log("tileset_index_from_px ",x, y);
return tileset_index_from_coords(coord_x, coord_y);
}
function level_index_from_px(x, y) {
let coord_x = Math.floor(x / g_ctx.tiledimx);
let coord_y = Math.floor(y / g_ctx.tiledimx);
return level_index_from_coords(coord_x, coord_y);
}
function tileset_coords_from_index(index) {
let x = index % (g_ctx.tilesettilew);
let y = Math.floor(index / (g_ctx.tilesettilew));
// console.log("tilesettilewidth: ",g_ctx.tilesettilew);
// console.log("tileset_coords_from_index tile coords: ",index,x,y);
return [x,y];
}
function tileset_px_from_index(index) {
let ret = tileset_coords_from_index(index);
return [ret[0] * (g_ctx.tiledimx+CONFIG.tilesetpadding), ret[1] * (g_ctx.tiledimx+CONFIG.tilesetpadding)] ;
}
// return a sprite of size tileDim given (x,y) starting location
function sprite_from_px(x, y) {
const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {
scaleMode: PIXI.SCALE_MODES.NEAREST,
});
let texture = new PIXI.Texture(bt,
new PIXI.Rectangle(x, y, g_ctx.tiledimx, g_ctx.tiledimx),
);
return new PIXI.Sprite(texture);
}
function DragState() {
this.square = new PIXI.Graphics();
this.tooltip = new PIXI.Text('', {
fontFamily: 'Courier',
fontSize: 12,
fill: 0xffffff,
align: 'center',
});
this.startx = 0;
this.starty = 0;
this.endx = 0;
this.endy = 0;
}
class LayerContext {
constructor(app, pane, num, mod = null) {
this.app = app;
this.scrollpane = pane;
this.num = num;
this.widthpx = CONFIG.levelwidth;
this.heightpx = CONFIG.levelheight;
this.container = new PIXI.Container();
this.sprites = {};
this.composite_sprites = {};
this.dragctx = new DragState();
app.stage.addChild(this.container);
this.mouseshadow = new PIXI.Container();
this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow;
this.lasttileindex = -1; // current tileset index
this.curanimatedtile = null;
this.fudgex = 0; // offset from 0,0
this.fudgey = 0;
this.square = new PIXI.Graphics();
this.square.beginFill(0x2980b9);
this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);
this.square.endFill();
this.square.eventMode = 'static';
this.container.addChild(this.square);
this.square.on('mousemove', onLevelMousemove.bind(this));
this.square.on('mouseover', onLevelMouseover.bind(this));
this.square.on('pointerout', onLevelMouseOut.bind(this))
this.square.on('pointerdown', onLevelPointerDown.bind(null, this))
.on('pointerup', onLevelDragEnd.bind(null, this))
.on('pointerupoutside', onLevelDragEnd.bind(null, this));
if (mod != null && !(mod === g_ctx)) {
this.loadFromMapFile(mod);
}
}
loadFromMapFile(mod) {
let tiles = [];
if (this.num == 0) {
tiles = mod.bgtiles[0];
} else if (this.num == 1) {
tiles = mod.bgtiles[1];
} else if (this.num == 2) {
tiles = mod.objmap[0];
} else if (this.num == 3) {
tiles = mod.objmap[1];
} else {
console.log("loadFromMapFile: Error unknow layer number");
return;
}
for (let x = 0; x < tiles.length; x++) {
for (let y = 0; y < tiles[0].length; y++) {
if (tiles[x][y] != -1) {
this.addTileLevelCoords(x, y, mod.tiledim, tiles[x][y]);
}
}
}
}
// this will create a rectangle with an alpha channel for every square that has a sprite. This helps find
// sprites that are purely transparent
drawFilter() {
if (typeof this.filtergraphics == 'undefined') {
this.filtertoggle = true;
this.filtergraphics = new PIXI.Graphics();
this.filtergraphics.zIndex = CONFIG.zIndexFilter;
}
if (this.filtertoggle) {
this.filtergraphics.beginFill(0xff0000, 0.3);
for (let i in this.sprites) {
let spr = this.sprites[i];
this.filtergraphics.drawRect(spr.x, spr.y, g_ctx.tiledimx, g_ctx.tiledimx);
}
this.filtergraphics.endFill();
this.container.addChild(this.filtergraphics);
}else{
this.filtergraphics.clear();
this.container.removeChild(this.filtergraphics);
}
this.filtertoggle = ! this.filtertoggle;
}
// add tile of "index" to Level at location x,y
addTileLevelCoords(x, y, dim, index) {
return this.addTileLevelPx(x * dim, y * dim, index);
}
// add tile of tileset "index" to Level at location x,y
addTileLevelPx(x, y, index) {
if (x > CONFIG.levelwidth || y > CONFIG.levelheight){
console.log("tile placed outside of level boundary, ignoring",x,y)
return -1;
}
let xPx = x;
let yPx = y;
let ctile = null;
let ctile2 = null;
if(g_ctx.spritesheet != null){
ctile = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);
ctile2 = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);
ctile.animationSpeed = .1;
ctile2.animationSpeed = .1;
ctile.autoUpdate = true;
ctile2.autoUpdate = true;
ctile.play();
ctile2.play();
// HACK for now just stuff animated sprite details into the sprite
ctile.animationname = 'row0';
ctile.spritesheetname = g_ctx.spritesheetname;
} else {
let pxloc = tileset_px_from_index(index);
ctile = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
ctile.index = index;
ctile2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
}
// snap to grid
const dx = g_ctx.tiledimx;
const dy = g_ctx.tiledimy;
ctile.x = Math.floor(xPx / dx) * dx;
ctile2.x = Math.floor(xPx / dx) * dx;
ctile.y = Math.floor(yPx / dy) * dy;
ctile2.y = Math.floor(yPx / dy) * dy;
ctile2.zIndex = this.num;
// console.log(xPx,yPx,ctile.x,ctile.y);
let new_index = level_index_from_px(ctile.x, ctile.y);
if(g_ctx.debug_flag2){
console.log('addTileLevelPx ',this.num,' ctile.x ', ctile.x, 'ctile.y ', ctile.y, "index ", index, "new_index", new_index);
}
if (!g_ctx.dkey) {
this.container.addChild(ctile);
g_ctx.composite.container.addChild(ctile2);
}
if (this.sprites.hasOwnProperty(new_index)) {
if(g_ctx.debug_flag){
console.log("addTileLevelPx: ",this.num,"removing old tile", new_index);
}
this.container.removeChild(this.sprites[new_index]);
delete this.sprites[new_index];
g_ctx.composite.container.removeChild(this.composite_sprites[new_index]);
delete this.composite_sprites[new_index];
}
if (!g_ctx.dkey) {
this.sprites[new_index] = ctile;
this.composite_sprites[new_index] = ctile2;
} else if (typeof this.filtergraphics != 'undefined') {
this.filtergraphics.clear();
this.drawFilter();
this.drawFilter();
}
// consolelog("SETTING ZINDEX ", this.composite_sprites[new_index].zIndex);
return new_index;
}
} // class LayerContext
class TilesetContext {
constructor(app, mod = g_ctx) {
this.app = app;
this.container = new PIXI.Container();
this.widthpx = g_ctx.tilesetpxw;
this.heightpx = g_ctx.tilesetpxh;
console.log(mod.tilesetpath);
const texture = PIXI.Texture.from(mod.tilesetpath);
const bg = new PIXI.Sprite(texture);
this.square = new PIXI.Graphics();
this.square.beginFill(0x2980b9);
this.square.drawRect(0, 0, mod.tilesetpxw, mod.tilesetpxh);
this.square.endFill();
this.square.eventMode = 'static';
this.container.addChild(this.square);
this.container.addChild(bg);
this.app.stage.addChild(this.container);
this.fudgex = 0; // offset from 0,0
this.fudgey = 0;
this.dragctx = new DragState();
this.square.on('mousedown', function (e) {
// if a spritesheet has been loaded from a file, delete
// FIXME, we should be able to add animated tiles to the
// tileset ...
if(g_ctx.spritesheet != null){
// FIXME .. creating a leak here. But animatedsprites are still on the map so
// cannot destroy. In the future these should be part of the UI
// g_ctx.spritesheet.destroy();
g_ctx.spritesheet = null;
}
g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y);
if(g_ctx.debug_flag) {
console.log("g_ctx.tileset mouse down. index "+g_ctx.tile_index);
}
});
this.square.on('pointerdown', onTilesetDragStart)
.on('pointerup', onTilesetDragEnd)
.on('pointerupoutside', onTilesetDragEnd);
}
addTileSheet(name, sheet){
console.log(" tileset.addTileSheet ", sheet);
// FIXME ... development code
g_ctx.spritesheet = sheet;
g_ctx.spritesheetname = name;
let as = new PIXI.AnimatedSprite(sheet.animations['row0']);
as.animationSpeed = .1;
as.autoUpdate = true;
as.play();
as.alpha = .5;
g_ctx.g_layers[0].curanimatedtile = as;
}
} // class TilesetContext
class CompositeContext {
constructor(app) {
this.app = app;
this.widthpx = CONFIG.levelwidth;
this.heightpx = CONFIG.levelheight;
this.container = new PIXI.Container();
this.container.sortableChildren = true;
this.app.stage.addChild(this.container);
this.sprites = {};
this.circle = new PIXI.Graphics();
this.circle.zIndex = CONFIG.zIndexCompositePointer;
this.fudgex = 0; // offset from 0,0
this.fudgey = 0;
this.mouseshadow = new PIXI.Container();
this.mouseshadow.zIndex = CONFIG.zIndexMouseShadow;
this.lasttileindex = -1;
this.square = new PIXI.Graphics();
this.square.beginFill(0x2980b9);
this.square.drawRect(0, 0, CONFIG.levelwidth, CONFIG.levelheight);
this.square.endFill();
this.square.eventMode = 'static';
this.container.addChild(this.square);
this.square.on('mousedown', onCompositeMousedown.bind(null, this));
}
} // class CompositeContext
function loadAnimatedSpritesFromModule(mod){
if(!('animatedsprites' in mod) || mod.animatedsprites.length <= 0){
return;
}
let m = new Map();
for(let x = 0; x < mod.animatedsprites.length; x++){
let spr = mod.animatedsprites[x];
if(! m.has(spr.sheet)){
m.set(spr.sheet, [spr]);
}else{
m.get(spr.sheet).push(spr);
}
}
for(let key of m.keys()){
console.log("loadAnimatedSpritesFromModule: ",key);
PIXI.Assets.load("./"+key).then(
function(sheet) {
// setup global state so we can use layer addTileLevelMethod
g_ctx.spritesheet = sheet;
g_ctx.spritesheetname = key;
let asprarray = m.get(key);
for (let asprite of asprarray) {
// TODO FIXME, pass in animation name
console.log("Loading animation", asprite.animation);
g_ctx.g_layers[asprite.layer].addTileLevelPx(asprite.x, asprite.y, -1);
}
g_ctx.spritesheet = null;
g_ctx.spritesheetname = null;
}
);
}
}
function loadMapFromModuleFinish(mod) {
g_ctx.composite.container.removeChildren();
g_ctx.tileset_app.stage.removeChildren()
g_ctx.tileset = new TilesetContext(g_ctx.tileset_app, mod);
g_ctx.g_layer_apps[0].stage.removeChildren()
g_ctx.g_layers[0] = new LayerContext(g_ctx.g_layer_apps[0], document.getElementById("layer0pane"), 0, mod);
g_ctx.g_layer_apps[1].stage.removeChildren()
g_ctx.g_layers[1] = new LayerContext(g_ctx.g_layer_apps[1], document.getElementById("layer1pane"), 1, mod);
g_ctx.g_layer_apps[2].stage.removeChildren()
g_ctx.g_layers[2] = new LayerContext(g_ctx.g_layer_apps[2], document.getElementById("layer2pane"), 2, mod);
g_ctx.g_layer_apps[3].stage.removeChildren()
g_ctx.g_layers[3] = new LayerContext(g_ctx.g_layer_apps[3], document.getElementById("layer3pane"), 3, mod);
loadAnimatedSpritesFromModule(mod);
}
function loadMapFromModule(mod) {
g_ctx.tilesetpath = mod.tilesetpath;
initTilesSync(loadMapFromModuleFinish.bind(null, mod));
initTiles();
}
function downloadpng(filename) {
let newcontainer = new PIXI.Container();
let children = [...g_ctx.composite.container.children];
for(let i = 0; i < children.length; i++) {
let child = children[i];
if (! child.hasOwnProperty('isSprite') || !child.isSprite){
console.log(child);
continue;
}
// console.log(child, typeof child);
g_ctx.composite.container.removeChild(child);
newcontainer.addChild(child);
}
const { renderer } = g_ctx.composite_app;
renderer.plugins.extract.canvas(newcontainer).toBlob(function (b) {
console.log(b);
var a = document.createElement("a");
document.body.append(a);
a.download = filename;
a.href = URL.createObjectURL(b);
a.click();
a.remove();
}, "image/png");
}
window.saveCompositeAsImage = () => {
downloadpng("g_ctx.composite.png");
}
window.onTab = (evt, tabName) => {
// Declare all variables
var i, tabcontent, tablinks;
// Get all elements with class="tabcontent" and hide them
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
// Get all elements with class="tablinks" and remove the class "active"
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the button that opened the tab
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
if (tabName == "map"){
g_ctx.map_app.stage.addChild(g_ctx.composite.container);
}else {
g_ctx.composite.app.stage.addChild(g_ctx.composite.container);
}
}
// fill base level with currentIndex tile
window.fill0 = () => {
UNDO.undo_mark_task_start(g_ctx.g_layers[0]);
for(let i = 0; i < CONFIG.levelwidth / g_ctx.tiledimx; i++){
for(let j = 0; j < CONFIG.levelheight / g_ctx.tiledimx; j++){
let ti = g_ctx.g_layers[0].addTileLevelCoords(i,j,g_ctx.tiledimx, g_ctx.tile_index);
UNDO.undo_add_index_to_task(ti);
}
}
UNDO.undo_mark_task_end();
}
window.addEventListener(
"keyup", (event) => {
if (event.code == "KeyD"){
g_ctx.dkey = false;
g_ctx.g_layers.map( (l) => l.container.addChild(l.mouseshadow));
g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);
}
});
window.addEventListener(
"keydown", (event) => {
if (event.code == "KeyD"){
g_ctx.dkey = true;
g_ctx.g_layers.map((l) => l.container.removeChild(l.mouseshadow) );
g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);
}
if (event.code == 'KeyF'){
window.fill0();
}
else if (event.code == 'KeyS'){
MAPFILE.generate_level_file();
}
else if (event.code == 'Escape'){
g_ctx.selected_tiles = [];
g_ctx.g_layers.map((l) => l.mouseshadow.removeChildren());
g_ctx.composite.mouseshadow.removeChildren();
}
else if (event.code == 'KeyM'){
g_ctx.g_layers.map((l) => l.drawFilter () );
}else if (event.code == 'KeyP'){
setGridSize((g_ctx.tiledimx == 16)?32:16);
}
else if (event.code == 'KeyG'){
g_ctx.g_layers.map((l) => redrawGrid (l, false) );
redrawGrid(g_ctx.tileset, false);
redrawGrid(g_ctx.composite, false);
}
else if (event.ctrlKey && event.code === 'KeyZ'){
let undome = UNDO.undo_pop();
if (!undome) {
return;
}
let layer = undome.shift();
for(let i = 0; i < undome.length; i++) {
if (g_ctx.debug_flag) {
console.log("Undo removing ", undome[i])
}
layer.container.removeChild(layer.sprites[undome[i]]);
g_ctx.composite.container.removeChild(layer.composite_sprites[undome[i]]);
}
}
else if (event.shiftKey && event.code == 'ArrowUp') {
g_ctx.tileset.fudgey -= 1;
redrawGrid(g_ctx.tileset, true);
}
else if (event.shiftKey && event.code == 'ArrowDown') {
g_ctx.tileset.fudgey += 1;
redrawGrid(g_ctx.tileset, true);
}
else if (event.shiftKey && event.code == 'ArrowLeft') {
g_ctx.tileset.fudgex -= 1;
redrawGrid(g_ctx.tileset, true);
}
else if (event.shiftKey && event.code == 'ArrowRight') {
g_ctx.tileset.fudgex += 1;
redrawGrid(g_ctx.tileset, true);
}
}
);
// Listen to pointermove on stage once handle is pressed.
function onTilesetDragStart(e)
{
if (g_ctx.debug_flag) {
console.log("onDragStartTileset()");
}
g_ctx.tileset.app.stage.eventMode = 'static';
g_ctx.tileset.app.stage.addEventListener('pointermove', onTilesetDrag);
g_ctx.tileset.dragctx.startx = e.data.global.x;
g_ctx.tileset.dragctx.starty = e.data.global.y;
g_ctx.tileset.dragctx.endx = e.data.global.x;
g_ctx.tileset.dragctx.endy = e.data.global.y;
g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.square);
// g_ctx.tileset.app.stage.addChild(g_ctx.tileset.dragctx.tooltip);
g_ctx.selected_tiles = [];
}
// Stop dragging feedback once the handle is released.
function onTilesetDragEnd(e)
{
if (g_ctx.debug_flag) {
console.log("onDragEndTileset()");
}
g_ctx.tileset.app.stage.eventMode = 'auto';
g_ctx.tileset.app.stage.removeEventListener('pointermove', onTilesetDrag);
g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.square);
g_ctx.tileset.app.stage.removeChild(g_ctx.tileset.dragctx.tooltip);
if(g_ctx.tileset.dragctx.endx < g_ctx.tileset.dragctx.startx){
let tmp = g_ctx.tileset.dragctx.endx;
g_ctx.tileset.dragctx.endx = g_ctx.tileset.dragctx.startx;
g_ctx.tileset.dragctx.startx = tmp;
}
if(g_ctx.tileset.dragctx.endy < g_ctx.tileset.dragctx.starty){
let tmp = g_ctx.tileset.dragctx.endy;
g_ctx.tileset.dragctx.endy = g_ctx.tileset.dragctx.starty;
g_ctx.tileset.dragctx.starty = tmp;
}
let starttilex = Math.floor(g_ctx.tileset.dragctx.startx / g_ctx.tiledimx);
let starttiley = Math.floor(g_ctx.tileset.dragctx.starty / g_ctx.tiledimx);
let endtilex = Math.floor(g_ctx.tileset.dragctx.endx / g_ctx.tiledimx);
let endtiley = Math.floor(g_ctx.tileset.dragctx.endy / g_ctx.tiledimx);
if (g_ctx.debug_flag) {
console.log("sx sy ex ey ", starttilex, ",", starttiley, ",", endtilex, ",", endtiley);
}
// let mouse clicked handle if there isn't a multiple tile square
if(starttilex === endtilex && starttiley === endtiley ){
return;
}
// g_ctx.tile_index = (starttiley * g_ctx.tilesettilew) + starttilex;
g_ctx.tile_index = tileset_index_from_px(e.global.x, e.global.y);
let origx = starttilex;
let origy = starttiley;
for(let y = starttiley; y <= endtiley; y++){
for(let x = starttilex; x <= endtilex; x++){
let squareindex = (y * g_ctx.tilesettilew) + x;
g_ctx.selected_tiles.push([x - origx,y - origy,squareindex]);
}
}
g_ctx.tileset.dragctx.square.clear();
// g_ctx.tileset.dragctx.tooltip.clear();
}
function onTilesetDrag(e)
{
if (g_ctx.debug_flag) {
console.log("onDragTileset()");
}
g_ctx.tileset.dragctx.endx = e.global.x;
g_ctx.tileset.dragctx.endy = e.global.y;
g_ctx.tileset.dragctx.square.clear();
g_ctx.tileset.dragctx.square.beginFill(0xFF3300, 0.3);
g_ctx.tileset.dragctx.square.lineStyle(2, 0xffd900, 1);
g_ctx.tileset.dragctx.square.moveTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.starty);
g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.starty);
g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.endx, g_ctx.tileset.dragctx.endy);
g_ctx.tileset.dragctx.square.lineTo(g_ctx.tileset.dragctx.startx, g_ctx.tileset.dragctx.endy);
g_ctx.tileset.dragctx.square.closePath();
g_ctx.tileset.dragctx.square.endFill();
// g_ctx.tileset.dragctx.tooltip.clear();
// g_ctx.tileset.dragctx.tooltip.beginFill(0xFF3300, 0.3);
// g_ctx.tileset.dragctx.tooltip.lineStyle(2, 0xffd900, 1);
// g_ctx.tileset.dragctx.tooltip.drawRect(e.global.x, e.global.y, 20,8);
// g_ctx.tileset.dragctx.tooltip.endFill();
}
//g_ctx.tileset.app.stage.addChild(g_ctx.tileset.container);
function redrawGrid(pane, redraw = false) {
if (typeof pane.gridtoggle == 'undefined') {
// first time we're being called, initialized
pane.gridtoggle = false;
pane.gridvisible = false;
redraw = true;
pane.gridvisible = true;
}
if (redraw) {
if (typeof pane.gridgraphics != 'undefined') {
pane.container.removeChild(pane.gridgraphics);
}
pane.gridgraphics = new PIXI.Graphics();
let gridsizex = g_ctx.tiledimx;
let gridsizey = g_ctx.tiledimy;
pane.gridgraphics.lineStyle(1, 0x000000, 1);
let index = 0;
for (let i = 0; i < pane.widthpx; i += gridsizex) {
pane.gridgraphics.moveTo(i + pane.fudgex, 0 + pane.fudgey);
pane.gridgraphics.lineTo(i + pane.fudgex, pane.heightpx + pane.fudgey);
pane.gridgraphics.moveTo(i + gridsizex + pane.fudgex, 0 + pane.fudgey);
pane.gridgraphics.lineTo(i + gridsizex + pane.fudgex, pane.heightpx + pane.fudgey);
}
for (let j = 0; j < pane.heightpx; j += gridsizey) {
pane.gridgraphics.moveTo(0 + pane.fudgex, j + gridsizey + pane.fudgey);
pane.gridgraphics.lineTo(pane.widthpx + pane.fudgex, j + gridsizey + pane.fudgey);
pane.gridgraphics.moveTo(0 + pane.fudgex, j + pane.fudgey);
pane.gridgraphics.lineTo(pane.heightpx + pane.fudgex, j + pane.fudgey);
}
if(pane.gridvisible){
pane.container.addChild(pane.gridgraphics);
}
return;
}
if (pane.gridtoggle) {
pane.container.addChild(pane.gridgraphics);
pane.gridvisible = true;
}else{
pane.container.removeChild(pane.gridgraphics);
pane.gridvisible = false;
}
pane.gridtoggle = !pane.gridtoggle;
}
// --
// Variable placement logic Level1
// --
function centerCompositePane(x, y){
var compositepane = document.getElementById("compositepane");
compositepane.scrollLeft = x - (CONFIG.htmlCompositePaneW/2);
compositepane.scrollTop = y - (CONFIG.htmlCompositePaneH/2);
}
function centerLayerPanes(x, y){
// TODO remove magic number pulled from index.html
g_ctx.g_layers.map((l) => {
l.scrollpane.scrollLeft = x - (CONFIG.htmlLayerPaneW/2);
l.scrollpane.scrollTop = y - (CONFIG.htmlLayerPaneH/2);
});
}
function onLevelMouseover(e) {
let x = e.data.global.x;
let y = e.data.global.y;
if(g_ctx.debug_flag2){
console.log("onLevelMouseOver ",this.num);
}
if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {
return;
}
if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {
return;
}
// FIXME test code
if ( g_ctx.spritesheet != null){
let ctile = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);
let ctile2 = new PIXI.AnimatedSprite(g_ctx.spritesheet.animations['row0']);
ctile.animationSpeed = .1;
ctile2.animationSpeed = .1;
ctile.autoUpdate = true;
ctile2.autoUpdate = true;
ctile.alpha = .5;
ctile2.alpha = .5;
ctile.play();
ctile2.play();
this.mouseshadow.addChild(ctile);
g_ctx.composite.mouseshadow.addChild(ctile2);
// FIXME test code
}
else if (this.lasttileindex != g_ctx.tile_index) {
this.mouseshadow.removeChildren(0);
g_ctx.composite.mouseshadow.removeChildren(0);
if (g_ctx.selected_tiles.length == 0) {
let shadowsprite = null;
let shadowsprite2 = null;
let pxloc = tileset_px_from_index(g_ctx.tile_index);
shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
shadowsprite.alpha = .5;
shadowsprite2.alpha = .5;
this.mouseshadow.addChild(shadowsprite);
g_ctx.composite.mouseshadow.addChild(shadowsprite2);
} else {
// TODO! adjust for fudge
for (let i = 0; i < g_ctx.selected_tiles.length; i++) {
let tile = g_ctx.selected_tiles[i];
let pxloc = tileset_px_from_index(tile[2]);
const shadowsprite = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
const shadowsprite2 = sprite_from_px(pxloc[0] + g_ctx.tileset.fudgex, pxloc[1] + g_ctx.tileset.fudgey);
shadowsprite.x = tile[0] * g_ctx.tiledimx;
shadowsprite.y = tile[1] * g_ctx.tiledimx;
shadowsprite2.x = tile[0] * g_ctx.tiledimx;
shadowsprite2.y = tile[1] * g_ctx.tiledimx;
shadowsprite.alpha = .5;
shadowsprite2.alpha = .5;
this.mouseshadow.addChild(shadowsprite);
g_ctx.composite.mouseshadow.addChild(shadowsprite2);
}
}
this.mouseshadow.x = x - 16;
this.mouseshadow.y = y - 16;
this.container.removeChild(this.mouseshadow);
g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);
this.container.addChild(this.mouseshadow);
g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);
}
g_ctx.composite.app.stage.removeChild(g_ctx.composite.circle);
g_ctx.composite.app.stage.addChild(g_ctx.composite.circle);
}
function onLevelMouseOut(e) {
if (g_ctx.debug_flag2) {
console.log("onLevelMouseOut ",this.num);
}
//FIXME there is a funky race condition where the mouse enters a second layer before leaving the last and the following line
//deletes the composite mouseshadow. I'm not quite sure how to solve without mapping the composite.mouseshadow to each layer
this.mouseshadow.removeChildren(0);
g_ctx.composite.mouseshadow.removeChildren();
}
function onLevelMousemove(e) {
let x = e.data.global.x;
let y = e.data.global.y;
// FIXME TEST CODE
this.mouseshadow.x = x-8;
this.mouseshadow.y = y-8;
g_ctx.composite.mouseshadow.x = x-8;
g_ctx.composite.mouseshadow.y = y-8;
// FIXME TEST CODE
if (x < this.scrollpane.scrollLeft || x > this.scrollpane.scrollLeft + CONFIG.htmlCompositePaneW) {
return;
}
if (y < this.scrollpane.scrollTop || y > this.scrollpane.scrollTop + CONFIG.htmlCompositePaneH) {
return;
}
g_ctx.composite.circle.clear();
g_ctx.composite.circle.beginFill(0xe50000, 0.5);
g_ctx.composite.circle.drawCircle(e.data.global.x, e.data.global.y, 3);
g_ctx.composite.circle.endFill();
}
function onCompositeMousedown(layer, e) {
if (g_ctx.debug_flag) {
console.log('onCompositeMouseDown: X', e.data.global.x, 'Y', e.data.global.y);
}
let xorig = e.data.global.x;
let yorig = e.data.global.y;
centerLayerPanes(xorig,yorig);
}
// Place with no variable target at destination
function levelPlaceNoVariable(layer, e) {
if (g_ctx.debug_flag) {
console.log('levelPlaceNoVariable: X', e.data.global.x, 'Y', e.data.global.y);
}
let xorig = e.data.global.x;
let yorig = e.data.global.y;
centerCompositePane(xorig,yorig);
if (g_ctx.dkey || g_ctx.selected_tiles.length == 0) {
let ti = layer.addTileLevelPx(e.data.global.x, e.data.global.y, g_ctx.tile_index);
UNDO.undo_add_single_index_as_task(layer, ti);
} else {
let undolist = [];
UNDO.undo_mark_task_start(layer);
for (let index of g_ctx.selected_tiles) {
let ti = layer.addTileLevelPx(xorig + index[0] * g_ctx.tiledimx, yorig + index[1] * g_ctx.tiledimx, index[2]);
UNDO.undo_add_index_to_task(ti);
}
UNDO.undo_mark_task_end();
}
}
// Listen to pointermove on stage once handle is pressed.
function onLevelPointerDown(layer, e)
{
if (g_ctx.debug_flag) {
console.log("onLevelPointerDown()");
}
layer.app.stage.eventMode = 'static';
layer.app.stage.addEventListener('pointermove', onLevelDrag.bind(null, layer, e));
layer.container.removeChild(layer.mouseshadow);
g_ctx.composite.container.removeChild(g_ctx.composite.mouseshadow);
layer.dragctx.startx = e.data.global.x;
layer.dragctx.starty = e.data.global.y;
layer.dragctx.endx = e.data.global.x;
layer.dragctx.endy = e.data.global.y;
layer.app.stage.addChild(layer.dragctx.square);
layer.app.stage.addChild(layer.dragctx.tooltip);
}
function onLevelDrag(layer, e)
{
if(layer.dragctx.startx == -1){
layer.dragctx.square.clear();
return;
}
layer.dragctx.endx = e.global.x;
layer.dragctx.endy = e.global.y;
if (g_ctx.debug_flag) {
console.log("onLevelDrag()");
}
layer.dragctx.square.clear();
layer.dragctx.square.beginFill(0xFF3300, 0.3);
layer.dragctx.square.lineStyle(2, 0xffd900, 1);
layer.dragctx.square.moveTo(layer.dragctx.startx, layer.dragctx.starty);
layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.starty);
layer.dragctx.square.lineTo(layer.dragctx.endx, layer.dragctx.endy);
layer.dragctx.square.lineTo(layer.dragctx.startx, layer.dragctx.endy);
layer.dragctx.square.closePath();
layer.dragctx.square.endFill();
const vwidth = Math.floor((layer.dragctx.endx - layer.dragctx.startx)/g_ctx.tiledimx);
const vheight = Math.floor((layer.dragctx.endy - layer.dragctx.starty)/g_ctx.tiledimx);
layer.dragctx.tooltip.x = e.global.x + 16;
layer.dragctx.tooltip.y = e.global.y - 4;
layer.dragctx.tooltip.text = "["+vwidth+","+vheight+"]\n"+
"("+Math.floor(e.global.x/g_ctx.tiledimx)+","+Math.floor(e.global.y/g_ctx.tiledimx)+")";
//layer.dragctx.tooltip.text = "("+e.global.x+","+e.global.y+")";
}
// Stop dragging feedback once the handle is released.
function onLevelDragEnd(layer, e)
{
layer.dragctx.endx = e.data.global.x;
layer.dragctx.endy = e.data.global.y;
if(layer.dragctx.startx == -1){
console.log("onLevelDragEnd() start is -1 bailing");
return;
}
if (g_ctx.debug_flag) {
console.log("onLevelDragEnd()");
}
if(layer.dragctx.endx < layer.dragctx.startx){
let tmp = layer.dragctx.endx;
layer.dragctx.endx = layer.dragctx.startx;
layer.dragctx.startx = tmp;
}
if(layer.dragctx.endy < layer.dragctx.starty){
let tmp = layer.dragctx.endy;
layer.dragctx.endy = layer.dragctx.starty;
layer.dragctx.starty = tmp;
}
//FIXME TEST CODE show mouseshadow again once done draggin
layer.container.addChild(layer.mouseshadow);
g_ctx.composite.container.addChild(g_ctx.composite.mouseshadow);
layer.app.stage.eventMode = 'auto';
layer.app.stage.removeChild(layer.dragctx.square);
layer.app.stage.removeChild(layer.dragctx.tooltip);
let starttilex = Math.floor(layer.dragctx.startx / g_ctx.tiledimx);
let starttiley = Math.floor(layer.dragctx.starty / g_ctx.tiledimx);
let endtilex = Math.floor(layer.dragctx.endx / g_ctx.tiledimx);
let endtiley = Math.floor(layer.dragctx.endy / g_ctx.tiledimx);
if (g_ctx.debug_flag) {
console.log("sx ", starttilex, " ex ", endtilex);
console.log("sy ", starttiley, " ey ", endtiley);
}
// no variable placement.
if(starttilex === endtilex && starttiley == endtiley ){
levelPlaceNoVariable(layer, e);
layer.dragctx.startx = -1;
layer.dragctx.endx = -1;
layer.dragctx.starty = -1;
layer.dragctx.endy = -1;
return;
}
if (g_ctx.selected_tiles.length == 0) {
UNDO.undo_mark_task_start(layer);
for (let i = starttilex; i <= endtilex; i++) {
for (let j = starttiley; j <= endtiley; j++) {
let squareindex = (j * g_ctx.tilesettilew) + i;
let ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, g_ctx.tile_index);
UNDO.undo_add_index_to_task(ti);
}
}
UNDO.undo_mark_task_end();
} else {
// figure out selected grid
let selected_grid = Array.from(Array(64), () => new Array(64)); // FIXME ... hope 64x64 is enough
let row = 0;
let column = 0;
let selected_row = g_ctx.selected_tiles[0][1];
// selected_grid[0] = [];
for (let index of g_ctx.selected_tiles) {
// console.log("Selected row ", selected_row, index);
if(index[1] != selected_row){
selected_row = index[1];
row++;
column = 0;
//selected_grid[row] = [];
}
selected_grid[column++][row] = index;
}
// at this point should have a 3D array of the selected tiles and the size should be row, column
UNDO.undo_mark_task_start(layer);
let ti=0;
for (let i = starttilex; i <= endtilex; i++) {
for (let j = starttiley; j <= endtiley; j++) {
let squareindex = (j * g_ctx.tilesettilew) + i;
if (j === starttiley) { // first row
if (i === starttilex) { // top left corner
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[0][0][2]);
}
else if (i == endtilex) { // top right corner
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][0][2]);
} else { // top middle
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[1][0][2]);
}
} else if (j === endtiley) { // last row
if (i === starttilex) { // bottom left corner
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[0][row][2]);
}
else if (i == endtilex) { // bottom right corner
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][row][2]);
} else { // bottom middle
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[1][row][2]);
}
} else { // middle row
if (i === starttilex) { // middle left
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[0][(row > 0)? 1 : 0][2]);
}
else if (i === endtilex) { // middle end
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[column - 1][(row > 0)? 1 : 0][2]);
} else { // middle middle
ti = layer.addTileLevelPx(i * g_ctx.tiledimx, j * g_ctx.tiledimx, selected_grid[1][(row > 0)? 1 : 0][2]);
}
}
UNDO.undo_add_index_to_task(ti);
}
}
UNDO.undo_mark_task_end();
}
layer.dragctx.square.clear();
layer.dragctx.startx = -1;
layer.dragctx.starty = -1;
}
// --
// Initialized all pixi apps / components for application
// --
function initPixiApps() {
// -- Editor wide globals --
// First layer of level
const level_app0 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level0') });
let layer0 = new LayerContext(level_app0, document.getElementById("layer0pane"), 0);
// second layer of level
const level_app1 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level1') });
let layer1 = new LayerContext(level_app1, document.getElementById("layer1pane"), 1);
// object layer of level
const level_app2 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level3') });
let layer2 = new LayerContext(level_app2, document.getElementById("layer2pane"), 2);
// object layer of level
const level_app3 = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('level4') });
let layer3 = new LayerContext(level_app3, document.getElementById("layer3pane"), 3);
g_ctx.g_layer_apps = [];
g_ctx.g_layer_apps.push(level_app0 );
g_ctx.g_layer_apps.push(level_app1);
g_ctx.g_layer_apps.push(level_app2);
g_ctx.g_layer_apps.push(level_app3);
g_ctx.g_layers = [];
g_ctx.g_layers.push(layer0);
g_ctx.g_layers.push(layer1);
g_ctx.g_layers.push(layer2);
g_ctx.g_layers.push(layer3);
// g_ctx.composite view
g_ctx.composite_app = new PIXI.Application({ backgroundAlpha: 0, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('composite') });
g_ctx.composite = new CompositeContext(g_ctx.composite_app);
// map tab
g_ctx.map_app = new PIXI.Application({ backgroundColor: 0x2980b9, width: CONFIG.levelwidth, height: CONFIG.levelheight, view: document.getElementById('mapcanvas') });
// g_ctx.tileset
g_ctx.tileset_app = new PIXI.Application({ width: 5632 , height: 8672, view: document.getElementById('tileset') });
//g_ctx.tileset_app = new PIXI.Application({ width: g_ctx.tilesetpxw, height: g_ctx.tilesetpxh, view: document.getElementById('tileset') });
// const { renderer } = g_ctx.tileset_app;
// // Install the EventSystem
// renderer.addSystem(EventSystem, 'tileevents');
g_ctx.tileset = new TilesetContext(g_ctx.tileset_app);
}
function setGridSize(size) {
if (size == 16) {
if (g_ctx.tiledimx == 16) { return; }
g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));
g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));
g_ctx.tiledimx = 16;
g_ctx.tiledimy = 16;
g_ctx.curtiles = g_ctx.tiles16;
console.log("set to curTiles16");
} else if (size == 32) {
if (g_ctx.tiledimx == 32) { return; }
g_ctx.tilesettilew = (g_ctx.tilesettilew/ (size / g_ctx.tiledimx));
g_ctx.tilesettileh = (g_ctx.tilesettileh / (size / g_ctx.tiledimy));
g_ctx.tiledimx = 32;
g_ctx.tiledimy = 32;
g_ctx.curtiles = g_ctx.tiles32;
console.log("set to curTiles32");
} else {
console.debug("Invalid TileDim!");
return;
}
g_ctx.g_layers.map((l) => redrawGrid (l, true) );
redrawGrid(g_ctx.tileset, true);
redrawGrid(g_ctx.composite, true);
}
function initRadios() {
var rad = document.myForm.radioTiledim;
var prev = null;
for (var i = 0; i < rad.length; i++) {
rad[i].addEventListener('change', function () {
if (this !== prev) {
prev = this;
}
setGridSize(this.value);
});
}
}
// --
// Load in default tileset and use to set properties
// --
function initTilesSync(callme) {
return new Promise((resolve, reject) => {
console.log("initTileSync");
const texture = new PIXI.BaseTexture(g_ctx.tilesetpath);
if(texture.valid) {
console.log("BaseTexture already valid");
callme();
return;
}
console.log("Loading texture ", g_ctx.tilesetpath);
texture.on('loaded', function () {
// size of g_ctx.tileset in px
g_ctx.tilesetpxw = texture.width;
g_ctx.tilesetpxh = texture.height;
console.log("Texture size w:", g_ctx.tilesetpxw, "h:", g_ctx.tilesetpxh);
// size of g_ctx.tileset in tiles
let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding;
let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad);
g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad)) / g_ctx.tiledimx);
let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad);
g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad)) / g_ctx.tiledimx);
console.log("Number of x tiles ", g_ctx.tilesettilew, " y tiles ", g_ctx.tilesettileh);
g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh;
texture.destroy();
resolve();
callme();
});
});
}
// --
// Load default Tileset
// --
const initTilesConfig = async () => {
g_ctx.tilesetpath = CONFIG.DEFAULTTILESETPATH;
return new Promise((resolve, reject) => {
const texture = new PIXI.BaseTexture(g_ctx.tilesetpath);
if (g_ctx.debug_flag) {
console.log("initTilessConfi: Loading texture ",g_ctx.tilesetpath);
}
texture .on('loaded', function() {
// size of g_ctx.tileset in px
g_ctx.tilesetpxw = texture.width;
g_ctx.tilesetpxh = texture.height;
if (g_ctx.debug_flag) {
console.log("\tsize w:", g_ctx.tilesetpxw, "h:", g_ctx.tilesetpxh);
}
// size of g_ctx.tileset in tiles
let tileandpad = g_ctx.tiledimx + CONFIG.tilesetpadding;
let numtilesandpadw = Math.floor(g_ctx.tilesetpxw / tileandpad);
g_ctx.tilesettilew = numtilesandpadw + Math.floor((g_ctx.tilesetpxw - (numtilesandpadw * tileandpad))/g_ctx.tiledimx);
let numtilesandpadh = Math.floor(g_ctx.tilesetpxh / tileandpad);
g_ctx.tilesettileh = numtilesandpadh + Math.floor((g_ctx.tilesetpxh - (numtilesandpadh * tileandpad))/g_ctx.tiledimx);
if (g_ctx.debug_flag) {
console.log("\tnum tiles x ", g_ctx.tilesettilew, " y ", g_ctx.tilesettileh);
}
g_ctx.MAXTILEINDEX = g_ctx.tilesettilew * g_ctx.tilesettileh;
texture.destroy();
resolve();
});
});
};
function initTiles() {
// load g_ctx.tileset into a global array of textures for blitting onto levels
const bt = PIXI.BaseTexture.from(g_ctx.tilesetpath, {
scaleMode: PIXI.SCALE_MODES.NEAREST,
});
for (let x = 0; x < CONFIG.tilesettilewidth; x++) {
for (let y = 0; y < CONFIG.tilesettileheight; y++) {
g_ctx.tiles32[x + y * CONFIG.tilesettilewidth] = new PIXI.Texture(
bt,
new PIXI.Rectangle(x * 32, y * 32, 32, 32),
);
}
}
for (let x = 0; x < CONFIG.tilesettilewidth * 2; x++) {
for (let y = 0; y < CONFIG.tilesettileheight * 2; y++) {
g_ctx.tiles16[x + y * CONFIG.tilesettilewidth * 2] = new PIXI.Texture(
bt,
new PIXI.Rectangle(x * 16, y * 16, 16, 16),
);
}
}
g_ctx.curtiles = g_ctx.tiles32;
}
async function init() {
UI.initMainHTMLWindow();
// We need to load the Tileset to know how to size things. So we block until done.
await initTilesConfig();
initPixiApps();
initRadios();
initTiles();
UI.initLevelLoader(loadMapFromModule);
UI.initCompositePNGLoader();
UI.initSpriteSheetLoader();
UI.initTilesetLoader( loadMapFromModule.bind(null, g_ctx));
}
init();