1 package Data::StarCraft::Map;
17 my ($fh, $size, $seek) = @_;
18 seek *$fh, $seek, 0 if $seek;
19 read(*$fh, my $in, $size) eq $size or return undef;
27 while (not eof $file) {
28 local $_ = $self->_read($file, 8)
29 and my ($type, $size) = unpack "a4V", $_
30 or die "Couldn't chunk header\n";
32 #printf STDERR "%s: %s\n", $type, $size;
33 defined $self->{$type} and warn "duplicate map chunk $type\n";
34 $self->{$type} = $self->_read($file, $size);
41 return 'v' . ord $self->{VER};
46 my ($x, $y) = unpack "vv", $self->{DIM};
54 return $_[0]->info->{x};
59 my @map = unpack 'v*', $self->{MTXM};
60 @map == $#map + 1 or warn(sprintf
61 "couldn't parse map: only %d tiles\n", scalar @map
68 'd' => [ 32.. 62, 63], # dirt (verified)
69 'h' => [ 64.. 94], # high dirt
70 "~" => [ 96..117, 118..127], # water
71 'j' => [128..159], # jungle/crushed rock
72 'o' => [160..186, 187..191], # rocky/shale (verified)
73 'R' => [192..223], # raised jungle
74 'l' => [224..252, 253..255], # lava/ruins (verified)/flagstones?
75 'b' => [256..287], # basilica?
76 'x' => [288..319], # high jungle
77 # 'x' => [3745..3792], # high jungle
78 'q' => [320..351], # high ruins
80 'a' => [384..415], # high basilica
81 'm' => [416..447], # mud
87 '/' => [576..607], # high dirt -> dirt (top left)
88 '/' => [608..639], # dirt -> high dirt
89 '\\' => [640..671], # high dirt -> dirt (top right)
90 '\\' => [672..703], # dirt -> high dirt (bottom left)
91 '\\' => [704..735], # high dirt -> dirt (bottom left)
92 '\\' => [736..767], # dirt -> high dirt (top right)
94 '=' => [864..1055], # some edge (tmp)
97 'D' => [1728..1780], # edge water
98 '/' => [1760..1791], # dirt -> water (top left)
99 '\\' => [1792..1823], # dirt -> water (top right)
110 # 'x' => [4096..4351],
112 # 'x' => [4352..4607],
113 # ' ' => [4608..4863],
119 # ' ' => [5824..5887],
120 '=' => [5888..6143], # raised jungle -> jungle
121 # ' ' => [6144..6655],
123 '/' => [7168..7359], # basilica -> crushed rock
125 'i' => [7552..7807], # high temple
126 # ' ' => [7808..8959],
127 # '=' => [8960..9087],
128 # 'd' => [9088..9215],
129 # ' ' => [9216..9727],
130 # 'm' => [9728..9983],
131 # # >= 9984 unencountered
132 # ' ' => [19968..20480], # something here on twilight
133 # ' ' => [20544..20736], # center thing on twilight
134 # '~' => [20896..21023], # something in the water on twilight
135 # 'x' => [23104..23231], # something on twilight (on X or H)
137 # 'm' => [9216..9776], # mud <-> dirt
138 # 'r' => [5792..5875, 5888..5904], # raised jungle edge
140 # 'd' => [3042..3250], # dirt<->grass
141 # 'P' => [4608..4977], # dirt<->shale (verified) [also seems to be rocks on mud]
142 # #'=' => [768..1731], # dirt<->lava ridge (verified)
145 '1' => [16405, 16388],
146 '2' => [16421, 16404, 16387],
147 '3' => [16437, 16420, 16403, 16386],
148 '4' => [16453, 16436, 16419, 16402, 16385],
149 '5' => [ 16435, 16418, 16401, 16384],
150 '6' => [ 16434, 16417, 16400],
151 '7' => [ 16433, 16416],
152 'd' => [16501, 16432],
153 '7' => [16485, 16500], # these ↕ aren't ramps in temple!!
154 '6' => [16469, 16484, 16499],
155 '5' => [16453, 16468, 16483, 16498],
156 '4' => [ 16452, 16467, 16482, 16497],
157 '3' => [ 16451, 16466, 16481, 16496],
158 '2' => [ 16450, 16465, 16480],
159 '1' => [ 16449, 16464],
160 'h' => [17248, 16448],
161 '1' => [17264, 17249],
162 '1' => [17280, 17265, 17250],
163 '2' => [17296, 17281, 17266, 17251],
164 '3' => [17312, 17297, 17282, 17267, 17252],
165 '4' => [17328, 17313, 17298, 17283, 17268, 17253],
166 '5' => [ 17329, 17314, 17299, 17284, 17269],
167 '6' => [ 17330, 17315, 17300, 17285],
168 '7' => [ 17331, 17316, 17301],
169 '7' => [ 17332, 17317],
170 'd' => [17232, 17333],
171 '7' => [17216, 17233],
172 '7' => [17200, 17217, 17234],
173 '6' => [17184, 17201, 17218, 17235],
174 '5' => [17168, 17185, 17202, 17219, 17236],
175 '4' => [17152, 17169, 17186, 17203, 17220, 17237],
176 '3' => [ 17153, 17170, 17187, 17204, 17221],
177 '2' => [ 17154, 17171, 17188, 17205],
178 '1' => [ 17155, 17172, 17189],
179 '1' => [ 17156, 17173],
185 # Longinus (by KuKulZa, modified MBCgame/iCCup)
189 # SIDE: 5,5,5,1,2,0,1,2,7,7,7,4
191 19760..19761, 19792, 19793, 19680, 19681, 19712, 19713, 19664, 19665, 19728, 19729, # D→J
192 19808,19809, 19776,19777, 19872,19873, 19920,19921,
193 19744,19745, 19696,19697, 19632,19633, 19696,19697, 19952,19953,
194 19568,19569, # below ridge
195 3618,3634, 3586,3602, # D←J
199 21968, 21969, 21984, 21985,
200 21824,21825,21840,21841, 21856,21857,21872,21873, 21792,21793,
201 21808,21809, 21888,21889,21904,21905,
202 3616,3632,3584,3600,3616,3632, # J→D
206 22608..22611, 22624..22627, 22640..22643,
209 24850, # besides ladder
210 20832,20833,20848,20849, 20768,20769,20784,20785,
211 20864,20865,20880,20881,
212 20704,20705,20720,20721, # large rock
213 4432,4433,4434,4448,4449,4464,4465,
214 4545,4561,4577,4593, 4544,4560,4576,4592,
218 17952,17953, 17936,17937, 17920,17921, 17856,17857, 17888,17889, # H→X unsure
219 17984,17985, 18160,18161, 18128,18129, 18048,18049,
220 19648,19649,19616,19617, 18016,18017,
221 17696,17697,17712,17713, # tree
222 17872,17873,17904,17905, 18144,18145,
223 18448,18449,18464,18465,18480,18481,
227 9218,9234,9795,9235,9811,9571,9587,
228 9472,9488,9250,9266,9635,9651,9280,9296,
229 9504,9520,9665,9681,9344,9360,9760,9776,9539,9555,
230 9475,9491,9889,9905,9376,9392,9440,9456,9571,9587,
234 9569,9585,9474,9490,9283,9299,9216,9232,9536,9552,
235 9536,9552,9923,9939,9283,9299,9506,9522,9827,9843,9728,9744,9568,9584,
236 9283,9299,9217,9233,9731,9747,9568,9584,9699,9715,9315,9331,9284,9300,9217,9233,9955,9971,
237 9763,9779,9793,9809,9379,9395,9664,9680,9316,9332,9249,9265,9536,9552,
238 9443,9459,9921,9937,9284,9300,9216,9232,9284,9300,9216,9232,9696,9712,9345,9361,9409,9425,9604,9620,9568,9584,
239 9216,9232,9764,9780,9248,9264,9316,9332,9248,9264,9346,9362,9441,9457,9860,9876,9537,9553,
240 9728,9744,9444,9460,9667,9683,9348,9364,9411,9427,9602,9618,9378,9394,9444,9460,9569,9585,
241 9891,9907,9380,9396,9443,9459,9858,9874,9540,9556,
242 9379,9395,9442,9458,9572,9588,
245 # Longinus (by KuKulZa, modified MBCgame/iCCup)
246 9125, 9141, 9126, 9142, # D→~
248 9059,9075, 9189,9205, # D→~
250 19200,19201,19216,19217, # object on D
253 17632,17633,17648,17649, # H rotating thing
254 18208,18209, # H skull thing
255 18224,18225,18240,18241, # H statuey thing
258 23872,23873,23874,23875,23888,23889,23890,23891,23904,23905,23906,23907, # Q crater
259 23680,23681,23696,23697,23712,23713,
262 24336,24337,24352,24353, 24272,24273,24288,24289,
263 24272,24273,24288,24289, 24368,24369,24384,24385,
264 4353,4369, 4385,4401,4353,4369, 4386,4402, 4385,4401,
265 4386,4402,4354,4370,4386,4402,
268 19856,19857,20144,20145, # D skeleton
270 '/' => [21184], 'W' => [21185,21200,21201,21216,21217], # standing rock
271 '/' => [21232], 'W' => [21233,21248,21249,21264,21265], # standing rock
272 '7' => [25011, 25010], # M»H
273 '7' => [24699, 25143], 6 => [24716], # M»H
274 'W' => # island o' rocks
275 [21120..21125, 21136..21141, 21152..21157, 21168..21173],
276 '/' => [21121,21122,21136..21139,21152..21154],
277 '~' => [21120,21124,21125,21141,21157,21171..21173],
280 8997,9013,8963,8979, 8996,9012,8963,8979,8993,9009, 8997,9013,
281 8998,9014, 8993,9009, 9187,9203, 9121,9137, 9131,9147, 8992,9008,
282 9190,9206, 9062,9078, 8996,9012, 9090,9106,9194,9210,
283 9061,9077,9029,9045,9184,9200,8998,9014, 9024,9040,8997,9013,
286 # Lost Temple (default?)
289 # SIDE: 5,5,5,5,0,2,1,0,7,7,7,4
293 8966,8982, 8962,8978, # H→~
295 16832..16835,16848..16851,16864..16867,16880..16883, # H→D ridge (ramp elsewhere?)
296 16384..16389, 16400..16405, 16416..16421, 16432..16437, # idem
297 8961,8977,9155,9171, # H←D→~
298 20160,20161,19984,19985,19986,19987,20000,20001,20002,20003, # D→H
299 9124,9140, 9122,9138, 9002,9018, 8960,8976, 9168,9152, 8995,9011, # H→D→~
300 9057,9073, # H→D→~ temple.6
307 4384,4400, 4352,4368, # X→H
310 18096,18097, 17968,17969, 18032,18033,18176,18177,
313 9347,9363,9953,9969,9473,9489,9251,9267,9633,9649,9505,9521,9410,9426,
314 9347,9363, 19840,19841, 19888,19889, 20208,20209,20224,20225,
315 20240,20241,20256,20257, 20272,20273,20288,20289, 19601,19600,
316 20304,20305,20320,20321, # tree
319 4673,4689,4737,4753,4705,4721,4769,4785, # rocks on D
320 4674,4690,4706,4722,4674,4690,4480,4496,4706,4722,4832,4848,4640,4656,
322 4482,4498, 4834,4850, 4642,4658,
323 ], 'd' => [4498,4642],
326 9281,9297,9313,9329, 9281,9297,9313,9329, 9538,9554,9570,9586,
327 9282,9298,9282,9298,9220,9236,9476,9492,9314,9330,9729,9745,9412,9428,9476,9492,9314,9330,9538,9554,9476,9492,9252,9268,
328 9508,9524,9668,9684,9600,9616,9508,9524,9666,9682,9601,9617,9570,9586,9508,9524,9408,9424,9700,9716,9632,9648,9698,9714,9282,9298,9314,9330,
329 9312,9328, 9412,9428, 9636,9652, 9634,9650,
330 9730,9746, 9761,9777,9762,9778, 9794,9810,
331 9732,9748, # temple.5
332 9603,9619, # temple.6
335 5859,5875, 5858,5874, # top edge
337 'O' => # temple center ornament
339 22320,22321,22336,22337,22352,22353,
340 22368,22369,22384,22385,22400,22401,
341 22464..22467,22480..22483,22496..22499,
342 22800,22801,22802,22803,22816,22817,22818,22819,22832,22833,22834,22835,
343 22752,22753,22754,22755,22768,22769,22770,22771,22784,22785,22786,22787,
345 'l' => [ # cut out shapes
346 22464,22467,22496,22499, 22321,22353, 22368,22400,
347 22800,22802,22803,22832,22835, 22819,
348 22752,22753,22755,22787,22784, 22768,
350 'D' => # underwater cave
352 21440,21441,21442,21443, 21456,21457,21458,21459,
353 21472,21473,21474,21475, 21488,21489,21490,21491,
355 'W' => [21456, 21472,21473,21474,21475],
356 'o' => [21442,21443],
357 '~' => [21488,21489, 21490,21491],
359 23632,23633,23648,23649,23664,23665, 23584,23585,23600,23601,23616,23617, # statue things on Q
361 'D' => [1824..1839], 'W' => [1840..1855], # D→~ bright transition
362 'D' => [1856..1871], 'd' => [1872..1887], # D→~ bleft ridge
363 'W' => [1888..1903], 'D' => [1904..1919], # D→~ bleft transition
364 '~' => [1920..1935], 'W' => [1936..1951], # w→~ bleft transition
365 'd' => [1952..1967], 'D' => [1968..1983],
366 'D' => [1984..1999], 'W' => [2000..2015],
367 'W' => [2016..2031], '~' => [2032..2047],
368 'D' => [8965,8981, 8964,8980, 9174,9158, 9010,8994], # H→D→~
369 'd' => [19936,19937], # D→~
370 '/' => [19905], 'd' => [19904], # H→D
372 5825,5841, 5857,5873, # J→R→map edge
377 23456,23457,23458,23459, 23472,23473,23474,23475,
378 23488,23489,23490,23491, 23504,23505,23506,23507,
380 23824,23825,23826,23827, 23840,23841,23842,23843, 23856,23857,23858,23859,
383 # Plains to Hill (v2.00 by Sir.Lupin)
386 # SIDE: 5,5,5,5,0,2,1,0,7,7,7,4
387 # FORC: 0,0,0,0,1,1,1,1,31,0,30,0,29,0,28,0,1,11,0,0
389 # H→D bottom left side (may be a ramp elsewhere)
390 16768,16769,16770,16771, 16784,16785,16786,16787,
391 16800,16801,16802,16803, 16816,16817,16818,16819,
392 # idem bottom right side
393 16960,16961,16962,16963, 16976,16977,16978,16979,
394 16992,16993,16994,16995, 17008,17009,17010,17011,
396 17088,17089,17090,17091, 17104,17105,17106,17107,
397 17120,17121,17122,17123, 17136,17137,17138,17139,
400 # Python (v1.3 by Terrance, modified by Forgotten_/KeSPA)
405 # SIDE: 5,5,5,5,0,2,1,0,7,7,7,4
406 # FORC: 0,0,0,0,1,1,1,1,2,0,4,0,5,0,6,0,1,14,15,15
408 24464,24465, 24448,24449
416 9154,9170,9027,9043,9157,9173, # H↔D
419 [19504,19505,19520,19521, 18720,18721,18736,18737], # rock things
422 [17488..17491,17504..17507,17520..17523,17536..17539],
423 'h' => [17490,17491,17507,17523,17536..17539],
425 4610, 4610,4626, 4481,4497,4513,4529, 4514,4530,
426 4417,4608,4624,4418, 4512,4528, 4450,4466,
427 4609,4625,4546,4562, 4833,4849, 4801,4817, 4800,4816,
430 4672,4688,4704,4720, # D↔O
433 4641,4657,4578,4594, 4802,4818, # O→D
436 9824,9840,9952,9968,9920,9936,9888,9904, 9796,9812,9924,9940,
437 9697,9713, 9890,9906, 9922,9938, 9792,9808,9856,9872,9826,9842,
439 'o' => [4738,4770], 'd' => [4754,4786],
440 '/' => [18256,18257,18272,18273], 'h' => [18288,18289], # obelisk
441 '/' => # sunken temple
442 [21024..21029, 21040..21045, 21056..21061, 21072..21077, 21088..21093,21104..21109],
443 '~' => [21024,21104,21109,21027,21059,21076,21072,21093,],
444 'W' => [21029, 21060,21045,21073,21090,21106,21092,21107,21075,21108,21058, 21056,21088,21105,21077],
445 'O' => # ruins similar to 22464
446 [22512..22515, 22528..22531, 22544..22547],
447 'l' => [22512,22515, 22544,22547],
448 'Q' => [17664,17665,17680,17681], # rock thing
449 'Q' => [17728,17729,17744,17745], # tree
450 '/' => [17808,17824,17840], 'h' => [17809,17825,17841],
451 '6' => [25025], # D»H ramp (between D and 17328 = level 4)
454 # Twilight Star (default)
455 # (all guessed from low res thumb)
456 'h' => [17584,17585], # H→D
457 'r' => [5827,5843, 5824,5840,5856,5872],
460 20912,20913,20928,20929, 20896,20897, 20944,
461 19440,19441,19456,19457, 20945,20960,20961,
464 'h' => [17760,17761,17776,17777, 17600,17601],
466 'o' => [19472..19475, 19488..19491],
468 21728,21729, 21744,21745, 21760,21761,
469 21776,21777, 19536,19537, 19538,19539, 19552..19555,
472 'O' => [19232..19235, 19248..19251], 'j' => [19218,19219],
473 'O' => [19938,19939,19954,19955],
474 'h' => [17792,17793],
476 'O' => [ # exotic/shiny things and other objects
477 18656,18657,18672,18673, 18496,18497,18512,18513,
478 18560..18563, 18576..18579, 18592..18595,
479 18528,18529,18544,18545, 18864,18865,
480 18608..18611,18624..18627,18640..18643,
481 18912..18915,18928..18931,18944..18947,
482 18816..18819,18832..18835,18848..18851,
483 18960..18963,18976..18979,18992..18995,
484 19008..19011,19024..19027,
485 19040..19043,19056..19059,
488 7811,7827,7873,7889,7809,7825,7875,7891,
489 8707,8723,8737,8753,7874,7890,7808,7824,7872,7888,7904,
490 8003,8019,8160,8176,7810,7826,
493 7843,7859,7905,7921,7841,7857,7907,7923,
494 8705,8721,7971,7987,8065,8081,8738,8754,7840,7856,
495 7920,7906,7922,7938,7954,7969,7985,8066,8082,8035,8051,
497 8034,8050,7936,7952,8033,8049,7937,7953,
500 '/' => [18080,18081],
501 'a' => [8032..8047], 'a' => [8048..8063], # #3 bottom left
502 'A' => [7841..7843], 'a' => [7857..7859], # #2 top left
503 'A' => [8000..8003], 'x' => [8016..8019], # #1 bottom right
504 'A' => [8064..8067], 'a' => [8080..8083], # #2 bottom left
505 'A' => [8224..8227], 'x' => [8240..8243], # #1 top right
506 'A' => [8288..8291], 'x' => [8304..8307], # #1 bottom right
507 'a' => [8355], 'A' => [8371], # #2 top right
508 'a' => [8322], 'A' => [8338], # #2 top right
509 'A' => [8512], 'x' => [8528], # #1 top right
510 'x' => [7811], 'A' => [7827],
511 'x' => [8096..8099], 'A' => [8112..8115],
512 'x' => [8163], 'a' => [8179],
513 'x' => [8192..8195], 'A' => [8208..8211],
514 'x' => [8128..8131], 'A' => [8144..8147],
516 'a' => [7939,7955], # #3 bottom right
517 'a' => [7968..7971], 'A' => [7984..7987],
519 'A' => [8257,8273, 8259,8275, 8162,8178],
523 # tau cross (v1.1 by Rose.Of.Dream/BW4eVeR)
525 # FORC: 0,0,0,1,1,1,1,1,7,0,6,0,5,0,4,0,1,15,15,15
526 # SIDE: 5,5,5,1,0,2,1,0,7,7,7,4
531 6176,6192, 20976,20977, 20592,20593, 21392,21393, 20800,20801,
532 20608,20609, 20624,20625, 20640,20641, 20992,20993, 21008,21009,
533 19922,19923,22070,22071, 19968..19971, 22230,
534 22133,22137,22272,22157,22288,22313,
535 20592,20593,21392,21393,20800,20801,20608,20609,20624,20625,20640,20641,20992,20993,21008,21009,19922,19923,
536 22070,22071,19968,19969,19970,19971,22230,22272,22157,22288,22313,6147,6163,6178,6194,3589,3605,22147,22162,22163,22164,
537 22180,22315,18400,18401,18336,18337,18192,18193,17730,17731,17826,17827,17746,17747,17842,17843,
538 17762,17763,17794,17795,17778,17779,17810,17811,17616,17617,
539 20688,20689,6145,6161,20736,20737,21408,21409,21410,21411,
540 20752,20753,21424,21425,21426,21427,21376,21377,20656,20657,
541 21312,21313,21314,21315,20672,20673,21328,21329,21330,21331,
542 6146,6162,21344,21345,21346,21347,21360,21361,21362,21363,6144,
547 22231..22235, 22246..22253, 22260..22269,
548 22273..22285, 22289..22301, 22306..22312, 22324..22328,
550 '4' => [22246..22253, 22273..22285, 22306..22312],
552 22084..22086, 22096..22103, 22112..22121, 22128..22139,
553 22144..22156, 22165..22171, 22181..22199,
555 '4' => [22084..22086, 22112..22121, 22144..22156, 22181..22199],
556 'r' => [22342,22343],
558 # Nostalgia (WGT13, v1.3 by Rose.of.Dream)
560 # FORC: 0,0,0,0,1,1,1,1,12,0,11,0,10,0,9,0,1,14,0,0
561 # SIDE: 5,5,5,5,0,2,1,0,7,7,7,4
570 while (my ($char, $matches) = splice @maptile, 0, 2) {
571 $tilechar{$_} = $char for @$matches;
573 while (my ($char, $matches) = splice @{$eratile{4}}, 0, 2) {
574 $tilechar{$_} = $char for @$matches;
577 my @mapunit = ( # character => width, height, ids
578 '$' => [2,1, 176..178], # minerals
579 '*' => [2,1, 188], # gas
580 '@' => [2,2, 214], # start pos
584 while (my ($char, $matches) = splice @mapunit, 0, 2) {
585 my @charinfo = ($char, splice @$matches, 0, 2);
586 $unitchar{$_} = \@charinfo for @$matches;
591 my $map = $self->tiles or return;
592 if ($self->{DEBUG}) {
594 tie my %unknown, 'Tie::IxHash';
595 defined $tilechar{$map->[$_]} or warn(sprintf
596 "unknown tile %d at (%d,%d)\n",
597 $map->[$_], $_ % $self->width, $_ / $self->width
598 ), $unknown{$map->[$_]} = $_ for 0 .. $#$map;
599 warn sprintf "unknown: %s\n", join ",", keys %unknown if keys %unknown;
601 $_ = defined $tilechar{$_} ? $tilechar{$_} : '?' for @$map;
603 my ($chr, $width, $height) = defined $unitchar{$_->{id}} ?
604 @{ $unitchar{$_->{id}} } : ('#', 1, 1);
605 for my $x ($_->{x} .. $_->{x} + $width - 1) {
606 for my $y ($_->{y} .. $_->{y} + $height - 1) {
607 $map->[$x + $y * $self->width] = $chr;
617 for (my $i = 0; $i < length $self->{UNIT}; $i += 36) {
618 # d1, d2, x*32, y*32, unitid, bytes1, playerid, bytes2, mineral, bytes3
619 my @pack = unpack "v5x6Cx3vx14", substr $self->{UNIT}, $i, 36;
637 my ($chr, $width, $height) = defined $unitchar{$_->{id}} ?
638 @{ $unitchar{delete $_->{id}} } : ('#', 1, 1);
640 $_->{width} = $width;
650 FF0000 0000FF 209070 88409C E87824 5C2C14 FFFFFF DCDC3C
651 0F930F FCFC8F EFCEBD 547CDC
653 12 => "pale green", "gray", "pale yellow", "cyan",
654 17 => "black", "neon blue",
655 21 => "lavender", "black",
660 for (unpack "C*", $self->{COLR}) {
661 push @players, $colormap[$_] || "? (#$_)";
668 return unpack "v", $self->{ERA};