Compare commits
	
		
			14 Commits
		
	
	
		
			b2624eb989
			...
			94b2c6b140
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 94b2c6b140 | |||
| d65f6af7f0 | |||
| 9ea7564ac8 | |||
| 499814553f | |||
| 64a21c91a3 | |||
| d14864c604 | |||
| 8537af9f08 | |||
| 56e6a728c3 | |||
| 60e8054ce8 | |||
| 452969c7be | |||
| 299f1dfcca | |||
| f99494c42b | |||
| 9fb09c1244 | |||
| b7e86ce8ac | 
							
								
								
									
										127
									
								
								rooms/sample.tmj
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								rooms/sample.tmj
									
									
									
									
									
								
							| @ -33,13 +33,134 @@ | |||||||
|          "x":0, |          "x":0, | ||||||
|          "y":0 |          "y":0 | ||||||
|         },  |         },  | ||||||
|  |         { | ||||||
|  |          "draworder":"topdown", | ||||||
|  |          "id":3, | ||||||
|  |          "name":"Object Layer 1", | ||||||
|  |          "objects":[ | ||||||
|  |                 { | ||||||
|  |                  "height":64, | ||||||
|  |                  "id":1, | ||||||
|  |                  "name":"hi_box", | ||||||
|  |                  "properties":[ | ||||||
|  |                         { | ||||||
|  |                          "name":"color", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"red" | ||||||
|  |                         },  | ||||||
|  |                         { | ||||||
|  |                          "name":"event", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"change_color" | ||||||
|  |                         }], | ||||||
|  |                  "rotation":0, | ||||||
|  |                  "type":"", | ||||||
|  |                  "visible":true, | ||||||
|  |                  "width":64, | ||||||
|  |                  "x":128, | ||||||
|  |                  "y":256 | ||||||
|  |                 },  | ||||||
|  |                 { | ||||||
|  |                  "height":64, | ||||||
|  |                  "id":4, | ||||||
|  |                  "name":"low_box", | ||||||
|  |                  "properties":[ | ||||||
|  |                         { | ||||||
|  |                          "name":"color", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"green" | ||||||
|  |                         },  | ||||||
|  |                         { | ||||||
|  |                          "name":"event", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"change_color" | ||||||
|  |                         },  | ||||||
|  |                         { | ||||||
|  |                          "name":"interactEvent", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"log_test" | ||||||
|  |                         }], | ||||||
|  |                  "rotation":0, | ||||||
|  |                  "type":"", | ||||||
|  |                  "visible":true, | ||||||
|  |                  "width":64, | ||||||
|  |                  "x":128, | ||||||
|  |                  "y":384 | ||||||
|  |                 },  | ||||||
|  |                 { | ||||||
|  |                  "height":46.3794477161778, | ||||||
|  |                  "id":5, | ||||||
|  |                  "name":"sign_crate", | ||||||
|  |                  "properties":[ | ||||||
|  |                         { | ||||||
|  |                          "name":"collides", | ||||||
|  |                          "type":"bool", | ||||||
|  |                          "value":true | ||||||
|  |                         },  | ||||||
|  |                         { | ||||||
|  |                          "name":"interactEvent", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"show_message" | ||||||
|  |                         },  | ||||||
|  |                         { | ||||||
|  |                          "name":"messageText", | ||||||
|  |                          "type":"string", | ||||||
|  |                          "value":"I'm just a humble box!\nIt's possible to say more than one thing you know." | ||||||
|  |                         }], | ||||||
|  |                  "rotation":0, | ||||||
|  |                  "type":"", | ||||||
|  |                  "visible":true, | ||||||
|  |                  "width":41.2588439219327, | ||||||
|  |                  "x":331.477887674811, | ||||||
|  |                  "y":401.217123575356 | ||||||
|  |                 },  | ||||||
|  |                 { | ||||||
|  |                  "height":37.6264667354163, | ||||||
|  |                  "id":6, | ||||||
|  |                  "name":"", | ||||||
|  |                  "properties":[ | ||||||
|  |                         { | ||||||
|  |                          "name":"collides", | ||||||
|  |                          "type":"bool", | ||||||
|  |                          "value":true | ||||||
|  |                         }], | ||||||
|  |                  "rotation":0, | ||||||
|  |                  "type":"", | ||||||
|  |                  "visible":true, | ||||||
|  |                  "width":23.6907383148917, | ||||||
|  |                  "x":403.02126592157, | ||||||
|  |                  "y":408.316842721369 | ||||||
|  |                 },  | ||||||
|  |                 { | ||||||
|  |                  "height":35.6754647565428, | ||||||
|  |                  "id":7, | ||||||
|  |                  "name":"", | ||||||
|  |                  "properties":[ | ||||||
|  |                         { | ||||||
|  |                          "name":"collides", | ||||||
|  |                          "type":"bool", | ||||||
|  |                          "value":true | ||||||
|  |                         }], | ||||||
|  |                  "rotation":0, | ||||||
|  |                  "type":"", | ||||||
|  |                  "visible":true, | ||||||
|  |                  "width":30.3798879567435, | ||||||
|  |                  "x":656.930237743527, | ||||||
|  |                  "y":411.661417542295 | ||||||
|  |                 }], | ||||||
|  |          "opacity":1, | ||||||
|  |          "type":"objectgroup", | ||||||
|  |          "visible":true, | ||||||
|  |          "x":0, | ||||||
|  |          "y":0 | ||||||
|  |         },  | ||||||
|         { |         { | ||||||
|          "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, |          "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, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             0, 182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|             182, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             182, 0, 0, 0, 0, 188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|             0, 0, 0, 0, 0, 189, 229, 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             0, 0, 0, 0, 0, 189, 229, 0, 0, 0, 190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
| @ -64,8 +185,8 @@ | |||||||
|          "x":0, |          "x":0, | ||||||
|          "y":0 |          "y":0 | ||||||
|         }], |         }], | ||||||
|  "nextlayerid":3, |  "nextlayerid":4, | ||||||
|  "nextobjectid":1, |  "nextobjectid":8, | ||||||
|  "orientation":"orthogonal", |  "orientation":"orthogonal", | ||||||
|  "renderorder":"right-down", |  "renderorder":"right-down", | ||||||
|  "tiledversion":"1.11.2", |  "tiledversion":"1.11.2", | ||||||
|  | |||||||
| @ -12,13 +12,12 @@ const ROOM_ASSETS = { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default class Assets { | export default class Assets { | ||||||
|   constructor() { |   constructor(game) { | ||||||
|  |     this.game = game | ||||||
|     this.assetMap = {} |     this.assetMap = {} | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get(assetName) { |   get(assetName) { | ||||||
|     console.log("getting", assetName) |  | ||||||
|     console.log("from", this.assetMap) |  | ||||||
|     return this.assetMap[assetName] |     return this.assetMap[assetName] | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -44,7 +43,6 @@ export default class Assets { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   loadImage(name, path) { |   loadImage(name, path) { | ||||||
|     console.log(name, path) |  | ||||||
|     return new Promise(resolve => { |     return new Promise(resolve => { | ||||||
|       const img = new Image() |       const img = new Image() | ||||||
|       this.assetMap[name] = img |       this.assetMap[name] = img | ||||||
| @ -55,13 +53,13 @@ export default class Assets { | |||||||
| 
 | 
 | ||||||
|   loadTileset(name, path) { |   loadTileset(name, path) { | ||||||
|     return fetch(path).then(rsp => rsp.json()).then(json => { |     return fetch(path).then(rsp => rsp.json()).then(json => { | ||||||
|       return this.assetMap[name] = new Tileset(json, name) |       return this.assetMap[name] = new Tileset(this.game, json, name) | ||||||
|     }).then(tileset => this.loadImages(tileset.imagesToLoad)) |     }).then(tileset => this.loadImages(tileset.imagesToLoad)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   loadRoom(name, path) { |   loadRoom(name, path) { | ||||||
|     return fetch(path).then(rsp => rsp.json()).then(json => { |     return fetch(path).then(rsp => rsp.json()).then(json => { | ||||||
|       return this.assetMap[name] = new Room(json, name) |       return this.assetMap[name] = new Room(this.game, json, name) | ||||||
|     }).then(room => this.loadTilesets(room.tilesetsToLoad)) |     }).then(room => this.loadTilesets(room.tilesetsToLoad)) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								src/event.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/event.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | export default class Event { | ||||||
|  |   constructor(name, action) { | ||||||
|  |     this.name = name | ||||||
|  |     this.action = action | ||||||
|  |     this.triggered = false | ||||||
|  |     this.triggeredThisFrame = false | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   trigger(object) { | ||||||
|  |     this.triggeredThisFrame = true | ||||||
|  |     if (!this.triggered) { | ||||||
|  |       this.triggered = true | ||||||
|  |       this.action(object) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   nextFrame() { | ||||||
|  |     this.triggered = this.triggeredThisFrame | ||||||
|  |     this.triggeredThisFrame = false | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/game.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								src/game.js
									
									
									
									
									
								
							| @ -1,40 +1,90 @@ | |||||||
| import Player from "./player.js" | import Player from "./player.js" | ||||||
| import Input from "./input.js" | import Input from "./input.js" | ||||||
|  | import Event from "./event.js" | ||||||
|  | import Message from "./message.js" | ||||||
|  | 
 | ||||||
|  | import { average } from "./util.js" | ||||||
| 
 | 
 | ||||||
| export default class Game { | export default class Game { | ||||||
|   constructor(canvas) { |   constructor(canvas) { | ||||||
|     this.canvas = canvas |     this.canvas = canvas | ||||||
|     this.ctx = canvas.getContext("2d") |     this.ctx = canvas.getContext("2d") | ||||||
|     this.timestamp = 0 |     this.timestamp = 0 | ||||||
|     this.actors = [] |     this.player = new Player(this, 200, 200) | ||||||
|     this.actors.push(new Player(this, 200, 200)) |     this.actors = [this.player] | ||||||
| 
 | 
 | ||||||
|     this.input = new Input().initialize() |     this.input = new Input().initialize() | ||||||
| 
 | 
 | ||||||
|     this.currentRoom = null |     this.currentRoom = null | ||||||
|  |     this.events = { | ||||||
|  |       "log_test": new Event("log_test", () => console.log("Log events work!")), | ||||||
|  |       "change_color": new Event("change_color", object => object.setProperty("color", "blue")), | ||||||
|  |       "show_message": new Event("show_message", object => this.message = new Message(this, object.getProperty("messageText"))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     this.message = null | ||||||
|  | 
 | ||||||
|  |     this.fpsBuffer = [60, 60, 60, 60, 60, 60, 60, 60, 60, 60] | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   triggerEvent(eventName, object) { | ||||||
|  |     const event = this.events[eventName] | ||||||
|  |     if (event) event.trigger(object) | ||||||
|  |     else console.error("Unknown event " + eventName) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   start() { |   start() { | ||||||
|     this.currentRoom = this.assets.get("sampleRoom") |     this.loadRoom(this.assets.get("sampleRoom")) | ||||||
|     requestAnimationFrame(this.loop.bind(this)) |     requestAnimationFrame(this.loop.bind(this)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   loadRoom(room) { | ||||||
|  |     this.currentRoom = room | ||||||
|  |     this.currentRoom.objects.forEach(roomObject => this.actors.push(roomObject)) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   closeMessage(message) { | ||||||
|  |     this.message = null | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   loop(timestamp) { |   loop(timestamp) { | ||||||
|     const dt = timestamp - this.timestamp |     this.dt = timestamp - this.timestamp | ||||||
|     this.timestamp = timestamp |     const fps = 1000 / this.dt | ||||||
|     this.tick(dt) |     this.fpsBuffer.pop() | ||||||
|  |     this.fpsBuffer.unshift(fps) | ||||||
|  |     this.timestamp= timestamp | ||||||
|  |     this.tick(this.dt) | ||||||
|     this.draw() |     this.draw() | ||||||
| 
 | 
 | ||||||
|     requestAnimationFrame(this.loop.bind(this)) |     requestAnimationFrame(this.loop.bind(this)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   tick(dt) { |   tick(dt) { | ||||||
|  |     if (this.message) { | ||||||
|  |       this.message?.tick(dt) | ||||||
|  |     } else { | ||||||
|       this.actors.forEach(actor => actor.tick(dt)) |       this.actors.forEach(actor => actor.tick(dt)) | ||||||
|  |       Object.values(this.events).forEach(e => e.nextFrame()) | ||||||
|  |     } | ||||||
|  |     this.input.tick() | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   draw() { |   draw() { | ||||||
|     const { canvas, ctx } = this |     const { canvas, ctx } = this | ||||||
|     this.currentRoom.draw(ctx) |     this.currentRoom.draw(ctx) | ||||||
|     this.actors.forEach(actor => actor.draw(ctx)) |     this.actors.forEach(actor => actor.draw(ctx)) | ||||||
|  |     this.message?.draw(ctx) | ||||||
|  |     this.drawFps(ctx) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   drawFps(ctx) { | ||||||
|  |     ctx.fillStyle = "white" | ||||||
|  |     ctx.fillRect(ctx.canvas.width, 0, -25, 20) | ||||||
|  |     ctx.strokeStyle = "black" | ||||||
|  |     ctx.textBaseline = "top" | ||||||
|  |     ctx.textAlign = "right" | ||||||
|  |     ctx.font = "bold 20px serif" | ||||||
|  |     ctx.fillText(Math.round(average(this.fpsBuffer)), ctx.canvas.width, 0) | ||||||
|  |     ctx.strokeText(Math.round(average(this.fpsBuffer)), ctx.canvas.width, 0) | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ document.addEventListener("DOMContentLoaded", async e => { | |||||||
|   const canvas = document.getElementById("game-canvas") |   const canvas = document.getElementById("game-canvas") | ||||||
|   const game = new Game(canvas) |   const game = new Game(canvas) | ||||||
| 
 | 
 | ||||||
|   game.assets = new Assets() |   game.assets = new Assets(game) | ||||||
|   await game.assets.load() |   await game.assets.load() | ||||||
| 
 | 
 | ||||||
|   game.start() |   game.start() | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								src/input.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/input.js
									
									
									
									
									
								
							| @ -12,11 +12,17 @@ export default class Input { | |||||||
|     this.inputsToKeys = Object.fromEntries(Object.keys(this.keysToInputs).map(key => [this.keysToInputs[key], key])) |     this.inputsToKeys = Object.fromEntries(Object.keys(this.keysToInputs).map(key => [this.keysToInputs[key], key])) | ||||||
| 
 | 
 | ||||||
|     this.inputPressed = Object.fromEntries(Object.keys(this.inputsToKeys).map(key => [key, false])) |     this.inputPressed = Object.fromEntries(Object.keys(this.inputsToKeys).map(key => [key, false])) | ||||||
|  |     this.inputJustPressed = Object.fromEntries(Object.keys(this.inputsToKeys).map(key => [key, false])) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   initialize() { |   initialize() { | ||||||
|     window.addEventListener("keydown", key => this.inputPressed[this.keyFromInput(key.key)] = true) |     window.addEventListener("keydown", key => { | ||||||
|     window.addEventListener("keyup", key => this.inputPressed[this.keyFromInput(key.key)] = false) |       this.inputJustPressed[this.keyFromInput(key.key)] = true | ||||||
|  |       this.inputPressed[this.keyFromInput(key.key)] = true | ||||||
|  |     }) | ||||||
|  |     window.addEventListener("keyup", key => { | ||||||
|  |       this.inputPressed[this.keyFromInput(key.key)] = false | ||||||
|  |     }) | ||||||
|     return this |     return this | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -24,6 +30,14 @@ export default class Input { | |||||||
|     return !!this.inputPressed[inputName] |     return !!this.inputPressed[inputName] | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   isInputJustPressed(inputName) { | ||||||
|  |     return !!this.inputJustPressed[inputName] | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tick() { | ||||||
|  |     this.inputJustPressed = Object.fromEntries(Object.keys(this.inputsToKeys).map(key => [key, false])) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   keyFromInput(input) { |   keyFromInput(input) { | ||||||
|     return this.keysToInputs[input] |     return this.keysToInputs[input] | ||||||
|   } |   } | ||||||
|  | |||||||
							
								
								
									
										65
									
								
								src/message.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/message.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | |||||||
|  | export default class Message { | ||||||
|  |   constructor(game, text) { | ||||||
|  |     this.game = game | ||||||
|  |     this.text = text.split("\n") | ||||||
|  |     this.currentText = this.text.shift() | ||||||
|  |     this.textColor = "white" | ||||||
|  |     this.backgroundColor = "black" | ||||||
|  |     this.textIndex = 0 | ||||||
|  |     this.textProgress = 0.0 | ||||||
|  |     this.textSpeed = 0.08 // seconds per character
 | ||||||
|  |     this.backgroundHeight = 40 | ||||||
|  |     this.textSize = 30 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tick(dt) { | ||||||
|  |     this.textProgress += (dt / 1000.0) / this.textSpeed | ||||||
|  |     this.textIndex = Math.floor(this.textProgress) | ||||||
|  | 
 | ||||||
|  |     const ijp = this.game.input.isInputJustPressed("interact") | ||||||
|  |     if (ijp) { | ||||||
|  |       if (this.messageComplete()) { | ||||||
|  | 	this.game.closeMessage(this) | ||||||
|  |       } else if (this.lineComplete()) { | ||||||
|  | 	this.currentText = this.text.shift() | ||||||
|  | 	this.textProgress = 0.0 | ||||||
|  | 	this.textIndex = 0 | ||||||
|  |       } else { | ||||||
|  | 	this.textProgress = this.currentText.length | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   draw(ctx) { | ||||||
|  |     ctx.fillStyle = this.backgroundColor | ||||||
|  |     ctx.fillRect(0, 0, ctx.canvas.width, this.backgroundHeight) | ||||||
|  |     ctx.font = `bold ${this.textSize}px sans-serif` | ||||||
|  |     ctx.textBaseline = "top" | ||||||
|  |     ctx.textAlign = "left" | ||||||
|  |     ctx.fillStyle = this.textColor | ||||||
|  |     ctx.fillText(this.currentText.substring(0, this.textIndex), 5, 5) | ||||||
|  |     if (this.messageComplete()) { | ||||||
|  |       ctx.fillRect( | ||||||
|  | 	ctx.canvas.width - 20,  | ||||||
|  | 	this.backgroundHeight - 20,  | ||||||
|  | 	10, 10 | ||||||
|  |       ) | ||||||
|  |     } else if (this.lineComplete()) { | ||||||
|  |       ctx.beginPath() | ||||||
|  |       ctx.moveTo(ctx.canvas.width - 20, this.backgroundHeight - 20) | ||||||
|  |       ctx.lineTo(ctx.canvas.width - 20, this.backgroundHeight - 10) | ||||||
|  |       ctx.lineTo(ctx.canvas.width - 15, this.backgroundHeight - 15) | ||||||
|  |       ctx.lineTo(ctx.canvas.width - 20, this.backgroundHeight - 20) | ||||||
|  |       ctx.fill() | ||||||
|  |       ctx.closePath() | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   lineComplete() { | ||||||
|  |     return this.textIndex >= this.currentText.length + 3 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   messageComplete() { | ||||||
|  |     return this.lineComplete() && !this.text.length | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -7,11 +7,12 @@ export default class Player extends Actor { | |||||||
|     this.x = x |     this.x = x | ||||||
|     this.y = y |     this.y = y | ||||||
|     this.width = 32 |     this.width = 32 | ||||||
|     this.height = 64 |     this.height = 32 | ||||||
|     this.xVel = 0 |     this.xVel = 0 | ||||||
|     this.yVel = 0 |     this.yVel = 0 | ||||||
|     this.color = "#56E" |     this.color = "#56E" | ||||||
|     this.playerDirection = { x: 0, y: 1 } |     this.playerDirection = { x: 0, y: 1 } | ||||||
|  |     this.interactHitbox = null | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   tick(dt) { |   tick(dt) { | ||||||
| @ -26,16 +27,51 @@ export default class Player extends Actor { | |||||||
|     this.x += this.xVel  |     this.x += this.xVel  | ||||||
|     this.y += this.yVel  |     this.y += this.yVel  | ||||||
| 
 | 
 | ||||||
|  |     if (this.collidesWithAbsolutelyAnything()) { | ||||||
|  |       this.x -= this.xVel | ||||||
|  |       this.y -= this.yVel | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if (!isZeroVector(dir)) this.playerDirection = dir |     if (!isZeroVector(dir)) this.playerDirection = dir | ||||||
|  | 
 | ||||||
|  |     if (this.isInputPressed("interact")) this.createInteractHitbox() | ||||||
|  |     else this.interactHitbox = null | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   collidesWithAbsolutelyAnything() { | ||||||
|  |     return this.collidesWithTiles() || this.collidesWithObjects() | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   collidesWithObjects() { | ||||||
|  |     const objects = this.game.currentRoom.objectsUnderRectangle(this) | ||||||
|  |     return !!objects.find(object => object.collides()) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   collidesWithTiles() { | ||||||
|  |     const tur = this.game.currentRoom.tilesUnderRectangle(this).filter(x => x) | ||||||
|  |     const colliders = tur.filter(tile => tile.properties.find(prop => prop.name == "collides" && prop.value)) | ||||||
|  |     return !!colliders.length | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   createInteractHitbox() { | ||||||
|  |     this.interactHitbox = { | ||||||
|  |       width: this.width, | ||||||
|  |       height: this.height, | ||||||
|  |       x: this.x + (this.playerDirection.x * this.width / 2), | ||||||
|  |       y: this.y + (this.playerDirection.y * this.height / 2), | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   isInputPressed(action) { | ||||||
|  |     return this.game.input.isInputPressed.call(this.game.input, action) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   inputDirection() { |   inputDirection() { | ||||||
|     const isInputPressed = this.game.input.isInputPressed.bind(this.game.input) |  | ||||||
|     const dir = { x: 0, y: 0 } |     const dir = { x: 0, y: 0 } | ||||||
|     if (isInputPressed("up")) dir.y -= 1 |     if (this.isInputPressed("up")) dir.y -= 1 | ||||||
|     if (isInputPressed("down")) dir.y += 1 |     if (this.isInputPressed("down")) dir.y += 1 | ||||||
|     if (isInputPressed("left")) dir.x -= 1 |     if (this.isInputPressed("left")) dir.x -= 1 | ||||||
|     if (isInputPressed("right")) dir.x += 1 |     if (this.isInputPressed("right")) dir.x += 1 | ||||||
| 
 | 
 | ||||||
|     if (Math.abs(dir.x, dir.y) == 2) { |     if (Math.abs(dir.x, dir.y) == 2) { | ||||||
|       dir.x *= SQRT_OF_TWO |       dir.x *= SQRT_OF_TWO | ||||||
| @ -47,10 +83,11 @@ export default class Player extends Actor { | |||||||
| 
 | 
 | ||||||
|   draw(ctx) { |   draw(ctx) { | ||||||
|     this.color = `rgb(128 ${(this.playerDirection.x * 128) + 128} ${(this.playerDirection.y * 128) + 128}` |     this.color = `rgb(128 ${(this.playerDirection.x * 128) + 128} ${(this.playerDirection.y * 128) + 128}` | ||||||
|     ctx.beginPath() |  | ||||||
|     ctx.fillStyle = this.color |     ctx.fillStyle = this.color | ||||||
|     ctx.rect(this.x, this.y, this.width, this.height) |     ctx.fillRect(this.x, this.y, this.width, this.height) | ||||||
|     ctx.fill() |     if (this.interactHitbox) { | ||||||
|     ctx.closePath() |       ctx.fillStyle = "#FF000088" | ||||||
|  |       ctx.fillRect(this.interactHitbox.x, this.interactHitbox.y, this.interactHitbox.width, this.interactHitbox.height) | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										61
									
								
								src/room.js
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								src/room.js
									
									
									
									
									
								
							| @ -1,8 +1,13 @@ | |||||||
|  | import RoomObject from "./roomObject.js" | ||||||
|  | import { doRectanglesOverlap } from "./util.js" | ||||||
|  | 
 | ||||||
| export default class Room { | export default class Room { | ||||||
|   constructor(json, name) { |   constructor(game, json, name) { | ||||||
|  |     this.game = game | ||||||
|     this.json = json |     this.json = json | ||||||
|     this.name = name |     this.name = name | ||||||
|     console.log(json) |     const objectJson = this.objectLayers.map(layer => layer.objects).flat() | ||||||
|  |     this.objects = objectJson.map(RoomObject.fromJson.bind(null, this.game)) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get tilesetsToLoad() { |   get tilesetsToLoad() { | ||||||
| @ -14,7 +19,6 @@ export default class Room { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   populateTilesets(assets) { |   populateTilesets(assets) { | ||||||
|     console.log(this.json) |  | ||||||
|     this.tilesets = this.json.tilesets.map((tileset, index) => { |     this.tilesets = this.json.tilesets.map((tileset, index) => { | ||||||
|       const ts = assets.get(`${this.name}-${index}`) |       const ts = assets.get(`${this.name}-${index}`) | ||||||
|       ts.populateImage(assets) |       ts.populateImage(assets) | ||||||
| @ -23,7 +27,46 @@ export default class Room { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   draw(ctx) { |   draw(ctx) { | ||||||
|     this.json.layers.forEach(layer => { |     this.tileLayers.forEach(this.drawTileLayer.bind(this, ctx)) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   drawLayer(ctx, layer) { | ||||||
|  |     if (layer.type == "tilelayer") this.drawTileLayer(ctx, layer) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get tileLayers() { | ||||||
|  |     return this.json.layers.filter(layer => layer.type == "tilelayer") | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   get objectLayers() { | ||||||
|  |     return this.json.layers.filter(layer => layer.type == "objectgroup") | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   objectsUnderRectangle(rect) { | ||||||
|  |     return this.objects.filter(object => doRectanglesOverlap(object, rect)) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tilesUnderRectangle(rect) { | ||||||
|  |     return this.tileLayers.map(layer => this.tilesUnderRectangleInLayer(layer, rect)).flat() | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   tilesUnderRectangleInLayer(layer, rect) { | ||||||
|  |     return [{ x: rect.x, y: rect.y }, | ||||||
|  | 	    { x: rect.x + rect.width, y: rect.y }, | ||||||
|  | 	    { x: rect.x, y: rect.y + rect.height }, | ||||||
|  | 	    { x: rect.x + rect.width, y: rect.y + rect.height } | ||||||
|  | 	   ].map(point => { | ||||||
|  | 	     const tileset = this.tilesets[0] | ||||||
|  | 	     const { x, y } = point | ||||||
|  | 	     const tileX = Math.floor(x / tileset.tileWidth) | ||||||
|  | 	     const tileY = Math.floor(y / tileset.tileHeight) | ||||||
|  | 	     const index = tileX + (tileY * layer.width) | ||||||
|  | 	     const tileIndex = layer.data[index] - 1 | ||||||
|  | 	     return tileset.tileAt(tileIndex) | ||||||
|  | 	   }) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   drawTileLayer(ctx, layer) { | ||||||
|     for (let y = 0; y < layer.height; y++) { |     for (let y = 0; y < layer.height; y++) { | ||||||
|       for (let x = 0; x < layer.width; x++) { |       for (let x = 0; x < layer.width; x++) { | ||||||
| 	const index = x + (y * layer.width) | 	const index = x + (y * layer.width) | ||||||
| @ -41,8 +84,16 @@ export default class Room { | |||||||
| 	  this.json.tilewidth, | 	  this.json.tilewidth, | ||||||
| 	  this.json.tileheight | 	  this.json.tileheight | ||||||
| 	) | 	) | ||||||
|  | 	if (tileset.collides(tileIndex)) { | ||||||
|  | 	  ctx.fillStyle = "#aa660088" | ||||||
|  | 	  ctx.fillRect( | ||||||
|  | 	  x * this.json.tilewidth, | ||||||
|  | 	  y * this.json.tileheight, | ||||||
|  | 	  this.json.tilewidth, | ||||||
|  | 	  this.json.tileheight | ||||||
|  | 	  ) | ||||||
|  | 	} | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     }) |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										52
									
								
								src/roomObject.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/roomObject.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | |||||||
|  | import { doRectanglesOverlap } from "./util.js" | ||||||
|  | 
 | ||||||
|  | export default class RoomObject { | ||||||
|  |   constructor(game) { | ||||||
|  |     this.game = game | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   static fromJson(game, json) { | ||||||
|  |     const roomObject = new RoomObject(game) | ||||||
|  |     Object.entries(json).forEach(([key, value]) => roomObject[key] = value) | ||||||
|  |     return roomObject | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   getProperty(name) { | ||||||
|  |     const property = this.properties.find(p => p.name == name) | ||||||
|  |     if (!property) { | ||||||
|  |       // console.error(`Unknown property ${name} on ${this.name}`)
 | ||||||
|  |       return null | ||||||
|  |     } | ||||||
|  |     return property.value | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   setProperty(name, value) { | ||||||
|  |     const p = this.properties.find(p => p.name == name) | ||||||
|  |     if (p) { | ||||||
|  |       p.value = value | ||||||
|  |     } else { | ||||||
|  |       this.properties[name] = value | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   tick(dt) { | ||||||
|  |     const { player } = this.game | ||||||
|  |     if (doRectanglesOverlap(player, this)) { | ||||||
|  |       const eventName = this.getProperty.call(this, "event") | ||||||
|  |       if (eventName) this.game.triggerEvent(eventName, this) | ||||||
|  |     } | ||||||
|  |     if (player.interactHitbox && doRectanglesOverlap(player.interactHitbox, this)) { | ||||||
|  |       const eventName = this.getProperty("interactEvent") | ||||||
|  |       if (eventName) this.game.triggerEvent(eventName, this) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   draw(ctx) { | ||||||
|  |     ctx.fillStyle = this.getProperty("color") || "#00000000" | ||||||
|  |     ctx.fillRect(this.x, this.y, this.width, this.height) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   collides() { | ||||||
|  |     return this.getProperty("collides") || false | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,7 +1,9 @@ | |||||||
| export default class Tileset { | export default class Tileset { | ||||||
|   constructor(json, name) { |   constructor(game, json, name) { | ||||||
|  |     this.game = game | ||||||
|     this.json = json |     this.json = json | ||||||
|     this.name = name |     this.name = name | ||||||
|  |     this.tiles = json.tiles | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get imagesToLoad() { |   get imagesToLoad() { | ||||||
| @ -32,4 +34,15 @@ export default class Tileset { | |||||||
|       (Math.floor(index / this.columns) * this.tileHeight) |       (Math.floor(index / this.columns) * this.tileHeight) | ||||||
|     ] |     ] | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   tileAt(index) { | ||||||
|  |     return this.tiles.find(tile => tile.id == index) | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   collides(index) { | ||||||
|  |     const tile = this.tileAt(index) | ||||||
|  |     if (!tile) return | ||||||
|  | 
 | ||||||
|  |     return tile.properties.find(prop => prop.name == "collides").value == "true" | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										24
									
								
								src/util.js
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								src/util.js
									
									
									
									
									
								
							| @ -2,7 +2,29 @@ const SQRT_OF_TWO = Math.sqrt(2) | |||||||
| 
 | 
 | ||||||
| const isZeroVector = vector => !vector.x && !vector.y | const isZeroVector = vector => !vector.x && !vector.y | ||||||
| 
 | 
 | ||||||
|  | const doRectanglesOverlap = (rect1, rect2) => { | ||||||
|  |   return ( | ||||||
|  |     doLengthsOverlap({ x: rect1.x, width: rect1.width }, { x: rect2.x, width: rect2.width }) | ||||||
|  |       && | ||||||
|  |       doLengthsOverlap({ x: rect1.y, width: rect1.height }, { x: rect2.y, width: rect2.height }) | ||||||
|  |   )       | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const doLengthsOverlap = (l1, l2) => { | ||||||
|  |   return !((l1.x + l1.width < l2.x) || (l2.x + l2.width) < l1.x) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function sum(array) { | ||||||
|  |   return array.reduce((a, b) => a + b, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function average(array) { | ||||||
|  |   return sum(array) / array.length | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export { | export { | ||||||
|   SQRT_OF_TWO, |   SQRT_OF_TWO, | ||||||
|   isZeroVector |   isZeroVector, | ||||||
|  |   doRectanglesOverlap, | ||||||
|  |   sum, average | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,6 +8,358 @@ | |||||||
|  "tilecount":260, |  "tilecount":260, | ||||||
|  "tiledversion":"1.11.2", |  "tiledversion":"1.11.2", | ||||||
|  "tileheight":64, |  "tileheight":64, | ||||||
|  |  "tiles":[ | ||||||
|  |         { | ||||||
|  |          "id":60, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":61, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":62, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":63, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":64, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":65, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":66, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":80, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":81, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":82, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":83, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":84, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":85, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":86, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":100, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":101, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":102, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":103, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":144, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":145, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":146, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":164, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":165, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":166, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":180, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":182, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":184, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":200, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":201, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":202, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":203, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":204, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":205, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":220, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":221, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":222, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":223, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":224, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         },  | ||||||
|  |         { | ||||||
|  |          "id":225, | ||||||
|  |          "properties":[ | ||||||
|  |                 { | ||||||
|  |                  "name":"collides", | ||||||
|  |                  "type":"bool", | ||||||
|  |                  "value":true | ||||||
|  |                 }] | ||||||
|  |         }], | ||||||
|  "tilewidth":64, |  "tilewidth":64, | ||||||
|  "type":"tileset", |  "type":"tileset", | ||||||
|  "version":"1.10" |  "version":"1.10" | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user