feat: Implement NN Dungeon with new zones, player start positions, heart upgrades, and Fat Hawk quest

- Added gulchExit and bossSave zones in dungeon map with proper object IDs
- Implemented player start position handling for NN Dungeon (startNNX/startNNY)
- Added heart upgrade system with visual feedback and inventory tracking
- Implemented Fat Hawk quest with animated tied hawk, proEdgeKey collection, and scene transition
- Updated game scaling to FIT mode with black background and centered display
- Enhanced global state management for dungeon start positions
This commit is contained in:
Brian Fertig 2025-07-31 20:25:12 -06:00
parent 6680df14f8
commit 74f1ac53bb
10 changed files with 196 additions and 73 deletions

View File

@ -262,13 +262,13 @@
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 109, 0, 0, 0, 0, 109, 0, 0, 0, 0, 0,
0, 10, 0, 0, 69, 119, 0, 0, 0, 0, 119, 69, 50, 0, 10, 0,
0, 30, 0, 60, 0, 0, 0, 0, 0, 80, 80, 0, 0, 0, 0, 0,
0, 0, 0, 0, 10, 0, 0, 100, 0, 0, 0, 10, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 80, 0, 0,
0, 0, 60, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 83, 84, 0, 0, 0, 0, 90, 0, 0, 0, 83, 84, 0, 0,
@ -285,12 +285,12 @@
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 90, 0,
0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 90, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -306,14 +306,14 @@
},
{
"data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 83, 84, 0, 0, 0, 83, 84, 0,
0, 0, 50, 0, 0, 0, 0, 0, 93, 94, 85, 86, 87, 93, 94, 0,
0, 10, 10, 10, 80, 0, 0, 0, 0, 2147483678, 95, 96, 97, 30, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0,
0, 10, 10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, 0,
0, 0, 70, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 139, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 0, 50, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 130, 0, 20, 0, 0,
@ -963,7 +963,29 @@
"draworder":"topdown",
"id":4,
"name":"zones",
"objects":[],
"objects":[
{
"height":100,
"id":28,
"name":"gulchExit",
"rotation":0,
"type":"",
"visible":true,
"width":200,
"x":2300,
"y":3500
},
{
"height":100,
"id":29,
"name":"bossSave",
"rotation":0,
"type":"",
"visible":true,
"width":200,
"x":2300,
"y":900
}],
"opacity":1,
"type":"objectgroup",
"visible":true,
@ -1017,7 +1039,7 @@
"y":0
}],
"nextlayerid":6,
"nextobjectid":28,
"nextobjectid":30,
"orientation":"orthogonal",
"renderorder":"right-down",
"tiledversion":"1.11.2",
@ -1103,8 +1125,8 @@
"properties":[
{
"name":"collides",
"type":"string",
"value":"true"
"type":"bool",
"value":true
}]
},
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 236 KiB

BIN
assets/proEdgeKey.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 KiB

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<title>Legends of Coyote Gulch</title>
<style>
body { margin: 0; }
body { margin: 0; background-color: black; }
canvas { display: block; }
</style>
</head>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="16" height="9" tilewidth="100" tileheight="100" infinite="1" nextlayerid="6" nextobjectid="28">
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="16" height="9" tilewidth="100" tileheight="100" infinite="1" nextlayerid="6" nextobjectid="30">
<editorsettings>
<export target="../assets/NNDungeon.json" format="json"/>
</editorsettings>
@ -42,7 +42,7 @@
</tile>
<tile id="9">
<properties>
<property name="collides" value="true"/>
<property name="collides" type="bool" value="true"/>
</properties>
</tile>
<tile id="10">
@ -576,13 +576,13 @@
</chunk>
<chunk x="16" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,109,0,0,0,0,109,0,0,0,0,0,
0,10,0,0,69,119,0,0,0,0,119,69,50,0,10,0,
0,30,0,60,0,0,0,0,0,80,80,0,0,0,0,0,
0,0,0,0,10,0,0,100,0,0,0,10,0,0,0,0,
0,0,0,0,0,0,0,80,0,0,0,0,0,80,0,0,
0,0,60,0,0,0,0,0,0,70,0,0,0,0,30,0,
0,10,0,0,0,0,0,0,0,0,0,0,0,0,10,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,83,84,0,0,0,0,90,0,0,0,83,84,0,0,
@ -595,12 +595,12 @@
<chunk x="32" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,80,0,0,0,0,0,0,
0,0,0,70,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,70,90,0,
0,0,0,0,0,0,70,0,0,0,0,0,0,0,90,0,
0,0,0,0,0,0,0,0,0,0,0,80,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,80,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -612,14 +612,14 @@
</chunk>
<chunk x="48" y="0" width="16" height="16">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,83,84,0,0,0,83,84,0,
0,0,50,0,0,0,0,0,93,94,85,86,87,93,94,0,
0,10,10,10,80,0,0,0,0,2147483678,95,96,97,30,0,0,
0,0,0,0,0,0,0,0,70,0,0,0,0,0,0,0,
0,10,10,10,0,0,0,0,0,0,0,0,0,129,0,0,
0,0,70,0,0,80,0,0,0,0,0,0,0,139,0,0,
0,0,0,0,0,0,0,0,0,0,0,150,0,50,0,0,
0,0,0,0,0,0,0,0,0,0,0,160,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,120,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,120,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,130,0,20,0,0,
@ -862,7 +862,10 @@
</properties>
</object>
</objectgroup>
<objectgroup id="4" name="zones"/>
<objectgroup id="4" name="zones">
<object id="28" name="gulchExit" x="2300" y="3500" width="200" height="100"/>
<object id="29" name="bossSave" x="2300" y="900" width="200" height="100"/>
</objectgroup>
<objectgroup id="5" name="interactive">
<object id="19" name="key" gid="110" x="198" y="3036" width="100" height="100">
<properties>

View File

@ -15,14 +15,14 @@
},
"NNDungeon.tmx": {
"expandedObjectLayers": [
3,
5
5,
3
],
"scale": 0.5,
"selectedLayer": 1,
"selectedLayer": 3,
"viewCenter": {
"x": 3252,
"y": 2399
"x": 2689,
"y": 2916
}
},
"NNDungeon.tmx#99boy-tiles": {
@ -49,8 +49,8 @@
"scale": 0.5,
"selectedLayer": 3,
"viewCenter": {
"x": 5589,
"y": 1076
"x": 4191,
"y": 920
}
},
"gulch.tmx#99boy-tiles": {
@ -76,7 +76,7 @@
"gulch.tmx"
],
"project": "Tile-Project-Legends.tiled-project",
"property.type": "int",
"property.type": "bool",
"recentFiles": [
"NNDungeon.tmx",
"gulch.tmx",

Binary file not shown.

View File

@ -8,6 +8,8 @@ export class GlobalState {
upgrades: [],
startGulchX: 950,
startGulchY: 4250,
startNNX: 2400,
startNNY: 3350,
};
}
@ -86,6 +88,22 @@ export class GlobalState {
this.playerData.startGulchY = value;
}
set startNNX(value) {
this.playerData.startNNX = value;
}
get startNNX() {
return this.playerData.startNNX;
}
set startNNY(value) {
this.playerData.startNNY = value;
}
get startNNY() {
return this.playerData.startNNY;
}
// Add safe score increment method (replaces manual score updates)
addScore(amount) {
if (amount < 0) throw new Error("Cannot add negative score");

View File

@ -6,9 +6,11 @@ import { GlobalState } from './globalState.js';
const config = {
type: Phaser.AUTO,
scale: {
mode: Phaser.Scale.EXPAND,
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
width: 1600,
height: 900,
parent: 'game-container'
},
physics: {
default: 'arcade',
@ -18,10 +20,11 @@ const config = {
}
},
backgroundColor: '#000000',
scene: [
MenuScene,
Gulch,
NNDungeon
NNDungeon,
]
};

View File

@ -21,6 +21,8 @@ export class NNDungeon extends Phaser.Scene {
this.load.image('dungeon-tiles', 'assets/dungeon-tiles.png');
this.load.image('heart-full', 'assets/heart-full.png');
this.load.image('heart-empty', 'assets/heart-empty.png');
this.load.image('heart-upgrade', 'assets/heart-upgrade.png');
this.load.image('proEdgeKey', 'assets/proEdgeKey.png');
this.load.tilemapTiledJSON('NNDungeonMap', 'assets/NNDungeon.json');
this.load.spritesheet('player-tiles', 'assets/player-tiles.png', {
frameWidth: 100,
@ -60,23 +62,31 @@ export class NNDungeon extends Phaser.Scene {
.setCollisionByProperty({ collides: true });
const enemiesLayer = NNDungeonMap.getObjectLayer('enemies');
const interactiveLayer = NNDungeonMap.getObjectLayer('interactive');
//const zoneLayer = gulchMap.getObjectLayer('zone');
const zoneLayer = NNDungeonMap.getObjectLayer('zones');
// Show Score
this.interface = new Interface(this);
this.interface.showScore();
// Add a player
this.player = new Player(this, 2400, 3450);
this.player = new Player(this, this.game.globalState.startNNX, this.game.globalState.startNNY);
//this.player = new Player(this, 3550, 350);
this.player.healthBars(true, 1, 3);
// zoneLayer.objects.forEach(object => {
// if (object.name === '99Dungeon') {
// const NNDungeon = this.add.rectangle(object.x, object.y, object.width, object.height);
// this.NNDungeon = this.physics.add.existing(NNDungeon);
// }
// });
// Set the exit back to the gulch
this.game.globalState.startGulchX = 5500;
this.game.globalState.startGulchY = 1600;
zoneLayer.objects.forEach(object => {
if (object.name === 'gulchExit') {
const Gulch = this.add.rectangle(object.x, object.y, object.width, object.height);
this.Gulch = this.physics.add.existing(Gulch);
}
if (object.name === 'bossSave') {
const bossSave = this.add.rectangle(object.x, object.y, object.width, object.height);
this.bossSave = this.physics.add.existing(bossSave);
}
});
// Add Interactive
this.interactive = this.physics.add.group();
@ -100,7 +110,6 @@ export class NNDungeon extends Phaser.Scene {
});
enemiesLayer.objects.forEach(object => {
let a99Boy;
console.log(object.gid);
if (object.gid > 300) {
a99Boy = this.enemies.create(object.x-50, object.y-50, 'boss-tiles', object.gid-301);
} else {
@ -165,9 +174,16 @@ export class NNDungeon extends Phaser.Scene {
this.physics.add.collider(this.player, this.enemies, (player, enemy) => {
player.takeDamage();
});
// this.physics.add.collider(this.player, this.NNDungeon, (player, zone) => {
// console.log('Hit');
// });
this.physics.add.collider(this.player, this.Gulch, (player, zone) => {
this.game.globalState.startNNX = 2400;
this.game.globalState.startNNY = 3350;
this.bgMusic.stop();
this.scene.start('Gulch');
});
this.physics.add.collider(this.player, this.bossSave, (player, zone) => {
this.game.globalState.startNNX = 2350;
this.game.globalState.startNNY = 550;
});
function touchInteractive(player, interactive) {
interactive.properties.forEach(prop => {
@ -213,6 +229,67 @@ export class NNDungeon extends Phaser.Scene {
heart.destroy();
});
// Add level heart upgrade
if (this.game.globalState.hasInventory('99-heart') === 0) {
this.upgrade = this.physics.add.image(5600, 500, 'heart-upgrade').setScale(0.2);
this.physics.add.collider(this.player, this.upgrade, (player, upgrade) => {
this.game.globalState.addToInventory('99-heart', 1);
this.player.maxHearts ++;
this.player.numHearts ++;
this.game.globalState.maxHearts = this.player.maxHearts;
this.game.globalState.hearts = this.player.numHearts;
this.player.updateHealth();
upgrade.destroy();
});
}
// Add Fat Hawk
if (this.game.globalState.hasInventory('fatHawk') === 0) {
this.tiedHawk = this.physics.add.sprite(5950, 300, 'player-tiles', 51);
this.fatHawk = this.physics.add.collider(this.player, this.tiedHawk, (player, upgrade) => {
this.fatHawk.destroy();
this.anims.create({
key: 'fatFly',
frames: this.anims.generateFrameNumbers('player-tiles', { start: 48, end: 50 }),
frameRate: 5,
repeat: -1
});
this.tiedHawk.setFrame(48);
this.tiedHawk.anims.play('fatFly');
// Animate the tiedHawk to move to position (5150, 500)
this.tweens.add({
targets: this.tiedHawk,
x: 6150,
y: 500,
duration: 3000,
ease: 'Linear',
onComplete: () => {
this.tiedHawk.anims.stop();
this.tiedHawk.setVelocity(0, 0);
this.proEdgeKey = this. physics.add.image(6150, 500, 'proEdgeKey').setScale(.1);
this.tweens.add({
targets: this.proEdgeKey,
x: 5950,
y: 650,
duration: 1500,
ease: 'Bounce'
});
this.keyCollider = this.physics.add.collider(this.player, this.proEdgeKey, (player, key) => {
this.keyCollider.destroy();
this.proEdgeKey.destroy();
this.game.globalState.addToInventory('fatHawk', 1);
this.game.globalState.addToInventory('proEdgeKey', 1);
this.cameras.main.fade(1500, 0, 0, 0, true);
this.time.delayedCall(1500, () => {
this.bgMusic.stop();
this.scene.start('Gulch');
});
});
}
});
});
}
//this.cameras.main.setScroll(800, 1100);
this.cameras.main.setBounds(1600, 2700, 1600, 900);