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