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