]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/c4.qc
Minigames: apply panel alpha to highlights too
[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
327
328 // Required function, draw the game status panel
329 void c4_hud_status(vector pos, vector mySize)
330 {
331         HUD_Panel_DrawBg();
332         vector ts;
333         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
334                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
335
336         pos_y += ts_y;
337         mySize_y -= ts_y;
338
339         vector player_fontsize = hud_fontsize * 1.75;
340         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
341         ts_x = mySize_x;
342         vector mypos;
343         vector tile_size = '48 48 0';
344
345         if(minigame_self.team != C4_SPECTATOR_TEAM)
346         {
347                 mypos = pos;
348                 if ( (active_minigame.minigame_flags&C4_TURN_TEAM) == 2 )
349                         mypos_y  += player_fontsize_y + ts_y;
350                 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5*panel_fg_alpha,DRAWFLAG_ADDITIVE);
351                 mypos_y += player_fontsize_y;
352                 drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25*panel_fg_alpha,DRAWFLAG_ADDITIVE);
353         }
354
355         entity e;
356         string allspecs = "";
357         FOREACH_MINIGAME_ENTITY(e)
358         {
359                 if ( e.classname == "minigame_player" && e.team != C4_SPECTATOR_TEAM )
360                 {
361                         mypos = pos;
362                         if ( e.team == 2 )
363                                 mypos_y  += player_fontsize_y + ts_y;
364                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
365                                 entcs_GetName(e.minigame_playerslot-1),
366                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
367
368                         mypos_y += player_fontsize_y;
369                         drawpic( mypos,
370                                         minigame_texture(strcat("c4/piece",ftos(e.team))),
371                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
372
373                         mypos_x += tile_size_x;
374                 }
375
376                 if (e.classname == "minigame_player" && e.team == C4_SPECTATOR_TEAM)
377                 {
378                         string current_spec = "";
379
380                         string cmpctString = entcs_GetName(e.minigame_playerslot - 1);
381                         if (strlennocol(cmpctString) > 8)
382                         {
383                                 int new_length = textLengthUpToLength(cmpctString, 8, strlennocol);
384                                 cmpctString = strcat(substring(cmpctString, 0, new_length), "..");
385                         }
386                         if (strlen(allspecs) > 0)
387                                 current_spec = strcat(",", cmpctString);
388                         else
389                                 current_spec = cmpctString;
390                         if (strlen(allspecs) < 90)
391                                 allspecs = strcat(allspecs, current_spec);
392                 }
393         }
394         if (strlen(allspecs) > 0)
395         {
396                 pos_y = pos_y * 0.3;
397                 pos_x = pos_x * 0.41;
398
399                 ts = minigame_drawstring_wrapped(mySize_x * 1.7, pos, "Spectators: ", '14 14 0', '0.85 0.47 0.42', panel_fg_alpha, DRAWFLAG_NORMAL, 0);
400                 pos_y += 14;
401                 ts = minigame_drawcolorcodedstring_wrapped(mySize_x * 1.7, pos, allspecs, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL, 0);
402         }
403 }
404
405 // Turn a set of flags into a help message
406 string c4_turn_to_string(int turnflags)
407 {
408         if(minigame_self.team == C4_SPECTATOR_TEAM)
409                 return _("You are spectating");
410
411         if ( turnflags & C4_TURN_DRAW )
412                 return _("Draw");
413
414         if ( turnflags & C4_TURN_WIN )
415         {
416                 if ( (turnflags&C4_TURN_TEAM) != minigame_self.team )
417                         return _("You lost the game!");
418                 return _("You win!");
419         }
420
421         if ( (turnflags & C4_TURN_TEAM) != minigame_self.team )
422                 return _("Wait for your opponent to make their move");
423
424         if ( turnflags & C4_TURN_PLACE )
425                 return _("Click on the game board to place your piece");
426
427         return "";
428 }
429
430 // Make the correct move
431 void c4_make_move(entity minigame)
432 {
433         if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
434         {
435                 minigame_cmd("move ",c4_curr_pos);
436         }
437 }
438
439 void c4_set_curr_pos(string s)
440 {
441         strfree(c4_curr_pos);
442         if ( s )
443                 s = strzone(s);
444         c4_curr_pos = s;
445 }
446
447 // Required function, handle client events
448 int c4_client_event(entity minigame, string event, ...)
449 {
450         switch(event)
451         {
452                 case "activate":
453                 {
454                         c4_set_curr_pos("");
455                         strcpy(minigame.message, c4_turn_to_string(minigame.minigame_flags));
456                         return false;
457                 }
458                 case "deactivate":
459                 {
460                         strfree(minigame.message);
461                         return false;
462                 }
463                 case "key_pressed":
464                 case "key_released":
465                 {
466                         bool event_blocked = ((event == "key_released")
467                                 || ((minigame.minigame_flags & C4_TURN_TEAM) != minigame_self.team));
468                         if (!(minigame.minigame_flags & C4_TURN_WIN) && !(minigame.minigame_flags & C4_TURN_DRAW))
469                         {
470                                 switch ( ...(0,int) )
471                                 {
472                                         case K_RIGHTARROW:
473                                         case K_KP_RIGHTARROW:
474                                                 if (event_blocked)
475                                                         return true;
476                                                 if ( ! c4_curr_pos )
477                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3"));
478                                                 else
479                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT)));
480                                                 return true;
481                                         case K_LEFTARROW:
482                                         case K_KP_LEFTARROW:
483                                                 if (event_blocked)
484                                                         return true;
485                                                 if ( ! c4_curr_pos )
486                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3"));
487                                                 else
488                                                         c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT)));
489                                                 return true;
490                                         case K_UPARROW:
491                                         case K_KP_UPARROW:
492                                         case K_DOWNARROW:
493                                         case K_KP_DOWNARROW:
494                                                 return true;
495                                         /*case K_UPARROW:
496                                         case K_KP_UPARROW:
497                                                 if (event_blocked)
498                                                         return true;
499                                                 if ( ! c4_curr_pos )
500                                                         c4_set_curr_pos("a1");
501                                                 else
502                                                         c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,1,6,7));
503                                                 return true;
504                                         case K_DOWNARROW:
505                                         case K_KP_DOWNARROW:
506                                                 if (event_blocked)
507                                                         return true;
508                                                 if ( ! c4_curr_pos )
509                                                         c4_set_curr_pos("a3");
510                                                 else
511                                                         c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,-1,6,7));
512                                                 return true;*/
513                                         case K_ENTER:
514                                         case K_KP_ENTER:
515                                         case K_SPACE:
516                                                 if (event_blocked)
517                                                         return true;
518                                                 c4_make_move(minigame);
519                                                 return true;
520                                 }
521                         }
522
523                         return false;
524                 }
525                 case "mouse_pressed":
526                 {
527                         if(...(0,int) == K_MOUSE1)
528                         {
529                                 c4_client_event(minigame, "mouse_moved");
530                                 c4_make_move(minigame);
531                                 return true;
532                         }
533
534                         return false;
535                 }
536                 case "mouse_moved":
537                 {
538                         vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize);
539                         if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
540                         {
541                                 c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT)));
542                         }
543                         if ( ! c4_valid_tile(c4_curr_pos) )
544                                 c4_set_curr_pos("");
545
546                         return true;
547                 }
548                 case "network_receive":
549                 {
550                         entity sent = ...(0,entity);
551                         int sf = ...(1,int);
552                         if ( sent.classname == "minigame" )
553                         {
554                                 if ( sf & MINIG_SF_UPDATE )
555                                 {
556                                         strcpy(sent.message, c4_turn_to_string(sent.minigame_flags));
557                                         if ( sent.minigame_flags & minigame_self.team )
558                                                 minigame_prompt();
559                                 }
560                         }
561
562                         return false;
563                 }
564         }
565
566         return false;
567 }
568
569 #endif