Compare commits
10 commits
a09b58b54c
...
4258619517
Author | SHA1 | Date | |
---|---|---|---|
4258619517 | |||
794ab39a6b | |||
8acc22138d | |||
8567ed9397 | |||
8ee24fdbdf | |||
4de2a6636a | |||
9f74746b2e | |||
a2ea4e5ef5 | |||
00970b9faa | |||
6c896c3f23 |
14 changed files with 223 additions and 86 deletions
|
@ -6,12 +6,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"css-loader": "^0.23.1",
|
||||
"html-webpack-plugin": "^1.7.0",
|
||||
"lodash": "^3.10.1",
|
||||
"style-loader": "^0.13.0",
|
||||
"style-loader": "^0.13.1",
|
||||
"ts-loader": "^0.7.2",
|
||||
"typescript": "^1.7.5",
|
||||
"webpack": "^1.12.9",
|
||||
"webpack-dev-server": "^1.14.0"
|
||||
"typescript": "^1.8.10",
|
||||
"webpack": "^1.13.1",
|
||||
"webpack-dev-server": "^1.14.1"
|
||||
}
|
||||
}
|
||||
|
|
12
src/app.ts
12
src/app.ts
|
@ -11,6 +11,16 @@ import MapContext from './control/MapContext';
|
|||
import MapData from './model/MapData';
|
||||
import InputHandler from './view/InputHandler';
|
||||
|
||||
import * as util from './util';
|
||||
|
||||
|
||||
var relevantKeys = [
|
||||
InputHandler.Up,
|
||||
InputHandler.Right,
|
||||
InputHandler.Down,
|
||||
InputHandler.Left,
|
||||
];
|
||||
|
||||
|
||||
var mapContext: MapContext;
|
||||
|
||||
|
@ -19,7 +29,7 @@ window.onload = () => {
|
|||
|
||||
xhr.onload = function() {
|
||||
var mapDef = new MapData(JSON.parse(this.responseText));
|
||||
var inputHandler = new InputHandler();
|
||||
var inputHandler = new InputHandler(util.arrayToObject(relevantKeys));
|
||||
mapContext = new MapContext(mapDef, inputHandler);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,13 +13,23 @@ import MapView from '../view/MapView';
|
|||
|
||||
|
||||
export default class MapContext {
|
||||
view: MapView;
|
||||
private view: MapView;
|
||||
|
||||
entities: EntityPosition[] = [];
|
||||
playerEntity: EntityPosition;
|
||||
private entities: EntityPosition[] = [];
|
||||
private playerEntity: EntityPosition;
|
||||
|
||||
private collision: number[][];
|
||||
|
||||
|
||||
constructor(private map: MapData, private inputHandler: InputHandler) {
|
||||
this.collision = new Array(map.width);
|
||||
for (let i = 0; i < map.width; i++) {
|
||||
this.collision[i] = new Array(map.height);
|
||||
|
||||
for (let j = 0; j < map.height; j++)
|
||||
this.collision[i][j] = map.collision[j][i] == '0' ? 0 : 1;
|
||||
}
|
||||
|
||||
this.playerEntity = new EntityPosition(
|
||||
new Entity('square'),
|
||||
new Position(8, 8),
|
||||
|
@ -28,6 +38,12 @@ export default class MapContext {
|
|||
|
||||
this.addEntity(this.playerEntity);
|
||||
|
||||
this.addEntity(new EntityPosition(
|
||||
new Entity('square'),
|
||||
new Position(10, 10),
|
||||
Direction.East
|
||||
));
|
||||
|
||||
this.view = new MapView(
|
||||
map,
|
||||
this.entities,
|
||||
|
@ -41,24 +57,50 @@ export default class MapContext {
|
|||
});
|
||||
}
|
||||
|
||||
private addEntity(entity: EntityPosition) {
|
||||
|
||||
private inMap(p: Position): boolean {
|
||||
return p.x >= 0 && p.x < this.map.width && p.y >= 0 && p.y < this.map.height;
|
||||
}
|
||||
|
||||
private incCollision(p: Position): void {
|
||||
if (this.inMap(p))
|
||||
this.collision[p.x][p.y]++;
|
||||
}
|
||||
|
||||
private decCollision(p: Position): void {
|
||||
if (this.inMap(p))
|
||||
this.collision[p.x][p.y]--;
|
||||
}
|
||||
|
||||
private collides(p: Position): boolean {
|
||||
return (!this.inMap(p)) || (this.collision[p.x][p.y] > 0);
|
||||
}
|
||||
|
||||
private addEntity(entity: EntityPosition): void {
|
||||
this.entities.push(entity);
|
||||
|
||||
this.incCollision(entity.position);
|
||||
}
|
||||
|
||||
private addTransition(entity: EntityPosition, dest: Position, start: number, dur: number) {
|
||||
private addTransition(entity: EntityPosition, dest: Position, start: number, dur: number): void {
|
||||
entity.transition = new Transition(start, start+dur, entity.position, dest);
|
||||
|
||||
this.incCollision(dest);
|
||||
}
|
||||
|
||||
private finishTransition(entity: EntityPosition) {
|
||||
private finishTransition(entity: EntityPosition): void {
|
||||
this.decCollision(entity.position);
|
||||
|
||||
entity.position = entity.transition.dest;
|
||||
entity.transition = null;
|
||||
|
||||
}
|
||||
|
||||
private updateState(time: number): boolean {
|
||||
var ret = false;
|
||||
|
||||
while (true) {
|
||||
var origTime = time;
|
||||
let origTime = time;
|
||||
|
||||
if (this.playerEntity.transition && this.playerEntity.transition.end <= time) {
|
||||
origTime = this.playerEntity.transition.end;
|
||||
|
@ -69,7 +111,7 @@ export default class MapContext {
|
|||
if (this.playerEntity.transition)
|
||||
return true;
|
||||
|
||||
var dir: Direction = null;
|
||||
let dir: Direction = null;
|
||||
|
||||
if (this.inputHandler.keys[InputHandler.Up])
|
||||
dir = Direction.North;
|
||||
|
@ -84,8 +126,13 @@ export default class MapContext {
|
|||
return ret;
|
||||
|
||||
this.playerEntity.direction = dir;
|
||||
this.addTransition(this.playerEntity, this.playerEntity.position.translate(dir, 1),
|
||||
origTime, 250);
|
||||
ret = true;
|
||||
|
||||
let dest = this.playerEntity.position.translate(dir, 1);
|
||||
if (this.collides(dest))
|
||||
return true;
|
||||
|
||||
this.addTransition(this.playerEntity, dest, origTime, 250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import Transition from './Transition';
|
|||
|
||||
|
||||
export default class EntityPosition {
|
||||
public transition: Transition = null;
|
||||
transition: Transition = null;
|
||||
|
||||
constructor(public entity: Entity, public position: Position, public direction: Direction) {}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
import TileData from './TileData';
|
||||
|
||||
|
||||
interface Input {
|
||||
tiles: {[key: string]: {file: string}};
|
||||
tiles: {[key: string]: TileData};
|
||||
collision: string[];
|
||||
layers: string[][][];
|
||||
}
|
||||
|
||||
export default class MapData {
|
||||
tiles: {[key: string]: {file: string}};
|
||||
public collision: string[];
|
||||
public layers: string[][][];
|
||||
tiles: {[key: string]: TileData};
|
||||
collision: string[];
|
||||
layers: string[][][];
|
||||
|
||||
width: number;
|
||||
height: number;
|
||||
|
||||
constructor(data: Input) {
|
||||
this.tiles = data.tiles;
|
||||
this.collision = data.collision;
|
||||
this.layers = data.layers;
|
||||
|
||||
this.height = this.collision.length;
|
||||
this.width = this.collision[0].length;
|
||||
}
|
||||
}
|
||||
|
|
7
src/model/TileData.ts
Normal file
7
src/model/TileData.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
export default class TileData {
|
||||
file: string;
|
||||
subtile: number;
|
||||
}
|
13
src/util.ts
13
src/util.ts
|
@ -2,8 +2,8 @@
|
|||
|
||||
|
||||
export function mapPromises<T>(promises: {[key: string]: Promise<T>}): Promise<{[key: string]: T}> {
|
||||
var p: Promise<void>[] = []
|
||||
var ret: {[key: string]: T} = {}
|
||||
var p: Promise<void>[] = [];
|
||||
var ret: {[key: string]: T} = {};
|
||||
|
||||
_.forOwn(promises, (v, k) => {
|
||||
p.push(v.then(r => {ret[k] = r;}));
|
||||
|
@ -11,3 +11,12 @@ export function mapPromises<T>(promises: {[key: string]: Promise<T>}): Promise<{
|
|||
|
||||
return Promise.all(p).then(() => ret);
|
||||
}
|
||||
|
||||
export function arrayToObject(arr: (number|string)[]): {[key: string]: boolean} {
|
||||
var ret: {[key: string]: boolean} = {};
|
||||
|
||||
for (let v of arr)
|
||||
ret[v] = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -7,14 +7,19 @@ import Direction from '../model/Direction';
|
|||
class InputHandler {
|
||||
keys: {[key: number]: boolean} = {};
|
||||
|
||||
listeners: (() => void)[] = [];
|
||||
private listeners: (() => void)[] = [];
|
||||
|
||||
private callListeners() {
|
||||
private callListeners(): void {
|
||||
this.listeners.forEach(l => l());
|
||||
}
|
||||
|
||||
constructor() {
|
||||
constructor(private relevantKeys: {[key: number]: boolean}) {
|
||||
window.addEventListener('keydown', (ev) => {
|
||||
if (!relevantKeys[ev.keyCode])
|
||||
return;
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
if (!this.keys[ev.keyCode]) {
|
||||
this.keys[ev.keyCode] = true;
|
||||
this.callListeners();
|
||||
|
@ -22,6 +27,11 @@ class InputHandler {
|
|||
});
|
||||
|
||||
window.addEventListener('keyup', (ev) => {
|
||||
if (!relevantKeys[ev.keyCode])
|
||||
return;
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
if (this.keys[ev.keyCode]) {
|
||||
delete this.keys[ev.keyCode];
|
||||
this.callListeners();
|
||||
|
|
|
@ -5,6 +5,7 @@ import * as util from '../util';
|
|||
import EntityPosition from '../model/EntityPosition';
|
||||
import MapData from '../model/MapData';
|
||||
import Position from '../model/Position';
|
||||
import TileData from '../model/TileData';
|
||||
|
||||
|
||||
const tileSize = 32;
|
||||
|
@ -25,8 +26,14 @@ function loadImages(imgs: {[key: string]: string}): Promise<{[key: string]: HTML
|
|||
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 loadTiles(tiles: {[key: string]: TileData}): Promise<{[key: string]: HTMLImageElement}> {
|
||||
var imgs: {[key: string]: string} = {}
|
||||
|
||||
_.forOwn(tiles, t => {
|
||||
imgs[t.file] = `resources/sprite/tile/${t.file}.png`
|
||||
});
|
||||
|
||||
return loadImages(imgs);
|
||||
}
|
||||
|
||||
function loadEntities(entities: EntityPosition[]): Promise<{[key: string]: HTMLImageElement}> {
|
||||
|
@ -100,7 +107,7 @@ export default class MapView {
|
|||
});
|
||||
}
|
||||
|
||||
private setSize() {
|
||||
private setSize(): void {
|
||||
var e = document.documentElement;
|
||||
var w = window.innerWidth || e.clientWidth || body.clientWidth;
|
||||
var h = window.innerHeight || e.clientHeight || body.clientHeight;
|
||||
|
@ -114,11 +121,27 @@ export default class MapView {
|
|||
this.redraw()
|
||||
}
|
||||
|
||||
private drawTile(x: number, y: number, tile: HTMLImageElement) {
|
||||
private drawSprite(img: HTMLImageElement, srcX: number, srcY: number, destX: number, destY: number): void {
|
||||
this.ctx.drawImage(
|
||||
img,
|
||||
tiles(srcX), tiles(srcY),
|
||||
tileSize, tileSize,
|
||||
tiles(destX)*this.scale,
|
||||
tiles(destY)*this.scale,
|
||||
tileSize*this.scale,
|
||||
tileSize*this.scale
|
||||
);
|
||||
}
|
||||
|
||||
private drawTile(x: number, y: number, tile: TileData): void {
|
||||
if (!tile)
|
||||
return;
|
||||
|
||||
this.ctx.drawImage(tile, tiles(x)*this.scale, tiles(y)*this.scale, tileSize*this.scale, tileSize*this.scale);
|
||||
var img = this.tiles[tile.file];
|
||||
if (!img)
|
||||
return;
|
||||
|
||||
this.drawSprite(img, tile.subtile || 0, 0, x, y);
|
||||
}
|
||||
|
||||
private drawEntity(e: EntityPosition, time: number): boolean {
|
||||
|
@ -127,19 +150,12 @@ export default class MapView {
|
|||
return false;
|
||||
|
||||
var p = entityPosition(e, time);
|
||||
|
||||
this.ctx.drawImage(
|
||||
sprite,
|
||||
tiles(e.direction), 0,
|
||||
tileSize, tileSize,
|
||||
tiles(p.x)*this.scale, tiles(p.y)*this.scale,
|
||||
tileSize*this.scale, tileSize*this.scale
|
||||
);
|
||||
this.drawSprite(sprite, e.direction, 0, p.x, p.y);
|
||||
|
||||
return !!e.transition;
|
||||
}
|
||||
|
||||
private setOrigin(time: number) {
|
||||
private setOrigin(time: number): Rect {
|
||||
var origin = entityPosition(this.origin, time);
|
||||
|
||||
var w = this.canvas.width;
|
||||
|
@ -156,7 +172,7 @@ export default class MapView {
|
|||
);
|
||||
}
|
||||
|
||||
private draw(time: number) {
|
||||
private draw(time: number): void {
|
||||
this.updateState(time);
|
||||
|
||||
this.redrawPending = false;
|
||||
|
@ -186,7 +202,7 @@ export default class MapView {
|
|||
if (!tile)
|
||||
continue;
|
||||
|
||||
this.drawTile(x, y, this.tiles[tile]);
|
||||
this.drawTile(x, y, this.map.tiles[tile]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -204,7 +220,7 @@ export default class MapView {
|
|||
this.redraw();
|
||||
}
|
||||
|
||||
redraw() {
|
||||
redraw(): void {
|
||||
if (this.redrawPending)
|
||||
return;
|
||||
|
||||
|
|
10
static/index.html
Normal file
10
static/index.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>RPGedit</title>
|
||||
</head>
|
||||
<body>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,42 +1,42 @@
|
|||
{
|
||||
"tiles": {
|
||||
"G": {"rotate": 0, "file": "grass"},
|
||||
"<": {"rotate": 0, "file": "road_left"},
|
||||
">": {"rotate": 0, "file": "road_right"}
|
||||
"G": {"file": "grass"},
|
||||
"<": {"file": "road", "subtile": 0},
|
||||
">": {"file": "road", "subtile": 1}
|
||||
},
|
||||
"collision": [
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111",
|
||||
"11111111111111111111111111111111"
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000110000000000000000000",
|
||||
"00000000000000000000000000000000"
|
||||
],
|
||||
"layers": [
|
||||
[
|
||||
|
|
BIN
static/resources/sprite/tile/road.png
Normal file
BIN
static/resources/sprite/tile/road.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 329 B |
|
@ -3,10 +3,32 @@
|
|||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"noImplicitAny": true,
|
||||
"sourceMap": true
|
||||
"sourceMap": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"files": [
|
||||
"require.d.ts",
|
||||
"typings/tsd.d.ts"
|
||||
]
|
||||
"typings/tsd.d.ts",
|
||||
"src/app.ts",
|
||||
"src/control/MapContext.ts",
|
||||
"src/model/Direction.ts",
|
||||
"src/model/Entity.ts",
|
||||
"src/model/EntityPosition.ts",
|
||||
"src/model/MapData.ts",
|
||||
"src/model/Position.ts",
|
||||
"src/model/TileData.ts",
|
||||
"src/model/Transition.ts",
|
||||
"src/util.ts",
|
||||
"src/view/InputHandler.ts",
|
||||
"src/view/MapView.ts"
|
||||
],
|
||||
"filesGlob": [
|
||||
"require.d.ts",
|
||||
"typings/tsd.d.ts",
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"compileOnSave": false,
|
||||
"atom": {
|
||||
"rewriteTsconfig": true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,6 @@ module.exports = {
|
|||
path: './build',
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
title: 'RPGedit'
|
||||
})
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{ test: /\.css$/, loader: 'style-loader!css-loader' },
|
||||
|
@ -19,5 +14,8 @@ module.exports = {
|
|||
},
|
||||
resolve: {
|
||||
extensions: ['', '.ts']
|
||||
},
|
||||
ts: {
|
||||
compilerOptions: { noEmit: false }
|
||||
}
|
||||
};
|
||||
|
|
Reference in a new issue