import standard; import node; import random; //consts var TILE_WIDTH: int const = 32; var TILE_HEIGHT: int const = 32; var MAP_WIDTH: int const = 16; var MAP_HEIGHT: int const = 16; var CAMERA_X: int const = 0; var CAMERA_Y: int const = 32; var tilemap: opaque = null; var player: opaque = null; var stepCounter: opaque = null; var enemies: [opaque] = []; var collisionMap: [bool] = null; //cache this, since it won't change during a level var rng: opaque = null; //lifecycle functions fn onLoad(node: opaque) { var empty = node.loadChild("scripts:/empty.toy"); tilemap = empty.loadChild("scripts:/gameplay/tilemap.toy"); stepCounter = empty.loadChild("scripts:/gameplay/step-counter.toy"); player = node.loadChild("scripts:/gameplay/lejana.toy"); } fn onInit(node: opaque) { node.generateLevel(clock().hash(), MAP_WIDTH, MAP_HEIGHT); stepCounter.callNodeFn("setMaxSteps", 100); } fn onFree(node: opaque) { rng.freeRandomGenerator(); } //fn onStep(node: opaque) { // // //} fn onDraw(node: opaque) { node.sortChildrenNode(depthComparator); stepCounter.callNodeFn("customOnDraw", 0, 0, TILE_WIDTH * MAP_WIDTH, 32); } //utils - polyfills fn loadChild(parent: opaque, fname: string) { var child: opaque = loadNode(fname); parent.pushNode(child); return child; } //gameplay functions fn generateLevel(node: opaque, seed: int, width: int, height: int) { //reset the array enemies = []; if (rng != null) rng.freeRandomGenerator(); rng = createRandomGenerator(seed); //place player player.callNodeFn("setGridPos", 1, 1); //generate map tilemap.callNodeFn("generateFromRng", rng, 16, 16); collisionMap = tilemap.callNodeFn("getCollisionMap"); //generate drones var droneCount: int const = rng.generateRandomNumber() % 2 + 20; for (var i = 0; i < droneCount; i++) { var d = node.loadChild("scripts:/gameplay/drone.toy"); d.initNode(); enemies.push(d); var x = 0; var y = 0; //while generated spot is a collision, or too close to the player while (x == 0 && y == 0 || node.getCollisionAt(x, y) == false || x < width / 6 * 2 && y < height / 6 * 2) { x = rng.generateRandomNumber() % width; y = rng.generateRandomNumber() % height; } d.callNodeFn("setGridPos", x, y); d.callNodeFn("setRealPos", (width -1) * TILE_WIDTH, (height - 1) * TILE_HEIGHT); } } fn getCollisionAt(node: opaque, x: int, y: int) { if (collisionMap == null || x == null || y == null) { return false; } //entities var pos = player.callNodeFn("getGridPos"); if (x == pos[0] && y == pos[1]) { return false; } for (var i = 0; i < enemies.length(); i++) { var pos = enemies[i].callNodeFn("getGridPos"); if (x == pos[0] && y == pos[1]) { return false; } } //default return collisionMap[y * MAP_WIDTH + x]; } fn runAI(node: opaque) { for (var i = 0; i < enemies.length(); i++) { enemies[i].callNodeFn("runAI", rng); } stepCounter.callNodeFn("alterRemainingSteps", -1); } fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth var lhsPos = lhs.callNodeFn("getRealPos"); var rhsPos = rhs.callNodeFn("getRealPos"); if (lhsPos == null || rhsPos == null) { //BUGFIX: children without that function return true; } if (lhsPos[1] == rhsPos[1]) { //BUGFIX: prevent z-fighting return lhsPos[0] <= rhsPos[0]; } return lhsPos[1] < rhsPos[1]; } fn getCameraPos(node: opaque) { return [CAMERA_X, CAMERA_Y]; }