Fix lives to not reset when rejoining
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / snake.qc
1 REGISTER_MINIGAME(snake, "Snake"); // SNAAAAKE
2
3 const float SNAKE_TURN_MOVE  = 0x0100; // the snake is moving, player must control it
4 const float SNAKE_TURN_WIN   = 0x0200; // multiplayer victory
5 const float SNAKE_TURN_LOSS  = 0x0400; // they did it?!
6 const float SNAKE_TURN_TYPE  = 0x0f00; // turn type mask
7
8 const int SNAKE_TURN_TEAM  = 0x000f; // turn team mask
9
10 const int SNAKE_SF_PLAYERSCORE = MINIG_SF_CUSTOM;
11
12 const int SNAKE_LET_CNT = 15;
13 const int SNAKE_NUM_CNT = 15;
14
15 const int SNAKE_TILE_SIZE = 15;
16
17 const int SNAKE_TEAMS = 6;
18
19 bool autocvar_sv_minigames_snake_wrap = false;
20 float autocvar_sv_minigames_snake_delay_initial = 0.7;
21 float autocvar_sv_minigames_snake_delay_multiplier = 50;
22 float autocvar_sv_minigames_snake_delay_min = 0.1;
23 int autocvar_sv_minigames_snake_lives = 3;
24
25 .int snake_score;
26
27 .float snake_delay;
28 .vector snake_dir;
29
30 .bool snake_tail;
31
32 .int snake_lives[SNAKE_TEAMS + 1];
33
34 .int snake_lost_teams;
35
36 bool snake_alone(entity minig)
37 {
38         int headcount = 0;
39         entity e = world;
40         while ( ( e = findentity(e,owner,minig) ) )
41                 if ( e.classname == "minigame_board_piece" && e.cnt == 1 )
42                         ++headcount;
43
44         return headcount <= 1;
45 }
46
47 // find same game piece given its tile name
48 entity snake_find_piece(entity minig, string tile)
49 {
50         entity e = world;
51         while ( ( e = findentity(e,owner,minig) ) )
52                 if ( e.classname == "minigame_board_piece" && e.netname == tile )
53                         return e;
54         return world;
55 }
56
57 // find same game piece given its cnt
58 entity snake_find_cnt(entity minig, int steam, int tile)
59 {
60         entity e = world;
61         while ( ( e = findentity(e,owner,minig) ) )
62                 if ( e.classname == "minigame_board_piece" && e.cnt == tile && e.team == steam )
63                         return e;
64         return world;
65 }
66
67 // check if the tile name is valid (15x15 grid)
68 bool snake_valid_tile(string tile)
69 {
70         if ( !tile )
71                 return false;
72         int number = minigame_tile_number(tile);
73         int letter = minigame_tile_letter(tile);
74         return 0 <= number && number < SNAKE_NUM_CNT && 0 <= letter && letter < SNAKE_LET_CNT;
75 }
76
77 entity snake_find_head(entity minig, int steam)
78 {
79         entity e = world;
80         while ( ( e = findentity(e,owner,minig) ) )
81                 if ( e.classname == "minigame_board_piece" && e.cnt == 1 && e.team == steam )
82                         return e;
83         return world;
84 }
85
86 void snake_new_mouse(entity minigame)
87 {
88         RandomSelection_Init();
89         int i, j;
90         for(i = 0; i < SNAKE_LET_CNT; ++i)
91         for(j = 0; j < SNAKE_NUM_CNT; ++j)
92         {
93                 string pos = minigame_tile_buildname(i, j);
94                 if(!snake_find_piece(minigame, pos))
95                         RandomSelection_Add(world, 0, pos, 1, 1);
96         }
97
98         entity piece = msle_spawn(minigame,"minigame_board_piece");
99         piece.team = 0;
100         piece.netname = strzone(RandomSelection_chosen_string);
101         minigame_server_sendflags(piece,MINIG_SF_ALL);
102
103         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
104 }
105
106 entity snake_get_player(entity minigame, int pteam);
107 int snake_winning_team(entity minigame)
108 {
109         int winning_team = 0;
110         for(int i = 1; i <= SNAKE_TEAMS; ++i)
111         {
112                 entity pl = snake_get_player(minigame, i);
113                 if(pl && minigame.snake_lives[i] > 0)
114                 {
115                         if(winning_team)
116                                 return 0;
117                         winning_team = i;
118                 }
119         }
120
121         return winning_team;
122 }
123
124 void snake_check_winner(entity minigame)
125 {
126         if(snake_alone(minigame) && !minigame.snake_lost_teams)
127                 return;
128
129         int winner = snake_winning_team(minigame);
130
131         int alivecnt = 0;
132         for(int i = 1; i <= SNAKE_TEAMS; ++i)
133         {
134                 entity pl = snake_get_player(minigame, i);
135                 if(pl && minigame.snake_lives[i] > 0)
136                         ++alivecnt;
137         }
138
139         if(!alivecnt)
140         {
141                 minigame.minigame_flags = SNAKE_TURN_LOSS;
142                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
143                 return;
144         }
145
146         if(winner)
147         {
148                 minigame.minigame_flags = SNAKE_TURN_WIN | winner;
149                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
150         }
151 }
152
153 void snake_move_head(entity minigame, entity head);
154 void snake_head_think()
155 {
156         entity minigame = self.owner;
157
158         if(minigame.minigame_flags & SNAKE_TURN_MOVE)
159                 snake_move_head(minigame, self);
160
161         snake_check_winner(minigame);
162
163         self.nextthink = time + self.snake_delay;
164 }
165
166 void minigame_setup_snake(entity minigame, int pteam)
167 {
168         RandomSelection_Init();
169         int i, j;
170         for(i = 1; i < SNAKE_LET_CNT - 1; ++i)
171         for(j = 1; j < SNAKE_NUM_CNT - 1; ++j)
172         {
173                 string pos = minigame_tile_buildname(i, j);
174                 if(!snake_find_piece(minigame, pos))
175                         RandomSelection_Add(world, 0, pos, 1, 1);
176         }
177
178         entity piece = msle_spawn(minigame,"minigame_board_piece");
179         piece.team = pteam;
180         piece.netname = strzone(RandomSelection_chosen_string);
181         piece.cnt = 1;
182         piece.think = snake_head_think;
183         piece.snake_delay = autocvar_sv_minigames_snake_delay_initial;
184         piece.nextthink = time + 0.1;
185         minigame_server_sendflags(piece,MINIG_SF_ALL);
186 }
187
188 void snake_setup_pieces(entity minigame)
189 {
190         snake_new_mouse(minigame);
191
192         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
193 }
194
195 entity snake_get_player(entity minigame, int pteam)
196 {
197         entity e;
198 #ifdef SVQC
199         for(e = minigame.minigame_players; e; e = e.list_next)
200 #elif defined(CSQC)
201         e = world;
202         while( (e = findentity(e,owner,minigame)) )
203                 if ( e.classname == "minigame_player" )
204 #endif
205         if(e.team == pteam)
206                 return e;
207         return world;
208 }
209
210 void snake_add_score(entity minigame, int pteam, int thescore)
211 {
212 #ifdef SVQC
213         if(!minigame)
214                 return;
215         entity pl = snake_get_player(minigame, pteam);
216         if(pl)
217         {
218                 pl.snake_score += thescore;
219                 pl.SendFlags |= SNAKE_SF_PLAYERSCORE;
220         }
221 #endif
222 }
223
224 void snake_move_body(entity minigame, entity head, bool ate_mouse)
225 {
226         entity tail = world;
227         string tailpos = string_null;
228         vector taildir = '0 0 0';
229
230         int i, pieces = 0;
231         for(i = (SNAKE_NUM_CNT * SNAKE_LET_CNT); i >= 2; --i)
232         {
233                 entity piece = snake_find_cnt(minigame, head.team, i);
234                 entity nextpiece = snake_find_cnt(minigame, head.team, i - 1);
235                 if(!piece)
236                         continue;
237
238                 pieces++;
239
240                 if(!tail)
241                 {
242                         tail = piece;
243                         tailpos = piece.netname;
244                         taildir = piece.snake_dir;
245                 }
246
247                 if(piece.netname) { strunzone(piece.netname); }
248                 piece.netname = strzone(nextpiece.netname);
249                 piece.snake_dir = nextpiece.snake_dir;
250                 minigame_server_sendflags(piece, MINIG_SF_ALL);
251         }
252
253         // just a head
254         if(!pieces)
255         {
256                 tail = head;
257                 tailpos = head.netname;
258                 taildir = head.snake_dir;
259         }
260
261         if(tail && ate_mouse)
262         {
263                 tail.snake_tail = false;
264
265                 int newcnt = tail.cnt + 1;
266                 head.snake_delay = max(autocvar_sv_minigames_snake_delay_min, autocvar_sv_minigames_snake_delay_initial - (newcnt / autocvar_sv_minigames_snake_delay_multiplier));
267                 snake_add_score(minigame, head.team, 1);
268
269                 entity piece = msle_spawn(minigame,"minigame_board_piece");
270                 piece.cnt = newcnt;
271                 piece.team = head.team;
272                 piece.snake_dir = taildir;
273                 piece.snake_tail = true;
274                 piece.netname = strzone(tailpos);
275                 minigame_server_sendflags(piece,MINIG_SF_ALL);
276
277                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
278         }
279 }
280
281 void snake_eat_team(entity minigame, int pteam)
282 {
283         entity head = snake_find_head(minigame, pteam);
284         if(!head) { return; }
285
286         minigame.snake_lives[pteam] -= 1;
287
288         entity pl = snake_get_player(minigame, pteam);
289 #ifdef SVQC
290         pl.SendFlags |= SNAKE_SF_PLAYERSCORE;
291 #endif
292
293         head.nextthink = time + 1; // make sure they don't to eat us somehow
294
295         entity e = world;
296         while ( ( e = findentity(e,owner,minigame) ) )
297                 if ( e.classname == "minigame_board_piece" && e.cnt && e.team == pteam )
298                 {
299                         if(e.netname) { strunzone(e.netname); }
300                         remove(e);
301                 }
302
303         if(minigame.snake_lives[pteam] <= 0)
304                 minigame.snake_lost_teams |= BIT(pteam);
305
306         if(pl && minigame.snake_lives[pteam] > 0)
307                 minigame_setup_snake(minigame, pteam);  
308 }
309
310 void snake_move_head(entity minigame, entity head)
311 {
312         if(!head.snake_dir_x && !head.snake_dir_y)
313                 return; // nope!
314
315         string newpos;
316
317         if(autocvar_sv_minigames_snake_wrap)
318                 newpos = minigame_relative_tile(head.netname, head.snake_dir_x, head.snake_dir_y, SNAKE_NUM_CNT, SNAKE_LET_CNT);
319         else
320         {
321                 int myx = minigame_tile_letter(head.netname);
322                 int myy = minigame_tile_number(head.netname);
323
324                 myx += head.snake_dir_x;
325                 myy += head.snake_dir_y;
326
327                 newpos = minigame_tile_buildname(myx, myy);
328         }
329
330         entity hit = snake_find_piece(minigame, newpos);
331
332         if(!snake_valid_tile(newpos) || (hit && hit.cnt && hit.team == head.team))
333         {
334                 if(snake_alone(minigame))
335                 {
336                         minigame.minigame_flags = SNAKE_TURN_LOSS;
337                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
338                 }
339                 else
340                 {
341                         snake_add_score(minigame, head.team, -1);
342                         snake_eat_team(minigame, head.team);
343                 }
344
345                 return;
346         }
347
348         bool ate_mouse = (hit && !hit.cnt);
349
350         // move the body first, then set the new head position?
351         snake_move_body(minigame, head, ate_mouse);
352
353         if(head.netname) { strunzone(head.netname); }
354         head.netname = strzone(newpos);
355         minigame_server_sendflags(head,MINIG_SF_ALL);
356
357         // above check makes sure it's not our team
358         if(hit.cnt)
359         {
360                 snake_eat_team(minigame, hit.team);
361                 snake_add_score(minigame, head.team, 1);
362         }
363
364         if(ate_mouse)
365         {
366                 if(hit.netname) { strunzone(hit.netname); }
367                 remove(hit);
368
369                 snake_new_mouse(minigame);
370         }
371 }
372
373 // make a move
374 void snake_move(entity minigame, entity player, string dxs, string dys )
375 {
376         if ( minigame.minigame_flags & SNAKE_TURN_MOVE )
377         if ( dxs || dys )
378         {
379                 //if ( snake_valid_tile(pos) )
380                 //if ( snake_find_piece(minigame, pos) )
381                 {
382                         entity head = snake_find_head(minigame, player.team);
383                         if(!head)
384                                 return; // their head is already dead
385
386                         int dx = ((dxs) ? bound(-1, stof(dxs), 1) : 0);
387                         int dy = ((dys) ? bound(-1, stof(dys), 1) : 0);
388
389                         int myl = minigame_tile_letter(head.netname);
390                         int myn = minigame_tile_number(head.netname);
391
392                         entity check_piece = snake_find_piece(minigame, minigame_tile_buildname(myl + dx, myn + dy));
393                         if(check_piece && check_piece.cnt == 2)
394                                 return; // nope!
395
396                         if(head.snake_dir == '0 0 0')
397                                 head.nextthink = time; // TODO: make sure this can't be exploited!
398                         head.snake_dir_x = dx;
399                         head.snake_dir_y = dy;
400                         head.snake_dir_z = 0;
401                         minigame_server_sendflags(head,MINIG_SF_UPDATE);
402                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
403                 }
404         }
405 }
406
407 #ifdef SVQC
408
409
410 // required function, handle server side events
411 int snake_server_event(entity minigame, string event, ...)
412 {
413         switch(event)
414         {
415                 case "start":
416                 {
417                         snake_setup_pieces(minigame);
418                         minigame.minigame_flags = SNAKE_TURN_MOVE;
419                         minigame.snake_lost_teams = 0;
420
421                         if(SNAKE_TEAMS > 1)
422                         {
423                                 for(int i = 1; i <= SNAKE_TEAMS; ++i)
424                                         minigame.snake_lives[i] = autocvar_sv_minigames_snake_lives;
425                         }
426                         else
427                                 minigame.snake_lives[1] = 1;
428                         
429                         return true;
430                 }
431                 case "end":
432                 {
433                         entity e = world;
434                         while( (e = findentity(e, owner, minigame)) )
435                         if(e.classname == "minigame_board_piece")
436                         {
437                                 if(e.netname) { strunzone(e.netname); }
438                                 remove(e);
439                         }
440                         return false;
441                 }
442                 case "join":
443                 {
444                         int pl_num = minigame_count_players(minigame);
445
446                         if(pl_num >= SNAKE_TEAMS) { return false; }
447
448                         int t = 1; // Team 1 by default
449
450                         for(int i = 1; i <= SNAKE_TEAMS; ++i)
451                         {
452                                 entity e = snake_get_player(minigame, i);
453                                 if(!e)
454                                 {
455                                         t = i;
456                                         break;
457                                 }
458                         }
459
460                         if(!snake_find_head(minigame, t) && !(minigame.snake_lost_teams & BIT(t)))
461                         {
462                                 entity pl = ...(1,entity);
463                                 if(pl)
464                                 {
465                                         //pl.snake_lives = ((SNAKE_TEAMS > 1) ? autocvar_sv_minigames_snake_lives : 1);
466                                         // send score anyway, lives are set
467                                         pl.SendFlags |= SNAKE_SF_PLAYERSCORE;
468                                 }
469                                 minigame_setup_snake(minigame, t);
470                         }
471
472                         return t;
473                 }
474                 case "cmd":
475                 {
476                         switch(argv(0))
477                         {
478                                 case "move": 
479                                         snake_move(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) == 3 ? argv(2) : string_null)); 
480                                         return true;
481                         }
482
483                         return false;
484                 }
485                 case "network_send":
486                 {
487                         entity sent = ...(0,entity);
488                         int sf = ...(1,int);
489                         if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) )
490                         {
491                                 WriteByte(MSG_ENTITY,sent.cnt);
492                                 WriteByte(MSG_ENTITY,sent.snake_tail);
493                                 WriteCoord(MSG_ENTITY,sent.snake_dir_x);
494                                 WriteCoord(MSG_ENTITY,sent.snake_dir_y);
495                         }
496                         else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
497                         {
498                                 WriteLong(MSG_ENTITY,sent.snake_score);
499                                 WriteByte(MSG_ENTITY,max(0, minigame.snake_lives[sent.team]));
500                         }
501                         else if ( sent.classname == "minigame" && (sf & MINIG_SF_UPDATE ) )
502                         {
503                                 WriteByte(MSG_ENTITY,autocvar_sv_minigames_snake_wrap);
504                         }
505                         return false;
506                 }
507         }
508         
509         return false;
510 }
511
512
513 #elif defined(CSQC)
514
515 vector snake_boardpos; // HUD board position
516 vector snake_boardsize;// HUD board size
517
518 bool snake_wrap;
519
520 vector snake_teamcolor(int steam)
521 {
522         switch(steam)
523         {
524                 case 1: return '1 0 0';
525                 case 2: return '0 0 1';
526                 case 3: return '1 1 0';
527                 case 4: return '1 0 1';
528                 case 5: return '0 1 0';
529                 case 6: return '0 1 1';
530         }
531
532         return '1 1 1';
533 }
534
535 // Required function, draw the game board
536 void snake_hud_board(vector pos, vector mySize)
537 {
538         minigame_hud_fitsqare(pos, mySize);
539         snake_boardpos = pos;
540         snake_boardsize = mySize;
541         
542         minigame_hud_simpleboard(pos,mySize,minigame_texture("snake/board"));
543
544         vector tile_size = minigame_hud_denormalize_size('1 1 0' / SNAKE_TILE_SIZE,pos,mySize);
545         vector tile_pos;
546
547         entity e;
548         FOREACH_MINIGAME_ENTITY(e)
549         {
550                 if ( e.classname == "minigame_board_piece" )
551                 {
552                         tile_pos = minigame_tile_pos(e.netname,SNAKE_NUM_CNT,SNAKE_LET_CNT);
553                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
554
555                         vector tile_color = snake_teamcolor(e.team);
556
557                         string thepiece = "snake/mouse";
558                         if(e.cnt)
559                                 thepiece = "snake/body";
560                         if(e.snake_tail)
561                                 thepiece = "snake/tail";
562                         if(e.cnt == 1)
563                         {
564                                 int dx = minigame_tile_letter(e.netname) + e.snake_dir_x * 2;
565                                 int dy = minigame_tile_number(e.netname) + e.snake_dir_y * 2;
566                                 entity mouse = snake_find_piece(active_minigame, minigame_tile_buildname(dx, dy));
567                                 thepiece = "snake/head";
568                                 if(mouse && mouse.team != e.team)
569                                 {
570                                         float myang = 0;
571                                         int myx = minigame_tile_letter(e.netname);
572                                         int myy = minigame_tile_number(e.netname);
573                                         if(myx - 2 == dx)
574                                                 myang = M_PI*3/2;
575                                         if(myx + 2 == dx)
576                                                 myang = M_PI/2;
577                                         if(myy - 2 == dy)
578                                                 myang = M_PI;
579
580                                         int newx = minigame_tile_letter(e.netname) + e.snake_dir_x;
581                                         int newy = minigame_tile_number(e.netname) + e.snake_dir_y;
582                                         string newpos = minigame_tile_buildname(newx, newy);
583
584                                         vector my_pos = minigame_tile_pos(newpos,SNAKE_NUM_CNT,SNAKE_LET_CNT);
585                                         my_pos = minigame_hud_denormalize(my_pos,pos,mySize);
586
587                                         drawrotpic(my_pos, myang, minigame_texture("snake/tongue"),
588                                                         tile_size, tile_size/2, tile_color,
589                                                         panel_fg_alpha, DRAWFLAG_NORMAL );
590                                 }
591                         }
592
593                         if(e.cnt == 1 || e.snake_tail)
594                         {
595                                 vector thedir = e.snake_dir;
596                                 float theang = 0;
597                                 if(e.snake_tail)
598                                 {
599                                         int thex = minigame_tile_letter(e.netname);
600                                         int they = minigame_tile_number(e.netname);
601                                         entity t = snake_find_cnt(active_minigame, minigame_self.team, e.cnt - 1);
602                                         int tx = minigame_tile_letter(t.netname);
603                                         int ty = minigame_tile_number(t.netname);
604
605                                         if(thex - 1 == tx)
606                                         {
607                                                 thedir_y = 0;
608                                                 thedir_x = -1;
609                                         }
610                                         if(they + 1 == ty)
611                                         {
612                                                 thedir_x = 0;
613                                                 thedir_y = 1;
614                                         }
615                                         if(they - 1 == ty)
616                                         {
617                                                 thedir_x = 0;
618                                                 thedir_y = -1;
619                                         }
620                                 }
621
622                                 if(thedir_y == -1)
623                                         theang = M_PI;
624                                 if(thedir_x == 1)
625                                         theang = M_PI/2;
626                                 if(thedir_x == -1)
627                                         theang = M_PI*3/2;
628
629                                 drawrotpic(tile_pos, theang, minigame_texture(thepiece),
630                                                         tile_size, tile_size/2, tile_color,
631                                                         panel_fg_alpha, DRAWFLAG_NORMAL );
632                         }
633                         else
634                         {
635                                 minigame_drawpic_centered( tile_pos,  
636                                                 minigame_texture(thepiece),
637                                                 tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
638                         }
639                 }
640         }
641
642         if ( (active_minigame.minigame_flags & SNAKE_TURN_LOSS) || (active_minigame.minigame_flags & SNAKE_TURN_WIN) || (active_minigame.snake_lives[minigame_self.team] <= 0) )
643         {
644                 int scores = minigame_self.snake_score;
645
646                 vector winfs = hud_fontsize*2;
647                 string scores_text, victory_text;
648                 victory_text = "Game over!";
649                 scores_text = strcat("Score: ", ftos(scores));
650
651                 if(active_minigame.minigame_flags & SNAKE_TURN_WIN)
652                 if((active_minigame.minigame_flags & SNAKE_TURN_TEAM) == minigame_self.team)
653                         victory_text = "You win!";
654                 if(active_minigame.snake_lives[minigame_self.team] <= 0)
655                         victory_text = "You ran out of lives!";
656                 
657                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
658                 vector win_sz;
659                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
660                         sprintf("%s %s", victory_text, scores_text), 
661                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
662                 
663                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'0.3 0.3 1',0.8,DRAWFLAG_ADDITIVE);
664                 
665                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
666                         sprintf("%s %s", victory_text, scores_text), 
667                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
668         }
669 }
670
671
672 // Required function, draw the game status panel
673 void snake_hud_status(vector pos, vector mySize)
674 {
675         HUD_Panel_DrawBg(1);
676         vector ts;
677         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
678                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
679
680         pos_y += ts_y;
681         mySize_y -= ts_y;
682
683         vector player_fontsize = hud_fontsize * 1.75;
684         ts_y = ( mySize_y - 2*player_fontsize_y ) / SNAKE_TEAMS;
685         ts_x = mySize_x;
686         vector mypos;
687         vector tile_size = '48 48 0';
688
689         mypos = pos;
690         if ( minigame_self.team > 1 )
691                 mypos_y  += player_fontsize_y + (ts_y * (minigame_self.team - 1));
692         drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
693         mypos_y += player_fontsize_y;
694         drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
695
696         entity e;
697         FOREACH_MINIGAME_ENTITY(e)
698         {
699                 if ( e.classname == "minigame_player" )
700                 {
701                         mypos = pos;
702                         if ( e.team > 1 )
703                                 mypos_y  += player_fontsize_y + (ts_y * (e.team - 1));
704                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
705                                 GetPlayerName(e.minigame_playerslot-1),
706                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
707
708                         vector tile_color = snake_teamcolor(e.team);
709
710                         mypos_y += player_fontsize_y;
711                         drawpic( mypos,
712                                         minigame_texture("snake/head"),
713                                         tile_size * 0.7, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
714
715                         mypos_x += tile_size_x;
716
717                         drawstring(mypos,ftos(e.snake_score),tile_size,
718                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
719
720                         mypos_x += tile_size_x;
721
722                         drawstring(mypos,strcat("1UP: ", ftos(active_minigame.snake_lives[e.team])),tile_size * 0.6,
723                                                          '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
724                 }
725         }
726 }
727
728 // Turn a set of flags into a help message
729 string snake_turn_to_string(int turnflags)
730 {
731         if ( turnflags & SNAKE_TURN_LOSS )
732                 return _("Game over!");
733
734         if ( turnflags & SNAKE_TURN_WIN )
735         {
736                 if ( (turnflags&SNAKE_TURN_TEAM) != minigame_self.team )
737                         return _("You ran out of lives!");
738                 return _("You win!");
739         }
740
741         if(active_minigame.snake_lives[minigame_self.team] <= 0)
742                 return _("You ran out of lives!");
743         
744         if ( (snake_find_head(active_minigame, minigame_self.team)).snake_dir == '0 0 0' )
745                 return _("Press an arrow key to begin the game");
746
747         if ( turnflags & SNAKE_TURN_MOVE )
748                 if(snake_wrap)
749                         return _("Avoid the snake's body, collect the mice!");
750                 else
751                         return _("Avoid the screen edges and the snake's body, collect the mice!");
752         
753         return "";
754 }
755
756 // Make the correct move
757 void snake_set_direction(entity minigame, int dx, int dy)
758 {
759         //if ( minigame.minigame_flags == SNAKE_TURN_MOVE )
760         //{
761                 minigame_cmd("move ",ftos(dx), " ", ftos(dy));
762         //}
763 }
764
765 // Required function, handle client events
766 int snake_client_event(entity minigame, string event, ...)
767 {
768         switch(event)
769         {
770                 case "activate":
771                 {
772                         minigame.message = snake_turn_to_string(minigame.minigame_flags);
773                         return false;
774                 }
775                 case "key_pressed":
776                 {
777                         //if((minigame.minigame_flags & SNAKE_TURN_TEAM) == minigame_self.team)
778                         {
779                                 switch ( ...(0,int) )
780                                 {
781                                         case K_RIGHTARROW:
782                                         case K_KP_RIGHTARROW:
783                                                 snake_set_direction(minigame, 1, 0);
784                                                 return true;
785                                         case K_LEFTARROW:
786                                         case K_KP_LEFTARROW:
787                                                 snake_set_direction(minigame, -1, 0);
788                                                 return true;
789                                         case K_UPARROW:
790                                         case K_KP_UPARROW:
791                                                 snake_set_direction(minigame, 0, 1);
792                                                 return true;
793                                         case K_DOWNARROW:
794                                         case K_KP_DOWNARROW:
795                                                 snake_set_direction(minigame, 0, -1);
796                                                 return true;
797                                 }
798                         }
799
800                         return false;
801                 }
802                 case "network_receive":
803                 {
804                         entity sent = ...(0,entity);
805                         int sf = ...(1,int);
806                         if ( sent.classname == "minigame" )
807                         {
808                                 if ( sf & MINIG_SF_UPDATE )
809                                 {
810                                         snake_wrap = ReadByte();
811                                         sent.message = snake_turn_to_string(sent.minigame_flags);
812                                         //if ( sent.minigame_flags & minigame_self.team )
813                                                 minigame_prompt();
814                                 }
815                         }
816                         else if(sent.classname == "minigame_board_piece")
817                         {
818                                 if(sf & MINIG_SF_UPDATE)
819                                 {
820                                         sent.cnt = ReadByte();
821                                         sent.snake_tail = ReadByte();
822                                         sent.snake_dir_x = ReadCoord();
823                                         sent.snake_dir_y = ReadCoord();
824                                         sent.snake_dir_z = 0;
825                                 }
826                         }
827                         else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
828                         {
829                                 sent.snake_score = ReadLong();
830                                 minigame.snake_lives[sent.team] = ReadByte();
831                         }
832
833                         return false;
834                 }
835         }
836
837         return false;
838 }
839
840 #endif