diff --git a/Airport.vcxproj b/Airport.vcxproj
index 9dbfdac..c9a4598 100644
--- a/Airport.vcxproj
+++ b/Airport.vcxproj
@@ -28,6 +28,7 @@
+
diff --git a/assets/scripts/gameplay/drone.toy b/assets/scripts/gameplay/drone.toy
new file mode 100644
index 0000000..6c31639
--- /dev/null
+++ b/assets/scripts/gameplay/drone.toy
@@ -0,0 +1,186 @@
+import standard;
+import node;
+
+//constants
+var SPEED: int const = 2;
+
+var SPRITE_WIDTH: int const = 64;
+var SPRITE_HEIGHT: int const = 64;
+
+var TILE_WIDTH: int const = 32;
+var TILE_HEIGHT: int const = 32;
+
+
+//variables
+var parent: opaque = null; //cache the parent for quick access
+
+var gridX: int = 1; //position on the game grid
+var gridY: int = 1;
+
+var realX: int = 0; //will change until realX = gridX * SPRITE_WIDTH
+var realY: int = 0;
+
+var motionX: int = 0; //normalized movement direction
+var motionY: int = 0;
+
+var direction: int = null; //BUGFIX: animation not looping properly
+
+var stepAI: int = 0;
+
+//polyfills - animating different cycles on one image
+var stepCount: int = 0;
+
+fn faceDown(node: opaque) {
+ if (direction == 0) return;
+ direction = 0;
+ node.setNodeRect(0, 0, 32, 32);
+ node.setNodeFrames(2);
+}
+
+fn faceUp(node: opaque) {
+ if (direction == 1) return;
+ direction = 1;
+ node.setNodeRect(32 * 2, 0, 32, 32);
+ node.setNodeFrames(2);
+}
+
+fn faceRight(node: opaque) {
+ if (direction == 2) return;
+ direction = 2;
+ node.setNodeRect(32 * 4, 0, 32, 32);
+ node.setNodeFrames(2);
+}
+
+fn faceLeft(node: opaque) {
+ if (direction == 3) return;
+ direction = 3;
+ node.setNodeRect(32 * 6, 0, 32, 32);
+ node.setNodeFrames(2);
+}
+
+
+//accessors & mutators
+fn setGridPos(node: opaque, x: int, y: int) {
+ gridX = x;
+ gridY = y;
+
+ if (realX == null) {
+ realX = gridX * TILE_WIDTH;
+ }
+
+ if (realY == null) {
+ realY = gridY * TILE_HEIGHT;
+ }
+}
+
+fn setRealPos(node: opaque, x: int, y: int) {
+ realX = x;
+ realY = y;
+}
+
+fn getGridPos(node: opaque) {
+ return [gridX, gridY];
+}
+
+fn getRealPos(node: opaque) {
+ return [realX, realY];
+}
+
+
+//lifecycle functions
+fn onLoad(node: opaque) {
+ node.loadTexture("sprites:/drone.png");
+ node.faceDown();
+}
+
+fn onInit(node: opaque) {
+ parent = node.getParentNode();
+}
+
+fn onStep(node: opaque) {
+ if (++stepCount >= 10) {
+ node.incrementCurrentNodeFrame();
+ stepCount = 0;
+ }
+
+ //calc movement
+ var distX = gridX * TILE_WIDTH - realX;
+ var distY = gridY * TILE_HEIGHT - realY;
+
+ motionX = normalize(distX);
+ motionY = normalize(distY);
+
+ //make movement
+ realX += abs(distX) > SPEED ? SPEED * motionX : distX;
+ realY += abs(distY) > SPEED ? SPEED * motionY : distY;
+}
+
+fn onFree(node: opaque) {
+ node.freeTexture();
+}
+
+fn customOnDraw(node: opaque) {
+ var px = 0;
+ var py = 0;
+
+ if (parent != null) {
+ px = parent.callNodeFn("getX");
+ py = parent.callNodeFn("getY");
+
+ px = px != null ? px : 0;
+ py = py != null ? py : 0;
+ }
+
+ node.drawNode(realX + px - SPRITE_WIDTH / 4, realY + py - SPRITE_HEIGHT / 2, SPRITE_WIDTH, SPRITE_HEIGHT);
+}
+
+
+//gameplay functions
+fn runAI(node: opaque, rng: opaque) {
+ if ((stepAI++) >= 1) { //TODO: Toy bug here, something to do with precedence of postfix++?
+ stepAI = 0;
+
+ import random;
+
+ var dir = rng.generateRandomNumber() % 4;
+ var moveX = 0;
+ var moveY = 0;
+
+ if (dir == 0) {
+ moveY += 1;
+ node.faceDown();
+ }
+
+ if (dir == 1) {
+ moveY -= 1;
+ node.faceUp();
+ }
+
+ if (dir == 2) {
+ moveX += 1;
+ node.faceRight();
+ }
+
+ if (dir == 3) {
+ moveX -= 1;
+ node.faceLeft();
+ }
+
+ if (parent.callNodeFn("getCollisionAt", gridX + moveX, gridY + moveY)) {
+ gridX += moveX;
+ gridY += moveY;
+ }
+ }
+}
+
+
+//polyfills - move these to standard
+fn normalize(x): int {
+ if (x > 0) {
+ return 1;
+ }
+ if (x < 0) {
+ return -1;
+ }
+ return 0;
+}
\ No newline at end of file
diff --git a/assets/scripts/gameplay/lejana.toy b/assets/scripts/gameplay/lejana.toy
index 21090be..3ba00d2 100644
--- a/assets/scripts/gameplay/lejana.toy
+++ b/assets/scripts/gameplay/lejana.toy
@@ -17,8 +17,8 @@ var parent: opaque = null; //cache the parent for quick access
var gridX: int = 1; //position on the game grid
var gridY: int = 1;
-var realX: int = 0; //will change until realX = gridX * SPRITE_WIDTH
-var realY: int = 0;
+var realX: int = null; //will change until realX = gridX * SPRITE_WIDTH
+var realY: int = null;
var motionX: int = 0; //normalized movement direction
var motionY: int = 0;
@@ -27,6 +27,7 @@ var inputX: int = 0; //cache the keyboard input
var inputY: int = 0;
var direction: int = null; //BUGFIX: animation not looping properly
+var enableMovementCounter: int = 60 * 4; //BUGFIX: freeze while drones reach their starting spot
//polyfills - animating different cycles on one image
@@ -46,13 +47,6 @@ fn faceUp(node: opaque) {
node.setNodeFrames(4);
}
-fn faceLeft(node: opaque) {
- if (direction == 3) return;
- direction = 3;
- node.setNodeRect(32 * 12, 0, 32, 32);
- node.setNodeFrames(4);
-}
-
fn faceRight(node: opaque) {
if (direction == 2) return;
direction = 2;
@@ -60,15 +54,39 @@ fn faceRight(node: opaque) {
node.setNodeFrames(4);
}
-//polyfills - move these to standard
-fn normalize(x): int {
- if (x > 0) {
- return 1;
+fn faceLeft(node: opaque) {
+ if (direction == 3) return;
+ direction = 3;
+ node.setNodeRect(32 * 12, 0, 32, 32);
+ node.setNodeFrames(4);
+}
+
+
+//accessors & mutators
+fn setGridPos(node: opaque, x: int, y: int) {
+ gridX = x;
+ gridY = y;
+
+ if (realX == null) {
+ realX = gridX * TILE_WIDTH;
}
- if (x < 0) {
- return -1;
+
+ if (realY == null) {
+ realY = gridY * TILE_HEIGHT;
}
- return 0;
+}
+
+fn setRealPos(node: opaque, x: int, y: int) {
+ realX = x;
+ realY = y;
+}
+
+fn getGridPos(node: opaque) {
+ return [gridX, gridY];
+}
+
+fn getRealPos(node: opaque) {
+ return [realX, realY];
}
@@ -83,6 +101,12 @@ fn onInit(node: opaque) {
}
fn onStep(node: opaque) {
+ //initial freeze
+ if (enableMovementCounter > 0) {
+ enableMovementCounter--;
+ return;
+ }
+
//process input when aligned to a grid
if (realX / TILE_WIDTH == gridX && realY / TILE_HEIGHT == gridY && motionX == 0 && motionY == 0) {
//disallow wall phasing
@@ -118,6 +142,11 @@ fn onStep(node: opaque) {
if (inputX < 0) {
node.faceLeft();
}
+
+ //trigger the world
+ if (inputX != 0 || inputY != 0) {
+ parent.callNodeFn("runAI");
+ }
}
//animation
@@ -147,7 +176,7 @@ fn onFree(node: opaque) {
node.freeTexture();
}
-fn onDraw(node: opaque) {
+fn customOnDraw(node: opaque) {
var px = 0;
var py = 0;
@@ -162,6 +191,7 @@ fn onDraw(node: opaque) {
node.drawNode(realX + px - SPRITE_WIDTH / 4, realY + py - SPRITE_HEIGHT / 2, SPRITE_WIDTH, SPRITE_HEIGHT);
}
+
//event functions
fn onKeyDown(node: opaque, event: string) {
if (event == "character_up") {
@@ -205,4 +235,16 @@ fn onKeyUp(node: opaque, event: string) {
inputX = 0;
return;
}
+}
+
+
+//polyfills - move these to standard
+fn normalize(x): int {
+ if (x > 0) {
+ return 1;
+ }
+ if (x < 0) {
+ return -1;
+ }
+ return 0;
}
\ No newline at end of file
diff --git a/assets/scripts/gameplay/scene.toy b/assets/scripts/gameplay/scene.toy
index 3775741..853d70c 100644
--- a/assets/scripts/gameplay/scene.toy
+++ b/assets/scripts/gameplay/scene.toy
@@ -1,11 +1,22 @@
import standard;
import node;
+//consts
+var TILE_WIDTH: int const = 32;
+var TILE_HEIGHT: int const = 32;
+
+
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;
@@ -16,8 +27,7 @@ fn onLoad(node: opaque) {
}
fn onInit(node: opaque) {
- tilemap.callNodeFn("generateFromSeed", clock().hash(), 16, 16);
- collisionMap = tilemap.callNodeFn("getCollisionMap");
+ node.generateLevel(clock().hash(), 16, 16);
}
fn onStep(node: opaque) {
@@ -26,10 +36,18 @@ fn onStep(node: opaque) {
stepCounter = 0;
drawCounter = 0;
}
+
+ entities = entities.sort(depthComparator);
}
fn onDraw(node: opaque) {
drawCounter++;
+
+ //call each child's custom sort fn
+ tilemap.callNodeFn("customOnDraw");
+ for (var i = 0; i < entities.length(); i++) {
+ entities[i].callNodeFn("customOnDraw");
+ }
}
//utils - polyfills
@@ -39,11 +57,78 @@ fn loadChild(parent: opaque, fname: string) {
return child;
}
-//connective functions
+//gameplay functions
+fn generateLevel(node: opaque, seed: int, width: int, height: int) {
+ //reset the array
+ enemies = [];
+ entities = [player]; //assume the player exists already
+
+ import random;
+
+ 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 + 2;
+ 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 / 2) && y < (height / 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) {
+ 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[x][y];
}
+
+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];
+}
\ No newline at end of file
diff --git a/assets/scripts/gameplay/tilemap.toy b/assets/scripts/gameplay/tilemap.toy
index 7490a48..e143283 100644
--- a/assets/scripts/gameplay/tilemap.toy
+++ b/assets/scripts/gameplay/tilemap.toy
@@ -48,7 +48,7 @@ fn onLoad(node: opaque) {
node.loadTexture("sprites:/tileset.png");
}
-fn onDraw(node: opaque) {
+fn customOnDraw(node: opaque) {
if (tilemap == null) {
return;
}
@@ -89,8 +89,8 @@ fn onDraw(node: opaque) {
//utils functions for map generation
-fn generateFromSeed(node: opaque, seed: int, width: int, height: int) {
- rawmap = generateRawTilemap(seed, width, height);
+fn generateFromRng(node: opaque, rng: opaque, width: int, height: int) {
+ rawmap = generateRawTilemap(rng, width, height);
tilemap = bakeTilemap(rawmap, width, height);
collisions = bakeCollisionMap(rawmap, width, height);
@@ -99,9 +99,8 @@ fn generateFromSeed(node: opaque, seed: int, width: int, height: int) {
print collisions;
}
-fn generateRawTilemap(seed: int, width: int, height: int) {
+fn generateRawTilemap(rng: opaque, width: int, height: int) {
import random;
- var rng: opaque = createRandomGenerator(seed);
//generate an empty grid
var result: [[string]] = [];