]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/bd.qc
Refactor spectator list code in the Connect Four minigame to better handle shortened...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / bd.qc
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
13 const int BD_SF_PLAYERMOVES = MINIG_SF_CUSTOM;
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
32 .int bd_tilelet;
33
34 .string bd_levelname;
35 .string bd_nextlevel;
36
37 #ifdef SVQC
38 .bool bd_canedit;
39 .int bd_forceupdate;
40 #endif
41
42 .int bd_tiletypes[BD_NUM_CNT];
43
44 .int bd_tiletype;
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
101         minigame_server_sendflags(controller,BD_SF_UPDATE_SINGLE);
102 }
103
104 entity bd_find_dozer(entity minig)
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
178 void bd_check_winner(entity minig)
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;
193                 minigame_server_sendflags(minig,MINIG_SF_UPDATE);
194         }
195 }
196
197 vector bd_get_dir(int bdir)
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; /// down
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);
314                         minigame_server_sendflags(hit,MINIG_SF_UPDATE);
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
375                         minigame_server_sendflags(player,BD_SF_PLAYERMOVES);
376                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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);
426                                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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;
446                                 minigame_server_sendflags(piece,MINIG_SF_UPDATE);
447                         }
448
449                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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;
559                         minigame_server_sendflags(e,BD_SF_PLAYERMOVES);
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;
596         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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;
625         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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;
643         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
644
645         bd_reset_moves(minigame);
646
647         bd_setup_pieces(minigame);
648 }
649
650 string bd_save_controller_piece(entity minigame, entity e, int number)
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
681 int bd_fix_dir(vector dir)
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;
720                 minigame_server_sendflags(e,MINIG_SF_ALL);
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;
837                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
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;
999         FOREACH_MINIGAME_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         }
1015         FOREACH_MINIGAME_ENTITY(e)
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
1056         FOREACH_MINIGAME_ENTITY(e)
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',
1077                                                 panel_fg_alpha, DRAWFLAG_NORMAL );
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',
1094                                                 panel_fg_alpha/2, DRAWFLAG_NORMAL );
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;
1151         FOREACH_MINIGAME_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