├── README.md
├── html5-rpg.png
├── images
├── forest.png
├── grass.png
├── hill.png
├── king.png
├── minister.png
├── mountain.png
├── player.png
├── soldier.png
└── water.png
├── index.html
├── js
├── character.js
├── main.js
├── map.js
├── player.js
└── utility.js
├── map
├── test.evt
└── test.map
└── music
├── castle.mp3
├── castle.wav
├── field.mp3
└── field.wav
/README.md:
--------------------------------------------------------------------------------
1 | html5-rpg
2 | =========
3 |
4 | old-fashioned role playing game using html5 canvas and javascript
5 |
6 | License
7 | -------
8 | MIT License
9 |
10 |
11 |
--------------------------------------------------------------------------------
/html5-rpg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/html5-rpg.png
--------------------------------------------------------------------------------
/images/forest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/forest.png
--------------------------------------------------------------------------------
/images/grass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/grass.png
--------------------------------------------------------------------------------
/images/hill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/hill.png
--------------------------------------------------------------------------------
/images/king.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/king.png
--------------------------------------------------------------------------------
/images/minister.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/minister.png
--------------------------------------------------------------------------------
/images/mountain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/mountain.png
--------------------------------------------------------------------------------
/images/player.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/player.png
--------------------------------------------------------------------------------
/images/soldier.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/soldier.png
--------------------------------------------------------------------------------
/images/water.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/images/water.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 | HTML5 RPG
16 |
17 |
18 | HTML5 RPG
19 | Project Home
20 | Under construction ...
21 | Test on newest Firefox, Safari and Chrome.
22 |
25 | usage: move (arrow-key)
26 | Debug Information
27 |
28 | keycode
29 | player position
30 |
31 |
32 |
--------------------------------------------------------------------------------
/js/character.js:
--------------------------------------------------------------------------------
1 | STOP = 0;
2 | MOVE = 1;
3 | PROB_MOVE = 0.1;
4 |
5 | function Character(name, x, y, dir, movetype, message) {
6 | this.name = name;
7 | this.x = x;
8 | this.y = y;
9 | this.px = this.x * GS;
10 | this.py = this.y * GS;
11 | this.vx = 0;
12 | this.vy = 0;
13 | this.speed = 4;
14 | this.moving = false;
15 | this.direction = dir;
16 | this.movetype = movetype;
17 | this.animcycle = 12;
18 | this.frame = 0;
19 | this.message = message;
20 |
21 | // images are class property
22 | Character.images = new Object();
23 | var names = ["player", "king", "minister", "soldier"];
24 | for (var i = 0; i < names.length; i++) {
25 | Character.images[names[i]] = new Image();
26 | Character.images[names[i]].src = "images/" + names[i] + ".png";
27 | }
28 | }
29 |
30 | Character.prototype.update = function(map) {
31 | this.frame += 1;
32 |
33 | // continue moving until player fits in the fixed cell
34 | if (this.moving == true) {
35 | this.px += this.vx;
36 | this.py += this.vy;
37 | if (this.px % GS == 0 && this.py % GS == 0) {
38 | this.moving = false;
39 | this.x = div(this.px, GS);
40 | this.y = div(this.py, GS);
41 | } else {
42 | return;
43 | }
44 | } else if (this.movetype == MOVE && Math.random() < PROB_MOVE) {
45 | this.direction = Math.floor(Math.random() * 4); // 0 - 3
46 | this.moveStart(this.direction, map);
47 | }
48 | }
49 |
50 | Character.prototype.draw = function(ctx, offset) {
51 | offsetx = offset[0];
52 | offsety = offset[1];
53 | var no = div(this.frame, this.animcycle) % 4
54 | ctx.drawImage(Character.images[this.name], no*GS, this.direction*GS, GS, GS,
55 | this.px-offsetx, this.py-offsety, GS, GS);
56 | }
57 |
58 | Character.prototype.moveStart = function(dir, map) {
59 | if (dir == LEFT) {
60 | this.direction = LEFT;
61 | if (map.isMovable(this.x-1, this.y)) {
62 | this.vx = - this.speed;
63 | this.vy = 0;
64 | this.moving = true;
65 | }
66 | } else if (dir == UP) {
67 | this.direction = UP;
68 | if (map.isMovable(this.x, this.y-1)) {
69 | this.vx = 0;
70 | this.vy = - this.speed;
71 | this.moving = true;
72 | }
73 | } else if (dir == RIGHT) {
74 | this.direction = RIGHT;
75 | if (map.isMovable(this.x+1, this.y)) {
76 | this.vx = this.speed;
77 | this.vy = 0;
78 | this.moving = true;
79 | }
80 | } else if (dir == DOWN) {
81 | this.direction = DOWN;
82 | if (map.isMovable(this.x, this.y+1)) {
83 | this.vx = 0;
84 | this.vy = this.speed;
85 | this.moving = true;
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | var musicList = [
2 | { name : "field", url: "music/field" },
3 | { name : "castle", url: "music/castle" }
4 | ];
5 |
6 | window.onload = function() {
7 | // initialize global objects
8 | activeKey = null;
9 | map = new Map("test");
10 | player = new Player("player", 1, 1, DOWN);
11 | map.addChara(player);
12 |
13 | // start mainloop
14 | setInterval('mainLoop()', 16);
15 |
16 | // start music
17 | var bgmNo = 0;
18 | var audioObj = new Audio();
19 | if (audioObj.canPlayType("audio/wav") == "maybe") { var ext = ".wav"; }
20 | if (audioObj.canPlayType("audio/mp3") == "maybe") { var ext = ".mp3"; }
21 | (new Audio(musicList[bgmNo].url + ext)).play();
22 | }
23 |
24 | function mainLoop() {
25 | var ctx = document.getElementById('canvas').getContext('2d');
26 |
27 | // initialize
28 | ctx.clearRect(0, 0, WIDTH, HEIGHT);
29 |
30 | // update
31 | map.update();
32 |
33 | // draw
34 | offset = calcOffset(player);
35 | map.draw(ctx, offset);
36 | player.draw(ctx, offset);
37 | }
38 |
39 | // key management
40 |
41 | document.onkeydown = function(e) {
42 | activeKey = e.which;
43 | e.preventDefault();
44 | }
45 |
46 | document.onkeyup = function(e) {
47 | activeKey = null;
48 | e.preventDefault();
49 | }
50 |
51 | // calculate player-map offset
52 | function calcOffset(player) {
53 | var offsetx = player.px - WIDTH / 2;
54 | var offsety = player.py - HEIGHT / 2;
55 | return [offsetx, offsety];
56 | }
57 |
--------------------------------------------------------------------------------
/js/map.js:
--------------------------------------------------------------------------------
1 | function Map(name) {
2 | this.name = name;
3 | this.row = -1;
4 | this.col = -1;
5 | this.defaultTile = 0;
6 | this.data = null;
7 | this.charas = []; // characters on this map
8 |
9 | // images are class property
10 | Map.images = new Array(256);
11 | for (var i = 0; i < 256; i++) {
12 | Map.images[i] = new Image();
13 | }
14 | Map.images[0].src = "images/grass.png";
15 | Map.images[1].src = "images/water.png";
16 | Map.images[2].src = "images/forest.png";
17 | Map.images[3].src = "images/hill.png";
18 | Map.images[4].src = "images/mountain.png";
19 |
20 | // load map data
21 | this.load("map/" + name + ".map");
22 |
23 | // load event data
24 | this.loadEvent("map/" + name + ".evt");
25 | }
26 |
27 | Map.prototype.load = function(filename) {
28 | // load map file
29 | var httpObj = $.ajax({
30 | url: filename,
31 | async: false // synchronous (wait until file is loaded)
32 | });
33 | var lines = httpObj.responseText.split("\n");
34 | // the number of row and column
35 | var temp = lines[0].split(" ");
36 | this.row = parseInt(temp[0]);
37 | this.col = parseInt(temp[1]);
38 | // default map tile value
39 | this.defaultTile = parseInt(lines[1]);
40 | // map data
41 | this.data = new Array(this.row);
42 | for (var i = 0; i < this.row; i++) {
43 | this.data[i] = new Array(this.col);
44 | for (var j = 0; j < this.col; j++) {
45 | // map data starts from lines[2]
46 | this.data[i][j] = parseInt(lines[i+2][j]);
47 | }
48 | }
49 | }
50 |
51 | Map.prototype.loadEvent = function(filename) {
52 | // load event file
53 | var httpObj = $.ajax({
54 | url: filename,
55 | async: false
56 | });
57 | var lines = httpObj.responseText.split("\n");
58 | for (var i = 0; i < lines.length - 1; i++) {
59 | if (lines[i][0] == "#") continue; // comment line
60 | var data = lines[i].split("\t");
61 | var eventType = data[0];
62 | if (eventType == "CHARA") {
63 | this.createChara(data);
64 | }
65 | }
66 | }
67 |
68 | Map.prototype.update = function() {
69 | // update characters on this map
70 | for (var i = 0; i < this.charas.length; i++) {
71 | this.charas[i].update(this);
72 | }
73 | }
74 |
75 | Map.prototype.draw = function(ctx, offset) {
76 | offsetx = offset[0];
77 | offsety = offset[1];
78 |
79 | // calculate the range of map
80 | startx = div(offsetx, GS);
81 | endx = startx + div(WIDTH, GS) + 1;
82 | starty = div(offsety, GS);
83 | endy = starty + div(HEIGHT, GS) + 1;
84 | for (var y = starty; y < endy; y++) {
85 | for (var x = startx; x < endx; x++) {
86 | // draw default image at the outside of map
87 | if (x < 0 || y < 0 || x > this.col-1 || y > this.row-1) {
88 | ctx.drawImage(Map.images[this.defaultTile], x*GS-offsetx, y*GS-offsety);
89 | } else {
90 | ctx.drawImage(Map.images[this.data[y][x]], x*GS-offsetx, y*GS-offsety);
91 | }
92 | }
93 | }
94 |
95 | // draw characters on this map
96 | for (var i = 0; i < this.charas.length; i++) {
97 | this.charas[i].draw(ctx, offset);
98 | }
99 | }
100 |
101 | Map.prototype.isMovable = function(x, y) {
102 | if (x < 0 || x > this.col-1 || y < 0 || y > this.row-1) {
103 | return false;
104 | }
105 | // cannot move at sea(1) and mountan(4) tile
106 | if (this.data[y][x] == 1 || this.data[y][x] == 4) {
107 | return false;
108 | }
109 |
110 | // cannot move to character cell
111 | for (var i = 0; i < this.charas.length; i++) {
112 | if (this.charas[i].x == x && this.charas[i].y == y) {
113 | return false;
114 | }
115 | }
116 |
117 | return true;
118 | }
119 |
120 | Map.prototype.addChara = function(chara) {
121 | this.charas.push(chara);
122 | }
123 |
124 | Map.prototype.createChara = function(data) {
125 | var name = data[1];
126 | var x = parseInt(data[2]);
127 | var y = parseInt(data[3]);
128 | var direction = parseInt(data[4]);
129 | var moveType = parseInt(data[5]);
130 | var message = data[6];
131 | var chara = new Character(name, x, y, direction, moveType, message);
132 | this.addChara(chara);
133 | }
134 |
--------------------------------------------------------------------------------
/js/player.js:
--------------------------------------------------------------------------------
1 | // Player is a subclass of Character
2 | function Player(name, x, y, dir) {
3 | Character.call(this, name, x, y, dir);
4 | }
5 |
6 | Player.prototype = new Character();
7 |
8 | Player.prototype.update = function(map) {
9 | this.frame += 1;
10 |
11 | // continue moving until player fits in the fixed cell
12 | if (this.moving == true) {
13 | this.px += this.vx;
14 | this.py += this.vy;
15 | if (this.px % GS == 0 && this.py % GS == 0) {
16 | this.moving = false;
17 | this.x = div(this.px, GS);
18 | this.y = div(this.py, GS);
19 | } else {
20 | return;
21 | }
22 | }
23 |
24 | // activeKey defined at main.js
25 | if (activeKey == 37) {
26 | this.moveStart(LEFT, map);
27 | } else if (activeKey == 38) {
28 | this.moveStart(UP, map);
29 | } else if (activeKey == 39) {
30 | this.moveStart(RIGHT, map);
31 | } else if (activeKey == 40) {
32 | this.moveStart(DOWN, map);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/js/utility.js:
--------------------------------------------------------------------------------
1 | // const
2 | WIDTH = 640;
3 | HEIGHT = 480;
4 | GS = 32;
5 |
6 | DOWN = 0;
7 | LEFT = 1;
8 | RIGHT = 2;
9 | UP = 3;
10 |
11 | function div(a, b) {
12 | return Math.round(a / b - 0.5);
13 | }
14 |
15 | function DisplayPropertyNames(obj) {
16 | var names = "";
17 | for (var name in obj) names += name + " / ";
18 | alert(names);
19 | }
20 |
--------------------------------------------------------------------------------
/map/test.evt:
--------------------------------------------------------------------------------
1 | # test2.evt
2 | # event file
3 | CHARA minister 3 1 0 1 I am a minister.
4 | CHARA king 2 1 0 0 I am a king!
5 | CHARA soldier 4 1 0 1 I am a soldier.
6 |
--------------------------------------------------------------------------------
/map/test.map:
--------------------------------------------------------------------------------
1 | 15 20
2 | 1
3 | 11111111111111111111
4 | 10000000000000000001
5 | 10003333300000000001
6 | 10004444400000000001
7 | 10000022222200000001
8 | 10000002222222000001
9 | 10000002211222200001
10 | 10000002211220000001
11 | 10000000222200000001
12 | 10000000000044400001
13 | 10000000004440000001
14 | 10000444444000000001
15 | 10000333300000000001
16 | 10000000000000000001
17 | 11111111111111111111
18 |
--------------------------------------------------------------------------------
/music/castle.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/music/castle.mp3
--------------------------------------------------------------------------------
/music/castle.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/music/castle.wav
--------------------------------------------------------------------------------
/music/field.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/music/field.mp3
--------------------------------------------------------------------------------
/music/field.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aidiary/html5-rpg/5580534154d58c9a3558db17692c803aab7d337d/music/field.wav
--------------------------------------------------------------------------------