Xonotic
bd.qc
Go to the documentation of this file.
1 #include "bd.qh"
2 REGISTER_MINIGAME(bd, _("Bulldozer"));
3 
4 REGISTER_NET_LINKED(ENT_CLIENT_BD_CONTROLLER)
5 
6 const int BD_TURN_MOVE = 0x0100; // player must move the bulldozer
7 const int BD_TURN_WIN = 0x0200; // victory
8 const int BD_TURN_LOSS = 0x0400; // they did it?!
9 const int BD_TURN_EDIT = 0x0800; // editing mode
10 const int BD_TURN_TYPE = 0x0f00; // turn type mask
11 
12 // send flags
14 const int BD_SF_UPDATE_SINGLE = MINIG_SF_CUSTOM<<1;
15 const int BD_SF_UPDATE_ALL = MINIG_SF_CUSTOM<<2;
16 
17 // 240 tiles...
18 const int BD_LET_CNT = 20;
19 const int BD_NUM_CNT = 20;
20 
21 const int BD_TILE_SIZE = 20;
22 
23 const int BD_TEAMS = 1;
24 const int BD_SPECTATOR_TEAM = 255; // must be above max teams and equal to or below 255
25 
26 .int bd_dir;
27 
28 .int bd_dirs[BD_NUM_CNT];
29 
30 .int bd_moves;
31 
33 
34 .string bd_levelname;
35 .string bd_nextlevel;
36 
37 #ifdef SVQC
38 .bool bd_canedit;
39 .int bd_forceupdate;
40 #endif
41 
43 
45 const int BD_TILE_DOZER = 1;
46 const int BD_TILE_TARGET = 2;
47 const int BD_TILE_BOULDER = 3;
48 const int BD_TILE_BRICK1 = 4;
49 const int BD_TILE_BRICK2 = 5;
50 const int BD_TILE_BRICK3 = 6;
51 const int BD_TILE_BRICK4 = 7;
52 const int BD_TILE_BRICK5 = 8;
53 const int BD_TILE_BRICK6 = 9;
54 const int BD_TILE_BRICK7 = 10;
55 const int BD_TILE_BRICK8 = 11;
56 const int BD_TILE_LAST = 11;
57 
58 const int BD_DIR_UP = 0;
59 const int BD_DIR_DN = 1;
60 const int BD_DIR_LF = 2;
61 const int BD_DIR_RT = 3;
62 
63 #ifdef SVQC
64 string autocvar_sv_minigames_bulldozer_startlevel = "level1";
65 #endif
66 
67 // find same game piece given its tile name
68 entity bd_find_piece(entity minig, string tile, bool check_target)
69 {
70  entity e = NULL;
71  while ( ( e = findentity(e,owner,minig) ) )
72  if ( e.classname == "minigame_board_piece" && e.netname == tile && ((check_target) ? e.bd_tiletype == BD_TILE_TARGET : e.bd_tiletype != BD_TILE_TARGET) )
73  return e;
74  return NULL;
75 }
76 
77 entity bd_find_controller(entity minig, int letter)
78 {
79  entity e = NULL;
80  while ( ( e = findentity(e,owner,minig) ) )
81  if ( e.classname == "bd_controller" && e.bd_tilelet == letter )
82  return e;
83  return NULL;
84 }
85 
86 // check if the tile name is valid (15x15 grid)
87 bool bd_valid_tile(string tile)
88 {
89  if ( !tile )
90  return false;
91  int number = minigame_tile_number(tile);
92  int letter = minigame_tile_letter(tile);
93  return 0 <= number && number < BD_NUM_CNT && 0 <= letter && letter < BD_LET_CNT;
94 }
95 
96 void bd_controller_update(entity controller, int number)
97 {
98 #ifdef SVQC
99  controller.bd_forceupdate = number;
100 #endif
102 }
103 
105 {
106  entity e = NULL;
107  while ( ( e = findentity(e,owner,minig) ) )
108  if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
109  return e;
110  return NULL;
111 }
112 
113 #ifdef SVQC
114 bool bd_controller_send(entity this, entity to, int sf)
115 {
116  WriteHeader(MSG_ENTITY, ENT_CLIENT_BD_CONTROLLER);
117  if(sf & BD_SF_UPDATE_ALL)
118  sf &= ~BD_SF_UPDATE_SINGLE;
119 
120  WriteByte(MSG_ENTITY, sf);
121  WriteByte(MSG_ENTITY, this.bd_tilelet);
122  WriteString(MSG_ENTITY,this.owner.netname);
123 
124  if(sf & BD_SF_UPDATE_SINGLE)
125  {
126  int number = this.bd_forceupdate;
127  //this.bd_forceupdate = 0;
128  int ttype = this.bd_tiletypes[number];
129  int dir = this.bd_dirs[number];
130  WriteByte(MSG_ENTITY, number);
131  WriteByte(MSG_ENTITY, ttype);
132  WriteByte(MSG_ENTITY, dir);
133  }
134 
135  if(sf & BD_SF_UPDATE_ALL)
136  {
137  for(int j = 0; j < BD_NUM_CNT; ++j)
138  {
139  int ttype = this.bd_tiletypes[j];
140  int dir = this.bd_dirs[j];
141  WriteByte(MSG_ENTITY, ttype);
142  WriteByte(MSG_ENTITY, dir);
143  }
144  }
145 
146  return true;
147 }
148 #elif defined(CSQC)
149 void minigame_read_owner(entity this);
150 
151 NET_HANDLE(ENT_CLIENT_BD_CONTROLLER, bool isNew)
152 {
153  this.classname = "bd_controller";
154  return = true;
155 
156  int sf = ReadByte();
157  this.bd_tilelet = ReadByte();
158  minigame_read_owner(this);
159 
160  if(sf & BD_SF_UPDATE_SINGLE)
161  {
162  int number = ReadByte();
163  this.bd_tiletypes[number] = ReadByte();
164  this.bd_dirs[number] = ReadByte();
165  }
166 
167  if(sf & BD_SF_UPDATE_ALL)
168  {
169  for(int j = 0; j < BD_NUM_CNT; ++j)
170  {
171  this.bd_tiletypes[j] = ReadByte();
172  this.bd_dirs[j] = ReadByte();
173  }
174  }
175 }
176 #endif
177 
179 {
180  int total = 0, valid = 0;
181  entity e = NULL;
182  while ( ( e = findentity(e,owner,minig) ) )
183  if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_TARGET )
184  {
185  ++total;
186  if(bd_find_piece(minig, e.netname, false).bd_tiletype == BD_TILE_BOULDER)
187  ++valid;
188  }
189 
190  if(valid >= total)
191  {
192  minig.minigame_flags = BD_TURN_WIN;
194  }
195 }
196 
198 {
199  switch(bdir)
200  {
201  case BD_DIR_UP: return '0 1 0'; // up
202  default:
203  case BD_DIR_DN: return '0 -1 0'; // down
204  case BD_DIR_LF: return '-1 0 0'; // left
205  case BD_DIR_RT: return '1 0 0'; // right
206  }
207 }
208 
209 string bd_get_dir_name(int bdir)
210 {
211  switch(bdir)
212  {
213  case BD_DIR_UP: return "u"; // up
214  default:
215  case BD_DIR_DN: return "d"; // down
216  case BD_DIR_LF: return "l"; // left
217  case BD_DIR_RT: return "r"; // right
218  }
219 }
220 
221 int bd_dir_fromname(string bdir)
222 {
223  if(bdir == "up" || bdir == "u")
224  return BD_DIR_UP; // up
225  if(bdir == "down" || bdir == "dn" || bdir == "d")
226  return BD_DIR_DN;
227  if(bdir == "left" || bdir == "lt" || bdir == "l")
228  return BD_DIR_LF; // left
229  if(bdir == "right" || bdir == "rt" || bdir == "r")
230  return BD_DIR_RT; // right
231 
232  return BD_DIR_DN; // down
233 }
234 
235 bool bd_canfill(int ttype)
236 {
237  switch(ttype)
238  {
239  case BD_TILE_BRICK8:
240  case BD_TILE_BRICK7:
241  case BD_TILE_BRICK6:
242  case BD_TILE_BRICK5:
243  case BD_TILE_BRICK4:
244  case BD_TILE_BRICK3:
245  case BD_TILE_BRICK2:
246  case BD_TILE_BRICK1: return true;
247  }
248 
249  return false;
250 }
251 
252 bool bd_move_dozer(entity minigame, entity dozer)
253 {
254  //if(!dozer.bd_dir)
255  //return false; // nope!
256 
257  int myx = minigame_tile_letter(dozer.netname);
258  int myy = minigame_tile_number(dozer.netname);
259 
260  vector dir = bd_get_dir(dozer.bd_dir);
261 
262  myx += dir.x;
263  myy += dir.y;
264 
265  string newpos = minigame_tile_buildname(myx, myy);
266  if(!bd_valid_tile(newpos))
267  return false;
268 
269  entity hit = bd_find_piece(minigame, newpos, false);
270 
271  if(hit)
272  switch(hit.bd_tiletype)
273  {
274  case BD_TILE_DOZER: // wtf, but let's do this incase
275  case BD_TILE_BRICK8:
276  case BD_TILE_BRICK7:
277  case BD_TILE_BRICK6:
278  case BD_TILE_BRICK5:
279  case BD_TILE_BRICK4:
280  case BD_TILE_BRICK3:
281  case BD_TILE_BRICK2:
282  case BD_TILE_BRICK1: return false;
283  case BD_TILE_BOULDER:
284  {
285  string testpos;
286  int tx = minigame_tile_letter(hit.netname);
287  int ty = minigame_tile_number(hit.netname);
288 
289  tx += dir.x;
290  ty += dir.y;
291 
292  testpos = minigame_tile_buildname(tx, ty);
293  if(!bd_valid_tile(testpos))
294  return false;
295  entity testhit = bd_find_piece(minigame, testpos, false);
296  if(testhit)
297  return false;
298 
299  entity controller = bd_find_controller(minigame, minigame_tile_letter(testpos));
300  int tnum = minigame_tile_number(testpos);
301  switch(controller.bd_tiletypes[tnum])
302  {
303  case BD_TILE_BRICK8:
304  case BD_TILE_BRICK7:
305  case BD_TILE_BRICK6:
306  case BD_TILE_BRICK5:
307  case BD_TILE_BRICK4:
308  case BD_TILE_BRICK3:
309  case BD_TILE_BRICK2:
310  case BD_TILE_BRICK1: return false;
311  }
312 
313  strcpy(hit.netname, testpos);
315  break;
316  }
317  }
318 
319  entity controller = bd_find_controller(minigame, minigame_tile_letter(newpos));
320  int number = minigame_tile_number(newpos);
321  switch(controller.bd_tiletypes[number])
322  {
323  case BD_TILE_BRICK8:
324  case BD_TILE_BRICK7:
325  case BD_TILE_BRICK6:
326  case BD_TILE_BRICK5:
327  case BD_TILE_BRICK4:
328  case BD_TILE_BRICK3:
329  case BD_TILE_BRICK2:
330  case BD_TILE_BRICK1: return false;
331  }
332 
333  strcpy(dozer.netname, newpos);
334 
335  return true;
336 }
337 
338 // make a move
339 void bd_move(entity minigame, entity player, string dir)
340 {
341  if ( minigame.minigame_flags & BD_TURN_MOVE )
342  if ( dir )
343  {
344  //if ( bd_valid_tile(pos) )
345  //if ( bd_find_piece(minigame, pos, false) )
346  {
347  entity dozer = bd_find_dozer(minigame);
348  if(!dozer)
349  {
350  LOG_INFO("Dozer wasn't found!");
351  return; // should not happen... TODO: end match?
352  }
353 
354  string thedir = strtolower(dir);
355  int bdir = bd_dir_fromname(thedir);
356 
357  int moved = 0;
358  entity e = NULL;
359  while ( ( e = findentity(e,owner,minigame) ) )
360  if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
361  {
362  e.bd_dir = bdir;
363 
364  if(bd_move_dozer(minigame, e))
365  ++moved;
366 
367  minigame_server_sendflags(e,MINIG_SF_UPDATE); // update anyway
368  }
369 
370  if(moved)
371  player.bd_moves++;
372 
373  bd_check_winner(minigame);
374 
377  }
378  }
379 }
380 
381 // editor
382 void bd_editor_place(entity minigame, entity player, string pos, int thetile, string thedir)
383 {
384  if ( minigame.minigame_flags & BD_TURN_EDIT )
385  if ( pos && thetile )
386  {
387  if ( bd_valid_tile(pos) )
388  {
389  entity found_piece = bd_find_piece(minigame, pos, false);
390  entity targ = bd_find_piece(minigame, pos, true);
391 
392  if(found_piece.bd_tiletype == BD_TILE_DOZER && thedir != "")
393  {
394  string newdir = strtolower(thedir);
395  int bdir = bd_dir_fromname(newdir);
396 
397  found_piece.bd_dir = bdir;
398  minigame_server_sendflags(found_piece,MINIG_SF_UPDATE); // update anyway
399  return;
400  }
401 
402  //entity dozer = bd_find_dozer(minigame);
403  //if(dozer && thetile == BD_TILE_DOZER && pos != dozer.netname)
404  //return; // nice try
405 
406  int tlet = minigame_tile_letter(pos);
407  int tnum = minigame_tile_number(pos);
408  entity controller = bd_find_controller(minigame, tlet);
409  if(controller.bd_tiletypes[tnum])
410  {
411  controller.bd_tiletypes[tnum] = 0;
412  controller.bd_dirs[tnum] = 0;
413  bd_controller_update(controller, tnum);
414  return;
415  }
416 
417  if(found_piece || (targ && thetile != BD_TILE_BOULDER))
418  {
419  entity piece = bd_find_piece(minigame, pos, false);
420  if(!piece) piece = bd_find_piece(minigame, pos, true);
421  if(!piece)
422  return; // how?!
423 
424  strfree(piece.netname);
425  delete(piece);
427  return;
428  }
429 
430  if(bd_canfill(thetile))
431  {
432  int number = minigame_tile_number(pos);
433  int letter = minigame_tile_letter(pos);
434  entity controller = bd_find_controller(minigame, letter);
435  controller.bd_tiletypes[number] = thetile;
436  controller.bd_dirs[number] = 0;
437  bd_controller_update(controller, number);
438  }
439  else
440  {
441  entity piece = msle_spawn(minigame,new(minigame_board_piece));
442  piece.team = 1;
443  piece.netname = strzone(pos);
444  piece.bd_tiletype = thetile;
445  piece.bd_dir = 0;
447  }
448 
450  }
451  }
452 }
453 
454 void bd_do_move(entity minigame, entity player, string dir, string thetile, string thedir)
455 {
456  if(minigame.minigame_flags & BD_TURN_MOVE)
457  bd_move(minigame, player, dir);
458 
459  if(minigame.minigame_flags & BD_TURN_EDIT)
460  bd_editor_place(minigame, player, dir, stof(thetile), thedir);
461 }
462 
463 void bd_fill_recurse(entity minigame, entity player, int thetype, int letter, int number)
464 {
465  string pos = minigame_tile_buildname(letter,number);
466  if(!bd_valid_tile(pos))
467  return;
468  if(bd_find_piece(minigame, pos, false) || bd_find_piece(minigame, pos, true))
469  return;
470 
471  bd_editor_place(minigame, player, pos, thetype, "");
472 
473  bd_fill_recurse(minigame, player, thetype, letter - 1, number);
474  bd_fill_recurse(minigame, player, thetype, letter + 1, number);
475  bd_fill_recurse(minigame, player, thetype, letter, number - 1);
476  bd_fill_recurse(minigame, player, thetype, letter, number + 1);
477 }
478 
479 void bd_unfill_recurse(entity minigame, entity player, int thetype, int letter, int number)
480 {
481  string pos = minigame_tile_buildname(letter,number);
482  if(!bd_valid_tile(pos))
483  return;
484 
485  entity targ = bd_find_piece(minigame, pos, true);
486  entity piece = bd_find_piece(minigame, pos, false);
487 
488  if(targ && thetype == targ.bd_tiletype)
489  {
490  strfree(targ.netname);
491  delete(targ);
492  }
493  else if(piece && thetype == piece.bd_tiletype)
494  {
495  strfree(piece.netname);
496  delete(piece);
497  }
498  else return;
499 
500  bd_unfill_recurse(minigame, player, thetype, letter - 1, number);
501  bd_unfill_recurse(minigame, player, thetype, letter + 1, number);
502  bd_unfill_recurse(minigame, player, thetype, letter, number - 1);
503  bd_unfill_recurse(minigame, player, thetype, letter, number + 1);
504 }
505 
506 void bd_do_fill(entity minigame, entity player, string dir, string thetile)
507 {
508 #ifdef SVQC
509  if(!player.minigame_players.bd_canedit)
510  {
511  sprint(player.minigame_players, "You're not allowed to edit levels, sorry!\n");
512  return;
513  }
514 #endif
515 
516  if(minigame.minigame_flags & BD_TURN_EDIT)
517  {
518  int thetype = stof(thetile);
519 
520  entity targ = bd_find_piece(minigame, dir, true);
521  entity piece = bd_find_piece(minigame, dir, false);
522 
523  if(!bd_canfill(thetype) || (piece || targ))
524  {
525  int killtype = 0;
526 
527  if(targ) { killtype = targ.bd_tiletype; }
528  if(piece) { killtype = piece.bd_tiletype; }
529 
530  if(killtype)
531  {
532  int letter = minigame_tile_letter(dir);
533  int number = minigame_tile_number(dir);
534  bd_unfill_recurse(minigame, player, killtype, letter, number);
535  }
536 
537  return;
538  }
539 
540  int letter = minigame_tile_letter(dir);
541  int number = minigame_tile_number(dir);
542 
543  bd_fill_recurse(minigame, player, thetype, letter, number);
544  }
545 }
546 
547 void bd_reset_moves(entity minigame)
548 {
549  entity e;
550 #ifdef SVQC
551  for(e = minigame.minigame_players; e; e = e.list_next)
552 #elif defined(CSQC)
553  e = NULL;
554  while( (e = findentity(e,owner,minigame)) )
555  if ( e.classname == "minigame_player" )
556 #endif
557  {
558  e.bd_moves = 0;
560  }
561 }
562 
563 void bd_load_level(entity minigame);
564 void bd_setup_pieces(entity minigame)
565 {
566  entity e = NULL;
567  while( (e = findentity(e, owner, minigame)) )
568  if(e.classname == "minigame_board_piece")
569  {
570  strfree(e.netname);
571  delete(e);
572  }
573  e = NULL;
574  while( (e = findentity(e, owner, minigame)) )
575  if(e.classname == "bd_controller")
576  {
577  delete(e);
578  }
579 
580  for(int letter = 0; letter < BD_LET_CNT; ++letter)
581  {
582  entity controller = new_pure(bd_controller);
583  controller.owner = minigame;
584  controller.bd_tilelet = letter;
585  #ifdef SVQC
586  Net_LinkEntity(controller, false, 0, bd_controller_send);
587  #endif
588  }
589 
590  bd_load_level(minigame);
591 }
592 
593 void bd_do_next_match(entity minigame, entity player)
594 {
595  minigame.minigame_flags = BD_TURN_MOVE;
597 
598  if(minigame.bd_nextlevel && minigame.bd_nextlevel != "")
599  {
600  strcpy(minigame.bd_levelname, minigame.bd_nextlevel);
601  }
602 
603  bd_setup_pieces(minigame);
604 
605  bd_reset_moves(minigame);
606 }
607 
608 void bd_set_next_match(entity minigame, string next)
609 {
610  strcpy(minigame.bd_nextlevel, next);
611 }
612 
613 void bd_next_match(entity minigame, entity player, string next)
614 {
615  if(minigame.minigame_flags & BD_TURN_WIN)
616  bd_do_next_match(minigame, player);
617  if(minigame.minigame_flags & BD_TURN_EDIT)
618  bd_set_next_match(minigame, next);
619 }
620 
621 // request a new match
622 void bd_restart_match(entity minigame, entity player)
623 {
624  minigame.minigame_flags = BD_TURN_MOVE;
626 
627  bd_setup_pieces(minigame);
628 
629  bd_reset_moves(minigame);
630 }
631 
632 void bd_activate_editor(entity minigame, entity player)
633 {
634 #ifdef SVQC
635  if(!player.minigame_players.bd_canedit)
636  {
637  sprint(player.minigame_players, "You're not allowed to edit levels, sorry!\n");
638  return;
639  }
640 #endif
641 
642  minigame.minigame_flags = BD_TURN_EDIT;
644 
645  bd_reset_moves(minigame);
646 
647  bd_setup_pieces(minigame);
648 }
649 
651 {
652  string bd_string = "";
653 
654  string tilename = minigame_tile_buildname(e.bd_tilelet, number);
655 
656  bd_string = strcat(bd_string, "\"", tilename, "\" ");
657  bd_string = strcat(bd_string, ftos(e.bd_tiletypes[number]), " ");
658  bd_string = strcat(bd_string, ftos(e.bd_dirs[number]));
659 
660  return bd_string;
661 }
662 
663 string bd_save_piece(entity minigame, entity e)
664 {
665  string bd_string = "";
666 
667  bd_string = strcat(bd_string, "\"", e.netname, "\" ");
668  bd_string = strcat(bd_string, ftos(e.bd_tiletype), " ");
669  bd_string = strcat(bd_string, ftos(e.bd_dir));
670 
671  return bd_string;
672 }
673 
674 void bd_set_nextlevel(entity minigame, string s)
675 {
676  tokenize_console(s);
677 
678  strcpy(minigame.bd_nextlevel, argv(2));
679 }
680 
682 {
683  if(dir.x == 0 && dir.y == 1) { return BD_DIR_UP; } // up
684  if(dir.x == 0 && dir.y == -1) { return BD_DIR_DN; } // down
685  if(dir.x == -1 && dir.y == 0) { return BD_DIR_LF; } // left
686  if(dir.x == 1 && dir.y == 0) { return BD_DIR_RT; } // right
687 
688  return BD_DIR_DN; // down if all else fails
689 }
690 
691 void bd_load_piece(entity minigame, string s)
692 {
693  // separate pieces between the ; symbols
694  string bd_string = s;
695 
696  tokenize_console(bd_string);
697 
698  int argv_num = 0;
699  string tilename = strzone(argv(argv_num)); ++argv_num;
700  int tiletype = stoi(argv(argv_num)); ++argv_num;
701  int dir = stoi(argv(argv_num)); ++argv_num;
702 
703  if(bd_canfill(tiletype))
704  {
705  int letter = minigame_tile_letter(tilename);
706  int number = minigame_tile_number(tilename);
707  entity controller = bd_find_controller(minigame, letter);
708  controller.bd_tiletypes[number] = tiletype;
709  controller.bd_dirs[number] = dir;
710 
711  bd_controller_update(controller, number);
712  }
713  else
714  {
715  entity e = msle_spawn(minigame,new(minigame_board_piece));
716  e.netname = tilename;
717  e.team = 1;
718  e.bd_dir = dir;
719  e.bd_tiletype = tiletype;
721  }
722 }
723 
724 bool bd_save_level(entity minigame)
725 {
726  if(minigame.bd_levelname && minigame.bd_levelname != "")
727  {
728  int target_count = 0, boulder_count = 0;
729  entity piece = NULL;
730  while((piece = findentity(piece,owner,minigame)))
731  if(piece.classname == "minigame_board_piece")
732  {
733  if(piece.bd_tiletype == BD_TILE_BOULDER)
734  ++boulder_count;
735  else if(piece.bd_tiletype == BD_TILE_TARGET)
736  ++target_count;
737  }
738 
739  if(boulder_count != target_count)
740  {
741  LOG_INFO("Not enough targets or boulders, fix your level!");
742  return false;
743  }
744 
745  // saves all objects to the database file
746  string file_name;
747  float file_get;
748 
749  file_name = strcat("minigames/bulldozer/storage_", minigame.bd_levelname, ".txt");
750  file_get = fopen(file_name, FILE_WRITE);
751  fputs(file_get, strcat("// bulldozer storage \"", minigame.bd_levelname, "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"), "\n"));
752 
753  if(minigame.bd_nextlevel && minigame.bd_nextlevel != "" && fexists(strcat("minigames/bulldozer/storage_", minigame.bd_nextlevel, ".txt")))
754  fputs(file_get, strcat("nextlevel = \"", minigame.bd_nextlevel, "\"\n"));
755 
756  entity e = NULL;
757  while ( ( e = findentity(e,owner,minigame) ) )
758  if ( e.classname == "bd_controller" )
759  {
760  for(int j = 0; j < BD_NUM_CNT; ++j)
761  {
762  // use a line of text for each object, listing all properties
763  fputs(file_get, strcat(bd_save_controller_piece(minigame, e, j), "\n"));
764  }
765  }
766  e = NULL;
767 
768  while ( ( e = findentity(e,owner,minigame) ) )
769  if ( e.classname == "minigame_board_piece" )
770  {
771  // use a line of text for each object, listing all properties
772  fputs(file_get, strcat(bd_save_piece(minigame, e), "\n"));
773  }
774  fclose(file_get);
775 
776  return true;
777  }
778 
779  return false;
780 }
781 
782 void bd_load_level(entity minigame)
783 {
784  // loads all items from the database file
785  string file_read, file_name;
786  float file_get;
787 
788  file_name = strcat("minigames/bulldozer/storage_", minigame.bd_levelname, ".txt");
789  file_get = fopen(file_name, FILE_READ);
790  if(file_get < 0)
791  {
792  LOG_INFO("^3BULLDOZER: ^7could not find storage file ^3", file_name, "^7, no items were loaded");
793  }
794  else
795  {
796  for(;;)
797  {
798  file_read = fgets(file_get);
799  if(file_read == "")
800  break;
801  if(substring(file_read, 0, 2) == "//")
802  continue;
803  if(substring(file_read, 0, 1) == "#")
804  continue;
805  if(substring(file_read, 0, 9) == "nextlevel")
806  {
807  bd_set_nextlevel(minigame, file_read);
808  continue;
809  }
810 
811  bd_load_piece(minigame, file_read);
812  }
813  }
814  fclose(file_get);
815 }
816 
817 void bd_close_editor(entity minigame, entity player)
818 {
819 #ifdef SVQC
820  if(!player.minigame_players.bd_canedit)
821  {
822  sprint(player.minigame_players, "You're not allowed to edit levels, sorry!\n");
823  return;
824  }
825 #endif
826 
827  entity dozer = bd_find_dozer(minigame);
828  if(!dozer)
829  {
830  LOG_INFO("You need to place a bulldozer on the level to save it!");
831  return;
832  }
833 
834  if(bd_save_level(minigame))
835  {
836  minigame.minigame_flags = BD_TURN_MOVE;
838  }
839  else
840  {
841  LOG_INFO("You need to set the level name!");
842  return;
843  }
844 }
845 
846 #ifdef SVQC
847 
848 // required function, handle server side events
849 int bd_server_event(entity minigame, string event, ...)
850 {
851  switch(event)
852  {
853  case "start":
854  {
855  strcpy(minigame.bd_levelname, autocvar_sv_minigames_bulldozer_startlevel);
856  bd_setup_pieces(minigame);
857  minigame.minigame_flags = BD_TURN_MOVE;
858 
859  return true;
860  }
861  case "end":
862  {
863  entity e = NULL;
864  while( (e = findentity(e, owner, minigame)) )
865  if(e.classname == "minigame_board_piece")
866  {
867  strfree(e.netname);
868  delete(e);
869  }
870  e = NULL;
871  while( (e = findentity(e, owner, minigame)) )
872  if(e.classname == "bd_controller")
873  {
874  delete(e);
875  }
876 
877  strfree(minigame.bd_nextlevel);
878  strfree(minigame.bd_levelname);
879  return false;
880  }
881  case "join":
882  {
883  int pl_num = minigame_count_players(minigame);
884 
885  if(pl_num >= BD_TEAMS) { return BD_SPECTATOR_TEAM; }
886 
887  return 1;
888  }
889  case "cmd":
890  {
891  entity player = ...(0,entity);
892  bool event_blocked = (player.team == BD_SPECTATOR_TEAM);
893  switch(argv(0))
894  {
895  case "move":
896  if(event_blocked)
897  return true;
898  bd_do_move(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) >= 3 ? argv(2) : string_null), ((...(1,int)) >= 4 ? argv(3) : string_null));
899  return true;
900  case "next":
901  if(event_blocked)
902  return true;
903  bd_next_match(minigame,...(0,entity), ((...(1,int) >= 2 ? argv(1) : string_null)));
904  return true;
905  case "restart":
906  if(event_blocked)
907  return true;
908  bd_restart_match(minigame,...(0,entity));
909  return true;
910  case "edit":
911  if(event_blocked)
912  return true;
913  bd_activate_editor(minigame,...(0,entity));
914  return true;
915  case "save":
916  if(event_blocked)
917  return true;
918  bd_close_editor(minigame,...(0,entity));
919  return true;
920  case "fill":
921  if(event_blocked)
922  return true;
923  bd_do_fill(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) >= 3 ? argv(2) : string_null));
924  return true;
925  }
926 
927  return false;
928  }
929  case "network_send":
930  {
931  entity sent = ...(0,entity);
932  int sf = ...(1,int);
933  if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) )
934  {
935  int letter = minigame_tile_letter(sent.netname);
936  int number = minigame_tile_number(sent.netname);
937 
938  WriteByte(MSG_ENTITY,letter);
939  WriteByte(MSG_ENTITY,number);
940 
941  WriteByte(MSG_ENTITY,sent.bd_tiletype);
942 
943  WriteByte(MSG_ENTITY,sent.bd_dir);
944  }
945  else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
946  WriteShort(MSG_ENTITY,sent.bd_moves);
947  return false;
948  }
949  }
950 
951  return false;
952 }
953 
954 
955 #elif defined(CSQC)
956 
957 int bd_curr_tile;
958 string bd_curr_pos;
959 
960 .entity bd_enemy;
961 .bool bd_hide;
962 
963 vector bd_boardpos; // HUD board position
964 vector bd_boardsize;// HUD board size
965 
966 string bd_get_tile_pic(int tileid)
967 {
968  switch(tileid)
969  {
970  case BD_TILE_BOULDER: return "bd/boulder";
971  case BD_TILE_BRICK1: return "bd/brick1";
972  case BD_TILE_BRICK2: return "bd/brick2";
973  case BD_TILE_BRICK3: return "bd/brick3";
974  case BD_TILE_BRICK4: return "bd/brick4";
975  case BD_TILE_BRICK5: return "bd/brick5";
976  case BD_TILE_BRICK6: return "bd/brick6";
977  case BD_TILE_BRICK7: return "bd/brick7";
978  case BD_TILE_BRICK8: return "bd/brick8";
979  case BD_TILE_TARGET: return "bd/target";
980  case BD_TILE_DOZER: return "bd/dozer";
981  }
982 
983  return string_null;
984 }
985 
986 // Required function, draw the game board
987 void bd_hud_board(vector pos, vector mySize)
988 {
989  minigame_hud_fitsqare(pos, mySize);
990  bd_boardpos = pos;
991  bd_boardsize = mySize;
992 
993  minigame_hud_simpleboard(pos,mySize,minigame_texture("bd/board"));
994 
995  vector tile_size = minigame_hud_denormalize_size('1 1 0' / BD_TILE_SIZE,pos,mySize);
996  vector tile_pos;
997 
998  entity e;
1000  {
1001  if(e.classname == "minigame_board_piece")
1002  {
1003  if(e.bd_tiletype == BD_TILE_TARGET)
1004  {
1005  e.bd_enemy = NULL;
1006  e.bd_enemy = bd_find_piece(active_minigame, e.netname, false);
1007  }
1008  else if(e.bd_tiletype == BD_TILE_BOULDER)
1009  {
1010  e.bd_hide = false; // reset either way
1011  e.bd_hide = ((bd_find_piece(active_minigame, e.netname, true)) != NULL);
1012  }
1013  }
1014  }
1016  {
1017  if ( e.classname == "bd_controller" )
1018  {
1019  for(int j = 0; j < BD_NUM_CNT; ++j)
1020  {
1021  if(!e.bd_tiletypes[j]) continue;
1022 
1023  int letter = e.bd_tilelet;
1024  string mypos = minigame_tile_buildname(letter, j);
1025 
1026  tile_pos = minigame_tile_pos(mypos,BD_NUM_CNT,BD_LET_CNT);
1027  tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
1028 
1029  string thepiece = bd_get_tile_pic(e.bd_tiletypes[j]);
1030 
1031  minigame_drawpic_centered( tile_pos,
1032  minigame_texture(thepiece),
1033  tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
1034  }
1035  }
1036  else if ( e.classname == "minigame_board_piece" )
1037  {
1038  if(e.bd_tiletype != BD_TILE_DOZER && !e.bd_hide) // hide boulders
1039  {
1040  tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
1041  tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
1042 
1043  string thepiece = bd_get_tile_pic(e.bd_tiletype);
1044 
1045  if(e.bd_enemy)
1046  thepiece = "bd/boulder_target";
1047 
1048  minigame_drawpic_centered( tile_pos,
1049  minigame_texture(thepiece),
1050  tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
1051  }
1052  }
1053  }
1054 
1055  // draw dozers on top, always
1057  {
1058  if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
1059  {
1060  tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
1061  tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
1062 
1063  int bdir = e.bd_dir;
1064  float theang = 0;
1065 
1066  switch(bdir)
1067  {
1068  case BD_DIR_UP: theang = 0; break;
1069  default:
1070  case BD_DIR_DN: theang = M_PI; break;
1071  case BD_DIR_LF: theang = M_PI * 3 / 2; break;
1072  case BD_DIR_RT: theang = M_PI / 2; break;
1073  }
1074 
1075  drawrotpic(tile_pos, theang, minigame_texture("bd/dozer"),
1076  tile_size, tile_size/2, '1 1 1',
1078  }
1079  }
1080 
1081  if(active_minigame.minigame_flags & BD_TURN_EDIT)
1082  if(bd_valid_tile(bd_curr_pos))
1083  {
1084  entity piece = bd_find_piece(active_minigame, bd_curr_pos, false);
1085  entity targ = bd_find_piece(active_minigame, bd_curr_pos, true);
1086  string thepiece = ((piece || (targ && bd_curr_tile != BD_TILE_BOULDER)) ? "bd/delete" : bd_get_tile_pic(bd_curr_tile));
1087 
1088  tile_pos = minigame_tile_pos(bd_curr_pos,BD_LET_CNT,BD_NUM_CNT);
1089  tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
1090  if(bd_curr_tile == BD_TILE_DOZER)
1091  {
1092  drawrotpic(tile_pos, M_PI, minigame_texture("bd/dozer"),
1093  tile_size, tile_size/2, '1 1 1',
1095  }
1096  else
1097  {
1098  minigame_drawpic_centered( tile_pos,
1099  minigame_texture(thepiece),
1100  tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL );
1101  }
1102  }
1103 
1104  if ( (active_minigame.minigame_flags & BD_TURN_LOSS) || (active_minigame.minigame_flags & BD_TURN_WIN) )
1105  {
1106  vector winfs = hud_fontsize*2;
1107  string victory_text = _("Game over!");
1108 
1109  if(active_minigame.minigame_flags & BD_TURN_WIN)
1110  victory_text = _("Well done! Click 'Next Level' to continue");
1111 
1112  vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
1113  vector win_sz;
1114  win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
1115  victory_text, winfs, 0, DRAWFLAG_NORMAL, 0.5);
1116 
1117  drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'0.3 0.3 1',0.8,DRAWFLAG_ADDITIVE);
1118 
1119  minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
1120  victory_text, winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
1121  }
1122 }
1123 
1124 
1125 // Required function, draw the game status panel
1126 void bd_hud_status(vector pos, vector mySize)
1127 {
1128  HUD_Panel_DrawBg();
1129  vector ts;
1130  ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
1131  hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
1132 
1133  pos_y += ts_y;
1134  mySize_y -= ts_y;
1135 
1136  vector player_fontsize = hud_fontsize * 1.75;
1137  ts_y = ( mySize_y - 2*player_fontsize_y ) / BD_TEAMS;
1138  ts_x = mySize_x;
1139  vector mypos;
1140  vector tile_size = '48 48 0';
1141 
1142  if(minigame_self.team != BD_SPECTATOR_TEAM)
1143  {
1144  mypos = pos;
1145  drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
1146  mypos_y += player_fontsize_y;
1147  drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
1148  }
1149 
1150  entity e;
1152  {
1153  if ( e.classname == "minigame_player" && e.team != BD_SPECTATOR_TEAM )
1154  {
1155  mypos = pos;
1156  minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
1157  entcs_GetName(e.minigame_playerslot-1),
1158  player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1159 
1160  mypos_y += player_fontsize_y;
1161  string thepiece = "bd/dozer";
1162  if(active_minigame.minigame_flags & BD_TURN_EDIT)
1163  thepiece = bd_get_tile_pic(bd_curr_tile);
1164  const float tile_scale = 0.7;
1165  drawpic( mypos + tile_size * 0.5 * (1 - tile_scale),
1166  minigame_texture(thepiece),
1167  tile_size * tile_scale, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
1168 
1169  mypos_x += tile_size_x;
1170 
1171  drawstring(mypos,ftos(e.bd_moves),tile_size,
1172  '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1173  }
1174  }
1175 }
1176 
1177 // Turn a set of flags into a help message
1178 string bd_turn_to_string(int turnflags)
1179 {
1180  if(minigame_self.team == BD_SPECTATOR_TEAM)
1181  return _("You are spectating");
1182 
1183  if ( turnflags & BD_TURN_LOSS )
1184  return _("Better luck next time!");
1185 
1186  if ( turnflags & BD_TURN_WIN )
1187  {
1188  if(random() > 0.5)
1189  return _("Tubular! Press \"Next Level\" to continue!");
1190  else
1191  return _("Wicked! Press \"Next Level\" to continue!");
1192  }
1193 
1194  if( turnflags & BD_TURN_EDIT )
1195  return _("Press the space bar to change your currently selected tile");
1196 
1197  if ( turnflags & BD_TURN_MOVE )
1198  return _("Push the boulders onto the targets");
1199 
1200  return "";
1201 }
1202 
1203 // Make the correct move
1204 void bd_make_move(entity minigame, string dir)
1205 {
1206  if ( minigame.minigame_flags == BD_TURN_MOVE )
1207  {
1208  minigame_cmd("move ", dir);
1209  }
1210 }
1211 
1212 void bd_editor_make_move(entity minigame, string dir)
1213 {
1214  if ( minigame.minigame_flags == BD_TURN_EDIT )
1215  {
1216  minigame_cmd("move ", bd_curr_pos, " ", ftos(bd_curr_tile), " ", dir);
1217  }
1218 }
1219 
1220 void bd_editor_fill(entity minigame)
1221 {
1222  if ( minigame.minigame_flags == BD_TURN_EDIT )
1223  {
1224  minigame_cmd("fill ", bd_curr_pos, " ", ftos(bd_curr_tile));
1225  }
1226 }
1227 
1228 void bd_set_curr_pos(string s)
1229 {
1230  strfree(bd_curr_pos);
1231  if ( s )
1232  s = strzone(s);
1233  bd_curr_pos = s;
1234 }
1235 
1236 bool bd_change_dozer_angle(entity minigame)
1237 {
1238  entity dozer = bd_find_piece(minigame, bd_curr_pos, false);
1239  if(!dozer || dozer.bd_tiletype != BD_TILE_DOZER)
1240  return false;
1241 
1242  switch(dozer.bd_dir)
1243  {
1244  case BD_DIR_UP: dozer.bd_dir = BD_DIR_LF; break; // up -> left
1245  default:
1246  case BD_DIR_DN: dozer.bd_dir = BD_DIR_RT; break; // down -> right
1247  case BD_DIR_LF: dozer.bd_dir = BD_DIR_DN; break; // left -> down
1248  case BD_DIR_RT: dozer.bd_dir = BD_DIR_UP; break; // right -> up
1249  }
1250  string thedir = bd_get_dir_name(dozer.bd_dir);
1251 
1252  bd_editor_make_move(minigame, thedir);
1253  return true;
1254 }
1255 
1256 // Required function, handle client events
1257 int bd_client_event(entity minigame, string event, ...)
1258 {
1259  switch(event)
1260  {
1261  case "activate":
1262  {
1263  strcpy(minigame.message, bd_turn_to_string(minigame.minigame_flags));
1264  bd_set_curr_pos("");
1265  bd_curr_tile = BD_TILE_BRICK1;
1266  return false;
1267  }
1268  case "deactivate":
1269  {
1270  strfree(minigame.message);
1271  return false;
1272  }
1273  case "key_pressed":
1274  case "key_released":
1275  {
1276  bool event_blocked = ((event == "key_released")
1277  || !(minigame.minigame_flags & BD_TURN_MOVE) || (minigame_self.team == BD_SPECTATOR_TEAM));
1278  if (!(minigame.minigame_flags & BD_TURN_WIN) && !(minigame.minigame_flags & BD_TURN_LOSS))
1279  {
1280  switch ( ...(0,int) )
1281  {
1282  case K_RIGHTARROW:
1283  case K_KP_RIGHTARROW:
1284  if (event_blocked)
1285  return true;
1286  bd_make_move(minigame, "r");
1287  return true;
1288  case K_LEFTARROW:
1289  case K_KP_LEFTARROW:
1290  if (event_blocked)
1291  return true;
1292  bd_make_move(minigame, "l");
1293  return true;
1294  case K_UPARROW:
1295  case K_KP_UPARROW:
1296  if (event_blocked)
1297  return true;
1298  bd_make_move(minigame, "u");
1299  return true;
1300  case K_DOWNARROW:
1301  case K_KP_DOWNARROW:
1302  if (event_blocked)
1303  return true;
1304  bd_make_move(minigame, "d");
1305  return true;
1306  }
1307  }
1308 
1309  if(minigame.minigame_flags & BD_TURN_EDIT)
1310  {
1311  switch ( ...(0,int) )
1312  {
1313  case K_RIGHTARROW:
1314  case K_KP_RIGHTARROW:
1315  if (event_blocked)
1316  return true;
1317  if ( ! bd_curr_pos )
1318  bd_set_curr_pos("a3");
1319  else
1320  bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,1,0,BD_NUM_CNT,BD_LET_CNT));
1321  return true;
1322  case K_LEFTARROW:
1323  case K_KP_LEFTARROW:
1324  if (event_blocked)
1325  return true;
1326  if ( ! bd_curr_pos )
1327  bd_set_curr_pos("c3");
1328  else
1329  bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,-1,0,BD_NUM_CNT,BD_LET_CNT));
1330  return true;
1331  case K_UPARROW:
1332  case K_KP_UPARROW:
1333  if (event_blocked)
1334  return true;
1335  if ( ! bd_curr_pos )
1336  bd_set_curr_pos("a1");
1337  else
1338  bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,0,1,BD_NUM_CNT,BD_LET_CNT));
1339  return true;
1340  case K_DOWNARROW:
1341  case K_KP_DOWNARROW:
1342  if (event_blocked)
1343  return true;
1344  if ( ! bd_curr_pos )
1345  bd_set_curr_pos("a3");
1346  else
1347  bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,0,-1,BD_NUM_CNT,BD_LET_CNT));
1348  return true;
1349  case K_ENTER:
1350  case K_KP_ENTER:
1351  if (event_blocked)
1352  return true;
1353  bd_editor_make_move(minigame, "");
1354  return true;
1355  case K_SPACE:
1356  if (event_blocked)
1357  return true;
1358  if(bd_change_dozer_angle(minigame))
1359  return true;
1360  bd_curr_tile += 1;
1361  if(bd_curr_tile > BD_TILE_LAST)
1362  bd_curr_tile = 1;
1363  return true;
1364  }
1365  }
1366 
1367  return false;
1368  }
1369  case "mouse_pressed":
1370  {
1371  if((minigame.minigame_flags & BD_TURN_EDIT) && minigame_self.team != BD_SPECTATOR_TEAM)
1372  {
1373  if(...(0,int) == K_MOUSE1)
1374  {
1375  bd_client_event(minigame, "mouse_moved");
1376  bd_editor_make_move(minigame, "");
1377  return true;
1378  }
1379 
1380  if(...(0,int) == K_MOUSE2)
1381  {
1382  bd_client_event(minigame, "mouse_moved");
1383  bd_editor_fill(minigame);
1384  return true;
1385  }
1386  }
1387 
1388  return false;
1389  }
1390  case "mouse_moved":
1391  {
1392  if((minigame.minigame_flags & BD_TURN_EDIT) && minigame_self.team != BD_SPECTATOR_TEAM)
1393  {
1394  vector mouse_pos = minigame_hud_normalize(mousepos,bd_boardpos,bd_boardsize);
1395  bd_set_curr_pos(minigame_tile_name(mouse_pos,BD_LET_CNT,BD_NUM_CNT));
1396  if ( ! bd_valid_tile(bd_curr_pos) )
1397  bd_set_curr_pos("");
1398  }
1399  return true;
1400  }
1401  case "network_receive":
1402  {
1403  entity sent = ...(0,entity);
1404  int sf = ...(1,int);
1405  if ( sent.classname == "minigame" )
1406  {
1407  if ( sf & MINIG_SF_UPDATE )
1408  {
1409  strcpy(sent.message, bd_turn_to_string(sent.minigame_flags));
1410  //if ( sent.minigame_flags & minigame_self.team )
1411  //minigame_prompt();
1412  }
1413  }
1414  else if(sent.classname == "minigame_board_piece")
1415  {
1416  if(sf & MINIG_SF_UPDATE)
1417  {
1418  int letter = ReadByte();
1419  int number = ReadByte();
1420  strcpy(sent.netname, minigame_tile_buildname(letter, number));
1421 
1422  sent.bd_tiletype = ReadByte();
1423 
1424  sent.bd_dir = ReadByte();
1425  }
1426  }
1427  else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
1428  sent.bd_moves = ReadShort(); // make this a byte when possible
1429 
1430  return false;
1431  }
1432  case "menu_show":
1433  {
1434  HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Level"),"next");
1435  HUD_MinigameMenu_CustomEntry(...(0,entity),_("Restart"),"restart");
1436  HUD_MinigameMenu_CustomEntry(...(0,entity),_("Editor"),"edit");
1437  HUD_MinigameMenu_CustomEntry(...(0,entity),_("Save"),"save");
1438  return false;
1439  }
1440  case "menu_click":
1441  {
1442  if(...(0,string) == "next")
1443  minigame_cmd("next");
1444  if(...(0,string) == "restart")
1445  minigame_cmd("restart");
1446  if(...(0,string) == "edit")
1447  minigame_cmd("edit");
1448  if(...(0,string) == "save")
1449  minigame_cmd("save");
1450  return false;
1451  }
1452  }
1453 
1454  return false;
1455 }
1456 
1457 #endif
float K_KP_RIGHTARROW
Definition: keycodes.qc:60
int bd_fix_dir(vector dir)
Definition: bd.qc:681
float K_UPARROW
Definition: keycodes.qc:15
int minigame_tile_number(string id)
Definition: minigames.qc:21
void bd_do_fill(entity minigame, entity player, string dir, string thetile)
Definition: bd.qc:506
void minigame_server_sendflags(entity ent, int mgflags)
Definition: minigames.qc:78
void HUD_MinigameMenu_CustomEntry(entity parent, string menumessage, string event_arg)
const int BD_DIR_DN
Definition: bd.qc:59
const int MINIG_SF_ALL
Definition: minigames.qh:112
float panel_fg_alpha
Definition: hud.qh:166
string string_null
Definition: nil.qh:9
REGISTER_MINIGAME(bd, _("Bulldozer"))
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
#define int
Definition: _all.inc:20
#define FOREACH_MINIGAME_ENTITY(entityvar)
Definition: cl_minigames.qh:98
int bd_dir
Definition: bd.qc:26
const vector eY
Definition: vector.qh:45
void bd_editor_place(entity minigame, entity player, string pos, int thetile, string thedir)
Definition: bd.qc:382
void bd_next_match(entity minigame, entity player, string next)
Definition: bd.qc:613
const int BD_SF_UPDATE_SINGLE
Definition: bd.qc:14
void bd_reset_moves(entity minigame)
Definition: bd.qc:547
entity bd_find_piece(entity minig, string tile, bool check_target)
Definition: bd.qc:68
vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:23
int int number
Definition: impulse.qc:89
void minigame_read_owner(entity this)
float K_DOWNARROW
Definition: keycodes.qc:16
int bd_moves
Definition: bd.qc:30
const int BD_TILE_BRICK1
Definition: bd.qc:48
#define minigame_hud_fitsqare(pos, mySize)
Definition: cl_minigames.qh:7
entity msle_spawn(entity minigame_session, entity e)
Definition: minigames.qc:87
float K_KP_DOWNARROW
Definition: keycodes.qc:53
int bd_tiletypes[BD_NUM_CNT]
Definition: bd.qc:42
entity() spawn
const int BD_TILE_TARGET
Definition: bd.qc:46
void bd_set_next_match(entity minigame, string next)
Definition: bd.qc:608
bool bd_move_dozer(entity minigame, entity dozer)
Definition: bd.qc:252
const int BD_LET_CNT
Definition: bd.qc:18
void bd_fill_recurse(entity minigame, entity player, int thetype, int letter, int number)
Definition: bd.qc:463
const float FILE_READ
Definition: csprogsdefs.qc:231
#define NET_HANDLE(id, param)
Definition: net.qh:12
vector minigame_hud_normalize(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:31
void bd_controller_update(entity controller, int number)
Definition: bd.qc:96
const int BD_TURN_TYPE
Definition: bd.qc:10
entity to
Definition: self.qh:96
entity bd_find_controller(entity minig, int letter)
Definition: bd.qc:77
const int BD_TILE_BRICK6
Definition: bd.qc:53
string classname
Definition: csprogsdefs.qc:107
const int BD_TILE_BOULDER
Definition: bd.qc:47
float K_SPACE
Definition: keycodes.qc:10
entity bd_find_dozer(entity minig)
Definition: bd.qc:104
float K_RIGHTARROW
Definition: keycodes.qc:18
const int MINIG_SF_UPDATE
Definition: minigames.qh:109
int bd_dir_fromname(string bdir)
Definition: bd.qc:221
vector bd_get_dir(int bdir)
Definition: bd.qc:197
entity owner
Definition: main.qh:73
string bd_nextlevel
Definition: bd.qc:35
const int BD_TILE_BRICK4
Definition: bd.qc:51
const int BD_DIR_UP
Definition: bd.qc:58
float K_KP_ENTER
Definition: keycodes.qc:74
#define strcpy(this, s)
Definition: string.qh:49
#define stoi(s)
Definition: int.qh:4
const int BD_TILE_BRICK8
Definition: bd.qc:55
string minigame_tile_name(vector pos, int rows, int columns)
Definition: minigames.qc:54
const float DRAWFLAG_ADDITIVE
Definition: csprogsdefs.qc:318
void bd_load_level(entity minigame)
Definition: bd.qc:782
const int MINIG_SF_CUSTOM
Definition: minigames.qh:110
#define HUD_Panel_DrawBg()
Definition: hud.qh:54
void minigame_drawpic_centered(vector pos, string texture, vector sz, vector color, float thealpha, int drawflags)
string minigame_texture(string name)
Definition: cl_minigames.qc:49
void bd_set_nextlevel(entity minigame, string s)
Definition: bd.qc:674
string bd_save_piece(entity minigame, entity e)
Definition: bd.qc:663
const int BD_TILE_DOZER
Definition: bd.qc:45
entity active_minigame
Definition: cl_minigames.qh:85
const int BD_TILE_BRICK2
Definition: bd.qc:49
vector minigame_tile_pos(string id, int rows, int columns)
Definition: minigames.qc:27
const int BD_DIR_LF
Definition: bd.qc:60
const int BD_TILE_SIZE
Definition: bd.qc:21
vector minigame_drawcolorcodedstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags, float align)
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
const int BD_SPECTATOR_TEAM
Definition: bd.qc:24
const int BD_SF_UPDATE_ALL
Definition: bd.qc:15
const int BD_TURN_EDIT
Definition: bd.qc:9
float K_MOUSE2
Definition: keycodes.qc:130
#define NULL
Definition: post.qh:17
#define LOG_INFO(...)
Definition: log.qh:70
const int BD_TURN_WIN
Definition: bd.qc:7
const float DRAWFLAG_NORMAL
Definition: csprogsdefs.qc:317
int minigame_tile_letter(string id)
Definition: minigames.qc:12
string bd_levelname
Definition: bd.qc:34
const int BD_TILE_BRICK5
Definition: bd.qc:52
const int BD_DIR_RT
Definition: bd.qc:61
#define minigame_cmd(...)
Definition: cl_minigames.qh:90
const int BD_TURN_LOSS
Definition: bd.qc:8
string minigame_tile_buildname(int letter, int number)
Definition: minigames.qc:34
void bd_check_winner(entity minig)
Definition: bd.qc:178
vector(float skel, float bonenum) _skel_get_boneabs_hidden
#define tokenize_console
Definition: dpextensions.qh:24
next
Definition: all.qh:88
const float M_PI
Definition: csprogsdefs.qc:269
bool bd_canfill(int ttype)
Definition: bd.qc:235
void bd_do_next_match(entity minigame, entity player)
Definition: bd.qc:593
const float FILE_WRITE
Definition: csprogsdefs.qc:233
string minigame_relative_tile(string start_id, int dx, int dy, int rows, int columns)
Definition: minigames.qc:40
void bd_move(entity minigame, entity player, string dir)
Definition: bd.qc:339
float K_MOUSE1
Definition: keycodes.qc:129
string bd_save_controller_piece(entity minigame, entity e, int number)
Definition: bd.qc:650
bool bd_valid_tile(string tile)
Definition: bd.qc:87
int minigame_count_players(entity minigame)
Definition: minigames.qc:121
int bd_tilelet
Definition: bd.qc:32
const vector eX
Definition: vector.qh:44
vector minigame_hud_denormalize(vector v, vector pos, vector mySize)
Definition: cl_minigames.qc:16
int bd_tiletype
Definition: bd.qc:44
vector mousepos
Definition: hud.qh:102
float K_LEFTARROW
Definition: keycodes.qc:17
string bd_get_dir_name(int bdir)
Definition: bd.qc:209
const int BD_TEAMS
Definition: bd.qc:23
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text, vector fontsize, float theAlpha, int drawflags)
vector hud_fontsize
Definition: main.qh:63
const int BD_TILE_LAST
Definition: bd.qc:56
#define strfree(this)
Definition: string.qh:56
float K_ENTER
Definition: keycodes.qc:8
void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture)
Definition: cl_minigames.qc:4
void bd_activate_editor(entity minigame, entity player)
Definition: bd.qc:632
const int BD_TURN_MOVE
Definition: bd.qc:6
const int BD_TILE_BRICK3
Definition: bd.qc:50
void bd_load_piece(entity minigame, string s)
Definition: bd.qc:691
void bd_close_editor(entity minigame, entity player)
Definition: bd.qc:817
const int BD_NUM_CNT
Definition: bd.qc:19
void bd_restart_match(entity minigame, entity player)
Definition: bd.qc:622
entity minigame_self
Definition: cl_minigames.qh:87
float K_KP_LEFTARROW
Definition: keycodes.qc:57
ERASEABLE bool fexists(string f)
Definition: file.qh:4
float K_KP_UPARROW
Definition: keycodes.qc:64
void bd_do_move(entity minigame, entity player, string dir, string thetile, string thedir)
Definition: bd.qc:454
int dir
Definition: impulse.qc:89
const int BD_TILE_BRICK7
Definition: bd.qc:54
int bd_dirs[BD_NUM_CNT]
Definition: bd.qc:28
void bd_unfill_recurse(entity minigame, entity player, int thetype, int letter, int number)
Definition: bd.qc:479
bool bd_save_level(entity minigame)
Definition: bd.qc:724
vector minigame_drawstring_wrapped(float maxwidth, vector pos, string text, vector fontsize, vector color, float theAlpha, int drawflags, float align)
void bd_setup_pieces(entity minigame)
Definition: bd.qc:564
const int BD_SF_PLAYERMOVES
Definition: bd.qc:13