]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/ps.qc
Merge branch 'terencehill/translation_system_improvements_2' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / ps.qc
1 const float PS_TURN_MOVE  = 0x0100; // player has to click on a piece on the board
2 const float PS_TURN_WIN   = 0x0200; // player has won
3 const float PS_TURN_DRAW  = 0x0400; // player can make no more moves
4 const float PS_TURN_TYPE  = 0x0f00; // turn type mask
5
6 const int PS_LET_CNT = 7;
7 const int PS_NUM_CNT = 7;
8
9 const int PS_TILE_SIZE = 8;
10
11 // find same game piece given its tile name
12 entity ps_find_piece(entity minig, string tile)
13 {
14         entity e = world;
15         while ( ( e = findentity(e,owner,minig) ) )
16                 if ( e.classname == "minigame_board_piece" && e.netname == tile )
17                         return e;
18         return world;
19 }
20
21 bool ps_draw(entity minigame)
22 {
23         int valid = 0;
24         entity e = world;
25         while( ( e = findentity(e,owner,minigame) ) )
26                 if( e.classname == "minigame_board_piece" )
27                 {
28                         ++valid;
29                 }
30
31         return ((valid > 0) ? true : false);
32 }
33
34 bool ps_tile_blacklisted(string tile)
35 {
36         int number = minigame_tile_number(tile);
37         int letter = minigame_tile_letter(tile);
38         if(letter < 2)
39                 if(number < 2)
40                         return true;
41                 else if(number > PS_NUM_CNT - 3)
42                         return true;
43         if(letter > PS_LET_CNT - 3)
44                 if(number < 2)
45                         return true;
46                 else if(number > PS_NUM_CNT - 3)
47                         return true;
48
49         return false;
50 }
51
52 // check if the tile name is valid (5x5 grid)
53 bool ps_valid_tile(string tile)
54 {
55         if ( !tile )
56                 return false;
57         if(ps_tile_blacklisted(tile))
58                 return false;
59         float number = minigame_tile_number(tile);
60         float letter = minigame_tile_letter(tile);
61         return 0 <= number && number < PS_NUM_CNT && 0 <= letter && letter < PS_LET_CNT;
62 }
63
64 // Checks if the given piece completes a row
65 bool ps_winning_piece(entity minigame)
66 {
67         //int number = minigame_tile_number(piece.netname);
68         //int letter = minigame_tile_letter(piece.netname);
69
70         entity e = world;
71         while ( ( e = findentity(e,owner,minigame) ) )
72                 if ( e.classname == "minigame_board_piece" )
73                 {
74                         int number = minigame_tile_number(e.netname);
75                         int letter = minigame_tile_letter(e.netname);
76                         string try = minigame_tile_buildname(letter - 1, number);
77                         if(ps_find_piece(minigame,try))
78                         {
79                                 try = minigame_tile_buildname(letter - 2, number);
80                                 if(ps_valid_tile(try) && !ps_find_piece(minigame,try))
81                                         return false; // a move is valid, abort!
82                         }
83                         try = minigame_tile_buildname(letter + 1, number);
84                         if(ps_find_piece(minigame,try))
85                         {
86                                 try = minigame_tile_buildname(letter + 2, number);
87                                 if(ps_valid_tile(try) && !ps_find_piece(minigame,try))
88                                         return false; // a move is valid, abort!
89                         }
90                         try = minigame_tile_buildname(letter, number - 1);
91                         if(ps_find_piece(minigame,try))
92                         {
93                                 try = minigame_tile_buildname(letter, number - 2);
94                                 if(ps_valid_tile(try) && !ps_find_piece(minigame,try))
95                                         return false; // a move is valid, abort!
96                         }
97                         try = minigame_tile_buildname(letter, number + 1);
98                         if(ps_find_piece(minigame,try))
99                         {
100                                 try = minigame_tile_buildname(letter, number + 2);
101                                 if(ps_valid_tile(try) && !ps_find_piece(minigame,try))
102                                         return false; // a move is valid, abort!
103                         }
104                 }
105
106         return true;
107 }
108
109 void ps_setup_pieces(entity minigame)
110 {
111         int i, t;
112         for(i = 0; i < PS_NUM_CNT; ++i)
113         for(t = 0; t < PS_LET_CNT; ++t)
114         {
115                 string try = minigame_tile_buildname(i,t);
116                 if(!ps_valid_tile(try))
117                         continue;
118                 if(i == floor(PS_NUM_CNT * 0.5) && t == floor(PS_LET_CNT * 0.5))
119                         continue; // middle piece is empty
120                 entity piece = msle_spawn(minigame,"minigame_board_piece");
121                 piece.team = 1; // init default team?
122                 piece.netname = strzone(minigame_tile_buildname(t,i));
123                 minigame_server_sendflags(piece,MINIG_SF_ALL);
124         }
125
126         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
127 }
128
129 bool ps_move_piece(entity minigame, entity piece, string pos, int leti, int numb)
130 {
131         if(!piece)
132                 return false;
133         if(ps_find_piece(minigame, pos))
134                 return false;
135         entity middle = ps_find_piece(minigame, minigame_tile_buildname(leti,numb));
136         if(!middle)
137                 return false;
138
139         if(middle.netname) { strunzone(middle.netname); }
140         remove(middle);
141
142         if(piece.netname) { strunzone(piece.netname); }
143         piece.netname = strzone(pos);
144
145         minigame_server_sendflags(piece,MINIG_SF_ALL);
146
147         return true;
148 }
149
150 // make a move
151 void ps_move(entity minigame, entity player, string thepiece, string pos )
152 {
153         if ( minigame.minigame_flags & PS_TURN_MOVE )
154         if ( pos )
155         {
156                 if ( ps_valid_tile(pos) )
157                 if ( !ps_find_piece(minigame, pos) && ps_find_piece(minigame, thepiece) )
158                 {
159                         entity piece = ps_find_piece(minigame, thepiece);
160                         int number = minigame_tile_number(thepiece);
161                         int letter = minigame_tile_letter(thepiece);
162                         bool done = false;
163                         string try;
164
165                         try = minigame_tile_buildname(letter-1,number);
166                         if(ps_find_piece(minigame,try))
167                         {
168                                 try = minigame_tile_buildname(letter-2,number);
169                                 if(ps_valid_tile(try) && try == pos)
170                                         done = ps_move_piece(minigame, piece, pos, letter - 1, number);
171                         }
172                         try = minigame_tile_buildname(letter+1,number);
173                         if(!done && ps_find_piece(minigame,try))
174                         {
175                                 try = minigame_tile_buildname(letter+2,number);
176                                 if(ps_valid_tile(try) && try == pos)
177                                         done = ps_move_piece(minigame, piece, pos, letter + 1, number);
178                         }
179                         try = minigame_tile_buildname(letter,number-1);
180                         if(!done && ps_find_piece(minigame,try))
181                         {
182                                 try = minigame_tile_buildname(letter,number-2);
183                                 if(ps_valid_tile(try) && try == pos)
184                                         done = ps_move_piece(minigame, piece, pos, letter, number - 1);
185                         }
186                         try = minigame_tile_buildname(letter,number+1);
187                         if(!done && ps_find_piece(minigame,try))
188                         {
189                                 try = minigame_tile_buildname(letter,number+2);
190                                 if(ps_valid_tile(try) && try == pos)
191                                         done = ps_move_piece(minigame, piece, pos, letter, number + 1);
192                         }
193
194                         if(!done)
195                                 return; // didn't make a move
196
197                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
198
199                         if ( ps_winning_piece(minigame) )
200                         {
201                                 if(ps_draw(minigame))
202                                         minigame.minigame_flags = PS_TURN_DRAW;
203                                 else
204                                         minigame.minigame_flags = PS_TURN_WIN;
205                         }
206                         else
207                                 minigame.minigame_flags = PS_TURN_MOVE;
208                 }
209         }
210 }
211
212 #ifdef SVQC
213
214
215 // required function, handle server side events
216 int ps_server_event(entity minigame, string event, ...)
217 {
218         switch(event)
219         {
220                 case "start":
221                 {
222                         ps_setup_pieces(minigame);
223                         minigame.minigame_flags = PS_TURN_MOVE;
224                         return true;
225                 }
226                 case "end":
227                 {
228                         entity e = world;
229                         while( (e = findentity(e, owner, minigame)) )
230                         if(e.classname == "minigame_board_piece")
231                         {
232                                 if(e.netname) { strunzone(e.netname); }
233                                 remove(e);
234                         }
235                         return false;
236                 }
237                 case "join":
238                 {
239                         int pl_num = minigame_count_players(minigame);
240
241                         // Don't allow more than 1 player
242                         if(pl_num >= 1) { return false; }
243
244                         // Team 1 by default
245                         return 1;
246                 }
247                 case "cmd":
248                 {
249                         switch(argv(0))
250                         {
251                                 case "move":
252
253                                         ps_move(minigame, ...(0,entity), (...(1,int) == 3 ? argv(1) : string_null), (...(1,int) == 3 ? argv(2) : string_null));
254                                         return true;
255                         }
256
257                         return false;
258                 }
259         }
260
261         return false;
262 }
263
264
265 #elif defined(CSQC)
266
267 entity ps_curr_piece; // identifier for the currently selected piece
268 string ps_curr_pos; // identifier of the tile under the mouse
269 vector ps_boardpos; // HUD board position
270 vector ps_boardsize;// HUD board size
271
272 // Required function, draw the game board
273 void ps_hud_board(vector pos, vector mySize)
274 {
275         minigame_hud_fitsqare(pos, mySize);
276         ps_boardpos = pos;
277         ps_boardsize = mySize;
278
279         minigame_hud_simpleboard(pos,mySize,minigame_texture("ps/board"));
280
281         vector tile_size = minigame_hud_denormalize_size('1 1 0' / PS_TILE_SIZE,pos,mySize);
282         vector tile_pos;
283
284         bool valid = ps_valid_tile(ps_curr_pos);
285         bool highlight = false;
286         if(valid)
287         {
288                 string try;
289                 int number = minigame_tile_number(ps_curr_pos);
290                 int letter = minigame_tile_letter(ps_curr_pos);
291                 try = minigame_tile_buildname(letter-1,number);
292                 if(ps_find_piece(active_minigame,try))
293                 {
294                         try = minigame_tile_buildname(letter-2,number);
295                         if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try))
296                                 highlight = true;
297                 }
298                 try = minigame_tile_buildname(letter+1,number);
299                 if(ps_find_piece(active_minigame,try))
300                 {
301                         try = minigame_tile_buildname(letter+2,number);
302                         if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try))
303                                 highlight = true;
304                 }
305                 try = minigame_tile_buildname(letter,number-1);
306                 if(ps_find_piece(active_minigame,try))
307                 {
308                         try = minigame_tile_buildname(letter,number-2);
309                         if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try))
310                                 highlight = true;
311                 }
312                 try = minigame_tile_buildname(letter,number+1);
313                 if(ps_find_piece(active_minigame,try))
314                 {
315                         try = minigame_tile_buildname(letter,number+2);
316                         if(ps_valid_tile(try) && !ps_find_piece(active_minigame,try))
317                                 highlight = true;
318                 }
319         }
320         bool draw_pos = false;
321         if(ps_curr_piece && valid && !ps_find_piece(active_minigame, ps_curr_pos))
322         {
323                 string try; // sigh
324                 int numb = minigame_tile_number(ps_curr_piece.netname);
325                 int leti = minigame_tile_letter(ps_curr_piece.netname);
326
327                 try = minigame_tile_buildname(leti-1,numb);
328                 if(ps_find_piece(active_minigame,try))
329                 {
330                         try = minigame_tile_buildname(leti-2,numb);
331                         if(try == ps_curr_pos)
332                                 draw_pos = true;
333                 }
334                 try = minigame_tile_buildname(leti+1,numb);
335                 if(ps_find_piece(active_minigame,try))
336                 {
337                         try = minigame_tile_buildname(leti+2,numb);
338                         if(try == ps_curr_pos)
339                                 draw_pos = true;
340                 }
341                 try = minigame_tile_buildname(leti,numb-1);
342                 if(ps_find_piece(active_minigame,try))
343                 {
344                         try = minigame_tile_buildname(leti,numb-2);
345                         if(try == ps_curr_pos)
346                                 draw_pos = true;
347                 }
348                 try = minigame_tile_buildname(leti,numb+1);
349                 if(ps_find_piece(active_minigame,try))
350                 {
351                         try = minigame_tile_buildname(leti,numb+2);
352                         if(try == ps_curr_pos)
353                                 draw_pos = true;
354                 }
355         }
356
357         entity e;
358         FOREACH_MINIGAME_ENTITY(e)
359         {
360                 if ( e.classname == "minigame_board_piece" )
361                 {
362                         tile_pos = minigame_tile_pos(e.netname,PS_NUM_CNT,PS_LET_CNT);
363                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
364
365                         vector tile_color = '1 1 1';
366
367                         if(highlight)
368                         if(e.netname == ps_curr_pos)
369                         if(ps_curr_piece.netname != ps_curr_pos)
370                         {
371                                 minigame_drawpic_centered( tile_pos,
372                                                 minigame_texture("ps/tile_available"),
373                                                 tile_size, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
374                         }
375                         if(e == ps_curr_piece)
376                         {
377                                 minigame_drawpic_centered( tile_pos,
378                                                 minigame_texture("ps/tile_selected"),
379                                                 tile_size, tile_color, panel_fg_alpha, DRAWFLAG_ADDITIVE );
380                         }
381
382                         minigame_drawpic_centered( tile_pos,
383                                         minigame_texture("ps/piece"),
384                                         tile_size * 0.8, tile_color, panel_fg_alpha, DRAWFLAG_NORMAL );
385                 }
386         }
387
388         if(draw_pos)
389         {
390                 tile_pos = minigame_tile_pos(ps_curr_pos,PS_NUM_CNT,PS_LET_CNT);
391                 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
392
393                 minigame_drawpic_centered(tile_pos,
394                                 minigame_texture("ps/piece"),
395                                 tile_size * 0.8, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL);
396         }
397
398         if ( ( active_minigame.minigame_flags & PS_TURN_WIN ) || ( active_minigame.minigame_flags & PS_TURN_DRAW ) )
399         {
400                 int remaining = 0;
401                 FOREACH_MINIGAME_ENTITY(e)
402                         if(e.classname == "minigame_board_piece")
403                                 ++remaining;
404
405                 vector winfs = hud_fontsize*2;
406                 string remaining_text;
407                 if(active_minigame.minigame_flags & PS_TURN_WIN)
408                         remaining_text = "All pieces cleared!";
409                 else
410                         remaining_text = strcat("Remaining pieces: ", ftos(remaining));
411
412                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
413                 vector win_sz;
414                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
415                         sprintf("Game over! %s", remaining_text),
416                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
417
418                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
419
420                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
421                         sprintf("Game over! %s", remaining_text),
422                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
423         }
424 }
425
426
427 // Required function, draw the game status panel
428 void ps_hud_status(vector pos, vector mySize)
429 {
430         HUD_Panel_DrawBg(1);
431         vector ts;
432         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
433                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
434
435         pos_y += ts_y;
436         mySize_y -= ts_y;
437
438         vector player_fontsize = hud_fontsize * 1.75;
439         ts_y = ( mySize_y - 2*player_fontsize_y ) / 2;
440         ts_x = mySize_x;
441         vector mypos;
442         vector tile_size = '48 48 0';
443
444         mypos = pos;
445         drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
446         mypos_y += player_fontsize_y;
447         drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
448
449         int remaining = 0;
450         entity e;
451         FOREACH_MINIGAME_ENTITY(e)
452         {
453                 if(e.classname == "minigame_board_piece")
454                 {
455                         ++remaining;
456                 }
457         }
458
459         FOREACH_MINIGAME_ENTITY(e)
460         {
461                 if ( e.classname == "minigame_player" )
462                 {
463                         mypos = pos;
464                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
465                                 GetPlayerName(e.minigame_playerslot-1),
466                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
467
468                         mypos_y += player_fontsize_y;
469                         //drawpic( mypos,
470                         //              minigame_texture("ps/piece"),
471                         //              tile_size, '1 0 0', panel_fg_alpha, DRAWFLAG_NORMAL );
472
473                         //mypos_x += tile_size_x;
474
475                         drawstring(mypos,sprintf(_("Pieces left: %s"), ftos(remaining)),'28 28 0',
476                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
477                 }
478         }
479 }
480
481 // Turn a set of flags into a help message
482 string ps_turn_to_string(int turnflags)
483 {
484         if (turnflags & PS_TURN_DRAW )
485                 return _("No more valid moves");
486
487         if ( turnflags & PS_TURN_WIN )
488                 return _("Well done, you win!");
489
490         if ( turnflags & PS_TURN_MOVE )
491                 return _("Jump a piece over another to capture it");
492
493         return "";
494 }
495
496 // Make the correct move
497 void ps_make_move(entity minigame)
498 {
499         if ( minigame.minigame_flags == PS_TURN_MOVE )
500         {
501                 entity piece = ps_find_piece(minigame,ps_curr_pos);
502                 if(!ps_curr_piece || piece)
503                         ps_curr_piece = ps_find_piece(minigame,ps_curr_pos);
504                 else
505                 {
506                         minigame_cmd("move ", ps_curr_piece.netname, " ", ps_curr_pos);
507                         ps_curr_piece = world;
508                 }
509         }
510 }
511
512 void ps_set_curr_pos(string s)
513 {
514         if ( ps_curr_pos )
515                 strunzone(ps_curr_pos);
516         if ( s )
517                 s = strzone(s);
518         ps_curr_pos = s;
519 }
520
521 // Required function, handle client events
522 int ps_client_event(entity minigame, string event, ...)
523 {
524         switch(event)
525         {
526                 case "activate":
527                 {
528                         ps_set_curr_pos("");
529                         ps_curr_piece = world;
530                         minigame.message = ps_turn_to_string(minigame.minigame_flags);
531                         return false;
532                 }
533                 case "key_pressed":
534                 {
535                         //if((minigame.minigame_flags & PS_TURN_TEAM) == minigame_self.team)
536                         {
537                                 switch ( ...(0,int) )
538                                 {
539                                         case K_RIGHTARROW:
540                                         case K_KP_RIGHTARROW:
541                                                 if ( ! ps_curr_pos )
542                                                         ps_set_curr_pos("a3");
543                                                 else
544                                                         ps_set_curr_pos( minigame_relative_tile(ps_curr_pos,1,0,PS_NUM_CNT,PS_LET_CNT));
545                                                 return true;
546                                         case K_LEFTARROW:
547                                         case K_KP_LEFTARROW:
548                                                 if ( ! ps_curr_pos )
549                                                         ps_set_curr_pos("c3");
550                                                 else
551                                                         ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,-1,0,PS_NUM_CNT,PS_LET_CNT));
552                                                 return true;
553                                         case K_UPARROW:
554                                         case K_KP_UPARROW:
555                                                 if ( ! ps_curr_pos )
556                                                         ps_set_curr_pos("a1");
557                                                 else
558                                                         ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,0,1,PS_NUM_CNT,PS_LET_CNT));
559                                                 return true;
560                                         case K_DOWNARROW:
561                                         case K_KP_DOWNARROW:
562                                                 if ( ! ps_curr_pos )
563                                                         ps_set_curr_pos("a3");
564                                                 else
565                                                         ps_set_curr_pos(minigame_relative_tile(ps_curr_pos,0,-1,PS_NUM_CNT,PS_LET_CNT));
566                                                 return true;
567                                         case K_ENTER:
568                                         case K_KP_ENTER:
569                                         case K_SPACE:
570                                                 ps_make_move(minigame);
571                                                 return true;
572                                 }
573                         }
574
575                         return false;
576                 }
577                 case "mouse_pressed":
578                 {
579                         if(...(0,int) == K_MOUSE1)
580                         {
581                                 ps_make_move(minigame);
582                                 return true;
583                         }
584
585                         return false;
586                 }
587                 case "mouse_moved":
588                 {
589                         vector mouse_pos = minigame_hud_normalize(mousepos,ps_boardpos,ps_boardsize);
590                         if ( minigame.minigame_flags == PS_TURN_MOVE )
591                         {
592                                 ps_set_curr_pos(minigame_tile_name(mouse_pos,PS_NUM_CNT,PS_LET_CNT));
593                         }
594                         if ( ! ps_valid_tile(ps_curr_pos) )
595                                 ps_set_curr_pos("");
596
597                         return true;
598                 }
599                 case "network_receive":
600                 {
601                         entity sent = ...(0,entity);
602                         int sf = ...(1,int);
603                         if ( sent.classname == "minigame" )
604                         {
605                                 if ( sf & MINIG_SF_UPDATE )
606                                 {
607                                         sent.message = ps_turn_to_string(sent.minigame_flags);
608                                         if ( sent.minigame_flags & minigame_self.team )
609                                                 minigame_prompt();
610                                 }
611                         }
612
613                         return false;
614                 }
615         }
616
617         return false;
618 }
619
620 #endif