-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
392 lines (341 loc) · 13.6 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
// const socket = io();
// This is how it waits for events from the server.
/*
socket.on('chat message', function(msg){
$('#messages').append($('<li>').text(msg));
});
*/
// Creating an instance of the PIXI application class.
// First by bringing it into the code, like the way you would
// import { Application, Loader } from 'pixi.js';
const Application = PIXI.Application;
// Then by creating a specific app intsance.
const app = new Application({
width: 256, // Gets overwritten immediately.
height: 256, // Gets overwritten immediately.
transparent: false, // Confused why this is needed.
antialias: true // I don't know what this is.
});
// Set properties like the background color.
app.renderer.backgroundColor = 0x23395D;
// Resize the app to the size of the window.
// app.renderer.resize(window.innerWidth, window.innerHeight);
app.renderer.view.style.position = "absolute";
// Curiously, all of that was not enough to get the window to resize as you
// drag it around. Bruno had us write a function that handled this on
// resize. I wonder if I could find it. But, will abstain, for a minute, here.
// Appending your app to the DOM like this is critical.
document.body.appendChild(app.view);
const name_expansions = {
"p": "pawn",
"k": "king",
"n": "knight",
"r": "rook",
"b": "bishop",
"q": "queen"
};
const color_expansions = { "b": "black", "w": "white" };
// Build an "existential" game engine and state
// manager module called "gui.".
const gui = (function gui_engine_and_state_manager_creator () {
let exp = {};
// Make an identical copy of the board in the engine
const board_object = engine.get_baord_object();
const board = ["", "", "", "", "", "", "", ""].map(function (row, r) {
return board_object[r].map(function (space_value, c) {
return space_value;
});
});
let selected_piece = undefined;
let selected_piece_coords = undefined;
exp.selected_piece_possibles = [];
const draw_movement_possibles = function (list) {
list.forEach(function (alpha_coord) {
const [r, c] = ur.in_r_c(alpha_coord);
if (board[r][c] === "") {
// Raise the dot
move_dots[r + "_" + c].visible = true;
} else {
// Raise the circle;
attack_circles[r + "_" + c].visible = true;
}
});
}
const erase_all_movement_possibles = function (list) {
list.forEach(function (alpha_coord) {
const [r, c] = ur.in_r_c(alpha_coord);
if (board[r][c] === "") {
// Erase the dot;
move_dots[r + "_" + c].visible = false;
} else {
// Erase the circle;
attack_circles[r + "_" + c].visible = false;
}
});
}
exp.a_piece_is_selected = function () {
return (
selected_piece
? true
: false
);
}
exp.get_selected_piece = function () {
return selected_piece;
}
exp.get_selected_piece_coords = function () {
return selected_piece_coords;
}
exp.select_piece = function (r, c) {
let id;
if (board[r][c] !== "") {
selected_piece = board[r][c];
id = board[r][c];
selected_piece_coords = {
"r" : r,
"c": c
};
}
highlights[r + "_" + c].visible = true;
exp.selected_piece_possibles = engine.get_possible_moves_by_id(id);
// DEBUG console.log(`Selected piece ${id} should have possible moves: `);
// DEBUG console.log(exp.selected_piece_possibles);
// For all the possible moves, if it is an enemy, give it a circle,
// and if it is an empty space, give it a little dot.
draw_movement_possibles(exp.selected_piece_possibles);
};
exp.deselect_piece = function () {
const selected_piece_store = selected_piece;
highlights[
selected_piece_coords.r + "_" + selected_piece_coords.c
].visible = false;
selected_piece = undefined;
selected_piece_coords = undefined;
// DEBUG console.log("deselected piece : " + selected_piece_store);
erase_all_movement_possibles(exp.selected_piece_possibles);
exp.selected_piece_possibles = [];
};
const contiuation_listeners = {};
contiuation_listeners.rook_move_from_castle = function (id, r, c) {
board[r][c] = id;
// Yeah this is some bullshit.
board[piece_sprites[id].y / 32][piece_sprites[id].x / 32] = "";
piece_sprites[id].x = 32 * c;
piece_sprites[id].y = 32 * r;
// Make sure its clicable layer element moves with it too.
// socket.emit(
// 'piece move',
// `${id} / r: ${r}, c: ${c}`
// );
};
contiuation_listeners.en_passant = function (passanted_id) {
app.stage.removeChild(piece_sprites[passanted_id]);
};
exp.move_piece = function (id, r, c) {
board[selected_piece_coords.r][selected_piece_coords.c] = "";
exp.deselect_piece()
board[r][c] = id;
// Move the piece in the engine as well.
// This will automatically update the turn object.
let contiuation_sets = engine.move_piece(id, ur.in_a1(r, c));
if (contiuation_sets !== undefined) {
contiuation_sets.forEach(function (set) {
contiuation_listeners[set[0]](...set[1]);
});
};
};
let pieces_promoted_count = { "w": 0, "b": 0 };
contiuation_listeners.promotion_prompt = function (pawn_id, a1_space) {
// Certainly room for improvement here, to actually prompt the user
// for what they want.
// TODO : add an interface to make actual choices.
const [at_r, at_c] = ur.in_r_c(a1_space);
const prompt_answer = "q";
const new_id = pawn_id[0] + prompt_answer + String(
parseInt(3 + pieces_promoted_count[pawn_id[0]])
);
// It's a security risk to have this on the client side.
pieces_promoted_count[pawn_id[0]] += 1;
// Delete the piece at that square.
app.stage.removeChild(piece_sprites[pawn_id]);
// Add a new sprite and place them into the square.
const texture_name = (
color_expansions[new_id[0]] + "_"
+ name_expansions[new_id[1]] + ".png"
);
piece_sprites[new_id] = PIXI.Sprite.from(
piece_sprites.spritesheet.textures[texture_name]
);
piece_sprites[new_id].x = 32 * at_c;
piece_sprites[new_id].y = 32 * at_r;
app.stage.addChild(piece_sprites[new_id]);
selected_piece_coords = { "r": at_r, "c": at_c };
console.log(`new id is ${new_id}`);
exp.move_piece(new_id, at_r, at_c);
};
exp.read_board = function (x, y) {
return board[x][y];
}
// Possible states (also the names of the actions that follow them):
// "Select piece".
// "Move piece" / "Select open square."
return exp;
}());
// Create some basic shapes.
const Graphics = PIXI.Graphics;
// Chessboard Layer
const tile_size = 32;
const light_tile_color = 0xffffff; // nice beige: 0xfff4d6; // 0xb5b5b5
const dark_tile_color = 0x5fcde4;// 0x007013; // the blue I had. 0x5fcde4; // green 0x038a1a
const move_dots = {};
const attack_circles = {};
const highlights = {};
const move_ops_color = 0xfbf236; // 0xfbf236;
const move_ops_opacity = 1;
// 0-1 percentage of how much of the tile it should take up,
// converted to whole pixels.
const dot_size_percentage = 0.35;
const half_tile = Math.floor(tile_size / 2); // Should be even, though.
const dot_radius = Math.floor((tile_size * dot_size_percentage) / 2);
const dot_offset = Math.floor(half_tile);
const attack_circle_line_width = 5;
// Shrink the radius to account for the border / edge width
const attack_circle_radius = half_tile - Math.floor(attack_circle_line_width/2);
for (let j=0; j<8; j++) {
for (let i=0; i<8; i++) {
// Draw the squares layer, alternating between light and dark
// squares depending on maths.
app.stage.addChild(
new Graphics()
.beginFill(
(i+j) % 2 === 0
? light_tile_color
: dark_tile_color
)
.drawRect(i*tile_size, j*tile_size, tile_size, tile_size)
.endFill()
);
// Draw the highlight layer.
highlights[String(j) + "_" + String(i)] = new Graphics()
.beginFill(
move_ops_color, move_ops_opacity
)
.drawRect(i*tile_size, j*tile_size, tile_size, tile_size)
.endFill();
highlights[String(j) + "_" + String(i)].visible = false;
app.stage.addChild(highlights[j + "_" + i]);
// Draw the move dots layer.
move_dots[String(j) + "_" + String(i)] = new Graphics()
.beginFill(
move_ops_color, move_ops_opacity
)
.drawCircle(
i*tile_size + dot_offset,
j*tile_size + dot_offset,
dot_radius
).endFill();
move_dots[String(j) + "_" + String(i)].visible = false;
app.stage.addChild(move_dots[j + "_" + i]);
// Draw the attack circles layer.
attack_circles[String(j) + "_" + String(i)] = new Graphics()
.lineStyle(
attack_circle_line_width, move_ops_color, move_ops_opacity
).drawCircle(
i*tile_size + half_tile,
j*tile_size + half_tile,
attack_circle_radius
).endFill();
attack_circles[String(j) + "_" + String(i)].visible = false;
app.stage.addChild(attack_circles[j + "_" + i]);
}
}
const piece_sprites = {};
async function load_sprites () {
const additions = { "b": 0, "w": 4 }
piece_sprites.spritesheet = await PIXI.Assets.load(
'images/chesspeople_spritesheet.json'
);
// console.log(piece_sprites.spritesheet.textures);
const roster_pointer = engine.get_roster_object();
Object.keys(roster_pointer).forEach(function (id) {
const texture_name = (
color_expansions[id[0]] + "_" + name_expansions[id[1]] + ".png"
);
piece_sprites[id] = PIXI.Sprite.from(
piece_sprites.spritesheet.textures[texture_name]
);
piece_sprites[id].x = 32 * roster_pointer[id].c;
piece_sprites[id].y = 32 * roster_pointer[id].r;
app.stage.addChild(piece_sprites[id]);
});
};
load_sprites();
const register_click_at = function (r, c) {
// State manager.
// Should be called "is there a piece selected"..
if (gui.a_piece_is_selected()) {
if (
r === gui.get_selected_piece_coords().r
&& c === gui.get_selected_piece_coords().c
) {
// deselect the peice.
gui.deselect_piece();
// Else move the piece if you clicked on one of its possible moves.
} else if (gui.selected_piece_possibles.includes(ur.in_a1(r, c))) {
const move_piece_name = gui.get_selected_piece();
piece_sprites[move_piece_name].x = 32 * c;
piece_sprites[move_piece_name].y = 32 * r;
const move_to_value = gui.read_board(r, c);
if (move_to_value !== "") {
app.stage.removeChild(piece_sprites[move_to_value]);
}
gui.move_piece(move_piece_name, r, c);
// socket.emit(
// 'piece move',
// `${move_piece_name} / r: ${r}, c: ${c}`
// );
}
}
// Else if there is a piece where you are clicking, and it is of the
// color whose turn it is...
else if (
// gui.read_board(r, c) !== "" (implied)
engine.get_current_turn_color_char() === gui.read_board(r, c)[0]
) {
// select it.
gui.select_piece(r, c);
}
}
// Clickable layer.
const visible_color = 0xf7766d;
// Preferred is 0x65e6fc when it doesn't clash with the background color.
const visibility_alpha = 0.001;
const click_border = 2;
for (let j=0; j<8; j++) {
for (let i=0; i<8; i++) {
const chess_alpha = ["A", "B", "C", "D", "E", "F", "G", "H"];
const clickable_square = new Graphics()
.beginFill(visible_color, visibility_alpha)
.drawRect(
j * tile_size + click_border,
i * tile_size + click_border,
tile_size - (click_border * 2),
tile_size - (click_border * 2)
).on("pointerdown", function () {
// DEBUG
/*
console.log(
"Registered click at: " + String(i) + ", " + String(j)
+ " (space " + chess_alpha[j] + String(8-i) + ")"
);
*/
register_click_at(i, j);
})
.endFill();
clickable_square.interactive = true;
app.stage.addChild(clickable_square);
}
}
// There it is. I have just finished my first ever custom clicable
// interface sitting invisibly on top of a high-performance 2D rendering
// library. We are officially out of CSS.