Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
c4.qc
Go to the documentation of this file.
1#include "c4.qh"
2REGISTER_MINIGAME(c4, _("Connect Four"));
3
4const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board
5const float C4_TURN_WIN = 0x0200; // player has won
6const float C4_TURN_DRAW = 0x0400; // no moves are possible
7
8const float C4_TURN_TEAM1 = 0x0001;
9const float C4_TURN_TEAM2 = 0x0002;
10const float C4_TURN_TEAM = 0x000f; // turn team mask
11
12const int C4_LET_CNT = 7;
13const int C4_NUM_CNT = 6;
14const int C4_WIN_CNT = 4;
15
16const int C4_MAX_TILES = 42;
17
18const int C4_TILE_SIZE = 8;
19
20const int C4_TEAMS = 2;
21const 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
27entity 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 {
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)
131bool 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
140string 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
157void 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
190int 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
247string c4_curr_pos; // identifier of the tile under the mouse
248vector c4_boardpos; // HUD board position
249vector 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
253void 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);
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
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*panel_fg_alpha,DRAWFLAG_ADDITIVE);
320
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
331void c4_hud_status(vector pos, vector mySize)
332{
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
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;
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;
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
380string c4_turn_to_string(int turnflags)
381{
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
405void 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
413void 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
422int 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 | 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 )
533 }
534 }
535
536 return false;
537 }
538 }
539
540 return false;
541}
542
543#endif
entity c4_find_piece(entity minig, string tile)
Definition c4.qc:27
const float C4_TURN_TEAM
Definition c4.qc:10
const float C4_TURN_PLACE
Definition c4.qc:4
const float C4_TURN_TEAM2
Definition c4.qc:9
const int C4_SPECTATOR_TEAM
Definition c4.qc:21
void c4_move(entity minigame, entity player, string pos)
Definition c4.qc:157
const int C4_TEAMS
Definition c4.qc:20
int c4_npieces
Definition c4.qc:23
int c4_nexteam
Definition c4.qc:24
const int C4_LET_CNT
Definition c4.qc:12
const float C4_TURN_TEAM1
Definition c4.qc:8
const int C4_NUM_CNT
Definition c4.qc:13
bool c4_valid_tile(string tile)
Definition c4.qc:131
bool c4_winning_piece(entity piece)
Definition c4.qc:37
const float C4_TURN_WIN
Definition c4.qc:5
int c4_server_event(entity minigame, string event,...)
Definition c4.qc:190
const int C4_MAX_TILES
Definition c4.qc:16
const int C4_WIN_CNT
Definition c4.qc:14
const float C4_TURN_DRAW
Definition c4.qc:6
const int C4_TILE_SIZE
Definition c4.qc:18
string c4_get_lowest_tile(entity minigame, string s)
Definition c4.qc:140
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags)
void minigame_show_allspecs(vector boardpos, vector boardsize)
void minigame_prompt()
vector minigame_drawstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, vector color, float theAlpha, int drawflags, float align)
void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture)
string minigame_texture(string name)
vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize)
vector minigame_drawcolorcodedstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags, float align)
vector minigame_hud_denormalize(vector v, vector pos, vector mySize)
vector minigame_hud_normalize(vector v, vector pos, vector mySize)
void minigame_drawpic_centered(vector pos, string texture, vector sz, vector color, float thealpha, int drawflags)
#define REGISTER_MINIGAME(name, nicename)
entity minigame_self
entity active_minigame
#define minigame_hud_fitsqare(pos, mySize)
#define minigame_cmd(...)
#define FOREACH_MINIGAME_ENTITY(entityvar)
#define drawpic(position, pic, size, rgb, alpha, flag)
Definition draw.qh:21
#define drawfill(position, size, rgb, alpha, flag)
Definition draw.qh:36
vector hud_fontsize
Definition main.qh:77
entity owner
Definition main.qh:87
int team
Definition main.qh:188
const float DRAWFLAG_NORMAL
const float DRAWFLAG_ADDITIVE
string entcs_GetName(int i)
Definition ent_cs.qh:151
float panel_fg_alpha
Definition hud.qh:169
#define HUD_Panel_DrawBg()
Definition hud.qh:55
vector mousepos
Definition hud.qh:103
float K_KP_RIGHTARROW
Definition keycodes.qc:60
float K_UPARROW
Definition keycodes.qc:15
float K_DOWNARROW
Definition keycodes.qc:16
float K_MOUSE1
Definition keycodes.qc:129
float K_RIGHTARROW
Definition keycodes.qc:18
float K_KP_UPARROW
Definition keycodes.qc:64
float K_SPACE
Definition keycodes.qc:10
float K_KP_LEFTARROW
Definition keycodes.qc:57
float K_ENTER
Definition keycodes.qc:8
float K_KP_DOWNARROW
Definition keycodes.qc:53
float K_LEFTARROW
Definition keycodes.qc:17
float K_KP_ENTER
Definition keycodes.qc:74
#define int
Definition _all.inc:20
entity findentity(entity start,.entity field, entity match)
string ftos(float f)
string strzone(string s)
string argv(float n)
entity msle_spawn(entity minigame_session, entity e)
Definition minigames.qc:87
string minigame_relative_tile(string start_id, int dx, int dy, int rows, int columns)
Definition minigames.qc:40
void minigame_server_sendflags(entity ent, int mgflags)
Definition minigames.qc:78
int minigame_count_players(entity minigame)
Definition minigames.qc:121
int minigame_next_team(int curr_team, int n_teams)
Definition minigames.qc:65
int minigame_tile_letter(string id)
Definition minigames.qc:12
string minigame_tile_name(vector pos, int rows, int columns)
Definition minigames.qc:54
vector minigame_tile_pos(string id, int rows, int columns)
Definition minigames.qc:27
string minigame_tile_buildname(int letter, int number)
Definition minigames.qc:34
int minigame_tile_number(string id)
Definition minigames.qc:21
const int MINIG_SF_UPDATE
Definition minigames.qh:109
const int MINIG_SF_ALL
Definition minigames.qh:112
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
vector
Definition self.qh:92
int int number
Definition impulse.qc:89
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
const vector eY
Definition vector.qh:45
const vector eX
Definition vector.qh:44