Research · Casual, Management · season 1 · shoal/overcooked

Overcooked

To beatStarter (GreedyHumanModel) 97.7778
Join
28c377d
497 lines · 17.3 KB Raw Permalink History
1/*
2
3Added state potential to HUD
4
5*/
6
7
8
9// How long a graphics update should take in milliseconds
10// Note that the server updates at 30 fps
11var ANIMATION_DURATION = 50;
12
13var DIRECTION_TO_NAME = {
14 '0,-1': 'NORTH',
15 '0,1': 'SOUTH',
16 '1,0': 'EAST',
17 '-1,0': 'WEST'
18};
19
20var scene_config = {
21 player_colors : {0: 'blue', 1: 'green'},
22 tileSize : 80,
23 animation_duration : ANIMATION_DURATION,
24 show_post_cook_time : false,
25 cook_time : 20,
26 assets_loc : "./static/assets/",
27 hud_size : 150
28};
29
30var game_config = {
31 type: Phaser.WEBGL,
32 pixelArt: true,
33 audio: {
34 noAudio: true
35 }
36};
37
38var graphics;
39
40// Invoked at every state_pong event from server
41function drawState(state) {
42 // Try catch necessary because state pongs can arrive before graphics manager has finished initializing
43 try {
44 graphics.set_state(state);
45 } catch {
46 console.log("error updating state");
47 }
48};
49
50// Invoked at 'start_game' event
51function graphics_start(graphics_config) {
52 graphics = new GraphicsManager(game_config, scene_config, graphics_config);
53};
54
55// Invoked at 'end_game' event
56function graphics_end() {
57 graphics.game.renderer.destroy();
58 graphics.game.loop.stop();
59 graphics.game.destroy();
60}
61
62class GraphicsManager {
63 constructor(game_config, scene_config, graphics_config) {
64 let start_info = graphics_config.start_info;
65 scene_config.terrain = start_info.terrain;
66 scene_config.start_state = start_info.state;
67 game_config.scene = new OvercookedScene(scene_config);
68 game_config.width = scene_config.tileSize*scene_config.terrain[0].length;
69 game_config.height = scene_config.tileSize*scene_config.terrain.length + scene_config.hud_size;
70 game_config.parent = graphics_config.container_id;
71 this.game = new Phaser.Game(game_config);
72 }
73
74 set_state(state) {
75 this.game.scene.getScene('PlayGame').set_state(state);
76 }
77}
78
79class OvercookedScene extends Phaser.Scene {
80 constructor(config) {
81 super({key: "PlayGame"});
82 this.state = config.start_state.state;
83 this.player_colors = config.player_colors;
84 this.terrain = config.terrain;
85 this.tileSize = config.tileSize;
86 this.animation_duration = config.animation_duration;
87 this.show_post_cook_time = config.show_post_cook_time;
88 this.cook_time = config.cook_time;
89 this.assets_loc = config.assets_loc;
90 this.hud_size = config.hud_size
91 this.hud_data = {
92 potential : config.start_state.potential,
93 score : config.start_state.score,
94 time : config.start_state.time_left,
95 bonus_orders : config.start_state.state.bonus_orders,
96 all_orders : config.start_state.state.all_orders
97 }
98 }
99
100 set_state(state) {
101 this.hud_data.potential = state.potential;
102 this.hud_data.score = state.score;
103 this.hud_data.time = Math.round(state.time_left);
104 this.hud_data.bonus_orders = state.state.bonus_orders;
105 this.hud_data.all_orders = state.state.all_orders;
106 this.state = state.state;
107 }
108
109 preload() {
110 this.load.atlas("tiles",
111 this.assets_loc + "terrain.png",
112 this.assets_loc + "terrain.json");
113 this.load.atlas("chefs",
114 this.assets_loc + "chefs.png",
115 this.assets_loc + "chefs.json");
116 this.load.atlas("objects",
117 this.assets_loc + "objects.png",
118 this.assets_loc + "objects.json");
119 this.load.multiatlas("soups",
120 this.assets_loc + "soups.json",
121 this.assets_loc)
122 }
123
124 create() {
125 this.sprites = {};
126 this.drawLevel();
127 this._drawState(this.state, this.sprites);
128 }
129
130 update() {
131 if (typeof(this.state) !== 'undefined') {
132 this._drawState(this.state, this.sprites);
133 }
134 if (typeof(this.hud_data) !== 'undefined') {
135 let { width, height } = this.game.canvas;
136 let board_height = height - this.hud_size;
137 this._drawHUD(this.hud_data, this.sprites, board_height);
138 }
139 }
140 drawLevel() {
141 // Fill canvas with white
142 this.cameras.main.setBackgroundColor('#e6b453')
143
144 //draw tiles
145 let terrain_to_img = {
146 ' ': 'floor.png',
147 'X': 'counter.png',
148 'P': 'pot.png',
149 'O': 'onions.png',
150 'T': 'tomatoes.png',
151 'D': 'dishes.png',
152 'S': 'serve.png'
153 };
154 let pos_dict = this.terrain;
155 for (let row in pos_dict) {
156 if (!pos_dict.hasOwnProperty(row)) {continue}
157 for (let col = 0; col < pos_dict[row].length; col++) {
158 let [x, y] = [col, row]
159 let ttype = pos_dict[row][col];
160 let tile = this.add.sprite(
161 this.tileSize * x,
162 this.tileSize * y,
163 "tiles",
164 terrain_to_img[ttype]
165 );
166 tile.setDisplaySize(this.tileSize, this.tileSize);
167 tile.setOrigin(0);
168 }
169 }
170 }
171 _drawState (state, sprites) {
172 sprites = typeof(sprites) === 'undefined' ? {} : sprites;
173
174 //draw chefs
175 sprites['chefs'] =
176 typeof(sprites['chefs']) === 'undefined' ? {} : sprites['chefs'];
177 for (let pi = 0; pi < state.players.length; pi++) {
178 let chef = state.players[pi];
179 let [x, y] = chef.position;
180 let dir = DIRECTION_TO_NAME[chef.orientation];
181 let held_obj = chef.held_object;
182 if (typeof(held_obj) !== 'undefined' && held_obj !== null) {
183 if (held_obj.name === 'soup') {
184 let ingredients = held_obj._ingredients.map(x => x['name']);
185 if (ingredients.includes('onion')) {
186 held_obj = "-soup-onion";
187 } else {
188 held_obj = "-soup-tomato";
189 }
190
191 }
192 else {
193 held_obj = "-"+held_obj.name;
194 }
195 }
196 else {
197 held_obj = "";
198 }
199 if (typeof(sprites['chefs'][pi]) === 'undefined') {
200 let chefsprite = this.add.sprite(
201 this.tileSize*x,
202 this.tileSize*y,
203 "chefs",
204 `${dir}${held_obj}.png`
205 );
206 chefsprite.setDisplaySize(this.tileSize, this.tileSize);
207 chefsprite.depth = 1;
208 chefsprite.setOrigin(0);
209 let hatsprite = this.add.sprite(
210 this.tileSize*x,
211 this.tileSize*y,
212 "chefs",
213 `${dir}-${this.player_colors[pi]}hat.png`
214 );
215 hatsprite.setDisplaySize(this.tileSize, this.tileSize);
216 hatsprite.depth = 2;
217 hatsprite.setOrigin(0);
218 sprites['chefs'][pi] = {chefsprite, hatsprite};
219 }
220 else {
221 let chefsprite = sprites['chefs'][pi]['chefsprite'];
222 let hatsprite = sprites['chefs'][pi]['hatsprite'];
223 chefsprite.setFrame(`${dir}${held_obj}.png`);
224 hatsprite.setFrame(`${dir}-${this.player_colors[pi]}hat.png`);
225 this.tweens.add({
226 targets: [chefsprite, hatsprite],
227 x: this.tileSize*x,
228 y: this.tileSize*y,
229 duration: this.animation_duration,
230 ease: 'Linear',
231 onComplete: (tween, target, player) => {
232 target[0].setPosition(this.tileSize*x, this.tileSize*y);
233 //this.animating = false;
234 }
235 })
236 }
237 }
238
239 //draw environment objects
240 if (typeof(sprites['objects']) !== 'undefined') {
241 for (let objpos in sprites.objects) {
242 let {objsprite, timesprite} = sprites.objects[objpos];
243 objsprite.destroy();
244 if (typeof(timesprite) !== 'undefined') {
245 timesprite.destroy();
246 }
247 }
248 }
249 sprites['objects'] = {};
250
251 for (let objpos in state.objects) {
252 if (!state.objects.hasOwnProperty(objpos)) { continue }
253 let obj = state.objects[objpos];
254 let [x, y] = obj.position;
255 let terrain_type = this.terrain[y][x];
256 let spriteframe;
257 let soup_status;
258 if ((obj.name === 'soup') && (terrain_type === 'P')) {
259 let ingredients = obj._ingredients.map(x => x['name']);
260
261 // select pot sprite
262 if (!obj.is_ready) {
263 soup_status = "idle";
264 }
265 else {
266 soup_status = "cooked";
267 }
268 spriteframe = this._ingredientsToSpriteFrame(ingredients, soup_status);
269 let objsprite = this.add.sprite(
270 this.tileSize*x,
271 this.tileSize*y,
272 "soups",
273 spriteframe
274 );
275 objsprite.setDisplaySize(this.tileSize, this.tileSize);
276 objsprite.depth = 1;
277 objsprite.setOrigin(0);
278 let objs_here = {objsprite};
279
280 // show time accordingly
281 let show_time = true;
282 if (obj._cooking_tick > obj.cook_time && !this.show_post_cook_time || obj._cooking_tick == -1) {
283 show_time = false;
284 }
285 if (show_time) {
286 let timesprite = this.add.text(
287 this.tileSize*(x+.5),
288 this.tileSize*(y+.6),
289 String(obj._cooking_tick),
290 {
291 font: "25px Arial",
292 fill: "red",
293 align: "center",
294 }
295 );
296 timesprite.depth = 2;
297 objs_here['timesprite'] = timesprite;
298 }
299
300 sprites['objects'][objpos] = objs_here
301 }
302 else if (obj.name === 'soup') {
303 let ingredients = obj._ingredients.map(x => x['name']);
304 let soup_status = "done";
305 spriteframe = this._ingredientsToSpriteFrame(ingredients, soup_status);
306 let objsprite = this.add.sprite(
307 this.tileSize*x,
308 this.tileSize*y,
309 "soups",
310 spriteframe
311 );
312 objsprite.setDisplaySize(this.tileSize, this.tileSize);
313 objsprite.depth = 1;
314 objsprite.setOrigin(0);
315 sprites['objects'][objpos] = {objsprite};
316 }
317 else {
318 if (obj.name === 'onion') {
319 spriteframe = "onion.png";
320 }
321 else if (obj.name === 'tomato') {
322 spriteframe = "tomato.png";
323 }
324 else if (obj.name === 'dish') {
325 spriteframe = "dish.png";
326 }
327 let objsprite = this.add.sprite(
328 this.tileSize*x,
329 this.tileSize*y,
330 "objects",
331 spriteframe
332 );
333 objsprite.setDisplaySize(this.tileSize, this.tileSize);
334 objsprite.depth = 1;
335 objsprite.setOrigin(0);
336 sprites['objects'][objpos] = {objsprite};
337 }
338 }
339 }
340
341 _drawHUD(hud_data, sprites, board_height) {
342 if (typeof(hud_data.all_orders) !== 'undefined') {
343 this._drawAllOrders(hud_data.all_orders, sprites, board_height);
344 }
345 if (typeof(hud_data.bonus_orders) !== 'undefined') {
346 this._drawBonusOrders(hud_data.bonus_orders, sprites, board_height);
347 }
348 if (typeof(hud_data.time) !== 'undefined') {
349 this._drawTimeLeft(hud_data.time, sprites, board_height);
350 }
351 if (typeof(hud_data.score) !== 'undefined') {
352 this._drawScore(hud_data.score, sprites, board_height);
353 }
354 if (typeof(hud_data.potential) !== 'undefined' && hud_data.potential !== null) {
355 console.log(hud_data.potential)
356 this._drawPotential(hud_data.potential, sprites, board_height);
357 }
358 }
359
360 _drawBonusOrders(orders, sprites, board_height) {
361 if (typeof(orders) !== 'undefined' && orders !== null) {
362 let orders_str = "Bonus Orders: ";
363 if (typeof(sprites['bonus_orders']) !== 'undefined') {
364 // Clear existing orders
365 sprites['bonus_orders']['orders'].forEach(element => {
366 element.destroy();
367 });
368 sprites['bonus_orders']['orders'] = [];
369
370 // Update with new orders
371 for (let i = 0; i < orders.length; i++) {
372 let spriteFrame = this._ingredientsToSpriteFrame(orders[i]['ingredients'], "done");
373 let orderSprite = this.add.sprite(
374 130 + 40 * i,
375 board_height + 40,
376 "soups",
377 spriteFrame
378 );
379 sprites['bonus_orders']['orders'].push(orderSprite);
380 orderSprite.setDisplaySize(60, 60);
381 orderSprite.setOrigin(0);
382 orderSprite.depth = 1;
383 }
384 }
385 else {
386 sprites['bonus_orders'] = {};
387 sprites['bonus_orders']['str'] = this.add.text(
388 5, board_height + 60, orders_str,
389 {
390 font: "20px Arial",
391 fill: "red",
392 align: "left"
393 }
394 )
395 sprites['bonus_orders']['orders'] = []
396 }
397 }
398 }
399
400 _drawAllOrders(orders, sprites, board_height) {
401 if (typeof(orders) !== 'undefined' && orders !== null) {
402 let orders_str = "All Orders: ";
403 if (typeof(sprites['all_orders']) !== 'undefined') {
404 // Clear existing orders
405 sprites['all_orders']['orders'].forEach(element => {
406 element.destroy();
407 });
408 sprites['all_orders']['orders'] = [];
409
410 // Update with new orders
411 for (let i = 0; i < orders.length; i++) {
412 let spriteFrame = this._ingredientsToSpriteFrame(orders[i]['ingredients'], "done");
413 let orderSprite = this.add.sprite(
414 90 + 40 * i,
415 board_height - 4,
416 "soups",
417 spriteFrame
418 );
419 sprites['all_orders']['orders'].push(orderSprite);
420 orderSprite.setDisplaySize(60, 60);
421 orderSprite.setOrigin(0);
422 orderSprite.depth = 1;
423 }
424 }
425 else {
426 sprites['all_orders'] = {};
427 sprites['all_orders']['str'] = this.add.text(
428 5, board_height + 15, orders_str,
429 {
430 font: "20px Arial",
431 fill: "red",
432 align: "left"
433 }
434 )
435 sprites['all_orders']['orders'] = []
436 }
437 }
438 }
439
440 _drawScore(score, sprites, board_height) {
441 score = "Score: "+score;
442 if (typeof(sprites['score']) !== 'undefined') {
443 sprites['score'].setText(score);
444 }
445 else {
446 sprites['score'] = this.add.text(
447 5, board_height + 90, score,
448 {
449 font: "20px Arial",
450 fill: "red",
451 align: "left"
452 }
453 )
454 }
455 }
456
457 _drawPotential(potential, sprites, board_height) {
458 potential = "Potential: "+potential;
459 if (typeof(sprites['potential']) !== 'undefined') {
460 sprites['potential'].setText(potential);
461 }
462 else {
463 sprites['potential'] = this.add.text(
464 100, board_height + 90, potential,
465 {
466 font: "20px Arial",
467 fill: "red",
468 align: "left"
469 }
470 )
471 }
472 }
473
474 _drawTimeLeft(time_left, sprites, board_height) {
475 time_left = "Time Left: "+time_left;
476 if (typeof(sprites['time_left']) !== 'undefined') {
477 sprites['time_left'].setText(time_left);
478 }
479 else {
480 sprites['time_left'] = this.add.text(
481 5, board_height + 115, time_left,
482 {
483 font: "20px Arial",
484 fill: "red",
485 align: "left"
486 }
487 )
488 }
489 }
490
491 _ingredientsToSpriteFrame(ingredients, status) {
492 let num_tomatoes = ingredients.filter(x => x === 'tomato').length;
493 let num_onions = ingredients.filter(x => x === 'onion').length;
494 return `soup_${status}_tomato_${num_tomatoes}_onion_${num_onions}.png`
495 }
496}
497