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
6 const int SNAKE_SF_PLAYERSCORE = MINIG_SF_CUSTOM;
8 const int SNAKE_LET_CNT = 15;
9 const int SNAKE_NUM_CNT = 15;
11 const int SNAKE_TILE_SIZE = 15;
13 float autocvar_sv_minigames_snake_delay_initial = 0.7;
14 float autocvar_sv_minigames_snake_delay_multiplier = 50;
15 float autocvar_sv_minigames_snake_delay_min = 0.1;
21 .float snake_nextmove;
24 // find same game piece given its tile name
25 entity snake_find_piece(entity minig, string tile)
28 while ( ( e = findentity(e,owner,minig) ) )
29 if ( e.classname == "minigame_board_piece" && e.netname == tile )
34 // find same game piece given its cnt
35 entity snake_find_cnt(entity minig, int tile)
38 while ( ( e = findentity(e,owner,minig) ) )
39 if ( e.classname == "minigame_board_piece" && e.cnt == tile )
44 // check if the tile name is valid (15x15 grid)
45 bool snake_valid_tile(string tile)
49 int number = minigame_tile_number(tile);
50 int letter = minigame_tile_letter(tile);
51 return 0 <= number && number < SNAKE_NUM_CNT && 0 <= letter && letter < SNAKE_LET_CNT;
54 void snake_new_mouse(entity minigame)
56 RandomSelection_Init();
58 for(i = 0; i < SNAKE_LET_CNT; ++i)
59 for(j = 0; j < SNAKE_NUM_CNT; ++j)
61 string pos = minigame_tile_buildname(i, j);
62 if(!snake_find_piece(minigame, pos))
63 RandomSelection_Add(world, 0, pos, 1, 1);
66 entity piece = msle_spawn(minigame,"minigame_board_piece");
68 piece.netname = strzone(RandomSelection_chosen_string);
69 minigame_server_sendflags(piece,MINIG_SF_ALL);
71 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
74 void snake_setup_pieces(entity minigame)
76 int targnum = bound(1, floor(random() * SNAKE_NUM_CNT), SNAKE_NUM_CNT - 1);
77 int targlet = bound(1, floor(random() * SNAKE_LET_CNT), SNAKE_LET_CNT - 1);
79 entity piece = msle_spawn(minigame,"minigame_board_piece");
80 piece.team = 1; // init default team?
81 piece.netname = strzone(minigame_tile_buildname(targlet,targnum));
83 minigame_server_sendflags(piece,MINIG_SF_ALL);
85 minigame.snake_head = piece;
87 snake_new_mouse(minigame);
89 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
92 void snake_add_score(entity minigame, int thescore)
97 if(minigame.minigame_players)
99 minigame.minigame_players.snake_score += thescore;
100 minigame.minigame_players.SendFlags |= SNAKE_SF_PLAYERSCORE;
105 void snake_move_body(entity minigame, bool ate_mouse)
108 string tailpos = string_null;
109 vector taildir = '0 0 0';
112 for(i = (SNAKE_NUM_CNT * SNAKE_LET_CNT); i >= 2; --i)
114 entity piece = snake_find_cnt(minigame, i);
115 entity nextpiece = snake_find_cnt(minigame, i - 1);
124 tailpos = piece.netname;
125 taildir = piece.snake_dir;
128 if(piece.netname) { strunzone(piece.netname); }
129 piece.netname = strzone(nextpiece.netname);
130 piece.snake_dir = nextpiece.snake_dir;
131 minigame_server_sendflags(piece, MINIG_SF_ALL);
137 tail = minigame.snake_head;
138 tailpos = minigame.snake_head.netname;
139 taildir = minigame.snake_head.snake_dir;
142 if(tail && ate_mouse)
144 int newcnt = tail.cnt + 1;
145 minigame.snake_delay = max(autocvar_sv_minigames_snake_delay_min, autocvar_sv_minigames_snake_delay_initial - (newcnt / autocvar_sv_minigames_snake_delay_multiplier));
146 snake_add_score(minigame, 1);
148 entity piece = msle_spawn(minigame,"minigame_board_piece");
151 piece.snake_dir = taildir;
152 piece.netname = strzone(tailpos);
153 minigame_server_sendflags(piece,MINIG_SF_ALL);
155 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
159 void snake_move_head(entity minigame)
161 entity head = minigame.snake_head;
163 int myx = minigame_tile_letter(head.netname);
164 int myy = minigame_tile_number(head.netname);
166 myx += minigame.snake_dir_x;
167 myy += minigame.snake_dir_y;
169 string newpos = minigame_tile_buildname(myx, myy);
171 if(!snake_valid_tile(newpos) || (snake_find_piece(minigame, newpos)).cnt)
173 minigame.minigame_flags = SNAKE_TURN_LOSS;
174 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
178 bool ate_mouse = false;
179 entity piece = snake_find_piece(minigame, newpos);
180 if(piece && !piece.cnt)
183 // move the body first, then set the new head position?
184 snake_move_body(minigame, ate_mouse);
188 if(piece.netname) { strunzone(piece.netname); }
191 snake_new_mouse(minigame);
194 if(head.netname) { strunzone(head.netname); }
195 head.netname = strzone(newpos);
196 minigame_server_sendflags(head,MINIG_SF_ALL);
200 void snake_move(entity minigame, entity player, string dxs, string dys )
202 if ( (minigame.minigame_flags & SNAKE_TURN_MOVE) || (minigame.minigame_flags & SNAKE_TURN_WAIT) )
205 //if ( snake_valid_tile(pos) )
206 //if ( snake_find_piece(minigame, pos) )
208 int dx = ((dxs) ? bound(-1, stof(dxs), 1) : 0);
209 int dy = ((dys) ? bound(-1, stof(dys), 1) : 0);
211 int myl = minigame_tile_letter(minigame.snake_head.netname);
212 int myn = minigame_tile_number(minigame.snake_head.netname);
214 entity head = snake_find_piece(minigame, minigame_tile_buildname(myl + dx, myn + dy));
215 if(head && head.cnt == 2)
218 if(minigame.minigame_flags & SNAKE_TURN_WAIT)
219 minigame.snake_nextmove = time;
220 minigame.minigame_flags = SNAKE_TURN_MOVE;
221 minigame.snake_dir_x = dx;
222 minigame.snake_dir_y = dy;
223 minigame.snake_dir_z = 0;
224 minigame.snake_head.snake_dir = minigame.snake_dir;
225 minigame_server_sendflags(minigame.snake_head,MINIG_SF_UPDATE);
226 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
234 // required function, handle server side events
235 int snake_server_event(entity minigame, string event, ...)
241 snake_setup_pieces(minigame);
242 minigame.snake_delay = autocvar_sv_minigames_snake_delay_initial;
243 minigame.minigame_flags = SNAKE_TURN_WAIT;
249 while( (e = findentity(e, owner, minigame)) )
250 if(e.classname == "minigame_board_piece")
252 if(e.netname) { strunzone(e.netname); }
255 minigame.snake_head = world;
260 int pl_num = minigame_count_players(minigame);
262 // Don't allow more than 1 player
263 // not sure if this should be a multiplayer game (might get crazy)
264 if(pl_num >= 1) { return false; }
271 if(minigame.minigame_flags & SNAKE_TURN_MOVE)
272 if(time >= minigame.snake_nextmove)
274 snake_move_head(minigame);
275 minigame.snake_nextmove = time + minigame.snake_delay;
284 snake_move(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) == 3 ? argv(2) : string_null));
292 entity sent = ...(0,entity);
294 if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) )
296 WriteByte(MSG_ENTITY,sent.cnt);
297 WriteCoord(MSG_ENTITY,sent.snake_dir_x);
298 WriteCoord(MSG_ENTITY,sent.snake_dir_y);
300 else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
302 WriteLong(MSG_ENTITY,sent.snake_score);
314 vector snake_boardpos; // HUD board position
315 vector snake_boardsize;// HUD board size
317 // Required function, draw the game board
318 void snake_hud_board(vector pos, vector mySize)
320 minigame_hud_fitsqare(pos, mySize);
321 snake_boardpos = pos;
322 snake_boardsize = mySize;
324 minigame_hud_simpleboard(pos,mySize,minigame_texture("snake/board"));
326 vector tile_size = minigame_hud_denormalize_size('1 1 0' / SNAKE_TILE_SIZE,pos,mySize);
331 for(i = (SNAKE_NUM_CNT * SNAKE_LET_CNT); i >= 2; --i)
333 entity piece = snake_find_cnt(active_minigame, i);
342 FOREACH_MINIGAME_ENTITY(e)
344 if ( e.classname == "minigame_board_piece" )
346 tile_pos = minigame_tile_pos(e.netname,SNAKE_NUM_CNT,SNAKE_LET_CNT);
347 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
348 string thepiece = "snake/mouse";
350 thepiece = "snake/body";
351 if(tail && e.cnt == tail.cnt)
352 thepiece = "snake/tail";
355 int dx = minigame_tile_letter(e.netname) + e.snake_dir_x * 2;
356 int dy = minigame_tile_number(e.netname) + e.snake_dir_y * 2;
357 entity mouse = snake_find_piece(active_minigame, minigame_tile_buildname(dx, dy));
358 thepiece = "snake/head";
359 if(mouse && !mouse.cnt)
362 int myx = minigame_tile_letter(e.netname);
363 int myy = minigame_tile_number(e.netname);
371 int newx = minigame_tile_letter(e.netname) + e.snake_dir_x;
372 int newy = minigame_tile_number(e.netname) + e.snake_dir_y;
373 string newpos = minigame_tile_buildname(newx, newy);
375 vector my_pos = minigame_tile_pos(newpos,SNAKE_NUM_CNT,SNAKE_LET_CNT);
376 my_pos = minigame_hud_denormalize(my_pos,pos,mySize);
378 drawrotpic(my_pos, myang, minigame_texture("snake/tongue"),
379 tile_size, tile_size/2, '1 1 1',
380 panel_fg_alpha, DRAWFLAG_NORMAL );
384 if(e.cnt == 1 || e.cnt == tail.cnt)
386 vector thedir = e.snake_dir;
388 if(e.cnt == tail.cnt)
390 int thex = minigame_tile_letter(e.netname);
391 int they = minigame_tile_number(e.netname);
392 entity t = snake_find_cnt(active_minigame, e.cnt - 1);
393 int tx = minigame_tile_letter(t.netname);
394 int ty = minigame_tile_number(t.netname);
420 drawrotpic(tile_pos, theang, minigame_texture(thepiece),
421 tile_size, tile_size/2, '1 1 1',
422 panel_fg_alpha, DRAWFLAG_NORMAL );
426 minigame_drawpic_centered( tile_pos,
427 minigame_texture(thepiece),
428 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
433 if ( active_minigame.minigame_flags & SNAKE_TURN_LOSS )
436 FOREACH_MINIGAME_ENTITY(e)
437 if(e.classname == "minigame_player")
438 scores = e.snake_score;
440 vector winfs = hud_fontsize*2;
442 scores_text = strcat("Score: ", ftos(scores));
444 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
446 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
447 sprintf("Game over! %s", scores_text),
448 winfs, 0, DRAWFLAG_NORMAL, 0.5);
450 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'0.3 0.3 1',0.8,DRAWFLAG_ADDITIVE);
452 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
453 sprintf("Game over! %s", scores_text),
454 winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
459 // Required function, draw the game status panel
460 void snake_hud_status(vector pos, vector mySize)
464 ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
465 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
470 vector player_fontsize = hud_fontsize * 1.75;
471 ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
474 vector tile_size = '48 48 0';
477 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
478 mypos_y += player_fontsize_y;
479 drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
482 FOREACH_MINIGAME_ENTITY(e)
484 if ( e.classname == "minigame_player" )
487 minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
488 GetPlayerName(e.minigame_playerslot-1),
489 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
491 mypos_y += player_fontsize_y;
493 // minigame_texture("snake/piece"),
494 // tile_size, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL );
496 //mypos_x += tile_size_x;
498 drawstring(mypos,ftos(e.snake_score),tile_size,
499 '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
504 // Turn a set of flags into a help message
505 string snake_turn_to_string(int turnflags)
507 if ( turnflags & SNAKE_TURN_LOSS )
508 return _("Game over!");
510 if ( turnflags & SNAKE_TURN_WAIT )
511 return _("Press an arrow key to begin the game");
513 if ( turnflags & SNAKE_TURN_MOVE )
514 return _("Avoid the walls and the snake's body, collect the mice!");
519 // Make the correct move
520 void snake_set_direction(entity minigame, int dx, int dy)
522 //if ( minigame.minigame_flags == SNAKE_TURN_MOVE )
524 minigame_cmd("move ",ftos(dx), " ", ftos(dy));
528 // Required function, handle client events
529 int snake_client_event(entity minigame, string event, ...)
535 minigame.message = snake_turn_to_string(minigame.minigame_flags);
540 //if((minigame.minigame_flags & SNAKE_TURN_TEAM) == minigame_self.team)
542 switch ( ...(0,int) )
545 case K_KP_RIGHTARROW:
546 snake_set_direction(minigame, 1, 0);
550 snake_set_direction(minigame, -1, 0);
554 snake_set_direction(minigame, 0, 1);
558 snake_set_direction(minigame, 0, -1);
565 case "network_receive":
567 entity sent = ...(0,entity);
569 if ( sent.classname == "minigame" )
571 if ( sf & MINIG_SF_UPDATE )
573 sent.message = snake_turn_to_string(sent.minigame_flags);
574 //if ( sent.minigame_flags & minigame_self.team )
578 else if(sent.classname == "minigame_board_piece")
580 if(sf & MINIG_SF_UPDATE)
582 sent.cnt = ReadByte();
583 sent.snake_dir_x = ReadCoord();
584 sent.snake_dir_y = ReadCoord();
585 sent.snake_dir_z = 0;
587 minigame.snake_head = sent; // hax
590 else if ( sent.classname == "minigame_player" && (sf & SNAKE_SF_PLAYERSCORE ) )
592 sent.snake_score = ReadLong();