added cutscene behaviors
added changing maps all done like this https://www.youtube.com/watch?v=U7fYOnedjzs&list=PLcjhmZ8oLT0r9dSiIK6RB_PuBWlG1KSq_&index=9
This commit is contained in:
		
							parent
							
								
									b42eb143f0
								
							
						
					
					
						commit
						90ff4fe2ff
					
				| @ -13,6 +13,8 @@ class GameObject { | ||||
| 
 | ||||
| 		this.behaviorLoop = config.behaviorLoop || []; | ||||
| 		this.behaviorLoopIndex = 0; | ||||
| 
 | ||||
| 		this.talking = config.talking || []; | ||||
| 	} | ||||
| 
 | ||||
| 	mount(map) { | ||||
|  | ||||
							
								
								
									
										26
									
								
								KeyPressListener.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								KeyPressListener.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | ||||
| class KeyPressListener { | ||||
| 	constructor(keyCode, callback) { | ||||
| 		let keySafe = true; | ||||
| 		this.keydownFunction = function(event) { | ||||
| 			if (event.code === keyCode) { | ||||
| 				if (keySafe) { | ||||
| 					keySafe = false; | ||||
| 					callback(); | ||||
| 				} | ||||
| 			} | ||||
| 		}; | ||||
| 		this.keyupFunction = function(event) { | ||||
| 			if (event.code === keyCode) { | ||||
| 				keySafe = true; | ||||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		document.addEventListener("keydown", this.keydownFunction); | ||||
| 		document.addEventListener("keyup", this.keyupFunction); | ||||
| 	} | ||||
| 
 | ||||
| 	unbind() { | ||||
| 		document.removeEventListener("keydown", this.keydownFunction); | ||||
| 		document.removeEventListener("keyup", this.keyupFunction); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										30
									
								
								Overworld.js
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								Overworld.js
									
									
									
									
									
								
							| @ -37,9 +37,33 @@ class Overworld { | ||||
| 		step(); | ||||
| 	} | ||||
| 
 | ||||
| 	init() { | ||||
| 		this.map = new OverworldMap(window.OverworldMaps.DemoRoom); | ||||
| 	bindActionInput() { | ||||
| 		new KeyPressListener("Enter", () => { | ||||
| 			// check if there is a person to talk to
 | ||||
| 			this.map.checkForActionCutscene(); | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	bindHeroPositionCheck() { | ||||
| 		document.addEventListener("PersonWalkingComplete", e => { | ||||
| 			if (e.detail.whoId === "hero") { | ||||
| 				// heros position changed
 | ||||
| 				this.map.checkForFootstepCutscene() | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	startMap(mapConfig) { | ||||
| 		this.map = new OverworldMap(mapConfig); | ||||
| 		this.map.overworld = this; | ||||
| 		this.map.mountObjects(); | ||||
| 	} | ||||
| 
 | ||||
| 	init() { | ||||
| 		this.startMap(window.OverworldMaps.DemoRoom); | ||||
| 
 | ||||
| 		this.bindActionInput(); | ||||
| 		this.bindHeroPositionCheck(); | ||||
| 
 | ||||
| 		this.directionInput = new DirectionInput(); | ||||
| 		this.directionInput.init(); | ||||
| @ -51,6 +75,8 @@ class Overworld { | ||||
| 			{ who: "hero", type: "walk", direction: "down" }, | ||||
| 			{ who: "hero", type: "walk", direction: "down" }, | ||||
| 			{ who: "npc1", type: "walk", direction: "right" }, | ||||
| 			{ who: "hero", type: "stand", direction: "left", time: 200 }, | ||||
| 			{ type: "textMessage", text: "Hi!" }, | ||||
| 		]) | ||||
| 	} | ||||
| } | ||||
| @ -46,6 +46,24 @@ class OverworldEvent { | ||||
| 		document.addEventListener("PersonWalkingComplete", completeHandler) | ||||
| 	} | ||||
| 
 | ||||
| 	textMessage(resolve) { | ||||
| 		if (this.event.faceHero) { | ||||
| 			const obj = this.map.gameObjects[this.event.faceHero]; | ||||
| 			obj.direction = utils.oppositeDirection(this.map.gameObjects["hero"].direction); | ||||
| 		} | ||||
| 
 | ||||
| 		const message = new TextMessage({ | ||||
| 			text: this.event.text, | ||||
| 			onComplete: () => resolve() | ||||
| 		}) | ||||
| 		message.init( document.querySelector(".game-container") ) | ||||
| 	} | ||||
| 
 | ||||
| 	changeMap(resolve) { | ||||
| 		this.map.overworld.startMap(window.OverworldMaps[this.event.map]); | ||||
| 		resolve(); | ||||
| 	} | ||||
| 
 | ||||
| 	init() { | ||||
| 		return new Promise(resolve => { | ||||
| 			this[this.event.type](resolve) | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| class OverworldMap { | ||||
| 	constructor(config) { | ||||
| 		this.overworld = null; | ||||
| 		this.gameObjects = config.gameObjects; | ||||
| 		this.cutsceneSpaces = config.cutsceneSpaces || {}; | ||||
| 		this.walls = config.walls || {}; | ||||
| 
 | ||||
| 		this.lowerImage = new Image(); | ||||
| @ -52,6 +54,25 @@ class OverworldMap { | ||||
| 		Object.values(this.gameObjects).forEach(object => object.doBehaviorEvent(this)) | ||||
| 	} | ||||
| 
 | ||||
| 	checkForActionCutscene() { | ||||
| 		const hero = this.gameObjects["hero"]; | ||||
| 		const nextCoords = utils.nextPosition(hero.x, hero.y, hero.direction); | ||||
| 		const match = Object.values(this.gameObjects).find(object => { | ||||
| 			return `${object.x},${object.y}` === `${nextCoords.x},${nextCoords.y}` | ||||
| 		}); | ||||
| 		if (!this.isCutscenePlaying && match && match.talking.length) { | ||||
| 			this.startCutscene(match.talking[0].events); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	checkForFootstepCutscene() { | ||||
| 		const hero = this.gameObjects["hero"]; | ||||
| 		const match = this.cutsceneSpaces[ `${hero.x},${hero.y}`]; | ||||
| 		if (!this.isCutscenePlaying && match) { | ||||
| 			this.startCutscene(match[0].events); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	addWall(x, y) { | ||||
| 		this.walls[`${x},${y}`] = true; | ||||
| 	} | ||||
| @ -85,17 +106,31 @@ window.OverworldMaps = { | ||||
| 					{ type: "stand", direction: "right", time: 300 }, | ||||
| 					{ type: "stand", direction: "down", time: 400 }, | ||||
| 					{ type: "stand", direction: "up", time: 700 }, | ||||
| 				], | ||||
| 				talking: [ | ||||
| 					{ | ||||
| 						events: [ | ||||
| 							{ type: "textMessage", text: "Schau dich ruhig um.", faceHero: "npc1" }, | ||||
| 							{ type: "textMessage", text: "Vielleicht findest du etwas interessantes ..." }, | ||||
| 						] | ||||
| 					} | ||||
| 				] | ||||
| 			}), | ||||
| 			npc2: new Person({ | ||||
| 				x: utils.withGrid(8), | ||||
| 				y: utils.withGrid(9), | ||||
| 				x: utils.withGrid(2), | ||||
| 				y: utils.withGrid(5), | ||||
| 				behaviorLoop: [ | ||||
| 					{ type: "walk", direction: "left" }, | ||||
| 					{ type: "stand", direction: "up", time: 800 }, | ||||
| 					{ type: "walk", direction: "up" }, | ||||
| 					{ type: "walk", direction: "right" }, | ||||
| 					{ type: "walk", direction: "down" }, | ||||
| 					{ type: "stand", direction: "right", time: 300 }, | ||||
| 					// { type: "stand", direction: "down", time: 300 },
 | ||||
| 				], | ||||
| 				talking: [ | ||||
| 					{ | ||||
| 						events: [ | ||||
| 							{ type: "textMessage", text: "Dieser Raum darf nicht betreten werden.", faceHero: "npc2" }, | ||||
| 							{ type: "textMessage", text: "Geh weg!" }, | ||||
| 							{ who: "hero", type: "walk", direction: "right" }, | ||||
| 						] | ||||
| 					} | ||||
| 				] | ||||
| 			}) | ||||
| 		}, | ||||
| @ -141,20 +176,51 @@ window.OverworldMaps = { | ||||
| 			[utils.asGridCoord(8,1)] : true, | ||||
| 			[utils.asGridCoord(7,1)] : true, | ||||
| 			[utils.asGridCoord(6,1)] : true, | ||||
| 		}, | ||||
| 		cutsceneSpaces: { | ||||
| 			[utils.asGridCoord(1,6)]: [ | ||||
| 				{ | ||||
| 					events: [ | ||||
| 						{ who: "npc2", type: "walk", direction: "down" }, | ||||
| 						{ who: "npc2", type: "stand", direction: "left" }, | ||||
| 						{ who: "hero", type: "stand", direction: "right", time: 300 }, | ||||
| 						{ type: "textMessage", text: "Du kannst hier nicht rein!" }, | ||||
| 						{ who: "npc2", type: "walk", direction: "up" }, | ||||
| 						{ who: "npc2", type: "stand", direction: "right" }, | ||||
| 						{ who: "hero", type: "walk", direction: "right" }, | ||||
| 						{ who: "hero", type: "walk", direction: "right" }, | ||||
| 					] | ||||
| 				} | ||||
| 			], | ||||
| 			[utils.asGridCoord(11,5)]: [ | ||||
| 				{ | ||||
| 					events: [ | ||||
| 						{ type: "changeMap", map: "Kitchen" } | ||||
| 					] | ||||
| 				} | ||||
| 			], | ||||
| 		} | ||||
| 	}, | ||||
| 	Kitchen: { | ||||
| 		lowerSrc: "/images/maps/room-builder.png", | ||||
| 		upperSrc: "/images/maps/room-builder.png", | ||||
| 		gameObjects: { | ||||
| 			hero: new GameObject({ | ||||
| 				x: 2, | ||||
| 				y: 3, | ||||
| 			hero: new Person({ | ||||
| 				isPlayerControlled: true, | ||||
| 				x: utils.withGrid(2), | ||||
| 				y: utils.withGrid(3), | ||||
| 			}), | ||||
| 			npc1: new GameObject({ | ||||
| 				x: 3, | ||||
| 				y: 6, | ||||
| 				src: "/images/characters/people/hero-run.png" | ||||
| 			npc1: new Person({ | ||||
| 				x: utils.withGrid(3), | ||||
| 				y: utils.withGrid(6), | ||||
| 				// src: "/images/characters/people/hero-run.png",
 | ||||
| 				talking: [ | ||||
| 					{ | ||||
| 						events: [ | ||||
| 							{ type: "textMessage", text: "You made it!", faceHero: "npc1" }, | ||||
| 						] | ||||
| 					} | ||||
| 				] | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										36
									
								
								TextMessage.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								TextMessage.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| class TextMessage { | ||||
| 	constructor({ text, onComplete }) { | ||||
| 		this.text = text; | ||||
| 		this.onComplete = onComplete; | ||||
| 		this.element = null; | ||||
| 	} | ||||
| 
 | ||||
| 	createElement() { | ||||
| 		this.element = document.createElement("div"); | ||||
| 		this.element.classList.add("TextMessage"); | ||||
| 
 | ||||
| 		this.element.innerHTML = (` | ||||
| 			<p class="TextMessage_p">${this.text}</p> | ||||
| 			<button class="TextMessage_button">Next</button> | ||||
| 		`);
 | ||||
| 
 | ||||
| 		this.element.querySelector("button").addEventListener("click", () => { | ||||
| 			this.done(); | ||||
| 		}); | ||||
| 
 | ||||
| 		this.actionListener = new KeyPressListener("Enter", () => { | ||||
| 			this.actionListener.unbind(); | ||||
| 			this.done(); | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| 	done() { | ||||
| 		this.element.remove(); | ||||
| 		this.onComplete(); | ||||
| 	} | ||||
| 
 | ||||
| 	init(container) { | ||||
| 		this.createElement(); | ||||
| 		container.appendChild(this.element); | ||||
| 	} | ||||
| } | ||||
| @ -3,7 +3,8 @@ | ||||
| <head> | ||||
| 	<meta charset="UTF-8"> | ||||
| 	<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
| 	<link rel="stylesheet" href="style.css"> | ||||
| 	<link rel="stylesheet" href="styles/global.css"> | ||||
| 	<link rel="stylesheet" href="styles/TextMessage.css"> | ||||
| 	<title>witchday</title> | ||||
| </head> | ||||
| <body> | ||||
| @ -19,6 +20,8 @@ | ||||
| 	<script src="/Sprite.js"></script> | ||||
| 	<script src="/OverworldMap.js"></script> | ||||
| 	<script src="/OverworldEvent.js"></script> | ||||
| 	<script src="/TextMessage.js"></script> | ||||
| 	<script src="/KeyPressListener.js"></script> | ||||
| 	<script src="/init.js"></script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										32
									
								
								styles/TextMessage.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								styles/TextMessage.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| .TextMessage { | ||||
| 	position: absolute; | ||||
| 	left: 0; | ||||
| 	right: 0; | ||||
| 	bottom: 0; | ||||
| 	height: 36px; | ||||
| 	font-size: 12px; | ||||
| 	padding: 4px; | ||||
| 	background: var(--menu-background); | ||||
| 	border-top: 1px solid var(--menu-border-color); | ||||
| 	color: var(--menu-font-color); | ||||
| } | ||||
| 
 | ||||
| .TextMessage_p { | ||||
| 	margin: 0; | ||||
| 	font-size: 12px; | ||||
| } | ||||
| 
 | ||||
| .TextMessage_button { | ||||
| 	margin: 0; | ||||
| 	font-size: 12px; | ||||
| 	padding: 0; | ||||
| 	-webkit-appearance: none; | ||||
| 	background: none; | ||||
| 	border: 0; | ||||
| 	font-family: inherit; | ||||
| 	cursor: pointer; | ||||
| 
 | ||||
| 	position: absolute; | ||||
| 	right: 2px; | ||||
| 	bottom: 0; | ||||
| } | ||||
| @ -1,3 +1,13 @@ | ||||
| :root { | ||||
| 	--border-color: #000; | ||||
| 	--dialog-background: #fff; | ||||
| 
 | ||||
| 	--menu-background: #fff; | ||||
| 	--menu-border-color: #aaa; | ||||
| 	--menu-font-color: #000; | ||||
| 	--menu-selected-background: #ddd; | ||||
| } | ||||
| 
 | ||||
| * { | ||||
| 	box-sizing: border-box; | ||||
| } | ||||
							
								
								
									
										7
									
								
								utils.js
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								utils.js
									
									
									
									
									
								
							| @ -22,6 +22,13 @@ const utils = { | ||||
| 
 | ||||
| 		return {x, y}; | ||||
| 	}, | ||||
| 	oppositeDirection(direction) { | ||||
| 		if (direction === "left") { return "right" } | ||||
| 		if (direction === "right") { return "left" } | ||||
| 		if (direction === "up") { return "down" } | ||||
| 		return "up"; | ||||
| 	}, | ||||
| 
 | ||||
| 	emitEvent(name, detail) { | ||||
| 		const event = new CustomEvent(name, { | ||||
| 			detail | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user