This commit is contained in:
Felix Baumgärtner 2024-08-02 16:01:51 +02:00
parent 6361a6c539
commit d4a7de346d
7 changed files with 162 additions and 9 deletions

View File

@ -1,5 +1,6 @@
class GameObject { class GameObject {
constructor(config) { constructor(config) {
this.id = null;
this.isMounted = false; this.isMounted = false;
this.x = config.x || 0; this.x = config.x || 0;
this.y = config.y || 0; this.y = config.y || 0;
@ -9,14 +10,42 @@ class GameObject {
src: config.src || "/images/characters/people/hero.png", src: config.src || "/images/characters/people/hero.png",
}); });
this.behaviorLoop = config.behaviorLoop || [];
this.behaviorLoopIndex = 0;
} }
mount(map) { mount(map) {
this.isMounted = true; this.isMounted = true;
map.addWall(this.x, this.y); map.addWall(this.x, this.y);
// when behavior kick off after delay
setTimeout(() => {
this.doBehaviorEvent(map);
}, 10);
} }
update() { update() {
} }
async doBehaviorEvent(map) {
// stop if cutscene is playing
if (map.isCutscenePlaying || this.behaviorLoop.length === 0) {
return;
}
let eventConfig = this.behaviorLoop[this.behaviorLoopIndex];
eventConfig.who = this.id;
const eventHandler = new OverworldEvent({ map, event: eventConfig });
await eventHandler.init();
this.behaviorLoopIndex += 1;
if (this.behaviorLoopIndex === this.behaviorLoop.length) {
this.behaviorLoopIndex = 0;
}
this.doBehaviorEvent(map);
}
} }

View File

@ -22,7 +22,9 @@ class Overworld {
this.map.drawLowerImage(this.ctx, cameraPerson); this.map.drawLowerImage(this.ctx, cameraPerson);
Object.values(this.map.gameObjects).forEach(object => { Object.values(this.map.gameObjects).sort((a,b) => {
return a.y - b.y;
}).forEach(object => {
object.sprite.draw(this.ctx, cameraPerson); object.sprite.draw(this.ctx, cameraPerson);
}) })
@ -43,5 +45,12 @@ class Overworld {
this.directionInput.init(); this.directionInput.init();
this.startGameLoop(); this.startGameLoop();
this.map.startCutscene([
{ who: "hero", type: "walk", direction: "down" },
{ who: "hero", type: "walk", direction: "down" },
{ who: "hero", type: "walk", direction: "down" },
{ who: "npc1", type: "walk", direction: "right" },
])
} }
} }

54
OverworldEvent.js Normal file
View File

@ -0,0 +1,54 @@
class OverworldEvent {
constructor({map, event}) {
this.map = map;
this.event = event;
}
stand(resolve) {
const who = this.map.gameObjects[ this.event.who ];
who.startBehavior({
map: this.map
}, {
type: "stand",
direction: this.event.direction,
time: this.event.time
})
// handler to complete when correct person is done walking
const completeHandler = e => {
if (e.detail.whoId === this.event.who) {
document.removeEventListener("PersonStandComplete", completeHandler);
resolve();
}
}
document.addEventListener("PersonStandComplete", completeHandler)
}
walk(resolve) {
const who = this.map.gameObjects[ this.event.who ];
who.startBehavior({
map: this.map
}, {
type: "walk",
direction: this.event.direction,
retry: true
})
// handler to complete when correct person is done walking
const completeHandler = e => {
if (e.detail.whoId === this.event.who) {
document.removeEventListener("PersonWalkingComplete", completeHandler);
resolve();
}
}
document.addEventListener("PersonWalkingComplete", completeHandler)
}
init() {
return new Promise(resolve => {
this[this.event.type](resolve)
})
}
}

View File

@ -8,6 +8,8 @@ class OverworldMap {
this.upperImage = new Image(); this.upperImage = new Image();
this.upperImage.src = config.upperSrc; this.upperImage.src = config.upperSrc;
this.isCutscenePlaying = false;
} }
drawLowerImage(ctx, cameraPerson) { drawLowerImage(ctx, cameraPerson) {
@ -24,13 +26,29 @@ class OverworldMap {
} }
mountObjects() { mountObjects() {
Object.values(this.gameObjects).forEach(o => { Object.keys(this.gameObjects).forEach(key => {
// todoL determine if this object should actually mount let object = this.gameObjects[key];
object.id = key;
o.mount(this);
object.mount(this);
}) })
} }
async startCutscene(events) {
this.isCutscenePlaying = true;
for (let i = 0; i < events.length; i++) {
const eventHandler = new OverworldEvent({
event: events[i],
map: this,
})
await eventHandler.init();
}
this.isCutscenePlaying = false;
}
addWall(x, y) { addWall(x, y) {
this.walls[`${x},${y}`] = true; this.walls[`${x},${y}`] = true;
} }
@ -56,10 +74,27 @@ window.OverworldMaps = {
x: utils.withGrid(8), x: utils.withGrid(8),
y: utils.withGrid(2), y: utils.withGrid(2),
}), }),
// npc1: new Person({ npc1: new Person({
// x: utils.withGrid(6), x: utils.withGrid(6),
// y: utils.withGrid(7), y: utils.withGrid(5),
// }) behaviorLoop: [
{ type: "stand", direction: "down", time: 800 },
{ type: "stand", direction: "right", time: 300 },
{ type: "stand", direction: "down", time: 400 },
{ type: "stand", direction: "up", time: 700 },
]
}),
npc2: new Person({
x: utils.withGrid(8),
y: utils.withGrid(9),
behaviorLoop: [
{ type: "walk", direction: "left" },
{ type: "stand", direction: "up", time: 800 },
{ type: "walk", direction: "up" },
{ type: "walk", direction: "right" },
{ type: "walk", direction: "down" },
]
})
}, },
walls: { walls: {
// "16,16": true // "16,16": true

View File

@ -21,7 +21,7 @@ class Person extends GameObject {
// more cases for starting to walk come here // more cases for starting to walk come here
// case: keyboard ready and arrow pressed // case: keyboard ready and arrow pressed
if (this.isPlayerControlled && state.arrow) { if (state.map.isCutscenePlaying == false && this.isPlayerControlled && state.arrow) {
this.startBehavior(state, { this.startBehavior(state, {
type: "walk", type: "walk",
direction: state.arrow direction: state.arrow
@ -37,12 +37,24 @@ class Person extends GameObject {
if (behavior.type === "walk") { if (behavior.type === "walk") {
// stop if space is not free // stop if space is not free
if (state.map.isSpaceTaken(this.x, this.y, this.direction)) { if (state.map.isSpaceTaken(this.x, this.y, this.direction)) {
behavior.retry && setTimeout(() => {
this.startBehavior(state, behavior);
}, 10);
return; return;
} }
// ready to walk // ready to walk
state.map.moveWall(this.x, this.y, this.direction); state.map.moveWall(this.x, this.y, this.direction);
this.movementProgressRemaining = 16; this.movementProgressRemaining = 16;
this.updateSprite(state);
}
if (behavior.type = "stand") {
setTimeout(() => {
utils.emitEvent("PersonStandComplete", {
whoId: this.id
})
}, behavior.time);
} }
} }
@ -50,6 +62,12 @@ class Person extends GameObject {
const [property, change] = this.directionUpdate[this.direction]; const [property, change] = this.directionUpdate[this.direction];
this[property] += change; this[property] += change;
this.movementProgressRemaining -= 1; this.movementProgressRemaining -= 1;
if (this.movementProgressRemaining === 0) {
utils.emitEvent("PersonWalkingComplete", {
whoId: this.id
})
}
} }
updateSprite() { updateSprite() {

View File

@ -18,6 +18,7 @@
<script src="/Person.js"></script> <script src="/Person.js"></script>
<script src="/Sprite.js"></script> <script src="/Sprite.js"></script>
<script src="/OverworldMap.js"></script> <script src="/OverworldMap.js"></script>
<script src="/OverworldEvent.js"></script>
<script src="/init.js"></script> <script src="/init.js"></script>
</body> </body>
</html> </html>

View File

@ -21,5 +21,12 @@ const utils = {
} }
return {x, y}; return {x, y};
},
emitEvent(name, detail) {
const event = new CustomEvent(name, {
detail
});
document.dispatchEvent(event);
} }
} }