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 tilemap: opaque = null; var player: opaque = null; var enemies: [opaque] = []; var entities: [opaque] = null; //full list of entities var collisionMap: [bool] = null; //cache this, since it won't change during a level var rng: opaque = null; //debugging tools var stepCounter: int = 0; var drawCounter: int = 0; var textNode: opaque = null; //lifecycle functions fn onLoad(node: opaque) { tilemap = node.loadChild("scripts:/gameplay/tilemap.toy"); player = node.loadChild("scripts:/gameplay/lejana.toy"); textNode = node.loadChild("scripts:/empty.toy"); } fn onInit(node: opaque) { node.generateLevel(clock().hash(), MAP_WIDTH, MAP_HEIGHT); //debugging // textNode.setNodeText("fonts:/alphbeta.ttf", 48, "Hello world", 128, 0, 0, 255); textNode.setNodeText("fonts:/Ancient God.ttf", 48, "Hello world", 128, 0, 0, 255); } fn onFree(node: opaque) { rng.freeRandomGenerator(); } fn onStep(node: opaque) { if (++stepCounter >= 30) { print "FPS: " + string drawCounter + " / 30"; stepCounter = 0; drawCounter = 0; } entities = entities.sort(depthComparator); } fn onDraw(node: opaque) { drawCounter++; //call each child's custom draw fn tilemap.callNodeFn("customOnDraw"); for (var i = 0; i < entities.length(); i++) { entities[i].callNodeFn("customOnDraw"); } } //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 = []; entities = [player]; //assume the player exists already 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); entities.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))) { //TODO: toy bug - no short-circuiting? 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); } } fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth var lhsPos = lhs.callNodeFn("getRealPos"); var rhsPos = rhs.callNodeFn("getRealPos"); return lhsPos[1] < rhsPos[1]; }