diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | package.json | 10 | ||||
-rw-r--r-- | require.d.ts | 5 | ||||
-rw-r--r-- | src/app.coffee | 21 | ||||
-rw-r--r-- | src/app.ts | 26 | ||||
-rw-r--r-- | src/control/MapContext.coffee | 29 | ||||
-rw-r--r-- | src/control/MapContext.ts | 34 | ||||
-rw-r--r-- | src/model/Direction.coffee | 13 | ||||
-rw-r--r-- | src/model/Direction.ts | 13 | ||||
-rw-r--r-- | src/model/Entity.coffee | 8 | ||||
-rw-r--r-- | src/model/Entity.ts | 6 | ||||
-rw-r--r-- | src/model/EntityPosition.coffee | 8 | ||||
-rw-r--r-- | src/model/EntityPosition.ts | 11 | ||||
-rw-r--r-- | src/model/MapData.coffee | 9 | ||||
-rw-r--r-- | src/model/MapData.ts | 20 | ||||
-rw-r--r-- | src/model/Position.coffee | 10 | ||||
-rw-r--r-- | src/model/Position.ts | 10 | ||||
-rw-r--r-- | src/util.coffee | 13 | ||||
-rw-r--r-- | src/util.ts | 13 | ||||
-rw-r--r-- | src/view/MapView.coffee | 108 | ||||
-rw-r--r-- | src/view/MapView.ts | 142 | ||||
-rw-r--r-- | tsconfig.json | 12 | ||||
-rw-r--r-- | tsd.json | 15 | ||||
-rw-r--r-- | webpack.config.js | 6 |
24 files changed, 315 insertions, 228 deletions
@@ -1,2 +1,3 @@ /build /node_modules +/typings diff --git a/package.json b/package.json index 15fd45b..771f0a0 100644 --- a/package.json +++ b/package.json @@ -4,16 +4,14 @@ "start": "webpack-dev-server -d --content-base static", "build": "webpack" }, - "devDependencies": { - "coffee-loader": "^0.7.2", - "coffee-script": "^1.10.0", + "dependencies": { "css-loader": "^0.23.1", "html-webpack-plugin": "^1.7.0", + "lodash": "^3.10.1", "style-loader": "^0.13.0", + "ts-loader": "^0.7.2", + "typescript": "^1.7.5", "webpack": "^1.12.9", "webpack-dev-server": "^1.14.0" - }, - "dependencies": { - "lodash": "^3.10.1" } } diff --git a/require.d.ts b/require.d.ts new file mode 100644 index 0000000..6c6222d --- /dev/null +++ b/require.d.ts @@ -0,0 +1,5 @@ +declare var require: { + <T>(path: string): T; + (paths: string[], callback: (...modules: any[]) => void): void; + ensure: (paths: string[], callback: (require: <T>(path: string) => T) => void) => void; +}; diff --git a/src/app.coffee b/src/app.coffee deleted file mode 100644 index ca3ddf8..0000000 --- a/src/app.coffee +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' - -require './style.css' - -window._ = require 'lodash' - -MapData = require './model/MapData' -MapContext = require './control/MapContext' - - -mapContext = null - - -window.onload = -> - xhr = new XMLHttpRequest() - xhr.onload = -> - mapDef = new MapData(JSON.parse this.responseText) - mapContext = new MapContext mapDef - - xhr.open 'GET', 'resources/map/test.json', true - xhr.send() diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..d1ca3cd --- /dev/null +++ b/src/app.ts @@ -0,0 +1,26 @@ +'use strict'; + + +require('./style.css'); + + +import * as lodash from 'lodash'; +_ = lodash; + +import MapContext from './control/MapContext'; +import MapData from './model/MapData'; + + +var mapContext: MapContext; + +window.onload = () => { + var xhr = new XMLHttpRequest(); + + xhr.onload = function() { + let mapDef = new MapData(JSON.parse(this.responseText)); + mapContext = new MapContext(mapDef); + } + + xhr.open('GET', 'resources/map/test.json', true); + xhr.send(); +}; diff --git a/src/control/MapContext.coffee b/src/control/MapContext.coffee deleted file mode 100644 index df6efec..0000000 --- a/src/control/MapContext.coffee +++ /dev/null @@ -1,29 +0,0 @@ -'use strict' - - -Direction = require '../model/Direction' -Entity = require '../model/Entity' -EntityPosition = require '../model/EntityPosition' -Position = require '../model/Position' - -MapView = require '../view/MapView' - - -class MapContext - constructor: (@map) -> - @entities = {} - - @playerEntity = new EntityPosition( - new Entity('square'), - new Position(8, 8), - Direction.EAST) - - @addEntity(@playerEntity) - - @mavView = new MapView @map, @entities - - addEntity: (entity) => - @entities[entity.position.asString()] = entity - - -module.exports = MapContext diff --git a/src/control/MapContext.ts b/src/control/MapContext.ts new file mode 100644 index 0000000..4bc75c7 --- /dev/null +++ b/src/control/MapContext.ts @@ -0,0 +1,34 @@ +'use strict'; + + +import Direction from '../model/Direction'; +import Entity from '../model/Entity'; +import EntityPosition from '../model/EntityPosition'; +import MapData from '../model/MapData'; +import Position from '../model/Position'; + +import MapView from '../view/MapView'; + + +export default class MapContext { + view: MapView; + + entities: {[key: string]: EntityPosition} = {}; + playerEntity: EntityPosition; + + constructor(public map: MapData) { + this.playerEntity = new EntityPosition( + new Entity('square'), + new Position(8, 8), + Direction.East + ); + + this.addEntity(this.playerEntity); + + this.view = new MapView(map, this.entities); + } + + addEntity(entity: EntityPosition) { + this.entities[entity.position.asString()] = entity; + } +} diff --git a/src/model/Direction.coffee b/src/model/Direction.coffee deleted file mode 100644 index 5c49c80..0000000 --- a/src/model/Direction.coffee +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - - -Direction = - NORTH: 0 - EAST: 1 - SOUTH: 2 - WEST: 3 - - reverse: (d) -> (d+2)%4 - - -module.exports = Direction diff --git a/src/model/Direction.ts b/src/model/Direction.ts new file mode 100644 index 0000000..a0cf45d --- /dev/null +++ b/src/model/Direction.ts @@ -0,0 +1,13 @@ +'use strict'; + + +export enum Direction { + North, + East, + South, + West +}; + +export function reverse(r: Direction): Direction { return (r+2) % 4; } + +export default Direction; diff --git a/src/model/Entity.coffee b/src/model/Entity.coffee deleted file mode 100644 index 70c81fa..0000000 --- a/src/model/Entity.coffee +++ /dev/null @@ -1,8 +0,0 @@ -'use strict' - - -class Entity - constructor: (@name) -> - - -module.exports = Entity diff --git a/src/model/Entity.ts b/src/model/Entity.ts new file mode 100644 index 0000000..b9a52eb --- /dev/null +++ b/src/model/Entity.ts @@ -0,0 +1,6 @@ +'use strict'; + + +export default class Entity { + constructor(public name: string) {} +} diff --git a/src/model/EntityPosition.coffee b/src/model/EntityPosition.coffee deleted file mode 100644 index aaf9531..0000000 --- a/src/model/EntityPosition.coffee +++ /dev/null @@ -1,8 +0,0 @@ -'use strict' - - -class EntityPosition - constructor: (@entity, @position, @direction) -> - - -module.exports = EntityPosition diff --git a/src/model/EntityPosition.ts b/src/model/EntityPosition.ts new file mode 100644 index 0000000..815eceb --- /dev/null +++ b/src/model/EntityPosition.ts @@ -0,0 +1,11 @@ +'use strict'; + + +import Direction from '../model/Direction'; +import Entity from './Entity'; +import Position from './Position'; + + +export default class EntityPosition { + constructor(public entity: Entity, public position: Position, public direction: Direction) {} +} diff --git a/src/model/MapData.coffee b/src/model/MapData.coffee deleted file mode 100644 index 39b2140..0000000 --- a/src/model/MapData.coffee +++ /dev/null @@ -1,9 +0,0 @@ -'use strict' - - -class MapData - constructor: (data) -> - {@tiles, @collition, @layers} = data - - -module.exports = MapData diff --git a/src/model/MapData.ts b/src/model/MapData.ts new file mode 100644 index 0000000..54894c0 --- /dev/null +++ b/src/model/MapData.ts @@ -0,0 +1,20 @@ +'use strict'; + + +interface Input { + tiles: {[key: string]: {file: string}}; + collision: string[]; + layers: string[][][]; +} + +export default class MapData { + tiles: {[key: string]: {file: string}}; + public collision: string[]; + public layers: string[][][]; + + constructor(data: Input) { + this.tiles = data.tiles; + this.collision = data.collision; + this.layers = data.layers; + } +} diff --git a/src/model/Position.coffee b/src/model/Position.coffee deleted file mode 100644 index 08bb999..0000000 --- a/src/model/Position.coffee +++ /dev/null @@ -1,10 +0,0 @@ -'use strict' - - -class Position - constructor: (@x, @y) -> - - asString: => "#{@x},#{@y}" - - -module.exports = Position diff --git a/src/model/Position.ts b/src/model/Position.ts new file mode 100644 index 0000000..1db37af --- /dev/null +++ b/src/model/Position.ts @@ -0,0 +1,10 @@ +'use strict'; + + +export default class Position { + constructor(public x: number, public y: number) {} + + asString(): string { + return `${this.x},${this.y}`; + } +} diff --git a/src/util.coffee b/src/util.coffee deleted file mode 100644 index 4e0cd76..0000000 --- a/src/util.coffee +++ /dev/null @@ -1,13 +0,0 @@ -'use strict' - - -module.exports = - mapPromises: (promises) -> - p = [] - ret = {} - - for own k, v of promises - do (k, v) -> - p.push(v.then (r) -> ret[k] = r) - - Promise.all(p).then -> ret diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..2292820 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,13 @@ +'use strict'; + + +export function mapPromises<T>(promises: {[key: string]: Promise<T>}): Promise<{[key: string]: T}> { + var p: Promise<void>[] = [] + var ret: {[key: string]: T} = {} + + _.forOwn(promises, (v, k) => { + p.push(v.then(r => {ret[k] = r;})); + }); + + return Promise.all(p).then(() => ret); +} diff --git a/src/view/MapView.coffee b/src/view/MapView.coffee deleted file mode 100644 index e051d48..0000000 --- a/src/view/MapView.coffee +++ /dev/null @@ -1,108 +0,0 @@ -'use strict' - -util = require '../util' - - -tileSize = 32 - -body = document.getElementsByTagName('body')[0] - - -loadImage = (url) -> - new Promise (resolve, reject) -> - img = new Image() - img.addEventListener 'load', -> resolve img - img.addEventListener 'error', -> reject Error('Failed to load ' + url) - img.src = url - -loadImages = (imgs) -> - util.mapPromises(_.mapValues imgs, loadImage) - -loadTiles = (tiles) -> - loadImages(_.mapValues tiles, (t) -> "resources/sprite/tile/#{t.file}.png") - -loadEntities = (entities) -> - p = {} - for e in entities - do (e) -> - p[e.entity.name] = loadImage "resources/sprite/entity/#{e.entity.name}.png" - - util.mapPromises p - - -class MapView - constructor: (@map, @entities) -> - @redrawPending = false - - @canvas = document.createElement 'canvas' - @canvas.style.position = 'absolute' - body.appendChild @canvas - - @ctx = @canvas.getContext '2d' - - window.addEventListener 'resize', @setSize - @setSize() - - tilesReady = loadTiles(@map.tiles).then (tiles) => - @tiles = tiles - - entitiesReady = loadEntities(_.values @entities).then (entities) => - @entitySprites = entities - - tilesReady.then(entitiesReady).then => - @redraw() - return - - drawTile: (x, y, tile) => - return unless tile - - @ctx.drawImage tile, x, y - - drawEntity: (e) => - sprite = @entitySprites[e.entity.name] - return unless sprite - - @ctx.drawImage( - sprite, - e.direction*tileSize, 0, - tileSize, tileSize, - e.position.x*tileSize, e.position.y*tileSize, - tileSize, tileSize) - - draw: => - @redrawPending = false - - @ctx.clearRect 0, 0, @canvas.width, @canvas.height - - return unless @tiles and @entitySprites - - for layer in @map.layers - y = 0 - - for row in layer - x = 0 - - for tile in row - @drawTile x, y, @tiles[tile] - x += tileSize - - y += tileSize - - for e in _.values @entities - @drawEntity e - - redraw: => - unless @redrawPending - @redrawPending = true - window.requestAnimationFrame @draw - - setSize: => - e = document.documentElement - @canvas.width = window.innerWidth || e.clientWidth || body.clientWidth - @canvas.height = window.innerHeight || e.clientHeight || body.clientHeight - - @redraw() - - - -module.exports = MapView diff --git a/src/view/MapView.ts b/src/view/MapView.ts new file mode 100644 index 0000000..b366d28 --- /dev/null +++ b/src/view/MapView.ts @@ -0,0 +1,142 @@ +'use strict'; + + +import * as util from '../util'; +import EntityPosition from '../model/EntityPosition'; +import MapData from '../model/MapData'; + + +const tileSize = 32; + +const body = document.getElementsByTagName('body')[0]; + + +function loadImage(url: string): Promise<HTMLImageElement> { + return new Promise(function(resolve, reject) { + var img = new Image(); + img.addEventListener('load', () => { resolve(img); }); + img.addEventListener('error', () => { reject(Error('Failed to load ' + url)); }); + img.src = url; + }); +} + +function loadImages(imgs: {[key: string]: string}): Promise<{[key: string]: HTMLImageElement}> { + return util.mapPromises(_.mapValues(imgs, loadImage)); +} + +function loadTiles(tiles: {[key: string]: {file: string}}): Promise<{[key: string]: HTMLImageElement}> { + return loadImages(_.mapValues(tiles, (t) => `resources/sprite/tile/${t.file}.png`)); +} + +function loadEntities(entities: EntityPosition[]): Promise<{[key: string]: HTMLImageElement}> { + var p: {[key: string]: Promise<HTMLImageElement>} = {}; + + entities.forEach(e => { + p[e.entity.name] = loadImage(`resources/sprite/entity/${e.entity.name}.png`); + }); + + return util.mapPromises(p); +} + + +export default class MapView { + redrawPending: boolean = false; + + canvas: HTMLCanvasElement; + ctx: CanvasRenderingContext2D; + + tiles: {[key: string]: HTMLImageElement}; + entitySprites: {[key: string]: HTMLImageElement}; + + constructor(private map: MapData, private entities: {[key: string]: EntityPosition}) { + this.canvas = document.createElement('canvas'); + this.canvas.style.position = 'absolute'; + body.appendChild(this.canvas); + + this.ctx = this.canvas.getContext('2d'); + + window.addEventListener('resize', () => this.setSize()); + this.setSize(); + + var tilesReady = loadTiles(map.tiles).then((tiles) => { + this.tiles = tiles; + }); + + var entitiesReady = loadEntities(this.getEntities()).then((entities) => { + this.entitySprites = entities; + }); + + Promise.all([tilesReady, entitiesReady]).then(() => { + this.redraw(); + }); + } + + getEntities(): EntityPosition[] { + return _.valuesIn<EntityPosition>(this.entities); + } + + setSize() { + var e = document.documentElement; + this.canvas.width = window.innerWidth || e.clientWidth || body.clientWidth; + this.canvas.height = window.innerHeight || e.clientHeight || body.clientHeight; + + this.redraw() + } + + drawTile(x: number, y: number, tile: HTMLImageElement) { + if (!tile) + return; + + this.ctx.drawImage(tile, x, y); + } + + drawEntity(e: EntityPosition) { + var sprite = this.entitySprites[e.entity.name]; + if (!sprite) + return; + + this.ctx.drawImage( + sprite, + e.direction*tileSize, 0, + tileSize, tileSize, + e.position.x*tileSize, e.position.y*tileSize, + tileSize, tileSize + ); + } + + draw() { + this.redrawPending = false; + + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + if (!this.tiles || !this.entitySprites) + return; + + this.map.layers.forEach((layer) => { + let y = 0; + + layer.forEach((row) => { + let x = 0; + + for (let tile in row) { + this.drawTile(x, y, this.tiles[row[tile]]); + x += tileSize; + } + + y += tileSize; + }); + }); + + this.getEntities().forEach(e => { + this.drawEntity(e); + }); + } + + redraw() { + if (this.redrawPending) + return; + + this.redrawPending = true; + window.requestAnimationFrame(() => this.draw()); + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1aef94c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "noImplicitAny": true, + "sourceMap": true + }, + "files": [ + "require.d.ts", + "typings/tsd.d.ts" + ] +} diff --git a/tsd.json b/tsd.json new file mode 100644 index 0000000..854a888 --- /dev/null +++ b/tsd.json @@ -0,0 +1,15 @@ +{ + "version": "v4", + "repo": "borisyankov/DefinitelyTyped", + "ref": "master", + "path": "typings", + "bundle": "typings/tsd.d.ts", + "installed": { + "lodash/lodash.d.ts": { + "commit": "f11e49cfb77f99678657ddfe5d43017849675c64" + }, + "es6-promise/es6-promise.d.ts": { + "commit": "f11e49cfb77f99678657ddfe5d43017849675c64" + } + } +} diff --git a/webpack.config.js b/webpack.config.js index d069bb6..1979538 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,7 +1,7 @@ var HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { - entry: './src/app.coffee', + entry: './src/app.ts', output: { path: './build', filename: 'bundle.js' @@ -14,10 +14,10 @@ module.exports = { module: { loaders: [ { test: /\.css$/, loader: 'style-loader!css-loader' }, - { test: /\.coffee$/, loader: 'coffee-loader' } + { test: /\.ts$/, loader: 'ts-loader' } ] }, resolve: { - extensions: ['', '.js', '.json', '.coffee'] + extensions: ['', '.ts'] } }; |