162 lines
4.4 KiB
Plaintext
162 lines
4.4 KiB
Plaintext
import standard;
|
|
import node;
|
|
import random;
|
|
|
|
|
|
//persistent members
|
|
var tilemap: opaque = null;
|
|
var stepCounter: opaque = null;
|
|
var player: opaque = null;
|
|
var enemies: [opaque] = null;
|
|
|
|
var collisionDataCache: [bool] = null;
|
|
|
|
var rng: opaque = null; //TODO: does this have an opaque tag?
|
|
|
|
//lifecycle functions
|
|
fn onInit(node: opaque) {
|
|
//load the map stuff
|
|
tilemap = node.loadChild("scripts:/gameplay/tilemap.toy");
|
|
node.generateScene(clock().hash(), MAP_GRID_WIDTH, MAP_GRID_HEIGHT);
|
|
|
|
//load and initialize the UI (depends on map stuff)
|
|
stepCounter = node.loadChild("scripts:/gameplay/step-counter.toy");
|
|
stepCounter.setNodeRect(0, 0, CAMERA_SCREEN_W, 32);
|
|
stepCounter.callNodeFn("setMaxSteps", 100);
|
|
stepCounter.setNodeLayer(100);
|
|
|
|
//offset the scene node, so it shows the map outside separate from the UI
|
|
node.setNodePositionY(16);
|
|
}
|
|
|
|
fn onFree(node: opaque) {
|
|
//cleanup
|
|
if (rng != null) {
|
|
rng.freeRandomGenerator();
|
|
}
|
|
}
|
|
|
|
//fn onUpdate(node: opaque, delta: int) {
|
|
// //
|
|
//}
|
|
|
|
//fn onStep(node: opaque) {
|
|
// //
|
|
//}
|
|
|
|
fn onDraw(node: opaque) {
|
|
//use the onDraw to sort - skip too many steps
|
|
node.sortChildrenNode(depthComparator);
|
|
|
|
var scaleX: float = player.getNodeWorldScaleX();
|
|
var scaleY: float = player.getNodeWorldScaleY();
|
|
|
|
//this offset is because the sprite cell for lejana is twice as big as the sprite cell for the floor tiles
|
|
var originOffsetX: int = player.getNodeRectW() / 2;
|
|
var originOffsetY: int = player.getNodeRectH() / 2;
|
|
|
|
//use the room scene's onDraw to set the global camera position
|
|
globalCameraX = floor((CAMERA_SCREEN_W / 2) - (player.getNodeWorldPositionX() * scaleX)) - originOffsetX;
|
|
globalCameraY = floor((CAMERA_SCREEN_H / 2) - (player.getNodeWorldPositionY() * scaleY)) - originOffsetY;
|
|
}
|
|
|
|
//gameplay functions
|
|
fn generateScene(node: opaque, seed: int, width: int, height: int) {
|
|
//reset the array
|
|
enemies = [];
|
|
|
|
if (rng != null) {
|
|
rng.freeRandomGenerator();
|
|
}
|
|
rng = createRandomGenerator(seed);
|
|
|
|
//generate map
|
|
tilemap.callNodeFn("generateFromRng", rng, width, height);
|
|
collisionDataCache = tilemap.callNodeFn("getCollisionData");
|
|
|
|
//place player here, so drop dropping can avoid the player
|
|
player = node.loadChild("scripts:/gameplay/lejana.toy");
|
|
player.callNodeFn("setGridPosition", 1, 1);
|
|
|
|
//generate drones
|
|
var droneCount: int const = rng.generateRandomNumber() % 2 + 20;
|
|
for (var i = 0; i < droneCount; i++) {
|
|
var d: opaque = 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.getWalkableAt(x, y) == false || x < width / 6 * 2 && y < height / 6 * 2) {
|
|
x = rng.generateRandomNumber() % width;
|
|
y = rng.generateRandomNumber() % height;
|
|
}
|
|
|
|
d.callNodeFn("setGridPosition", x, y);
|
|
d.setNodePositionX(MAP_GRID_WIDTH * TILE_PIXEL_WIDTH);
|
|
d.setNodePositionY(MAP_GRID_HEIGHT * TILE_PIXEL_HEIGHT);
|
|
}
|
|
}
|
|
|
|
fn getWalkableAt(node: opaque, x: int, y: int) {
|
|
if (collisionDataCache == null || x == null || y == null) {
|
|
return false;
|
|
}
|
|
|
|
//entities
|
|
if (x == player.callNodeFn("getGridPositionX") && y == player.callNodeFn("getGridPositionY")) {
|
|
return false;
|
|
}
|
|
|
|
for (var i = 0; i < enemies.length(); i++) {
|
|
if (x == enemies[i].callNodeFn("getGridPositionX") && y == enemies[i].callNodeFn("getGridPositionY")) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
//outside the grid
|
|
if (y * MAP_GRID_WIDTH + x < 0 || y * MAP_GRID_WIDTH + x >= collisionDataCache.length()) {
|
|
return false;
|
|
}
|
|
|
|
//default
|
|
return collisionDataCache[y * MAP_GRID_WIDTH + x];
|
|
}
|
|
|
|
fn runAI(node: opaque) {
|
|
for (var i = 0; i < enemies.length(); i++) {
|
|
enemies[i].callNodeFn("runAI", rng);
|
|
}
|
|
|
|
stepCounter.callNodeFn("alterRemainingSteps", -1);
|
|
}
|
|
|
|
//utils - polyfills
|
|
fn loadChild(parent: opaque, fname: string) {
|
|
//TODO: add this to the API proper
|
|
var child: opaque = loadNode(fname);
|
|
parent.pushNode(child);
|
|
return child;
|
|
}
|
|
|
|
fn depthComparator(lhs: opaque, rhs: opaque) { //for sorting by depth
|
|
var lhsPositionY = lhs.getNodeWorldPositionY();
|
|
var rhsPositionY = rhs.getNodeWorldPositionY();
|
|
|
|
if (lhsPositionY == null || rhsPositionY == null) { //BUGFIX: children without that function
|
|
return true;
|
|
}
|
|
|
|
if (lhsPositionY == rhsPositionY) { //BUGFIX: prevent z-fighting
|
|
var lhsPositionX = lhs.getNodeWorldPositionX();
|
|
var rhsPositionX = rhs.getNodeWorldPositionX();
|
|
|
|
return lhsPositionX < rhsPositionX;
|
|
}
|
|
|
|
return lhsPositionY < rhsPositionY;
|
|
}
|