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