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