forked from cacciatc/scriptcraft-lsystems
-
Notifications
You must be signed in to change notification settings - Fork 0
/
drone.js
929 lines (903 loc) · 31.4 KB
/
drone.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
load(__folder + "../core/_primitives.js");
var global = this;
//
// Interface
// =========
//
// Please read the following section to understand what a Minecraft Drone can do.
//
var Drone = Drone || {
//
// TLDNR; (Just read this if you're impatient)
// ======
// At the in-game command prompt type...
//
// /js box(5)
//
// ... creates a single wooden block at the cross-hairs or player location
//
// /js box(5).right(2).box('35:15',4,9,1)
//
// ... creates a single wooden block and a 2001 black obelisk that is
// 4 wide x 9 tall x 1 long in size.
//
// If you want to see what else ScriptCraft's Drone can do, read on...
//
// creating a new drone
// ====================
//
// Drones can be created in 3 ways...
//
// [1] Calling any one of the methods listed below will return a Drone object. For example...
//
// /js var d = box(5)
//
// ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone
// object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all
// of the Drone class's methods are also global functions that return new Drone objects.
// This is short-hand for creating drones and is useful for playing around with Drones at the in-game
// command prompt. It's shorter than typing ...
//
// /js var d = new Drone().box(5)
//
// ... All of the Drone's methods return `this` (self) so you can chain operations together like this...
//
// /js var d = box(5).up().box(5,3,1,3).down().fwd(2).box(5).turn().fwd(2).box(5).turn().fwd(2).box(5)
//
// [2] /js d = new Drone()
//
// This will create a new Drone. If the cross-hairs are pointing
// at a block at the time then, that block's location becomes the drone's
// starting point.
// If the cross-hairs are _not_ pointing at a block, then the drone's starting
// location will be 2 blocks directly in front of the player.
// TIP: Building always happens right and front of the drone's position...
//
// Plan View:
//
// ^
// |
// |
// D---->
//
// For convenience you can use a 'corner stone' to begin building.
// The corner stone should be located just above ground level.
// If the cross-hair is point at or into ground level when you create a new Drone(),
// then building begins at that point. You can get around this by pointing at a
// 'corner stone' just above ground level or alternatively use the following statement...
//
// /js d = new Drone().up()
//
// ... which will move the drone up one block as soon as it's created.
//
// [3] d = new Drone(x,y,z,direction)
//
// This will create a new Drone at the location you specified using x, y, z
// In minecraft, the X axis runs west to east and the Z axis runs north to south.
// The direction parameter says what direction you want the drone to face:
// 0 = east, 1 = south, 2 = west, 3 = north.
// If the direction parameter is omitted, the player's direction is used instead.
//
// When you load() the drone.js javascript file, a default `drone` Object is created at
// the cross-hair's or player's location.
//
// movement
// ========
//
// Drones can move freely in minecraft's 3-D world. You control the Drone's movement using
// any of the following methods..
//
// move up - n is the number of blocks you want to move (default 1)
up: function(n){},
// move down - n is the number of blocks you want to move (default 1)
down: function(n){},
// move left - n is the number of blocks you want to move (default 1)
left: function(n){},
// move right - n is the number of blocks you want to move (default 1)
right: function(n){},
// move forward - n is the number of blocks you want to move (default 1)
fwd: function(n){},
// move backwards - n is the number of blocks you want to move (default 1)
back: function(n){},
// to change direction - n is the number of turns right you want to make (default 1)
// turns are always clock-wise. If the drone is facing north, then drone.turn() will
// make the turn face east. If the drone is facing east then drone.turn(2) will make the
// drone turn twice so that it is facing west.
//
turn: function(n){},
// markers
// =======
//
// Markers are useful when your Drone has to do a lot of work. You can set a check-point
// and return to the check-point using the move() method.
// if your drone is about to undertake a lot of work - e.g. building a road, skyscraper or forest
// you should set a check-point before doing so if you want your drone to return to its current location.
// e.g:
//
// drone.chkpt('town-square');
// for (i = 0; i< 100; i++){
// drone.fwd(12).box(6);
// }
// drone.move('town-square');
//
// A 'start' checkpoint is automatically created when the Drone is first created.
//
chkpt: function(checkpoint_name){},
move: function(checkpoint_name){},
// building
// ========
//
// the box() method is the main method for building things.
// parameters
// ----------
// b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling.
// An array of block ids can also be used in which case each block in the array
// will be placed in turn.
// w - the width of the structure (optional - default value is 1)
// h - the height of the structure (optional - default value is 1)
// d - the depth of the structure - NB this is *not* how deep underground the structure lies - this is
// how far away (depth of field) from the drone the structure will extend.
// (optional - default value is 1)
// e.g:
// To create a stone structure 5 blocks wide, 8 blocks tall and 15 blocks long...
//
// drone.box(48, 5, 8, 15);
//
// To create an oak tree...
// (we can't control the dimensions of a tree since it's a natural object in minecraft)
//
// drone.box(6);
//
box: function(block,width,height,depth){},
//
// like box but empties out the inside - ideal for buildings.
//
box0: function(block,width,height,depth){},
prism: function(block,width,depth){},
prism0: function(block,width,depth){},
//
// create a cylinder - building begins radius blocks to the right and forward.
//
cylinder: function(block,radius,height){},
//
// create an empty cylinder
//
cylinder0: function(block,radius,height){},
// miscellaneous
// =============
// create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created.
door: function(b){},
// create double doors (left and right side)
door2: function(b){},
// signs must use block 63 (stand-alone signs) or 68 (signs on walls)
// s can be a string or an array of strings.
sign: function(s,b){},
// create trees.
oak: function(){},
spruce: function(){},
birch: function(){},
jungle: function(){},
//
// rand takes either an array (if each blockid is the same weight)
// or an object where each property is a blockid and the value is it's weight (an integer)
// e.g.
// rand([98,'98:1','98:2'],w,d,h) will place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked)
// rand({98: 5, '98:1': 3,'98:2': 2},w,d,h) will place random blocks stone has a 50% chance of being picked,
// mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked.
//
rand: function(distribution,w,h,d){},
//
// places random flowers and long grass (similar to the effect of placing bonemeal on grass)
//
garden: function(w,d){},
// Copy & Paste
// ============
// copy an area so it can be pasted elsewhere. The name can be used for pasting the copied area elsewhere...
// drone.copy('somethingCool',10,5,10).right(12).paste('somethingCool');
// ... copies a 10x5x10 area (using the drone's coordinates as the starting point) into memory.
// the copied area can be referenced using the name 'somethingCool'. The drone moves 12 blocks right then
// pastes the copy.
//
copy: function(name,w,h,d){},
paste: function(name){}
// Chaining
// ========
//
// All of the above methods return a Drone object, which means methods can be 'chained' together so instead of writing this...
//
// drone = new Drone();
// drone.fwd(3);
// drone.left(2);
// drone.box(2); // create a grass block
// drone.up();
// drone.box(2); // create another grass block
// drone.down();
//
// ...you could simply write ...
//
// var drone = new Drone().fwd(3).left(2).box(2).up().box(2).down();
//
// The Drone object uses a Fluent Interface ( http://en.wikipedia.org/wiki/Fluent_interface )
// to make ScriptCraft scripts more concise and easier to write and read.
// Minecraft's in-game command prompt is limited to about 80 characters so chaining drone commands together
// means more can be done before hitting the command prompt limit. For complex building you should save your
// commands in a new script file and load it using /js load()
//
// Properties
// ==========
//
// x - The Drone's position along the west-east axis (x increases as you move east)
// y - The Drone's position along the vertical axis (y increses as you move up)
// z - The Drone's position along the north-south axis (z increases as you move south)
// dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north.
};
//
// Implementation
// ==============
//
// There is no need to read any further unless you want to understand how the Drone object works.
//
(function(){
//
// don't want to declare object more than once
//
if (Drone.constructor == Function)
return;
//
// Drone Constructor
//
Drone = function(x,y,z,dir)
{
var usePlayerCoords = false;
var playerPos = getPlayerPos();
if (typeof x == "undefined"){
var mp = getMousePos();
if (mp){
this.x = mp.x;
this.y = mp.y;
this.z = mp.z;
}else{
// base it on the player's current location
usePlayerCoords = true;
//
// it's possible that drone.js could be loaded by a non-playing op
// (from the server console)
//
if (!playerPos){
return null;
}
this.x = playerPos.x;
this.y = playerPos.y;
this.z = playerPos.z;
}
}else{
this.x = x;
this.y = y;
this.z = z;
}
if (typeof dir == "undefined"){
this.dir = _getDirFromRotation(playerPos.yaw);
}else{
this.dir = dir%4;
}
if (usePlayerCoords){
this.fwd(3);
}
this.chkpt('start');
};
Drone.prototype.chkpt = function(name){
this.checkpoints[name] = {x:this.x,y:this.y,z:this.z,dir:this.dir};
return this;
};
Drone.prototype.move = function(name){
var coords = this.checkpoints[name];
if (coords){
this.x = coords.x;
this.y = coords.y;
this.z = coords.z;
this.dir = coords.dir%4;
}
return this;
};
Drone.prototype.checkpoints = {};
Drone.prototype.turn = function(n)
{
if (typeof n == "undefined"){
this.dir++;
}else{
this.dir += n;
}
this.dir %=4;
return this;
};
Drone.prototype.right = function(n){ if (typeof n == "undefined"){ n = 1;} return _movements[this.dir].right(this,n);};
Drone.prototype.left = function(n){ if (typeof n == "undefined"){ n = 1;} return _movements[this.dir].left(this,n);};
Drone.prototype.fwd = function(n){ if (typeof n == "undefined"){ n = 1;} return _movements[this.dir].fwd(this,n);};
Drone.prototype.back = function(n){ if (typeof n == "undefined"){ n = 1;} return _movements[this.dir].back(this,n);};
Drone.prototype.up = function(n){ if (typeof n == "undefined"){ n = 1;} this.y+=n; return this;};
Drone.prototype.down = function(n){ if (typeof n == "undefined"){ n = 1;} this.y-=n; return this;};
//
// building
//
Drone.prototype.sign = function(message,block){
if (message.constructor == Array){
}else{
message = [message];
}
var bm = _getBlockIdAndMeta(block);
block = bm[0];
meta = bm[1];
if (block != 63 && block != 68){
print("ERROR: Invalid block id for use in signs");
return;
}
if (block == 68){
meta = Drone.PLAYER_SIGN_FACING[this.dir%4];
this.back();
}
putSign(message,this.x,this.y,this.z,block,meta);
if (block == 68){
this.fwd();
}
return this;
};
Drone.prototype.cuboid = function(block,w,h,d){
var md = 0;
if (typeof h == "undefined"){
h = 1;
}
if (typeof d == "undefined"){
d = 1;
}
if (typeof w == "undefined"){
w = 1;
}
var bi = 0;
var that = this;
_traverse[this.dir].width(that,w,function(){
_traverseHeight(that,h,function(){
_traverse[that.dir].depth(that,d,function(){
//
// wph 20121229 - slower but supports random blocks
//
var cb = 0;
if (block.constructor == Array){
cb = block[bi%block.length];
}else{
cb = block;
}
var bm = _getBlockIdAndMeta(cb);
cb = bm[0];
md = bm[1];
putBlock(that.x,that.y,that.z,cb,md);
bi++;
});
});
});
return this;
};
Drone.prototype.cuboid0 = function(block,w,h,d){
return this.cuboid(block,w,h,d).fwd().right().cuboid(0,w-2,h,d-2).back().left();
};
Drone.prototype.door = function(door){
if (typeof door == "undefined"){
door = 64;
}else{
door = 71;
}
return this.cuboid(door+':' + this.dir).up().cuboid(door+':8').down();
};
Drone.prototype.door2 = function(door){
if (typeof door == "undefined"){
door = 64;
}else{
door = 71;
}
return this.cuboid(door+':' + this.dir).up().cuboid(door+':8').right().cuboid(door+':9').down().cuboid(door+':' + this.dir).left();
};
// player dirs: 0 = east, 1 = south, 2 = west, 3 = north
// block dirs: 0 = east, 1 = west, 2 = south , 3 = north
// sign dirs: 5 = east, 3 = south, 4 = west, 2 = north
Drone.PLAYER_STAIRS_FACING = [0,2,1,3];
// for blocks 68 (wall signs) 65 (ladders) 61,62 (furnaces) 23 (dispenser) and 54 (chest)
Drone.PLAYER_SIGN_FACING = [4,2,5,3];
Drone.PLAYER_TORCH_FACING = [2,4,1,3];
//
// add custom methods to the Drone object using this function
//
Drone.extend = function(name, func)
{
Drone.prototype[name] = func;
global[name] = function(){
var result = new Drone();
result[name].apply(result,arguments);
return result;
};
};
Drone.prototype.prism0 = function(block,w,d){
this.prism(block,w,d).fwd().right().prism(0,w-2,d-2).left().back();
var se = Drone.STAIRBLOCKS[block];
if (d % 2 == 1 && se){
// top of roof will be open - need repair
var f = Math.floor(d/2);
this.fwd(f).up(f).cuboid(se,w).down(f).back(f);
}
};
Drone.STAIRBLOCKS = {53: '5:0' // oak wood
,67: 4 // cobblestone
,108: 45 // brick
,109: 98 // stone brick
,114: 112 // nether brick
,128: 24 // sandstone
,134: '5:1' // spruce wood
,135: '5:2' // birch wood
,136: '5:3' // jungle wood
};
// /\
// /##\
// /####\
// 012345
// d = 6, m = 3
//
// /#\
// /###\
// 01234
// d = 5, m = 2
//
// /\
// /##\
// 0123
// d = 4, m = 2
//
// /#\
// 012
// d = 3, m = 1
//
Drone.prototype.prism = function(block,w,d)
{
var stairEquiv = Drone.STAIRBLOCKS[block];
if (stairEquiv){
this.fwd().prism(stairEquiv,w,d-2).back();
var d2 = 0;
var middle = Math.floor(d/2);
var uc = 0,dc = 0;
while (d2 < d)
{
var di = (d2 < middle?this.dir:(this.dir+2)%4);
var bd = block + ':' + Drone.PLAYER_STAIRS_FACING[di];
var putStep = true;
if (d2 == middle){
if (d % 2 == 1){
putStep = false;
}
}
if (putStep)
this.cuboid(bd,w);
if (d2 < middle-1){
this.up();
uc++;
}
var modulo = d % 2;
if (modulo == 1){
if (d2 > middle && d2<d-1){
this.down();
dc++;
}
}else{
if (d2 >= middle && d2<d-1){
this.down();
dc++;
}
}
this.fwd();
d2++;
}
this.back(d);
}else{
var c = 0;
var d2 = d;
while(d2 >= 1){
this.cuboid(block,w,1,d2);
d2 -= 2;
this.fwd().up();
c++;
}
this.down(c).back(c);
}
return this;
};
Drone.prototype.box = Drone.prototype.cuboid;
Drone.prototype.box0 = Drone.prototype.cuboid0;
//
// show the Drone's position and direction
//
Drone.prototype.toString = function(){
var dirs = ["east","south","west","north"];
return "x: " + this.x + " y: "+this.y + " z: " + this.z + " dir: " + this.dir + " "+dirs[this.dir];
};
Drone.prototype.debug = function(){
print(this);
return this;
};
Drone.prototype.moveTo = function(x,y){
this.x = x;
this.y = y;
return this;
};
Drone.prototype.x = function(){
return this.x;
};
Drone.prototype.y = function(){
return this.y;
};
Drone.prototype.z = function(){
return this.z;
};
// ========================================================================
// Private variables and functions
// ========================================================================
var _cylinderX = function(block,radius,height,drone,fill)
{
drone.chkpt('cylinderX');
var x0, y0, gotoxy;
drone.right(radius).fwd(radius).chkpt('center');
switch (drone.dir){
case 0: // east
x0 = drone.z;
y0 = drone.x;
gotoxy = function(xo,yo){ return drone.right(xo).fwd(yo);};
break;
case 1: // south
x0 = drone.x;
y0 = drone.z;
gotoxy = function(xo,yo){ return drone.right(xo).fwd(0-yo);};
break;
case 2: // west
x0 = drone.z;
y0 = drone.x;
gotoxy = function(xo,yo){ return drone.right(0-xo).fwd(0-yo);};
break;
case 3: // north
x0 = drone.x;
y0 = drone.z;
gotoxy = function(xo,yo){ return drone.right(xo).fwd(0-yo);};
break;
}
var points = [];
var setPixel = function(a,b){
var xo = (a-x0);
var yo = (b-y0);
if (fill){
if (xo < 0 && yo < 0){
// bottom left quadrant
drone.fwd(yo).right(xo)
.box(block,Math.abs(xo*2)+1,height,Math.abs(yo*2)+1)
.back(yo).left(xo);
}
}
gotoxy(xo,yo).box(block,1,height,1).move('center');
};
//
// credit: Following code is copied almost verbatim from
// http://en.wikipedia.org/wiki/Midpoint_circle_algorithm
// Bresenham's circle algorithm
//
var f = 1 - radius;
var ddF_x = 1;
var ddF_y = -2 * radius;
var x = 0;
var y = radius;
setPixel(x0, y0 + radius);
setPixel(x0, y0 - radius);
setPixel(x0 + radius, y0);
setPixel(x0 - radius, y0);
while(x < y)
{
// ddF_x == 2 * x + 1;
// ddF_y == -2 * y;
// f == x*x + y*y - radius*radius + 2*x - y + 1;
if(f >= 0)
{
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
setPixel(x0 + x, y0 + y);
setPixel(x0 - x, y0 + y);
setPixel(x0 + x, y0 - y);
setPixel(x0 - x, y0 - y);
setPixel(x0 + y, y0 + x);
setPixel(x0 - y, y0 + x);
setPixel(x0 + y, y0 - x);
setPixel(x0 - y, y0 - x);
}
return drone.move('cylinderX');
}
var _cylinder0 = function(block,radius,height){
return _cylinderX(block,radius,height,this,false);
};
var _cylinder1 = function(block,radius,height){
return _cylinderX(block,radius,height,this,true);
};
var _getDirFromRotation = function(r){
var result = 1;
r = Math.abs(Math.ceil(r));
if (r >= 45 && r < 135){
result = 0;
}
if (r >= 135 && r < 225){
result = 3;
}
if (r >= 225 && r < 315){
result = 2;
}
return result;
};
var _getBlockIdAndMeta = function(b){
if (typeof b == 'string'){
var bs = b;
var sp = bs.indexOf(':');
if (sp == -1){
return [parseInt(bs),0];
}
b = parseInt(bs.substring(0,sp));
md = parseInt(bs.substring(sp+1,bs.length));
return [b,md];
}else{
return [b,0];
}
};
//
// movement
//
var _movements = [{},{},{},{}];
// east
_movements[0].right = function(that,n){ that.z +=n; return that;};
_movements[0].left = function(that,n){ that.z -=n; return that;};
_movements[0].fwd = function(that,n){ that.x +=n; return that;};
_movements[0].back = function(that,n){ that.x -= n; return that;};
// south
_movements[1].right = _movements[0].back;
_movements[1].left = _movements[0].fwd;
_movements[1].fwd = _movements[0].right;
_movements[1].back = _movements[0].left;
// west
_movements[2].right = _movements[0].left;
_movements[2].left = _movements[0].right;
_movements[2].fwd = _movements[0].back;
_movements[2].back = _movements[0].fwd;
// north
_movements[3].right = _movements[0].fwd;
_movements[3].left = _movements[0].back;
_movements[3].fwd = _movements[0].left;
_movements[3].back = _movements[0].right;
var _traverse = [{},{},{},{}];
// east
_traverse[0].width = function(that,n,callback){
var s = that.z, e = s + n;
for (; that.z < e; that.z++){
callback(that.z-s);
}
that.z = s;
};
_traverse[0].depth = function(that,n,callback){
var s = that.x, e = s+n;
for (;that.x < e;that.x++){
callback(that.x-s);
}
that.x = s;
};
// south
_traverse[1].width = function(that,n,callback){
var s = that.x, e = s-n;
for (;that.x > e;that.x--){
callback(s-that.x);
}
that.x = s;
};
_traverse[1].depth = _traverse[0].width;
// west
_traverse[2].width = function(that,n,callback){
var s = that.z, e = s-n;
for (;that.z > e;that.z--){
callback(s-that.z);
}
that.z = s;
};
_traverse[2].depth = _traverse[1].width;
// north
_traverse[3].width = _traverse[0].depth;
_traverse[3].depth = _traverse[2].width;
var _traverseHeight = function(that,n,callback){
var s = that.y, e = s + n;
for (; that.y < e; that.y++){
callback(that.y-s);
}
that.y = s;
};
//
// standard fisher-yates shuffle algorithm
//
var _fisherYates = function( myArray ) {
var i = myArray.length;
if ( i == 0 ) return false;
while ( --i ) {
var j = Math.floor( Math.random() * ( i + 1 ) );
var tempi = myArray[i];
var tempj = myArray[j];
myArray[i] = tempj;
myArray[j] = tempi;
}
};
var _rand = function(blockDistribution){
if (!(blockDistribution.constructor == Array)){
var a = [];
for (var p in blockDistribution){
var n = blockDistribution[p];
for (var i = 0;i < n;i++){
a.push(p);
}
}
blockDistribution = a;
}
while (blockDistribution.length < 1000){
// make array bigger so that it's more random
blockDistribution = blockDistribution.concat(blockDistribution);
}
_fisherYates(blockDistribution);
return blockDistribution;
};
Drone.prototype.rand = function(dist,w,h,d){
var randomized = _rand(dist);
return this.box(randomized,w,h,d);
};
var _trees = {oak: 6 ,spruce: '6:1' ,birch: '6:2' ,jungle: '6:3' };
for (var p in _trees){
Drone.prototype[p] = function(v){return function(){ return this.box(v);};}(_trees[p]);
}
Drone.prototype.garden = function(w,d)
{
// make sure grass is present first
this.down().box(2,w,1,d).up();
// make flowers more common than long grass
var dist = {37: 3, // red flower
38: 3, // yellow flower
'31:1': 2, // long grass
0: 1
};
return this.rand(dist,w,1,d);
};
//
// Drone's clipboard
//
Drone.clipBoard = {};
Drone.prototype.copy = function(name, w, h, d)
{
var that = this;
var ccContent = [];
_traverse[this.dir].width(that,w,function(ww){
ccContent.push([]);
_traverseHeight(that,h,function(hh){
ccContent[ww].push([]);
_traverse[that.dir].depth(that,d,function(dd){
var b = getBlock(that.x,that.y,that.z);
ccContent[ww][hh][dd] = b;
});
});
});
Drone.clipBoard[name] = {dir: this.dir, blocks: ccContent};
return this;
};
Drone.prototype.paste = function(name)
{
var ccContent = Drone.clipBoard[name];
var srcBlocks = ccContent.blocks;
var srcDir = ccContent.dir; // direction player was facing when copied.
var dirOffset = (4 + (this.dir - srcDir)) %4;
var that = this;
_traverse[this.dir].width(that,srcBlocks.length,function(ww){
var h = srcBlocks[ww].length;
_traverseHeight(that,h,function(hh){
var d = srcBlocks[ww][hh].length;
_traverse[that.dir].depth(that,d,function(dd){
var b = srcBlocks[ww][hh][dd];
var bm = _getBlockIdAndMeta(b);
var cb = bm[0];
var md = bm[1];
//
// need to adjust blocks which face a direction
//
switch (cb)
{
//
// doors
//
case 64: // wood
case 71: // iron
// top half of door doesn't need to change
if (md < 8) {
md = (md + dirOffset) % 4;
}
break;
//
// stairs
//
case 53: // oak
case 67: // cobblestone
case 108: // red brick
case 109: // stone brick
case 114: // nether brick
case 128: // sandstone
case 134: // spruce
case 135: // birch
case 136: // junglewood
var dir = md & 0x3;
var a = Drone.PLAYER_STAIRS_FACING;
var len = a.length;
for (var c=0;c < len;c++){
if (a[c] == dir){
break;
}
}
c = (c + dirOffset) %4;
var newDir = a[c];
md = (md >>2<<2) + newDir;
break;
//
// signs , ladders etc
//
case 23: // dispenser
case 54: // chest
case 61: // furnace
case 62: // burning furnace
case 65: // ladder
case 68: // wall sign
var a = Drone.PLAYER_SIGN_FACING;
var len = a.length;
for (var c=0;c < len;c++){
if (a[c] == md){
break;
}
}
c = (c + dirOffset) %4;
var newDir = a[c];
md = newDir;
break;
}
putBlock(that.x,that.y,that.z,cb,md);
});
});
});
return this;
};
Drone.prototype.cylinder0 = _cylinder0;
Drone.prototype.cylinder = _cylinder1;
//
// make all Drone's methods available also as standalone functions
// which return a drone object
// this way drones can be created and used as follows...
//
// /js box(5,7,3,4)
//
// ... which is a short-hand way to create a wooden building 7x3x4
//
var ops = ['up','down','left','right','fwd','back','turn',
'chkpt','move',
'box','box0','prism','prism0','cylinder','cylinder0',
'door','door2','sign','oak','spruce','birch','jungle',
'rand','garden',
'copy','paste'
];
for (var i = 0;i < ops.length; i++){
global[ops[i]] = function(op){
return function(){
var result = new Drone();
result[op].apply(result,arguments);
return result;
};
}(ops[i]);
}
}());