]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/snake.qc
Merge branch 'master' into Mario/wepent_experimental
[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_AddString(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_AddString(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();
678         vector 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 = player_fontsize.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 + eY * (e.team - 1) * ts.y;
695
696                         if (e == minigame_self)
697                         {
698                                 const vector hl_size = '1 1 0';
699                                 drawfill(mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE);
700                                 drawborderlines(hl_size.x, mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), panel_fg_alpha, DRAWFLAG_NORMAL);
701                         }
702                         else
703                                 drawfill(mypos, ts, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE);
704
705                         minigame_drawcolorcodedstring_trunc(mySize.x - hud_fontsize.x * 0.5, mypos + eX * hud_fontsize.x * 0.25,
706                                 entcs_GetName(e.minigame_playerslot - 1),
707                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
708
709                         mypos.y += player_fontsize.y;
710                         drawstring_aspect(mypos, ftos(e.snake_score), ts - eY * player_fontsize.y - eX * ts.x * (3 / 4),
711                                                                 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
712                         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),
713                                                                 '1 0.44 0.54', panel_fg_alpha, DRAWFLAG_NORMAL);
714                 }
715         }
716 }
717
718 // Turn a set of flags into a help message
719 string snake_turn_to_string(int turnflags)
720 {
721         if ( turnflags & SNAKE_TURN_LOSS )
722                 return _("Game over!");
723
724         if ( turnflags & SNAKE_TURN_WIN )
725         {
726                 if ( (turnflags&SNAKE_TURN_TEAM) != minigame_self.team )
727                         return _("You ran out of lives!");
728                 return _("You win!");
729         }
730
731         if(active_minigame.snake_lives[minigame_self.team] <= 0)
732                 return _("You ran out of lives!");
733
734         if ( (snake_find_head(active_minigame, minigame_self.team)).snake_dir == '0 0 0' )
735                 return _("Press an arrow key to begin the game");
736
737         if ( turnflags & SNAKE_TURN_MOVE )
738                 if(snake_wrap)
739                         return _("Avoid the snake's body, collect the mice!");
740                 else
741                         return _("Avoid the screen edges and the snake's body, collect the mice!");
742
743         return "";
744 }
745
746 // Make the correct move
747 void snake_set_direction(entity minigame, int dx, int dy)
748 {
749         //if ( minigame.minigame_flags == SNAKE_TURN_MOVE )
750         //{
751                 minigame_cmd("move ",ftos(dx), " ", ftos(dy));
752         //}
753 }
754
755 // Required function, handle client events
756 int snake_client_event(entity minigame, string event, ...)
757 {
758         switch(event)
759         {
760                 case "activate":
761                 {
762                         minigame.message = snake_turn_to_string(minigame.minigame_flags);
763                         return false;
764                 }
765                 case "key_pressed":
766                 {
767                         //if((minigame.minigame_flags & SNAKE_TURN_TEAM) == minigame_self.team)
768                         {
769                                 switch ( ...(0,int) )
770                                 {
771                                         case K_RIGHTARROW:
772                                         case K_KP_RIGHTARROW:
773                                                 snake_set_direction(minigame, 1, 0);
774                                                 return true;
775                                         case K_LEFTARROW:
776                                         case K_KP_LEFTARROW:
777                                                 snake_set_direction(minigame, -1, 0);
778                                                 return true;
779                                         case K_UPARROW:
780                                         case K_KP_UPARROW:
781                                                 snake_set_direction(minigame, 0, 1);
782                                                 return true;
783                                         case K_DOWNARROW:
784                                         case K_KP_DOWNARROW:
785                                                 snake_set_direction(minigame, 0, -1);
786                                                 return true;
787                                 }
788                         }
789
790                         return false;
791                 }
792                 case "network_receive":
793                 {
794                         entity sent = ...(0,entity);
795                         int sf = ...(1,int);
796                         if ( sent.classname == "minigame" )
797                         {
798                                 if ( sf & MINIG_SF_UPDATE )
799                                 {
800                                         snake_wrap = ReadByte();
801                                         sent.message = snake_turn_to_string(sent.minigame_flags);
802                                         //if ( sent.minigame_flags & minigame_self.team )
803                                                 minigame_prompt();
804                                 }
805                         }
806                         else if(sent.classname == "minigame_board_piece")
807                         {
808                                 if(sf & MINIG_SF_UPDATE)
809                                 {
810                                         int letter = ReadByte();
811                                         int number = ReadByte();
812                                         if(sent.netname) { strunzone(sent.netname); }
813                                         sent.netname = strzone(minigame_tile_buildname(letter, number));
814
815                                         sent.cnt = ReadByte();
816                                         sent.snake_tail = ReadByte();
817
818                                         int dx = ReadByte();
819                                         int dy = ReadByte();
820
821                                         if(dx == 2) dx = -1;
822                                         if(dy == 2) dy = -1;
823
824                                         sent.snake_dir_x = dx;
825                                         sent.snake_dir_y = dy;
826                                         sent.snake_dir_z = 0;
827                                 }
828                         }
829                         else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
830                         {
831                                 sent.snake_score = ReadLong();
832                                 minigame.snake_lives[sent.team] = ReadByte();
833                         }
834
835                         return false;
836                 }
837         }
838
839         return false;
840 }
841
842 #endif