]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/pp.qc
Merge branch 'terencehill/itemstime_checkmark2' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / pp.qc
1 REGISTER_MINIGAME(pp, "Push-Pull");
2
3 const int PP_TURN_PLACE = 0x0100; // player has to place a piece on the board
4 const int PP_TURN_WIN   = 0x0200; // player has won
5 const int PP_TURN_DRAW  = 0x0400; // players have equal scores
6 const int PP_TURN_NEXT  = 0x0800; // a player wants to start a new match
7 const int PP_TURN_TYPE  = 0x0f00; // turn type mask
8
9 const int PP_TURN_TEAM1 = 0x0001;
10 const int PP_TURN_TEAM2 = 0x0002;
11 const int PP_TURN_TEAM  = 0x000f; // turn team mask
12
13 const int PP_BLOCKED_TEAM = 5; // there won't ever be a 5th team, so we can abuse this
14
15 const int PP_LET_CNT = 7;
16 const int PP_NUM_CNT = 7;
17
18 const int PP_TILE_SIZE = 7;
19
20 .int pp_team1_score;
21 .int pp_team2_score;
22
23 .int pp_nexteam;
24
25 .entity pp_curr_piece; // identifier for the current target piece
26
27 // find tic tac toe piece given its tile name
28 entity pp_find_piece(entity minig, string tile)
29 {
30         entity e = world;
31         while ( ( e = findentity(e,owner,minig) ) )
32                 if ( e.classname == "minigame_board_piece" && e.netname == tile )
33                         return e;
34         return world;
35 }
36
37 // check if the tile name is valid (3x3 grid)
38 bool pp_valid_tile(string tile)
39 {
40         if ( !tile )
41                 return 0;
42         int number = minigame_tile_number(tile);
43         int letter = minigame_tile_letter(tile);
44         return 0 <= number && number < PP_NUM_CNT && 0 <= letter && letter < PP_LET_CNT;
45 }
46
47 // Checks if the given piece completes a row
48 bool pp_winning_piece(entity piece)
49 {
50         int number = minigame_tile_number(piece.netname);
51         int letter = minigame_tile_letter(piece.netname);
52
53         // here goes
54         if(!pp_valid_tile(minigame_tile_buildname(letter-1,number)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number)).team == 5)
55         if(!pp_valid_tile(minigame_tile_buildname(letter+1,number)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number)).team == 5)
56         if(!pp_valid_tile(minigame_tile_buildname(letter,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter,number-1)).team == 5)
57         if(!pp_valid_tile(minigame_tile_buildname(letter,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter,number+1)).team == 5)
58         if(!pp_valid_tile(minigame_tile_buildname(letter+1,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number+1)).team == 5)
59         if(!pp_valid_tile(minigame_tile_buildname(letter-1,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number-1)).team == 5)
60         if(!pp_valid_tile(minigame_tile_buildname(letter+1,number-1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter+1,number-1)).team == 5)
61         if(!pp_valid_tile(minigame_tile_buildname(letter-1,number+1)) || pp_find_piece(piece.owner,minigame_tile_buildname(letter-1,number+1)).team == 5)
62                 return true;
63
64         return false;
65 }
66
67 bool pp_valid_move(entity minigame, string pos)
68 {
69         if(!pp_valid_tile(pos))
70                 return false;
71         if(pp_find_piece(minigame,pos).team == 5)
72                 return false;
73
74         entity current = minigame.pp_curr_piece;
75         if(!current)
76                 return true; // no current piece? allow the move anywhere
77
78         int number = minigame_tile_number(pos);
79         int letter = minigame_tile_letter(pos);
80
81         if( (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number)) == current)
82         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number)) == current)
83         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter,number-1)) == current)
84         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter,number+1)) == current)
85         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number+1)) == current)
86         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number-1)) == current)
87         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter+1,number-1)) == current)
88         ||      (pp_find_piece(minigame,minigame_tile_buildname(letter-1,number+1)) == current)
89         ) { return true; }
90
91         return false;
92 }
93
94 // make a move
95 void pp_move(entity minigame, entity player, string pos )
96 {
97         if ( minigame.minigame_flags & PP_TURN_PLACE )
98         if ( pos && player.team == (minigame.minigame_flags & PP_TURN_TEAM) )
99         {
100                 if ( pp_valid_move(minigame,pos))
101                 {
102                         entity existing = pp_find_piece(minigame,pos);
103
104                         if(existing && existing.team != 5)
105                         {
106                                 if(existing.team == 1)
107                                         minigame.pp_team1_score++;
108                                 if(existing.team == 2)
109                                         minigame.pp_team2_score++;
110                         }
111
112                         if(minigame.pp_curr_piece)
113                         {
114                                 minigame.pp_curr_piece.cnt = 0;
115                                 minigame.pp_curr_piece.team = 5;
116                                 minigame_server_sendflags(minigame.pp_curr_piece,MINIG_SF_ALL);
117                         }
118
119                         if(existing)
120                         {
121                                 if(existing.netname) { strunzone(existing.netname); }
122                                 remove(existing);
123                         }
124
125                         entity piece = msle_spawn(minigame,"minigame_board_piece");
126                         piece.cnt = 1;
127                         piece.team = player.team; // temporary
128                         piece.netname = strzone(pos);
129                         minigame_server_sendflags(piece,MINIG_SF_ALL);
130                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
131                         minigame.pp_nexteam = minigame_next_team(player.team,2);
132                         minigame.pp_curr_piece = piece;
133                         if ( pp_winning_piece(piece) )
134                         {
135                                 if(minigame.pp_team1_score == minigame.pp_team2_score)
136                                         minigame.minigame_flags = PP_TURN_DRAW;
137                                 else
138                                         minigame.minigame_flags = PP_TURN_WIN | ((minigame.pp_team1_score > minigame.pp_team2_score) ? 1 : 2);
139                         }
140                         else
141                                 minigame.minigame_flags = PP_TURN_PLACE | minigame.pp_nexteam;
142                 }
143         }
144 }
145
146 void pp_setup_pieces(entity minigame)
147 {
148         int i, t; // letter, number
149         for(i = 0; i < PP_LET_CNT; ++i)
150         for(t = 0; t < PP_NUM_CNT; ++t)
151         {
152                 bool t2_true = ((i == 0 || i == 6) && t > 0 && t < 6);
153                 bool t1_true = (i > 0 && i < 6 && (t == 0 || t == 6));
154
155                 if(t1_true || t2_true)
156                 {
157                         entity piece = msle_spawn(minigame,"minigame_board_piece");
158                         piece.team = ((t1_true) ? 1 : 2);
159                         piece.netname = strzone(minigame_tile_buildname(i,t));
160                         minigame_server_sendflags(piece,MINIG_SF_ALL);
161                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
162                 }
163         }
164
165         minigame.pp_curr_piece = world;
166 }
167
168 // request a new match
169 void pp_next_match(entity minigame, entity player)
170 {
171 #ifdef SVQC
172         // on multiplayer matches, wait for both players to agree
173         if ( minigame.minigame_flags & (PP_TURN_WIN|PP_TURN_DRAW) )
174         {
175                 minigame.minigame_flags = PP_TURN_NEXT | player.team;
176                 minigame.SendFlags |= MINIG_SF_UPDATE;
177         }
178         else if ( (minigame.minigame_flags & PP_TURN_NEXT) &&
179                         !( minigame.minigame_flags & player.team ) )
180 #endif
181         {
182                 minigame.minigame_flags = PP_TURN_PLACE | minigame.pp_nexteam;
183                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
184                 entity e = world;
185                 while ( ( e = findentity(e,owner,minigame) ) )
186                         if ( e.classname == "minigame_board_piece" )
187                                 remove(e);
188                 minigame.pp_team1_score = 0;
189                 minigame.pp_team2_score = 0;
190
191                 pp_setup_pieces(minigame);
192         }
193 }
194
195 #ifdef SVQC
196
197
198 // required function, handle server side events
199 int pp_server_event(entity minigame, string event, ...)
200 {
201         switch(event)
202         {
203                 case "start":
204                 {
205                         minigame.minigame_flags = (PP_TURN_PLACE | PP_TURN_TEAM1);
206                         pp_setup_pieces(minigame);
207                         return true;
208                 }
209                 case "end":
210                 {
211                         entity e = world;
212                         while( (e = findentity(e, owner, minigame)) )
213                         if(e.classname == "minigame_board_piece")
214                         {
215                                 if(e.netname) { strunzone(e.netname); }
216                                 remove(e);
217                         }
218                         return false;
219                 }
220                 case "join":
221                 {
222                         int pl_num = minigame_count_players(minigame);
223
224                         // Don't allow more than 2 players
225                         if(pl_num >= 2) { return false; }
226
227                         // Get the right team
228                         if(minigame.minigame_players)
229                                 return minigame_next_team(minigame.minigame_players.team, 2);
230
231                         // Team 1 by default
232                         return 1;
233                 }
234                 case "cmd":
235                 {
236                         switch(argv(0))
237                         {
238                                 case "move":
239                                         pp_move(minigame, ...(0,entity), ...(1,int) == 2 ? argv(1) : string_null );
240                                         return true;
241                                 case "next":
242                                         pp_next_match(minigame,...(0,entity));
243                                         return true;
244                         }
245
246                         return false;
247                 }
248                 case "network_send":
249                 {
250                         entity sent = ...(0,entity);
251                         int sf = ...(1,int);
252                         if ( sent.classname == "minigame" && (sf & MINIG_SF_UPDATE ) )
253                         {
254                                 WriteByte(MSG_ENTITY,sent.pp_team1_score);
255                                 WriteByte(MSG_ENTITY,sent.pp_team2_score);
256                         }
257                         else if(sent.classname == "minigame_board_piece")
258                                 WriteByte(MSG_ENTITY,sent.cnt);
259                         return false;
260                 }
261         }
262
263         return false;
264 }
265
266
267 #elif defined(CSQC)
268
269 string pp_curr_pos; // identifier of the tile under the mouse
270 vector pp_boardpos; // HUD board position
271 vector pp_boardsize;// HUD board size
272 .int pp_checkwin; // Used to optimize checks to display a win
273
274 // Required function, draw the game board
275 void pp_hud_board(vector pos, vector mySize)
276 {
277         minigame_hud_fitsqare(pos, mySize);
278         pp_boardpos = pos;
279         pp_boardsize = mySize;
280
281         minigame_hud_simpleboard(pos,mySize,minigame_texture("pp/board"));
282
283         vector tile_size = minigame_hud_denormalize_size('1 1 0'/PP_TILE_SIZE,pos,mySize);
284         vector tile_pos;
285
286         active_minigame.pp_curr_piece = world;
287         entity e;
288         FOREACH_MINIGAME_ENTITY(e)
289         if(e.classname == "minigame_board_piece")
290         if(e.cnt)
291         {
292                 active_minigame.pp_curr_piece = e;
293                 break;
294         }
295
296         FOREACH_MINIGAME_ENTITY(e)
297         {
298                 if ( e.classname == "minigame_board_piece" )
299                 {
300                         tile_pos = minigame_tile_pos(e.netname,PP_LET_CNT,PP_NUM_CNT);
301                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
302
303                         vector tile_color = '1 1 1';
304                         switch(e.team)
305                         {
306                                 case 1: tile_color = '1 0.3 0.3'; break;
307                                 case 2: tile_color = '0.3 0.3 1'; break;
308                                 // 3, 4 coming later?
309                         }
310
311                         string tile_name = strcat("pp/piece",ftos(e.team));
312                         if(e.team == 5) { tile_name = "pp/piece_taken"; }
313
314                         if(e == active_minigame.pp_curr_piece)
315                         {
316                                 tile_name = "pp/piece_current";
317
318                                 // draw the splat too
319                                 minigame_drawpic_centered( tile_pos,
320                                                 minigame_texture("pp/piece_taken"),
321                                                 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
322                         }
323
324                         minigame_drawpic_centered( tile_pos,
325                                         minigame_texture(tile_name),
326                                         tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
327                 }
328         }
329
330         if ( (active_minigame.minigame_flags & PP_TURN_TEAM) == minigame_self.team )
331         if ( pp_valid_move(active_minigame, pp_curr_pos) )
332         {
333                 tile_pos = minigame_tile_pos(pp_curr_pos,PP_LET_CNT,PP_NUM_CNT);
334                 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
335                 minigame_drawpic_centered( tile_pos,
336                                 minigame_texture("pp/piece_current"),
337                                 tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
338         }
339         else if(pp_valid_tile(pp_curr_pos))
340         {
341                 tile_pos = minigame_tile_pos(pp_curr_pos,PP_LET_CNT,PP_NUM_CNT);
342                 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
343                 minigame_drawpic_centered( tile_pos,
344                                 minigame_texture("pp/piece_selected"),
345                                 tile_size, '1 1 1', panel_fg_alpha / 2, DRAWFLAG_NORMAL );
346         }
347
348         if ( active_minigame.minigame_flags & PP_TURN_WIN )
349         {
350                 vector winfs = hud_fontsize*2;
351                 string playername = "";
352                 FOREACH_MINIGAME_ENTITY(e)
353                         if ( e.classname == "minigame_player" &&
354                                         e.team == (active_minigame.minigame_flags & PP_TURN_TEAM) )
355                                 playername = GetPlayerName(e.minigame_playerslot-1);
356
357                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
358                 vector win_sz;
359                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
360                         sprintf("%s^7 won the game!",playername),
361                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
362
363                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
364
365                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
366                         sprintf("%s^7 won the game!",playername),
367                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
368         }
369 }
370
371
372 // Required function, draw the game status panel
373 void pp_hud_status(vector pos, vector mySize)
374 {
375         HUD_Panel_DrawBg(1);
376         vector ts;
377         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
378                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
379
380         pos_y += ts_y;
381         mySize_y -= ts_y;
382
383         vector player_fontsize = hud_fontsize * 1.75;
384         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
385         ts_x = mySize_x;
386         vector mypos;
387         vector tile_size = '48 48 0';
388
389         mypos = pos;
390         if ( (active_minigame.minigame_flags&PP_TURN_TEAM) == 2 )
391                 mypos_y  += player_fontsize_y + ts_y;
392         drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
393         mypos_y += player_fontsize_y;
394         drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
395
396         entity e;
397         FOREACH_MINIGAME_ENTITY(e)
398         {
399                 if ( e.classname == "minigame_player" )
400                 {
401                         vector tile_color = '1 1 1';
402                         switch(e.team)
403                         {
404                                 case 1: tile_color = '1 0.3 0.3'; break;
405                                 case 2: tile_color = '0.3 0.3 1'; break;
406                                 // 3, 4 coming later?
407                         }
408
409                         mypos = pos;
410                         if ( e.team == 2 )
411                                 mypos_y  += player_fontsize_y + ts_y;
412                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
413                                 GetPlayerName(e.minigame_playerslot-1),
414                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
415
416                         mypos_y += player_fontsize_y;
417                         drawpic( mypos,
418                                         minigame_texture(strcat("pp/piece",ftos(e.team))),
419                                         tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
420
421                         mypos_x += tile_size_x;
422                         int myscore = 0;
423                         if(e.team == 1) { myscore = active_minigame.pp_team1_score; }
424                         if(e.team == 2) { myscore = active_minigame.pp_team2_score; }
425
426                         drawstring(mypos,ftos(myscore),tile_size,
427                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
428                 }
429         }
430 }
431
432 // Turn a set of flags into a help message
433 string pp_turn_to_string(int turnflags)
434 {
435         if ( turnflags & PP_TURN_DRAW )
436                 return _("Draw");
437
438         if ( turnflags & PP_TURN_WIN )
439         {
440                 if ( (turnflags&PP_TURN_TEAM) != minigame_self.team )
441                         return _("You lost the game!\nSelect \"^1Next Match^7\" on the menu for a rematch!");
442                 return _("You win!\nSelect \"^1Next Match^7\" on the menu to start a new match!");
443         }
444
445         if ( turnflags & PP_TURN_NEXT )
446         {
447                 if ( (turnflags&PP_TURN_TEAM) != minigame_self.team )
448                         return _("Select \"^1Next Match^7\" on the menu to start a new match!");
449                 return _("Wait for your opponent to confirm the rematch");
450         }
451
452         if ( (turnflags & PP_TURN_TEAM) != minigame_self.team )
453                 return _("Wait for your opponent to make their move");
454
455         if ( turnflags & PP_TURN_PLACE )
456                 return _("Click on the game board to place your piece");
457
458         return "";
459 }
460
461 // Make the correct move
462 void pp_make_move(entity minigame)
463 {
464         if ( minigame.minigame_flags == (PP_TURN_PLACE|minigame_self.team) )
465         {
466                 minigame_cmd("move ",pp_curr_pos);
467         }
468 }
469
470 void pp_set_curr_pos(string s)
471 {
472         if ( pp_curr_pos )
473                 strunzone(pp_curr_pos);
474         if ( s )
475                 s = strzone(s);
476         pp_curr_pos = s;
477 }
478
479 // Required function, handle client events
480 int pp_client_event(entity minigame, string event, ...)
481 {
482         switch(event)
483         {
484                 case "activate":
485                 {
486                         pp_set_curr_pos("");
487                         minigame.message = pp_turn_to_string(minigame.minigame_flags);
488                         return false;
489                 }
490                 case "key_pressed":
491                 {
492                         if((minigame.minigame_flags & PP_TURN_TEAM) == minigame_self.team)
493                         {
494                                 switch ( ...(0,int) )
495                                 {
496                                         case K_RIGHTARROW:
497                                         case K_KP_RIGHTARROW:
498                                                 if ( ! pp_curr_pos )
499                                                         pp_set_curr_pos("a3");
500                                                 else
501                                                         pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,1,0,PP_LET_CNT,PP_NUM_CNT));
502                                                 return true;
503                                         case K_LEFTARROW:
504                                         case K_KP_LEFTARROW:
505                                                 if ( ! pp_curr_pos )
506                                                         pp_set_curr_pos("c3");
507                                                 else
508                                                         pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,-1,0,PP_LET_CNT,PP_NUM_CNT));
509                                                 return true;
510                                         case K_UPARROW:
511                                         case K_KP_UPARROW:
512                                                 if ( ! pp_curr_pos )
513                                                         pp_set_curr_pos("a1");
514                                                 else
515                                                         pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,0,1,PP_LET_CNT,PP_NUM_CNT));
516                                                 return true;
517                                         case K_DOWNARROW:
518                                         case K_KP_DOWNARROW:
519                                                 if ( ! pp_curr_pos )
520                                                         pp_set_curr_pos("a3");
521                                                 else
522                                                         pp_set_curr_pos(minigame_relative_tile(pp_curr_pos,0,-1,PP_LET_CNT,PP_NUM_CNT));
523                                                 return true;
524                                         case K_ENTER:
525                                         case K_KP_ENTER:
526                                         case K_SPACE:
527                                                 pp_make_move(minigame);
528                                                 return true;
529                                 }
530                         }
531
532                         return false;
533                 }
534                 case "mouse_pressed":
535                 {
536                         if(...(0,int) == K_MOUSE1)
537                         {
538                                 pp_make_move(minigame);
539                                 return true;
540                         }
541
542                         return false;
543                 }
544                 case "mouse_moved":
545                 {
546                         vector mouse_pos = minigame_hud_normalize(mousepos,pp_boardpos,pp_boardsize);
547                         if ( minigame.minigame_flags == (PP_TURN_PLACE|minigame_self.team) )
548                                 pp_set_curr_pos(minigame_tile_name(mouse_pos,PP_LET_CNT,PP_NUM_CNT));
549                         if ( ! pp_valid_tile(pp_curr_pos) )
550                                 pp_set_curr_pos("");
551
552                         return true;
553                 }
554                 case "network_receive":
555                 {
556                         entity sent = ...(0,entity);
557                         int sf = ...(1,int);
558                         if ( sent.classname == "minigame" )
559                         {
560                                 if ( sf & MINIG_SF_UPDATE )
561                                 {
562                                         sent.message = pp_turn_to_string(sent.minigame_flags);
563                                         if ( sent.minigame_flags & minigame_self.team )
564                                                 minigame_prompt();
565                                         sent.pp_team1_score = ReadByte();
566                                         sent.pp_team2_score = ReadByte();
567                                 }
568                         }
569                         else if(sent.classname == "minigame_board_piece")
570                         {
571                                 sent.cnt = ReadByte();
572                                 if(sent.cnt)
573                                         minigame.pp_curr_piece = sent;
574                         }
575
576                         return false;
577                 }
578                 case "menu_show":
579                 {
580                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Match"),"next");
581                         return false;
582                 }
583                 case "menu_click":
584                 {
585                         if(...(0,string) == "next")
586                                 minigame_cmd("next");
587                         return false;
588                 }
589         }
590
591         return false;
592 }
593
594 #endif