]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/minigame/bd.qc
Add an unfill mode to the fill tool
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / minigame / bd.qc
1 REGISTER_MINIGAME(bd, "Bulldozer");
2
3 const int BD_TURN_MOVE  = 0x0100; // player must move the bulldozer
4 const int BD_TURN_WIN   = 0x0200; // victory
5 const int BD_TURN_LOSS  = 0x0400; // they did it?!
6 const int BD_TURN_EDIT  = 0x0800; // editing mode
7 const int BD_TURN_TYPE  = 0x0f00; // turn type mask
8
9 const int BD_SF_PLAYERMOVES = MINIG_SF_CUSTOM;
10
11 // 240 tiles...
12 const int BD_LET_CNT = 20;
13 const int BD_NUM_CNT = 20;
14
15 const int BD_TILE_SIZE = 20;
16
17 const int BD_TEAMS = 1;
18
19 .vector bd_dir;
20
21 .int bd_moves;
22
23 .string bd_levelname;
24 .string bd_nextlevel;
25
26 #ifdef SVQC
27 .bool bd_canedit;
28 #endif
29
30 .int bd_tiletype;
31 const int BD_TILE_DOZER = 1;
32 const int BD_TILE_TARGET = 2;
33 const int BD_TILE_BOULDER = 3;
34 const int BD_TILE_BRICK1 = 4;
35 const int BD_TILE_BRICK2 = 5;
36 const int BD_TILE_BRICK3 = 6;
37 const int BD_TILE_BRICK4 = 7;
38 const int BD_TILE_BRICK5 = 8;
39 const int BD_TILE_BRICK6 = 9;
40 const int BD_TILE_LAST = 9;
41
42 string autocvar_sv_minigames_bulldozer_startlevel = "level1";
43
44 // find same game piece given its tile name
45 entity bd_find_piece(entity minig, string tile, bool check_target)
46 {
47         entity e = world;
48         while ( ( e = findentity(e,owner,minig) ) )
49                 if ( e.classname == "minigame_board_piece" && e.netname == tile && ((check_target) ? e.bd_tiletype == BD_TILE_TARGET : e.bd_tiletype != BD_TILE_TARGET) )
50                         return e;
51         return world;
52 }
53
54 // check if the tile name is valid (15x15 grid)
55 bool bd_valid_tile(string tile)
56 {
57         if ( !tile )
58                 return false;
59         int number = minigame_tile_number(tile);
60         int letter = minigame_tile_letter(tile);
61         return 0 <= number && number < BD_NUM_CNT && 0 <= letter && letter < BD_LET_CNT;
62 }
63
64 entity bd_find_dozer(entity minig)
65 {
66         entity e = world;
67         while ( ( e = findentity(e,owner,minig) ) )
68                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
69                         return e;
70         return world;
71 }
72
73 void bd_check_winner(entity minig)
74 {
75         int total = 0, valid = 0;
76         entity e = world;
77         while ( ( e = findentity(e,owner,minig) ) )
78                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_TARGET )
79                 {
80                         ++total;
81                         if(bd_find_piece(minig, e.netname, false).bd_tiletype == BD_TILE_BOULDER)
82                                 ++valid;
83                 }
84
85         if(valid >= total)
86         {
87                 minig.minigame_flags = BD_TURN_WIN;
88                 minigame_server_sendflags(minig,MINIG_SF_UPDATE);
89         }
90 }
91
92 bool bd_canfill(int ttype)
93 {
94         switch(ttype)
95         {
96                 case BD_TILE_BRICK6:
97                 case BD_TILE_BRICK5:
98                 case BD_TILE_BRICK4:
99                 case BD_TILE_BRICK3:
100                 case BD_TILE_BRICK2:
101                 case BD_TILE_BRICK1: return true;
102         }
103
104         return false;
105 }
106
107 bool bd_move_dozer(entity minigame, entity dozer)
108 {
109         if(!dozer.bd_dir_x && !dozer.bd_dir_y)
110                 return false; // nope!
111
112         int myx = minigame_tile_letter(dozer.netname);
113         int myy = minigame_tile_number(dozer.netname);
114
115         myx += dozer.bd_dir_x;
116         myy += dozer.bd_dir_y;
117
118         string newpos = minigame_tile_buildname(myx, myy);
119         entity hit = bd_find_piece(minigame, newpos, false);
120
121         if(!bd_valid_tile(newpos))
122                 return false;
123
124         if(hit)
125         switch(hit.bd_tiletype)
126         {
127                 case BD_TILE_DOZER: // wtf, but let's do this incase
128                 case BD_TILE_BRICK6:
129                 case BD_TILE_BRICK5:
130                 case BD_TILE_BRICK4:
131                 case BD_TILE_BRICK3:
132                 case BD_TILE_BRICK2:
133                 case BD_TILE_BRICK1: return false;
134                 case BD_TILE_BOULDER:
135                 {
136                         string testpos;
137                         int tx = minigame_tile_letter(hit.netname);
138                         int ty = minigame_tile_number(hit.netname);
139
140                         tx += dozer.bd_dir_x;
141                         ty += dozer.bd_dir_y;
142
143                         testpos = minigame_tile_buildname(tx, ty);
144                         entity testhit = bd_find_piece(minigame, testpos, false);
145
146                         if(!bd_valid_tile(testpos) || testhit)
147                                 return false;
148
149                         if(hit.netname) { strunzone(hit.netname); }
150                         hit.netname = strzone(testpos);
151                         minigame_server_sendflags(hit,MINIG_SF_UPDATE);
152                         break;
153                 }
154         }
155
156         if(dozer.netname) { strunzone(dozer.netname); }
157         dozer.netname = strzone(newpos);
158
159         return true;
160 }
161
162 // make a move
163 void bd_move(entity minigame, entity player, string dir)
164 {
165         if ( minigame.minigame_flags & BD_TURN_MOVE )
166         if ( dir )
167         {
168                 //if ( bd_valid_tile(pos) )
169                 //if ( bd_find_piece(minigame, pos, false) )
170                 {
171                         entity dozer = bd_find_dozer(minigame);
172                         if(!dozer)
173                         {
174                                 LOG_INFO("Dozer wasn't found!\n");
175                                 return; // should not happen... TODO: end match?
176                         }
177
178                         int dxs = 0, dys = 0;
179                         string thedir = strtolower(dir);
180                         if(thedir == "up" || thedir == "u") { dxs = 0; dys = 1; }
181                         if(thedir == "down" || thedir == "dn" || thedir == "d") { dxs = 0; dys = -1; }
182                         if(thedir == "left" || thedir == "lt" || thedir == "l") { dxs = -1; dys = 0; }
183                         if(thedir == "right" || thedir == "rt" || thedir == "r") { dxs = 1; dys = 0; }
184
185                         int dx = bound(-1, dxs, 1);
186                         int dy = bound(-1, dys, 1);
187
188                         dozer.bd_dir_x = dx;
189                         dozer.bd_dir_y = dy;
190                         dozer.bd_dir_z = 0;
191
192                         if(bd_move_dozer(minigame, dozer))
193                                 player.bd_moves++;
194
195                         bd_check_winner(minigame);
196
197                         minigame_server_sendflags(dozer,MINIG_SF_UPDATE); // update anyway
198                         minigame_server_sendflags(player,BD_SF_PLAYERMOVES);
199                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
200                 }
201         }
202 }
203
204 // editor
205 void bd_editor_place(entity minigame, entity player, string pos, int thetile, string thedir)
206 {
207         if ( minigame.minigame_flags & BD_TURN_EDIT )
208         if ( pos && thetile )
209         {
210                 if ( bd_valid_tile(pos) )
211                 {
212                         entity found_piece = bd_find_piece(minigame, pos, false);
213                         entity targ = bd_find_piece(minigame, pos, true);
214
215                         if(found_piece.bd_tiletype == BD_TILE_DOZER && thedir != "")
216                         {
217                                 int dxs = 0, dys = 0;
218                                 string newdir = strtolower(thedir);
219                                 if(newdir == "up" || newdir == "u") { dxs = 0; dys = 1; }
220                                 if(newdir == "down" || newdir == "dn" || newdir == "d") { dxs = 0; dys = -1; }
221                                 if(newdir == "left" || newdir == "lt" || newdir == "l") { dxs = -1; dys = 0; }
222                                 if(newdir == "right" || newdir == "rt" || newdir == "r") { dxs = 1; dys = 0; }
223
224                                 int dx = bound(-1, dxs, 1);
225                                 int dy = bound(-1, dys, 1);
226
227                                 found_piece.bd_dir_x = dx;
228                                 found_piece.bd_dir_y = dy;
229                                 found_piece.bd_dir_z = 0;
230                                 minigame_server_sendflags(found_piece,MINIG_SF_UPDATE); // update anyway
231                                 return;
232                         }
233
234                         entity dozer = bd_find_dozer(minigame);
235                         if(dozer && thetile == BD_TILE_DOZER && pos != dozer.netname)
236                                 return; // nice try
237
238                         if(found_piece || (targ && thetile != BD_TILE_BOULDER))
239                         {
240                                 entity piece = bd_find_piece(minigame, pos, false);
241                                 if(!piece) piece = bd_find_piece(minigame, pos, true);
242                                 if(!piece)
243                                         return; // how?!
244
245                                 if(piece.netname) { strunzone(piece.netname); }
246                                 remove(piece);
247                                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
248                                 return;
249                         }
250
251                         entity piece = msle_spawn(minigame,"minigame_board_piece");
252                         piece.team = 1;
253                         piece.netname = strzone(pos);
254                         piece.bd_tiletype = thetile;
255                         piece.bd_dir = '0 -1 0';
256                         minigame_server_sendflags(piece,MINIG_SF_UPDATE);
257
258                         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
259                 }
260         }
261 }
262
263 void bd_do_move(entity minigame, entity player, string dir, string thetile, string thedir)
264 {
265         if(minigame.minigame_flags & BD_TURN_MOVE)
266                 bd_move(minigame, player, dir);
267
268         if(minigame.minigame_flags & BD_TURN_EDIT)
269                 bd_editor_place(minigame, player, dir, stof(thetile), thedir);
270 }
271
272 void bd_fill_recurse(entity minigame, entity player, int thetype, int letter, int number)
273 {
274         string pos = minigame_tile_buildname(letter,number);
275         if(!bd_valid_tile(pos))
276                 return;
277         if(bd_find_piece(minigame, pos, false) || bd_find_piece(minigame, pos, true))
278                 return;
279
280         bd_editor_place(minigame, player, pos, thetype, "");
281
282         bd_fill_recurse(minigame, player, thetype, letter - 1, number);
283         bd_fill_recurse(minigame, player, thetype, letter + 1, number);
284         bd_fill_recurse(minigame, player, thetype, letter, number - 1);
285         bd_fill_recurse(minigame, player, thetype, letter, number + 1);
286 }
287
288 void bd_unfill_recurse(entity minigame, entity player, int thetype, int letter, int number)
289 {
290         string pos = minigame_tile_buildname(letter,number);
291         if(!bd_valid_tile(pos))
292                 return;
293
294         entity targ = bd_find_piece(minigame, pos, true);
295         entity piece = bd_find_piece(minigame, pos, false);
296
297         if(targ && thetype == targ.bd_tiletype)
298         {
299                 if(targ.netname) { strunzone(targ.netname); }
300                 remove(targ);
301         }
302         else if(piece && thetype == piece.bd_tiletype)
303         {
304                 if(piece.netname) { strunzone(piece.netname); }
305                 remove(piece);
306         }
307         else return;
308
309         bd_unfill_recurse(minigame, player, thetype, letter - 1, number);
310         bd_unfill_recurse(minigame, player, thetype, letter + 1, number);
311         bd_unfill_recurse(minigame, player, thetype, letter, number - 1);
312         bd_unfill_recurse(minigame, player, thetype, letter, number + 1);
313 }
314
315 void bd_do_fill(entity minigame, entity player, string dir, string thetile)
316 {
317         if(minigame.minigame_flags & BD_TURN_EDIT)
318         {
319                 int thetype = stof(thetile);
320
321                 entity targ = bd_find_piece(minigame, dir, true);
322                 entity piece = bd_find_piece(minigame, dir, false);
323
324                 if(!bd_canfill(thetype) || (piece || targ))
325                 {
326                         int killtype = 0;
327
328                         if(targ) { killtype = targ.bd_tiletype; }
329                         if(piece) { killtype = piece.bd_tiletype; }
330
331                         if(killtype)
332                         {
333                                 int letter = minigame_tile_letter(dir);
334                                 int number = minigame_tile_number(dir);
335                                 bd_unfill_recurse(minigame, player, killtype, letter, number);
336                         }
337
338                         return;
339                 }
340
341                 int letter = minigame_tile_letter(dir);
342                 int number = minigame_tile_number(dir);
343
344                 bd_fill_recurse(minigame, player, thetype, letter, number);
345         }
346 }
347
348 void bd_reset_moves(entity minigame)
349 {
350         entity e;
351 #ifdef SVQC
352         for(e = minigame.minigame_players; e; e = e.list_next)
353 #elif defined(CSQC)
354         e = world;
355         while( (e = findentity(e,owner,minigame)) )
356                 if ( e.classname == "minigame_player" )
357 #endif
358                 {
359                         e.bd_moves = 0;
360                         minigame_server_sendflags(e,BD_SF_PLAYERMOVES);
361                 }
362 }
363
364 void bd_load_level(entity minigame);
365 void bd_setup_pieces(entity minigame)
366 {
367         entity e = world;
368         while( (e = findentity(e, owner, minigame)) )
369                 if(e.classname == "minigame_board_piece")
370                 {
371                         if(e.netname) { strunzone(e.netname); }
372                         remove(e);
373                 }
374
375         bd_load_level(minigame);
376 }
377
378 void bd_do_next_match(entity minigame, entity player)
379 {
380         minigame.minigame_flags = BD_TURN_MOVE;
381         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
382
383         if(minigame.bd_nextlevel && minigame.bd_nextlevel != "")
384         {
385                 if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); }
386                 minigame.bd_levelname = strzone(minigame.bd_nextlevel);
387         }
388
389         bd_setup_pieces(minigame);
390
391         bd_reset_moves(minigame);
392 }
393
394 void bd_set_next_match(entity minigame, string next)
395 {
396         if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); }
397         minigame.bd_nextlevel = strzone(next);
398 }
399
400 void bd_next_match(entity minigame, entity player, string next)
401 {
402         if(minigame.minigame_flags & BD_TURN_WIN)
403                 bd_do_next_match(minigame, player);
404         if(minigame.minigame_flags & BD_TURN_EDIT)
405                 bd_set_next_match(minigame, next);
406 }
407
408 // request a new match
409 void bd_restart_match(entity minigame, entity player)
410 {
411         minigame.minigame_flags = BD_TURN_MOVE;
412         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
413
414         bd_setup_pieces(minigame);
415
416         bd_reset_moves(minigame);
417 }
418
419 void bd_activate_editor(entity minigame, entity player)
420 {
421 #ifdef SVQC
422         if(!player.minigame_players.bd_canedit)
423         {
424                 sprint(player.minigame_players, "You're not allowed to edit levels, sorry!\n");
425                 return;
426         }
427 #endif
428
429         minigame.minigame_flags = BD_TURN_EDIT;
430         minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
431
432         bd_reset_moves(minigame);
433
434         bd_setup_pieces(minigame);
435 }
436
437 string bd_save_piece(entity minigame, entity e)
438 {
439         string bd_string = "";
440
441         bd_string = strcat(bd_string, "\"", e.netname, "\" ");
442         bd_string = strcat(bd_string, ftos(e.bd_tiletype), " ");
443         bd_string = strcat(bd_string, sprintf("\"%.9v\"", e.bd_dir), " ");
444         bd_string = strcat(bd_string, "; ");
445
446         return bd_string;
447 }
448
449 void bd_set_nextlevel(entity minigame, string s)
450 {
451         tokenize_console(s);
452
453         if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); }
454         minigame.bd_nextlevel = strzone(argv(2));
455 }
456
457 entity bd_load_piece(entity minigame, string s)
458 {
459         // separate pieces between the ; symbols
460         tokenizebyseparator(s, "; ");
461         string bd_string = argv(0);
462
463         tokenize_console(bd_string);
464
465         entity e = msle_spawn(minigame,"minigame_board_piece");
466         e.team = 1;
467         e.bd_dir = '0 -1 0';
468
469         int argv_num = 0;
470         e.netname = strzone(argv(argv_num)); ++argv_num;
471         e.bd_tiletype = stof(argv(argv_num)); ++argv_num;
472         e.bd_dir = stov(argv(argv_num)); ++argv_num;
473
474         minigame_server_sendflags(e,MINIG_SF_ALL);
475
476         return e;
477 }
478
479 bool bd_save_level(entity minigame)
480 {
481         if(minigame.bd_levelname && minigame.bd_levelname != "")
482         {
483                 int target_count = 0, boulder_count = 0;
484                 entity piece = world;
485                 while((piece = findentity(piece,owner,minigame)))
486                 if(piece.classname == "minigame_board_piece")
487                         if(piece.bd_tiletype == BD_TILE_BOULDER)
488                                 ++boulder_count;
489                         else if(piece.bd_tiletype == BD_TILE_TARGET)
490                                 ++target_count;
491
492                 if(boulder_count != target_count)
493                 {
494                         LOG_INFO("Not enough targets or boulders, fix your level!\n");
495                         return false;
496                 }
497
498                 // saves all objects to the database file
499                 string file_name;
500                 float file_get;
501
502                 file_name = strcat("minigames/bulldozer/storage_", minigame.bd_levelname, ".txt");
503                 file_get = fopen(file_name, FILE_WRITE);
504                 fputs(file_get, strcat("// bulldozer storage \"", minigame.bd_levelname, "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"), "\n"));
505
506                 if(minigame.bd_nextlevel && minigame.bd_nextlevel != "" && fexists(strcat("minigames/bulldozer/storage_", minigame.bd_nextlevel, ".txt")))
507                         fputs(file_get, strcat("nextlevel = \"", minigame.bd_nextlevel, "\"\n"));
508
509                 entity e = world;
510                 while ( ( e = findentity(e,owner,minigame) ) )
511                 if ( e.classname == "minigame_board_piece" )
512                 {
513                         // use a line of text for each object, listing all properties
514                         fputs(file_get, strcat(bd_save_piece(minigame, e), "\n"));
515                 }
516                 fclose(file_get);
517
518                 return true;
519         }
520
521         return false;
522 }
523
524 void bd_load_level(entity minigame)
525 {
526         // loads all items from the database file
527         string file_read, file_name;
528         float file_get;
529
530         file_name = strcat("minigames/bulldozer/storage_", minigame.bd_levelname, ".txt");
531         file_get = fopen(file_name, FILE_READ);
532         if(file_get < 0)
533         {
534                 LOG_INFO("^3BULLDOZER: ^7could not find storage file ^3", file_name, "^7, no items were loaded\n");
535         }
536         else
537         {
538                 for(;;)
539                 {
540                         file_read = fgets(file_get);
541                         if(file_read == "")
542                                 break;
543                         if(substring(file_read, 0, 2) == "//")
544                                 continue;
545                         if(substring(file_read, 0, 1) == "#")
546                                 continue;
547                         if(substring(file_read, 0, 9) == "nextlevel")
548                         {
549                                 bd_set_nextlevel(minigame, file_read);
550                                 continue;
551                         }
552
553                         entity e;
554                         e = bd_load_piece(minigame, file_read);
555                 }
556         }
557         fclose(file_get);
558 }
559
560 void bd_close_editor(entity minigame)
561 {
562         entity dozer = bd_find_dozer(minigame);
563         if(!dozer)
564         {
565                 LOG_INFO("You need to place a bulldozer on the level to save it!\n");
566                 return;
567         }
568
569         if(bd_save_level(minigame))
570         {
571                 minigame.minigame_flags = BD_TURN_MOVE;
572                 minigame_server_sendflags(minigame,MINIG_SF_UPDATE);
573         }
574         else
575         {
576                 LOG_INFO("You need to set the level name!\n");
577                 return;
578         }
579 }
580
581 #ifdef SVQC
582
583 // required function, handle server side events
584 int bd_server_event(entity minigame, string event, ...)
585 {
586         switch(event)
587         {
588                 case "start":
589                 {
590                         if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); }
591                         minigame.bd_levelname = strzone(autocvar_sv_minigames_bulldozer_startlevel);
592                         bd_setup_pieces(minigame);
593                         minigame.minigame_flags = BD_TURN_MOVE;
594                         
595                         return true;
596                 }
597                 case "end":
598                 {
599                         entity e = world;
600                         while( (e = findentity(e, owner, minigame)) )
601                         if(e.classname == "minigame_board_piece")
602                         {
603                                 if(e.netname) { strunzone(e.netname); }
604                                 remove(e);
605                         }
606
607                         if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); }
608                         if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); }
609                         return false;
610                 }
611                 case "join":
612                 {
613                         int pl_num = minigame_count_players(minigame);
614
615                         if(pl_num >= BD_TEAMS) { return false; }
616
617                         return 1;
618                 }
619                 case "cmd":
620                 {
621                         switch(argv(0))
622                         {
623                                 case "move":
624                                         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)); 
625                                         return true;
626                                 case "next":
627                                         bd_next_match(minigame,...(0,entity), ((...(1,int) >= 2 ? argv(1) : string_null)));
628                                         return true;
629                                 case "restart":
630                                         bd_restart_match(minigame,...(0,entity));
631                                         return true;
632                                 case "edit":
633                                         bd_activate_editor(minigame,...(0,entity));
634                                         return true;
635                                 case "save":
636                                         bd_close_editor(minigame);
637                                         return true;
638                                 case "fill":
639                                         bd_do_fill(minigame, ...(0,entity), ((...(1,int)) >= 2 ? argv(1) : string_null), ((...(1,int)) >= 3 ? argv(2) : string_null)); 
640                                         return true;
641                         }
642
643                         return false;
644                 }
645                 case "network_send":
646                 {
647                         entity sent = ...(0,entity);
648                         int sf = ...(1,int);
649                         if ( sent.classname == "minigame_board_piece" && (sf & MINIG_SF_UPDATE) )
650                         {
651                                 int letter = minigame_tile_letter(sent.netname);
652                                 int number = minigame_tile_number(sent.netname);
653
654                                 WriteByte(MSG_ENTITY,letter);
655                                 WriteByte(MSG_ENTITY,number);
656
657                                 WriteByte(MSG_ENTITY,sent.bd_tiletype);
658
659                                 int dx = sent.bd_dir_x;
660                                 int dy = sent.bd_dir_y;
661                                 if(dx == -1) dx = 2;
662                                 if(dy == -1) dy = 2;
663                                 WriteByte(MSG_ENTITY,dx);
664                                 WriteByte(MSG_ENTITY,dy);
665                         }
666                         else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
667                                 WriteShort(MSG_ENTITY,sent.bd_moves);
668                         return false;
669                 }
670         }
671         
672         return false;
673 }
674
675
676 #elif defined(CSQC)
677
678 int bd_curr_tile;
679 string bd_curr_pos;
680
681 vector bd_boardpos; // HUD board position
682 vector bd_boardsize;// HUD board size
683
684 string bd_get_tile_pic(int tileid)
685 {
686         switch(tileid)
687         {
688                 case BD_TILE_BOULDER: return "bd/boulder";
689                 case BD_TILE_BRICK1: return "bd/brick1";
690                 case BD_TILE_BRICK2: return "bd/brick2";
691                 case BD_TILE_BRICK3: return "bd/brick3";
692                 case BD_TILE_BRICK4: return "bd/brick4";
693                 case BD_TILE_BRICK5: return "bd/brick5";
694                 case BD_TILE_BRICK6: return "bd/brick6";
695                 case BD_TILE_TARGET: return "bd/target";
696                 case BD_TILE_DOZER: return "bd/dozer";
697         }
698
699         return string_null;
700 }
701
702 // Required function, draw the game board
703 void bd_hud_board(vector pos, vector mySize)
704 {
705         minigame_hud_fitsqare(pos, mySize);
706         bd_boardpos = pos;
707         bd_boardsize = mySize;
708         
709         minigame_hud_simpleboard(pos,mySize,minigame_texture("bd/board"));
710
711         vector tile_size = minigame_hud_denormalize_size('1 1 0' / BD_TILE_SIZE,pos,mySize);
712         vector tile_pos;
713
714         entity e;
715         FOREACH_MINIGAME_ENTITY(e)
716         {
717                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype != BD_TILE_TARGET && e.bd_tiletype != BD_TILE_DOZER )
718                 {
719                         tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
720                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
721
722                         string thepiece = bd_get_tile_pic(e.bd_tiletype);
723
724                         minigame_drawpic_centered( tile_pos,  
725                                         minigame_texture(thepiece),
726                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
727                 }
728
729                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_TARGET && e.bd_tiletype != BD_TILE_DOZER )
730                 {
731                         tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
732                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
733
734                         minigame_drawpic_centered( tile_pos,  
735                                         minigame_texture("bd/target"),
736                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
737                 }
738
739                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype != BD_TILE_TARGET && e.bd_tiletype == BD_TILE_DOZER )
740                 {
741                         tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
742                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
743
744                         vector thedir = e.bd_dir;
745                         float theang = 0;
746
747                         if(thedir_y == -1) { theang = M_PI; }
748                         if(thedir_x == 1) { theang = M_PI/2; }
749                         if(thedir_x == -1) { theang = M_PI*3/2; }
750
751                         drawrotpic(tile_pos, theang, minigame_texture("bd/dozer"),
752                                                 tile_size, tile_size/2, '1 1 1',
753                                                 panel_fg_alpha, DRAWFLAG_NORMAL );
754                 }
755         }
756
757         FOREACH_MINIGAME_ENTITY(e)
758         {
759                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_TARGET )
760                 {
761                         tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
762                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
763
764                         minigame_drawpic_centered( tile_pos,  
765                                         minigame_texture("bd/target"),
766                                         tile_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
767                 }
768         }
769
770         FOREACH_MINIGAME_ENTITY(e)
771         {
772                 if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER )
773                 {
774                         tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT);
775                         tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
776
777                         vector thedir = e.bd_dir;
778                         float theang = 0;
779
780                         if(thedir_y == -1) { theang = M_PI; }
781                         if(thedir_x == 1) { theang = M_PI/2; }
782                         if(thedir_x == -1) { theang = M_PI*3/2; }
783
784                         drawrotpic(tile_pos, theang, minigame_texture("bd/dozer"),
785                                                 tile_size, tile_size/2, '1 1 1',
786                                                 panel_fg_alpha, DRAWFLAG_NORMAL );
787                 }
788         }
789
790         if(active_minigame.minigame_flags & BD_TURN_EDIT)
791         if(bd_valid_tile(bd_curr_pos))
792         {
793                 entity piece = bd_find_piece(active_minigame, bd_curr_pos, false);
794                 entity targ = bd_find_piece(active_minigame, bd_curr_pos, true);
795                 string thepiece = ((piece || (targ && bd_curr_tile != BD_TILE_BOULDER)) ? "bd/delete" : bd_get_tile_pic(bd_curr_tile));
796
797                 tile_pos = minigame_tile_pos(bd_curr_pos,BD_LET_CNT,BD_NUM_CNT);
798                 tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize);
799                 if(bd_curr_tile == BD_TILE_DOZER)
800                 {
801                         drawrotpic(tile_pos, M_PI, minigame_texture("bd/dozer"),
802                                                 tile_size, tile_size/2, '1 1 1',
803                                                 panel_fg_alpha/2, DRAWFLAG_NORMAL );
804                 }
805                 else
806                 {
807                         minigame_drawpic_centered( tile_pos,
808                                         minigame_texture(thepiece),
809                                         tile_size, '1 1 1', panel_fg_alpha/2, DRAWFLAG_NORMAL );
810                 }
811         }
812
813         if ( (active_minigame.minigame_flags & BD_TURN_LOSS) || (active_minigame.minigame_flags & BD_TURN_WIN) )
814         {
815                 vector winfs = hud_fontsize*2;
816                 string victory_text = "Game over!";
817
818                 if(active_minigame.minigame_flags & BD_TURN_WIN)
819                         victory_text = "You win!";
820                 
821                 vector win_pos = pos+eY*(mySize_y-winfs_y)/2;
822                 vector win_sz;
823                 win_sz = minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
824                         sprintf("%s", victory_text), 
825                         winfs, 0, DRAWFLAG_NORMAL, 0.5);
826                 
827                 drawfill(win_pos-eY*hud_fontsize_y,win_sz+2*eY*hud_fontsize_y,'0.3 0.3 1',0.8,DRAWFLAG_ADDITIVE);
828                 
829                 minigame_drawcolorcodedstring_wrapped(mySize_x,win_pos,
830                         sprintf("%s", victory_text), 
831                         winfs, panel_fg_alpha, DRAWFLAG_NORMAL, 0.5);
832         }
833 }
834
835
836 // Required function, draw the game status panel
837 void bd_hud_status(vector pos, vector mySize)
838 {
839         HUD_Panel_DrawBg(1);
840         vector ts;
841         ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message,
842                 hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5);
843
844         pos_y += ts_y;
845         mySize_y -= ts_y;
846
847         vector player_fontsize = hud_fontsize * 1.75;
848         ts_y = ( mySize_y - 2*player_fontsize_y ) / BD_TEAMS;
849         ts_x = mySize_x;
850         vector mypos;
851         vector tile_size = '48 48 0';
852
853         mypos = pos;
854         drawfill(mypos,eX*mySize_x+eY*player_fontsize_y,'1 1 1',0.5,DRAWFLAG_ADDITIVE);
855         mypos_y += player_fontsize_y;
856         drawfill(mypos,eX*mySize_x+eY*tile_size_y,'1 1 1',0.25,DRAWFLAG_ADDITIVE);
857
858         entity e;
859         FOREACH_MINIGAME_ENTITY(e)
860         {
861                 if ( e.classname == "minigame_player" )
862                 {
863                         mypos = pos;
864                         minigame_drawcolorcodedstring_trunc(mySize_x,mypos,
865                                 GetPlayerName(e.minigame_playerslot-1),
866                                 player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
867
868                         mypos_y += player_fontsize_y;
869                         string thepiece = "bd/dozer";
870                         if(active_minigame.minigame_flags & BD_TURN_EDIT)
871                                 thepiece = bd_get_tile_pic(bd_curr_tile);
872                         drawpic( mypos,
873                                         minigame_texture(thepiece),
874                                         tile_size * 0.7, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL );
875
876                         mypos_x += tile_size_x;
877
878                         drawstring(mypos,ftos(e.bd_moves),tile_size,
879                                            '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL);
880                 }
881         }
882 }
883
884 // Turn a set of flags into a help message
885 string bd_turn_to_string(int turnflags)
886 {
887         if ( turnflags & BD_TURN_LOSS )
888                 return _("Better luck next time!");
889
890         if ( turnflags & BD_TURN_WIN )
891                 if(random() > 0.5)
892                         return _("Tubular! Press ""Next Level"" to continue!");
893                 else
894                         return _("Wicked! Press ""Next Level"" to continue!");
895
896         if( turnflags & BD_TURN_EDIT )
897                 return _("Press the space bar to change your currently selected tile");
898
899         if ( turnflags & BD_TURN_MOVE )
900                 return _("Push the boulders onto the targets");
901         
902         return "";
903 }
904
905 // Make the correct move
906 void bd_make_move(entity minigame, string dir)
907 {
908         if ( minigame.minigame_flags == BD_TURN_MOVE )
909         {
910                 minigame_cmd("move ", dir);
911         }
912 }
913
914 void bd_editor_make_move(entity minigame, string dir)
915 {
916         if ( minigame.minigame_flags == BD_TURN_EDIT )
917         {
918                 minigame_cmd("move ", bd_curr_pos, " ", ftos(bd_curr_tile), " ", dir);
919         }
920 }
921
922 void bd_editor_fill(entity minigame)
923 {
924         if ( minigame.minigame_flags == BD_TURN_EDIT )
925         {
926                 minigame_cmd("fill ", bd_curr_pos, " ", ftos(bd_curr_tile));
927         }
928 }
929
930 void bd_set_curr_pos(string s)
931 {
932         if ( bd_curr_pos )
933                 strunzone(bd_curr_pos);
934         if ( s )
935                 s = strzone(s);
936         bd_curr_pos = s;
937 }
938
939 bool bd_normal_move(entity minigame, int themove)
940 {
941         switch ( themove )
942         {
943                 case K_RIGHTARROW:
944                 case K_KP_RIGHTARROW:
945                         bd_make_move(minigame, "r");
946                         return true;
947                 case K_LEFTARROW:
948                 case K_KP_LEFTARROW:
949                         bd_make_move(minigame, "l");
950                         return true;
951                 case K_UPARROW:
952                 case K_KP_UPARROW:
953                         bd_make_move(minigame, "u");
954                         return true;
955                 case K_DOWNARROW:
956                 case K_KP_DOWNARROW:
957                         bd_make_move(minigame, "d");
958                         return true;
959         }
960
961         return false;
962 }
963
964 bool bd_change_dozer_angle(entity minigame)
965 {
966         entity dozer = bd_find_piece(minigame, bd_curr_pos, false);
967         if(!dozer || dozer.bd_tiletype != BD_TILE_DOZER)
968                 return false;
969
970         string thedir = "";
971         vector dir = dozer.bd_dir;
972         if(dir.x == 0 && dir.y == 0) { thedir = "r"; }
973
974         if(dir.x == 0 && dir.y == 1) { thedir = "r"; }
975         if(dir.x == 0 && dir.y ==-1) { thedir = "l"; }
976         if(dir.x ==-1 && dir.y == 0) { thedir = "u"; }
977         if(dir.x == 1 && dir.y == 0) { thedir = "d"; }
978
979         bd_editor_make_move(minigame, thedir);
980         return true;
981 }
982
983 bool bd_editor_move(entity minigame, int themove)
984 {
985         switch ( themove )
986         {
987                 case K_RIGHTARROW:
988                 case K_KP_RIGHTARROW:
989                         if ( ! bd_curr_pos )
990                                 bd_set_curr_pos("a3");
991                         else
992                                 bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,1,0,BD_NUM_CNT,BD_LET_CNT));
993                         return true;
994                 case K_LEFTARROW:
995                 case K_KP_LEFTARROW:
996                         if ( ! bd_curr_pos )
997                                 bd_set_curr_pos("c3");
998                         else
999                                 bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,-1,0,BD_NUM_CNT,BD_LET_CNT));
1000                         return true;
1001                 case K_UPARROW:
1002                 case K_KP_UPARROW:
1003                         if ( ! bd_curr_pos )
1004                                 bd_set_curr_pos("a1");
1005                         else
1006                                 bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,0,1,BD_NUM_CNT,BD_LET_CNT));
1007                         return true;
1008                 case K_DOWNARROW:
1009                 case K_KP_DOWNARROW:
1010                         if ( ! bd_curr_pos )
1011                                 bd_set_curr_pos("a3");
1012                         else
1013                                 bd_set_curr_pos(minigame_relative_tile(bd_curr_pos,0,-1,BD_NUM_CNT,BD_LET_CNT));
1014                         return true;
1015                 case K_ENTER:
1016                 case K_KP_ENTER:
1017                         bd_editor_make_move(minigame, "");
1018                         return true;
1019                 case K_SPACE:
1020                         if(bd_change_dozer_angle(minigame))
1021                                 return true;
1022                         bd_curr_tile += 1;
1023                         if(bd_curr_tile > BD_TILE_LAST)
1024                                 bd_curr_tile = 1;
1025                         return true;
1026         }
1027
1028         return false;
1029 }
1030
1031 // Required function, handle client events
1032 int bd_client_event(entity minigame, string event, ...)
1033 {
1034         switch(event)
1035         {
1036                 case "activate":
1037                 {
1038                         minigame.message = bd_turn_to_string(minigame.minigame_flags);
1039                         bd_set_curr_pos("");
1040                         bd_curr_tile = BD_TILE_BRICK1;
1041                         return false;
1042                 }
1043                 case "key_pressed":
1044                 {
1045                         if(minigame.minigame_flags & BD_TURN_MOVE)
1046                         {
1047                                 if(bd_normal_move(minigame, ...(0,int)))
1048                                         return true;
1049                         }
1050
1051                         if(minigame.minigame_flags & BD_TURN_EDIT)
1052                         {
1053                                 if(bd_editor_move(minigame, ...(0,int)))
1054                                         return true;
1055                         }
1056
1057                         return false;
1058                 }
1059                 case "mouse_pressed":
1060                 {
1061                         if(minigame.minigame_flags & BD_TURN_EDIT)
1062                         {
1063                                 if(...(0,int) == K_MOUSE1)
1064                                 {
1065                                         bd_editor_make_move(minigame, "");
1066                                         return true;
1067                                 }
1068
1069                                 if(...(0,int) == K_MOUSE2)
1070                                 {
1071                                         bd_editor_fill(minigame);
1072                                         return true;
1073                                 }
1074                         }
1075
1076                         return false;
1077                 }
1078                 case "mouse_moved":
1079                 {
1080                         if(minigame.minigame_flags & BD_TURN_EDIT)
1081                         {
1082                                 vector mouse_pos = minigame_hud_normalize(mousepos,bd_boardpos,bd_boardsize);
1083                                 bd_set_curr_pos(minigame_tile_name(mouse_pos,BD_LET_CNT,BD_NUM_CNT));
1084                                 if ( ! bd_valid_tile(bd_curr_pos) )
1085                                         bd_set_curr_pos("");
1086                         }
1087                         return true;
1088                 }
1089                 case "network_receive":
1090                 {
1091                         entity sent = ...(0,entity);
1092                         int sf = ...(1,int);
1093                         if ( sent.classname == "minigame" )
1094                         {
1095                                 if ( sf & MINIG_SF_UPDATE )
1096                                 {
1097                                         sent.message = bd_turn_to_string(sent.minigame_flags);
1098                                         //if ( sent.minigame_flags & minigame_self.team )
1099                                                 minigame_prompt();
1100                                 }
1101                         }
1102                         else if(sent.classname == "minigame_board_piece")
1103                         {
1104                                 if(sf & MINIG_SF_UPDATE)
1105                                 {
1106                                         int letter = ReadByte();
1107                                         int number = ReadByte();
1108                                         if(sent.netname) { strunzone(sent.netname); }
1109                                         sent.netname = strzone(minigame_tile_buildname(letter, number));
1110
1111                                         sent.bd_tiletype = ReadByte();
1112
1113                                         int dx = ReadByte();
1114                                         int dy = ReadByte();
1115
1116                                         if(dx == 2) dx = -1;
1117                                         if(dy == 2) dy = -1;
1118
1119                                         sent.bd_dir_x = dx;
1120                                         sent.bd_dir_y = dy;
1121                                         sent.bd_dir_z = 0;
1122                                 }
1123                         }
1124                         else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES))
1125                                 sent.bd_moves = ReadShort(); // make this a byte when possible
1126
1127                         return false;
1128                 }
1129                 case "menu_show":
1130                 {
1131                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Next Level"),"next");
1132                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Restart"),"restart");
1133                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Editor"),"edit");
1134                         HUD_MinigameMenu_CustomEntry(...(0,entity),_("Save"),"save");
1135                         return false;
1136                 }
1137                 case "menu_click":
1138                 {
1139                         if(...(0,string) == "next")
1140                                 minigame_cmd("next");
1141                         if(...(0,string) == "restart")
1142                                 minigame_cmd("restart");
1143                         if(...(0,string) == "edit")
1144                                 minigame_cmd("edit");
1145                         if(...(0,string) == "save")
1146                                 minigame_cmd("save");
1147                         return false;
1148                 }
1149         }
1150
1151         return false;
1152 }
1153
1154 #endif