From 7b45e2d5d71caa0f5d25bee132e5e57e8b774789 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sun, 16 Jul 2023 13:06:16 +1000 Subject: [PATCH] Hey Ike, kick the drone! --- Box | 2 +- assets/scripts/entities/drone.toy | 182 ++++++++++++ assets/scripts/entities/explosion.toy | 79 +++++ assets/scripts/entities/player.toy | 276 ++++++++++++++++++ assets/scripts/scene.toy | 136 ++++++++- assets/scripts/tilemap/generator.toy | 10 +- assets/scripts/tilemap/renderer-child.toy | 2 +- assets/sprites/explosion.png | Bin 0 -> 1173 bytes .../{lejana-mask.png => parvati-mask.png} | Bin assets/sprites/{lejana.png => parvati.png} | Bin 10 files changed, 675 insertions(+), 12 deletions(-) create mode 100644 assets/scripts/entities/drone.toy create mode 100644 assets/scripts/entities/explosion.toy create mode 100644 assets/scripts/entities/player.toy create mode 100644 assets/sprites/explosion.png rename assets/sprites/{lejana-mask.png => parvati-mask.png} (100%) rename assets/sprites/{lejana.png => parvati.png} (100%) diff --git a/Box b/Box index 032ea0d..e240b7a 160000 --- a/Box +++ b/Box @@ -1 +1 @@ -Subproject commit 032ea0d34f1e896d9fd9300287a018f059cd4011 +Subproject commit e240b7ac3fedc1bf912016ca1d6b822db1d6c7e9 diff --git a/assets/scripts/entities/drone.toy b/assets/scripts/entities/drone.toy new file mode 100644 index 0000000..01e222d --- /dev/null +++ b/assets/scripts/entities/drone.toy @@ -0,0 +1,182 @@ +import standard; +import node; +import random; + + +//constants +var TILE_PIXEL_WIDTH: int const = 16; +var TILE_PIXEL_HEIGHT: int const = 16; + +var DIRECTION_DOWN: int const = 0; +var DIRECTION_UP: int const = 1; +var DIRECTION_RIGHT: int const = 2; +var DIRECTION_LEFT: int const = 3; + + +//variables +var gridPositionX: int = null; +var gridPositionY: int = null; + +var direction: int = null; //BUGFIX: animation not looping properly + +var stepAI: int = 0; +var health: int = 2; +var requestFree: bool = false; + +//polyfills - animating different cycles on one image +var walkAnimationCounter: int = 0; + + +//accessors & mutators +fn setGridPosition(node: opaque, x: int, y: int) { + gridPositionX = x; + gridPositionY = y; + + node.setNodePositionX(floor( gridPositionX * TILE_PIXEL_WIDTH / node.getNodeScaleX() )); + node.setNodePositionY(floor( gridPositionY * TILE_PIXEL_HEIGHT / node.getNodeScaleY() )); +} + +fn getGridPositionX(node: opaque) { + return gridPositionX; +} + +fn getGridPositionY(node: opaque) { + return gridPositionY; +} + +fn applyDamage(node: opaque, damage: int) { + health -= damage; +} + +fn isRequestingFree(node: opaque) { + if (requestFree) { + var parent = node.getParentNode(); + + var explosion: opaque = parent.loadChild("scripts:/entities/explosion.toy"); + explosion.callNodeFn("setGridPosition", gridPositionX, gridPositionY); + return true; + } + else { + return false; + } +} + + +//lifecycle functions +fn onLoad(node: opaque) { + node.loadNodeTexture("sprites:/drone.png"); + node.faceDirection(DIRECTION_DOWN); +} + +//fn onUpdate(node: opaque, delta: int) { +// // +//} + +fn onStep(node: opaque) { + if (++walkAnimationCounter >= 12) { + node.incrementCurrentNodeFrame(); + walkAnimationCounter = 0; + } + + //move in realspace + var distX = gridPositionX * TILE_PIXEL_WIDTH - node.getNodePositionX(); + var distY = gridPositionY * TILE_PIXEL_HEIGHT - node.getNodePositionY(); + + node.setNodeMotionX( normalize(distX) ); + node.setNodeMotionY( normalize(distY) ); +} + +fn onFree(node: opaque) { + node.freeNodeTexture(); +} + +fn onDraw(node: opaque) { + var posX: int = node.getNodeWorldPositionX(); + var posY: int = node.getNodeWorldPositionY(); + + var scaleX: float = node.getNodeWorldScaleX(); + var scaleY: float = node.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 = node.getNodeRectW() * int scaleX / 4; + var originOffsetY: int = node.getNodeRectH() * int scaleY / 2; + + node.drawNode( + floor(posX * scaleX) - originOffsetX /* + globalCameraX */, + floor(posY * scaleY) - originOffsetY /* + globalCameraY */, + floor(node.getNodeRectW() * scaleX), + floor(node.getNodeRectH() * scaleY) + ); +} + +//gameplay functions +fn runAI(node: opaque, rng: opaque) { + //If I'm out of health, I need to be removed + if (health <= 0) { + requestFree = true; + return; + } + + //every second call + if (++stepAI >= 2) { + stepAI = 0; + + var dir = rng.generateRandomNumber() % 4; + var moveX = 0; + var moveY = 0; + + if (dir == DIRECTION_DOWN) { + moveY += 1; + node.faceDirection(DIRECTION_DOWN); + } + + else if (dir == DIRECTION_UP) { + moveY -= 1; + node.faceDirection(DIRECTION_UP); + } + + else if (dir == DIRECTION_RIGHT) { + moveX += 1; + node.faceDirection(DIRECTION_RIGHT); + } + + else if (dir == DIRECTION_LEFT) { + moveX -= 1; + node.faceDirection(DIRECTION_LEFT); + } + + if (node.getParentNode().callNodeFn("getWalkableAt", gridPositionX + moveX, gridPositionY + moveY)) { + gridPositionX += moveX; + gridPositionY += moveY; + } + } +} + + +//utils for facing different directions (idling) +fn faceDirection(node: opaque, dir: int) { + if (direction == dir) return; + direction = dir; + node.setNodeRect(32 * 2 * dir, 0, 32, 32); + node.setNodeFrames(2); + walkAnimationCounter = 12; +} + + +//polyfills - move these to standard +fn normalize(x): int { + if (x > 0) { + return 1; + } + if (x < 0) { + return -1; + } + return 0; +} + +fn loadChild(parent: opaque, fname: string) { + //TODO: add this to the API proper + var child: opaque = loadNode(fname); + parent.pushNode(child); + return child; +} \ No newline at end of file diff --git a/assets/scripts/entities/explosion.toy b/assets/scripts/entities/explosion.toy new file mode 100644 index 0000000..3f0d38c --- /dev/null +++ b/assets/scripts/entities/explosion.toy @@ -0,0 +1,79 @@ +import standard; +import node; + + +//constants +var TILE_PIXEL_WIDTH: int const = 16; +var TILE_PIXEL_HEIGHT: int const = 16; + + +//variables +var gridPositionX: int = null; +var gridPositionY: int = null; + +//polyfills - animating different cycles on one image +var walkAnimationCounter: int = 0; +var ticksToDeath: int = 4 * 8 - 1; + + +//accessors & mutators +fn setGridPosition(node: opaque, x: int, y: int) { + gridPositionX = x; + gridPositionY = y; + + node.setNodePositionX(floor( gridPositionX * TILE_PIXEL_WIDTH / node.getNodeScaleX() )); + node.setNodePositionY(floor( gridPositionY * TILE_PIXEL_HEIGHT / node.getNodeScaleY() )); +} + +fn getGridPositionX(node: opaque) { + return gridPositionX; +} + +fn getGridPositionY(node: opaque) { + return gridPositionY; +} + +fn isRequestingFree(node: opaque) { + return ticksToDeath <= 0; +} + + +//lifecycle functions +fn onLoad(node: opaque) { + node.loadNodeTexture("sprites:/explosion.png"); + + node.setNodeRect(0, 0, 32, 32); + node.setNodeFrames(8); +} + +fn onStep(node: opaque) { + if (++walkAnimationCounter >= 4) { + node.incrementCurrentNodeFrame(); + walkAnimationCounter = 0; + } + ticksToDeath--; +} + +fn onFree(node: opaque) { + node.freeNodeTexture(); +} + +fn onDraw(node: opaque) { + var posX: int = node.getNodeWorldPositionX(); + var posY: int = node.getNodeWorldPositionY(); + + var scaleX: float = node.getNodeWorldScaleX(); + var scaleY: float = node.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 = node.getNodeRectW() * int scaleX / 4; + var originOffsetY: int = node.getNodeRectH() * int scaleY / 2; + + node.drawNode( + floor(posX * scaleX) - originOffsetX /* + globalCameraX */, + floor(posY * scaleY) - originOffsetY /* + globalCameraY */, + floor(node.getNodeRectW() * scaleX), + floor(node.getNodeRectH() * scaleY) + ); +} + diff --git a/assets/scripts/entities/player.toy b/assets/scripts/entities/player.toy new file mode 100644 index 0000000..85c3fe3 --- /dev/null +++ b/assets/scripts/entities/player.toy @@ -0,0 +1,276 @@ +import standard; +import node; + + +//constants +var TILE_PIXEL_WIDTH: int const = 16; +var TILE_PIXEL_HEIGHT: int const = 16; + +var DIRECTION_DOWN: int const = 0; +var DIRECTION_UP: int const = 1; +var DIRECTION_RIGHT: int const = 2; +var DIRECTION_LEFT: int const = 3; + + +//position on the game grid +var gridPositionX: int = 1; +var gridPositionY: int = 1; + +//cache the keyboard input +var inputX: int = 0; +var inputY: int = 0; + +var direction: int = null; //BUGFIX: animation not looping properly + +var attackPositionX: int = null; +var attackPositionY: int = null; + +//polyfills - animating different cycles on one image +var walkAnimationCounter: int = 0; +var attackAnimationCounter: int = 0; + + +//accessors & mutators +fn setGridPosition(node: opaque, x: int, y: int) { + gridPositionX = x; + gridPositionY = y; + + node.setNodePositionX(floor( gridPositionX * TILE_PIXEL_WIDTH / node.getNodeScaleX() )); + node.setNodePositionY(floor( gridPositionY * TILE_PIXEL_HEIGHT / node.getNodeScaleY() )); +} + +fn getGridPositionX(node: opaque) { + return gridPositionX; +} + +fn getGridPositionY(node: opaque) { + return gridPositionY; +} + +fn setAttackPosition(node: opaque, x: int, y: int) { + attackPositionX = x; + attackPositionY = y; +} + +fn getAttackPositionX(node: opaque) { + return attackPositionX; +} + +fn getAttackPositionY(node: opaque) { + return attackPositionY; +} + + +//lifecycle functions +fn onLoad(node: opaque) { + node.loadNodeTexture("sprites:/parvati.png"); //NOTE: all of this script is mapped to this sprite sheet + node.faceDirection(DIRECTION_DOWN); +} + +//fn onUpdate(node: opaque, delta: int) { +// // +//} + +fn onStep(node: opaque) { + //animation - start idling + if (attackAnimationCounter == 0) { + //move to standing state, from any other animation state + if (node.getNodeRectY() != 0) { + node.setNodeRect(direction * 32 * 4, 0, 32, 32); + node.setNodeFrames(4); + walkAnimationCounter = 12; + } + } + + //animation - start attacking + if (attackAnimationCounter-- > 0) { + //move to attacking state, from any other animation state + if (node.getNodeRectY() != 32) { + node.setNodeRect(direction * 32 * 4, 32, 32, 32); + node.setNodeFrames(3); + } + + if ((attackAnimationCounter+1) % 4 == 0) { //+1 for a bugfix + node.incrementCurrentNodeFrame(); + } + + //skip out + return; + } + + //make sure you're facing the right direction + if (inputY > 0) { + node.faceDirection(DIRECTION_DOWN); + } + + else if (inputY < 0) { + node.faceDirection(DIRECTION_UP); + } + + else if (inputX > 0) { + node.faceDirection(DIRECTION_RIGHT); + } + + else if (inputX < 0) { + node.faceDirection(DIRECTION_LEFT); + } + + //BUGFIX: for smooth animations? + var lesser = 0; + if ((inputX != 0 || inputY != 0) && attackAnimationCounter == 0) { + lesser = 6; + } + + //actually animate + if (walkAnimationCounter-- < lesser) { + node.incrementCurrentNodeFrame(); + walkAnimationCounter = 12; + } + + var parent = node.getParentNode(); //check for collisions from the parent + + if (abs(inputX) != abs(inputY) && parent.callNodeFn("getWalkableAt", gridPositionX + inputX, gridPositionY + inputY)) { + //calc movement + gridPositionX += inputX; + gridPositionY += inputY; + + parent.callNodeFn("runAI"); + } + + //if you attacked + else if (attackPositionX != null || attackPositionY != null) { + parent.callNodeFn("runAI"); + + attackPositionX = null; + attackPositionY = null; + } + + //move in realspace + var distX = gridPositionX * TILE_PIXEL_WIDTH - node.getNodePositionX(); + var distY = gridPositionY * TILE_PIXEL_HEIGHT - node.getNodePositionY(); + + node.setNodeMotionX( normalize(distX) ); + node.setNodeMotionY( normalize(distY) ); + + //reset input + inputX = 0; + inputY = 0; +} + +fn onFree(node: opaque) { + node.freeNodeTexture(); +} + +fn onDraw(node: opaque) { + var posX: int = node.getNodeWorldPositionX(); + var posY: int = node.getNodeWorldPositionY(); + + var scaleX: float = node.getNodeWorldScaleX(); + var scaleY: float = node.getNodeWorldScaleY(); + + //this offset is because the sprite cell for parvati.png is twice as big as the sprite cell for the floor tiles + var originOffsetX: int = player.getNodeRectW() / 4; + var originOffsetY: int = player.getNodeRectH() / 2; + + node.drawNode( + floor(posX * scaleX) - originOffsetX /* + globalCameraX */, + floor(posY * scaleY) - originOffsetY /* + globalCameraY */, + floor(node.getNodeRectW() * scaleX), + floor(node.getNodeRectH() * scaleY) + ); +} + + +//event functions +fn onKeyDown(node: opaque, event: string) { + //enable attack + if (event == "character_attack" && inputX == 0 && inputY == 0) { + attackAnimationCounter = 12; + + if (direction == DIRECTION_DOWN) { + node.setAttackPosition(gridPositionX, gridPositionY + 1); + } + + else if (direction == DIRECTION_UP) { + node.setAttackPosition(gridPositionX, gridPositionY - 1); + } + + else if (direction == DIRECTION_RIGHT) { + node.setAttackPosition(gridPositionX + 1, gridPositionY); + } + + else if (direction == DIRECTION_LEFT) { + node.setAttackPosition(gridPositionX - 1, gridPositionY); + } + + return; + } + + //if moving, don't take any more input + if (node.getNodeMotionX() != 0 || node.getNodeMotionY() != 0) { + return; + } + + if (event == "character_up") { + inputY -= 1; + return; + } + + if (event == "character_down") { + inputY += 1; + return; + } + + if (event == "character_left") { + inputX -= 1; + return; + } + + if (event == "character_right") { + inputX += 1; + return; + } +} + +fn onKeyUp(node: opaque, event: string) { + if (event == "character_up" && inputY < 0) { + inputY = 0; + return; + } + + if (event == "character_down" && inputY > 0) { + inputY = 0; + return; + } + + if (event == "character_left" && inputX < 0) { + inputX = 0; + return; + } + + if (event == "character_right" && inputX > 0) { + inputX = 0; + return; + } +} + + +//utils for facing different directions (idling) +fn faceDirection(node: opaque, dir: int) { + if (direction == dir) return; + direction = dir; + node.setNodeRect(32 * 4 * dir, 0, 32, 32); + node.setNodeFrames(4); + walkAnimationCounter = 12; +} + +//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/scene.toy b/assets/scripts/scene.toy index b74657b..b9a28ee 100644 --- a/assets/scripts/scene.toy +++ b/assets/scripts/scene.toy @@ -1,14 +1,20 @@ import standard; import random; +import runner; import node; //persistent members of the scene node var rng: opaque = null; -//map data (interleaved) +//map data var tilemap: [int] = null; +var metadata: [[[string: any]]] = null; +var mapGridWidth: int = null; +var mapGridHeight: int = null; +//entities +var player: opaque = null; //lifetime functions fn onInit(node: opaque) { @@ -18,13 +24,27 @@ fn onInit(node: opaque) { } rng = createRandomGenerator(clock().hash()); - //generate the level, filling out the data - generateLevel(rng); + + //generate the level, filling out the entity data + node.generateLevel(rng); //generate the child node to render the map - var child: opaque = node.loadChild("scripts:/tilemap/renderer.toy"); + var renderer: opaque = node.loadChild("scripts:/tilemap/renderer.toy"); + renderer.callNodeFn("setTilemap", tilemap); +} - child.callNodeFn("setTilemap", tilemap); +fn onStep(node: opaque) { + for (var i: int = 0; i < node.getChildNodeCount(); i++) { + var child = node.getChildNode(i); + + if (child != null) { + if (child.callNodeFn("isRequestingFree") == true) { + node.freeChildNode(i); + } + } + } + + node.sortChildrenNode(depthComparator); } fn onFree(node: opaque) { @@ -35,9 +55,7 @@ fn onFree(node: opaque) { //scene loading -fn generateLevel(rng: opaque) { - import runner; - +fn generateLevel(node: opaque, rng: opaque) { //load and run the generator script var generatorScript: opaque = loadScript("scripts:/tilemap/generator.toy"); generatorScript.runScript(); @@ -47,14 +65,114 @@ fn generateLevel(rng: opaque) { //get the resulting blobs of data for processing tilemap = generatorScript.getScriptVar("tilemap"); + metadata = generatorScript.getScriptVar("metadata"); + mapGridWidth = generatorScript.getScriptVar("MAP_GRID_WIDTH"); //cache these for lookups + mapGridHeight = generatorScript.getScriptVar("MAP_GRID_HEIGHT"); - //free + //free the generator generatorScript.freeScript(); + + + //spawn && position the player + player = node.loadChild("scripts:/entities/player.toy"); + + var w: int const = metadata.length(); + var h: int const = metadata[0].length(); + + var room = metadata[rng.generateRandomNumber() % w][rng.generateRandomNumber() % h]; + + player.callNodeFn("setGridPosition", room["doorX"], room["doorY"]); + + + //spawn && position the enemies + for (var i: int = 0; i < 20; /* EMPTY */) { + //get random position within a room random room + var room = metadata[rng.generateRandomNumber() % w][rng.generateRandomNumber() % h]; + + var x = rng.generateRandomNumber() % room["w"]; + var y = rng.generateRandomNumber() % room["h"]; + + if (!node.getWalkableAt(room["x"] + x, room["y"] + y)) { + continue; + } + + var drone: opaque = node.loadChild("scripts:/entities/drone.toy"); + drone.callNodeFn("setGridPosition", room["x"] + x, room["y"] + y); + + //increment here + i++; + } } +//to allow the game to progress +fn runAI(node: opaque) { + //player attacking the drones + var attackPositionX: int = player.callNodeFn("getAttackPositionX"); + var attackPositionY: int = player.callNodeFn("getAttackPositionY"); + + //if the player is attacking... + if (attackPositionX != null && attackPositionY != null) { + //for each child node + for (var i = 0; i < node.getChildNodeCount(); i++) { + var child = node.getChildNode(i); + + //find the target + if (child.callNodeFn("getGridPositionX") == attackPositionX && child.callNodeFn("getGridPositionY") == attackPositionY) { + child.callNodeFn("applyDamage", 1); //just 1 damage for now + } + } + } + + //run normal routines + for (var i: int = 0; i < node.getChildNodeCount(); i++) { + node.getChildNode(i).callNodeFn("runAI", rng); + } +} +//for collisions +fn getWalkableAt(node: opaque, x: int, y: int) { + if (tilemap == null || x == null || y == null) { + return false; + } + + //entities + if (x == player.callNodeFn("getGridPositionX") && y == player.callNodeFn("getGridPositionY")) { + return false; + } + + for (var i = 0; i < node.getChildNodeCount(); i++) { + var child = node.getChildNode(i); + + if (x == child.callNodeFn("getGridPositionX") && y == child.callNodeFn("getGridPositionY")) { + return false; + } + } + + //default + return tilemap[y * mapGridWidth * 3 + x * 3 + 2] == 1; // 1 = walkable +} + + +//for sorting by depth +fn depthComparator(lhs: opaque, rhs: opaque) { + 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; +} //polyfills diff --git a/assets/scripts/tilemap/generator.toy b/assets/scripts/tilemap/generator.toy index 19ab6a3..35ae88b 100644 --- a/assets/scripts/tilemap/generator.toy +++ b/assets/scripts/tilemap/generator.toy @@ -46,10 +46,15 @@ var CELL_HEIGHT: int const = 16; var CELL_COUNT_X: int const = 3; var CELL_COUNT_Y: int const = 3; +var MAP_GRID_WIDTH: int const = CELL_WIDTH * CELL_COUNT_X; +var MAP_GRID_HEIGHT: int const = CELL_HEIGHT * CELL_COUNT_Y; + //raw interleaved tile data var tilemap: [int] = null; +//room metadata for placing entities +var metadata: [[[string: any]]] = null; //public functions fn generateTilemapData(rng: opaque) { @@ -87,7 +92,7 @@ fn generateTilemapData(rng: opaque) { } //generate corridor metadata - var corridorData: any = generateCorridorData(rng); + var corridorData = generateCorridorData(rng); //etch each tile string into the tilemap for (var j: int = 0; j < CELL_COUNT_Y; j++) { @@ -100,6 +105,9 @@ fn generateTilemapData(rng: opaque) { etchCorridors(roomData, corridorData, rng); //TODO: etch the walls with a filter, based on the room's themes + + //save the metadata for later retrieval + metadata = roomData; } fn generateRoomMetadata(rng: opaque, left: int, top: int, width: int, height: int) { diff --git a/assets/scripts/tilemap/renderer-child.toy b/assets/scripts/tilemap/renderer-child.toy index 984d76d..a631d0b 100644 --- a/assets/scripts/tilemap/renderer-child.toy +++ b/assets/scripts/tilemap/renderer-child.toy @@ -3,7 +3,7 @@ import standard; import node; //lifecycle functions -fn onLoad(node: opaque) { +fn onInit(node: opaque) { // node.setNodeScaleX(CAMERA_SCALE_X); // node.setNodeScaleY(CAMERA_SCALE_Y); } diff --git a/assets/sprites/explosion.png b/assets/sprites/explosion.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b5455414b334036d7c08cb7e8cda839a31bde6 GIT binary patch literal 1173 zcmV;G1Zw+Px(Pf0{URCt{2o3TzKF%X8wa5Se>6uI}{0hYH&zfuaG0U?Sgx@Vw3O3z!62jD$I zL4{~e!B^Z38l16rygT0AZ03`YLbCBXk^kQ_;|YLa7=~dOhG7_nVHk#C7?Z{gJpsco z@>#Ez9v|P`KepDf8GvE5Kudsa?_JIJ006LkJ?}J!A9~X=jIwMWYTtYnKy|YzCP)-M z3Xne6rxBp=yK<&7ieVH+Dt^2+=lWcz`04*NDkw>4G+m6xA^=0o`x7J=_-s)or7S7t z9QqQt1g&VpA^<}~llbu({Oe;*O^n4j#*7(&k)*Al$6^GUCV;zcn15^K<3nI6fDt7o z;Ir%c;JMG|O>~S>slgCb^F3@|&ksaj&G$u%kEIY9fcM@z=SK74#x|cR!ik>~e)t+^ z1R5rAb@_eVm$&#_x{|8uS2r8SMHQOj@AHUJG`RQP0|2xYUDx*;{Sz8MO8gw55#$;G zC$i840KOYR?DZURa)rL3&-qK!wKh$1waizJbR_!NLHtkfb5ccW4w%^g>Bha3vYXVCtfZN;Da-rQqZNFq0ixVwLJDe)+vB<&fx+JcVKSe zmENJ7q5%5(DMbrojE+j21hgpxN>hcHXkvm(i4{){w4ghCHQ&Q}we;Nm@9rO6QzVhy z2OwQoV_1M=n1dEmh+tA^4GO#u4@w0^D|Lu*3{#EEdpTD2$pIz5q&q+)Guc!Y5}pR7gBW6S&l!ec7()I6GSc!5|2{5I00000NkvXXu0mjfq$V8> literal 0 HcmV?d00001 diff --git a/assets/sprites/lejana-mask.png b/assets/sprites/parvati-mask.png similarity index 100% rename from assets/sprites/lejana-mask.png rename to assets/sprites/parvati-mask.png diff --git a/assets/sprites/lejana.png b/assets/sprites/parvati.png similarity index 100% rename from assets/sprites/lejana.png rename to assets/sprites/parvati.png