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 {
constructor(config) {
this.id = null;
this.isMounted = false;
this.x = config.x || 0;
this.y = config.y || 0;
@ -9,14 +10,42 @@ class GameObject {
src: config.src || "/images/characters/people/hero.png",
});
this.behaviorLoop = config.behaviorLoop || [];
this.behaviorLoopIndex = 0;
}
mount(map) {
this.isMounted = true;
map.addWall(this.x, this.y);
// when behavior kick off after delay
setTimeout(() => {
this.doBehaviorEvent(map);
}, 10);
}
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);
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);
})
@ -43,5 +45,12 @@ class Overworld {
this.directionInput.init();
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.src = config.upperSrc;
this.isCutscenePlaying = false;
}
drawLowerImage(ctx, cameraPerson) {
@ -24,13 +26,29 @@ class OverworldMap {
}
mountObjects() {
Object.values(this.gameObjects).forEach(o => {
// todoL determine if this object should actually mount
Object.keys(this.gameObjects).forEach(key => {
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) {
this.walls[`${x},${y}`] = true;
}
@ -56,10 +74,27 @@ window.OverworldMaps = {
x: utils.withGrid(8),
y: utils.withGrid(2),
}),
// npc1: new Person({
// x: utils.withGrid(6),
// y: utils.withGrid(7),
// })
npc1: new Person({
x: utils.withGrid(6),
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: {
// "16,16": true

View File

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

View File

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

View File

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