Xonotic
c4.qc
Go to the documentation of this file.
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
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  {
148  {
149  end = i;
150  break;
151  }
152  }
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);
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;
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 = "";
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,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 {
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,DRAWFLAG_ADDITIVE);
351  mypos_y += player_fontsize_y;
352  drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
353  }
354 
355  entity e;
357  {
358  if ( e.classname == "minigame_player" && e.team != C4_SPECTATOR_TEAM )
359  {
360  mypos = pos;
361  if ( e.team == 2 )
362  mypos_y += player_fontsize_y + ts_y;
364  entcs_GetName(e.minigame_playerslot-1),
365  player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
366 
367  mypos_y += player_fontsize_y;
368  drawpic( mypos,
369  minigame_texture(strcat("c4/piece",ftos(e.team))),
370  tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
371 
372  mypos_x += tile_size_x;
373  }
374  }
375 }
376 
377 // Turn a set of flags into a help message
378 string c4_turn_to_string(int turnflags)
379 {
380  if(minigame_self.team == C4_SPECTATOR_TEAM)
381  return _("You are spectating");
382 
383  if ( turnflags & C4_TURN_DRAW )
384  return _("Draw");
385 
386  if ( turnflags & C4_TURN_WIN )
387  {
388  if ( (turnflags&C4_TURN_TEAM) != minigame_self.team )
389  return _("You lost the game!");
390  return _("You win!");
391  }
392 
393  if ( (turnflags & C4_TURN_TEAM) != minigame_self.team )
394  return _("Wait for your opponent to make their move");
395 
396  if ( turnflags & C4_TURN_PLACE )
397  return _("Click on the game board to place your piece");
398 
399  return "";
400 }
401 
402 // Make the correct move
403 void c4_make_move(entity minigame)
404 {
405  if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
406  {
407  minigame_cmd("move ",c4_curr_pos);
408  }
409 }
410 
411 void c4_set_curr_pos(string s)
412 {
413  strfree(c4_curr_pos);
414  if ( s )
415  s = strzone(s);
416  c4_curr_pos = s;
417 }
418 
419 // Required function, handle client events
420 int c4_client_event(entity minigame, string event, ...)
421 {
422  switch(event)
423  {
424  case "activate":
425  {
426  c4_set_curr_pos("");
427  strcpy(minigame.message, c4_turn_to_string(minigame.minigame_flags));
428  return false;
429  }
430  case "deactivate":
431  {
432  strfree(minigame.message);
433  return false;
434  }
435  case "key_pressed":
436  case "key_released":
437  {
438  bool event_blocked = ((event == "key_released")
439  || ((minigame.minigame_flags & C4_TURN_TEAM) != minigame_self.team));
440  if (!(minigame.minigame_flags & C4_TURN_WIN) && !(minigame.minigame_flags & C4_TURN_DRAW))
441  {
442  switch ( ...(0,int) )
443  {
444  case K_RIGHTARROW:
445  case K_KP_RIGHTARROW:
446  if (event_blocked)
447  return true;
448  if ( ! c4_curr_pos )
449  c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3"));
450  else
451  c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT)));
452  return true;
453  case K_LEFTARROW:
454  case K_KP_LEFTARROW:
455  if (event_blocked)
456  return true;
457  if ( ! c4_curr_pos )
458  c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3"));
459  else
460  c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT)));
461  return true;
462  case K_UPARROW:
463  case K_KP_UPARROW:
464  case K_DOWNARROW:
465  case K_KP_DOWNARROW:
466  return true;
467  /*case K_UPARROW:
468  case K_KP_UPARROW:
469  if (event_blocked)
470  return true;
471  if ( ! c4_curr_pos )
472  c4_set_curr_pos("a1");
473  else
474  c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,1,6,7));
475  return true;
476  case K_DOWNARROW:
477  case K_KP_DOWNARROW:
478  if (event_blocked)
479  return true;
480  if ( ! c4_curr_pos )
481  c4_set_curr_pos("a3");
482  else
483  c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,-1,6,7));
484  return true;*/
485  case K_ENTER:
486  case K_KP_ENTER:
487  case K_SPACE:
488  if (event_blocked)
489  return true;
490  c4_make_move(minigame);
491  return true;
492  }
493  }
494 
495  return false;
496  }
497  case "mouse_pressed":
498  {
499  if(...(0,int) == K_MOUSE1)
500  {
501  c4_client_event(minigame, "mouse_moved");
502  c4_make_move(minigame);
503  return true;
504  }
505 
506  return false;
507  }
508  case "mouse_moved":
509  {
510  vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize);
511  if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
512  {
513  c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT)));
514  }
515  if ( ! c4_valid_tile(c4_curr_pos) )
516  c4_set_curr_pos("");
517 
518  return true;
519  }
520  case "network_receive":
521  {
522  entity sent = ...(0,entity);
523  int sf = ...(1,int);
524  if ( sent.classname == "minigame" )
525  {
526  if ( sf & MINIG_SF_UPDATE )
527  {
528  strcpy(sent.message, c4_turn_to_string(sent.minigame_flags));
529  if ( sent.minigame_flags & minigame_self.team )
530  minigame_prompt();
531  }
532  }
533 
534  return false;
535  }
536  }
537 
538  return false;
539 }
540 
541 #endif
float K_KP_RIGHTARROW
Definition: keycodes.qc:60
float K_UPARROW
Definition: keycodes.qc:15
const float C4_TURN_DRAW
Definition: c4.qc:6
int minigame_tile_number(string id)
Definition: minigames.qc:21
void minigame_server_sendflags(entity ent, int mgflags)
Definition: minigames.qc:78
const int MINIG_SF_ALL
Definition: minigames.qh:112
float panel_fg_alpha
Definition: hud.qh:166
string string_null
Definition: nil.qh:9
bool c4_valid_tile(string tile)
Definition: c4.qc:131
#define int
Definition: _all.inc:20
#define FOREACH_MINIGAME_ENTITY(entityvar)
Definition: cl_minigames.qh:98
const vector eY
Definition: vector.qh:45
vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:23
const float C4_TURN_TEAM2
Definition: c4.qc:9
int int number
Definition: impulse.qc:89
float K_DOWNARROW
Definition: keycodes.qc:16
int team
Definition: main.qh:157
#define minigame_hud_fitsqare(pos, mySize)
Definition: cl_minigames.qh:7
entity msle_spawn(entity minigame_session, entity e)
Definition: minigames.qc:87
float K_KP_DOWNARROW
Definition: keycodes.qc:53
int minigame_next_team(int curr_team, int n_teams)
Definition: minigames.qc:65
entity() spawn
const int C4_NUM_CNT
Definition: c4.qc:13
vector minigame_hud_normalize(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:31
int c4_nexteam
Definition: c4.qc:24
bool c4_winning_piece(entity piece)
Definition: c4.qc:37
string c4_get_lowest_tile(entity minigame, string s)
Definition: c4.qc:140
const int C4_TILE_SIZE
Definition: c4.qc:18
float K_SPACE
Definition: keycodes.qc:10
float K_RIGHTARROW
Definition: keycodes.qc:18
const int MINIG_SF_UPDATE
Definition: minigames.qh:109
entity owner
Definition: main.qh:73
const float C4_TURN_PLACE
Definition: c4.qc:4
float K_KP_ENTER
Definition: keycodes.qc:74
const float C4_TURN_TEAM1
Definition: c4.qc:8
#define strcpy(this, s)
Definition: string.qh:49
string minigame_tile_name(vector pos, int rows, int columns)
Definition: minigames.qc:54
const float DRAWFLAG_ADDITIVE
Definition: csprogsdefs.qc:318
#define HUD_Panel_DrawBg()
Definition: hud.qh:54
void minigame_drawpic_centered(vector pos, string texture, vector sz, vector color, float thealpha, int drawflags)
string minigame_texture(string name)
Definition: cl_minigames.qc:49
const int C4_MAX_TILES
Definition: c4.qc:16
entity active_minigame
Definition: cl_minigames.qh:85
vector minigame_tile_pos(string id, int rows, int columns)
Definition: minigames.qc:27
vector minigame_drawcolorcodedstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags, float align)
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
REGISTER_MINIGAME(c4, _("Connect Four"))
const int C4_TEAMS
Definition: c4.qc:20
#define NULL
Definition: post.qh:17
const float DRAWFLAG_NORMAL
Definition: csprogsdefs.qc:317
int minigame_tile_letter(string id)
Definition: minigames.qc:12
entity c4_find_piece(entity minig, string tile)
Definition: c4.qc:27
#define minigame_cmd(...)
Definition: cl_minigames.qh:90
string minigame_tile_buildname(int letter, int number)
Definition: minigames.qc:34
const int C4_LET_CNT
Definition: c4.qc:12
void c4_move(entity minigame, entity player, string pos)
Definition: c4.qc:157
const float C4_TURN_WIN
Definition: c4.qc:5
vector(float skel, float bonenum) _skel_get_boneabs_hidden
string minigame_relative_tile(string start_id, int dx, int dy, int rows, int columns)
Definition: minigames.qc:40
float K_MOUSE1
Definition: keycodes.qc:129
void minigame_prompt()
int minigame_count_players(entity minigame)
Definition: minigames.qc:121
const int C4_SPECTATOR_TEAM
Definition: c4.qc:21
const vector eX
Definition: vector.qh:44
vector minigame_hud_denormalize(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:16
vector mousepos
Definition: hud.qh:102
float K_LEFTARROW
Definition: keycodes.qc:17
void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags)
vector hud_fontsize
Definition: main.qh:63
#define strfree(this)
Definition: string.qh:56
float K_ENTER
Definition: keycodes.qc:8
void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture)
Definition: cl_minigames.qc:4
const int C4_WIN_CNT
Definition: c4.qc:14
entity minigame_self
Definition: cl_minigames.qh:87
float K_KP_LEFTARROW
Definition: keycodes.qc:57
float K_KP_UPARROW
Definition: keycodes.qc:64
int c4_npieces
Definition: c4.qc:23
vector minigame_drawstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, vector color, float theAlpha, int drawflags, float align)
const float C4_TURN_TEAM
Definition: c4.qc:10