]> git.plutz.net Git - flarejs/blob - engine.js
46cb2299d7d5b3469c67fabcc2dbe175d72743bd
[flarejs] / engine.js
1 var gamedata = {
2   load: function(def) {
3     if (this[def]) return this[def];
4     else return this[def] = new Textfile(def);
5   }
6 }
7 var gfx = {
8   load: function(def) {
9     if (this[def]) return this[def];
10     else {
11       this[def] = document.createElement("img");
12       this[def].setAttribute("src", def);
13       return this[def];
14     }
15   }
16 };
17
18 function Textfile(txtfile) {
19   var lines, fetch = new XMLHttpRequest();
20   fetch.open("GET", txtfile, false);
21   fetch.send();
22
23   var line, key, value, ref = this, section;
24   for (line of fetch.responseText.split('\n')) switch(true) {
25     case /^\[.*\]$/.test(line):
26       section = line.split(/[\]\[]/)[1]; ref = {};
27       if                     (!this[section]) this[section] = ref;
28       else if (!Array.isArray(this[section])) this[section] = [this[section],ref];
29       else if ( Array.isArray(this[section])) this[section].push(ref);
30       break;
31     case /^frame=[0-9]+,[0-7](,[0-9-]+){6}$/.test(line):
32       key = line.split(/[=,]/).slice(1,3);  value = line.split(/,/).slice(-6);
33       if (!ref.frame) ref.frame = [];
34       if (!ref.frame[key[0]]) ref.frame[key[0]] = [];
35       ref.frame[key[0]][key[1]] = value.map(x => parseInt(x));
36       break;
37     case /^layer=[0-9]+,/.test(line):
38       key = line.split(/[=,]/)[1];  value = line.split(/,/).slice(1);
39       if (!ref.layer) ref.layer = []; ref.layer[key] = value;
40       break;
41     case /^data=$/.test(line):
42       ref.data = [];
43       break;
44     case /^[0-9,]+,$/.test(line):
45       value = line.split(/,/).slice(0,-1);
46       ref.data = ref.data.concat(value);
47       break;
48     case /^[0-9,]+$/.test(line):
49       value = line.split(/,/);
50       ref.data = ref.data.concat(value);
51       ref.data = ref.data.map(x => parseInt(x));
52       break;
53     case /^tile=[0-9]+,/.test(line):
54       key = line.split(/[=,]/)[1];  value = line.split(/,/).slice(1);
55       if (!ref.tile) ref.tile = []; ref.tile[key] = value.map(x => parseInt(x));
56       break;
57     case /^animation=[0-9]+;.*;$/.test(line):
58       key = line.split(/[=;]/)[1];  value = line.split(/;/).slice(1,-1);
59       value = value.map(x => x.split(/,/));
60       value.forEach(x => x[2] = parseInt(x[2]));
61       if (!ref.animation) ref.animation = []; ref.animation[key] = value;
62       break;
63     case /^duration=[0-9]+ms$/.test(line):
64       ref["duration"] = line.split(/=|ms$/)[1];
65       break;
66     case /^duration=[0-9]+s$/.test(line):
67       ref["duration"] = line.split(/=|s$/)[1] * 1000;
68       break;
69     case /^[^#].*=[0-9]+$/.test(line):
70       key = line.split(/[=]/)[0];  value = line.split(/=/).slice(1) * 1;
71       if                     (!ref[key]) ref[key] = value;
72       else if (!Array.isArray(ref[key])) ref[key] = [ref[key],value];
73       else if ( Array.isArray(ref[key])) ref[key].push(value);
74       break;
75     case /^[^#].*=.+$/.test(line):
76       key = line.split(/[=]/)[0];  value = line.split(/=/).slice(1).join("=");
77       if                     (!ref[key]) ref[key] = value;
78       else if (!Array.isArray(ref[key])) ref[key] = [ref[key],value];
79       else if ( Array.isArray(ref[key])) ref[key].push(value);
80       break;
81   }
82 }
83
84 function Map(textdef) {
85   this.frametime = performance.now();
86   this.info = gamedata.load(textdef);
87   this.tileset = gamedata.load(this.info.header.tileset);
88   gfx.load(this.tileset.img);
89
90   var  h = this.info.header.height,      w = this.info.header.width;
91   var th = this.info.header.tileheight, tw = this.info.header.tilewidth;
92   var posx = canvas.canvas.width / 2, posy = canvas.canvas.height / 2 - h * th/2;
93
94   canvas.fillStyle = "rgba("+this.info.header.background_color+")";
95
96   this.tileAt = function(x, y) {
97     nx = (y + th / 2) / th + (x - w * tw / 2) / tw |0;
98     ny = (y + th / 2) / th - (x - w * tw / 2) / tw |0;
99     return ny * w + nx;
100   }
101
102   this.center = function(x, y) {
103     posx = canvas.canvas.width  / 2 - x;
104     posy = canvas.canvas.height / 2 - y;
105     return this;
106   }
107
108   this.draw = function(mobs) {
109     var x, y, dx, dy, i;
110     var bg = this.info.layer.find(l => l.type == "background").data;
111     var ob = this.info.layer.find(l => l.type == "object").data;
112     var mm = [];
113     mobs.forEach(m => {
114       i = this.tileAt(m.position[0], m.position[1]);
115       mm[i] = mobs.filter(m => i == this.tileAt(m.position[0], m.position[1]));
116     });
117     canvas.fillRect(0,0, canvas.canvas.width, canvas.canvas.height);
118
119     for ( y = 0; y < h; y++ ) for ( x = 0; x < w; x++ ) {
120       i = y * h + x;
121       dx = posx + (w + x - y) * tw /2;
122       dy = posy + (x + y) * th / 2;
123       this.draw_tile(bg[i], dx, dy);
124       this.draw_tile(ob[i], dx, dy);
125       if (mm[i]) mm[i].forEach(m => m.draw(dx, dy));
126     }
127   }
128
129   this.draw_tile = function(tile, x, y) {
130     x = x |0; y = y |0;
131     var t = this.tileset.tile[tile];
132     var f = this.tileset.animation[tile];
133
134     if (t && f) {
135       frame = ((performance.now() - this.frametime) / f[0][2] |0) % f.length;
136       canvas.drawImage(gfx[this.tileset.img], f[frame][0], f[frame][1], t[2], t[3],
137                                                     x - t[4], y - t[5], t[2], t[3]);
138     } else if (t) {
139       canvas.drawImage(gfx[this.tileset.img], t[0], t[1], t[2], t[3],
140                                       x - t[4], y - t[5], t[2], t[3]);
141     }
142   }
143 }
144
145 function Mob(textdef) {
146   this.position = [0, 0];
147   var info = gamedata.load(textdef);
148   var direction = 0;
149   var animation = "stance";
150   var previous_animation = "";
151   var frametime = performance.now();
152   gfx.load(info.image)
153
154   this.place = function(x, y) { this.position = [x, y]; return this; }
155   this.direct = function(d) { direction = d % 8; return this; }
156   this.animate = function(a) {
157     if ( a != animation ) {
158       previous_animation = animation;
159       animation = a;
160       frametime = performance.now();
161     }
162     return this;
163   }
164
165   this.draw = function(x, y){
166     var f, a = info[animation];
167     var frame = ( performance.now() - frametime ) * a.frames / a.duration | 0;
168
169     switch(a.type){
170       case "looped":
171         frame = frame % a.frames;
172         break;
173       case "play_once":
174         if ( frame >= a.frames ){
175           animation = previous_animation;
176           previous_animation = "";
177           frametime = performance.now();
178           a = info[animation];
179           frame = 0;
180         }
181         break;
182       case "back_forth":
183         frame = frame % (a.frames * 2 - 2);
184         if ( frame >= a.frames ){
185          frame = a.frames - frame % a.frames - 1;
186         }
187         break;
188       default: break;
189     }
190     f = a.frame[frame][direction];
191
192     canvas.drawImage(gfx[info.image], f[0], f[1], f[2], f[3],
193                      x - f[4], y - f[5],
194                      f[2], f[3]);
195   }
196
197   this.block  = function() { this.animate("block" ); return this; };
198   this.cast   = function() { this.animate("cast"  ); return this; };
199   this.die    = function() { this.animate("die"   ); return this; };
200   this.hit    = function() { this.animate("hit"   ); return this; };
201   this.run    = function() { this.animate("run"   ); return this; };
202   this.shoot  = function() { this.animate("shoot" ); return this; };
203   this.stance = function() { this.animate("stance"); return this; };
204   this.swing  = function() { this.animate("swing" ); return this; };
205 }
206
207 function Hero(gender = "female", hair = "short"){
208   this.position = [0,0];
209   this.stats = gamedata.load("/engine/stats.txt");
210   var layers = gamedata.load("/engine/hero_layers.txt");
211   var hair = (gender == "female")?"long":hair;
212   var direction = 0, animation = "stance";
213
214   limbs = {
215     head : new Mob("/animations/avatar/"+gender+"/head_"+hair+".txt"),
216     chest: new Mob("/animations/avatar/"+gender+"/default_chest.txt"),
217     hands: new Mob("/animations/avatar/"+gender+"/default_hands.txt"),
218     legs : new Mob("/animations/avatar/"+gender+"/default_legs.txt"),
219     feet : new Mob("/animations/avatar/"+gender+"/default_feet.txt"),
220     main : new Mob("/animations/avatar/"+gender+"/dagger.txt"),
221     off  : new Mob("/animations/avatar/"+gender+"/shield.txt")
222   }
223
224   this.dress = function(limb, item) {
225     limbs[limb] = new Mob("/animations/avatar/"+gender+"/"+item+".txt")
226     limbs[limb].place(this.position[0], this.position[1]).direct(direction);
227     this.animate(animation);
228     return this;
229   }
230
231   this.place   = function(x,y) {
232     this.position = [x,y];
233     for (var limb in limbs) limbs[limb].place(x,y);
234     return this;
235   }
236   this.direct  = function(d)   {
237     direction = d;
238     for (var limb in limbs) limbs[limb].direct(d);
239     return this;
240   }
241   this.animate = function(anim){
242     animation = anim;
243     for (var limb in limbs) limbs[limb].animate(anim);
244     return this;
245   }
246   this.draw    = function(x, y){
247     layers.layer[direction].forEach(limb => limbs[limb].draw(x, y));
248     return this;
249   }
250
251   this.block  = function() { this.animate("block" ); return this; };
252   this.cast   = function() { this.animate("cast"  ); return this; };
253   this.die    = function() { this.animate("die"   ); return this; };
254   this.hit    = function() { this.animate("hit"   ); return this; };
255   this.run    = function() { this.animate("run"   ); return this; };
256   this.shoot  = function() { this.animate("shoot" ); return this; };
257   this.stance = function() { this.animate("stance"); return this; };
258   this.swing  = function() { this.animate("swing" ); return this; };
259 }
260
261 function Controls(hero, map){
262   var keys = [];
263
264   function input(){
265     var d = {};
266     d.l = keys[65] || keys[37]; d.r = keys[68] || keys[39];
267     d.u = keys[87] || keys[38]; d.d = keys[83] || keys[40];
268     if ( d.l ) hero.direct(0).run(); if ( d.r ) hero.direct(4).run();
269     if ( d.u ) hero.direct(2).run(); if ( d.d ) hero.direct(6).run();
270     if ( d.u && d.l ) hero.direct(1).run();
271     if ( d.u && d.r ) hero.direct(3).run();
272     if ( d.d && d.l ) hero.direct(7).run();
273     if ( d.d && d.r ) hero.direct(5).run();
274     if ( ! (d.l || d.r || d.u || d.d) ) hero.stance();
275   }
276   window.addEventListener("keydown", function(e){ keys[e.keyCode] = true ; input();} );
277   window.addEventListener("keyup"  , function(e){ keys[e.keyCode] = false; input();} );
278 }
279
280 document.querySelector("body").setAttribute("style", "margin: 0; padding: 0;");
281 canvas = document.createElement("canvas").getContext("2d");
282 canvas.canvas.setAttribute("style", "display: block; border: 0; margin: 0; padding: 0;");
283 document.querySelector("body").appendChild(canvas.canvas);
284
285 canvas.canvas.width  = window.innerWidth;
286 canvas.canvas.height = window.innerHeight;
287 window.addEventListener("resize", function(){
288   canvas.canvas.width  = window.innerWidth;
289   canvas.canvas.height = window.innerHeight;
290 });
291
292 player = new Hero();
293 map = new Map("/maps/arrival.txt");
294 player.place(map.info.header.hero_pos.split(/,/)[0] * map.info.header.tilewidth,
295              map.info.header.hero_pos.split(/,/)[1] * map.info.header.tileheight);
296 player.direct(5).stance();
297 c = new Controls(player, map);
298
299 setInterval (function() { map.center(player.position[0], player.position[1]).draw([player]);}, 33.33 );