]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/snake.qc
40e253d4743f4d1375a6753bfd0c4d7761ac4df2
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / snake.qc
1 const float SNAKE_TURN_MOVE  = 0x0100; // the snake is moving, player must control it
2 const float SNAKE_TURN_LOSS  = 0x0200; // they did it?!
3 const float SNAKE_TURN_WAIT  = 0x0400; // the snake is waiting for the player to make their first move and begin the game
4 const float SNAKE_TURN_TYPE  = 0x0f00; // turn type mask
5
6 const int SNAKE_SF_PLAYERSCORE = MINIG_SF_CUSTOM;
7
8 const int SNAKE_LET_CNT = 15;
9 const int SNAKE_NUM_CNT = 15;
10
11 const int SNAKE_TILE_SIZE = 15;
12
13 float autocvar_sv_minigames_snake_delay_initial = 0.7;
14 float autocvar_sv_minigames_snake_delay_multiplier = 50;
15
16 .int snake_score;
17 .entity snake_head;
18
19 .float snake_delay;
20 .float snake_nextmove;
21 .vector snake_dir;
22
23 // find same game piece given its tile name
24 entity snake_find_piece(entity minig, string tile)
25 {
26         entity e = world;
27         while ( ( e = findentity(e,owner,minig) ) )
28                 if ( e.classname == "minigame_board_piece" && e.netname == tile )
29                         return e;
30         return world;
31 }
32
33 // find same game piece given its cnt
34 entity snake_find_cnt(entity minig, int tile)
35 {
36         entity e = world;
37         while ( ( e = findentity(e,owner,minig) ) )
38                 if ( e.classname == "minigame_board_piece" && e.cnt == tile )
39                         return e;
40         return world;
41 }
42
43 // check if the tile name is valid (15x15 grid)
44 bool snake_valid_tile(string tile)
45 {
46         if ( !tile )
47                 return false;
48         int number = minigame_tile_number(tile);
49         int letter = minigame_tile_letter(tile);
50         return 0 <= number && number < SNAKE_NUM_CNT && 0 <= letter && letter < SNAKE_LET_CNT;
51 }
52
53 void snake_new_mouse(entity minigame)
54 {
55         RandomSelection_Init();
56         int i, j;
57         for(i = 0; i < SNAKE_LET_CNT; ++i)
58         for(j = 0; j < SNAKE_NUM_CNT; ++j)
59         {
60                 string pos = minigame_tile_buildname(i, j);
61                 if(!snake_find_piece(minigame, pos))
62                         RandomSelection_Add(world, 0, pos, 1, 1);
63         }
64
65         entity piece = msle_spawn(minigame,"minigame_board_piece");
66         piece.team = 1;
67         piece.netname = strzone(RandomSelection_chosen_string);
68         minigame_server_sendflags(piece,MINIG_SF_ALL);
69
70         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
71 }
72
73 void snake_setup_pieces(entity minigame)
74 {
75         int targnum = bound(1, floor(random() * SNAKE_NUM_CNT), SNAKE_NUM_CNT - 1);
76         int targlet = bound(1, floor(random() * SNAKE_LET_CNT), SNAKE_LET_CNT - 1);
77
78         entity piece = msle_spawn(minigame,"minigame_board_piece");
79         piece.team = 1; // init default team?
80         piece.netname = strzone(minigame_tile_buildname(targlet,targnum));
81         piece.cnt = 1;
82         minigame_server_sendflags(piece,MINIG_SF_ALL);
83
84         minigame.snake_head = piece;
85
86         snake_new_mouse(minigame);
87
88         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
89 }
90
91 void snake_add_score(entity minigame, int thescore)
92 {
93 #ifdef SVQC
94         if(!minigame)
95                 return;
96         if(minigame.minigame_players)
97         {
98                 minigame.minigame_players.snake_score += thescore;
99                 minigame.minigame_players.SendFlags |= SNAKE_SF_PLAYERSCORE;
100         }
101 #endif
102 }
103
104 void snake_move_body(entity minigame, bool ate_mouse)
105 {
106         entity tail = world;
107         string tailpos = string_null;
108         vector taildir = '0 0 0';
109
110         int i, pieces = 0;
111         for(i = (SNAKE_NUM_CNT * SNAKE_LET_CNT); i >= 2; --i)
112         {
113                 entity piece = snake_find_cnt(minigame, i);
114                 entity nextpiece = snake_find_cnt(minigame, i - 1);
115                 if(!piece)
116                         continue;
117
118                 pieces++;
119
120                 if(!tail)
121                 {
122                         tail = piece;
123                         tailpos = piece.netname;
124                         taildir = piece.snake_dir;
125                 }
126
127                 if(piece.netname) { strunzone(piece.netname); }
128                 piece.netname = strzone(nextpiece.netname);
129                 piece.snake_dir = nextpiece.snake_dir;
130                 minigame_server_sendflags(piece, MINIG_SF_ALL);
131         }
132
133         // just a head
134         if(!pieces)
135         {
136                 tail = minigame.snake_head;
137                 tailpos = minigame.snake_head.netname;
138                 taildir = minigame.snake_head.snake_dir;
139         }
140
141         if(tail && ate_mouse)
142         {
143                 int newcnt = tail.cnt + 1;
144                 minigame.snake_delay = max(0.1, autocvar_sv_minigames_snake_delay_initial - (newcnt / autocvar_sv_minigames_snake_delay_multiplier));
145                 snake_add_score(minigame, 1);
146
147                 entity piece = msle_spawn(minigame,"minigame_board_piece");
148                 piece.cnt = newcnt;
149                 piece.team = 1;
150                 piece.snake_dir = taildir;
151                 piece.netname = strzone(tailpos);
152                 minigame_server_sendflags(piece,MINIG_SF_ALL);
153
154                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
155         }
156 }
157
158 void snake_move_head(entity minigame)
159 {
160         entity head = minigame.snake_head;
161
162         int myx = minigame_tile_letter(head.netname);
163         int myy = minigame_tile_number(head.netname);
164
165         myx += minigame.snake_dir_x;
166         myy += minigame.snake_dir_y;
167
168         string newpos = minigame_tile_buildname(myx, myy);
169
170         if(!snake_valid_tile(newpos) || (snake_find_piece(minigame, newpos)).cnt)
171         {
172                 minigame.minigame_flags = SNAKE_TURN_LOSS;
173                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
174                 return;
175         }
176
177         bool ate_mouse = false;
178         entity piece = snake_find_piece(minigame, newpos);
179         if(piece && !piece.cnt)
180                 ate_mouse = true;
181
182         // move the body first, then set the new head position?
183         snake_move_body(minigame, ate_mouse);
184
185         if(ate_mouse)
186         {
187                 if(piece.netname) { strunzone(piece.netname); }
188                 remove(piece);
189
190                 snake_new_mouse(minigame);
191         }
192
193         if(head.netname) { strunzone(head.netname); }
194         head.netname = strzone(newpos);
195         minigame_server_sendflags(head,MINIG_SF_ALL);
196 }
197
198 // make a move
199 void snake_move(entity minigame, entity player, string dxs, string dys )
200 {
201         if ( (minigame.minigame_flags & SNAKE_TURN_MOVE) || (minigame.minigame_flags & SNAKE_TURN_WAIT) )
202         if ( dxs || dys )
203         {
204                 //if ( snake_valid_tile(pos) )
205                 //if ( snake_find_piece(minigame, pos) )
206                 {
207                         int dx = ((dxs) ? stof(dxs) : 0);
208                         int dy = ((dys) ? stof(dys) : 0);
209
210                         int myl = minigame_tile_letter(minigame.snake_head.netname);
211                         int myn = minigame_tile_number(minigame.snake_head.netname);
212
213                         entity head = snake_find_piece(minigame, minigame_tile_buildname(myl + dx, myn + dy));
214                         if(head && head.cnt == 2)
215                                 return; // nope!
216
217                         if(minigame.minigame_flags & SNAKE_TURN_WAIT)
218                                 minigame.snake_nextmove = time;
219                         minigame.minigame_flags = SNAKE_TURN_MOVE;
220                         minigame.snake_dir_x = dx;
221                         minigame.snake_dir_y = dy;
222                         minigame.snake_dir_z = 0;
223                         minigame.snake_head.snake_dir = minigame.snake_dir;
224                         minigame_server_sendflags(minigame.snake_head,MINIG_SF_UPDATE);
225                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
226                 }
227         }
228 }
229
230 #ifdef SVQC
231
232
233 // required function, handle server side events
234 int snake_server_event(entity minigame, string event, ...)
235 {
236         switch(event)
237         {
238                 case "start":
239                 {
240                         snake_setup_pieces(minigame);
241                         minigame.snake_delay = autocvar_sv_minigames_snake_delay_initial;
242                         minigame.minigame_flags = SNAKE_TURN_WAIT;
243                         return true;
244                 }
245                 case "end":
246                 {
247                         entity e = world;
248                         while( (e = findentity(e, owner, minigame)) )
249                         if(e.classname == "minigame_board_piece")
250                         {
251                                 if(e.netname) { strunzone(e.netname); }
252                                 remove(e);
253                         }
254                         minigame.snake_head = world;
255                         return false;
256                 }
257                 case "join":
258                 {
259                         int pl_num = minigame_count_players(minigame);
260
261                         // Don't allow more than 1 player
262                         // not sure if this should be a multiplayer game (might get crazy)
263                         if(pl_num >= 1) { return false; }
264
265                         // Team 1 by default
266                         return 1;
267                 }
268                 case "frame":
269                 {
270                         if(minigame.minigame_flags & SNAKE_TURN_MOVE)
271                         if(time >= minigame.snake_nextmove)
272                         {
273                                 snake_move_head(minigame);
274                                 minigame.snake_nextmove = time + minigame.snake_delay;
275                         }
276                         return false;
277                 }
278                 case "cmd":
279                 {
280                         switch(argv(0))
281                         {
282                                 case "move": 
283                                         snake_move(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) == 3 ? argv(2) : string_null)); 
284                                         return true;
285                         }
286
287                         return false;
288                 }
289                 case "network_send":
290                 {
291                         entity sent = ...(0,entity);
292                         int sf = ...(1,int);
293                         if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) )
294                         {
295                                 WriteByte(MSG_ENTITY,sent.cnt);
296                                 WriteCoord(MSG_ENTITY,sent.snake_dir_x);
297                                 WriteCoord(MSG_ENTITY,sent.snake_dir_y);
298                         }
299                         else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
300                         {
301                                 WriteLong(MSG_ENTITY,sent.snake_score);
302                         }
303                         return false;
304                 }
305         }
306         
307         return false;
308 }
309
310
311 #elif defined(CSQC)
312
313 vector snake_boardpos; // HUD board position
314 vector snake_boardsize;// HUD board size
315
316 // Required function, draw the game board
317 void snake_hud_board(vector pos, vector mySize)
318 {
319         minigame_hud_fitsqare(pos, mySize);
320         snake_boardpos = pos;
321         snake_boardsize = mySize;
322         
323         minigame_hud_simpleboard(pos,mySize,minigame_texture("snake/board"));
324
325         vector tile_size = minigame_hud_denormalize_size('1 1 0' / SNAKE_TILE_SIZE,pos,mySize);
326         vector tile_pos;
327
328         entity tail = world;
329         int i;
330         for(i = (SNAKE_NUM_CNT * SNAKE_LET_CNT); i >= 2; --i)
331         {
332                 entity piece = snake_find_cnt(active_minigame, i);
333                 if(piece)
334                 {
335                         tail = piece;
336                         break;
337                 }
338         }
339
340         entity e;
341         FOREACH_MINIGAME_ENTITY(e)
342         {
343                 if ( e.classname == "minigame_board_piece" )
344                 {
345                         tile_pos = minigame_tile_pos(e.netname,SNAKE_NUM_CNT,SNAKE_LET_CNT);
346                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
347                         string thepiece = "snake/mouse";
348                         if(e.cnt)
349                                 thepiece = "snake/body";
350                         if(tail && e.cnt == tail.cnt)
351                                 thepiece = "snake/tail";
352                         if(e.cnt == 1)
353                         {
354                                 int dx = minigame_tile_letter(e.netname) + e.snake_dir_x;
355                                 int dy = minigame_tile_number(e.netname) + e.snake_dir_y;
356                                 entity mouse = snake_find_piece(active_minigame, minigame_tile_buildname(dx, dy));
357                                 thepiece = "snake/head";
358                                 if(mouse && !mouse.cnt)
359                                         thepiece = "snake/feed";
360                         }
361
362                         minigame_drawpic_centered( tile_pos,  
363                                         minigame_texture(thepiece),
364                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
365                 }
366         }
367
368         if ( active_minigame.minigame_flags & SNAKE_TURN_LOSS )
369         {
370                 int scores = 0;
371                 FOREACH_MINIGAME_ENTITY(e)
372                         if(e.classname == "minigame_player")
373                                 scores = e.snake_score;
374
375                 vector winfs = hud_fontsize*2;
376                 string scores_text;
377                 scores_text = strcat("Score: ", ftos(scores));
378                 
379                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
380                 vector win_sz;
381                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
382                         sprintf("Game over! %s", scores_text), 
383                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
384                 
385                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'0.3 0.3 1',0.8,DRAWFLAG_ADDITIVE);
386                 
387                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
388                         sprintf("Game over! %s", scores_text), 
389                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
390         }
391 }
392
393
394 // Required function, draw the game status panel
395 void snake_hud_status(vector pos, vector mySize)
396 {
397         HUD_Panel_DrawBg(1);
398         vector ts;
399         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
400                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
401         
402         pos_y += ts_y;
403         mySize_y -= ts_y;
404         
405         vector player_fontsize = hud_fontsize * 1.75;
406         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
407         ts_x = mySize_x;
408         vector mypos;
409         vector tile_size = '48 48 0';
410
411         mypos = pos;
412         drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
413         mypos_y += player_fontsize_y;
414         drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
415
416         entity e;
417         FOREACH_MINIGAME_ENTITY(e)
418         {
419                 if ( e.classname == "minigame_player" )
420                 {
421                         mypos = pos;
422                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
423                                 GetPlayerName(e.minigame_playerslot-1),
424                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
425                         
426                         mypos_y += player_fontsize_y;
427                         //drawpic( mypos,  
428                         //              minigame_texture("snake/piece"),
429                         //              tile_size, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL );
430                         
431                         //mypos_x += tile_size_x;
432
433                         drawstring(mypos,ftos(e.snake_score),tile_size,
434                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
435                 }
436         }
437 }
438
439 // Turn a set of flags into a help message
440 string snake_turn_to_string(int turnflags)
441 {
442         if ( turnflags & SNAKE_TURN_LOSS )
443                 return _("Game over!");
444         
445         if ( turnflags & SNAKE_TURN_WAIT )
446                 return _("Press an arrow key to begin the game");
447
448         if ( turnflags & SNAKE_TURN_MOVE )
449                 return _("Avoid the walls and the snake's body, collect the mice!");
450         
451         return "";
452 }
453
454 // Make the correct move
455 void snake_set_direction(entity minigame, int dx, int dy)
456 {
457         //if ( minigame.minigame_flags == SNAKE_TURN_MOVE )
458         //{
459                 minigame_cmd("move ",ftos(dx), " ", ftos(dy));
460         //}
461 }
462
463 // Required function, handle client events
464 int snake_client_event(entity minigame, string event, ...)
465 {
466         switch(event)
467         {
468                 case "activate":
469                 {
470                         minigame.message = snake_turn_to_string(minigame.minigame_flags);
471                         return false;
472                 }
473                 case "key_pressed":
474                 {
475                         //if((minigame.minigame_flags & SNAKE_TURN_TEAM) == minigame_self.team)
476                         {
477                                 switch ( ...(0,int) )
478                                 {
479                                         case K_RIGHTARROW:
480                                         case K_KP_RIGHTARROW:
481                                                 snake_set_direction(minigame, 1, 0);
482                                                 return true;
483                                         case K_LEFTARROW:
484                                         case K_KP_LEFTARROW:
485                                                 snake_set_direction(minigame, -1, 0);
486                                                 return true;
487                                         case K_UPARROW:
488                                         case K_KP_UPARROW:
489                                                 snake_set_direction(minigame, 0, 1);
490                                                 return true;
491                                         case K_DOWNARROW:
492                                         case K_KP_DOWNARROW:
493                                                 snake_set_direction(minigame, 0, -1);
494                                                 return true;
495                                 }
496                         }
497
498                         return false;
499                 }
500                 case "network_receive":
501                 {
502                         entity sent = ...(0,entity);
503                         int sf = ...(1,int);
504                         if ( sent.classname == "minigame" )
505                         {
506                                 if ( sf & MINIG_SF_UPDATE )
507                                 {
508                                         sent.message = snake_turn_to_string(sent.minigame_flags);
509                                         //if ( sent.minigame_flags & minigame_self.team )
510                                                 minigame_prompt();
511                                 }
512                         }
513                         else if(sent.classname == "minigame_board_piece")
514                         {
515                                 if(sf & MINIG_SF_UPDATE)
516                                 {
517                                         sent.cnt = ReadByte();
518                                         sent.snake_dir_x = ReadCoord();
519                                         sent.snake_dir_y = ReadCoord();
520                                         sent.snake_dir_z = 0;
521                                         if(sent.cnt == 1)
522                                                 minigame.snake_head = sent; // hax
523                                 }
524                         }
525                         else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
526                         {
527                                 sent.snake_score = ReadLong();
528                         }
529
530                         return false;
531                 }
532         }
533
534         return false;
535 }
536
537 #endif