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