]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/c4.qc
Transifex autosync
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / c4.qc
1 #include "c4.qh"
2 REGISTER_MINIGAME(c4, _("Connect Four"));
3
4 const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board
5 const float C4_TURN_WIN   = 0x0200; // player has won
6 const float C4_TURN_DRAW  = 0x0400; // no moves are possible
7
8 const float C4_TURN_TEAM1 = 0x0001;
9 const float C4_TURN_TEAM2 = 0x0002;
10 const float C4_TURN_TEAM  = 0x000f; // turn team mask
11
12 const int C4_LET_CNT = 7;
13 const int C4_NUM_CNT = 6;
14 const int C4_WIN_CNT = 4;
15
16 const int C4_MAX_TILES = 42;
17
18 const int C4_TILE_SIZE = 8;
19
20 const int C4_TEAMS = 2;
21 const int C4_SPECTATOR_TEAM = 255; // must be above max teams and equal to or below 255
22
23 .int c4_npieces; // (minigame) number of pieces on the board (simplifies checking a draw)
24 .int c4_nexteam; // (minigame) next team (used to change the starting team on following matches)
25
26 // find connect 4 piece given its tile name
27 entity c4_find_piece(entity minig, string tile)
28 {
29         entity e = NULL;
30         while ( ( e = findentity(e,owner,minig) ) )
31                 if ( e.classname == "minigame_board_piece" && e.netname == tile )
32                         return e;
33         return NULL;
34 }
35
36 // Checks if the given piece completes a row
37 bool c4_winning_piece(entity piece)
38 {
39         int number = minigame_tile_number(piece.netname);
40         int letter = minigame_tile_letter(piece.netname);
41
42         int i;
43         entity top = piece;
44         entity left = piece;
45         entity topleft = piece;
46         entity botleft = piece;
47         for(i = number; i < C4_NUM_CNT; ++i)
48         {
49                 entity p = c4_find_piece(piece.owner,minigame_tile_buildname(letter, i));
50                 if(p.team == piece.team)
51                         top = p;
52                 else break;
53         }
54
55         for(i = letter; i >= 0; --i)
56         {
57                 entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, number));
58                 if(p.team == piece.team)
59                         left = p;
60                 else break;
61         }
62
63         int j;
64         for(i = letter, j = number; i >= 0, j >= 0; --i, --j)
65         {
66                 entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, j));
67                 if(p.team == piece.team)
68                         botleft = p;
69                 else break;
70         }
71         for(i = letter, j = number; i >= 0, j < C4_NUM_CNT; --i, ++j)
72         {
73                 entity p = c4_find_piece(piece.owner,minigame_tile_buildname(i, j));
74                 if(p.team == piece.team)
75                         topleft = p;
76                 else break;
77         }
78
79         // down
80         int found = 0;
81         for(i = minigame_tile_number(top.netname); i >= 0; --i)
82         {
83                 if(c4_find_piece(piece.owner,minigame_tile_buildname(letter, i)).team == piece.team)
84                         ++found;
85                 else break;
86         }
87
88         if(found >= C4_WIN_CNT)
89                 return true;
90
91         // right
92         found = 0;
93         for(i = minigame_tile_letter(left.netname); i < C4_LET_CNT; ++i)
94         {
95                 if(c4_find_piece(piece.owner,minigame_tile_buildname(i, number)).team == piece.team)
96                         ++found;
97                 else break;
98         }
99
100         if(found >= C4_WIN_CNT)
101                 return true;
102
103         // diagright down
104         found = 0;
105         for(i = minigame_tile_letter(topleft.netname), j = minigame_tile_number(topleft.netname); i < C4_LET_CNT, j >= 0; ++i, --j)
106         {
107                 if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team)
108                         ++found;
109                 else break;
110         }
111
112         if(found >= C4_WIN_CNT)
113                 return true;
114
115         // diagright up
116         found = 0;
117         for(i = minigame_tile_letter(botleft.netname), j = minigame_tile_number(botleft.netname); i < C4_LET_CNT, j < C4_NUM_CNT; ++i, ++j)
118         {
119                 if(c4_find_piece(piece.owner,minigame_tile_buildname(i, j)).team == piece.team)
120                         ++found;
121                 else break;
122         }
123
124         if(found >= C4_WIN_CNT)
125                 return true;
126
127         return false;
128 }
129
130 // check if the tile name is valid (6x7 grid)
131 bool c4_valid_tile(string tile)
132 {
133         if ( !tile )
134                 return false;
135         float number = minigame_tile_number(tile);
136         float letter = minigame_tile_letter(tile);
137         return 0 <= number && number < C4_NUM_CNT && 0 <= letter && letter < C4_LET_CNT;
138 }
139
140 string c4_get_lowest_tile(entity minigame, string s)
141 {
142         int i;
143         int end = 0;
144         for(i = C4_NUM_CNT; i >= 0; --i)
145         {
146                 if(!c4_find_piece(minigame,minigame_tile_buildname(minigame_tile_letter(s), i)))
147                 if(c4_find_piece(minigame,minigame_tile_buildname(minigame_tile_letter(s), i - 1)))
148                 {
149                         end = i;
150                         break;
151                 }
152         }
153         return minigame_tile_buildname(minigame_tile_letter(s), end);
154 }
155
156 // make a move
157 void c4_move(entity minigame, entity player, string pos )
158 {
159         pos = c4_get_lowest_tile(minigame, pos);
160
161         if ( minigame.minigame_flags & C4_TURN_PLACE )
162         if ( pos && player.team == (minigame.minigame_flags & C4_TURN_TEAM) )
163         {
164                 if ( c4_valid_tile(pos) )
165                 if ( !c4_find_piece(minigame,pos) )
166                 {
167                         entity piece = msle_spawn(minigame,new(minigame_board_piece));
168                         piece.team = player.team;
169                         piece.netname = strzone(pos);
170                         minigame_server_sendflags(piece,MINIG_SF_ALL);
171                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
172                         minigame.c4_npieces++;
173                         minigame.c4_nexteam = minigame_next_team(player.team,C4_TEAMS);
174                         if ( c4_winning_piece(piece) )
175                         {
176                                 minigame.minigame_flags = C4_TURN_WIN | player.team;
177                         }
178                         else if ( minigame.c4_npieces >= C4_MAX_TILES )
179                                 minigame.minigame_flags = C4_TURN_DRAW;
180                         else
181                                 minigame.minigame_flags = C4_TURN_PLACE | minigame.c4_nexteam;
182                 }
183         }
184 }
185
186 #ifdef SVQC
187
188
189 // required function, handle server side events
190 int c4_server_event(entity minigame, string event, ...)
191 {
192         switch(event)
193         {
194                 case "start":
195                 {
196                         minigame.minigame_flags = (C4_TURN_PLACE | C4_TURN_TEAM1);
197                         return true;
198                 }
199                 case "end":
200                 {
201                         entity e = NULL;
202                         while( (e = findentity(e, owner, minigame)) )
203                         if(e.classname == "minigame_board_piece")
204                         {
205                                 strfree(e.netname);
206                                 delete(e);
207                         }
208                         return false;
209                 }
210                 case "join":
211                 {
212                         int pl_num = minigame_count_players(minigame);
213
214                         // Don't allow more than 2 players
215                         if(pl_num >= C4_TEAMS) { return C4_SPECTATOR_TEAM; }
216
217                         // Get the right team
218                         if(minigame.minigame_players)
219                                 return minigame_next_team(minigame.minigame_players.team, C4_TEAMS);
220
221                         // Team 1 by default
222                         return 1;
223                 }
224                 case "cmd":
225                 {
226                         entity player = ...(0,entity);
227                         bool event_blocked = (player.team == C4_SPECTATOR_TEAM);
228                         switch(argv(0))
229                         {
230                                 case "move":
231                                         if(event_blocked)
232                                                 return true;
233                                         c4_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null );
234                                         return true;
235                         }
236
237                         return false;
238                 }
239         }
240
241         return false;
242 }
243
244
245 #elif defined(CSQC)
246
247 string c4_curr_pos; // identifier of the tile under the mouse
248 vector c4_boardpos; // HUD board position
249 vector c4_boardsize;// HUD board size
250 .int c4_checkwin; // Used to optimize checks to display a win
251
252 // Required function, draw the game board
253 void c4_hud_board(vector pos, vector mySize)
254 {
255         minigame_hud_fitsqare(pos, mySize);
256         c4_boardpos = pos;
257         c4_boardsize = mySize;
258
259         minigame_hud_simpleboard(pos,mySize,minigame_texture("c4/board_under"));
260
261         drawpic(pos, minigame_texture("c4/board_over"), mySize, '1 1 1', 1, 0);
262
263         vector tile_size = minigame_hud_denormalize_size('1 1 0' / C4_TILE_SIZE,pos,mySize);
264         vector tile_pos;
265
266         if ( (active_minigame.minigame_flags & C4_TURN_TEAM) == minigame_self.team )
267         if ( c4_valid_tile(c4_curr_pos) )
268         {
269                 tile_pos = minigame_tile_pos(c4_curr_pos,C4_NUM_CNT,C4_LET_CNT);
270                 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
271                 minigame_drawpic_centered( tile_pos,
272                                 minigame_texture(strcat("c4/piece",ftos(minigame_self.team))),
273                                 tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL );
274         }
275
276         entity e;
277         FOREACH_MINIGAME_ENTITY(e)
278         {
279                 if ( e.classname == "minigame_board_piece" )
280                 {
281                         tile_pos = minigame_tile_pos(e.netname,C4_NUM_CNT,C4_LET_CNT);
282                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
283
284                         if ( active_minigame.minigame_flags & C4_TURN_WIN )
285                         if ( !e.c4_checkwin )
286                                 e.c4_checkwin = c4_winning_piece(e) ? 1 : -1;
287
288                         float icon_color = 1;
289                         if ( e.c4_checkwin == -1 )
290                                 icon_color = 0.4;
291                         else if ( e.c4_checkwin == 1 )
292                         {
293                                 icon_color = 2;
294                                 minigame_drawpic_centered( tile_pos, minigame_texture("c4/winglow"),
295                                                 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE );
296                         }
297
298                         minigame_drawpic_centered( tile_pos,
299                                         minigame_texture(strcat("c4/piece",ftos(e.team))),
300                                         tile_size, '1 1 1'*icon_color, panel_fg_alpha, DRAWFLAG_NORMAL );
301                 }
302         }
303
304         if ( active_minigame.minigame_flags & C4_TURN_WIN )
305         {
306                 vector winfs = hud_fontsize*2;
307                 string pname = "";
308                 FOREACH_MINIGAME_ENTITY(e)
309                         if ( e.classname == "minigame_player" &&
310                                         e.team == (active_minigame.minigame_flags & C4_TURN_TEAM) )
311                                 pname = entcs_GetName(e.minigame_playerslot-1);
312
313                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
314                 vector win_sz;
315                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
316                         sprintf(_("%s^7 won the game!"), pname),
317                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
318
319                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5*panel_fg_alpha,DRAWFLAG_ADDITIVE);
320
321                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
322                         sprintf(_("%s^7 won the game!"), pname),
323                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
324         }
325
326         minigame_show_allspecs(c4_boardpos, c4_boardsize);
327 }
328
329
330 // Required function, draw the game status panel
331 void c4_hud_status(vector pos, vector mySize)
332 {
333         HUD_Panel_DrawBg();
334         vector ts;
335         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
336                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
337
338         pos_y += ts_y;
339         mySize_y -= ts_y;
340
341         vector player_fontsize = hud_fontsize * 1.75;
342         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
343         ts_x = mySize_x;
344         vector mypos;
345         vector tile_size = '48 48 0';
346
347         if(minigame_self.team != C4_SPECTATOR_TEAM)
348         {
349                 mypos = pos;
350                 if ( (active_minigame.minigame_flags&C4_TURN_TEAM) == 2 )
351                         mypos_y  += player_fontsize_y + ts_y;
352                 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5*panel_fg_alpha,DRAWFLAG_ADDITIVE);
353                 mypos_y += player_fontsize_y;
354                 drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25*panel_fg_alpha,DRAWFLAG_ADDITIVE);
355         }
356
357         entity e;
358         FOREACH_MINIGAME_ENTITY(e)
359         {
360                 if ( e.classname == "minigame_player" && e.team != C4_SPECTATOR_TEAM )
361                 {
362                         mypos = pos;
363                         if ( e.team == 2 )
364                                 mypos_y  += player_fontsize_y + ts_y;
365                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
366                                 entcs_GetName(e.minigame_playerslot-1),
367                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
368
369                         mypos_y += player_fontsize_y;
370                         drawpic( mypos,
371                                         minigame_texture(strcat("c4/piece",ftos(e.team))),
372                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
373
374                         mypos_x += tile_size_x;
375                 }
376         }
377 }
378
379 // Turn a set of flags into a help message
380 string c4_turn_to_string(int turnflags)
381 {
382         if(minigame_self.team == C4_SPECTATOR_TEAM)
383                 return _("You are spectating");
384
385         if ( turnflags & C4_TURN_DRAW )
386                 return _("Draw");
387
388         if ( turnflags & C4_TURN_WIN )
389         {
390                 if ( (turnflags&C4_TURN_TEAM) != minigame_self.team )
391                         return _("You lost the game!");
392                 return _("You win!");
393         }
394
395         if ( (turnflags & C4_TURN_TEAM) != minigame_self.team )
396                 return _("Wait for your opponent to make their move");
397
398         if ( turnflags & C4_TURN_PLACE )
399                 return _("Click on the game board to place your piece");
400
401         return "";
402 }
403
404 // Make the correct move
405 void c4_make_move(entity minigame)
406 {
407         if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
408         {
409                 minigame_cmd("move ",c4_curr_pos);
410         }
411 }
412
413 void c4_set_curr_pos(string s)
414 {
415         strfree(c4_curr_pos);
416         if ( s )
417                 s = strzone(s);
418         c4_curr_pos = s;
419 }
420
421 // Required function, handle client events
422 int c4_client_event(entity minigame, string event, ...)
423 {
424         switch(event)
425         {
426                 case "activate":
427                 {
428                         c4_set_curr_pos("");
429                         strcpy(minigame.message, c4_turn_to_string(minigame.minigame_flags));
430                         return false;
431                 }
432                 case "deactivate":
433                 {
434                         strfree(minigame.message);
435                         return false;
436                 }
437                 case "key_pressed":
438                 case "key_released":
439                 {
440                         bool event_blocked = ((event == "key_released")
441                                 || ((minigame.minigame_flags & C4_TURN_TEAM) != minigame_self.team));
442                         if (!(minigame.minigame_flags & C4_TURN_WIN) && !(minigame.minigame_flags & C4_TURN_DRAW))
443                         {
444                                 switch ( ...(0,int) )
445                                 {
446                                         case K_RIGHTARROW:
447                                         case K_KP_RIGHTARROW:
448                                                 if (event_blocked)
449                                                         return true;
450                                                 if ( ! c4_curr_pos )
451                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3"));
452                                                 else
453                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT)));
454                                                 return true;
455                                         case K_LEFTARROW:
456                                         case K_KP_LEFTARROW:
457                                                 if (event_blocked)
458                                                         return true;
459                                                 if ( ! c4_curr_pos )
460                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3"));
461                                                 else
462                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT)));
463                                                 return true;
464                                         case K_UPARROW:
465                                         case K_KP_UPARROW:
466                                         case K_DOWNARROW:
467                                         case K_KP_DOWNARROW:
468                                                 return true;
469                                         /*case K_UPARROW:
470                                         case K_KP_UPARROW:
471                                                 if (event_blocked)
472                                                         return true;
473                                                 if ( ! c4_curr_pos )
474                                                         c4_set_curr_pos("a1");
475                                                 else
476                                                         c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,1,6,7));
477                                                 return true;
478                                         case K_DOWNARROW:
479                                         case K_KP_DOWNARROW:
480                                                 if (event_blocked)
481                                                         return true;
482                                                 if ( ! c4_curr_pos )
483                                                         c4_set_curr_pos("a3");
484                                                 else
485                                                         c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,-1,6,7));
486                                                 return true;*/
487                                         case K_ENTER:
488                                         case K_KP_ENTER:
489                                         case K_SPACE:
490                                                 if (event_blocked)
491                                                         return true;
492                                                 c4_make_move(minigame);
493                                                 return true;
494                                 }
495                         }
496
497                         return false;
498                 }
499                 case "mouse_pressed":
500                 {
501                         if(...(0,int) == K_MOUSE1)
502                         {
503                                 c4_client_event(minigame, "mouse_moved");
504                                 c4_make_move(minigame);
505                                 return true;
506                         }
507
508                         return false;
509                 }
510                 case "mouse_moved":
511                 {
512                         vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize);
513                         if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
514                         {
515                                 c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT)));
516                         }
517                         if ( ! c4_valid_tile(c4_curr_pos) )
518                                 c4_set_curr_pos("");
519
520                         return true;
521                 }
522                 case "network_receive":
523                 {
524                         entity sent = ...(0,entity);
525                         int sf = ...(1,int);
526                         if ( sent.classname == "minigame" )
527                         {
528                                 if ( sf & MINIG_SF_UPDATE )
529                                 {
530                                         strcpy(sent.message, c4_turn_to_string(sent.minigame_flags));
531                                         if ( sent.minigame_flags & minigame_self.team )
532                                                 minigame_prompt();
533                                 }
534                         }
535
536                         return false;
537                 }
538         }
539
540         return false;
541 }
542
543 #endif