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 end = 0;
143 for(int i = C4_NUM_CNT; i >= 0; --i)
144 {
147 {
148 end = i;
149 break;
150 }
151 }
153}
154
155// make a move
156void c4_move(entity minigame, entity player, string pos )
157{
158 pos = c4_get_lowest_tile(minigame, pos);
159
160 if ( minigame.minigame_flags & C4_TURN_PLACE )
161 if ( pos && player.team == (minigame.minigame_flags & C4_TURN_TEAM) )
162 {
163 if ( c4_valid_tile(pos) )
164 if ( !c4_find_piece(minigame,pos) )
165 {
166 entity piece = msle_spawn(minigame,new(minigame_board_piece));
167 piece.team = player.team;
168 piece.netname = strzone(pos);
171 ++minigame.c4_npieces;
172 minigame.c4_nexteam = minigame_next_team(player.team,C4_TEAMS);
173 if ( c4_winning_piece(piece) )
174 {
175 minigame.minigame_flags = C4_TURN_WIN | player.team;
176 }
177 else if ( minigame.c4_npieces >= C4_MAX_TILES )
178 minigame.minigame_flags = C4_TURN_DRAW;
179 else
180 minigame.minigame_flags = C4_TURN_PLACE | minigame.c4_nexteam;
181 }
182 }
183}
184
185#ifdef SVQC
186
187
188// required function, handle server side events
189int c4_server_event(entity minigame, string event, ...)
190{
191 switch(event)
192 {
193 case "start":
194 {
195 minigame.minigame_flags = (C4_TURN_PLACE | C4_TURN_TEAM1);
196 return true;
197 }
198 case "end":
199 {
200 entity e = NULL;
201 while( (e = findentity(e, owner, minigame)) )
202 if(e.classname == "minigame_board_piece")
203 {
204 strfree(e.netname);
205 delete(e);
206 }
207 return false;
208 }
209 case "join":
210 {
211 int pl_num = minigame_count_players(minigame);
212
213 // Don't allow more than 2 players
214 if(pl_num >= C4_TEAMS) { return C4_SPECTATOR_TEAM; }
215
216 // Get the right team
217 if(minigame.minigame_players)
218 return minigame_next_team(minigame.minigame_players.team, C4_TEAMS);
219
220 // Team 1 by default
221 return 1;
222 }
223 case "cmd":
224 {
225 entity player = ...(0,entity);
226 bool event_blocked = (player.team == C4_SPECTATOR_TEAM);
227 switch(argv(0))
228 {
229 case "move":
230 if(event_blocked)
231 return true;
232 c4_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null );
233 return true;
234 }
235
236 return false;
237 }
238 }
239
240 return false;
241}
242
243
244#elif defined(CSQC)
245
246string c4_curr_pos; // identifier of the tile under the mouse
247vector c4_boardpos; // HUD board position
248vector c4_boardsize;// HUD board size
249.int c4_checkwin; // Used to optimize checks to display a win
250
251// Required function, draw the game board
252void c4_hud_board(vector pos, vector mySize)
253{
254 minigame_hud_fitsqare(pos, mySize);
255 c4_boardpos = pos;
256 c4_boardsize = mySize;
257
258 minigame_hud_simpleboard(pos,mySize,minigame_texture("c4/board_under"));
259
260 drawpic(pos, minigame_texture("c4/board_over"), mySize, '1 1 1', 1, 0);
261
262 vector tile_size = minigame_hud_denormalize_size('1 1 0' / C4_TILE_SIZE,pos,mySize);
263 vector tile_pos;
264
265 if ( (active_minigame.minigame_flags & C4_TURN_TEAM) == minigame_self.team )
266 if ( c4_valid_tile(c4_curr_pos) )
267 {
268 tile_pos = minigame_tile_pos(c4_curr_pos,C4_NUM_CNT,C4_LET_CNT);
269 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
271 minigame_texture(strcat("c4/piece",ftos(minigame_self.team))),
272 tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL );
273 }
274
275 entity e;
277 {
278 if ( e.classname == "minigame_board_piece" )
279 {
280 tile_pos = minigame_tile_pos(e.netname,C4_NUM_CNT,C4_LET_CNT);
281 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
282
283 if ( active_minigame.minigame_flags & C4_TURN_WIN )
284 if ( !e.c4_checkwin )
285 e.c4_checkwin = c4_winning_piece(e) ? 1 : -1;
286
287 float icon_color = 1;
288 if ( e.c4_checkwin == -1 )
289 icon_color = 0.4;
290 else if ( e.c4_checkwin == 1 )
291 {
292 icon_color = 2;
293 minigame_drawpic_centered( tile_pos, minigame_texture("c4/winglow"),
294 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_ADDITIVE );
295 }
296
298 minigame_texture(strcat("c4/piece",ftos(e.team))),
299 tile_size, '1 1 1'*icon_color, panel_fg_alpha, DRAWFLAG_NORMAL );
300 }
301 }
302
303 if ( active_minigame.minigame_flags & C4_TURN_WIN )
304 {
305 vector winfs = hud_fontsize*2;
306 string pname = "";
308 if ( e.classname == "minigame_player" &&
309 e.team == (active_minigame.minigame_flags & C4_TURN_TEAM) )
310 pname = entcs_GetName(e.minigame_playerslot-1);
311
312 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
313 vector win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
314 sprintf(_("%s^7 won the game!"), pname),
315 winfs, 0, DRAWFLAG_NORMAL, 0.5);
316
317 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5*panel_fg_alpha,DRAWFLAG_ADDITIVE);
318
320 sprintf(_("%s^7 won the game!"), pname),
321 winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
322 }
323
324 minigame_show_allspecs(c4_boardpos, c4_boardsize);
325}
326
327
328// Required function, draw the game status panel
329void c4_hud_status(vector pos, vector mySize)
330{
332 vector ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
333 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
334
335 pos_y += ts_y;
336 mySize_y -= ts_y;
337
338 vector player_fontsize = hud_fontsize * 1.75;
339 ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
340 ts_x = mySize_x;
341 vector mypos;
342 vector tile_size = '48 48 0';
343
345 {
346 mypos = pos;
347 if ( (active_minigame.minigame_flags&C4_TURN_TEAM) == 2 )
348 mypos_y += player_fontsize_y + ts_y;
349 drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5*panel_fg_alpha,DRAWFLAG_ADDITIVE);
350 mypos_y += player_fontsize_y;
351 drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25*panel_fg_alpha,DRAWFLAG_ADDITIVE);
352 }
353
354 entity e;
356 {
357 if ( e.classname == "minigame_player" && e.team != C4_SPECTATOR_TEAM )
358 {
359 mypos = pos;
360 if ( e.team == 2 )
361 mypos_y += player_fontsize_y + ts_y;
363 entcs_GetName(e.minigame_playerslot-1),
364 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
365
366 mypos_y += player_fontsize_y;
367 drawpic( mypos,
368 minigame_texture(strcat("c4/piece",ftos(e.team))),
369 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
370
371 mypos_x += tile_size_x;
372 }
373 }
374}
375
376// Turn a set of flags into a help message
377string c4_turn_to_string(int turnflags)
378{
380 return _("You are spectating");
381
382 if ( turnflags & C4_TURN_DRAW )
383 return _("Draw");
384
385 if ( turnflags & C4_TURN_WIN )
386 {
387 if ( (turnflags&C4_TURN_TEAM) != minigame_self.team )
388 return _("You lost the game!");
389 return _("You win!");
390 }
391
392 if ( (turnflags & C4_TURN_TEAM) != minigame_self.team )
393 return _("Wait for your opponent to make their move");
394
395 if ( turnflags & C4_TURN_PLACE )
396 return _("Click on the game board to place your piece");
397
398 return "";
399}
400
401// Make the correct move
402void c4_make_move(entity minigame)
403{
404 if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
405 {
406 minigame_cmd("move ",c4_curr_pos);
407 }
408}
409
410void c4_set_curr_pos(string s)
411{
412 strfree(c4_curr_pos);
413 if ( s )
414 s = strzone(s);
415 c4_curr_pos = s;
416}
417
418// Required function, handle client events
419int c4_client_event(entity minigame, string event, ...)
420{
421 switch(event)
422 {
423 case "activate":
424 {
425 c4_set_curr_pos("");
426 strcpy(minigame.message, c4_turn_to_string(minigame.minigame_flags));
427 return false;
428 }
429 case "deactivate":
430 {
431 strfree(minigame.message);
432 return false;
433 }
434 case "key_pressed":
435 case "key_released":
436 {
437 bool event_blocked = ((event == "key_released")
438 || ((minigame.minigame_flags & C4_TURN_TEAM) != minigame_self.team));
439 if (!(minigame.minigame_flags & (C4_TURN_WIN | C4_TURN_DRAW)))
440 {
441 switch ( ...(0,int) )
442 {
443 case K_RIGHTARROW:
444 case K_KP_RIGHTARROW:
445 if (event_blocked)
446 return true;
447 if ( ! c4_curr_pos )
448 c4_set_curr_pos(c4_get_lowest_tile(minigame, "a3"));
449 else
450 c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,1,0,C4_NUM_CNT,C4_LET_CNT)));
451 return true;
452 case K_LEFTARROW:
453 case K_KP_LEFTARROW:
454 if (event_blocked)
455 return true;
456 if ( ! c4_curr_pos )
457 c4_set_curr_pos(c4_get_lowest_tile(minigame, "c3"));
458 else
459 c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_relative_tile(c4_curr_pos,-1,0,C4_NUM_CNT,C4_LET_CNT)));
460 return true;
461 case K_UPARROW:
462 case K_KP_UPARROW:
463 case K_DOWNARROW:
464 case K_KP_DOWNARROW:
465 return true;
466 /*case K_UPARROW:
467 case K_KP_UPARROW:
468 if (event_blocked)
469 return true;
470 if ( ! c4_curr_pos )
471 c4_set_curr_pos("a1");
472 else
473 c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,1,6,7));
474 return true;
475 case K_DOWNARROW:
476 case K_KP_DOWNARROW:
477 if (event_blocked)
478 return true;
479 if ( ! c4_curr_pos )
480 c4_set_curr_pos("a3");
481 else
482 c4_set_curr_pos(minigame_relative_tile(c4_curr_pos,0,-1,6,7));
483 return true;*/
484 case K_ENTER:
485 case K_KP_ENTER:
486 case K_SPACE:
487 if (event_blocked)
488 return true;
489 c4_make_move(minigame);
490 return true;
491 }
492 }
493
494 return false;
495 }
496 case "mouse_pressed":
497 {
498 if(...(0,int) == K_MOUSE1)
499 {
500 c4_client_event(minigame, "mouse_moved");
501 c4_make_move(minigame);
502 return true;
503 }
504
505 return false;
506 }
507 case "mouse_moved":
508 {
509 vector mouse_pos = minigame_hud_normalize(mousepos,c4_boardpos,c4_boardsize);
510 if ( minigame.minigame_flags == (C4_TURN_PLACE|minigame_self.team) )
511 {
512 c4_set_curr_pos(c4_get_lowest_tile(minigame, minigame_tile_name(mouse_pos,C4_NUM_CNT,C4_LET_CNT)));
513 }
514 if ( ! c4_valid_tile(c4_curr_pos) )
515 c4_set_curr_pos("");
516
517 return true;
518 }
519 case "network_receive":
520 {
521 entity sent = ...(0,entity);
522 int sf = ...(1,int);
523 if ( sent.classname == "minigame" )
524 {
525 if ( sf & MINIG_SF_UPDATE )
526 {
527 strcpy(sent.message, c4_turn_to_string(sent.minigame_flags));
528 if ( sent.minigame_flags & minigame_self.team )
530 }
531 }
532
533 return false;
534 }
535 }
536
537 return false;
538}
539
540#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:156
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:189
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