summaryrefslogtreecommitdiff
path: root/jeu-test
diff options
context:
space:
mode:
authorDamien Appert <dappert>2010-09-12 10:38:09 +0000
committerDamien Appert <dappert>2010-09-12 10:38:09 +0000
commit6c63104925067616f97d009eccf1ed56cab37d96 (patch)
tree090a36ec0b85167a65e1f51ddc4778aa18561c4f /jeu-test
parent78da285c0890e5d60d10f21c5bbcb8f12a4b0575 (diff)
download2010-netlemmings-6c63104925067616f97d009eccf1ed56cab37d96.tar.gz
2010-netlemmings-6c63104925067616f97d009eccf1ed56cab37d96.tar.bz2
2010-netlemmings-6c63104925067616f97d009eccf1ed56cab37d96.zip
Moteur Lemming en Java
git-svn-id: file:///var/svn/2010-netlemmings/trunk@14 077b3477-7977-48bd-8428-443f22f7bfda
Diffstat (limited to 'jeu-test')
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/background.gifbin0 -> 18895 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/crc.ini486
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/disclaimer.htm53
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/extract.ini322
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/icon_32.pngbin0 -> 1957 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/lemmini.pngbin0 -> 95921 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@levelpack.ini295
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0000.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0001.difbin0 -> 70 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0002.difbin0 -> 70 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0003.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0004.difbin0 -> 70 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0005.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006b.difbin0 -> 136 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007b.difbin0 -> 117 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0010.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0011.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012b.difbin0 -> 134 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0013.difbin0 -> 70 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0014.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0015.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0016.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017b.difbin0 -> 122 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0020.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021.difbin0 -> 25 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021b.difbin0 -> 144 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022b.difbin0 -> 161 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0023.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024b.difbin0 -> 143 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0025.difbin0 -> 344 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0026.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027b.difbin0 -> 135 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030b.difbin0 -> 189 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031b.difbin0 -> 141 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032b.difbin0 -> 139 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033b.difbin0 -> 142 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034b.difbin0 -> 159 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0035.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0036.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0037.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0040.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041b.difbin0 -> 136 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042b.difbin0 -> 145 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043b.difbin0 -> 92 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0044.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0045.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046b.difbin0 -> 138 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047b.difbin0 -> 112 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0050.difbin0 -> 30 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051b.difbin0 -> 144 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0052.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0053.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0054.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0055.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0056.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057b.difbin0 -> 141 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060b.difbin0 -> 139 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061b.difbin0 -> 153 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062b.difbin0 -> 132 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063b.difbin0 -> 135 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0064.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065b.difbin0 -> 149 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0066.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067b.difbin0 -> 85 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067c.difbin0 -> 85 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0070.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071b.difbin0 -> 140 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0072.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073b.difbin0 -> 143 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0074.difbin0 -> 32 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0075.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0076.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077b.difbin0 -> 129 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080b.difbin0 -> 140 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0081.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082b.difbin0 -> 131 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083b.difbin0 -> 127 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084.difbin0 -> 30 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084b.difbin0 -> 117 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0085.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0086.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0087.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0090.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091b.difbin0 -> 88 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092b.difbin0 -> 135 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093b.difbin0 -> 112 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094.dif2
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094b.difbin0 -> 104 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095b.difbin0 -> 118 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0096.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097b.difbin0 -> 137 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@levelpack.ini248
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1000.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1001.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1002.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1003.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1004.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1005.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1006.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1007.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1010.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1011.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1012.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1013.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1014.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1015.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1016.difbin0 -> 238 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1017.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1020.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1021.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1022.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1023.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1024.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1025.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1026.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1027.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1030.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1031.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1032.dif2
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1033.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1034.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1035.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1036.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1037.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1040.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1041.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1042.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1043.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1044.difbin0 -> 30 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1045.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1046.difbin0 -> 49 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1047.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1050.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1051.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1052.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1053.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1054.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1055.difbin0 -> 93 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1056.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1057.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1060.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1061.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1062.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1063.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1064.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1065.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1066.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1067.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1070.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1071.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1072.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1073.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1074.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1075.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1076.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1077.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1080.difbin0 -> 25 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1081.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1082.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1083.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1084.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1085.dif2
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1086.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1087.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1090.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1091.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1092.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1093.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1094.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1095.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1096.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1097.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1100.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1101.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1102.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1103.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1104.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1105.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1106.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1107.difbin0 -> 25 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1110.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1111.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1112.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1113.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1114.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1115.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1116.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1117.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_17.ini123
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_18.ini131
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_19.ini101
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_20.ini128
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@border.gifbin0 -> 504 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@cursor.gifbin0 -> 705 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@explode.gifbin0 -> 529 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_0.gifbin0 -> 1603 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_1.gifbin0 -> 1662 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_10.gifbin0 -> 1647 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_11.gifbin0 -> 1928 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_12.gifbin0 -> 1642 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13.gifbin0 -> 1121 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13b.gifbin0 -> 1169 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_2.gifbin0 -> 1888 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_3.gifbin0 -> 1961 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_4.gifbin0 -> 1881 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_5.gifbin0 -> 1907 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6.gifbin0 -> 1921 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6_alt.gifbin0 -> 1674 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_7.gifbin0 -> 1988 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_8.gifbin0 -> 1891 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_9.gifbin0 -> 1957 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_13.gifbin0 -> 81 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_14.gifbin0 -> 873 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_15.gifbin0 -> 888 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_1.difbin0 -> 387 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_11.difbin0 -> 1396 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_13.difbin0 -> 1481 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_14.difbin0 -> 2254 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_15.difbin0 -> 2275 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_16.gifbin0 -> 1125 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_2.difbin0 -> 451 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_4.difbin0 -> 298 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_6.difbin0 -> 1206 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_8.difbin0 -> 930 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemmfont.gifbin0 -> 15691 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemming.ini105
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_10.difbin0 -> 151 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_11.difbin0 -> 69 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_13.difbin0 -> 100 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_14.difbin0 -> 244 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_15.difbin0 -> 186 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_6.gifbin0 -> 884 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@numfont.gifbin0 -> 1039 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/misc@select.gifbin0 -> 949 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@awesome.modbin0 -> 41886 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@beasti.modbin0 -> 45722 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@beastii.modbin0 -> 40286 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@cancan.modbin0 -> 36076 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@doggie.modbin0 -> 37544 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming1.modbin0 -> 46930 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming2.modbin0 -> 50906 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming3.modbin0 -> 46886 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@menace.modbin0 -> 49190 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@mountain.modbin0 -> 39974 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tenlemms.modbin0 -> 29198 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim1.modbin0 -> 48298 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim10.modbin0 -> 40924 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim2.modbin0 -> 35754 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim3.modbin0 -> 36226 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim4.modbin0 -> 38286 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim5.modbin0 -> 47718 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim6.modbin0 -> 26928 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim7.modbin0 -> 36550 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim8.modbin0 -> 37186 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tim9.modbin0 -> 39924 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune1.modbin0 -> 36180 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune2.modbin0 -> 28208 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune3.modbin0 -> 37620 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune4.modbin0 -> 39776 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune5.modbin0 -> 39850 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/music@tune6.modbin0 -> 34316 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/patch.ini996
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/sound@sound_4.difbin0 -> 3848 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brick.ini81
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_0.difbin0 -> 584 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_1.difbin0 -> 3823 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_6.difbin0 -> 3393 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_0.gifbin0 -> 206 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_3.gifbin0 -> 92 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_4.gifbin0 -> 92 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_5.gifbin0 -> 178 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_6.gifbin0 -> 137 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_7.gifbin0 -> 155 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble.ini87
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_0.difbin0 -> 1842 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_1.difbin0 -> 1900 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_10.difbin0 -> 304 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_16.difbin0 -> 269 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_2.difbin0 -> 634 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_3.difbin0 -> 568 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleo_1.difbin0 -> 4599 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_0.gifbin0 -> 199 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_3.gifbin0 -> 92 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_4.gifbin0 -> 92 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_5.gifbin0 -> 199 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_8.gifbin0 -> 167 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_9.gifbin0 -> 111 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal.ini87
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal_8.difbin0 -> 972 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalo_1.difbin0 -> 4090 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_0.gifbin0 -> 170 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_10.gifbin0 -> 112 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_5.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_6.gifbin0 -> 143 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_7.gifbin0 -> 127 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_9.gifbin0 -> 167 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt.ini87
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_0.difbin0 -> 2058 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_1.difbin0 -> 1129 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_16.dif1
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_17.difbin0 -> 98 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_22.difbin0 -> 567 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_27.difbin0 -> 430 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_29.difbin0 -> 720 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_30.difbin0 -> 453 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_31.difbin0 -> 234 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_32.difbin0 -> 148 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_33.difbin0 -> 196 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_34.difbin0 -> 164 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_35.difbin0 -> 302 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_36.difbin0 -> 294 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_37.difbin0 -> 268 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_38.difbin0 -> 179 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_39.difbin0 -> 133 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_40.difbin0 -> 88 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_41.difbin0 -> 291 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_44.difbin0 -> 289 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirto_1.difbin0 -> 4849 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_0.gifbin0 -> 177 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_10.gifbin0 -> 168 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_3.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_5.gifbin0 -> 144 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_6.gifbin0 -> 111 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_8.gifbin0 -> 123 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire.ini88
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_5.difbin0 -> 611 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_6.difbin0 -> 756 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_7.difbin0 -> 98 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_8.difbin0 -> 195 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_1.difbin0 -> 5152 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_6.difbin0 -> 4556 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_0.gifbin0 -> 192 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_10.gifbin0 -> 206 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_3.gifbin0 -> 100 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_4.gifbin0 -> 100 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_5.gifbin0 -> 163 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_7.gifbin0 -> 141 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_8.gifbin0 -> 207 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble.ini81
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_25.difbin0 -> 987 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_26.difbin0 -> 2460 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_46.difbin0 -> 119 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_55.difbin0 -> 1011 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_56.difbin0 -> 828 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleo_1.difbin0 -> 5472 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_0.gifbin0 -> 173 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_3.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_5.gifbin0 -> 173 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_8.gifbin0 -> 141 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_9.gifbin0 -> 138 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillar.ini88
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_1.difbin0 -> 4822 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_8.difbin0 -> 11165 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_0.gifbin0 -> 176 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_10.gifbin0 -> 154 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_3.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_5.gifbin0 -> 143 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_8.gifbin0 -> 130 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_9.gifbin0 -> 154 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock.ini95
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_10.difbin0 -> 162 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_12.difbin0 -> 155 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_6.difbin0 -> 950 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_9.difbin0 -> 898 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rocko_1.difbin0 -> 4776 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_0.gifbin0 -> 180 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_10.gifbin0 -> 167 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_3.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_5.gifbin0 -> 177 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_6.gifbin0 -> 96 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_8.gifbin0 -> 167 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow.ini81
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_16.difbin0 -> 491 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_20.difbin0 -> 966 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_24.difbin0 -> 1996 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_34.difbin0 -> 239 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_35.difbin0 -> 130 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_36.difbin0 -> 83 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_7.difbin0 -> 1546 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_8.difbin0 -> 1016 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowo_1.difbin0 -> 4779 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_0.gifbin0 -> 205 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_3.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_4.gifbin0 -> 116 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_5.gifbin0 -> 146 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_8.gifbin0 -> 164 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_9.gifbin0 -> 236 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special.ini39
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_0.gifbin0 -> 150889 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_1.gifbin0 -> 189310 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_2.gifbin0 -> 100786 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_3.gifbin0 -> 112592 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialo_1.difbin0 -> 4849 bytes
-rw-r--r--jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialom_0.gifbin0 -> 177 bytes
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/Diff.java641
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/Extract.java847
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/ExtractException.java42
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/ExtractLevel.java412
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/ExtractSPR.java439
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/FolderDialog.java337
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/GifEncoder.java551
-rw-r--r--jeu-test/Lemmini/0.84/src/Extract/OutputDialog.java207
-rw-r--r--jeu-test/Lemmini/0.84/src/GUI/GainDialog.java180
-rw-r--r--jeu-test/Lemmini/0.84/src/GUI/LegalDialog.java222
-rw-r--r--jeu-test/Lemmini/0.84/src/GUI/LevelCodeDialog.java256
-rw-r--r--jeu-test/Lemmini/0.84/src/GUI/PlayerDialog.java287
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Core.java314
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Explosion.java197
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/GameController.java1733
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/GroupBitfield.java47
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Icons.java224
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LemmCursor.java209
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LemmException.java41
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LemmFont.java184
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Lemming.java1691
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Level.java1026
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LevelCode.java120
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LevelInfo.java79
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/LevelPack.java208
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Mask.java288
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/MidiMusic.java135
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/MiniMap.java121
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/MiscGfx.java108
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/ModMusic.java187
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Music.java212
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/NumFont.java74
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Player.java178
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/ReplayLevelInfo.java77
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/ReplayStream.java382
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/ResourceException.java41
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/SpriteObject.java202
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/Stencil.java248
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/TextDialog.java439
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/TextScreen.java377
-rw-r--r--jeu-test/Lemmini/0.84/src/Game/UpdateListener.java29
-rw-r--r--jeu-test/Lemmini/0.84/src/GameUtil/Fader.java189
-rw-r--r--jeu-test/Lemmini/0.84/src/GameUtil/KeyRepeat.java178
-rw-r--r--jeu-test/Lemmini/0.84/src/GameUtil/Sound.java369
-rw-r--r--jeu-test/Lemmini/0.84/src/GameUtil/Sprite.java270
-rw-r--r--jeu-test/Lemmini/0.84/src/Lemmini.java1688
-rw-r--r--jeu-test/Lemmini/0.84/src/Tools/JFileFilter.java293
-rw-r--r--jeu-test/Lemmini/0.84/src/Tools/MicrosecondTimer.java126
-rw-r--r--jeu-test/Lemmini/0.84/src/Tools/Props.java314
-rw-r--r--jeu-test/Lemmini/0.84/src/Tools/ToolBox.java274
-rw-r--r--jeu-test/Lemmini/0.84/src/micromod/Micromod.java560
486 files changed, 21820 insertions, 0 deletions
diff --git a/jeu-test/Lemmini/0.84/bin_copy/background.gif b/jeu-test/Lemmini/0.84/bin_copy/background.gif
new file mode 100644
index 0000000..e068cad
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/background.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/crc.ini b/jeu-test/Lemmini/0.84/bin_copy/crc.ini
new file mode 100644
index 0000000..c6dded0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/crc.ini
@@ -0,0 +1,486 @@
+crc_0 = GFX/BLINK.SPR, 8256, 0x1a3f9bb6
+crc_1 = GFX/BLINK2.SPR, 8256, 0x9dfe9bb8
+crc_2 = GFX/BLINK3.SPR, 8392, 0x5e23f45d
+crc_3 = GFX/DBCOLL.BMP, 1334, 0xe6700efe
+crc_4 = GFX/DIALOG.BMP, 22070, 0x248cdfa1
+crc_5 = GFX/ES.SPR, 162867, 0xdee15ed9
+crc_6 = GFX/FE.PAL, 1030, 0x1368601c
+crc_7 = GFX/FE.SPR, 112254, 0xcdda5180
+crc_8 = GFX/FEFONT.SPR, 39686, 0x70b743df
+crc_9 = GFX/ICONS.BMP, 60278, 0x31d2d29a
+crc_10 = GFX/ICO_AXE.SPR, 6880, 0x88b2bcb3
+crc_11 = GFX/ICO_BASH.SPR, 7463, 0x84d2aac2
+crc_12 = GFX/ICO_BILD.SPR, 6902, 0xc8678edd
+crc_13 = GFX/ICO_BLOK.SPR, 14360, 0x11e6c321
+crc_14 = GFX/ICO_BOMB.SPR, 6425, 0x243bb6de
+crc_15 = GFX/ICO_CLIM.SPR, 4029, 0xab157da6
+crc_16 = GFX/ICO_DIG.SPR, 5859, 0xe5cdda66
+crc_17 = GFX/ICO_FF.SPR, 11624, 0x9fc4d21
+crc_18 = GFX/ICO_FLOT.SPR, 10810, 0xa9526727
+crc_19 = GFX/ICO_MINS.SPR, 326, 0xc3aa73a6
+crc_20 = GFX/ICO_NUKE.SPR, 5990, 0xae761f7b
+crc_21 = GFX/ICO_PAWS.SPR, 4212, 0x9d59b557
+crc_22 = GFX/ICO_PLUS.SPR, 520, 0xdab99803
+crc_23 = GFX/LEMMBIG.SPR, 77621, 0x811bd342
+crc_24 = GFX/LEMMING.BMP, 19558, 0x406a5d8c
+crc_25 = GFX/LEMMSMA.SPR, 27857, 0x3ac4ca36
+crc_26 = GFX/LOGO.BMP, 28008, 0xf7e3f4b
+crc_27 = GFX/NOBOXLM.BMP, 328758, 0xecd49a73
+crc_28 = GFX/NOBOXLM2.BMP, 746038, 0xa97cfa47
+crc_29 = GFX/RR.SPR, 1231, 0xcd259b4
+crc_30 = GFX/SLAPL.SPR, 27122, 0x639b803d
+crc_31 = GFX/SLAPR.SPR, 27099, 0xcd127e97
+crc_32 = GFX/VSFX.BMP, 10198, 0x8984cb25
+crc_33 = GFX/VSFXBIG.SPR, 10502, 0x8df66460
+crc_34 = GFX/VSFXSMA.SPR, 2898, 0x547c6454
+crc_35 = GFX/WLEMLOGO.BMP, 107030, 0x7cca253b
+crc_36 = LEVEL/OHNO/LS1000.BMP, 4118, 0x930d5da9
+crc_37 = LEVEL/OHNO/LS1001.BMP, 4118, 0x1aaf68ad
+crc_38 = LEVEL/OHNO/LS1002.BMP, 4118, 0x9d20742f
+crc_39 = LEVEL/OHNO/LS1003.BMP, 4118, 0xe90e82f6
+crc_40 = LEVEL/OHNO/LS1004.BMP, 4118, 0x9d79d171
+crc_41 = LEVEL/OHNO/LS1005.BMP, 4118, 0x59468eab
+crc_42 = LEVEL/OHNO/LS1006.BMP, 4118, 0x5a8a3764
+crc_43 = LEVEL/OHNO/LS1007.BMP, 4118, 0xf60b571b
+crc_44 = LEVEL/OHNO/LS1010.BMP, 4118, 0x5ed6333c
+crc_45 = LEVEL/OHNO/LS1011.BMP, 4118, 0xc781972f
+crc_46 = LEVEL/OHNO/LS1012.BMP, 4118, 0x2e8b9415
+crc_47 = LEVEL/OHNO/LS1013.BMP, 4118, 0x8c28345
+crc_48 = LEVEL/OHNO/LS1014.BMP, 4118, 0x7b317e80
+crc_49 = LEVEL/OHNO/LS1015.BMP, 4118, 0x98c7a184
+crc_50 = LEVEL/OHNO/LS1016.BMP, 4118, 0xd066d979
+crc_51 = LEVEL/OHNO/LS1017.BMP, 4118, 0x9103bb1e
+crc_52 = LEVEL/OHNO/LS1020.BMP, 4118, 0xa9e1420c
+crc_53 = LEVEL/OHNO/LS1021.BMP, 4118, 0x53cf81fd
+crc_54 = LEVEL/OHNO/LS1022.BMP, 4118, 0x21833c1a
+crc_55 = LEVEL/OHNO/LS1023.BMP, 4118, 0x5c44f4e1
+crc_56 = LEVEL/OHNO/LS1024.BMP, 4118, 0x5d936d87
+crc_57 = LEVEL/OHNO/LS1025.BMP, 4118, 0x80e1a81b
+crc_58 = LEVEL/OHNO/LS1026.BMP, 4118, 0x7269728e
+crc_59 = LEVEL/OHNO/LS1027.BMP, 4118, 0xeaf47d28
+crc_60 = LEVEL/OHNO/LS1030.BMP, 4118, 0x9777013f
+crc_61 = LEVEL/OHNO/LS1031.BMP, 4118, 0xbe6aa248
+crc_62 = LEVEL/OHNO/LS1032.BMP, 4118, 0xb2ec1603
+crc_63 = LEVEL/OHNO/LS1033.BMP, 4118, 0x12b3b15e
+crc_64 = LEVEL/OHNO/LS1034.BMP, 4118, 0x907d28c4
+crc_65 = LEVEL/OHNO/LS1035.BMP, 4118, 0x841e2f0b
+crc_66 = LEVEL/OHNO/LS1036.BMP, 4118, 0x5ec36983
+crc_67 = LEVEL/OHNO/LS1037.BMP, 4118, 0xcbc36cfe
+crc_68 = LEVEL/OHNO/LS1040.BMP, 4118, 0xa60ab2b3
+crc_69 = LEVEL/OHNO/LS1041.BMP, 4118, 0xc3916961
+crc_70 = LEVEL/OHNO/LS1042.BMP, 4118, 0xdd9f6792
+crc_71 = LEVEL/OHNO/LS1043.BMP, 4118, 0xc9bf03ea
+crc_72 = LEVEL/OHNO/LS1044.BMP, 4118, 0xa29f76d1
+crc_73 = LEVEL/OHNO/LS1045.BMP, 4118, 0xcbe61631
+crc_74 = LEVEL/OHNO/LS1046.BMP, 4118, 0x3d718b64
+crc_75 = LEVEL/OHNO/LS1047.BMP, 4118, 0xe209395
+crc_76 = LEVEL/OHNO/LS1050.BMP, 4118, 0x3ed9f446
+crc_77 = LEVEL/OHNO/LS1051.BMP, 4118, 0xb5c0e737
+crc_78 = LEVEL/OHNO/LS1052.BMP, 4118, 0x61aba9eb
+crc_79 = LEVEL/OHNO/LS1053.BMP, 4118, 0x30258108
+crc_80 = LEVEL/OHNO/LS1054.BMP, 4118, 0x64f49b3e
+crc_81 = LEVEL/OHNO/LS1055.BMP, 4118, 0x860cd350
+crc_82 = LEVEL/OHNO/LS1056.BMP, 4118, 0xd25b7913
+crc_83 = LEVEL/OHNO/LS1057.BMP, 9078, 0x4a186388
+crc_84 = LEVEL/OHNO/LS1060.BMP, 4118, 0x6094bbde
+crc_85 = LEVEL/OHNO/LS1061.BMP, 4118, 0x42287518
+crc_86 = LEVEL/OHNO/LS1062.BMP, 4118, 0xb84fc497
+crc_87 = LEVEL/OHNO/LS1063.BMP, 4118, 0x6715a3dc
+crc_88 = LEVEL/OHNO/LS1064.BMP, 4118, 0x34103e32
+crc_89 = LEVEL/OHNO/LS1065.BMP, 4118, 0xec46c680
+crc_90 = LEVEL/OHNO/LS1066.BMP, 4118, 0xedb4a1a2
+crc_91 = LEVEL/OHNO/LS1067.BMP, 4118, 0x88f7e50c
+crc_92 = LEVEL/OHNO/LS1070.BMP, 4118, 0x669cef5f
+crc_93 = LEVEL/OHNO/LS1071.BMP, 4118, 0xc2ea9890
+crc_94 = LEVEL/OHNO/LS1072.BMP, 4118, 0x5ddf0d05
+crc_95 = LEVEL/OHNO/LS1073.BMP, 4118, 0xc72bbed6
+crc_96 = LEVEL/OHNO/LS1074.BMP, 4118, 0x4e9ed69b
+crc_97 = LEVEL/OHNO/LS1075.BMP, 4118, 0x4f5eb590
+crc_98 = LEVEL/OHNO/LS1076.BMP, 4118, 0x92379da1
+crc_99 = LEVEL/OHNO/LS1077.BMP, 4118, 0xed5210ee
+crc_100 = LEVEL/OHNO/LS1080.BMP, 4118, 0xef4276e4
+crc_101 = LEVEL/OHNO/LS1081.BMP, 4118, 0xc01b4ce4
+crc_102 = LEVEL/OHNO/LS1082.BMP, 4118, 0xb816605e
+crc_103 = LEVEL/OHNO/LS1083.BMP, 4118, 0x5f7679a1
+crc_104 = LEVEL/OHNO/LS1084.BMP, 4118, 0x959f5245
+crc_105 = LEVEL/OHNO/LS1085.BMP, 4118, 0xaf83e356
+crc_106 = LEVEL/OHNO/LS1086.BMP, 4118, 0x178eb734
+crc_107 = LEVEL/OHNO/LS1087.BMP, 4118, 0xb0159db0
+crc_108 = LEVEL/OHNO/LS1090.BMP, 4118, 0x5893824d
+crc_109 = LEVEL/OHNO/LS1091.BMP, 4118, 0xbd576e40
+crc_110 = LEVEL/OHNO/LS1092.BMP, 4118, 0x8b5097f0
+crc_111 = LEVEL/OHNO/LS1093.BMP, 4118, 0x9dfdda3f
+crc_112 = LEVEL/OHNO/LS1094.BMP, 4118, 0xaeda9e47
+crc_113 = LEVEL/OHNO/LS1095.BMP, 4118, 0xf36ce981
+crc_114 = LEVEL/OHNO/LS1096.BMP, 4118, 0x4d3066d6
+crc_115 = LEVEL/OHNO/LS1097.BMP, 4118, 0xd1e6761e
+crc_116 = LEVEL/OHNO/LS1100.BMP, 4118, 0x8cc38092
+crc_117 = LEVEL/OHNO/LS1101.BMP, 4118, 0x3587b138
+crc_118 = LEVEL/OHNO/LS1102.BMP, 4118, 0xd20058a6
+crc_119 = LEVEL/OHNO/LS1103.BMP, 4118, 0xcb7e54a7
+crc_120 = LEVEL/OHNO/LS1104.BMP, 4118, 0xaabcc39b
+crc_121 = LEVEL/OHNO/LS1105.BMP, 4118, 0x785820e2
+crc_122 = LEVEL/OHNO/LS1106.BMP, 4118, 0x9e62dd14
+crc_123 = LEVEL/OHNO/LS1107.BMP, 4118, 0xf313937a
+crc_124 = LEVEL/OHNO/LS1110.BMP, 4118, 0xf189a31c
+crc_125 = LEVEL/OHNO/LS1111.BMP, 4118, 0x370ff260
+crc_126 = LEVEL/OHNO/LS1112.BMP, 4118, 0xf44399df
+crc_127 = LEVEL/OHNO/LS1113.BMP, 4118, 0x5c060eca
+crc_128 = LEVEL/OHNO/LS1114.BMP, 4118, 0xd70f8dc0
+crc_129 = LEVEL/OHNO/LS1115.BMP, 4118, 0x3f79e0a7
+crc_130 = LEVEL/OHNO/LS1116.BMP, 4118, 0xb9fb0e00
+crc_131 = LEVEL/OHNO/LS1117.BMP, 4118, 0x4aebee06
+crc_132 = LEVEL/OHNO/LVL1000.LVL, 2048, 0xc48f796a
+crc_133 = LEVEL/OHNO/LVL1001.LVL, 2048, 0xbd1f3589
+crc_134 = LEVEL/OHNO/LVL1002.LVL, 2048, 0xc0b687d0
+crc_135 = LEVEL/OHNO/LVL1003.LVL, 2048, 0x257fc730
+crc_136 = LEVEL/OHNO/LVL1004.LVL, 2048, 0xc63a548e
+crc_137 = LEVEL/OHNO/LVL1005.LVL, 2048, 0xd54e5b3b
+crc_138 = LEVEL/OHNO/LVL1006.LVL, 2048, 0x258a542e
+crc_139 = LEVEL/OHNO/LVL1007.LVL, 2048, 0x31cef68d
+crc_140 = LEVEL/OHNO/LVL1010.LVL, 2048, 0xa188efe4
+crc_141 = LEVEL/OHNO/LVL1011.LVL, 2048, 0x4f1036bb
+crc_142 = LEVEL/OHNO/LVL1012.LVL, 2048, 0x5698a36b
+crc_143 = LEVEL/OHNO/LVL1013.LVL, 2048, 0xf757d198
+crc_144 = LEVEL/OHNO/LVL1014.LVL, 2048, 0x61da8bc6
+crc_145 = LEVEL/OHNO/LVL1015.LVL, 2048, 0xbf9d7f67
+crc_146 = LEVEL/OHNO/LVL1016.LVL, 2048, 0x2c63aa88
+crc_147 = LEVEL/OHNO/LVL1017.LVL, 2048, 0xa992b46
+crc_148 = LEVEL/OHNO/LVL1020.LVL, 2048, 0x8d973860
+crc_149 = LEVEL/OHNO/LVL1021.LVL, 2048, 0xecbd9f14
+crc_150 = LEVEL/OHNO/LVL1022.LVL, 2048, 0xcb3dd3c2
+crc_151 = LEVEL/OHNO/LVL1023.LVL, 2048, 0xec70d965
+crc_152 = LEVEL/OHNO/LVL1024.LVL, 2048, 0xca743e76
+crc_153 = LEVEL/OHNO/LVL1025.LVL, 2048, 0xb3727961
+crc_154 = LEVEL/OHNO/LVL1026.LVL, 2048, 0x9ac2b7d9
+crc_155 = LEVEL/OHNO/LVL1027.LVL, 2048, 0xd3547be0
+crc_156 = LEVEL/OHNO/LVL1030.LVL, 2048, 0x94fed4ec
+crc_157 = LEVEL/OHNO/LVL1031.LVL, 2048, 0x2243ac96
+crc_158 = LEVEL/OHNO/LVL1032.LVL, 2048, 0x3d39704b
+crc_159 = LEVEL/OHNO/LVL1033.LVL, 2048, 0x51e8374d
+crc_160 = LEVEL/OHNO/LVL1034.LVL, 2048, 0xf6a210cd
+crc_161 = LEVEL/OHNO/LVL1035.LVL, 2048, 0x149201f5
+crc_162 = LEVEL/OHNO/LVL1036.LVL, 2048, 0xccb11176
+crc_163 = LEVEL/OHNO/LVL1037.LVL, 2048, 0xab699a74
+crc_164 = LEVEL/OHNO/LVL1040.LVL, 2048, 0x3e6624c6
+crc_165 = LEVEL/OHNO/LVL1041.LVL, 2048, 0x16edfe76
+crc_166 = LEVEL/OHNO/LVL1042.LVL, 2048, 0xb1bdeb82
+crc_167 = LEVEL/OHNO/LVL1043.LVL, 2048, 0x6318b22e
+crc_168 = LEVEL/OHNO/LVL1044.LVL, 2048, 0x851eb460
+crc_169 = LEVEL/OHNO/LVL1045.LVL, 2048, 0x9949469
+crc_170 = LEVEL/OHNO/LVL1046.LVL, 2048, 0xdb2d92e5
+crc_171 = LEVEL/OHNO/LVL1047.LVL, 2048, 0x98d4b31
+crc_172 = LEVEL/OHNO/LVL1050.LVL, 2048, 0x1d9b66b3
+crc_173 = LEVEL/OHNO/LVL1051.LVL, 2048, 0xc7618df9
+crc_174 = LEVEL/OHNO/LVL1052.LVL, 2048, 0x70479c6e
+crc_175 = LEVEL/OHNO/LVL1053.LVL, 2048, 0xc6f28f43
+crc_176 = LEVEL/OHNO/LVL1054.LVL, 2048, 0xc950e8a9
+crc_177 = LEVEL/OHNO/LVL1055.LVL, 2048, 0xae44c526
+crc_178 = LEVEL/OHNO/LVL1056.LVL, 2048, 0x80d5fd06
+crc_179 = LEVEL/OHNO/LVL1057.LVL, 2048, 0xd99a210e
+crc_180 = LEVEL/OHNO/LVL1060.LVL, 2048, 0xbc7eb2df
+crc_181 = LEVEL/OHNO/LVL1061.LVL, 2048, 0x705c3f82
+crc_182 = LEVEL/OHNO/LVL1062.LVL, 2048, 0xa2218307
+crc_183 = LEVEL/OHNO/LVL1063.LVL, 2048, 0x8d1c5af4
+crc_184 = LEVEL/OHNO/LVL1064.LVL, 2048, 0x68b6621d
+crc_185 = LEVEL/OHNO/LVL1065.LVL, 2048, 0xe03e02eb
+crc_186 = LEVEL/OHNO/LVL1066.LVL, 2048, 0x79c8454b
+crc_187 = LEVEL/OHNO/LVL1067.LVL, 2048, 0x566f3276
+crc_188 = LEVEL/OHNO/LVL1070.LVL, 2048, 0x478f0ced
+crc_189 = LEVEL/OHNO/LVL1071.LVL, 2048, 0xe3861cda
+crc_190 = LEVEL/OHNO/LVL1072.LVL, 2048, 0xf1e610a1
+crc_191 = LEVEL/OHNO/LVL1073.LVL, 2048, 0x980dfa2c
+crc_192 = LEVEL/OHNO/LVL1074.LVL, 2048, 0x6724f76f
+crc_193 = LEVEL/OHNO/LVL1075.LVL, 2048, 0xc98c2148
+crc_194 = LEVEL/OHNO/LVL1076.LVL, 2048, 0x5cbbac84
+crc_195 = LEVEL/OHNO/LVL1077.LVL, 2048, 0xe91160c9
+crc_196 = LEVEL/OHNO/LVL1080.LVL, 2048, 0x501376ce
+crc_197 = LEVEL/OHNO/LVL1081.LVL, 2048, 0xabe78ac0
+crc_198 = LEVEL/OHNO/LVL1082.LVL, 2048, 0xe9256147
+crc_199 = LEVEL/OHNO/LVL1083.LVL, 2048, 0x4f3f6af9
+crc_200 = LEVEL/OHNO/LVL1084.LVL, 2048, 0x11d328a5
+crc_201 = LEVEL/OHNO/LVL1085.LVL, 2048, 0x124dc5d1
+crc_202 = LEVEL/OHNO/LVL1086.LVL, 2048, 0x7f4baa1
+crc_203 = LEVEL/OHNO/LVL1087.LVL, 2048, 0x8eb65cde
+crc_204 = LEVEL/OHNO/LVL1090.LVL, 2048, 0xf392c155
+crc_205 = LEVEL/OHNO/LVL1091.LVL, 2048, 0xb32cc62e
+crc_206 = LEVEL/OHNO/LVL1092.LVL, 2048, 0x24195c04
+crc_207 = LEVEL/OHNO/LVL1093.LVL, 2048, 0x523cc5ea
+crc_208 = LEVEL/OHNO/LVL1094.LVL, 2048, 0x2c986b0a
+crc_209 = LEVEL/OHNO/LVL1095.LVL, 2048, 0x24443edc
+crc_210 = LEVEL/OHNO/LVL1096.LVL, 2048, 0xb76ea39
+crc_211 = LEVEL/OHNO/LVL1097.LVL, 2048, 0x5a8f8c11
+crc_212 = LEVEL/OHNO/LVL1100.LVL, 2048, 0x7e9f377b
+crc_213 = LEVEL/OHNO/LVL1101.LVL, 2048, 0xbbe100a3
+crc_214 = LEVEL/OHNO/LVL1102.LVL, 2048, 0x924aeab1
+crc_215 = LEVEL/OHNO/LVL1103.LVL, 2048, 0x5e8ec08a
+crc_216 = LEVEL/OHNO/LVL1104.LVL, 2048, 0x103ae16d
+crc_217 = LEVEL/OHNO/LVL1105.LVL, 2048, 0x8df7c47d
+crc_218 = LEVEL/OHNO/LVL1106.LVL, 2048, 0x8ca78ecc
+crc_219 = LEVEL/OHNO/LVL1107.LVL, 2048, 0x623b413c
+crc_220 = LEVEL/OHNO/LVL1110.LVL, 2048, 0x486255c4
+crc_221 = LEVEL/OHNO/LVL1111.LVL, 2048, 0xdc9ee694
+crc_222 = LEVEL/OHNO/LVL1112.LVL, 2048, 0x9f03f5d0
+crc_223 = LEVEL/OHNO/LVL1113.LVL, 2048, 0xd3abcc54
+crc_224 = LEVEL/OHNO/LVL1114.LVL, 2048, 0x4f6c5cae
+crc_225 = LEVEL/OHNO/LVL1115.LVL, 2048, 0xeed5b4f1
+crc_226 = LEVEL/OHNO/LVL1116.LVL, 2048, 0xfb3a38f3
+crc_227 = LEVEL/OHNO/LVL1117.LVL, 2048, 0x517b3738
+crc_228 = LEVEL/ORIG/LS0000.BMP, 4118, 0x5c3d7b9c
+crc_229 = LEVEL/ORIG/LS0001.BMP, 4118, 0x86c1a97
+crc_230 = LEVEL/ORIG/LS0002.BMP, 4118, 0x12cb1a98
+crc_231 = LEVEL/ORIG/LS0003.BMP, 4118, 0xd1fcb452
+crc_232 = LEVEL/ORIG/LS0004.BMP, 4118, 0x71d91ada
+crc_233 = LEVEL/ORIG/LS0005.BMP, 4118, 0xa9bf6502
+crc_234 = LEVEL/ORIG/LS0006.BMP, 4118, 0xcdd65f54
+crc_235 = LEVEL/ORIG/LS0007.BMP, 4118, 0xaa529f7c
+crc_236 = LEVEL/ORIG/LS0010.BMP, 4118, 0xe03e30aa
+crc_237 = LEVEL/ORIG/LS0011.BMP, 4118, 0xf37322c9
+crc_238 = LEVEL/ORIG/LS0012.BMP, 4118, 0x45015175
+crc_239 = LEVEL/ORIG/LS0013.BMP, 4118, 0xc8591af6
+crc_240 = LEVEL/ORIG/LS0014.BMP, 4118, 0xefb19db9
+crc_241 = LEVEL/ORIG/LS0015.BMP, 4118, 0xb265b4e0
+crc_242 = LEVEL/ORIG/LS0016.BMP, 4118, 0xf9c40f1f
+crc_243 = LEVEL/ORIG/LS0017.BMP, 4118, 0xff094a1d
+crc_244 = LEVEL/ORIG/LS0020.BMP, 4118, 0x3fa521c5
+crc_245 = LEVEL/ORIG/LS0021.BMP, 4118, 0x4e5d90c0
+crc_246 = LEVEL/ORIG/LS0022.BMP, 4118, 0x9b66c503
+crc_247 = LEVEL/ORIG/LS0023.BMP, 4118, 0xc754ad42
+crc_248 = LEVEL/ORIG/LS0024.BMP, 4118, 0x76d95d9f
+crc_249 = LEVEL/ORIG/LS0025.BMP, 4118, 0x145c4282
+crc_250 = LEVEL/ORIG/LS0026.BMP, 4118, 0x8df672cf
+crc_251 = LEVEL/ORIG/LS0027.BMP, 4118, 0x36a707fc
+crc_252 = LEVEL/ORIG/LS0030.BMP, 4118, 0x79722a8b
+crc_253 = LEVEL/ORIG/LS0031.BMP, 4118, 0x2c103204
+crc_254 = LEVEL/ORIG/LS0032.BMP, 4118, 0xc68465d
+crc_255 = LEVEL/ORIG/LS0033.BMP, 4118, 0x99e3a3de
+crc_256 = LEVEL/ORIG/LS0034.BMP, 4118, 0x2b4dfca7
+crc_257 = LEVEL/ORIG/LS0035.BMP, 4118, 0x3baee21d
+crc_258 = LEVEL/ORIG/LS0036.BMP, 4118, 0x30a7a06
+crc_259 = LEVEL/ORIG/LS0037.BMP, 4118, 0x9f407aca
+crc_260 = LEVEL/ORIG/LS0040.BMP, 4118, 0xa01a7504
+crc_261 = LEVEL/ORIG/LS0041.BMP, 4118, 0x772b82e
+crc_262 = LEVEL/ORIG/LS0042.BMP, 4118, 0xc8ccaea4
+crc_263 = LEVEL/ORIG/LS0043.BMP, 4118, 0x6e924919
+crc_264 = LEVEL/ORIG/LS0044.BMP, 4118, 0xeebb7f3a
+crc_265 = LEVEL/ORIG/LS0045.BMP, 4118, 0x2fb55c9b
+crc_266 = LEVEL/ORIG/LS0046.BMP, 4118, 0xd3aef1bb
+crc_267 = LEVEL/ORIG/LS0047.BMP, 4118, 0x717c3f8a
+crc_268 = LEVEL/ORIG/LS0050.BMP, 4118, 0x669597c7
+crc_269 = LEVEL/ORIG/LS0051.BMP, 4118, 0x2a410625
+crc_270 = LEVEL/ORIG/LS0052.BMP, 4118, 0xe0cd51ef
+crc_271 = LEVEL/ORIG/LS0053.BMP, 4118, 0x4e204534
+crc_272 = LEVEL/ORIG/LS0054.BMP, 4118, 0x1985e43b
+crc_273 = LEVEL/ORIG/LS0055.BMP, 4118, 0x7e38e385
+crc_274 = LEVEL/ORIG/LS0056.BMP, 4118, 0x2df4ffe5
+crc_275 = LEVEL/ORIG/LS0057.BMP, 4118, 0x17a1ca7e
+crc_276 = LEVEL/ORIG/LS0060.BMP, 4118, 0x8d3942b
+crc_277 = LEVEL/ORIG/LS0061.BMP, 4118, 0x37acf9a2
+crc_278 = LEVEL/ORIG/LS0062.BMP, 4118, 0x8075ca67
+crc_279 = LEVEL/ORIG/LS0063.BMP, 4118, 0xcbe9e513
+crc_280 = LEVEL/ORIG/LS0064.BMP, 4118, 0x78d7bf3b
+crc_281 = LEVEL/ORIG/LS0065.BMP, 4118, 0xfa8fbf07
+crc_282 = LEVEL/ORIG/LS0066.BMP, 4118, 0x7e97e79a
+crc_283 = LEVEL/ORIG/LS0067.BMP, 4118, 0x829c0e6f
+crc_284 = LEVEL/ORIG/LS0070.BMP, 4118, 0x71a28d81
+crc_285 = LEVEL/ORIG/LS0071.BMP, 4118, 0xf07d194c
+crc_286 = LEVEL/ORIG/LS0072.BMP, 4118, 0x707a8512
+crc_287 = LEVEL/ORIG/LS0073.BMP, 4118, 0x49817f2b
+crc_288 = LEVEL/ORIG/LS0074.BMP, 4118, 0x296339b7
+crc_289 = LEVEL/ORIG/LS0075.BMP, 4118, 0x883d3d73
+crc_290 = LEVEL/ORIG/LS0076.BMP, 4118, 0xf65fbc2c
+crc_291 = LEVEL/ORIG/LS0077.BMP, 4118, 0xda9cf683
+crc_292 = LEVEL/ORIG/LS0080.BMP, 4118, 0x1a8b011f
+crc_293 = LEVEL/ORIG/LS0081.BMP, 4118, 0xa9126d54
+crc_294 = LEVEL/ORIG/LS0082.BMP, 4118, 0xf8d699a2
+crc_295 = LEVEL/ORIG/LS0083.BMP, 4118, 0xf173d2c7
+crc_296 = LEVEL/ORIG/LS0084.BMP, 4118, 0x529ccb01
+crc_297 = LEVEL/ORIG/LS0085.BMP, 4118, 0x6f3f38af
+crc_298 = LEVEL/ORIG/LS0086.BMP, 4118, 0x5790d873
+crc_299 = LEVEL/ORIG/LS0087.BMP, 4118, 0xb44c391a
+crc_300 = LEVEL/ORIG/LS0090.BMP, 4118, 0xde5add3f
+crc_301 = LEVEL/ORIG/LS0091.BMP, 4118, 0x1f3c6e97
+crc_302 = LEVEL/ORIG/LS0092.BMP, 4118, 0x8e87f65e
+crc_303 = LEVEL/ORIG/LS0093.BMP, 4118, 0x9f3f557e
+crc_304 = LEVEL/ORIG/LS0094.BMP, 4118, 0xebc7b53f
+crc_305 = LEVEL/ORIG/LS0095.BMP, 4118, 0xd78793d7
+crc_306 = LEVEL/ORIG/LS0096.BMP, 4118, 0xbb205959
+crc_307 = LEVEL/ORIG/LS0097.BMP, 4118, 0xfdf060da
+crc_308 = LEVEL/ORIG/LVL0000.LVL, 2048, 0xa2318e48
+crc_309 = LEVEL/ORIG/LVL0001.LVL, 2048, 0x23b14623
+crc_310 = LEVEL/ORIG/LVL0002.LVL, 2048, 0x7569438f
+crc_311 = LEVEL/ORIG/LVL0003.LVL, 2048, 0x21a67be3
+crc_312 = LEVEL/ORIG/LVL0004.LVL, 2048, 0xfaa145a2
+crc_313 = LEVEL/ORIG/LVL0005.LVL, 2048, 0x70d28f8b
+crc_314 = LEVEL/ORIG/LVL0006.LVL, 2048, 0x14de878a
+crc_315 = LEVEL/ORIG/LVL0007.LVL, 2048, 0x98ac84f9
+crc_316 = LEVEL/ORIG/LVL0010.LVL, 2048, 0x76920ee
+crc_317 = LEVEL/ORIG/LVL0011.LVL, 2048, 0xd4511f14
+crc_318 = LEVEL/ORIG/LVL0012.LVL, 2048, 0x8a72c7a5
+crc_319 = LEVEL/ORIG/LVL0013.LVL, 2048, 0x92fc4523
+crc_320 = LEVEL/ORIG/LVL0014.LVL, 2048, 0xa99cb7f2
+crc_321 = LEVEL/ORIG/LVL0015.LVL, 2048, 0x7888efb
+crc_322 = LEVEL/ORIG/LVL0016.LVL, 2048, 0xf31c5c53
+crc_323 = LEVEL/ORIG/LVL0017.LVL, 2048, 0xa909cbc2
+crc_324 = LEVEL/ORIG/LVL0020.LVL, 2048, 0x48bdc5ed
+crc_325 = LEVEL/ORIG/LVL0021.LVL, 2048, 0xfdd8ce16
+crc_326 = LEVEL/ORIG/LVL0022.LVL, 2048, 0xab1eff50
+crc_327 = LEVEL/ORIG/LVL0023.LVL, 2048, 0xa83ad74a
+crc_328 = LEVEL/ORIG/LVL0024.LVL, 2048, 0xa17075e3
+crc_329 = LEVEL/ORIG/LVL0025.LVL, 2048, 0x3224e69a
+crc_330 = LEVEL/ORIG/LVL0026.LVL, 2048, 0xca45920a
+crc_331 = LEVEL/ORIG/LVL0027.LVL, 2048, 0xba73951f
+crc_332 = LEVEL/ORIG/LVL0030.LVL, 2048, 0x21224eed
+crc_333 = LEVEL/ORIG/LVL0031.LVL, 2048, 0x1e0b3097
+crc_334 = LEVEL/ORIG/LVL0032.LVL, 2048, 0xdb53d4ea
+crc_335 = LEVEL/ORIG/LVL0033.LVL, 2048, 0xc75400ec
+crc_336 = LEVEL/ORIG/LVL0034.LVL, 2048, 0x46d1aa
+crc_337 = LEVEL/ORIG/LVL0035.LVL, 2048, 0xd3ae431
+crc_338 = LEVEL/ORIG/LVL0036.LVL, 2048, 0xc2cfd119
+crc_339 = LEVEL/ORIG/LVL0037.LVL, 2048, 0xeb4a95d7
+crc_340 = LEVEL/ORIG/LVL0040.LVL, 2048, 0xe0178876
+crc_341 = LEVEL/ORIG/LVL0041.LVL, 2048, 0x75107704
+crc_342 = LEVEL/ORIG/LVL0042.LVL, 2048, 0x94c8e266
+crc_343 = LEVEL/ORIG/LVL0043.LVL, 2048, 0x7d091980
+crc_344 = LEVEL/ORIG/LVL0044.LVL, 2048, 0x22772edd
+crc_345 = LEVEL/ORIG/LVL0045.LVL, 2048, 0xc0d7dba7
+crc_346 = LEVEL/ORIG/LVL0046.LVL, 2048, 0xca809f3a
+crc_347 = LEVEL/ORIG/LVL0047.LVL, 2048, 0xbac67239
+crc_348 = LEVEL/ORIG/LVL0050.LVL, 2048, 0x195d80a7
+crc_349 = LEVEL/ORIG/LVL0051.LVL, 2048, 0x9a9110cd
+crc_350 = LEVEL/ORIG/LVL0052.LVL, 2048, 0x3658c4d6
+crc_351 = LEVEL/ORIG/LVL0053.LVL, 2048, 0xe505bd1c
+crc_352 = LEVEL/ORIG/LVL0054.LVL, 2048, 0x38bd21e0
+crc_353 = LEVEL/ORIG/LVL0055.LVL, 2048, 0x93cd63ce
+crc_354 = LEVEL/ORIG/LVL0056.LVL, 2048, 0xa8c381f
+crc_355 = LEVEL/ORIG/LVL0057.LVL, 2048, 0xaff3616b
+crc_356 = LEVEL/ORIG/LVL0060.LVL, 2048, 0xa723926b
+crc_357 = LEVEL/ORIG/LVL0061.LVL, 2048, 0x1c46d230
+crc_358 = LEVEL/ORIG/LVL0062.LVL, 2048, 0x88fb24af
+crc_359 = LEVEL/ORIG/LVL0063.LVL, 2048, 0x3ef9f92b
+crc_360 = LEVEL/ORIG/LVL0064.LVL, 2048, 0x4c09c2ff
+crc_361 = LEVEL/ORIG/LVL0065.LVL, 2048, 0x30db83ad
+crc_362 = LEVEL/ORIG/LVL0066.LVL, 2048, 0xd4d78a4c
+crc_363 = LEVEL/ORIG/LVL0067.LVL, 2048, 0x37bd69d1
+crc_364 = LEVEL/ORIG/LVL0070.LVL, 2048, 0x3aa68f28
+crc_365 = LEVEL/ORIG/LVL0071.LVL, 2048, 0x725f4b7
+crc_366 = LEVEL/ORIG/LVL0072.LVL, 2048, 0xc6b2ea25
+crc_367 = LEVEL/ORIG/LVL0073.LVL, 2048, 0x55fcdaac
+crc_368 = LEVEL/ORIG/LVL0074.LVL, 2048, 0x52596fcd
+crc_369 = LEVEL/ORIG/LVL0075.LVL, 2048, 0x42cc90f7
+crc_370 = LEVEL/ORIG/LVL0076.LVL, 2048, 0x2e9448c5
+crc_371 = LEVEL/ORIG/LVL0077.LVL, 2048, 0x9c39e589
+crc_372 = LEVEL/ORIG/LVL0080.LVL, 2048, 0xc4e56fec
+crc_373 = LEVEL/ORIG/LVL0081.LVL, 2048, 0x6b8ca087
+crc_374 = LEVEL/ORIG/LVL0082.LVL, 2048, 0xf2a4142f
+crc_375 = LEVEL/ORIG/LVL0083.LVL, 2048, 0xa3b7ade4
+crc_376 = LEVEL/ORIG/LVL0084.LVL, 2048, 0x2ef4086f
+crc_377 = LEVEL/ORIG/LVL0085.LVL, 2048, 0x72756851
+crc_378 = LEVEL/ORIG/LVL0086.LVL, 2048, 0x35052457
+crc_379 = LEVEL/ORIG/LVL0087.LVL, 2048, 0xb0853df5
+crc_380 = LEVEL/ORIG/LVL0090.LVL, 2048, 0x14750547
+crc_381 = LEVEL/ORIG/LVL0091.LVL, 2048, 0x671d4f54
+crc_382 = LEVEL/ORIG/LVL0092.LVL, 2048, 0x7b488aff
+crc_383 = LEVEL/ORIG/LVL0093.LVL, 2048, 0x5111f1
+crc_384 = LEVEL/ORIG/LVL0094.LVL, 2048, 0xf183af1
+crc_385 = LEVEL/ORIG/LVL0095.LVL, 2048, 0x2df8bac5
+crc_386 = LEVEL/ORIG/LVL0096.LVL, 2048, 0x24303ebc
+crc_387 = LEVEL/ORIG/LVL0097.LVL, 2048, 0xaa4338ba
+crc_388 = MUSIC/CANCANP.MID, 7695, 0x5deb4581
+crc_389 = MUSIC/LEMMIN1P.MID, 8391, 0x42d6312c
+crc_390 = MUSIC/LEMMIN2P.MID, 22995, 0x890c371c
+crc_391 = MUSIC/LEMMIN3P.MID, 8851, 0xec3ad1d8
+crc_392 = MUSIC/MOUNTAIP.MID, 9445, 0x27d11bda
+crc_393 = MUSIC/TENLEMSP.MID, 5319, 0x89d03f3d
+crc_394 = MUSIC/TIM1P.MID, 17951, 0x7c54c8b0
+crc_395 = MUSIC/TIM2P.MID, 9387, 0x16482056
+crc_396 = MUSIC/TIM3P.MID, 9077, 0xc08e01d5
+crc_397 = MUSIC/TIM4P.MID, 13632, 0xd56e4fd0
+crc_398 = MUSIC/TIM5P.MID, 15695, 0x82277868
+crc_399 = MUSIC/TIM6P.MID, 11586, 0xf6da3e91
+crc_400 = MUSIC/TIM7P.MID, 15232, 0x8042539c
+crc_401 = MUSIC/TIM8P.MID, 14863, 0xee5e3e3c
+crc_402 = MUSIC/TIM9P.MID, 9104, 0x82790d1b
+crc_403 = SOUND/BANG.WAV, 1280, 0x54056532
+crc_404 = SOUND/CHAIN.WAV, 4704, 0x500813c1
+crc_405 = SOUND/CHANGEOP.WAV, 4138, 0xb186b072
+crc_406 = SOUND/CHINK.WAV, 1194, 0x5b604a03
+crc_407 = SOUND/DIE.WAV, 4002, 0xfbc7c133
+crc_408 = SOUND/DOOR.WAV, 4110, 0x1890fa60
+crc_409 = SOUND/ELECTRIC.WAV, 4340, 0xf9012625
+crc_410 = SOUND/EXPLODE.WAV, 1196, 0x216a3900
+crc_411 = SOUND/FIRE.WAV, 5026, 0x784ab053
+crc_412 = SOUND/GLUG.WAV, 4306, 0xf8e54fb
+crc_413 = SOUND/LETSGO.WAV, 6390, 0xd2f25f54
+crc_414 = SOUND/MANTRAP.WAV, 558, 0x9643059f
+crc_415 = SOUND/MOUSEPRE.WAV, 1780, 0x56ad6a1e
+crc_416 = SOUND/OHNO.WAV, 5412, 0x281877b2
+crc_417 = SOUND/OING.WAV, 3126, 0xcd151389
+crc_418 = SOUND/SCRAPE.WAV, 4814, 0xc547501c
+crc_419 = SOUND/SLICER.WAV, 1138, 0x42f41fa1
+crc_420 = SOUND/SPLASH.WAV, 4038, 0x9c037f8a
+crc_421 = SOUND/SPLAT.WAV, 4374, 0x4157353
+crc_422 = SOUND/TENTON.WAV, 6090, 0xc02bca19
+crc_423 = SOUND/THUD.WAV, 1612, 0x5e9a1895
+crc_424 = SOUND/THUNK.WAV, 1332, 0xaa1885bb
+crc_425 = SOUND/TING.WAV, 642, 0x5e4b3508
+crc_426 = SOUND/YIPPEE.WAV, 4592, 0x51d7edd2
+crc_427 = STYLES/BRICK.DAT, 1056, 0x6c37aa34
+crc_428 = STYLES/BRICK.PAL, 262, 0x6ecf3247
+crc_429 = STYLES/BRICKB.SPR, 89318, 0xf93c92c3
+crc_430 = STYLES/BRICKOB.SPR, 169152, 0x9d7ce228
+crc_431 = STYLES/BRICKOS.SPR, 47220, 0x8a3ed869
+crc_432 = STYLES/BRICKS.SPR, 24350, 0x5385a091
+crc_433 = STYLES/BUBBLE.PAL, 262, 0xf6f53588
+crc_434 = STYLES/BUBBLEB.SPR, 111793, 0x38e2d37d
+crc_435 = STYLES/BUBBLEOB.SPR, 103513, 0x4181a912
+crc_436 = STYLES/BUBBLEOS.SPR, 29841, 0x553235c9
+crc_437 = STYLES/BUBBLES.DAT, 1056, 0x7045be72
+crc_438 = STYLES/BUBBLES.SPR, 31327, 0x5d30ceda
+crc_439 = STYLES/GROUND0O.DAT, 1056, 0x6aa0be52
+crc_440 = STYLES/GROUND1O.DAT, 1056, 0x765cdfaf
+crc_441 = STYLES/GROUND2O.DAT, 1056, 0x28d6c04e
+crc_442 = STYLES/GROUND3O.DAT, 1056, 0xc9bdd26a
+crc_443 = STYLES/GROUND4O.DAT, 1056, 0x109f91b4
+crc_444 = STYLES/LEVEL0.PAL, 262, 0x5f1c2d45
+crc_445 = STYLES/LEVEL0B.SPR, 127309, 0xea826e7b
+crc_446 = STYLES/LEVEL0O.SPR, 904, 0xc9237ee8
+crc_447 = STYLES/LEVEL0OB.SPR, 129205, 0x1d648c8d
+crc_448 = STYLES/LEVEL0OS.SPR, 37072, 0x1dafcd90
+crc_449 = STYLES/LEVEL0S.SPR, 34671, 0xb501a278
+crc_450 = STYLES/LEVEL1.PAL, 262, 0x74d13310
+crc_451 = STYLES/LEVEL1B.SPR, 153485, 0xbaa60341
+crc_452 = STYLES/LEVEL1O.SPR, 792, 0x29266e13
+crc_453 = STYLES/LEVEL1OB.SPR, 107112, 0x261abf43
+crc_454 = STYLES/LEVEL1OS.SPR, 32411, 0x399602af
+crc_455 = STYLES/LEVEL1S.SPR, 42421, 0x8eec9bc1
+crc_456 = STYLES/LEVEL2.PAL, 262, 0xcd7f32ea
+crc_457 = STYLES/LEVEL2B.SPR, 130314, 0x7c7f3d4f
+crc_458 = STYLES/LEVEL2O.SPR, 800, 0xe2da6ca5
+crc_459 = STYLES/LEVEL2OB.SPR, 138652, 0xc51cf8ce
+crc_460 = STYLES/LEVEL2OS.SPR, 39087, 0x487fda44
+crc_461 = STYLES/LEVEL2S.SPR, 35114, 0x4905c00f
+crc_462 = STYLES/LEVEL3.PAL, 262, 0x8d283831
+crc_463 = STYLES/LEVEL3B.SPR, 139863, 0x8b6ed870
+crc_464 = STYLES/LEVEL3O.SPR, 960, 0xd4838b53
+crc_465 = STYLES/LEVEL3OB.SPR, 143928, 0x9f39e150
+crc_466 = STYLES/LEVEL3OS.SPR, 42380, 0xe3d87c86
+crc_467 = STYLES/LEVEL3S.SPR, 37336, 0x4f653e0f
+crc_468 = STYLES/LEVEL4.PAL, 262, 0xdf04258d
+crc_469 = STYLES/LEVEL4B.SPR, 132586, 0xc877061c
+crc_470 = STYLES/LEVEL4O.SPR, 944, 0x6ae7734b
+crc_471 = STYLES/LEVEL4OB.SPR, 120237, 0x950d4087
+crc_472 = STYLES/LEVEL4OS.SPR, 35600, 0x44c5107b
+crc_473 = STYLES/LEVEL4S.SPR, 37463, 0x506384de
+crc_474 = STYLES/ROCK.DAT, 1056, 0x9c43d32f
+crc_475 = STYLES/ROCK.PAL, 262, 0xf9de35e0
+crc_476 = STYLES/ROCKB.SPR, 120054, 0x107b2c62
+crc_477 = STYLES/ROCKOB.SPR, 87216, 0x5e5cb0f
+crc_478 = STYLES/ROCKOS.SPR, 25233, 0x4988c301
+crc_479 = STYLES/ROCKS.SPR, 32917, 0x97cf76e8
+crc_480 = STYLES/SNOW.DAT, 1056, 0xe428a062
+crc_481 = STYLES/SNOW.PAL, 262, 0xaf763dee
+crc_482 = STYLES/SNOWB.SPR, 183925, 0xdcd03c7b
+crc_483 = STYLES/SNOWOB.SPR, 118356, 0xef2a2273
+crc_484 = STYLES/SNOWOS.SPR, 33492, 0x38705fdf
+crc_485 = STYLES/SNOWS.SPR, 48639, 0xda5570c5 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/disclaimer.htm b/jeu-test/Lemmini/0.84/bin_copy/disclaimer.htm
new file mode 100644
index 0000000..9de62be
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/disclaimer.htm
@@ -0,0 +1,53 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+
+ <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type">
+ <title>Lemmini Disclaimer</title></head>
+
+<body>
+
+<h2>Lemmini</h2>
+
+<br>
+
+This is the first time you start Lemmini, so please read the text below
+carefully.<br>
+
+<br>
+
+Lemmini&nbsp;is a platform independent&nbsp;engine for the game
+Lemmings. The original game was developed by DMA Design back in 1990
+and released by Psygnosis in 1991. All the rights for the game
+"Lemmings" are now owned by Sony (as far as I know).<br>
+
+Lemmini&nbsp;does not include the resources necessary to actually
+play
+the game. You need a legal copy of "Lemmings for Windows" to be able to
+play&nbsp;Lemmini.<br>
+
+When starting&nbsp;Lemmini for the first time&nbsp;or
+when updating to a new version with different resources you will be
+prompted to select your "WINLEMM"&nbsp;path and a destination path
+that the
+resources will be extracted to. You don't actually need to install
+Lemmings for Windows, it's enough if you enter the original CD-ROM.<br>
+
+While
+this is a bit of a hassle,&nbsp;this step is necessary to not
+infringe the
+rights of the copyright holders.<br>
+
+Lemmini&nbsp;itself (the game engine) is free for all to use.
+Download it, give it
+to your friends or whatever. If you intend to put it as download on
+your site, please make a reference to the original homepage. Anyway, to
+respect the rights of the original copyright holders, I explicitly
+forbid to distribute&nbsp;Lemmini with the "Lemmings for Windows"
+resources in any form. If you still intend to do so, please contact the
+copyright holders.<br>For more details please visit <a href="http://lemmini.de">http://lemmini.de</a>.<br>
+
+<span style="font-weight: bold;">Lemmini&nbsp;is
+provided "as is"
+without warranty of any kind. Use it at your own risk.</span><br>
+
+</body></html> \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/extract.ini b/jeu-test/Lemmini/0.84/bin_copy/extract.ini
new file mode 100644
index 0000000..33cf919
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/extract.ini
@@ -0,0 +1,322 @@
+# extract ini for Lemmini
+
+# Levels
+
+# srcPath destPath
+level_0 = LEVEL/OHNO, levels/2_ohno
+level_1 = LEVEL/ORIG, levels/1_orig
+
+
+
+# Styles
+# SPR PAL path fname
+style_0 = STYLES/BRICKB.SPR, STYLES/BRICK.PAL, styles/brick, brick # brick terrain
+style_1 = STYLES/BUBBLEB.SPR, STYLES/BUBBLE.PAL, styles/bubble, bubble # bubble terrain
+style_2 = STYLES/LEVEL0B.SPR, STYLES/LEVEL0.PAL, styles/dirt, dirt # dirt terrain
+style_3 = STYLES/LEVEL1B.SPR, STYLES/LEVEL1.PAL, styles/fire, fire # fire terrain
+style_4 = STYLES/LEVEL2B.SPR, STYLES/LEVEL2.PAL, styles/marble, marble # marble terrain
+style_5 = STYLES/LEVEL3B.SPR, STYLES/LEVEL3.PAL, styles/pillar, pillar # pillar terrain
+style_6 = STYLES/LEVEL4B.SPR, STYLES/LEVEL4.PAL, styles/crystal, crystal # crystal terrain
+style_7 = STYLES/ROCKB.SPR, STYLES/ROCK.PAL, styles/rock, rock # rock terrain
+style_8 = STYLES/SNOWB.SPR, STYLES/SNOW.PAL, styles/snow, snow # snow terrain
+
+# Objects
+#
+# SPR PAL resource path
+objects_0 = STYLES/BRICKOB.SPR, STYLES/BRICK.PAL, bricko, styles/brick # brick objects
+# idx pix name
+bricko_0 = 0, 1, bricko_0.gif # exit - non animated part
+bricko_1 = 1, 10, bricko_1.gif # entry - animate once
+bricko_2 = 11, 14, bricko_2.gif # green flag - always animate
+bricko_3 = 25, 7, bricko_3.gif # arrows left - always animate
+bricko_4 = 32, 7, bricko_4.gif # arrows right - always animate
+bricko_5 = 39, 9, bricko_5.gif # water - always animate
+bricko_6 = 48, 18, bricko_6.gif # stamper trap - animate on trigger
+bricko_7 = 66, 20, bricko_7.gif # buzz saw trap - animate on trigger
+bricko_8 = 86, 14, bricko_8.gif # blue flag - always animate
+bricko_9 = 100, 4, bricko_9.gif # animated part of exit - always animate
+
+objects_1 = STYLES/BUBBLEOB.SPR, STYLES/BUBBLE.PAL, bubbleo, styles/bubble # bubble objects
+# idx pix name
+bubbleo_0 = 0, 1, bubbleo_0.gif # exit - non animated part
+bubbleo_1 = 1, 10, bubbleo_1.gif # entry - animate once
+bubbleo_2 = 11, 14, bubbleo_2.gif # green flag - always animate
+bubbleo_3 = 25, 7, bubbleo_3.gif # arrows left - always animate
+bubbleo_4 = 32, 7, bubbleo_4.gif # arrows left - always animate
+bubbleo_5 = 39, 6, bubbleo_5.gif # bubble water - always animate
+bubbleo_6 = 45, 7, bubbleo_6.gif # red arrow - always animate (animated part of exit)
+bubbleo_7 = 52, 14, bubbleo_7.gif # blue flag - always animate
+bubbleo_8 = 66, 14, bubbleo_8.gif # zapper trap - animate on trigger
+bubbleo_9 = 80, 20, bubbleo_9.gif # pipe trap - animate on trigger
+bubbleo_10 = 100, 1, bubbleo_10.gif # zapper
+
+objects_2 = STYLES/LEVEL0OB.SPR, STYLES/LEVEL0.PAL, dirto, styles/dirt # dirt objects
+# idx pix name
+dirto_0 = 0, 1, dirto_0.gif # exit - non animated part
+dirto_1 = 1, 10, dirto_1.gif # entry - animate once
+dirto_2 = 11, 14, dirto_2.gif # green flag - always animate
+dirto_3 = 25, 7, dirto_3.gif # arrows left - always animate
+dirto_4 = 32, 7, dirto_4.gif # arrows right - always animate
+dirto_5 = 39, 8, dirto_5.gif # water - always animate
+dirto_6 = 47, 15, dirto_6.gif # bear trap - animate on trigger
+dirto_7 = 62, 6, dirto_7.gif # animated part of exit - always animate
+dirto_8 = 68, 17, dirto_8.gif # falling stone trap - animate on trigger
+dirto_9 = 85, 14, dirto_9.gif # blue flag - always animate
+dirto_10 = 99, 12, dirto_10.gif # 10 ton trap - animate on trigger
+
+objects_3 = STYLES/LEVEL1OB.SPR, STYLES/LEVEL1.PAL, fireo, styles/fire # fire objects
+# idx pix name
+fireo_0 = 0, 1, fireo_0.gif # exit - non animated part
+fireo_1 = 1, 10, fireo_1.gif # entry - animate once
+fireo_2 = 11, 14, fireo_2.gif # green flag - always animate
+fireo_3 = 25, 8, fireo_3.gif # arrows left - always animate
+fireo_4 = 33, 8, fireo_4.gif # arrows right - always animate
+fireo_5 = 41, 8, fireo_5.gif # lava - always animate
+fireo_6 = 49, 6, fireo_6.gif # animated part of exit - always animate
+fireo_7 = 55, 8, fireo_7.gif # fire pit trap - animate on trigger
+fireo_8 = 63, 10, fireo_8.gif # flame thrower left trap - animate on trigger
+fireo_9 = 73, 14, fireo_9.gif # blue flag - always animate
+fireo_10 = 87, 10, fireo_10.gif # flame thrower right trap - animate on trigger
+
+objects_4 = STYLES/LEVEL2OB.SPR, STYLES/LEVEL2.PAL, marbleo, styles/marble # marble objects
+# idx pix name
+marbleo_0 = 0, 1, marbleo_0.gif # exit - non animated part
+marbleo_1 = 1, 10, marbleo_1.gif # entry - animate once
+marbleo_2 = 11, 14, marbleo_2.gif # green flag - always animate
+marbleo_3 = 25, 7, marbleo_3.gif # arrows left - always animate
+marbleo_4 = 32, 7, marbleo_4.gif # arrows right - always animate
+marbleo_5 = 39, 8, marbleo_5.gif # toxic water - always animate
+marbleo_6 = 47, 6, marbleo_6.gif # animated part of exit - always animate
+marbleo_7 = 53, 14, marbleo_7.gif # blue flag - always animate
+marbleo_8 = 67, 15, marbleo_8.gif # squasher trap - animate on trigger
+marbleo_9 = 82, 16, marbleo_9.gif # spinning blade trap - always animate
+
+objects_5 = STYLES/LEVEL3OB.SPR, STYLES/LEVEL3.PAL, pillaro, styles/pillar # pillar objects
+# idx pix name
+pillaro_0 = 0, 1, pillaro_0.gif # exit - non animated part
+pillaro_1 = 1, 10, pillaro_1.gif # entry - animate once
+pillaro_2 = 11, 14, pillaro_2.gif # green flag - always animate
+pillaro_3 = 25, 7, pillaro_3.gif # arrows left - always animate
+pillaro_4 = 32, 7, pillaro_4.gif # arrows right - always animate
+pillaro_5 = 39, 8, pillaro_5.gif # water - always animate
+pillaro_6 = 47, 6, pillaro_6.gif # animated part of exit - always animate
+pillaro_7 = 53, 14, pillaro_7.gif # blue flag - always animate
+pillaro_8 = 67, 37, pillaro_8.gif # wheel&rope trap - animate on trigger
+pillaro_9 = 104, 7, pillaro_9.gif # spikes left trap - animate on trigger
+pillaro_10 = 111, 7, pillaro_10.gif # spikes right trap - animate on trigger
+
+objects_6 = STYLES/LEVEL4OB.SPR, STYLES/LEVEL4.PAL, crystalo, styles/crystal # crystal objects
+# idx pix name
+crystalo_0 = 0, 1, crystalo_0.gif # exit - non animated part
+crystalo_1 = 1, 10, crystalo_1.gif # entry - animate once
+crystalo_2 = 11, 14, crystalo_2.gif # green flag - always animate
+crystalo_3 = 25, 14, crystalo_3.gif # blue flag - always animate
+crystalo_4 = 39, 7, crystalo_4.gif # arrows left - always animate
+crystalo_5 = 46, 7, crystalo_5.gif # arrows right - always animate
+crystalo_6 = 53, 8, crystalo_6.gif # water - always animate
+crystalo_7 = 61, 25, crystalo_7.gif # blade trap - animate on trigger
+crystalo_8 = 86, 6, crystalo_8.gif # animated part of exit - always animate
+crystalo_9 = 92, 8, crystalo_9.gif # zapper trap - animate on trigger
+crystalo_10 = 100, 16, crystalo_10.gif # zapper trap - animate on trigger
+
+objects_7 = STYLES/ROCKOB.SPR, STYLES/ROCK.PAL, rocko, styles/rock # rock objects
+# idx pix name
+rocko_0 = 0, 2, rocko_0.gif # animated exit (atomic)
+rocko_1 = 2, 10, rocko_1.gif # entry - animate once
+rocko_2 = 12, 14, rocko_2.gif # green flag - always animate
+rocko_3 = 26, 7, rocko_3.gif # arrows left - always animate
+rocko_4 = 33, 7, rocko_4.gif # arrows right - always animate
+rocko_5 = 40, 4, rocko_5.gif # deady grass - always animate
+rocko_6 = 44, 10, rocko_6.gif # tentacle trap - animate on trigger
+rocko_7 = 54, 1, rocko_7.gif # chameleon body right
+rocko_8 = 55, 5, rocko_8.gif # chameleon tongue trap right - animate on trigger
+rocko_9 = 60, 1, rocko_9.gif # chameleon body left
+rocko_10 = 61, 5, rocko_10.gif # chameleon tongue trap right - animate on trigger
+rocko_11 = 66, 14, rocko_11.gif # blue flag - always animate
+
+objects_8 = STYLES/SNOWOB.SPR, STYLES/SNOW.PAL, snowo, styles/snow # snow objects
+# idx pix name
+snowo_0 = 0, 1, snowo_0.gif # exit - non animated part
+snowo_1 = 1, 10, snowo_1.gif # entry - animate once
+snowo_2 = 11, 14, snowo_2.gif # green flag - always animate
+snowo_3 = 25, 7, snowo_3.gif # arrows left - always animate
+snowo_4 = 32, 7, snowo_4.gif # arrows right - always animate
+snowo_5 = 39, 8, snowo_5.gif # ice water - always animate
+snowo_6 = 47, 5, snowo_6.gif # red flag - always animate (animated part of exit)
+snowo_7 = 52, 14, snowo_7.gif # blue flag - always animate
+snowo_8 = 66, 14, snowo_8.gif # icicle trap - animate on trigger
+snowo_9 = 80, 11, snowo_9.gif # steam trap - animate on trigger
+
+objects_9 = STYLES/LEVEL0OB.SPR, STYLES/LEVEL0.PAL, specialo, styles/special # dirt objects for special levels
+# idx pix name
+specialo_0 = 0, 1, specialo_0.gif # exit - non animated part
+specialo_1 = 1, 10, specialo_1.gif # entry - animate once
+specialo_7 = 62, 6, specialo_7.gif # animated part of exit - always animate
+
+
+# Lemmings
+# SPR PAL resource path
+objects_10 = GFX/LEMMBIG.SPR, GFX/FE.PAL, lemm, misc
+# idx, pix name
+lemm_0 = 0, 8, lemm_0.gif # walker
+lemm_1 = 16, 4, lemm_1.gif # faller
+lemm_2 = 24, 8, lemm_2.gif # climber
+lemm_3 = 40, 8, lemm_3.gif # climber to faller
+lemm_4 = 56, 10, lemm_4.gif # floater
+lemm_5 = 88, 16, lemm_5.gif # splat
+lemm_6 = 104, 16, lemm_6.gif # blocker
+lemm_7 = 120, 16, lemm_7.gif # drowning
+lemm_8 = 136, 14, lemm_8.gif # trapped
+lemm_9 = 150, 8, lemm_9.gif # exiting
+lemm_10 = 72, 16, lemm_10.gif # bomber
+lemm_11 = 158, 16, lemm_11.gif # builder
+lemm_12 = 190, 8, lemm_12.gif # builder end
+lemm_13 = 206, 16, lemm_13.gif # digger
+lemm_14 = 222, 32, lemm_14.gif # basher
+lemm_15 = 286, 24, lemm_15.gif # miner
+# now not really lemmings, but in the same SPR
+lemm_16 = 335, 1, mask_10.gif # explode mask
+lemm_17 = 334, 1, mask_13.gif # digger mask
+lemm_18 = 344, 2, mask_15.gif # miner mask
+lemm_19 = 336, 4, mask_14.gif # basher mask
+lemm_20 = 348, 1, mask_11.gif # step mask
+# blocker mask is no extracted since it's a new creation
+lemm_21 = 349, 5, countdown.gif # the explode countdown
+
+# Font
+# SPR PAL ressource path
+#objects_11 = GFX/ES.SPR, GFX/FE.PAL, font, misc
+# idx, pix name
+#font_0 = 4, 76, font.gif
+
+# Misc
+# SPR PAL ressource path
+#objects_12 = GFX/FE.SPR, GFX/FE.PAL, misc, misc
+# idx, pix name
+#misc_0 = 0, 1, background.gif
+#misc_1 = 1, 1, lemmings.gif
+
+# Replay
+# SPR PAL ressource path
+objects_11 = GFX/RR.SPR, GFX/FE.PAL, replay, misc
+# idx pix name
+replay_0 = 0, 2, replay.gif # replay "R"
+
+
+
+# Create destination directories
+mkdir_0 = music
+mkdir_1 = sound
+
+# Copy midi and sound resources
+# music
+#copy_0 = MUSIC/CANCANP.MID, music/cancanp.mid
+#copy_1 = MUSIC/LEMMIN1P.MID, music/lemmin1p.mid
+#copy_2 = MUSIC/TIM2P.MID, music/tim2p.mid
+#copy_3 = MUSIC/LEMMIN2P.MID, music/lemmin2p.mid
+#copy_4 = MUSIC/TIM8P.MID, music/tim8p.mid
+#copy_5 = MUSIC/TIM3P.MID, music/tim3p.mid
+#copy_6 = MUSIC/TIM5P.MID, music/tim5p.mid
+#copy_7 = MUSIC/TIM6P.MID, music/tim6p.mid
+#copy_8 = MUSIC/LEMMIN3P.MID, music/lemmin3p.mid
+#copy_9 = MUSIC/TIM7P.MID, music/tim7p.mid
+#copy_10 = MUSIC/TIM9P.MID, music/tim9p.mid
+#copy_11 = MUSIC/TIM1P.MID, music/tim1p.mid
+#copy_12 = MUSIC/TIM4P.MID, music/tim4p.mid
+#copy_13 = MUSIC/TENLEMSP.MID, music/tenlemsp.mid
+#copy_14 = MUSIC/MOUNTAIP.MID, music/mountaip.mid
+
+# sound
+copy_0 = SOUND/BANG.WAV, sound/sound_0.wav
+copy_1 = SOUND/CHAIN.WAV, sound/sound_1.wav
+copy_2 = SOUND/CHANGEOP.WAV, sound/sound_2.wav
+copy_3 = SOUND/CHINK.WAV, sound/sound_3.wav
+copy_4 = SOUND/DIE.WAV, sound/sound_4.wav
+copy_5 = SOUND/DOOR.WAV, sound/sound_5.wav
+copy_6 = SOUND/ELECTRIC.WAV, sound/sound_6.wav
+copy_7 = SOUND/EXPLODE.WAV, sound/sound_7.wav
+copy_8 = SOUND/FIRE.WAV, sound/sound_8.wav
+copy_9 = SOUND/GLUG.WAV, sound/sound_9.wav
+copy_10 = SOUND/LETSGO.WAV, sound/sound_10.wav
+copy_11 = SOUND/MANTRAP.WAV, sound/sound_11.wav
+copy_12 = SOUND/MOUSEPRE.WAV, sound/sound_12.wav
+copy_13 = SOUND/OHNO.WAV, sound/sound_13.wav
+copy_14 = SOUND/OING.WAV, sound/sound_14.wav
+copy_15 = SOUND/SCRAPE.WAV, sound/sound_15.wav
+copy_16 = SOUND/SLICER.WAV, sound/sound_16.wav
+copy_17 = SOUND/SPLASH.WAV, sound/sound_17.wav
+copy_18 = SOUND/SPLAT.WAV, sound/sound_18.wav
+copy_19 = SOUND/TENTON.WAV, sound/sound_19.wav
+copy_20 = SOUND/THUD.WAV, sound/sound_20.wav
+copy_21 = SOUND/THUNK.WAV, sound/sound_21.wav
+copy_22 = SOUND/TING.WAV, sound/sound_22.wav
+copy_23 = SOUND/YIPPEE.WAV, sound/sound_23.wav
+
+
+#clone files in target directoy
+
+clone_0 = levels/1_orig/lvl0006.ini, levels/1_orig/lvl0006b.ini
+clone_1 = levels/1_orig/lvl0007.ini, levels/1_orig/lvl0007b.ini
+clone_2 = levels/1_orig/lvl0012.ini, levels/1_orig/lvl0012b.ini
+clone_3 = levels/1_orig/lvl0017.ini, levels/1_orig/lvl0017b.ini
+clone_4 = levels/1_orig/lvl0021.ini, levels/1_orig/lvl0021b.ini
+clone_5 = levels/1_orig/lvl0022.ini, levels/1_orig/lvl0022b.ini
+clone_6 = levels/1_orig/lvl0024.ini, levels/1_orig/lvl0024b.ini
+clone_7 = levels/1_orig/lvl0027.ini, levels/1_orig/lvl0027b.ini
+clone_8 = levels/1_orig/lvl0030.ini, levels/1_orig/lvl0030b.ini
+clone_9 = levels/1_orig/lvl0031.ini, levels/1_orig/lvl0031b.ini
+clone_10 = levels/1_orig/lvl0032.ini, levels/1_orig/lvl0032b.ini
+clone_11 = levels/1_orig/lvl0033.ini, levels/1_orig/lvl0033b.ini
+clone_12 = levels/1_orig/lvl0034.ini, levels/1_orig/lvl0034b.ini
+clone_13 = levels/1_orig/lvl0041.ini, levels/1_orig/lvl0041b.ini
+clone_14 = levels/1_orig/lvl0042.ini, levels/1_orig/lvl0042b.ini
+clone_15 = levels/1_orig/lvl0043.ini, levels/1_orig/lvl0043b.ini
+clone_16 = levels/1_orig/lvl0046.ini, levels/1_orig/lvl0046b.ini
+clone_17 = levels/1_orig/lvl0047.ini, levels/1_orig/lvl0047b.ini
+clone_18 = levels/1_orig/lvl0051.ini, levels/1_orig/lvl0051b.ini
+clone_19 = levels/1_orig/lvl0057.ini, levels/1_orig/lvl0057b.ini
+clone_20 = levels/1_orig/lvl0060.ini, levels/1_orig/lvl0060b.ini
+clone_21 = levels/1_orig/lvl0061.ini, levels/1_orig/lvl0061b.ini
+clone_22 = levels/1_orig/lvl0062.ini, levels/1_orig/lvl0062b.ini
+clone_23 = levels/1_orig/lvl0063.ini, levels/1_orig/lvl0063b.ini
+clone_24 = levels/1_orig/lvl0065.ini, levels/1_orig/lvl0065b.ini
+clone_25 = levels/1_orig/lvl0067.ini, levels/1_orig/lvl0067b.ini
+clone_26 = levels/1_orig/lvl0067.ini, levels/1_orig/lvl0067c.ini
+clone_27 = levels/1_orig/lvl0071.ini, levels/1_orig/lvl0071b.ini
+clone_28 = levels/1_orig/lvl0073.ini, levels/1_orig/lvl0073b.ini
+clone_29 = levels/1_orig/lvl0077.ini, levels/1_orig/lvl0077b.ini
+clone_30 = levels/1_orig/lvl0080.ini, levels/1_orig/lvl0080b.ini
+clone_31 = levels/1_orig/lvl0082.ini, levels/1_orig/lvl0082b.ini
+clone_32 = levels/1_orig/lvl0083.ini, levels/1_orig/lvl0083b.ini
+clone_33 = levels/1_orig/lvl0084.ini, levels/1_orig/lvl0084b.ini
+clone_34 = levels/1_orig/lvl0091.ini, levels/1_orig/lvl0091b.ini
+clone_35 = levels/1_orig/lvl0092.ini, levels/1_orig/lvl0092b.ini
+clone_36 = levels/1_orig/lvl0093.ini, levels/1_orig/lvl0093b.ini
+clone_37 = levels/1_orig/lvl0094.ini, levels/1_orig/lvl0094b.ini
+clone_38 = levels/1_orig/lvl0095.ini, levels/1_orig/lvl0095b.ini
+clone_39 = levels/1_orig/lvl0097.ini, levels/1_orig/lvl0097b.ini
+clone_40 = styles/dirt/dirto_0.gif, styles/special/specialo_0.gif
+clone_41 = styles/dirt/dirto_1.gif, styles/special/specialo_1.gif
+clone_42 = styles/dirt/dirto_7.gif, styles/special/specialo_2.gif
+
+
+# create/apply patches for files in these paths
+ppatch_0 = styles/brick
+ppatch_1 = styles/bubble
+ppatch_2 = styles/crystal
+ppatch_3 = styles/dirt
+ppatch_4 = styles/fire
+ppatch_5 = styles/pillar
+ppatch_6 = styles/rock
+ppatch_7 = styles/snow
+ppatch_8 = styles/special
+ppatch_9 = styles/marble
+ppatch_10 = levels
+ppatch_11 = levels/2_ohno
+ppatch_12 = levels/1_orig
+ppatch_13 = misc
+ppatch_14 = music
+ppatch_15 = sound
+
+# don't patch/extract files with the following endings
+ignore_ext = bak,db,class
diff --git a/jeu-test/Lemmini/0.84/bin_copy/icon_32.png b/jeu-test/Lemmini/0.84/bin_copy/icon_32.png
new file mode 100644
index 0000000..1090f3a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/icon_32.png
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/lemmini.png b/jeu-test/Lemmini/0.84/bin_copy/lemmini.png
new file mode 100644
index 0000000..9c5bffc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/lemmini.png
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@levelpack.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@levelpack.ini
new file mode 100644
index 0000000..5d105cc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@levelpack.ini
@@ -0,0 +1,295 @@
+# based on http://www.deveria.com/alexis/lemmings/lemmings/amigacodes.html
+# and http://home.wanadoo.nl/lemmings-solution/index2.html
+# and http://www.deinonych.com/lemmings/lemm1fun_walk.shtml
+
+name = Lemmings
+#path = lvl_orig
+
+maxFallDistance = 126
+
+codeSeed = AJHLDHBBCJ
+
+# music selection
+music_0 = cancan.mod
+music_1 = lemming1.mod
+music_2 = tim2.mod
+music_3 = lemming2.mod
+music_4 = tim8.mod
+music_5 = tim3.mod
+music_6 = tim5.mod
+music_7 = doggie.mod
+music_8 = tim6.mod
+music_9 = lemming3.mod
+music_10 = tim7.mod
+music_11 = tim9.mod
+music_12 = tim1.mod
+music_13 = tim10.mod
+music_14 = tim4.mod
+music_15 = tenlemms.mod
+music_16 = mountain.mod
+music_17 = beasti.mod
+music_18 = menace.mod
+music_19 = awesome.mod
+music_20 = beastii.mod
+
+
+# levels of difficulty
+level_0 = Fun
+level_1 = Tricky
+level_2 = Taxing
+level_3 = Mayhem
+
+#Levels: name, code, music id
+
+# Level 0 - Fun
+# Fun 1 - Just dig!
+fun_0 = lvl0091.ini,0
+# Fun 2 - Only floaters can survive this
+fun_1 = lvl0095.ini,1
+# Fun 3 - Tailor-made for blockers
+fun_2 = lvl0096.ini,2
+# Fun 4 - Now use miners and climbers
+fun_3 = lvl0092.ini,3
+# Fun 5 - You need bashers this time
+fun_4 = lvl0093.ini,4
+# Fun 6 - A task for blockers and bombers
+fun_5 = lvl0094.ini,5
+# Fun 7 - Builders will help you here
+fun_6 = lvl0097.ini,6
+# Fun 8 - Not as complicated as it looks (based on "Turn around"/lvl0006.ini, check number of lemmings)
+fun_7 = lvl0006b.ini,7
+# Fun 9 - As long as you try your best (based on "I have a cunning plan"/lvl0012.ini )
+fun_8 = lvl0012b.ini,8
+# Fun 10 - Smile if you love lemmings (based on "Perseverance"/lvl0032.ini)
+fun_9 = lvl0032b.ini,9
+# Fun 11 - Keep your hair on Mr. Lemming (based on "Tribute to M.C.Escher"/lvl0042.ini)
+fun_10 = lvl0042b.ini,10
+# Fun 12 - Patience (based on "From The Boundary Line"/lvl0007.ini)
+fun_11 = lvl0007b.ini,11
+# Fun 13 - We all fall down (1)
+fun_12 = lvl0016.ini,12
+# Fun 14 - Origins and Lemmings (based on "The Crankshaft"/lvl0017.ini)
+fun_13 = lvl0017b.ini,13
+# Fun 15 - Don't let your eyes deceive you (based on "If at first you don't succeed.."/lvl0022.ini)
+fun_14 = lvl0022b.ini,14
+# Fun 16 - Don't do anything too hasty (based on "Heaven can wait (we hope!!!!)"/lvl0024.ini )
+fun_15 = lvl0024b.ini,15
+# Fun 17 - Easy when you know how (based on "Compression Method 1"/lvl0027.ini)
+fun_16 = lvl0027b.ini,16
+# Fun 18 - Let's block and blow (based on "Bomboozal"/lvl0043.ini)
+fun_17 = lvl0043b.ini,0
+# Fun 19 - Take good care of my Lemmings (based on "Follow the leader..."/lvl0051.ini)
+fun_18 = lvl0051b.ini,1
+# Fun 20 - We are now at LEMCON ONE (based on "Poles Apart"/lvl0063.ini)
+fun_19 = lvl0063b.ini,2
+# Fun 21 - You Live and Lem (based on "The Steel Mines of Kessel"/lvl0084.ini)
+fun_20 = lvl0084b.ini,3
+# Fun 22 - A Beast of a level
+fun_21 = lvl0013.ini,17
+# Fun 23 - I've lost that Lemming feeling (based on "X marks the spot"/lvl0041.ini)
+fun_22 = lvl0041b.ini,5
+# Fun 24 - Konbanwa Lemming san (based on "It's hero time!"/lvl0057.ini)
+fun_23 = lvl0057b.ini,6
+# Fun 25 - Lemmings Lemmings everywhere (based on "The Crossroads"/lvl0060.ini)
+fun_24 = lvl0060b.ini,7
+# Fun 26 - Nightmare on Lem street (based on "The Great Lemming Caper"/lvl0071.ini)
+fun_25 = lvl0071b.ini,8
+# Fun 27 - Let's be careful out there (based on "Come on over to my place"/lvl0046.ini)
+fun_26 = lvl0046b.ini,9
+# Fun 28 - If only they could fly (based on "Down, along, up. In that order"/lvl0061.ini)
+fun_27 = lvl0061b.ini,10
+# Fun 29 - worra lorra lemmings (based on "Curse of the Pharaohs"/lvl0065.ini)
+fun_28 = lvl0065b.ini,11
+# Fun 30 - Lock up your Lemmings (based on "All or Nothing"/lvl0082.ini)
+fun_29 = lvl0082b.ini,12
+
+
+# Level 1 - Tricky
+
+# Tricky 1 - This should be a doddle!
+tricky_0 = lvl0000.ini,13
+# Tricky 2 - We all fall down (2)
+tricky_1 = lvl0067b.ini,14
+# Tricky 3 - A ladder would be handy (based on "How do I dig up the way?"/lvl0021.ini)
+tricky_2 = lvl0021b.ini,15
+# Tricky 4 - Here's one I prepared earlier (based on "Every Lemming for Himself!!!"/lvl0030.ini)
+tricky_3 = lvl0030b.ini,16
+# Tricky 5 - Careless clicking costs lives (based on "The Art Gallery"/lvl0031.ini)
+tricky_4 = lvl0031b.ini,0
+# Tricky 6 - Lemmingology (based on "Izzie Wizzie lemmings get busy"/lvl0033.ini)
+tricky_5 = lvl0033b.ini,1
+# Tricky 7 - Been there, seen it, done it (based on "The ascending pillar scenario"/lvl0034.ini)
+tricky_6 = lvl0034b.ini,2
+# Tricky 8 - Lemming sanctuary in sight (based on "King of the castle"/lvl0047.ini)
+tricky_7 = lvl0047b.ini,3
+# Tricky 9 - They just keep on coming (based on "One way or another"/lvl0062.ini)
+tricky_8 = lvl0062b.ini,4
+# Tricky 10 - There's a lot of them about (based on "The Fast Food Kitchen"/lvl0073.ini)
+tricky_9 = lvl0073b.ini,5
+# Tricky 11 - Lemmings in the attic (based on "Time to get up!"/lvl0077.ini)
+tricky_10 = lvl0077b.ini,6
+# Tricky 12 - Bitter Lemming (based on "With a twist of lemming please"/lvl0080.ini)
+tricky_11 = lvl0080b.ini,7
+# Tricky 13 - Lemming Drops (based on "Have a nice day!"/lvl0083.ini)
+tricky_12 = lvl0083b.ini,8
+# Tricky 14 - MENACING !!
+tricky_13 = lvl0002.ini,18
+# Tricky 15 - Ozone friendly Lemmings (based on "Just dig!"/lvl0091.ini)
+tricky_14 = lvl0091b.ini,10
+# Tricky 16 - Luvly Jubly (based on "You need bashers this time"/lvl0093.ini)
+tricky_15 = lvl0093b.ini,11
+# Tricky 17 - Diet Lemmingaid (based on "A task for blockers and bombers"/lvl0094.ini
+tricky_16 = lvl0094b.ini,12
+# Tricky 18 - It's Lemmingentry Watson (based on "Only floaters can survive this"/lvl0095.ini)
+tricky_17 = lvl0095b.ini,13
+# Tricky 19 - Postcard from Lemmingland (based on "Builders will help you here"/lvl0097.ini)
+tricky_18 = lvl0097b.ini,14
+# Tricky 20 - One way digging to freedom
+tricky_19 = lvl0003.ini,15
+# Tricky 21 - All the 6's ........
+tricky_20 = lvl0005.ini,16
+# Tricky 22 - Turn around young lemmings!
+tricky_21 = lvl0006.ini,0
+# Tricky 23 - From The Boundary Line
+tricky_22 = lvl0007.ini,1
+# Tricky 24 - Tightrope City
+tricky_23 = lvl0010.ini,2
+# Tricky 25 - Cascade
+tricky_24 = lvl0011.ini,3
+# Tricky 26 - I have a cunning plan
+tricky_25 = lvl0012.ini,4
+# Tricky 27 - The Island of the Wicker people
+tricky_26 = lvl0014.ini,5
+# Tricky 28 - Lost something? (invisible exit)
+tricky_27 = lvl0015.ini,6
+# Tricky 29 - Rainbow Island
+tricky_28 = lvl0020.ini,7
+# Tricky 30 - The Crankshaft
+tricky_29 = lvl0017.ini,8
+
+
+# Level 2 - Taxing
+
+# Taxing 1 - If at first you don't succeed..
+taxing_0 = lvl0022.ini,9
+# Taxing 2 - Watch out, there's traps about
+taxing_1 = lvl0023.ini,10
+# Taxing 3 - Heaven can wait (we hope!!!!)
+taxing_2 = lvl0024.ini,11
+# Taxing 4 - Lend a helping hand....
+taxing_3 = lvl0025.ini,12
+# Taxing 5 - The Prison!
+taxing_4 = lvl0026.ini,13
+# Taxing 6 - Compression Method 1
+taxing_5 = lvl0027.ini,14
+# Taxing 7 - Every Lemming for Himself!!!
+taxing_6 = lvl0030.ini,15
+# Taxing 8 - The Art Gallery
+taxing_7 = lvl0031.ini,16
+# Taxing 9 - Perseverance
+taxing_8 = lvl0032.ini,0
+# Taxing 10 - Izzie Wizzie lemmings get busy
+taxing_9 = lvl0033.ini,1
+# Taxing 11 - The Ascending Pillar Scenario
+taxing_10 = lvl0034.ini,2
+# Taxing 12 - Livin' on the Edge
+taxing_11 = lvl0035.ini,3
+# Taxing 13 - Upsidedown World (some objects with ID 4 (arrow to right) contain overwrite AND visible on terrain: impossible)
+taxing_12 = lvl0036.ini,4
+# Taxing 14 - Hunt the Nessy....
+taxing_13 = lvl0037.ini,5
+# Taxing 15 - What an AWESOME level
+taxing_14 = lvl0001.ini,19
+# Taxing 16 - Mary Poppins' Land
+taxing_15 = lvl0040.ini,7
+# Taxing 17 - X marks the spot
+taxing_16 = lvl0041.ini,8
+# Taxing 18 - Tribute to M.C.Escher
+taxing_17 = lvl0042.ini,9
+# Taxing 19 - Bomboozal
+taxing_18 = lvl0043.ini,10
+# Taxing 20 - Walk the web rope
+taxing_19 = lvl0044.ini,11
+# Taxing 21 - Feel the heat!
+taxing_20 = lvl0045.ini,12
+# Taxing 22 - Come on over to my place
+taxing_21 = lvl0046.ini,13
+# Taxing 23 - King of the castle
+taxing_22 = lvl0047.ini,14
+# Taxing 24 - Take a running jump.....
+taxing_23 = lvl0050.ini,15
+# Taxing 25 - Follow the leader...
+taxing_24 = lvl0051.ini,16
+# Taxing 26 - Triple Trouble
+taxing_25 = lvl0052.ini,0
+# Taxing 27 - Call in the bomb squad
+taxing_26 = lvl0053.ini,1
+# Taxing 28 - POOR WEE CREATURES!
+taxing_27 = lvl0054.ini,2
+# Taxing 29 - How do I dig up the way?
+taxing_28 = lvl0021.ini,3
+# Taxing 30 - We all fall down (3)
+taxing_29 = lvl0067.ini,4
+
+# Level 3 - Mayhem
+
+# Mayhem 1 - Steel Works
+mayhem_0 = lvl0055.ini,5
+# Mayhem 2 - The Boiler Room
+mayhem_1 = lvl0056.ini,6
+# Mayhem 3 - It's hero time!
+mayhem_2 = lvl0057.ini,7
+# Mayhem 4 - The Crossroads
+mayhem_3 = lvl0060.ini,8
+# Mayhem 5 - Down, along, up. In that order
+mayhem_4 = lvl0061.ini,9
+# Mayhem 6 - One way or another
+mayhem_5 = lvl0062.ini,10
+# Mayhem 7 - Poles Apart
+mayhem_6 = lvl0063.ini,11
+# Mayhem 8 - Last one out is a rotten egg!
+mayhem_7 = lvl0064.ini,12
+# Mayhem 9 - Curse of the Pharaohs
+mayhem_8 = lvl0065.ini,13
+# Mayhem 10 - Pillars of Hercules
+mayhem_9 = lvl0066.ini,14
+# Mayhem 11 - We all fall down (4)
+mayhem_10 = lvl0067c.ini,15
+# Mayhem 12 - The Far Side
+mayhem_11 = lvl0070.ini,16
+# Mayhem 13 - The Great Lemming Caper (contains objects completely outside the playfield)
+mayhem_12 = lvl0071.ini,0
+# Mayhem 14 - Pea Soup
+mayhem_13 = lvl0072.ini,1
+# Mayhem 15 - The Fast Food Kitchen...
+mayhem_14 = lvl0073.ini,2
+# Mayhem 16 - Just a Minute...
+mayhem_15 = lvl0074.ini,3
+# Mayhem 17 - Stepping Stones
+mayhem_16 = lvl0075.ini,4
+# Mayhem 18 - And then there were four....
+mayhem_17 = lvl0076.ini,5
+# Mayhem 19 - Time to get up!
+mayhem_18 = lvl0077.ini,6
+# Mayhem 20 - No added colours or Lemmings
+mayhem_19 = lvl0092b.ini,7
+# Mayhem 21 - With a twist of lemming please (based on "Now use miners and climbers")
+mayhem_20 = lvl0080.ini,8
+# Mayhem 22 - A BeastII of a level
+mayhem_21 = lvl0004.ini,20
+# Mayhem 23 - Going up.......
+mayhem_22 = lvl0081.ini,10
+# Mayhem 24 - All or Nothing
+mayhem_23 = lvl0082.ini,11
+# Mayhem 25 - Have a nice day!
+mayhem_24 = lvl0083.ini,12
+# Mayhem 26 - The Steel Mines of Kessel
+mayhem_25 = lvl0084.ini,13
+# Mayhem 27 - Just a Minute (Part Two)
+mayhem_26 = lvl0085.ini,14
+# Mayhem 28 - Mind the step.....
+mayhem_27 = lvl0086.ini,15
+# Mayhem 29 - Save Me
+mayhem_28 = lvl0087.ini,16
+# Mayhem 30 - Rendezvous at the Mountain
+mayhem_29 = lvl0090.ini,0 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0000.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0000.dif
new file mode 100644
index 0000000..486470f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0000.dif
@@ -0,0 +1 @@
+ï¾­Þñ+ð+%k,ýk/9ÿà­Ô+ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0001.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0001.dif
new file mode 100644
index 0000000..df4bd01
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0001.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0002.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0002.dif
new file mode 100644
index 0000000..0fa3294
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0002.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0003.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0003.dif
new file mode 100644
index 0000000..d1da4c8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0003.dif
@@ -0,0 +1 @@
+ï¾­ÞŒ‹­êÁê'jÿà­Ï772 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0004.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0004.dif
new file mode 100644
index 0000000..2d8db8c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0004.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0005.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0005.dif
new file mode 100644
index 0000000..3214c8d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0005.dif
@@ -0,0 +1 @@
+ï¾­Þ½+¼+|[ÝŸW[szÿà­Ø164Å) \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006.dif
new file mode 100644
index 0000000..a6826ae
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006.dif
@@ -0,0 +1 @@
+ï¾­Þ¼,»,§{þ{(Ûÿà­Ñ832Ë* \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006b.dif
new file mode 100644
index 0000000..de7c7d4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0006b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007.dif
new file mode 100644
index 0000000..621c499
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007.dif
@@ -0,0 +1 @@
+ï¾­Þš1™1–ãCv·*ÿà­ý0 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007b.dif
new file mode 100644
index 0000000..d2ecfae
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0007b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0010.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0010.dif
new file mode 100644
index 0000000..08b1113
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0010.dif
@@ -0,0 +1 @@
+ï¾­Þ®­[(ƒ‚6(´æÿà­Ï820¿ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0011.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0011.dif
new file mode 100644
index 0000000..4e79ac4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0011.dif
@@ -0,0 +1 @@
+ï¾­Þó$ó$jŠàQsŠ`ýÿà­+10¥724‚# \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012.dif
new file mode 100644
index 0000000..2b1a979
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012.dif
@@ -0,0 +1 @@
+ï¾­ÞßÞH9ÆñG‹¤ÿà­Â \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012b.dif
new file mode 100644
index 0000000..5ee6891
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0012b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0013.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0013.dif
new file mode 100644
index 0000000..3abf4cf
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0013.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0014.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0014.dif
new file mode 100644
index 0000000..ef2315f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0014.dif
@@ -0,0 +1 @@
+ï¾­Þ«=ª=ÕyÚµyµÿà­Ž= \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0015.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0015.dif
new file mode 100644
index 0000000..5f0daaf
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0015.dif
@@ -0,0 +1 @@
+ï¾­Þ‰@ˆ@ýÊKîÝÊ0çÿà­ì? \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0016.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0016.dif
new file mode 100644
index 0000000..b7d8f51
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0016.dif
@@ -0,0 +1 @@
+ï¾­Þï4ï4ºw¯Öw*¡ÿà­Ï4763 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017.dif
new file mode 100644
index 0000000..813bd88
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017.dif
@@ -0,0 +1 @@
+ï¾­Þ”“QžÚöPP²ÿà­÷ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017b.dif
new file mode 100644
index 0000000..f6d6557
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0017b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0020.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0020.dif
new file mode 100644
index 0000000..641a9dd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0020.dif
@@ -0,0 +1 @@
+ï¾­ÞÞÝã {Ãàíqÿà­Á \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021.dif
new file mode 100644
index 0000000..d45bd65
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021b.dif
new file mode 100644
index 0000000..7864886
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0021b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022.dif
new file mode 100644
index 0000000..1e98102
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022.dif
@@ -0,0 +1 @@
+ï¾­Þª!ª!Ó%…å%GÈÿà­Ï372¼ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022b.dif
new file mode 100644
index 0000000..ea0ef0e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0022b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0023.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0023.dif
new file mode 100644
index 0000000..2dd0da3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0023.dif
@@ -0,0 +1 @@
+ï¾­Þö@õ@Þðéf¾ð.Rÿà­Ù@ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024.dif
new file mode 100644
index 0000000..b3bf74e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024.dif
@@ -0,0 +1 @@
+ï¾­ÞÛÚÆÂùù¦Â˘ÿà­¾ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024b.dif
new file mode 100644
index 0000000..b5df687
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0024b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0025.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0025.dif
new file mode 100644
index 0000000..76929e2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0025.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0026.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0026.dif
new file mode 100644
index 0000000..8c3a3b0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0026.dif
@@ -0,0 +1 @@
+ï¾­Þêéã¾Pþ3£ÿà­Í \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027.dif
new file mode 100644
index 0000000..ed2ef77
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027.dif
@@ -0,0 +1 @@
+ï¾­Þë,ê,<|ín|¡›ÿà­Î, \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027b.dif
new file mode 100644
index 0000000..b47064e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0027b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030.dif
new file mode 100644
index 0000000..d24c371
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030.dif
@@ -0,0 +1 @@
+ï¾­Þ·c¶cGù*£'ù1fÿà­šc \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030b.dif
new file mode 100644
index 0000000..4de3341
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0030b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031.dif
new file mode 100644
index 0000000..dd5b99a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031.dif
@@ -0,0 +1 @@
+ï¾­Þ‰3ˆ3xB«ÌfBìíÿà­Ô748•1 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031b.dif
new file mode 100644
index 0000000..0dde938
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0031b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032.dif
new file mode 100644
index 0000000..2d4d683
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032.dif
@@ -0,0 +1 @@
+ï¾­Þ„%„%m‘ÔÌz‘MÙÿà­Ï118–# \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032b.dif
new file mode 100644
index 0000000..9bfa12f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0032b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033.dif
new file mode 100644
index 0000000..e9222b7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033.dif
@@ -0,0 +1 @@
+ï¾­Þ"Ž"Ù?W¼¾?Oåÿà­Î876¡ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033b.dif
new file mode 100644
index 0000000..336506c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0033b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034.dif
new file mode 100644
index 0000000..eb5bd10
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034.dif
@@ -0,0 +1 @@
+ï¾­Þùù$3r¥03ã'ÿà­Ð1260‰ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034b.dif
new file mode 100644
index 0000000..95735c2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0034b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0035.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0035.dif
new file mode 100644
index 0000000..b4a3301
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0035.dif
@@ -0,0 +1 @@
+ï¾­Þ”*“* *n-í)…ÿà­÷) \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0036.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0036.dif
new file mode 100644
index 0000000..b72634f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0036.dif
@@ -0,0 +1 @@
+ï¾­ÞßVÞV~~oc~ÛÑÿà­Ñ740îT \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0037.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0037.dif
new file mode 100644
index 0000000..b679dc1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0037.dif
@@ -0,0 +1 @@
+ï¾­Þµ*´*µ=%†•=™Ùÿà­˜* \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0040.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0040.dif
new file mode 100644
index 0000000..dd58bbe
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0040.dif
@@ -0,0 +1 @@
+ï¾­Þ´³Á3ƪ]Ÿÿà­Ð688Ä \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041.dif
new file mode 100644
index 0000000..7dddcd7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041.dif
@@ -0,0 +1 @@
+ï¾­Þí1ì1ÜÅݼ9ºÿà­Ð1 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041b.dif
new file mode 100644
index 0000000..e0016b4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0041b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042.dif
new file mode 100644
index 0000000..44a4863
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042.dif
@@ -0,0 +1 @@
+ï¾­Þ°¯}Tû½]T-’ÿà­“ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042b.dif
new file mode 100644
index 0000000..09da23d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0042b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043.dif
new file mode 100644
index 0000000..689bc5d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043.dif
@@ -0,0 +1 @@
+ï¾­Þ®­@§nê §àÿà­‘ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043b.dif
new file mode 100644
index 0000000..d6e24c4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0043b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0044.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0044.dif
new file mode 100644
index 0000000..6c032bd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0044.dif
@@ -0,0 +1 @@
+ï¾­ÞñðçDŽÇD‘rÿà­Ô \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0045.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0045.dif
new file mode 100644
index 0000000..31624ac
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0045.dif
@@ -0,0 +1 @@
+ï¾­Þœ›ýöCZÝöõ`ÿà­ÿ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046.dif
new file mode 100644
index 0000000..592d8ce
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046.dif
@@ -0,0 +1 @@
+ï¾­Þì,ë,0„e›„ùÇÿà­Ï, \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046b.dif
new file mode 100644
index 0000000..e820d90
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0046b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047.dif
new file mode 100644
index 0000000..5af9d9d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047.dif
@@ -0,0 +1 @@
+ï¾­ÞÓÒß÷=—¿÷ÿà­¶ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047b.dif
new file mode 100644
index 0000000..7bb6358
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0047b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0050.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0050.dif
new file mode 100644
index 0000000..b916f15
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0050.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051.dif
new file mode 100644
index 0000000..a5dfe06
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051.dif
@@ -0,0 +1 @@
+ï¾­Þ’7‘7è¿ÀÈ“Hÿà­õ6 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051b.dif
new file mode 100644
index 0000000..f0e9d39
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0051b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0052.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0052.dif
new file mode 100644
index 0000000..40b3ed7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0052.dif
@@ -0,0 +1 @@
+ï¾­Þúù÷,b×,Txÿà­Ý \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0053.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0053.dif
new file mode 100644
index 0000000..91f216e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0053.dif
@@ -0,0 +1 @@
+ï¾­Þïcîcv1V)Ëÿà­Òc \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0054.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0054.dif
new file mode 100644
index 0000000..1d79f6b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0054.dif
@@ -0,0 +1 @@
+ï¾­Þ´"³"öE˜™ÖE;mÿà­—" \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0055.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0055.dif
new file mode 100644
index 0000000..97b1cb3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0055.dif
@@ -0,0 +1 @@
+ï¾­Þ’`‘`…ŽÃ„jŽ.hÿà­Ñ780¡^ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0056.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0056.dif
new file mode 100644
index 0000000..8b41f7c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0056.dif
@@ -0,0 +1 @@
+ï¾­Þåå¶AE¶e_ÿà­Ó1320ò \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057.dif
new file mode 100644
index 0000000..d03a4bb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057.dif
@@ -0,0 +1 @@
+ï¾­Þë2ê2?5ÛJ5ÿà­Î2 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057b.dif
new file mode 100644
index 0000000..77047dc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0057b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060.dif
new file mode 100644
index 0000000..710ce9a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060.dif
@@ -0,0 +1 @@
+ï¾­Þ¬« xGéw‘ñÿà­Ñ904» \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060b.dif
new file mode 100644
index 0000000..e60532e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0060b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061.dif
new file mode 100644
index 0000000..5162715
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061.dif
@@ -0,0 +1 @@
+ï¾­Þô>ô>¨©R¥¸©²µÿà­Ñ1232ƒ= \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061b.dif
new file mode 100644
index 0000000..3e8efaa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0061b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062.dif
new file mode 100644
index 0000000..833b165
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062.dif
@@ -0,0 +1 @@
+ï¾­Þ¦f¥f_ÿ ø^¶ÿà­‰f \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062b.dif
new file mode 100644
index 0000000..f090b36
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0062b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063.dif
new file mode 100644
index 0000000..2d2c127
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063.dif
@@ -0,0 +1 @@
+ï¾­Þ8€8™Ý`a~ݹbÿà­Ð780‘6 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063b.dif
new file mode 100644
index 0000000..f354faf
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0063b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0064.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0064.dif
new file mode 100644
index 0000000..b6518a9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0064.dif
@@ -0,0 +1 @@
+ï¾­Þ•,”,lR÷tlyƒÿà­Ð852¥* \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065.dif
new file mode 100644
index 0000000..7fd00c8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065.dif
@@ -0,0 +1 @@
+ï¾­ÞÃ4Â4mg MgÅQÿà­¦4 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065b.dif
new file mode 100644
index 0000000..a70d35d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0065b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0066.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0066.dif
new file mode 100644
index 0000000..9ce5356
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0066.dif
@@ -0,0 +1 @@
+ï¾­ÞŸ/ž/±Å¤V‘ÅØ\ÿà­‚/ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067.dif
new file mode 100644
index 0000000..19b691d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067.dif
@@ -0,0 +1 @@
+ï¾­Þ33WC¡jtC]ÿà­Ð96¢1 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067b.dif
new file mode 100644
index 0000000..b2a9bd4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067c.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067c.dif
new file mode 100644
index 0000000..ebe0d24
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0067c.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0070.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0070.dif
new file mode 100644
index 0000000..f715a31
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0070.dif
@@ -0,0 +1 @@
+ï¾­ÞèFçFj—éJ—vÿà­ËF \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071.dif
new file mode 100644
index 0000000..b79d8ec
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071.dif
@@ -0,0 +1 @@
+ï¾­Þâ#â#Dm%7YmÃÿà­Ð1388ò! \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071b.dif
new file mode 100644
index 0000000..610075e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0071b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0072.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0072.dif
new file mode 100644
index 0000000..02e92ad
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0072.dif
@@ -0,0 +1 @@
+ï¾­Þ”“qi/[i?-ÿà­Ó744¡ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073.dif
new file mode 100644
index 0000000..68a540e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073.dif
@@ -0,0 +1 @@
+ï¾­Þ®®²È¾ÀŽÿà­×1260· \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073b.dif
new file mode 100644
index 0000000..251d07c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0073b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0074.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0074.dif
new file mode 100644
index 0000000..540f1d6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0074.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0075.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0075.dif
new file mode 100644
index 0000000..527fa38
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0075.dif
@@ -0,0 +1 @@
+ï¾­Þ¬'«'~ÛXæcÛ·Èÿà­Ð844¼% \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0076.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0076.dif
new file mode 100644
index 0000000..24dba19
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0076.dif
@@ -0,0 +1 @@
+ï¾­ÞŒO‹O©“͉“~Õÿà­ïN \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077.dif
new file mode 100644
index 0000000..717908f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077.dif
@@ -0,0 +1 @@
+ï¾­Þ®#®#Êb¹ØbpËÿà­Ð1212¾! \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077b.dif
new file mode 100644
index 0000000..0624fb5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0077b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080.dif
new file mode 100644
index 0000000..f913212
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080.dif
@@ -0,0 +1 @@
+ï¾­Þ’4‘4Œfâ¢lf¶Zÿà­õ3 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080b.dif
new file mode 100644
index 0000000..167aca8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0080b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0081.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0081.dif
new file mode 100644
index 0000000..53da778
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0081.dif
@@ -0,0 +1 @@
+ï¾­ÞÙ+Ø+ú] è]º€ÿà­Ó868æ) \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082.dif
new file mode 100644
index 0000000..feff1f1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082.dif
@@ -0,0 +1 @@
+ï¾­ÞÓ"Ò"6IìÝI¯­ÿà­¶" \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082b.dif
new file mode 100644
index 0000000..743a702
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0082b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083.dif
new file mode 100644
index 0000000..881e86e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083.dif
@@ -0,0 +1 @@
+ï¾­Þœ,›,7nkn•¡ÿà­ÿ+ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083b.dif
new file mode 100644
index 0000000..42f4252
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0083b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084.dif
new file mode 100644
index 0000000..f48ebcc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084b.dif
new file mode 100644
index 0000000..fde15e2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0084b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0085.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0085.dif
new file mode 100644
index 0000000..1ab5991
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0085.dif
@@ -0,0 +1 @@
+ï¾­Þ´³[4‰>4‡Œÿà­Ï792Å \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0086.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0086.dif
new file mode 100644
index 0000000..ca754b4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0086.dif
@@ -0,0 +1 @@
+ï¾­Þ‹IŠIDÕú $Õtÿà­îH \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0087.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0087.dif
new file mode 100644
index 0000000..ef68e9e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0087.dif
@@ -0,0 +1 @@
+ï¾­ÞÁ6À6"¦R¦näÿà­Ð2256Ð4 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0090.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0090.dif
new file mode 100644
index 0000000..69d5ab4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0090.dif
@@ -0,0 +1 @@
+ï¾­ÞÂ8Á8†êffê+yÿà­¥8 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091.dif
new file mode 100644
index 0000000..c0555e8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091.dif
@@ -0,0 +1 @@
+ï¾­Þ1Œ1ÎÊó®>Üÿà­ð0 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091b.dif
new file mode 100644
index 0000000..e90b532
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0091b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092.dif
new file mode 100644
index 0000000..a3f22a5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092.dif
@@ -0,0 +1 @@
+ï¾­ÞíìÏÇÔÁ¯Çf^ÿà­Ð \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092b.dif
new file mode 100644
index 0000000..9c6d5c6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0092b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093.dif
new file mode 100644
index 0000000..a398605
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093.dif
@@ -0,0 +1 @@
+ï¾­Þ$$,zöKzÊYÿà­Ï808¡" \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093b.dif
new file mode 100644
index 0000000..9b15b6f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0093b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094.dif
new file mode 100644
index 0000000..bd30122
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094.dif
@@ -0,0 +1,2 @@
+ï¾­ÞÇÆÁîÉ¡î
+ÿà­ª \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094b.dif
new file mode 100644
index 0000000..baa59ad
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0094b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095.dif
new file mode 100644
index 0000000..4db2711
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095.dif
@@ -0,0 +1 @@
+ï¾­Þ•”Z:E::-ÿà­ø \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095b.dif
new file mode 100644
index 0000000..a922c54
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0095b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0096.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0096.dif
new file mode 100644
index 0000000..4459d12
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0096.dif
@@ -0,0 +1 @@
+ï¾­Þ¥¤àQñKÁQ±­ÿà­Ï94· \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097.dif
new file mode 100644
index 0000000..3e8c856
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097.dif
@@ -0,0 +1 @@
+ï¾­Þ‘33eE¼ÿDE¯ÿà­Ð64¢1 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097b.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097b.dif
new file mode 100644
index 0000000..50e6b63
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@1_orig@lvl0097b.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@levelpack.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@levelpack.ini
new file mode 100644
index 0000000..145fba9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@levelpack.ini
@@ -0,0 +1,248 @@
+# based on http://www.deveria.com/alexis/lemmings/lemmings/amigacodes.html
+# and http://home.wanadoo.nl/lemmings-solution/index2.html
+# and http://www.deinonych.com/lemmings/lemm1fun_walk.shtml
+
+name = Oh No! More Lemmings
+#path = lvl_ohno
+
+maxFallDistance = 126
+
+codeSeed = AHPTDHBBAD
+
+# music selection
+music_0 = tune1.mod
+music_1 = tune2.mod
+music_2 = tune3.mod
+music_3 = tune4.mod
+music_4 = tune5.mod
+music_5 = tune6.mod
+
+# levels of difficulty
+level_0 = Tame
+level_1 = Crazy
+level_2 = Wild
+level_3 = Wicked
+level_4 = Havoc
+
+#Levels: name, code, music id
+
+
+# Level 0 - Tame
+
+# Tame 1 - Down And Out Lemmings
+tame_0 = lvl1100.ini,0
+# Tame 2 - Rent-a-Lemming
+tame_1 = lvl1101.ini,1
+# Tame 3 - Undercover Lemming
+tame_2 = lvl1102.ini,2
+# Tame 4 - Downwardly Mobile Lemmings
+tame_3 = lvl1103.ini,3
+# Tame 5 - Snuggle up to a Lemming
+tame_4 = lvl1104.ini,4
+# Tame 6 - Intsy-Wintsy...Lemming?
+tame_5 = lvl1105.ini,5
+# Tame 7 - Who's That Lemming
+tame_6 = lvl1106.ini,0
+# Tame 8 - Dangerzone
+tame_7 = lvl1107.ini,1
+# Tame 9 - And now this...
+tame_8 = lvl1110.ini,2
+# Tame 10 - New Lemmings On The Block
+tame_9 = lvl1111.ini,3
+# Tame 11 - With Compliments
+tame_10 = lvl1112.ini,4
+# Tame 12 - Citizen Lemming
+tame_11 = lvl1113.ini,5
+# Tame 13 - Thunder-Lemmings are go!
+tame_12 = lvl1114.ini,0
+# Tame 14 - Get a little extra help
+tame_13 = lvl1115.ini,1
+# Tame 15 - Not just a pretty Lemming
+tame_14 = lvl1116.ini,2
+# Tame 16 - Gone With The Lemming
+tame_15 = lvl1117.ini,3
+# 17-20 were missing in the windows version
+# Tame 17 - Honey, I Saved The Lemmings
+tame_16 = tame_17.ini,4
+# Tame 18 - Lemmings For Presidents!
+tame_17 = tame_18.ini,5
+# Tame 19 - Lemming Productions Present...
+tame_18 = tame_19.ini,0
+# Tame 20 - Custom built for Lemmings
+tame_19 = tame_20.ini,1
+
+
+# Level 1 - Crazy
+
+# Crazy 1 - Quote: "That's a good level"
+crazy_0 = lvl1001.ini,2
+# Crazy 2 - Dolly Dimple
+crazy_1 = lvl1010.ini,3
+# Crazy 3 - Many Lemmings make level work
+crazy_2 = lvl1014.ini,4
+# Crazy 4 - Lemming Express
+crazy_3 = lvl1020.ini,5
+# Crazy 5 - 24 hour Lemathon
+crazy_4 = lvl1021.ini,0
+# Crazy 6 - The Stack
+crazy_5 = lvl1030.ini,1
+# Crazy 7 - And now, the end is near...
+crazy_6 = lvl1031.ini,2
+# Crazy 8 - KEEP ON TRUCKING
+crazy_7 = lvl1035.ini,3
+# Crazy 9 - On the Antarctic Coast
+crazy_8 = lvl1051.ini,4
+# Crazy 10 - ROCKY VI
+crazy_9 = lvl1054.ini,5
+# Crazy 11 - No Problemming!
+crazy_10 = lvl1057.ini,0
+# Crazy 12 - Lemming Friendly
+crazy_11 = lvl1067.ini,1
+# Crazy 13 - It's a trade off
+crazy_12 = lvl1004.ini,2
+# Crazy 14 - Time waits for no Lemming
+crazy_13 = lvl1015.ini,3
+# Crazy 15 - Worra load of old blocks!
+crazy_14 = lvl1016.ini,4
+# Crazy 16 - Across The Gap
+crazy_15 = lvl1026.ini,5
+# Crazy 17 - DIGGING FOR VICTORY
+crazy_16 = lvl1034.ini,0
+# Crazy 18 - NO PROBLEM
+crazy_17 = lvl1037.ini,1
+# Crazy 19 - DON'T PANIC
+crazy_18 = lvl1043.ini,2
+# Crazy 20 - Ice Ice Lemming
+crazy_19 = lvl1064.ini,3
+
+
+# Level 2 - Wild
+
+# Wild 1 - PoP YoR ToP!!!
+wild_0 = lvl1092.ini,4
+# Wild 2 - Lemming Hotel
+wild_1 = lvl1070.ini,5
+# Wild 3 - Lemming Rhythms
+wild_2 = lvl1071.ini,0
+# Wild 4 - Meeting Adjourned
+wild_3 = lvl1072.ini,1
+# Wild 5 - Lemming Head
+wild_4 = lvl1073.ini,2
+# Wild 6 - Just A Quicky
+wild_5 = lvl1075.ini,3
+# Wild 7 - You Take the High Road
+wild_6 = lvl1086.ini,4
+# Wild 8 - It's a tight fit!
+wild_7 = lvl1007.ini,5
+# Wild 9 - Ice Station Lemming
+wild_8 = lvl1022.ini,0
+# Wild 10 - Higgledy Piggledy
+wild_9 = lvl1025.ini,1
+# Wild 11 - Mutiny On The Bounty
+wild_10 = lvl1033.ini,2
+# Wild 12 - SNOW JOKE
+wild_11 = lvl1036.ini,3
+# Wild 13 - ONWARD AND UPWARD
+wild_12 = lvl1040.ini,4
+# Wild 14 - ICE SPY
+wild_13 = lvl1042.ini,5
+# Wild 15 - THE SILENCE OF THE LEMMINGS
+wild_14 = lvl1044.ini,0
+# Wild 16 - Take care, Sweetie
+wild_15 = lvl1050.ini,1
+# Wild 17 - The Chain with no name
+wild_16 = lvl1063.ini,2
+# Wild 18 - Dr Lemminggood
+wild_17 = lvl1065.ini,3
+# Wild 19 - Lemmingdelica
+wild_18 = lvl1066.ini,4
+# Wild 20 - Got anything....Lemmingy???
+wild_19 = lvl1047.ini,5
+
+
+# Level 3 - Wicked
+
+# Wicked 1 - LeMming ToMato KetchUp fAcilitY
+wicked_0 = lvl1096.ini,0
+# Wicked 2 - Introducing SUPERLEMMING
+wicked_1 = lvl1046.ini,1
+# Wicked 3 - This Corrosion
+wicked_2 = lvl1090.ini,2
+# Wicked 4 - Oh No! It's the 4TH DIMENSION!
+wicked_3 = lvl1091.ini,3
+# Wicked 5 - Chill out!
+wicked_4 = lvl1005.ini,4
+# Wicked 6 - PoP TiL YoU DrOp!
+wicked_5 = lvl1094.ini,5
+# Wicked 7 - Last Lemming To Lemmingcentral
+wicked_6 = lvl1061.ini,0
+# Wicked 8 - A TOWERING PROBLEM
+wicked_7 = lvl1006.ini,1
+# Wicked 9 - How on Earth?
+wicked_8 = lvl1012.ini,2
+# Wicked 10 - Temple of Love
+wicked_9 = lvl1087.ini,3
+# Wicked 11 - ROCKY ROAD
+wicked_10 = lvl1041.ini,4
+# Wicked 12 - Suicidal Tendencies
+wicked_11 = lvl1055.ini,5
+# Wicked 13 - Almost Nearly Virtual Reality
+wicked_12 = lvl1060.ini,0
+# Wicked 14 - The Lemming Learning Curve
+wicked_13 = lvl1062.ini,1
+# Wicked 15 - SPAM,SPAM,SPAM,EGG AND LEMMING
+wicked_14 = lvl1077.ini,2
+# Wicked 16 - Five Alive
+wicked_15 = lvl1081.ini,3
+# Wicked 17 - Down the tube
+wicked_16 = lvl1097.ini,4
+# Wicked 18 - LoTs moRe wHeRe TheY caMe fRom
+wicked_17 = lvl1093.ini,5
+# Wicked 19 - Up, Down or Round and Round
+wicked_18 = lvl1080.ini,0
+# Wicked 20 - The Lemming Funhouse
+wicked_19 = lvl1076.ini,1
+
+
+# Level 4 - Havoc
+
+# Havoc 1 - Tubular Lemmings
+havoc_0 = lvl1074.ini,2
+# Havoc 2 - Be more than just a number
+havoc_1 = lvl1053.ini,3
+# Havoc 3 - It's the price you have to pay
+havoc_2 = lvl1032.ini,4
+# Havoc 4 - The race against cliches
+havoc_3 = lvl1027.ini,5
+# Havoc 5 - There's madness in the method
+havoc_4 = lvl1024.ini,0
+# Havoc 6 - Now get out of that!
+havoc_5 = lvl1023.ini,1
+# Havoc 7 - Creature Discomforts
+havoc_6 = lvl1052.ini,2
+# Havoc 8 - Lemming about town
+havoc_7 = lvl1017.ini,3
+# Havoc 9 - AAAAAARRRRRRGGGGGGHHHHHH!!!!!!
+havoc_8 = lvl1011.ini,4
+# Havoc 10 - Flow Control
+havoc_9 = lvl1003.ini,5
+# Havoc 11 - Welcome to the party, pal!
+havoc_10 = lvl1002.ini,0
+# Havoc 12 - It's all a matter of timing
+havoc_11 = lvl1000.ini,1
+# Havoc 13 - HIGHLAND FLING
+havoc_12 = lvl1045.ini,2
+# Havoc 14 - Synchronised Lemming
+havoc_13 = lvl1085.ini,3
+# Havoc 15 - Have an ice day
+havoc_14 = lvl1013.ini,4
+# Havoc 16 - Scaling the Heights
+havoc_15 = lvl1082.ini,5
+# Havoc 17 - Where Lemmings Dare
+havoc_16 = lvl1083.ini,0
+# Havoc 18 - Lemmings in a situation
+havoc_17 = lvl1056.ini,1
+# Havoc 19 - Looks a Bit Nippy Out There
+havoc_18 = lvl1084.ini,2
+# Havoc 20 - LOoK BeFoRe YoU LeAp!
+havoc_19 = lvl1095.ini,3
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1000.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1000.dif
new file mode 100644
index 0000000..b49cf6e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1000.dif
@@ -0,0 +1 @@
+ï¾­Þ³²…Ö|eÖÕÿà­– \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1001.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1001.dif
new file mode 100644
index 0000000..9e3bf42
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1001.dif
@@ -0,0 +1 @@
+ï¾­Þ™™”¥íî’¥Oûÿà­Î1200« \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1002.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1002.dif
new file mode 100644
index 0000000..67dfb53
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1002.dif
@@ -0,0 +1 @@
+ï¾­ÞÄS¦‹eSÀÏÿà­Ï852è44.8448844875656,92489248R \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1003.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1003.dif
new file mode 100644
index 0000000..2152583
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1003.dif
@@ -0,0 +1 @@
+ï¾­Þ€ÿo.ÖÄO.¯ÿà­ã \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1004.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1004.dif
new file mode 100644
index 0000000..dbcc6c0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1004.dif
@@ -0,0 +1 @@
+ï¾­Þð*ï*õHãHõÿà­Î748‚) \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1005.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1005.dif
new file mode 100644
index 0000000..0dc6f29
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1005.dif
@@ -0,0 +1 @@
+ï¾­ÞÍÌÕù;¶ù¡¨ÿà­Ð592Ý \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1006.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1006.dif
new file mode 100644
index 0000000..5a43f20
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1006.dif
@@ -0,0 +1 @@
+ï¾­ÞÇÆÀT ã Tr4ÿà­ª \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1007.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1007.dif
new file mode 100644
index 0000000..385bc46
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1007.dif
@@ -0,0 +1 @@
+ï¾­Þœ œ VeßfZÿà­Ð1232¬
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1010.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1010.dif
new file mode 100644
index 0000000..4bca98f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1010.dif
@@ -0,0 +1 @@
+ï¾­Þ‰ ˆ ¨—Œˆ—)Åÿà­ì \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1011.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1011.dif
new file mode 100644
index 0000000..186afe9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1011.dif
@@ -0,0 +1 @@
+ï¾­ÞÎÍ•Vm¦uVÐöÿà­± \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1012.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1012.dif
new file mode 100644
index 0000000..ed03ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1012.dif
@@ -0,0 +1 @@
+ï¾­Þöõ˜†Ztx†Ì/ÿà­Ù \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1013.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1013.dif
new file mode 100644
index 0000000..1851f81
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1013.dif
@@ -0,0 +1 @@
+ï¾­ÞËÊu?vgU?HHÿà­® \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1014.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1014.dif
new file mode 100644
index 0000000..d6e698e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1014.dif
@@ -0,0 +1 @@
+ï¾­Þîí©Ä«î¨6Xÿà­Ñ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1015.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1015.dif
new file mode 100644
index 0000000..acc4800
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1015.dif
@@ -0,0 +1 @@
+ï¾­Þ÷önéļNéHÿà­Ú \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1016.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1016.dif
new file mode 100644
index 0000000..874d808
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1016.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1017.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1017.dif
new file mode 100644
index 0000000..403deee
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1017.dif
@@ -0,0 +1 @@
+ï¾­Þ™2˜2æ",ÒÊ"ç ÿà­Î7­0 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1020.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1020.dif
new file mode 100644
index 0000000..b8ec1c1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1020.dif
@@ -0,0 +1 @@
+ï¾­Þû6ú6E±P\%±õæÿà­Þ6 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1021.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1021.dif
new file mode 100644
index 0000000..6aefb81
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1021.dif
@@ -0,0 +1 @@
+ï¾­Þ¡ ðVË{ÐVÝQÿà­„ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1022.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1022.dif
new file mode 100644
index 0000000..7da31b7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1022.dif
@@ -0,0 +1 @@
+ï¾­Þ¨§Gü”'üY™ÿà­Ð880¸ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1023.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1023.dif
new file mode 100644
index 0000000..5420a3f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1023.dif
@@ -0,0 +1 @@
+ï¾­ÞÓÒ¯é…ü—éŽ?ÿà­Ð756ã \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1024.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1024.dif
new file mode 100644
index 0000000..59f41bd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1024.dif
@@ -0,0 +1 @@
+ï¾­Þ¥¤VP2P¿5ÿà­Ï104¶ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1025.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1025.dif
new file mode 100644
index 0000000..23762b7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1025.dif
@@ -0,0 +1 @@
+ï¾­ÞÂÁ}Ÿ1gíÿà­Î744Ô \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1026.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1026.dif
new file mode 100644
index 0000000..62ca583
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1026.dif
@@ -0,0 +1 @@
+ï¾­Þ¬«“W\ësWÀÿà­ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1027.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1027.dif
new file mode 100644
index 0000000..a13d394
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1027.dif
@@ -0,0 +1 @@
+ï¾­Þ£,¢,às¯ÆÃs<ÿà­Ñ912²* \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1030.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1030.dif
new file mode 100644
index 0000000..904dd46
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1030.dif
@@ -0,0 +1 @@
+ï¾­Þ´3ÙáÅ3KŒÿà­Î1224¯ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1031.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1031.dif
new file mode 100644
index 0000000..b1e7c3a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1031.dif
@@ -0,0 +1 @@
+ï¾­Þ¥¤•ÏTÞuÏæsÿà­ˆ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1032.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1032.dif
new file mode 100644
index 0000000..bb995a6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1032.dif
@@ -0,0 +1,2 @@
+ï¾­Þþ-ý-5«
+ «^'ÿà­á- \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1033.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1033.dif
new file mode 100644
index 0000000..67a815c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1033.dif
@@ -0,0 +1 @@
+ï¾­ÞÛ"Û"ÇFé8ÅFÑ?ÿà­Î1200í \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1034.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1034.dif
new file mode 100644
index 0000000..d0f88d9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1034.dif
@@ -0,0 +1 @@
+ï¾­Þð4ï4ªyç΂yÖ®ÿà­Î300‚3 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1035.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1035.dif
new file mode 100644
index 0000000..be0f662
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1035.dif
@@ -0,0 +1 @@
+ï¾­Þý5ü5“•ljs•àÿà­à5 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1036.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1036.dif
new file mode 100644
index 0000000..cb9dd86
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1036.dif
@@ -0,0 +1 @@
+ï¾­Þ‹$Š$eu{ÐEu>‰ÿà­î# \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1037.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1037.dif
new file mode 100644
index 0000000..44deab4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1037.dif
@@ -0,0 +1 @@
+ï¾­ÞÊHÉH‘ÏðPqϦÁÿà­­H \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1040.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1040.dif
new file mode 100644
index 0000000..3ba5a60
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1040.dif
@@ -0,0 +1 @@
+ï¾­ÞÐJÏJ\o$<etÿà­³J \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1041.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1041.dif
new file mode 100644
index 0000000..0ae3783
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1041.dif
@@ -0,0 +1 @@
+ï¾­Þƒd‚dÛ‹Å´Öÿà­Ï248”b \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1042.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1042.dif
new file mode 100644
index 0000000..f32c1e0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1042.dif
@@ -0,0 +1 @@
+ï¾­Þƒ(‚(/íyñíÒÿà­Î300•& \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1043.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1043.dif
new file mode 100644
index 0000000..bf36fe6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1043.dif
@@ -0,0 +1 @@
+ï¾­ÞÇ,Æ,wz¾Wzöïÿà­ª, \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1044.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1044.dif
new file mode 100644
index 0000000..a0474a0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1044.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1045.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1045.dif
new file mode 100644
index 0000000..9c6117d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1045.dif
@@ -0,0 +1 @@
+ï¾­Þ¢A¡Aqê†,QêKÿà­…A \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1046.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1046.dif
new file mode 100644
index 0000000..8607bed
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1046.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1047.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1047.dif
new file mode 100644
index 0000000..23fc1a9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1047.dif
@@ -0,0 +1 @@
+ï¾­Þ›š߆ƒ«¿†Fâÿà­þ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1050.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1050.dif
new file mode 100644
index 0000000..b5fb60c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1050.dif
@@ -0,0 +1 @@
+ï¾­Þ‘®d:DŽd=Œÿà­ô \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1051.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1051.dif
new file mode 100644
index 0000000..220fd6c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1051.dif
@@ -0,0 +1 @@
+ï¾­Þ­¬æ1º•Æ1=úÿà­ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1052.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1052.dif
new file mode 100644
index 0000000..8de7b79
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1052.dif
@@ -0,0 +1 @@
+ï¾­Þü(û(«-Žz&ÿà­Ò856Š' \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1053.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1053.dif
new file mode 100644
index 0000000..0e66e21
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1053.dif
@@ -0,0 +1 @@
+ï¾­Þœ¿E…7ŸEŽÿà­€ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1054.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1054.dif
new file mode 100644
index 0000000..24c3411
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1054.dif
@@ -0,0 +1 @@
+ï¾­ÞÒ&Ð&JÂXëÂuÿà­Î97ä$ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1055.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1055.dif
new file mode 100644
index 0000000..4fe4ac3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1055.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1056.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1056.dif
new file mode 100644
index 0000000..f5f5dca
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1056.dif
@@ -0,0 +1 @@
+ï¾­ÞÍ"Ì"Hß‘åGbbÿà­°" \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1057.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1057.dif
new file mode 100644
index 0000000..570f6c2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1057.dif
@@ -0,0 +1 @@
+ï¾­Þ¤J£JojæP$`ÿà­Ð04µH \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1060.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1060.dif
new file mode 100644
index 0000000..c9124cb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1060.dif
@@ -0,0 +1 @@
+ï¾­Þ”“Lz,§ñÿà­÷ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1061.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1061.dif
new file mode 100644
index 0000000..a705c54
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1061.dif
@@ -0,0 +1 @@
+ï¾­Þ¿¾ƒ8#æc8uHÿà­¢ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1062.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1062.dif
new file mode 100644
index 0000000..e3e5c80
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1062.dif
@@ -0,0 +1 @@
+ï¾­ÞÜÛA½Y-«Qÿà­Î888î \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1063.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1063.dif
new file mode 100644
index 0000000..c61b3f5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1063.dif
@@ -0,0 +1 @@
+ï¾­Þ‡† bÚëal#ÿà­ê \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1064.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1064.dif
new file mode 100644
index 0000000..49d954f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1064.dif
@@ -0,0 +1 @@
+ï¾­ÞžtÙH×YÙ+'ÿà­Ñ876­ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1065.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1065.dif
new file mode 100644
index 0000000..98bf28f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1065.dif
@@ -0,0 +1 @@
+ï¾­Þüûhø¿HøSüÿà­ß \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1066.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1066.dif
new file mode 100644
index 0000000..96b0cc2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1066.dif
@@ -0,0 +1 @@
+ï¾­Þ®"­"é7ekÉ7È?ÿà­‘" \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1067.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1067.dif
new file mode 100644
index 0000000..4cdb65c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1067.dif
@@ -0,0 +1 @@
+ï¾­Þ¦¥æÆ\LÏÆËáÿà­Î848¸ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1070.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1070.dif
new file mode 100644
index 0000000..44cdf60
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1070.dif
@@ -0,0 +1 @@
+ï¾­Þ’"‘"]7T•=77mÿà­õ! \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1071.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1071.dif
new file mode 100644
index 0000000..5f9d1c7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1071.dif
@@ -0,0 +1 @@
+ï¾­ÞÖÕhÐÁpHÐ$€ÿà­¹ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1072.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1072.dif
new file mode 100644
index 0000000..55c9f1a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1072.dif
@@ -0,0 +1 @@
+ï¾­Þ÷ ÷ 5^YJ“Çÿà­Î1388‰ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1073.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1073.dif
new file mode 100644
index 0000000..6cc12ae
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1073.dif
@@ -0,0 +1 @@
+ï¾­Þ— – Íÿ­ÿHþÿà­ú \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1074.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1074.dif
new file mode 100644
index 0000000..36ed698
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1074.dif
@@ -0,0 +1 @@
+ï¾­ÞÜ%Û%u¤÷†T¤µÿà­Ï852í# \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1075.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1075.dif
new file mode 100644
index 0000000..9906aaa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1075.dif
@@ -0,0 +1 @@
+ï¾­Þš™\i&ç<i.ÿà­ý \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1076.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1076.dif
new file mode 100644
index 0000000..138f3cb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1076.dif
@@ -0,0 +1 @@
+ï¾­ÞÈ*Ç*ª=ÏÊ=òÿà­«* \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1077.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1077.dif
new file mode 100644
index 0000000..76da921
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1077.dif
@@ -0,0 +1 @@
+ï¾­Þ±°‰×¼_‰Úðÿà­” \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1080.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1080.dif
new file mode 100644
index 0000000..d44bceb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1080.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1081.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1081.dif
new file mode 100644
index 0000000..f0ae70f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1081.dif
@@ -0,0 +1 @@
+ï¾­ÞÒÐgû~,û‹ ÿà­¡22, 138, 96, 26 80, 46, 28 8, 48, 488, 138, 48, 480 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1082.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1082.dif
new file mode 100644
index 0000000..0c9205a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1082.dif
@@ -0,0 +1 @@
+ï¾­Þº¹˜4áx4Ääÿà­ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1083.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1083.dif
new file mode 100644
index 0000000..2ed1ac5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1083.dif
@@ -0,0 +1 @@
+ï¾­ÞŒ8‹8~Ψˆ^Î<ÿà­ï7 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1084.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1084.dif
new file mode 100644
index 0000000..542ad80
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1084.dif
@@ -0,0 +1 @@
+ï¾­ÞîNíNË‘î?«‘$Lÿà­ÑN \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1085.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1085.dif
new file mode 100644
index 0000000..b5221ec
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1085.dif
@@ -0,0 +1,2 @@
+ï¾­Þ¿8¿8Ù{dÄÙE«ÿà­Ï1236û0949494949494422842284228422842284228Ä80261428814
+0, 28, 12684 8840260 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1086.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1086.dif
new file mode 100644
index 0000000..2a542d5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1086.dif
@@ -0,0 +1 @@
+ï¾­Þô1ó1oUvLÚ ÿà­Ð164„0 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1087.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1087.dif
new file mode 100644
index 0000000..17876ef
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1087.dif
@@ -0,0 +1 @@
+ï¾­ÞÚÙçU"ËU¹…ÿà­Ð6ì \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1090.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1090.dif
new file mode 100644
index 0000000..7ebb9af
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1090.dif
@@ -0,0 +1 @@
+ï¾­Þâá„÷—¥d÷‰#ÿà­Å \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1091.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1091.dif
new file mode 100644
index 0000000..39bda94
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1091.dif
@@ -0,0 +1 @@
+ï¾­Þ–•©f·÷¨Ø^ÿà­ù \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1092.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1092.dif
new file mode 100644
index 0000000..2cf4eea
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1092.dif
@@ -0,0 +1 @@
+ï¾­ÞÜ,Û,~tu8^t gÿà­¿, \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1093.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1093.dif
new file mode 100644
index 0000000..c350155
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1093.dif
@@ -0,0 +1 @@
+ï¾­Þ»º»\©£\¹¾ÿà­Ð788Ë \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1094.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1094.dif
new file mode 100644
index 0000000..a645a1b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1094.dif
@@ -0,0 +1 @@
+ï¾­ÞUŒUEçõD+«ÿà­Ñ044œS \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1095.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1095.dif
new file mode 100644
index 0000000..fcbb21b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1095.dif
@@ -0,0 +1 @@
+ï¾­Þ€ÿZ –: ¹Cÿà­ã \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1096.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1096.dif
new file mode 100644
index 0000000..3469f94
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1096.dif
@@ -0,0 +1 @@
+ï¾­Þëê7)ê£)¼ÿà­Î \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1097.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1097.dif
new file mode 100644
index 0000000..daafed5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1097.dif
@@ -0,0 +1 @@
+ï¾­ÞÅÄØFçë×<¢ÿà­Ï8Ø \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1100.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1100.dif
new file mode 100644
index 0000000..3f2b357
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1100.dif
@@ -0,0 +1 @@
+ï¾­Þ‰ˆÒHJu²HMÎÿà­ì \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1101.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1101.dif
new file mode 100644
index 0000000..f1a48da
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1101.dif
@@ -0,0 +1 @@
+ï¾­ÞÜ Û gk°L±ÿà­Ö908æ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1102.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1102.dif
new file mode 100644
index 0000000..a0d8ee1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1102.dif
@@ -0,0 +1 @@
+ï¾­Þó'ò'ïÒ«èî•'ÿà­Ö' \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1103.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1103.dif
new file mode 100644
index 0000000..e02e2e5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1103.dif
@@ -0,0 +1 @@
+ï¾­Þƒ‚pR^ëKRîœÿà­Ö820 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1104.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1104.dif
new file mode 100644
index 0000000..b1b2955
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1104.dif
@@ -0,0 +1 @@
+ï¾­Þ©$¨$Þé)¾ÝÞÿà­Œ$ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1105.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1105.dif
new file mode 100644
index 0000000..36090f7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1105.dif
@@ -0,0 +1 @@
+ï¾­Þ´³@‰’î?;vÿà­— \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1106.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1106.dif
new file mode 100644
index 0000000..7bebff0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1106.dif
@@ -0,0 +1 @@
+ï¾­Þëêƒã#¥cãõ1ÿà­Î \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1107.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1107.dif
new file mode 100644
index 0000000..41d3f60
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1107.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1110.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1110.dif
new file mode 100644
index 0000000..84ea415
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1110.dif
@@ -0,0 +1 @@
+ï¾­Þëëœ@šu±@™ÿà­Ö1228õ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1111.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1111.dif
new file mode 100644
index 0000000..6c16e59
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1111.dif
@@ -0,0 +1 @@
+ï¾­ÞÀ"¿"IK,7K‚Fÿà­Ö748Ê \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1112.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1112.dif
new file mode 100644
index 0000000..86173b0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1112.dif
@@ -0,0 +1 @@
+ï¾­ÞÇ Æ ‘ÇΑzÇçÿà­Ö848Ñ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1113.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1113.dif
new file mode 100644
index 0000000..00d49a5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1113.dif
@@ -0,0 +1 @@
+ï¾­ÞÒ%Ñ%¡§n§Q/ÿà­µ% \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1114.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1114.dif
new file mode 100644
index 0000000..85796cd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1114.dif
@@ -0,0 +1 @@
+ï¾­Þº¹ VHÉ€V:ÿà­ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1115.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1115.dif
new file mode 100644
index 0000000..934e1c4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1115.dif
@@ -0,0 +1 @@
+ï¾­ÞØ;×;(HuH™¸ÿà­Ö848â9 \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1116.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1116.dif
new file mode 100644
index 0000000..a617144
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1116.dif
@@ -0,0 +1 @@
+ï¾­Þ¸·ÁÏ¡Ï;"ÿà­› \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1117.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1117.dif
new file mode 100644
index 0000000..ade7686
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@lvl1117.dif
@@ -0,0 +1 @@
+ï¾­Þ·¶Ër«raaÿà­š \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_17.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_17.ini
new file mode 100644
index 0000000..54880dc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_17.ini
@@ -0,0 +1,123 @@
+# LVL extracted by Lemmini #LVL Tame17.lvl
+releaseRate = 1
+numLemmings = 50
+numToRescue = 25
+timeLimit = 4
+numClimbers = 20
+numFloaters = 20
+numBombers = 20
+numBlockers = 20
+numBuilders = 20
+numBashers = 20
+numMiners = 20
+numDiggers = 20
+xPos = 1056
+style = brick
+
+# Objects
+# id, xpos, ypos, paint mode (), upside down (0,1)
+# paint modes: 8=VIS_ON_TERRAIN, 4=NO_OVERWRITE, 0=FULL (only one value possible)
+object_0 = 1, 1120, 32, 4, 0
+object_1 = 0, 1152, 168, 4, 0
+object_2 = 9, 1152, 200, 4, 0
+
+# Terrain
+# id, xpos, ypos, modifier
+# modifier: 8=NO_OVERWRITE, 4=UPSIDE_DOWN, 2=REMOVE (combining allowed, 0=FULL)
+terrain_0 = 42, 1126, 238, 0
+terrain_1 = 42, 1158, 238, 0
+terrain_2 = 42, 1190, 238, 0
+terrain_3 = 42, 1222, 238, 0
+terrain_4 = 42, 1254, 238, 0
+terrain_5 = 42, 1286, 238, 0
+terrain_6 = 42, 1318, 238, 0
+terrain_7 = 42, 1350, 238, 0
+terrain_8 = 42, 1382, 238, 0
+terrain_9 = 42, 1414, 238, 0
+terrain_10 = 42, 1446, 238, 0
+terrain_11 = 42, 1478, 238, 0
+terrain_12 = 42, 1510, 238, 0
+terrain_13 = 42, 1542, 238, 0
+terrain_14 = 42, 1574, 238, 0
+terrain_15 = 42, 1606, 238, 0
+terrain_16 = 42, 1574, 206, 0
+terrain_17 = 42, 1574, 174, 0
+terrain_18 = 42, 1574, 142, 0
+terrain_19 = 42, 1574, 110, 0
+terrain_20 = 42, 1574, 78, 0
+terrain_21 = 42, 1574, 46, 0
+terrain_22 = 42, 1462, 206, 0
+terrain_23 = 42, 1462, 174, 0
+terrain_24 = 42, 1430, 174, 0
+terrain_25 = 42, 1398, 174, 0
+terrain_26 = 42, 1366, 174, 0
+terrain_27 = 40, 1302, 170, 0
+terrain_28 = 40, 1248, 134, 0
+terrain_29 = 40, 1302, 142, 0
+terrain_30 = 40, 1248, 106, 0
+terrain_31 = 42, 1216, 106, 0
+terrain_32 = 42, 1184, 106, 0
+terrain_33 = 42, 1152, 106, 0
+terrain_34 = 42, 1120, 106, 0
+terrain_35 = 42, 1088, 106, 0
+terrain_36 = 22, 1056, -8, 0
+terrain_37 = 22, 1120, -8, 0
+terrain_38 = 22, 1184, -8, 0
+terrain_39 = 22, 1248, -8, 0
+terrain_40 = 22, 1312, -8, 0
+terrain_41 = 22, 1376, -8, 0
+terrain_42 = 22, 1440, -8, 0
+terrain_43 = 22, 1504, -8, 0
+terrain_44 = 22, 1568, -8, 0
+terrain_45 = 22, 1632, -8, 0
+terrain_46 = 31, 1486, 8, 0
+terrain_47 = 30, 1486, 24, 0
+terrain_48 = 29, 1486, 56, 0
+terrain_49 = 27, 1482, 72, 0
+terrain_50 = 27, 1466, 88, 0
+terrain_51 = 27, 1498, 88, 0
+terrain_52 = 27, 1482, 104, 0
+terrain_53 = 31, 1370, 8, 0
+terrain_54 = 29, 1370, 24, 0
+terrain_55 = 27, 1366, 40, 0
+terrain_56 = 27, 1382, 56, 0
+terrain_57 = 27, 1398, 72, 0
+terrain_58 = 27, 1414, 56, 0
+terrain_59 = 27, 1430, 40, 0
+terrain_60 = 29, 1434, 24, 0
+terrain_61 = 31, 1434, 8, 0
+terrain_62 = 1, 1056, 298, 0
+terrain_63 = 1, 1184, 298, 0
+terrain_64 = 1, 1312, 298, 0
+terrain_65 = 1, 1440, 298, 0
+terrain_66 = 1, 1568, 298, 0
+terrain_67 = 15, 1664, 262, 0
+terrain_68 = 15, 1664, 226, 0
+terrain_69 = 15, 1664, 190, 0
+terrain_70 = 15, 1664, 154, 0
+terrain_71 = 15, 1664, 118, 0
+terrain_72 = 15, 1664, 82, 0
+terrain_73 = 15, 1664, 46, 0
+terrain_74 = 15, 1664, 10, 0
+terrain_75 = 15, 1664, 8, 8
+terrain_76 = 15, 1056, 262, 8
+terrain_77 = 15, 1056, 226, 8
+terrain_78 = 15, 1056, 190, 8
+terrain_79 = 15, 1056, 154, 8
+terrain_80 = 15, 1056, 118, 8
+terrain_81 = 15, 1056, 82, 8
+terrain_82 = 15, 1056, 46, 8
+terrain_83 = 15, 1056, 10, 8
+terrain_84 = 15, 1056, 8, 8
+terrain_85 = 28, 1398, 142, 8
+terrain_86 = 29, 1402, 126, 8
+terrain_87 = 30, 1402, 102, 8
+terrain_88 = 31, 1402, 104, 0
+terrain_89 = 26, 1494, 194, 8
+terrain_90 = 26, 1526, 194, 8
+
+#Steel
+# id, xpos, ypos, width, height
+
+#Name
+name = Honey, I Saved The Lemmings
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_18.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_18.ini
new file mode 100644
index 0000000..ffcc9a3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_18.ini
@@ -0,0 +1,131 @@
+# LVL extracted by Lemmini #LVL Tame18.lvl
+releaseRate = 1
+numLemmings = 50
+numToRescue = 25
+timeLimit = 4
+numClimbers = 20
+numFloaters = 20
+numBombers = 20
+numBlockers = 20
+numBuilders = 20
+numBashers = 20
+numMiners = 20
+numDiggers = 20
+xPos = 1268
+style = rock
+
+# Objects
+# id, xpos, ypos, paint mode (), upside down (0,1)
+# paint modes: 8=VIS_ON_TERRAIN, 4=NO_OVERWRITE, 0=FULL (only one value possible)
+object_0 = 1, 1728, 88, 4, 0
+object_1 = 0, 960, 224, 4, 0
+
+# Terrain
+# id, xpos, ypos, modifier
+# modifier: 8=NO_OVERWRITE, 4=UPSIDE_DOWN, 2=REMOVE (combining allowed, 0=FULL)
+terrain_0 = 1, 1510, 22, 0
+terrain_1 = 1, 1356, 102, 0
+terrain_2 = 1, 1186, 206, 0
+terrain_3 = 0, 1186, -4, 0
+terrain_4 = 0, 1186, -78, 0
+terrain_5 = 0, 1354, -78, 0
+terrain_6 = 0, 1464, 228, 0
+terrain_7 = 0, 1622, 148, 0
+terrain_8 = 0, 1494, -182, 0
+terrain_9 = 7, 1622, 278, 0
+terrain_10 = 7, 1428, 280, 0
+terrain_11 = 7, 1274, 274, 0
+terrain_12 = 7, 1046, 264, 0
+terrain_13 = 7, 1620, 176, 0
+terrain_14 = 7, 1756, 172, 0
+terrain_15 = 7, 1832, 268, 0
+terrain_16 = 3, 1922, 172, 0
+terrain_17 = 0, 1898, -26, 0
+terrain_18 = 2, 1930, -24, 0
+terrain_19 = 2, 1850, -50, 0
+terrain_20 = 1, 1548, 228, 8
+terrain_21 = 1, 1456, 198, 8
+terrain_22 = 3, 1204, 216, 8
+terrain_23 = 3, 1254, 240, 8
+terrain_24 = 11, 1022, 286, 8
+terrain_25 = 11, 990, 286, 8
+terrain_26 = 11, 958, 286, 8
+terrain_27 = 10, 942, 286, 8
+terrain_28 = 8, 952, 290, 8
+terrain_29 = 7, 770, 288, 8
+terrain_30 = 6, 778, 226, 8
+terrain_31 = 5, 774, 168, 8
+terrain_32 = 6, 774, 134, 8
+terrain_33 = 7, 684, 78, 8
+terrain_34 = 7, 1700, -26, 8
+terrain_35 = 7, 1532, -26, 8
+terrain_36 = 7, 1362, -26, 8
+terrain_37 = 7, 1202, -24, 8
+terrain_38 = 27, 1970, 124, 8
+terrain_39 = 27, 2040, 212, 8
+terrain_40 = 27, 1430, 76, 8
+terrain_41 = 26, 862, 240, 8
+terrain_42 = 28, 2040, 232, 8
+terrain_43 = 29, 2040, 180, 8
+terrain_44 = 30, 2046, 156, 8
+terrain_45 = 39, 2046, 192, 8
+terrain_46 = 40, 2050, 154, 8
+terrain_47 = 38, 2040, 146, 8
+terrain_48 = 38, 2034, 180, 8
+terrain_49 = 39, 1984, 132, 8
+terrain_50 = 38, 1966, 116, 8
+terrain_51 = 39, 1976, 96, 8
+terrain_52 = 29, 1430, 68, 8
+terrain_53 = 30, 1434, 48, 8
+terrain_54 = 30, 878, 216, 8
+terrain_55 = 40, 1644, 84, 8
+terrain_56 = 40, 1608, 60, 8
+terrain_57 = 20, 2064, 184, 0
+terrain_58 = 20, 1998, 124, 0
+terrain_59 = 20, 1952, 106, 0
+terrain_60 = 22, 2004, 90, 0
+terrain_61 = 22, 2058, 148, 0
+terrain_62 = 23, 2024, 176, 0
+terrain_63 = 23, 2036, 144, 0
+terrain_64 = 23, 1656, 78, 0
+terrain_65 = 22, 1622, 54, 0
+terrain_66 = 22, 1428, 44, 0
+terrain_67 = 22, 1436, 72, 0
+terrain_68 = 22, 1416, 90, 0
+terrain_69 = 20, 864, 204, 0
+terrain_70 = 21, 832, 242, 0
+terrain_71 = 13, 1676, 266, 0
+terrain_72 = 13, 1734, 268, 0
+terrain_73 = 13, 1792, 264, 0
+terrain_74 = 13, 1848, 264, 0
+terrain_75 = 13, 1870, 258, 0
+terrain_76 = 52, 1918, -6, 4
+terrain_77 = 49, 1896, -12, 4
+terrain_78 = 49, 1680, -12, 4
+terrain_79 = 49, 1502, -12, 4
+terrain_80 = 49, 1478, -16, 4
+terrain_81 = 52, 1320, -10, 4
+terrain_82 = 50, 1170, 6, 12
+terrain_83 = 51, 1176, 48, 12
+terrain_84 = 51, 1160, 26, 12
+terrain_85 = 47, 1168, 142, 8
+terrain_86 = 47, 1178, 106, 8
+terrain_87 = 47, 1162, 114, 8
+terrain_88 = 48, 1150, 88, 8
+terrain_89 = 54, 1234, 82, 8
+terrain_90 = 53, 1234, 44, 8
+terrain_91 = 53, 1994, 20, 8
+terrain_92 = 44, 1944, 274, 0
+terrain_93 = 44, 1958, 240, 0
+terrain_94 = 44, 1952, 278, 0
+terrain_95 = 44, 1968, 282, 0
+terrain_96 = 44, 1540, 278, 0
+terrain_97 = 44, 1554, 250, 0
+terrain_98 = 44, 1548, 284, 0
+terrain_99 = 44, 1566, 286, 0
+
+#Steel
+# id, xpos, ypos, width, height
+
+#Name
+name = Lemmings For Presidents!
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_19.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_19.ini
new file mode 100644
index 0000000..8f81eec
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_19.ini
@@ -0,0 +1,101 @@
+# LVL extracted by Lemmini #LVL Tame19.lvl
+releaseRate = 1
+numLemmings = 50
+numToRescue = 25
+timeLimit = 4
+numClimbers = 20
+numFloaters = 20
+numBombers = 20
+numBlockers = 20
+numBuilders = 20
+numBashers = 20
+numMiners = 20
+numDiggers = 20
+xPos = 1056
+style = snow
+
+# Objects
+# id, xpos, ypos, paint mode (), upside down (0,1)
+# paint modes: 8=VIS_ON_TERRAIN, 4=NO_OVERWRITE, 0=FULL (only one value possible)
+object_0 = 1, 1056, 40, 4, 0
+object_1 = 0, 1600, 144, 4, 0
+object_2 = 6, 1664, 128, 4, 0
+
+# Terrain
+# id, xpos, ypos, modifier
+# modifier: 8=NO_OVERWRITE, 4=UPSIDE_DOWN, 2=REMOVE (combining allowed, 0=FULL)
+terrain_0 = 25, 984, 152, 0
+terrain_1 = 25, 1066, 106, 0
+terrain_2 = 25, 1146, 62, 0
+terrain_3 = 25, 1226, 14, 0
+terrain_4 = 25, 1274, -18, 0
+terrain_5 = 25, 916, 192, 0
+terrain_6 = 25, 842, 238, 0
+terrain_7 = 25, 778, 274, 0
+terrain_8 = 25, 938, 274, 0
+terrain_9 = 25, 1012, 230, 0
+terrain_10 = 25, 1076, 190, 0
+terrain_11 = 25, 1150, 146, 0
+terrain_12 = 25, 1222, 100, 0
+terrain_13 = 25, 1304, 50, 0
+terrain_14 = 25, 1380, 4, 0
+terrain_15 = 25, 1434, -26, 0
+terrain_16 = 25, 1090, 270, 0
+terrain_17 = 25, 1170, 224, 0
+terrain_18 = 25, 1248, 182, 0
+terrain_19 = 25, 1324, 136, 0
+terrain_20 = 25, 1398, 88, 0
+terrain_21 = 25, 1472, 38, 0
+terrain_22 = 25, 1550, -12, 0
+terrain_23 = 25, 1234, 272, 0
+terrain_24 = 25, 1310, 228, 8
+terrain_25 = 25, 1388, 180, 8
+terrain_26 = 25, 1462, 134, 8
+terrain_27 = 25, 1538, 86, 8
+terrain_28 = 25, 1612, 38, 8
+terrain_29 = 25, 1682, -4, 8
+terrain_30 = 25, 1718, -26, 8
+terrain_31 = 25, 1406, 268, 8
+terrain_32 = 25, 1480, 220, 8
+terrain_33 = 24, 1558, 214, 8
+terrain_34 = 24, 1596, 214, 8
+terrain_35 = 24, 1628, 214, 8
+terrain_36 = 26, 1706, 214, 8
+terrain_37 = 26, 1766, 252, 8
+terrain_38 = 24, 1302, 304, 0
+terrain_39 = 24, 1260, 304, 0
+terrain_40 = 24, 1126, 306, 0
+terrain_41 = 24, 992, 304, 0
+terrain_42 = 24, 828, 308, 0
+terrain_43 = 26, 1812, 280, 0
+terrain_44 = 25, 988, -22, 0
+terrain_45 = 25, 910, 22, 0
+terrain_46 = 25, 838, 60, 0
+terrain_47 = 25, 772, 96, 0
+terrain_48 = 24, 940, -18, 0
+terrain_49 = 24, 838, -18, 0
+terrain_50 = 24, 746, -14, 0
+terrain_51 = 24, 666, -10, 0
+terrain_52 = 24, 580, -6, 0
+terrain_53 = 23, 580, -20, 0
+terrain_54 = 25, 662, -10, 8
+terrain_55 = 25, 690, 144, 8
+terrain_56 = 25, 624, 184, 8
+terrain_57 = 25, 572, 220, 8
+terrain_58 = 25, 516, 256, 8
+terrain_59 = 25, 480, 280, 8
+terrain_60 = 24, 692, 300, 8
+terrain_61 = 24, 598, 300, 8
+terrain_62 = 24, 526, 296, 8
+terrain_63 = 24, 1342, -22, 8
+terrain_64 = 24, 1488, -18, 8
+terrain_65 = 24, 1626, -16, 8
+terrain_66 = 24, 1208, -22, 8
+terrain_67 = 24, 1116, -22, 8
+terrain_68 = 24, 1040, -22, 8
+
+#Steel
+# id, xpos, ypos, width, height
+
+#Name
+name = Lemming Productions Present...
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_20.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_20.ini
new file mode 100644
index 0000000..b2c96bd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/levels@2_ohno@tame_20.ini
@@ -0,0 +1,128 @@
+# LVL extracted by Lemmini #LVL Tame20.lvl
+releaseRate = 1
+numLemmings = 50
+numToRescue = 25
+timeLimit = 4
+numClimbers = 20
+numFloaters = 20
+numBombers = 20
+numBlockers = 20
+numBuilders = 20
+numBashers = 20
+numMiners = 20
+numDiggers = 20
+xPos = 1201
+style = bubble
+
+# Objects
+# id, xpos, ypos, paint mode (), upside down (0,1)
+# paint modes: 8=VIS_ON_TERRAIN, 4=NO_OVERWRITE, 0=FULL (only one value possible)
+object_0 = 1, 1312, 88, 4, 0
+object_1 = 0, 1824, 80, 4, 0
+object_2 = 6, 1856, 64, 4, 0
+
+# Terrain
+# id, xpos, ypos, modifier
+# modifier: 8=NO_OVERWRITE, 4=UPSIDE_DOWN, 2=REMOVE (combining allowed, 0=FULL)
+terrain_0 = 20, 1348, 256, 0
+terrain_1 = 20, 1412, 256, 0
+terrain_2 = 20, 1476, 256, 0
+terrain_3 = 20, 1540, 256, 0
+terrain_4 = 20, 1604, 256, 0
+terrain_5 = 20, 1668, 256, 0
+terrain_6 = 20, 1732, 256, 0
+terrain_7 = 20, 1796, 256, 0
+terrain_8 = 21, 1284, 256, 0
+terrain_9 = 21, 1348, 192, 0
+terrain_10 = 20, 1412, 192, 0
+terrain_11 = 20, 1476, 192, 0
+terrain_12 = 20, 1540, 192, 0
+terrain_13 = 20, 1604, 192, 0
+terrain_14 = 20, 1668, 192, 0
+terrain_15 = 20, 1732, 192, 0
+terrain_16 = 21, 1412, 128, 0
+terrain_17 = 20, 1476, 128, 0
+terrain_18 = 20, 1540, 128, 0
+terrain_19 = 21, 1476, 64, 0
+terrain_20 = 22, 1540, 64, 0
+terrain_21 = 22, 1604, 128, 0
+terrain_22 = 21, 1668, 128, 0
+terrain_23 = 22, 1732, 128, 0
+terrain_24 = 22, 1796, 192, 0
+terrain_25 = 22, 1860, 256, 0
+terrain_26 = 49, 1816, 134, 8
+terrain_27 = 49, 1880, 134, 8
+terrain_28 = 59, 1790, 134, 8
+terrain_29 = 53, 1944, 124, 8
+terrain_30 = 50, 1954, 66, 0
+terrain_31 = 50, 1954, 4, 0
+terrain_32 = 50, 1954, -14, 0
+terrain_33 = 51, 1954, 38, 0
+terrain_34 = 53, 1986, 28, 0
+terrain_35 = 52, 1922, 28, 4
+terrain_36 = 54, 1976, 60, 2
+terrain_37 = 54, 1930, 60, 2
+terrain_38 = 50, 1914, -28, 4
+terrain_39 = 50, 1996, -24, 4
+terrain_40 = 43, 1264, 150, 0
+terrain_41 = 45, 1328, 168, 0
+terrain_42 = 45, 1392, 168, 0
+terrain_43 = 45, 1454, 168, 0
+terrain_44 = 40, 1506, 130, 0
+terrain_45 = 38, 1548, 106, 0
+terrain_46 = 40, 1602, 64, 0
+terrain_47 = 41, 1624, 18, 0
+terrain_48 = 40, 1688, -24, 0
+terrain_49 = 37, 1564, -24, 0
+terrain_50 = 42, 1480, 172, 0
+terrain_51 = 37, 1500, 224, 0
+terrain_52 = 41, 1562, 266, 0
+terrain_53 = 45, 1626, 262, 0
+terrain_54 = 45, 1684, 262, 0
+terrain_55 = 45, 1746, 262, 0
+terrain_56 = 45, 1810, 262, 0
+terrain_57 = 45, 1872, 262, 0
+terrain_58 = 45, 1936, 262, 0
+terrain_59 = 41, 1950, 266, 0
+terrain_60 = 45, 2014, 262, 0
+terrain_61 = 40, 2076, 224, 0
+terrain_62 = 39, 2076, 160, 0
+terrain_63 = 38, 2016, 160, 0
+terrain_64 = 48, 2012, 212, 4
+terrain_65 = 43, 1204, 150, 0
+terrain_66 = 44, 1282, 86, 0
+terrain_67 = 44, 1282, 22, 0
+terrain_68 = 44, 1282, -36, 0
+terrain_69 = 44, 1282, 212, 0
+terrain_70 = 44, 1282, 264, 0
+terrain_71 = 44, 1222, 214, 0
+terrain_72 = 44, 1222, 274, 0
+terrain_73 = 44, 1222, 90, 0
+terrain_74 = 44, 1222, 30, 0
+terrain_75 = 44, 1222, -30, 0
+terrain_76 = 37, 1140, 130, 0
+terrain_77 = 48, 1136, 100, 0
+terrain_78 = 4, 1228, -50, 8
+terrain_79 = 4, 1262, -50, 8
+terrain_80 = 4, 1314, -50, 8
+terrain_81 = 4, 1374, -50, 8
+terrain_82 = 5, 1420, -46, 8
+terrain_83 = 5, 1274, -30, 8
+terrain_84 = 4, 1318, -10, 8
+terrain_85 = 5, 1356, -18, 8
+terrain_86 = 5, 1456, -50, 8
+terrain_87 = 5, 1492, -50, 8
+terrain_88 = 5, 1540, -32, 8
+terrain_89 = 5, 1580, -20, 8
+terrain_90 = 5, 1624, -30, 8
+terrain_91 = 5, 1674, -22, 8
+terrain_92 = 5, 1712, -22, 8
+terrain_93 = 5, 1748, -28, 8
+terrain_94 = 5, 1784, -36, 8
+terrain_95 = 5, 1826, -46, 8
+
+#Steel
+# id, xpos, ypos, width, height
+
+#Name
+name = Custom built for Lemmings
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@border.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@border.gif
new file mode 100644
index 0000000..b3b89a5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@border.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@cursor.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@cursor.gif
new file mode 100644
index 0000000..8fd9be2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@cursor.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@explode.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@explode.gif
new file mode 100644
index 0000000..62eacbe
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@explode.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_0.gif
new file mode 100644
index 0000000..2d25526
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_1.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_1.gif
new file mode 100644
index 0000000..108955f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_1.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_10.gif
new file mode 100644
index 0000000..fed237a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_11.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_11.gif
new file mode 100644
index 0000000..4d395c3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_11.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_12.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_12.gif
new file mode 100644
index 0000000..a121b6d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_12.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13.gif
new file mode 100644
index 0000000..fc22d7a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13b.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13b.gif
new file mode 100644
index 0000000..0b7c614
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_13b.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_2.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_2.gif
new file mode 100644
index 0000000..a005d38
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_2.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_3.gif
new file mode 100644
index 0000000..04e496c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_4.gif
new file mode 100644
index 0000000..f3486f3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_5.gif
new file mode 100644
index 0000000..b7ee0aa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6.gif
new file mode 100644
index 0000000..19e7b42
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6_alt.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6_alt.gif
new file mode 100644
index 0000000..af910ee
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_6_alt.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_7.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_7.gif
new file mode 100644
index 0000000..2cdb249
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_7.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_8.gif
new file mode 100644
index 0000000..3e2edae
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_9.gif
new file mode 100644
index 0000000..8577b2b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@icon_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_13.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_13.gif
new file mode 100644
index 0000000..9b09292
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_13.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_14.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_14.gif
new file mode 100644
index 0000000..3287559
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_14.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_15.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_15.gif
new file mode 100644
index 0000000..01ac46b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@imask_15.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_1.dif
new file mode 100644
index 0000000..557c3a2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_11.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_11.dif
new file mode 100644
index 0000000..479fd47
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_11.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_13.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_13.dif
new file mode 100644
index 0000000..44954a8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_13.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_14.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_14.dif
new file mode 100644
index 0000000..fb769a7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_14.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_15.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_15.dif
new file mode 100644
index 0000000..90ad386
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_15.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_16.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_16.gif
new file mode 100644
index 0000000..a6ac83e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_16.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_2.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_2.dif
new file mode 100644
index 0000000..cd09a2c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_2.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_4.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_4.dif
new file mode 100644
index 0000000..4bdb727
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_4.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_6.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_6.dif
new file mode 100644
index 0000000..0188950
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_6.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_8.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_8.dif
new file mode 100644
index 0000000..8457ca1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemm_8.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemmfont.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemmfont.gif
new file mode 100644
index 0000000..05c31da
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemmfont.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemming.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemming.ini
new file mode 100644
index 0000000..7a75ca9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@lemming.ini
@@ -0,0 +1,105 @@
+# lemmings description file
+
+# Lemming:
+# lemm_X: type, frames, direction, animation type
+# type: 0: faller, 1: walker, 2: climber, 3: climber to faller, 4: floater,
+# 5: splat, 6: stopper, 7: drowning, 8: trap death, 9: exit,
+# 10: bomber, 11: builder, 12: builder end, 13: digger, 14: basher, 15: miner
+#
+# frames: number of animation frames (1..N)
+#
+# directions: 1 or 2 (for two directions, only the "right" animation is stored, the left animation is created automatically)
+#
+# animation type: 0: loop, 1: once (state change if finished)
+
+# Mask:
+# mask_Y: type, frames, direction, step
+# type, frames amd direction see lemm_X
+# step: after <step> animations steps of lemming animation, do mask action and increase mask frame idx
+
+# walker, 8 frames, 2 dirs, loop
+lemm_0 = 8, 2, 0
+pos_0 = 16, 19, 8
+
+# faller, 4 frames, 2 dirs, loop
+lemm_1 = 4, 2, 0
+pos_1 = 16, 17, 8
+
+# climber, 8 frames, 2 dirs, loop
+lemm_2 = 8, 2, 0
+pos_2 = 13, 23, 8
+
+# climber to walker, 8 frames, 2 dirs, once
+lemm_3 = 8, 2, 1
+pos_3 = 14, 25, 8
+
+# floater, 10 frames, 2 dirs, once
+lemm_4 = 10, 2, 1
+pos_4 = 18, 29, 8
+
+# splat, 16 frames, one dir, once
+lemm_5 = 16, 1, 1
+pos_5 = 18, 19, 8
+
+# blocker, 16 frames, one dir, loop
+lemm_6 = 16, 1, 0
+pos_6 = 16, 19, 8
+# mask:blocker, 1 frame, 1 dir, mask action after 0 lemming animation frames
+mask_6 = 1, 1, 0
+
+# drowning, 16 frames, one dir, once
+lemm_7 = 16, 1, 1
+pos_7 = 19, 19, 8
+
+# trapped, 14 frames, one dir, once
+lemm_8 = 14, 1, 1
+pos_8 = 18, 25, 8
+
+# exiting, 8 frames, one dir, once
+lemm_9 = 8, 1, 1
+pos_9 = 16, 25, 8
+
+# bomber, 16 frames, 1 dir, once
+lemm_10 = 16, 1, 1
+pos_10 = 18, 19, 8
+# mask: 1 frame, 1 dir, mask action after 16 lemming animation frames
+mask_10 = 1, 1, 16
+
+# builder, 16 frames, 2 dirs, loop
+lemm_11 = 16, 2, 0
+pos_11 = 16, 25, 8
+# mask: 1 frame, 2 dirs, mask action after 9 lemming animation frames
+mask_11 = 1, 2, 9
+
+# builder end, 8 frames, 2 dirs, once
+lemm_12 = 8, 2, 1
+pos_12 = 20, 25, 8
+
+# digger, 16 frames, 1 dir, loop
+lemm_13 = 16, 1, 0
+pos_13 = 19, 23, 8
+# mask: 1 frame, no direction, mask action after 8 lemming animation frames
+mask_13 = 1, 1, 8
+# indestructible mask: 1 frame, 1 dir
+imask_13 = 1, 1
+
+# basher, 32 frames, 2 dirs, loop
+lemm_14 = 32, 2, 0
+pos_14 = 17, 19, 8
+# mask: 4 frames, 2 dirs, mask action after 16 lemming animation frames
+mask_14 = 4, 2, 8
+# indestructible mask: 1 frame, 2 dirs
+imask_14 = 1, 2
+
+# miner, 24 frames, 2 dirs, loop
+lemm_15 = 24, 2, 0
+pos_15 = 15, 23, 8
+# mask: 2 frames, 2 dirs, mask action after 12 lemming animation frames
+mask_15 = 2, 2, 12
+# indestructible mask: 1 frame, 2 dirs
+imask_15 = 1, 2
+
+
+# jumper, 2 frames, 2 dirs, loop
+lemm_16 = 4, 2, 0
+pos_16 = 16, 19, 8
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_10.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_10.dif
new file mode 100644
index 0000000..0c3695d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_10.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_11.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_11.dif
new file mode 100644
index 0000000..f13959f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_11.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_13.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_13.dif
new file mode 100644
index 0000000..c7c89e5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_13.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_14.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_14.dif
new file mode 100644
index 0000000..fd8e8e3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_14.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_15.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_15.dif
new file mode 100644
index 0000000..d6e1e3c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_15.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_6.gif
new file mode 100644
index 0000000..4be0c70
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@mask_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@numfont.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@numfont.gif
new file mode 100644
index 0000000..e34b7ad
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@numfont.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/misc@select.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@select.gif
new file mode 100644
index 0000000..c569586
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/misc@select.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@awesome.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@awesome.mod
new file mode 100644
index 0000000..e24b388
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@awesome.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@beasti.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@beasti.mod
new file mode 100644
index 0000000..79cb83b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@beasti.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@beastii.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@beastii.mod
new file mode 100644
index 0000000..537bf92
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@beastii.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@cancan.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@cancan.mod
new file mode 100644
index 0000000..a7bb4fb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@cancan.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@doggie.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@doggie.mod
new file mode 100644
index 0000000..24fa66a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@doggie.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming1.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming1.mod
new file mode 100644
index 0000000..59e7f2e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming1.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming2.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming2.mod
new file mode 100644
index 0000000..b877c37
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming2.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming3.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming3.mod
new file mode 100644
index 0000000..85dac85
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@lemming3.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@menace.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@menace.mod
new file mode 100644
index 0000000..30b5782
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@menace.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@mountain.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@mountain.mod
new file mode 100644
index 0000000..fae77a5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@mountain.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tenlemms.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tenlemms.mod
new file mode 100644
index 0000000..558d5a8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tenlemms.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim1.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim1.mod
new file mode 100644
index 0000000..a469549
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim1.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim10.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim10.mod
new file mode 100644
index 0000000..056de9c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim10.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim2.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim2.mod
new file mode 100644
index 0000000..cfeae39
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim2.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim3.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim3.mod
new file mode 100644
index 0000000..ea21388
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim3.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim4.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim4.mod
new file mode 100644
index 0000000..77f1103
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim4.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim5.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim5.mod
new file mode 100644
index 0000000..1190480
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim5.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim6.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim6.mod
new file mode 100644
index 0000000..adcc41f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim6.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim7.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim7.mod
new file mode 100644
index 0000000..c071c61
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim7.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim8.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim8.mod
new file mode 100644
index 0000000..b23a112
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim8.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim9.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim9.mod
new file mode 100644
index 0000000..b2a3c08
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tim9.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune1.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune1.mod
new file mode 100644
index 0000000..18cadfe
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune1.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune2.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune2.mod
new file mode 100644
index 0000000..f32f73e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune2.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune3.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune3.mod
new file mode 100644
index 0000000..ce61e3d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune3.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune4.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune4.mod
new file mode 100644
index 0000000..9edcc98
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune4.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune5.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune5.mod
new file mode 100644
index 0000000..1a68781
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune5.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune6.mod b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune6.mod
new file mode 100644
index 0000000..26590cb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/music@tune6.mod
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/patch.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/patch.ini
new file mode 100644
index 0000000..a9f0ad5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/patch.ini
@@ -0,0 +1,996 @@
+extract_0 = styles/brick/brick.ini, 0x3827c88a
+extract_1 = styles/brick/brickom_0.gif, 0x34ee5de5
+extract_2 = styles/brick/brickom_3.gif, 0xe2ba235a
+extract_3 = styles/brick/brickom_4.gif, 0xe2ba235a
+extract_4 = styles/brick/brickom_5.gif, 0xbb75577e
+extract_5 = styles/brick/brickom_6.gif, 0x404b3b28
+extract_6 = styles/brick/brickom_7.gif, 0xf0944762
+patch_0 = styles/brick/bricko_0.gif, 0xd4056755
+patch_1 = styles/brick/bricko_1.gif, 0xcb3ee928
+check_0 = styles/brick/bricko_2.gif, 0xc1117221
+check_1 = styles/brick/bricko_3.gif, 0xec37e255
+check_2 = styles/brick/bricko_4.gif, 0x651feeee
+check_3 = styles/brick/bricko_5.gif, 0xa96630a5
+patch_2 = styles/brick/bricko_6.gif, 0xc6a82128
+check_4 = styles/brick/bricko_7.gif, 0x52e45f82
+check_5 = styles/brick/bricko_8.gif, 0xb16e71d1
+check_6 = styles/brick/bricko_9.gif, 0xca3c2d49
+check_7 = styles/brick/brick_0.gif, 0x294ad9b4
+check_8 = styles/brick/brick_1.gif, 0xadd5446a
+check_9 = styles/brick/brick_10.gif, 0x7d7c60c9
+check_10 = styles/brick/brick_11.gif, 0xac966fe4
+check_11 = styles/brick/brick_12.gif, 0x390e73df
+check_12 = styles/brick/brick_13.gif, 0xcd9b7323
+check_13 = styles/brick/brick_14.gif, 0x66713804
+check_14 = styles/brick/brick_15.gif, 0x5279d42a
+check_15 = styles/brick/brick_16.gif, 0x2c30769
+check_16 = styles/brick/brick_17.gif, 0xff315039
+check_17 = styles/brick/brick_18.gif, 0xcca6d4e5
+check_18 = styles/brick/brick_19.gif, 0xeacd636a
+check_19 = styles/brick/brick_2.gif, 0xbfddd5c7
+check_20 = styles/brick/brick_20.gif, 0xad2b816d
+check_21 = styles/brick/brick_21.gif, 0x7408853d
+check_22 = styles/brick/brick_22.gif, 0xe01a85d9
+check_23 = styles/brick/brick_23.gif, 0xe0404831
+check_24 = styles/brick/brick_24.gif, 0xb0f86180
+check_25 = styles/brick/brick_25.gif, 0xd0af4aeb
+check_26 = styles/brick/brick_26.gif, 0x90607e4b
+check_27 = styles/brick/brick_27.gif, 0x44fd9d71
+check_28 = styles/brick/brick_28.gif, 0x8e2598a8
+check_29 = styles/brick/brick_29.gif, 0x89866f6e
+check_30 = styles/brick/brick_3.gif, 0xa7497d5a
+check_31 = styles/brick/brick_30.gif, 0xef771b
+check_32 = styles/brick/brick_31.gif, 0x99926b02
+check_33 = styles/brick/brick_32.gif, 0x7e716a86
+check_34 = styles/brick/brick_33.gif, 0x2be6790f
+check_35 = styles/brick/brick_34.gif, 0x6ca36da6
+check_36 = styles/brick/brick_35.gif, 0xd4f1af6c
+check_37 = styles/brick/brick_36.gif, 0x7fb30124
+check_38 = styles/brick/brick_37.gif, 0xae7d6fab
+check_39 = styles/brick/brick_38.gif, 0x7c3d5417
+check_40 = styles/brick/brick_39.gif, 0x98f069bb
+check_41 = styles/brick/brick_4.gif, 0xca80defb
+check_42 = styles/brick/brick_40.gif, 0x4fdfa7a3
+check_43 = styles/brick/brick_41.gif, 0xd628a696
+check_44 = styles/brick/brick_42.gif, 0x5609c982
+check_45 = styles/brick/brick_43.gif, 0x6b2b3ab1
+check_46 = styles/brick/brick_44.gif, 0x8995e01a
+check_47 = styles/brick/brick_45.gif, 0xd7fe6162
+check_48 = styles/brick/brick_46.gif, 0xef86911e
+check_49 = styles/brick/brick_47.gif, 0x1718b609
+check_50 = styles/brick/brick_48.gif, 0xdb32b102
+check_51 = styles/brick/brick_49.gif, 0xf0e5ce56
+check_52 = styles/brick/brick_5.gif, 0xac647304
+check_53 = styles/brick/brick_50.gif, 0xaf70ac65
+check_54 = styles/brick/brick_51.gif, 0x4ae3889f
+check_55 = styles/brick/brick_52.gif, 0xd33dfbdb
+check_56 = styles/brick/brick_53.gif, 0x76141e29
+check_57 = styles/brick/brick_54.gif, 0xf124d1b3
+check_58 = styles/brick/brick_55.gif, 0x6ea19cd9
+check_59 = styles/brick/brick_56.gif, 0x9c195c51
+check_60 = styles/brick/brick_57.gif, 0xf5ae4924
+check_61 = styles/brick/brick_58.gif, 0xe36a937f
+check_62 = styles/brick/brick_59.gif, 0x2bb63e27
+check_63 = styles/brick/brick_6.gif, 0xb33170e0
+check_64 = styles/brick/brick_7.gif, 0xb06597f5
+check_65 = styles/brick/brick_8.gif, 0xa3f16607
+check_66 = styles/brick/brick_9.gif, 0xfca756eb
+extract_7 = styles/bubble/bubble.ini, 0xf9a1e004
+extract_8 = styles/bubble/bubbleom_0.gif, 0x77355896
+extract_9 = styles/bubble/bubbleom_3.gif, 0xe2ba235a
+extract_10 = styles/bubble/bubbleom_4.gif, 0xe2ba235a
+extract_11 = styles/bubble/bubbleom_5.gif, 0x6ffc6057
+extract_12 = styles/bubble/bubbleom_8.gif, 0xd42e4fbb
+extract_13 = styles/bubble/bubbleom_9.gif, 0xd9132a7f
+check_67 = styles/bubble/bubbleo_0.gif, 0xf165558a
+patch_3 = styles/bubble/bubbleo_1.gif, 0xea18b24
+check_68 = styles/bubble/bubbleo_10.gif, 0xf56e1142
+check_69 = styles/bubble/bubbleo_2.gif, 0xdf7c7a4b
+check_70 = styles/bubble/bubbleo_3.gif, 0x5c955852
+check_71 = styles/bubble/bubbleo_4.gif, 0xe5184a88
+check_72 = styles/bubble/bubbleo_5.gif, 0x645357e6
+check_73 = styles/bubble/bubbleo_6.gif, 0x2befeaa4
+check_74 = styles/bubble/bubbleo_7.gif, 0xcfd979fb
+check_75 = styles/bubble/bubbleo_8.gif, 0x3ab8873a
+check_76 = styles/bubble/bubbleo_9.gif, 0x47fa6044
+patch_4 = styles/bubble/bubble_0.gif, 0xea4e8a4c
+patch_5 = styles/bubble/bubble_1.gif, 0x75c0c38f
+patch_6 = styles/bubble/bubble_10.gif, 0x23a3c83c
+check_77 = styles/bubble/bubble_11.gif, 0xc3d66472
+check_78 = styles/bubble/bubble_12.gif, 0xa6a283e1
+check_79 = styles/bubble/bubble_13.gif, 0x15108f56
+check_80 = styles/bubble/bubble_14.gif, 0x850cb575
+check_81 = styles/bubble/bubble_15.gif, 0xabed1dde
+patch_7 = styles/bubble/bubble_16.gif, 0xcc07bea3
+check_82 = styles/bubble/bubble_17.gif, 0x72da6554
+check_83 = styles/bubble/bubble_18.gif, 0xa32b7730
+check_84 = styles/bubble/bubble_19.gif, 0x9f118186
+patch_8 = styles/bubble/bubble_2.gif, 0x5d4c4f92
+check_85 = styles/bubble/bubble_20.gif, 0x243cdaf9
+check_86 = styles/bubble/bubble_21.gif, 0x92596363
+check_87 = styles/bubble/bubble_22.gif, 0x4e5b5e6a
+check_88 = styles/bubble/bubble_23.gif, 0x3366bf78
+check_89 = styles/bubble/bubble_24.gif, 0x982a1198
+check_90 = styles/bubble/bubble_25.gif, 0xcac75a54
+check_91 = styles/bubble/bubble_26.gif, 0xe1cf70f0
+check_92 = styles/bubble/bubble_27.gif, 0x6d5b8a49
+check_93 = styles/bubble/bubble_28.gif, 0xbd2ca51a
+check_94 = styles/bubble/bubble_29.gif, 0xa8012bfa
+patch_9 = styles/bubble/bubble_3.gif, 0xa8633e2c
+check_95 = styles/bubble/bubble_30.gif, 0x42ef3092
+check_96 = styles/bubble/bubble_31.gif, 0x4d81dd0c
+check_97 = styles/bubble/bubble_32.gif, 0x3583cfa7
+check_98 = styles/bubble/bubble_33.gif, 0x2b7dbb17
+check_99 = styles/bubble/bubble_34.gif, 0x2477a83d
+check_100 = styles/bubble/bubble_35.gif, 0x6dbe56d3
+check_101 = styles/bubble/bubble_36.gif, 0x374357f6
+check_102 = styles/bubble/bubble_37.gif, 0xfabf7a7e
+check_103 = styles/bubble/bubble_38.gif, 0x92d182c6
+check_104 = styles/bubble/bubble_39.gif, 0x2ac279c5
+check_105 = styles/bubble/bubble_4.gif, 0x98437bab
+check_106 = styles/bubble/bubble_40.gif, 0x977d7cd7
+check_107 = styles/bubble/bubble_41.gif, 0x96657ec1
+check_108 = styles/bubble/bubble_42.gif, 0x53477c57
+check_109 = styles/bubble/bubble_43.gif, 0xa4d8105
+check_110 = styles/bubble/bubble_44.gif, 0x275becaf
+check_111 = styles/bubble/bubble_45.gif, 0x4e26c61b
+check_112 = styles/bubble/bubble_46.gif, 0xd8c79863
+check_113 = styles/bubble/bubble_47.gif, 0xfcae96e3
+check_114 = styles/bubble/bubble_48.gif, 0xd79acbc
+check_115 = styles/bubble/bubble_49.gif, 0xbb8c9beb
+check_116 = styles/bubble/bubble_5.gif, 0x79a92ce2
+check_117 = styles/bubble/bubble_50.gif, 0xd795c11d
+check_118 = styles/bubble/bubble_51.gif, 0x4a65b755
+check_119 = styles/bubble/bubble_52.gif, 0x2ce1ac3c
+check_120 = styles/bubble/bubble_53.gif, 0xe7eeaf6a
+check_121 = styles/bubble/bubble_54.gif, 0x3200aed2
+check_122 = styles/bubble/bubble_55.gif, 0xbe279fb2
+check_123 = styles/bubble/bubble_56.gif, 0xc6b1a68d
+check_124 = styles/bubble/bubble_57.gif, 0xc3899497
+check_125 = styles/bubble/bubble_58.gif, 0xce5a841a
+check_126 = styles/bubble/bubble_59.gif, 0x47b8872c
+check_127 = styles/bubble/bubble_6.gif, 0x93103c46
+check_128 = styles/bubble/bubble_60.gif, 0xf4198f71
+check_129 = styles/bubble/bubble_61.gif, 0x21248d37
+check_130 = styles/bubble/bubble_62.gif, 0xa32da164
+check_131 = styles/bubble/bubble_63.gif, 0x980977d7
+check_132 = styles/bubble/bubble_7.gif, 0x497723bc
+check_133 = styles/bubble/bubble_8.gif, 0x82fab00e
+check_134 = styles/bubble/bubble_9.gif, 0x636410ae
+extract_14 = styles/crystal/crystal.ini, 0x2864df9b
+extract_15 = styles/crystal/crystalom_0.gif, 0x18f741cf
+extract_16 = styles/crystal/crystalom_10.gif, 0x96a42a1e
+extract_17 = styles/crystal/crystalom_4.gif, 0x29d731e0
+extract_18 = styles/crystal/crystalom_5.gif, 0x29d731e0
+extract_19 = styles/crystal/crystalom_6.gif, 0xacad45ab
+extract_20 = styles/crystal/crystalom_7.gif, 0x2a30341d
+extract_21 = styles/crystal/crystalom_9.gif, 0x6f1e492d
+check_135 = styles/crystal/crystalo_0.gif, 0xc433bd8e
+patch_10 = styles/crystal/crystalo_1.gif, 0x3e0859f7
+check_136 = styles/crystal/crystalo_10.gif, 0xd598970d
+check_137 = styles/crystal/crystalo_2.gif, 0x6cef6d67
+check_138 = styles/crystal/crystalo_3.gif, 0x5d4c6d17
+check_139 = styles/crystal/crystalo_4.gif, 0x60d8c52a
+check_140 = styles/crystal/crystalo_5.gif, 0x4c48c434
+check_141 = styles/crystal/crystalo_6.gif, 0xb34eca12
+check_142 = styles/crystal/crystalo_7.gif, 0xf994cd15
+check_143 = styles/crystal/crystalo_8.gif, 0xd0a913e0
+check_144 = styles/crystal/crystalo_9.gif, 0xddd89d5f
+check_145 = styles/crystal/crystal_0.gif, 0xc4a7a679
+check_146 = styles/crystal/crystal_1.gif, 0x16e8a27f
+check_147 = styles/crystal/crystal_10.gif, 0x70c08997
+check_148 = styles/crystal/crystal_11.gif, 0x1e3bf253
+check_149 = styles/crystal/crystal_12.gif, 0xb8b1448
+check_150 = styles/crystal/crystal_13.gif, 0x1ce68c77
+check_151 = styles/crystal/crystal_14.gif, 0xdc7a4d7b
+check_152 = styles/crystal/crystal_15.gif, 0x36d7f43d
+check_153 = styles/crystal/crystal_16.gif, 0xde0b1392
+check_154 = styles/crystal/crystal_17.gif, 0xa28d968
+check_155 = styles/crystal/crystal_18.gif, 0x2d759b1a
+check_156 = styles/crystal/crystal_19.gif, 0x582f275b
+check_157 = styles/crystal/crystal_2.gif, 0x9f596932
+check_158 = styles/crystal/crystal_20.gif, 0xb7bcd8b9
+check_159 = styles/crystal/crystal_21.gif, 0x2337e3be
+check_160 = styles/crystal/crystal_22.gif, 0x712e8b51
+check_161 = styles/crystal/crystal_23.gif, 0x7d957d72
+check_162 = styles/crystal/crystal_24.gif, 0xdd044fe9
+check_163 = styles/crystal/crystal_25.gif, 0xde8d3f38
+check_164 = styles/crystal/crystal_26.gif, 0xffbaf7c6
+check_165 = styles/crystal/crystal_27.gif, 0xff88c273
+check_166 = styles/crystal/crystal_28.gif, 0xb8206259
+check_167 = styles/crystal/crystal_29.gif, 0xa8c9521
+check_168 = styles/crystal/crystal_3.gif, 0x49f36024
+check_169 = styles/crystal/crystal_30.gif, 0x567d923e
+check_170 = styles/crystal/crystal_31.gif, 0xc3bd3cc1
+check_171 = styles/crystal/crystal_32.gif, 0x1ee7e5b3
+check_172 = styles/crystal/crystal_33.gif, 0xe6ccf3f5
+check_173 = styles/crystal/crystal_34.gif, 0x6029acfb
+check_174 = styles/crystal/crystal_35.gif, 0x4404d2b1
+check_175 = styles/crystal/crystal_36.gif, 0x5fb47b4d
+check_176 = styles/crystal/crystal_4.gif, 0x34a448d5
+check_177 = styles/crystal/crystal_5.gif, 0x49f36024
+check_178 = styles/crystal/crystal_6.gif, 0xa9e16204
+check_179 = styles/crystal/crystal_7.gif, 0x947d54d9
+patch_11 = styles/crystal/crystal_8.gif, 0x92e7125d
+check_180 = styles/crystal/crystal_9.gif, 0x37e2961b
+extract_22 = styles/dirt/dirt.ini, 0xb250e27c
+extract_23 = styles/dirt/dirtom_0.gif, 0xc91e4707
+extract_24 = styles/dirt/dirtom_10.gif, 0x1bde50a6
+extract_25 = styles/dirt/dirtom_3.gif, 0x29d731e0
+extract_26 = styles/dirt/dirtom_4.gif, 0x29d731e0
+extract_27 = styles/dirt/dirtom_5.gif, 0x4d74669
+extract_28 = styles/dirt/dirtom_6.gif, 0xaf1b2a8a
+extract_29 = styles/dirt/dirtom_8.gif, 0x74a53407
+check_181 = styles/dirt/dirto_0.gif, 0x28c966f8
+patch_12 = styles/dirt/dirto_1.gif, 0x74ab0a8d
+check_182 = styles/dirt/dirto_10.gif, 0x8b7286c1
+check_183 = styles/dirt/dirto_2.gif, 0x4326739f
+check_184 = styles/dirt/dirto_3.gif, 0xa89ede6
+check_185 = styles/dirt/dirto_4.gif, 0x36adf92a
+check_186 = styles/dirt/dirto_5.gif, 0xe3ea975b
+check_187 = styles/dirt/dirto_6.gif, 0x92425837
+check_188 = styles/dirt/dirto_7.gif, 0x8ac9571a
+check_189 = styles/dirt/dirto_8.gif, 0xcaf4d8df
+check_190 = styles/dirt/dirto_9.gif, 0x3383734f
+patch_13 = styles/dirt/dirt_0.gif, 0x4f5f25ab
+patch_14 = styles/dirt/dirt_1.gif, 0x7b7351ff
+check_191 = styles/dirt/dirt_10.gif, 0xb0c22181
+check_192 = styles/dirt/dirt_11.gif, 0x86efefc1
+check_193 = styles/dirt/dirt_12.gif, 0x9d0a5629
+check_194 = styles/dirt/dirt_13.gif, 0xf4a2d858
+check_195 = styles/dirt/dirt_14.gif, 0x9c92cd71
+check_196 = styles/dirt/dirt_15.gif, 0x5f1ce459
+patch_15 = styles/dirt/dirt_16.gif, 0xe4fe790f
+patch_16 = styles/dirt/dirt_17.gif, 0xedb55267
+check_197 = styles/dirt/dirt_18.gif, 0xa9e41bd9
+check_198 = styles/dirt/dirt_19.gif, 0xf992dc65
+check_199 = styles/dirt/dirt_2.gif, 0xb641bd84
+check_200 = styles/dirt/dirt_20.gif, 0x91459e4f
+check_201 = styles/dirt/dirt_21.gif, 0x714d11db
+patch_17 = styles/dirt/dirt_22.gif, 0x105744f5
+check_202 = styles/dirt/dirt_23.gif, 0x84668e30
+check_203 = styles/dirt/dirt_24.gif, 0xcf0e4096
+check_204 = styles/dirt/dirt_25.gif, 0x7ed15698
+check_205 = styles/dirt/dirt_26.gif, 0xc435f48b
+patch_18 = styles/dirt/dirt_27.gif, 0x6026f4d2
+check_206 = styles/dirt/dirt_28.gif, 0x559a7483
+patch_19 = styles/dirt/dirt_29.gif, 0x500d81b1
+check_207 = styles/dirt/dirt_3.gif, 0xeb31da52
+patch_20 = styles/dirt/dirt_30.gif, 0x7566f4e0
+patch_21 = styles/dirt/dirt_31.gif, 0x9ee799fc
+patch_22 = styles/dirt/dirt_32.gif, 0x30996347
+patch_23 = styles/dirt/dirt_33.gif, 0xf0c07e3d
+patch_24 = styles/dirt/dirt_34.gif, 0x825c722f
+patch_25 = styles/dirt/dirt_35.gif, 0xeda9b7d5
+patch_26 = styles/dirt/dirt_36.gif, 0x81d7bdf1
+patch_27 = styles/dirt/dirt_37.gif, 0x4466a3aa
+patch_28 = styles/dirt/dirt_38.gif, 0xef917399
+patch_29 = styles/dirt/dirt_39.gif, 0x210557c2
+check_208 = styles/dirt/dirt_4.gif, 0xe3926f1a
+patch_30 = styles/dirt/dirt_40.gif, 0x6d8c4b90
+patch_31 = styles/dirt/dirt_41.gif, 0xa49bb7af
+check_209 = styles/dirt/dirt_42.gif, 0xaa44cf28
+check_210 = styles/dirt/dirt_43.gif, 0x1f38d49b
+patch_32 = styles/dirt/dirt_44.gif, 0xe7bb34a
+check_211 = styles/dirt/dirt_45.gif, 0xc22c5eb5
+check_212 = styles/dirt/dirt_46.gif, 0xc7a5703
+check_213 = styles/dirt/dirt_47.gif, 0x87a34df4
+check_214 = styles/dirt/dirt_48.gif, 0x1f7f427d
+check_215 = styles/dirt/dirt_49.gif, 0xdcc1aca5
+check_216 = styles/dirt/dirt_5.gif, 0x47160c2f
+check_217 = styles/dirt/dirt_6.gif, 0xc2e2e4f4
+check_218 = styles/dirt/dirt_7.gif, 0x75aef9d9
+check_219 = styles/dirt/dirt_8.gif, 0x34e1bcaf
+check_220 = styles/dirt/dirt_9.gif, 0x89baee1
+extract_30 = styles/fire/fire.ini, 0xc2ebe9db
+extract_31 = styles/fire/fireom_0.gif, 0xaca14ead
+extract_32 = styles/fire/fireom_10.gif, 0xc39a647e
+extract_33 = styles/fire/fireom_3.gif, 0x24a12769
+extract_34 = styles/fire/fireom_4.gif, 0x24a12769
+extract_35 = styles/fire/fireom_5.gif, 0x2e7503b
+extract_36 = styles/fire/fireom_7.gif, 0xcd283c17
+extract_37 = styles/fire/fireom_8.gif, 0x222b6528
+check_221 = styles/fire/fireo_0.gif, 0xb8ba5fdb
+patch_33 = styles/fire/fireo_1.gif, 0x91cc7028
+check_222 = styles/fire/fireo_10.gif, 0x40b67b7a
+check_223 = styles/fire/fireo_2.gif, 0x2ac97330
+check_224 = styles/fire/fireo_3.gif, 0x298fdc61
+check_225 = styles/fire/fireo_4.gif, 0x670c0cbf
+check_226 = styles/fire/fireo_5.gif, 0x285aaa73
+patch_34 = styles/fire/fireo_6.gif, 0xd2464e0e
+check_227 = styles/fire/fireo_7.gif, 0x8cc5776e
+check_228 = styles/fire/fireo_8.gif, 0xb8bf6c83
+check_229 = styles/fire/fireo_9.gif, 0x72a16fbd
+check_230 = styles/fire/fire_0.gif, 0xa2f597ce
+check_231 = styles/fire/fire_1.gif, 0x34e8febf
+check_232 = styles/fire/fire_10.gif, 0x10b514d6
+check_233 = styles/fire/fire_11.gif, 0xccf65e2
+check_234 = styles/fire/fire_12.gif, 0x5a1b0d8f
+check_235 = styles/fire/fire_13.gif, 0xf9969135
+check_236 = styles/fire/fire_14.gif, 0x685b62be
+check_237 = styles/fire/fire_15.gif, 0xa4db03ee
+check_238 = styles/fire/fire_16.gif, 0x409a07d3
+check_239 = styles/fire/fire_17.gif, 0x2164a3c9
+check_240 = styles/fire/fire_18.gif, 0x2bebea1b
+check_241 = styles/fire/fire_19.gif, 0x600074d8
+check_242 = styles/fire/fire_2.gif, 0x694d12f2
+check_243 = styles/fire/fire_20.gif, 0xd96a6cbd
+check_244 = styles/fire/fire_21.gif, 0x687e5f62
+check_245 = styles/fire/fire_22.gif, 0xb01e9ff1
+check_246 = styles/fire/fire_23.gif, 0xbda69808
+check_247 = styles/fire/fire_24.gif, 0x6c485c3e
+check_248 = styles/fire/fire_25.gif, 0xfadda17d
+check_249 = styles/fire/fire_26.gif, 0x2af55ff1
+check_250 = styles/fire/fire_27.gif, 0x10e6a8d1
+check_251 = styles/fire/fire_28.gif, 0x672a13a8
+check_252 = styles/fire/fire_29.gif, 0x7e0627c3
+check_253 = styles/fire/fire_3.gif, 0x8d20d0b3
+check_254 = styles/fire/fire_30.gif, 0x89a1e121
+check_255 = styles/fire/fire_31.gif, 0x3d5b71d0
+check_256 = styles/fire/fire_32.gif, 0x510a8296
+check_257 = styles/fire/fire_33.gif, 0x770a0bf6
+check_258 = styles/fire/fire_34.gif, 0x238a6fb1
+check_259 = styles/fire/fire_35.gif, 0x3ce13f38
+check_260 = styles/fire/fire_36.gif, 0x8a0321e0
+check_261 = styles/fire/fire_37.gif, 0xede7568b
+check_262 = styles/fire/fire_38.gif, 0x924271d2
+check_263 = styles/fire/fire_39.gif, 0x67fc27f3
+check_264 = styles/fire/fire_4.gif, 0xfb23979b
+check_265 = styles/fire/fire_40.gif, 0x1a053c5d
+check_266 = styles/fire/fire_41.gif, 0xdd92f39
+check_267 = styles/fire/fire_42.gif, 0xb4bef300
+check_268 = styles/fire/fire_43.gif, 0x27de9718
+check_269 = styles/fire/fire_44.gif, 0xed8b586c
+check_270 = styles/fire/fire_45.gif, 0xa9fe7126
+check_271 = styles/fire/fire_46.gif, 0xa1c32c64
+check_272 = styles/fire/fire_47.gif, 0x5853a26e
+check_273 = styles/fire/fire_48.gif, 0x83d53cba
+check_274 = styles/fire/fire_49.gif, 0x989c0ae2
+patch_35 = styles/fire/fire_5.gif, 0xeef93fc8
+check_275 = styles/fire/fire_50.gif, 0x904fe936
+check_276 = styles/fire/fire_51.gif, 0x13660961
+check_277 = styles/fire/fire_52.gif, 0x57d202db
+check_278 = styles/fire/fire_53.gif, 0x871ea6fe
+check_279 = styles/fire/fire_54.gif, 0x8c602195
+check_280 = styles/fire/fire_55.gif, 0xbd472895
+check_281 = styles/fire/fire_56.gif, 0x64922ca4
+check_282 = styles/fire/fire_57.gif, 0x6cd671
+check_283 = styles/fire/fire_58.gif, 0x37be8ce7
+check_284 = styles/fire/fire_59.gif, 0xf02ae7dc
+patch_36 = styles/fire/fire_6.gif, 0x22cb9085
+check_285 = styles/fire/fire_60.gif, 0x3f6b6b53
+check_286 = styles/fire/fire_61.gif, 0x66889ff
+check_287 = styles/fire/fire_62.gif, 0xc867fc0d
+check_288 = styles/fire/fire_63.gif, 0xbeb9e334
+patch_37 = styles/fire/fire_7.gif, 0xa33d51f8
+patch_38 = styles/fire/fire_8.gif, 0xe538793
+check_289 = styles/fire/fire_9.gif, 0xbf7941b1
+extract_38 = styles/pillar/pillar.ini, 0x7030e7a9
+extract_39 = styles/pillar/pillarom_0.gif, 0xa21431e
+extract_40 = styles/pillar/pillarom_10.gif, 0xe2433ab8
+extract_41 = styles/pillar/pillarom_3.gif, 0x29d731e0
+extract_42 = styles/pillar/pillarom_4.gif, 0x29d731e0
+extract_43 = styles/pillar/pillarom_5.gif, 0xacad45ab
+extract_44 = styles/pillar/pillarom_8.gif, 0xa4b1370f
+extract_45 = styles/pillar/pillarom_9.gif, 0xa78a3bfc
+check_290 = styles/pillar/pillaro_0.gif, 0x8d2c5b4
+patch_39 = styles/pillar/pillaro_1.gif, 0xfd43dfef
+check_291 = styles/pillar/pillaro_10.gif, 0x53f1a6dc
+check_292 = styles/pillar/pillaro_2.gif, 0x52b27dcb
+check_293 = styles/pillar/pillaro_3.gif, 0x23edf8dd
+check_294 = styles/pillar/pillaro_4.gif, 0xc35d1123
+check_295 = styles/pillar/pillaro_5.gif, 0x3690995b
+check_296 = styles/pillar/pillaro_6.gif, 0x62606041
+check_297 = styles/pillar/pillaro_7.gif, 0x430f7d7b
+patch_40 = styles/pillar/pillaro_8.gif, 0x79c74e60
+check_298 = styles/pillar/pillaro_9.gif, 0x93577ea7
+check_299 = styles/pillar/pillar_0.gif, 0x46195a8b
+check_300 = styles/pillar/pillar_1.gif, 0xde61f57
+check_301 = styles/pillar/pillar_10.gif, 0x5d6de5bb
+check_302 = styles/pillar/pillar_11.gif, 0xe276e7c8
+check_303 = styles/pillar/pillar_12.gif, 0x59a4ccec
+check_304 = styles/pillar/pillar_13.gif, 0xed8ed4e6
+check_305 = styles/pillar/pillar_14.gif, 0x154b0c6b
+check_306 = styles/pillar/pillar_15.gif, 0x7e9bd673
+check_307 = styles/pillar/pillar_16.gif, 0x7a9bd723
+check_308 = styles/pillar/pillar_17.gif, 0x4e66125a
+check_309 = styles/pillar/pillar_18.gif, 0x8faecc35
+check_310 = styles/pillar/pillar_19.gif, 0x324cc971
+check_311 = styles/pillar/pillar_2.gif, 0x1258a321
+check_312 = styles/pillar/pillar_20.gif, 0xed6f0d87
+check_313 = styles/pillar/pillar_21.gif, 0x1c336d3a
+check_314 = styles/pillar/pillar_22.gif, 0x90758894
+check_315 = styles/pillar/pillar_23.gif, 0xedac362b
+check_316 = styles/pillar/pillar_24.gif, 0xe9533715
+check_317 = styles/pillar/pillar_25.gif, 0x52d4eb9a
+check_318 = styles/pillar/pillar_26.gif, 0xcebcbf54
+check_319 = styles/pillar/pillar_27.gif, 0xc6f29bef
+check_320 = styles/pillar/pillar_28.gif, 0xa1be1bc3
+check_321 = styles/pillar/pillar_29.gif, 0x32a36fdf
+check_322 = styles/pillar/pillar_3.gif, 0x2295daff
+check_323 = styles/pillar/pillar_30.gif, 0x1c66db1
+check_324 = styles/pillar/pillar_31.gif, 0x2df65bdb
+check_325 = styles/pillar/pillar_32.gif, 0x6fca3e66
+check_326 = styles/pillar/pillar_33.gif, 0x51ed8b3e
+check_327 = styles/pillar/pillar_34.gif, 0xdf7f8eac
+check_328 = styles/pillar/pillar_35.gif, 0xa89629ec
+check_329 = styles/pillar/pillar_36.gif, 0x757d7720
+check_330 = styles/pillar/pillar_37.gif, 0xefa25841
+check_331 = styles/pillar/pillar_38.gif, 0xbd091fab
+check_332 = styles/pillar/pillar_39.gif, 0xf4d4677c
+check_333 = styles/pillar/pillar_4.gif, 0x64756544
+check_334 = styles/pillar/pillar_40.gif, 0xaacfbe28
+check_335 = styles/pillar/pillar_41.gif, 0x14958fcd
+check_336 = styles/pillar/pillar_42.gif, 0xe397c9e0
+check_337 = styles/pillar/pillar_43.gif, 0xfcefae35
+check_338 = styles/pillar/pillar_44.gif, 0xb79cb868
+check_339 = styles/pillar/pillar_45.gif, 0x4194b6f4
+check_340 = styles/pillar/pillar_46.gif, 0xdb7cdd0
+check_341 = styles/pillar/pillar_47.gif, 0x59a9ce0f
+check_342 = styles/pillar/pillar_48.gif, 0xb9d91bbf
+check_343 = styles/pillar/pillar_49.gif, 0x78f52881
+check_344 = styles/pillar/pillar_5.gif, 0x251271a1
+check_345 = styles/pillar/pillar_50.gif, 0x3ff2d0a6
+check_346 = styles/pillar/pillar_51.gif, 0x4c10242b
+check_347 = styles/pillar/pillar_52.gif, 0x52a75256
+check_348 = styles/pillar/pillar_53.gif, 0x7fbd515c
+check_349 = styles/pillar/pillar_54.gif, 0xc6407a30
+check_350 = styles/pillar/pillar_55.gif, 0xb1ab0b56
+check_351 = styles/pillar/pillar_56.gif, 0xbbb708d7
+check_352 = styles/pillar/pillar_57.gif, 0xa8f07f2e
+check_353 = styles/pillar/pillar_58.gif, 0x7f9386ad
+check_354 = styles/pillar/pillar_59.gif, 0x10d5608e
+check_355 = styles/pillar/pillar_6.gif, 0x31919686
+check_356 = styles/pillar/pillar_60.gif, 0xae396bbc
+check_357 = styles/pillar/pillar_61.gif, 0xf9ad6c4c
+check_358 = styles/pillar/pillar_7.gif, 0xb8a1b98b
+check_359 = styles/pillar/pillar_8.gif, 0x33bffacd
+check_360 = styles/pillar/pillar_9.gif, 0x9668f6f3
+extract_46 = styles/rock/rock.ini, 0x65ba01e8
+extract_47 = styles/rock/rockom_0.gif, 0xb83d4929
+extract_48 = styles/rock/rockom_10.gif, 0xf6954a68
+extract_49 = styles/rock/rockom_3.gif, 0x29d731e0
+extract_50 = styles/rock/rockom_4.gif, 0x29d731e0
+extract_51 = styles/rock/rockom_5.gif, 0x62455707
+extract_52 = styles/rock/rockom_6.gif, 0x1ebb2250
+extract_53 = styles/rock/rockom_8.gif, 0x82d14d1f
+check_361 = styles/rock/rocko_0.gif, 0x2244f7e5
+patch_41 = styles/rock/rocko_1.gif, 0xca3ac40d
+check_362 = styles/rock/rocko_10.gif, 0x167fbda
+check_363 = styles/rock/rocko_11.gif, 0x473277fc
+check_364 = styles/rock/rocko_2.gif, 0x56d5784c
+check_365 = styles/rock/rocko_3.gif, 0x710534b3
+check_366 = styles/rock/rocko_4.gif, 0xf505511b
+check_367 = styles/rock/rocko_5.gif, 0x5eeba8fe
+check_368 = styles/rock/rocko_6.gif, 0x51adfb32
+check_369 = styles/rock/rocko_7.gif, 0xfbc8824c
+check_370 = styles/rock/rocko_8.gif, 0x84a8eff4
+check_371 = styles/rock/rocko_9.gif, 0x8d9e6a20
+check_372 = styles/rock/rock_0.gif, 0x23ffac27
+check_373 = styles/rock/rock_1.gif, 0xd9d30297
+patch_42 = styles/rock/rock_10.gif, 0x1dd27595
+check_374 = styles/rock/rock_11.gif, 0x98498326
+patch_43 = styles/rock/rock_12.gif, 0x34997a69
+check_375 = styles/rock/rock_13.gif, 0xd7b327e3
+check_376 = styles/rock/rock_14.gif, 0x5e714697
+check_377 = styles/rock/rock_15.gif, 0xcce9bd6d
+check_378 = styles/rock/rock_16.gif, 0x5443d52e
+check_379 = styles/rock/rock_17.gif, 0xceb1b34b
+check_380 = styles/rock/rock_18.gif, 0x3a3cc62a
+check_381 = styles/rock/rock_19.gif, 0x7f0a3611
+check_382 = styles/rock/rock_2.gif, 0x45e74c72
+check_383 = styles/rock/rock_20.gif, 0xd7dccf61
+check_384 = styles/rock/rock_21.gif, 0x83e730ef
+check_385 = styles/rock/rock_22.gif, 0x151e6e57
+check_386 = styles/rock/rock_23.gif, 0x94b5d78
+check_387 = styles/rock/rock_24.gif, 0x888bfcf1
+check_388 = styles/rock/rock_25.gif, 0x8a1bb113
+check_389 = styles/rock/rock_26.gif, 0xf0801841
+check_390 = styles/rock/rock_27.gif, 0xf7251523
+check_391 = styles/rock/rock_28.gif, 0xa65c86d2
+check_392 = styles/rock/rock_29.gif, 0x8149194
+check_393 = styles/rock/rock_3.gif, 0xba67fd2e
+check_394 = styles/rock/rock_30.gif, 0x3ee56e5b
+check_395 = styles/rock/rock_31.gif, 0x51f1711d
+check_396 = styles/rock/rock_32.gif, 0x5f1af6c1
+check_397 = styles/rock/rock_33.gif, 0xb45df341
+check_398 = styles/rock/rock_34.gif, 0xf204c7f2
+check_399 = styles/rock/rock_35.gif, 0x6fbf93d9
+check_400 = styles/rock/rock_36.gif, 0x9087629b
+check_401 = styles/rock/rock_37.gif, 0xcbed5b84
+check_402 = styles/rock/rock_38.gif, 0x14257dd
+check_403 = styles/rock/rock_39.gif, 0x1495b37a
+check_404 = styles/rock/rock_4.gif, 0xe8e09de1
+check_405 = styles/rock/rock_40.gif, 0x66b761a
+check_406 = styles/rock/rock_41.gif, 0xf40e610b
+check_407 = styles/rock/rock_42.gif, 0x3bb535d1
+check_408 = styles/rock/rock_43.gif, 0xc13af748
+check_409 = styles/rock/rock_44.gif, 0x17f90022
+check_410 = styles/rock/rock_45.gif, 0xda2b8780
+check_411 = styles/rock/rock_46.gif, 0xaca59bbc
+check_412 = styles/rock/rock_47.gif, 0x3b943e66
+check_413 = styles/rock/rock_48.gif, 0x30ebe8b4
+check_414 = styles/rock/rock_49.gif, 0x9bee0179
+check_415 = styles/rock/rock_5.gif, 0x2f96d541
+check_416 = styles/rock/rock_50.gif, 0xd8dfd05c
+check_417 = styles/rock/rock_51.gif, 0x46ce7441
+check_418 = styles/rock/rock_52.gif, 0x51f35d
+check_419 = styles/rock/rock_53.gif, 0x9beef33c
+check_420 = styles/rock/rock_54.gif, 0xe5c98353
+check_421 = styles/rock/rock_55.gif, 0xeeba604b
+check_422 = styles/rock/rock_56.gif, 0xa6f803ce
+check_423 = styles/rock/rock_57.gif, 0xd52f5137
+check_424 = styles/rock/rock_58.gif, 0x1eff57ed
+check_425 = styles/rock/rock_59.gif, 0x8965eafd
+patch_44 = styles/rock/rock_6.gif, 0xfa75e9b5
+check_426 = styles/rock/rock_60.gif, 0x12644448
+check_427 = styles/rock/rock_61.gif, 0x4427a35e
+check_428 = styles/rock/rock_62.gif, 0x9f096358
+check_429 = styles/rock/rock_63.gif, 0x78504eaf
+check_430 = styles/rock/rock_7.gif, 0x64da14c6
+check_431 = styles/rock/rock_8.gif, 0xa5bbf57f
+patch_45 = styles/rock/rock_9.gif, 0x7192e53e
+extract_54 = styles/snow/snow.ini, 0x7aa8cb08
+extract_55 = styles/snow/snowom_0.gif, 0x5871589a
+extract_56 = styles/snow/snowom_3.gif, 0x29d731e0
+extract_57 = styles/snow/snowom_4.gif, 0x29d731e0
+extract_58 = styles/snow/snowom_5.gif, 0xa067477c
+extract_59 = styles/snow/snowom_8.gif, 0xf3f64dc2
+extract_60 = styles/snow/snowom_9.gif, 0x64a17079
+check_432 = styles/snow/snowo_0.gif, 0x8f224170
+patch_46 = styles/snow/snowo_1.gif, 0x69adc620
+check_433 = styles/snow/snowo_2.gif, 0x2ed98281
+check_434 = styles/snow/snowo_3.gif, 0xaa0f18a6
+check_435 = styles/snow/snowo_4.gif, 0xe529dec9
+check_436 = styles/snow/snowo_5.gif, 0xcb378db
+check_437 = styles/snow/snowo_6.gif, 0xfdef6e0d
+check_438 = styles/snow/snowo_7.gif, 0x1f368231
+check_439 = styles/snow/snowo_8.gif, 0x42ac835d
+check_440 = styles/snow/snowo_9.gif, 0x9d996981
+check_441 = styles/snow/snow_0.gif, 0x2b4e6570
+check_442 = styles/snow/snow_1.gif, 0x47f3d754
+check_443 = styles/snow/snow_10.gif, 0x41d39dc0
+check_444 = styles/snow/snow_11.gif, 0xff44c6c8
+check_445 = styles/snow/snow_12.gif, 0xe0157676
+check_446 = styles/snow/snow_13.gif, 0x126a9f01
+check_447 = styles/snow/snow_14.gif, 0x73feb2c1
+check_448 = styles/snow/snow_15.gif, 0x2276a51e
+patch_47 = styles/snow/snow_16.gif, 0xf9f7249f
+check_449 = styles/snow/snow_17.gif, 0xc2c464c2
+check_450 = styles/snow/snow_18.gif, 0x86852673
+check_451 = styles/snow/snow_19.gif, 0xd6cdb61d
+check_452 = styles/snow/snow_2.gif, 0x62627b6d
+patch_48 = styles/snow/snow_20.gif, 0xfdc32d3c
+check_453 = styles/snow/snow_21.gif, 0xa23febf9
+check_454 = styles/snow/snow_22.gif, 0x5545deb3
+check_455 = styles/snow/snow_23.gif, 0x2a5de9d0
+patch_49 = styles/snow/snow_24.gif, 0x24271008
+check_456 = styles/snow/snow_25.gif, 0x826948ff
+check_457 = styles/snow/snow_26.gif, 0xab4568d5
+check_458 = styles/snow/snow_27.gif, 0xac7396f2
+check_459 = styles/snow/snow_28.gif, 0x365fedbc
+check_460 = styles/snow/snow_29.gif, 0xd1f9011d
+check_461 = styles/snow/snow_3.gif, 0x41f5d6af
+check_462 = styles/snow/snow_30.gif, 0xa2e24ede
+check_463 = styles/snow/snow_31.gif, 0xd6376874
+check_464 = styles/snow/snow_32.gif, 0xfe99f929
+check_465 = styles/snow/snow_33.gif, 0xc5864e83
+patch_50 = styles/snow/snow_34.gif, 0xbbfcac12
+patch_51 = styles/snow/snow_35.gif, 0xd9d96a66
+patch_52 = styles/snow/snow_36.gif, 0x54035807
+check_466 = styles/snow/snow_4.gif, 0x37fef2c9
+check_467 = styles/snow/snow_5.gif, 0xb0bc20b
+check_468 = styles/snow/snow_6.gif, 0x71e56c40
+patch_53 = styles/snow/snow_7.gif, 0xfd374b99
+patch_54 = styles/snow/snow_8.gif, 0xf00e337c
+check_469 = styles/snow/snow_9.gif, 0x5e96c51
+extract_61 = styles/special/special.ini, 0x5f8a162a
+extract_62 = styles/special/specialom_0.gif, 0xc91e4707
+check_470 = styles/special/specialo_0.gif, 0x28c966f8
+patch_55 = styles/special/specialo_1.gif, 0x74ab0a8d
+check_471 = styles/special/specialo_2.gif, 0x8ac9571a
+extract_63 = styles/special/special_0.gif, 0xc1f1d3fb
+extract_64 = styles/special/special_1.gif, 0xb9209371
+extract_65 = styles/special/special_2.gif, 0x5229b822
+extract_66 = styles/special/special_3.gif, 0x19d1b3e5
+extract_67 = styles/marble/marble.ini, 0x4fe9ce1e
+extract_68 = styles/marble/marbleom_0.gif, 0x648441ff
+extract_69 = styles/marble/marbleom_3.gif, 0x29d731e0
+extract_70 = styles/marble/marbleom_4.gif, 0x29d731e0
+extract_71 = styles/marble/marbleom_5.gif, 0xc225538d
+extract_72 = styles/marble/marbleom_8.gif, 0xdc90413c
+extract_73 = styles/marble/marbleom_9.gif, 0xaec83c58
+check_472 = styles/marble/marbleo_0.gif, 0x52c1a5c8
+patch_56 = styles/marble/marbleo_1.gif, 0x7ce2021d
+check_473 = styles/marble/marbleo_2.gif, 0x27f66d8b
+check_474 = styles/marble/marbleo_3.gif, 0xf5fcf185
+check_475 = styles/marble/marbleo_4.gif, 0x5c2ded1b
+check_476 = styles/marble/marbleo_5.gif, 0x675e8695
+check_477 = styles/marble/marbleo_6.gif, 0xd37140b2
+check_478 = styles/marble/marbleo_7.gif, 0x8f856df6
+check_479 = styles/marble/marbleo_8.gif, 0x3b5e6f03
+check_480 = styles/marble/marbleo_9.gif, 0x106c3bd2
+check_481 = styles/marble/marble_0.gif, 0x961c6384
+check_482 = styles/marble/marble_1.gif, 0xa25e9f43
+check_483 = styles/marble/marble_10.gif, 0xdf4e1410
+check_484 = styles/marble/marble_11.gif, 0x1f961753
+check_485 = styles/marble/marble_12.gif, 0x2bbb97dd
+check_486 = styles/marble/marble_13.gif, 0x79205407
+check_487 = styles/marble/marble_14.gif, 0x5fa161b2
+check_488 = styles/marble/marble_15.gif, 0x4f9f617e
+check_489 = styles/marble/marble_16.gif, 0xc7275746
+check_490 = styles/marble/marble_17.gif, 0xee5aaf1d
+check_491 = styles/marble/marble_18.gif, 0x3eca8559
+check_492 = styles/marble/marble_19.gif, 0x1728dac7
+check_493 = styles/marble/marble_2.gif, 0x1f7b0dd3
+check_494 = styles/marble/marble_20.gif, 0xf49a91d1
+check_495 = styles/marble/marble_21.gif, 0x335e3319
+check_496 = styles/marble/marble_22.gif, 0xe6fb8f0d
+check_497 = styles/marble/marble_23.gif, 0x572d7fb9
+check_498 = styles/marble/marble_24.gif, 0xe19b5824
+patch_57 = styles/marble/marble_25.gif, 0xae3627a0
+patch_58 = styles/marble/marble_26.gif, 0xf578da33
+check_499 = styles/marble/marble_27.gif, 0xc585df51
+check_500 = styles/marble/marble_28.gif, 0x4d3fd3e4
+check_501 = styles/marble/marble_29.gif, 0xd80aa858
+check_502 = styles/marble/marble_3.gif, 0x5ccce896
+check_503 = styles/marble/marble_30.gif, 0xd86ba85d
+check_504 = styles/marble/marble_31.gif, 0xe5024b44
+check_505 = styles/marble/marble_32.gif, 0xf4a26463
+check_506 = styles/marble/marble_33.gif, 0xbbbf4a04
+check_507 = styles/marble/marble_34.gif, 0x7a163ed
+check_508 = styles/marble/marble_35.gif, 0xeb28600b
+check_509 = styles/marble/marble_36.gif, 0xe311a875
+check_510 = styles/marble/marble_37.gif, 0x107062a2
+check_511 = styles/marble/marble_38.gif, 0x9cdca26c
+check_512 = styles/marble/marble_39.gif, 0x6e742ed6
+check_513 = styles/marble/marble_4.gif, 0xafa9b1d5
+check_514 = styles/marble/marble_40.gif, 0xdb99f58e
+check_515 = styles/marble/marble_41.gif, 0x77fef368
+check_516 = styles/marble/marble_42.gif, 0x3ecdea
+check_517 = styles/marble/marble_43.gif, 0xbc9c4ffa
+check_518 = styles/marble/marble_44.gif, 0x1606cf92
+check_519 = styles/marble/marble_45.gif, 0xd8b68484
+patch_59 = styles/marble/marble_46.gif, 0x3fb591e
+check_520 = styles/marble/marble_47.gif, 0x101f88f4
+check_521 = styles/marble/marble_48.gif, 0xb9777fcb
+check_522 = styles/marble/marble_49.gif, 0xe65960dc
+check_523 = styles/marble/marble_5.gif, 0xf2dc442b
+check_524 = styles/marble/marble_50.gif, 0xe45360e1
+check_525 = styles/marble/marble_51.gif, 0x2dd010bb
+check_526 = styles/marble/marble_52.gif, 0x25f212ae
+check_527 = styles/marble/marble_53.gif, 0x54b4cea5
+check_528 = styles/marble/marble_54.gif, 0x576efaf5
+patch_60 = styles/marble/marble_55.gif, 0x679e3f9f
+patch_61 = styles/marble/marble_56.gif, 0xb643bf16
+check_529 = styles/marble/marble_57.gif, 0x1925dd61
+check_530 = styles/marble/marble_58.gif, 0x4d6bea0d
+check_531 = styles/marble/marble_59.gif, 0xcef0a254
+check_532 = styles/marble/marble_6.gif, 0x4527d342
+check_533 = styles/marble/marble_7.gif, 0x4527d342
+check_534 = styles/marble/marble_8.gif, 0xba21b7f1
+check_535 = styles/marble/marble_9.gif, 0xb0b1bb93
+extract_74 = levels/2_ohno/levelpack.ini, 0x8f1e9e2e
+patch_62 = levels/2_ohno/lvl1000.ini, 0xfd5d665
+patch_63 = levels/2_ohno/lvl1001.ini, 0xfb4fa592
+patch_64 = levels/2_ohno/lvl1002.ini, 0xcfc05365
+patch_65 = levels/2_ohno/lvl1003.ini, 0xaf082e4f
+patch_66 = levels/2_ohno/lvl1004.ini, 0x8f548e3
+patch_67 = levels/2_ohno/lvl1005.ini, 0xa8a1f9b6
+patch_68 = levels/2_ohno/lvl1006.ini, 0x347254a0
+patch_69 = levels/2_ohno/lvl1007.ini, 0x5a149d66
+patch_70 = levels/2_ohno/lvl1010.ini, 0xc5299788
+patch_71 = levels/2_ohno/lvl1011.ini, 0xf6d05675
+patch_72 = levels/2_ohno/lvl1012.ini, 0x2fcc8678
+patch_73 = levels/2_ohno/lvl1013.ini, 0x48483f55
+patch_74 = levels/2_ohno/lvl1014.ini, 0x5836a8ee
+patch_75 = levels/2_ohno/lvl1015.ini, 0x4816e94e
+patch_76 = levels/2_ohno/lvl1016.ini, 0x7448db8a
+patch_77 = levels/2_ohno/lvl1017.ini, 0x9e722ca
+patch_78 = levels/2_ohno/lvl1020.ini, 0xe6f5b125
+patch_79 = levels/2_ohno/lvl1021.ini, 0x51dd56d0
+patch_80 = levels/2_ohno/lvl1022.ini, 0x9959fc27
+patch_81 = levels/2_ohno/lvl1023.ini, 0x3f8ee997
+patch_82 = levels/2_ohno/lvl1024.ini, 0x35bf5032
+patch_83 = levels/2_ohno/lvl1025.ini, 0x14ed1867
+patch_84 = levels/2_ohno/lvl1026.ini, 0xc00e5773
+patch_85 = levels/2_ohno/lvl1027.ini, 0x3c1473c3
+patch_86 = levels/2_ohno/lvl1030.ini, 0x8c4b33c5
+patch_87 = levels/2_ohno/lvl1031.ini, 0x73e6cf75
+patch_88 = levels/2_ohno/lvl1032.ini, 0x275eab15
+patch_89 = levels/2_ohno/lvl1033.ini, 0x3fd146c5
+patch_90 = levels/2_ohno/lvl1034.ini, 0xaed67982
+patch_91 = levels/2_ohno/lvl1035.ini, 0x4e09573
+patch_92 = levels/2_ohno/lvl1036.ini, 0x893e7545
+patch_93 = levels/2_ohno/lvl1037.ini, 0xc1a6cf71
+patch_94 = levels/2_ohno/lvl1040.ini, 0x74650e3c
+patch_95 = levels/2_ohno/lvl1041.ini, 0xd6b40ec2
+patch_96 = levels/2_ohno/lvl1042.ini, 0xd27fed07
+patch_97 = levels/2_ohno/lvl1043.ini, 0xeff67a57
+patch_98 = levels/2_ohno/lvl1044.ini, 0xd25aa63
+patch_99 = levels/2_ohno/lvl1045.ini, 0x124bea51
+patch_100 = levels/2_ohno/lvl1046.ini, 0x4288b76e
+patch_101 = levels/2_ohno/lvl1047.ini, 0xe24686bf
+patch_102 = levels/2_ohno/lvl1050.ini, 0x8c3d648e
+patch_103 = levels/2_ohno/lvl1051.ini, 0xfa3d31c6
+patch_104 = levels/2_ohno/lvl1052.ini, 0x267a0e8e
+patch_105 = levels/2_ohno/lvl1053.ini, 0x8e08459f
+patch_106 = levels/2_ohno/lvl1054.ini, 0x7510c204
+patch_107 = levels/2_ohno/lvl1055.ini, 0x6c555cb2
+patch_108 = levels/2_ohno/lvl1056.ini, 0x626247e5
+patch_109 = levels/2_ohno/lvl1057.ini, 0x60240450
+patch_110 = levels/2_ohno/lvl1060.ini, 0xf1a7072c
+patch_111 = levels/2_ohno/lvl1061.ini, 0x48753863
+patch_112 = levels/2_ohno/lvl1062.ini, 0x51ab1a2d
+patch_113 = levels/2_ohno/lvl1063.ini, 0x236c61eb
+patch_114 = levels/2_ohno/lvl1064.ini, 0x272bd959
+patch_115 = levels/2_ohno/lvl1065.ini, 0xfc53f848
+patch_116 = levels/2_ohno/lvl1066.ini, 0x3fc837c9
+patch_117 = levels/2_ohno/lvl1067.ini, 0xe1cbc6cf
+patch_118 = levels/2_ohno/lvl1070.ini, 0x6d37373d
+patch_119 = levels/2_ohno/lvl1071.ini, 0x8024d048
+patch_120 = levels/2_ohno/lvl1072.ini, 0xc793154a
+patch_121 = levels/2_ohno/lvl1073.ini, 0xfe48ffad
+patch_122 = levels/2_ohno/lvl1074.ini, 0x13b5a454
+patch_123 = levels/2_ohno/lvl1075.ini, 0x2e18693c
+patch_124 = levels/2_ohno/lvl1076.ini, 0x14f23d8a
+patch_125 = levels/2_ohno/lvl1077.ini, 0xf0da895f
+patch_126 = levels/2_ohno/lvl1080.ini, 0xc4fe0097
+patch_127 = levels/2_ohno/lvl1081.ini, 0xd8bfb2c
+patch_128 = levels/2_ohno/lvl1082.ini, 0xe4c43478
+patch_129 = levels/2_ohno/lvl1083.ini, 0x13cce5e
+patch_130 = levels/2_ohno/lvl1084.ini, 0x4c2491ab
+patch_131 = levels/2_ohno/lvl1085.ini, 0xab45d9c4
+patch_132 = levels/2_ohno/lvl1086.ini, 0x9da124c
+patch_133 = levels/2_ohno/lvl1087.ini, 0x85b955cb
+patch_134 = levels/2_ohno/lvl1090.ini, 0x2389f764
+patch_135 = levels/2_ohno/lvl1091.ini, 0x5ed8a8f7
+patch_136 = levels/2_ohno/lvl1092.ini, 0x6709745e
+patch_137 = levels/2_ohno/lvl1093.ini, 0xbeb95ca3
+patch_138 = levels/2_ohno/lvl1094.ini, 0xab2b44f5
+patch_139 = levels/2_ohno/lvl1095.ini, 0x43b9a03a
+patch_140 = levels/2_ohno/lvl1096.ini, 0x90bc2917
+patch_141 = levels/2_ohno/lvl1097.ini, 0xa23cd7eb
+patch_142 = levels/2_ohno/lvl1100.ini, 0xce4d48b2
+patch_143 = levels/2_ohno/lvl1101.ini, 0x7b18f4c
+patch_144 = levels/2_ohno/lvl1102.ini, 0x2795eee8
+patch_145 = levels/2_ohno/lvl1103.ini, 0x9cee524b
+patch_146 = levels/2_ohno/lvl1104.ini, 0xdedd7fbe
+patch_147 = levels/2_ohno/lvl1105.ini, 0x763b3fee
+patch_148 = levels/2_ohno/lvl1106.ini, 0x31f5e363
+patch_149 = levels/2_ohno/lvl1107.ini, 0x3ea5de
+patch_150 = levels/2_ohno/lvl1110.ini, 0x991940b1
+patch_151 = levels/2_ohno/lvl1111.ini, 0x46824b37
+patch_152 = levels/2_ohno/lvl1112.ini, 0xe78dc77a
+patch_153 = levels/2_ohno/lvl1113.ini, 0x2f51a781
+patch_154 = levels/2_ohno/lvl1114.ini, 0x1c3a5680
+patch_155 = levels/2_ohno/lvl1115.ini, 0xb8994811
+patch_156 = levels/2_ohno/lvl1116.ini, 0x223bcfa1
+patch_157 = levels/2_ohno/lvl1117.ini, 0x616172ab
+extract_75 = levels/2_ohno/tame_17.ini, 0xd60c51cd
+extract_76 = levels/2_ohno/tame_18.ini, 0x7dc787fe
+extract_77 = levels/2_ohno/tame_19.ini, 0x1778b6eb
+extract_78 = levels/2_ohno/tame_20.ini, 0x47e677e8
+extract_79 = levels/1_orig/levelpack.ini, 0xda925982
+patch_158 = levels/1_orig/lvl0000.ini, 0x392f6b05
+patch_159 = levels/1_orig/lvl0001.ini, 0xff34d4cd
+patch_160 = levels/1_orig/lvl0002.ini, 0xa80bd2d4
+patch_161 = levels/1_orig/lvl0003.ini, 0x6a27ea90
+patch_162 = levels/1_orig/lvl0004.ini, 0xafa5d578
+patch_163 = levels/1_orig/lvl0005.ini, 0x7a735b57
+patch_164 = levels/1_orig/lvl0006.ini, 0xdb287b8f
+patch_165 = levels/1_orig/lvl0006b.ini, 0x6c577d44
+patch_166 = levels/1_orig/lvl0007.ini, 0x2ab70576
+patch_167 = levels/1_orig/lvl0007b.ini, 0x8367046d
+patch_168 = levels/1_orig/lvl0010.ini, 0xe6b42836
+patch_169 = levels/1_orig/lvl0011.ini, 0xfd608a73
+patch_170 = levels/1_orig/lvl0012.ini, 0xa48b47f1
+patch_171 = levels/1_orig/lvl0012b.ini, 0x4ff34df9
+patch_172 = levels/1_orig/lvl0013.ini, 0xe378d4fb
+patch_173 = levels/1_orig/lvl0014.ini, 0xb57f79b5
+patch_174 = levels/1_orig/lvl0015.ini, 0xe730cadd
+patch_175 = levels/1_orig/lvl0016.ini, 0xa12a77d6
+patch_176 = levels/1_orig/lvl0017.ini, 0xb25050f6
+patch_177 = levels/1_orig/lvl0017b.ini, 0xc90f5263
+patch_178 = levels/1_orig/lvl0020.ini, 0x71eda0c3
+patch_179 = levels/1_orig/lvl0021.ini, 0xccd4aae8
+patch_180 = levels/1_orig/lvl0021b.ini, 0x5b39ae54
+patch_181 = levels/1_orig/lvl0022.ini, 0xc84725e5
+patch_182 = levels/1_orig/lvl0022b.ini, 0xd9bf2ea7
+patch_183 = levels/1_orig/lvl0023.ini, 0x522ef0be
+patch_184 = levels/1_orig/lvl0024.ini, 0x98cbc2a6
+patch_185 = levels/1_orig/lvl0024b.ini, 0x7c75c88d
+patch_186 = levels/1_orig/lvl0025.ini, 0x140be555
+patch_187 = levels/1_orig/lvl0026.ini, 0xa333bec3
+patch_188 = levels/1_orig/lvl0027.ini, 0x9ba17c1c
+patch_189 = levels/1_orig/lvl0027b.ini, 0xe0e1803c
+patch_190 = levels/1_orig/lvl0030.ini, 0x6631f927
+patch_191 = levels/1_orig/lvl0030b.ini, 0x5f3400b6
+patch_192 = levels/1_orig/lvl0031.ini, 0xedec4266
+patch_193 = levels/1_orig/lvl0031b.ini, 0x74ca481a
+patch_194 = levels/1_orig/lvl0032.ini, 0xd94d917a
+patch_195 = levels/1_orig/lvl0032b.ini, 0xb387961a
+patch_196 = levels/1_orig/lvl0033.ini, 0xe54f3fbe
+patch_197 = levels/1_orig/lvl0033b.ini, 0x764a41be
+patch_198 = levels/1_orig/lvl0034.ini, 0x27e33330
+patch_199 = levels/1_orig/lvl0034b.ini, 0x642839ac
+patch_200 = levels/1_orig/lvl0035.ini, 0x850229ed
+patch_201 = levels/1_orig/lvl0036.ini, 0xd1db7e63
+patch_202 = levels/1_orig/lvl0037.ini, 0xd9993d95
+patch_203 = levels/1_orig/lvl0040.ini, 0x9f5d11aa
+patch_204 = levels/1_orig/lvl0041.ini, 0xba3917bc
+patch_205 = levels/1_orig/lvl0041b.ini, 0x65701dcd
+patch_206 = levels/1_orig/lvl0042.ini, 0x922d545d
+patch_207 = levels/1_orig/lvl0042b.ini, 0x3ae05a6f
+patch_208 = levels/1_orig/lvl0043.ini, 0xee0a720
+patch_209 = levels/1_orig/lvl0043b.ini, 0xa194a76a
+patch_210 = levels/1_orig/lvl0044.ini, 0x729144c7
+patch_211 = levels/1_orig/lvl0045.ini, 0x60f5f6dd
+patch_212 = levels/1_orig/lvl0046.ini, 0xc7f98410
+patch_213 = levels/1_orig/lvl0046b.ini, 0xf7c489b0
+patch_214 = levels/1_orig/lvl0047.ini, 0x170ff7bf
+patch_215 = levels/1_orig/lvl0047b.ini, 0x1941fc3f
+patch_216 = levels/1_orig/lvl0050.ini, 0x25ef53d7
+patch_217 = levels/1_orig/lvl0051.ini, 0x4893c2c8
+patch_218 = levels/1_orig/lvl0051b.ini, 0x1f72c910
+patch_219 = levels/1_orig/lvl0052.ini, 0x78542cd7
+patch_220 = levels/1_orig/lvl0053.ini, 0xcb291456
+patch_221 = levels/1_orig/lvl0054.ini, 0x6d3b45d6
+patch_222 = levels/1_orig/lvl0055.ini, 0x682e8e6a
+patch_223 = levels/1_orig/lvl0056.ini, 0x5f65b611
+patch_224 = levels/1_orig/lvl0057.ini, 0x178f351f
+patch_225 = levels/1_orig/lvl0057b.ini, 0x73b837eb
+patch_226 = levels/1_orig/lvl0060.ini, 0xf19177e9
+patch_227 = levels/1_orig/lvl0060b.ini, 0x36a57e56
+patch_228 = levels/1_orig/lvl0061.ini, 0xb5b2a9b8
+patch_229 = levels/1_orig/lvl0061b.ini, 0x7c3daecb
+patch_230 = levels/1_orig/lvl0062.ini, 0xb6175ef8
+patch_231 = levels/1_orig/lvl0062b.ini, 0x6021631f
+patch_232 = levels/1_orig/lvl0063.ini, 0x62b9dd7e
+patch_233 = levels/1_orig/lvl0063b.ini, 0xda58df6f
+patch_234 = levels/1_orig/lvl0064.ini, 0x83796c74
+patch_235 = levels/1_orig/lvl0065.ini, 0x51c5674d
+patch_236 = levels/1_orig/lvl0065b.ini, 0x8472744e
+patch_237 = levels/1_orig/lvl0066.ini, 0x5cd8c591
+patch_238 = levels/1_orig/lvl0067.ini, 0x5d194374
+patch_239 = levels/1_orig/lvl0067b.ini, 0xe384731
+patch_240 = levels/1_orig/lvl0067c.ini, 0x90424718
+patch_241 = levels/1_orig/lvl0070.ini, 0x761d974a
+patch_242 = levels/1_orig/lvl0071.ini, 0xc3026d59
+patch_243 = levels/1_orig/lvl0071b.ini, 0x765272a4
+patch_244 = levels/1_orig/lvl0072.ini, 0x2d3f695b
+patch_245 = levels/1_orig/lvl0073.ini, 0x8ec01abe
+patch_246 = levels/1_orig/lvl0073b.ini, 0xde641f3c
+patch_247 = levels/1_orig/lvl0074.ini, 0x6601cf9
+patch_248 = levels/1_orig/lvl0075.ini, 0xc8b7db63
+patch_249 = levels/1_orig/lvl0076.ini, 0xd57e9389
+patch_250 = levels/1_orig/lvl0077.ini, 0xcb7062d8
+patch_251 = levels/1_orig/lvl0077b.ini, 0x1afb659e
+patch_252 = levels/1_orig/lvl0080.ini, 0x5ab6666c
+patch_253 = levels/1_orig/lvl0080b.ini, 0xa35e68cb
+patch_254 = levels/1_orig/lvl0081.ini, 0x80ba5de8
+patch_255 = levels/1_orig/lvl0082.ini, 0xadaf4916
+patch_256 = levels/1_orig/lvl0082b.ini, 0x72254c1c
+patch_257 = levels/1_orig/lvl0083.ini, 0xa1956e17
+patch_258 = levels/1_orig/lvl0083b.ini, 0x54926e72
+patch_259 = levels/1_orig/lvl0084.ini, 0xb9d62b7
+patch_260 = levels/1_orig/lvl0084b.ini, 0x5536465
+patch_261 = levels/1_orig/lvl0085.ini, 0x8c87343e
+patch_262 = levels/1_orig/lvl0086.ini, 0x7490d524
+patch_263 = levels/1_orig/lvl0087.ini, 0xe46ea602
+patch_264 = levels/1_orig/lvl0090.ini, 0x792bea66
+patch_265 = levels/1_orig/lvl0091.ini, 0xdc3e08ae
+patch_266 = levels/1_orig/lvl0091b.ini, 0x30780a78
+patch_267 = levels/1_orig/lvl0092.ini, 0x5e66c7af
+patch_268 = levels/1_orig/lvl0092b.ini, 0xc52ccd68
+patch_269 = levels/1_orig/lvl0093.ini, 0x59ca7a11
+patch_270 = levels/1_orig/lvl0093b.ini, 0x896b79b2
+patch_271 = levels/1_orig/lvl0094.ini, 0xa0ceea1
+patch_272 = levels/1_orig/lvl0094b.ini, 0x3c49f00e
+patch_273 = levels/1_orig/lvl0095.ini, 0x2d133a3a
+patch_274 = levels/1_orig/lvl0095b.ini, 0x9b0b3ed1
+patch_275 = levels/1_orig/lvl0096.ini, 0xadb151c1
+patch_276 = levels/1_orig/lvl0097.ini, 0xaf104544
+patch_277 = levels/1_orig/lvl0097b.ini, 0x84cf4aff
+extract_80 = misc/border.gif, 0xd192045d
+check_536 = misc/countdown.gif, 0xdc7fc0ed
+extract_81 = misc/cursor.gif, 0xd94e2ef6
+extract_82 = misc/explode.gif, 0x255c033f
+extract_83 = misc/icon_0.gif, 0xee9b0898
+extract_84 = misc/icon_1.gif, 0xa3fb36e5
+extract_85 = misc/icon_10.gif, 0xbfc32a79
+extract_86 = misc/icon_11.gif, 0xe2e99669
+extract_87 = misc/icon_12.gif, 0x53401c2b
+extract_88 = misc/icon_13.gif, 0xbf311e61
+extract_89 = misc/icon_13b.gif, 0x3ba62e74
+extract_90 = misc/icon_2.gif, 0xb62584f6
+extract_91 = misc/icon_3.gif, 0xad8aab8a
+extract_92 = misc/icon_4.gif, 0x111189d9
+extract_93 = misc/icon_5.gif, 0x55348831
+extract_94 = misc/icon_6.gif, 0xb8d295c3
+extract_95 = misc/icon_6_alt.gif, 0x64743212
+extract_96 = misc/icon_7.gif, 0x1c0eb62f
+extract_97 = misc/icon_8.gif, 0x857d8308
+extract_98 = misc/icon_9.gif, 0x19aba353
+extract_99 = misc/imask_13.gif, 0x68941c71
+extract_100 = misc/imask_14.gif, 0x907f8726
+extract_101 = misc/imask_15.gif, 0x420b8f33
+extract_102 = misc/lemmfont.gif, 0xfe736bf0
+extract_103 = misc/lemming.ini, 0xce0e312f
+check_537 = misc/lemm_0.gif, 0x5024a42f
+patch_278 = misc/lemm_1.gif, 0x84220e86
+check_538 = misc/lemm_10.gif, 0x377bf55
+patch_279 = misc/lemm_11.gif, 0x4c443384
+check_539 = misc/lemm_12.gif, 0x7980c42d
+patch_280 = misc/lemm_13.gif, 0xad8f104a
+patch_281 = misc/lemm_14.gif, 0x662d4174
+patch_282 = misc/lemm_15.gif, 0x51abd325
+extract_104 = misc/lemm_16.gif, 0x49090b61
+patch_283 = misc/lemm_2.gif, 0x9be8c569
+check_540 = misc/lemm_3.gif, 0x113f8e81
+patch_284 = misc/lemm_4.gif, 0xd3c6d159
+check_541 = misc/lemm_5.gif, 0x9274fbff
+patch_285 = misc/lemm_6.gif, 0xb9b6f29a
+check_542 = misc/lemm_7.gif, 0x39fde0ba
+patch_286 = misc/lemm_8.gif, 0x3950315a
+check_543 = misc/lemm_9.gif, 0x767666ba
+patch_287 = misc/mask_10.gif, 0x1f2ea3c3
+patch_288 = misc/mask_11.gif, 0xa74384b0
+patch_289 = misc/mask_13.gif, 0x657c2028
+patch_290 = misc/mask_14.gif, 0xd922cb63
+patch_291 = misc/mask_15.gif, 0x8089b587
+extract_105 = misc/mask_6.gif, 0x88ee8d27
+extract_106 = misc/numfont.gif, 0x1ab7e6d3
+check_544 = misc/replay.gif, 0x1c8e0a66
+extract_107 = misc/select.gif, 0xd29c5d99
+extract_108 = music/awesome.mod, 0xcae73a0b
+extract_109 = music/beasti.mod, 0xca6415e6
+extract_110 = music/beastii.mod, 0xfbea7229
+extract_111 = music/cancan.mod, 0xd3f5253d
+extract_112 = music/doggie.mod, 0x148b7756
+extract_113 = music/lemming1.mod, 0x6e0a5965
+extract_114 = music/lemming2.mod, 0xae0887c6
+extract_115 = music/lemming3.mod, 0x47b782da
+extract_116 = music/menace.mod, 0xc43773b1
+extract_117 = music/mountain.mod, 0x81c9f463
+extract_118 = music/tenlemms.mod, 0x6ccd4f78
+extract_119 = music/tim1.mod, 0xd9c31538
+extract_120 = music/tim10.mod, 0x61b83215
+extract_121 = music/tim2.mod, 0xb48a52d3
+extract_122 = music/tim3.mod, 0xf3dde603
+extract_123 = music/tim4.mod, 0xfd4f9f61
+extract_124 = music/tim5.mod, 0xaf4a4765
+extract_125 = music/tim6.mod, 0xdc64537a
+extract_126 = music/tim7.mod, 0xa4a4a732
+extract_127 = music/tim8.mod, 0x16959daf
+extract_128 = music/tim9.mod, 0x66dc3d21
+extract_129 = music/tune1.mod, 0x85ab3858
+extract_130 = music/tune2.mod, 0x5da50a02
+extract_131 = music/tune3.mod, 0xbc25359e
+extract_132 = music/tune4.mod, 0x61ce8f38
+extract_133 = music/tune5.mod, 0xd136ce9a
+extract_134 = music/tune6.mod, 0xe4a56b6f
+check_545 = sound/sound_0.wav, 0x54056532
+check_546 = sound/sound_1.wav, 0x500813c1
+check_547 = sound/sound_10.wav, 0xd2f25f54
+check_548 = sound/sound_11.wav, 0x9643059f
+check_549 = sound/sound_12.wav, 0x56ad6a1e
+check_550 = sound/sound_13.wav, 0x281877b2
+check_551 = sound/sound_14.wav, 0xcd151389
+check_552 = sound/sound_15.wav, 0xc547501c
+check_553 = sound/sound_16.wav, 0x42f41fa1
+check_554 = sound/sound_17.wav, 0x9c037f8a
+check_555 = sound/sound_18.wav, 0x4157353
+check_556 = sound/sound_19.wav, 0xc02bca19
+check_557 = sound/sound_2.wav, 0xb186b072
+check_558 = sound/sound_20.wav, 0x5e9a1895
+check_559 = sound/sound_21.wav, 0xaa1885bb
+check_560 = sound/sound_22.wav, 0x5e4b3508
+check_561 = sound/sound_23.wav, 0x51d7edd2
+check_562 = sound/sound_3.wav, 0x5b604a03
+patch_292 = sound/sound_4.wav, 0x31027655
+check_563 = sound/sound_5.wav, 0x1890fa60
+check_564 = sound/sound_6.wav, 0xf9012625
+check_565 = sound/sound_7.wav, 0x216a3900
+check_566 = sound/sound_8.wav, 0x784ab053
+check_567 = sound/sound_9.wav, 0xf8e54fb
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/sound@sound_4.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/sound@sound_4.dif
new file mode 100644
index 0000000..9674df3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/sound@sound_4.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brick.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brick.ini
new file mode 100644
index 0000000..0a14272
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brick.ini
@@ -0,0 +1,81 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xd0b080
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xd0b080,0xd0b080,0xC00000,0xA00000,0x801000,0x601000,0xd0d0d0,0xa0a0c0,0x506070,0x777500
+
+tiles = 60
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# water - always animate
+frames_5 = 9
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# stamper trap - animate on trigger
+frames_6 = 18
+anim_6 = 2
+type_6 = 6
+sound_6 = 20
+
+# buzz saw trap - animate on trigger
+frames_7 = 20
+anim_7 = 2
+type_7 = 6
+sound_7 = 1
+
+# blue flag - always animate
+frames_8 = 14
+anim_8 = 1
+type_8 = 0
+sound_8 = -1
+
+# animated part of exit - always animate
+frames_9 = 4
+anim_9 = 1
+type_9 = 0
+sound_9 = -1
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_0.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_0.dif
new file mode 100644
index 0000000..2cd113b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_0.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_1.dif
new file mode 100644
index 0000000..520bea4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_6.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_6.dif
new file mode 100644
index 0000000..dc287ef
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@bricko_6.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_0.gif
new file mode 100644
index 0000000..e4b749d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_3.gif
new file mode 100644
index 0000000..6b770c9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_4.gif
new file mode 100644
index 0000000..6b770c9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_5.gif
new file mode 100644
index 0000000..ac1b8b0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_6.gif
new file mode 100644
index 0000000..f40b026
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_7.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_7.gif
new file mode 100644
index 0000000..e3068eb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@brick@brickom_7.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble.ini
new file mode 100644
index 0000000..c212edc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble.ini
@@ -0,0 +1,87 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0x50d0f0
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0x50d0f0,0x50d0f0,0x30a0f0,0x1060b0,0x004090,0xf0f070,0xc0a040,0x906010,0x603000,0x286877
+
+tiles = 64
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# bubble water - always animate
+frames_5 = 6
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# red arrow - always animate (animated part of exit)
+frames_6 = 7
+anim_6 = 1
+type_6 = 0
+sound_6 = -1
+
+# blue flag - always animate
+frames_7 = 14
+anim_7 = 1
+type_7 = 0
+sound_7 = -1
+
+# zapper trap - animate on trigger
+frames_8 = 14
+anim_8 = 2
+type_8 = 6
+sound_8 = 6
+
+# pipe trap - animate on trigger
+frames_9 = 20
+anim_9 = 2
+type_9 = 6
+sound_9 = 19
+
+# zapper (?)
+frames_10 = 1
+anim_10 = 0
+type_10 = 0
+sound_10 = -1
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_0.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_0.dif
new file mode 100644
index 0000000..49423af
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_0.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_1.dif
new file mode 100644
index 0000000..bad11fa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_10.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_10.dif
new file mode 100644
index 0000000..a1e9d92
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_10.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_16.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_16.dif
new file mode 100644
index 0000000..49edbca
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_16.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_2.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_2.dif
new file mode 100644
index 0000000..e4e0656
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_2.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_3.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_3.dif
new file mode 100644
index 0000000..83da24c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubble_3.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleo_1.dif
new file mode 100644
index 0000000..cdcc7c8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_0.gif
new file mode 100644
index 0000000..cb5a524
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_3.gif
new file mode 100644
index 0000000..6b770c9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_4.gif
new file mode 100644
index 0000000..6b770c9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_5.gif
new file mode 100644
index 0000000..dd2d8ce
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_8.gif
new file mode 100644
index 0000000..3578930
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_9.gif
new file mode 100644
index 0000000..743c7f0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@bubble@bubbleom_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal.ini
new file mode 100644
index 0000000..345580f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal.ini
@@ -0,0 +1,87 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xf0f0f0
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xe08020,0xf0f0f0,0xa0c0d0,0x70a0b0,0x4a80a0,0x206080,0x004050,0x504040,0xc8d8e0,0x88b0c0
+
+tiles = 37
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# blue flag - always animate
+frames_3 = 14
+anim_3 = 1
+type_3 = 0
+sound_3 = -1
+
+# arrows left - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 4
+sound_4 = -1
+
+# arrows right - always animate
+frames_5 = 7
+anim_5 = 1
+type_5 = 3
+sound_5 = -1
+
+# water - always animate
+frames_6 = 8
+anim_6 = 1
+type_6 = 5
+sound_6 = 9
+
+# blade trap - animate on trigger
+frames_7 = 25
+anim_7 = 2
+type_7 = 6
+
+# animated part of exit - always animate
+frames_8 = 6
+anim_8 = 1
+type_8 = 0
+sound_8 = -1
+
+# zapper trap - animate on trigger
+frames_9 = 8
+anim_9 = 2
+type_9 = 6
+sound_9 = 6
+
+# zapper trap - animate on trigger
+frames_10 = 16
+anim_10 = 2
+type_10 = 6
+sound_10 = 6
+
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal_8.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal_8.dif
new file mode 100644
index 0000000..ffe5b03
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystal_8.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalo_1.dif
new file mode 100644
index 0000000..8e486fc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_0.gif
new file mode 100644
index 0000000..3880a93
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_10.gif
new file mode 100644
index 0000000..1035033
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_5.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_6.gif
new file mode 100644
index 0000000..53cf307
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_7.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_7.gif
new file mode 100644
index 0000000..e99f959
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_7.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_9.gif
new file mode 100644
index 0000000..54138ea
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@crystal@crystalom_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt.ini
new file mode 100644
index 0000000..6ebc20e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt.ini
@@ -0,0 +1,87 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xe08020
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xe08020,0xe08020,0xc05010,0x902010,0x600010,0x404050,0x606070,0x709000,0x206020,0x787800
+
+tiles = 50
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# water - always animate
+frames_5 = 8
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# bear trap - animate on trigger
+frames_6 = 15
+anim_6 = 2
+type_6 = 6
+sound_6 = 11
+
+# animated part of exit - always animate
+frames_7 = 6
+anim_7 = 1
+type_7 = 0
+sound_7 = -1
+
+# falling stone trap - animate on trigger
+frames_8 = 17
+anim_8 = 2
+type_8 = 6
+sound_8 = 20
+
+# blue flag - always animate
+frames_9 = 14
+anim_9 = 1
+type_9 = 0
+sound_9 = -1
+
+# 10 ton trap - animate on trigger
+frames_10 = 12
+anim_10 = 2
+type_10 = 6
+sound_10 = 19
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_0.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_0.dif
new file mode 100644
index 0000000..0eb9501
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_0.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_1.dif
new file mode 100644
index 0000000..f25bee9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_16.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_16.dif
new file mode 100644
index 0000000..778a2f1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_16.dif
@@ -0,0 +1 @@
+ï¾­ÞÁÁir\Pyþäÿà­³‰ J”±¢ÅŠ3R¼È‘£Æ;Š|²ä‚‘( š4™åÊ•-;¾|ÓâÌ™5!Þ¤™³áNœ=þätàP E%Zt)̤J–„UêGªVAbͪq+׉^¿*„*lX±T –=˜V-Ú¶U¿Âu›u®Ñºvï:Í[p/ß¾CÿFÜ)Ø!ÓÂ>¯ \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_17.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_17.dif
new file mode 100644
index 0000000..e1dea58
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_17.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_22.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_22.dif
new file mode 100644
index 0000000..80e156a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_22.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_27.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_27.dif
new file mode 100644
index 0000000..49291ab
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_27.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_29.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_29.dif
new file mode 100644
index 0000000..cdb82fc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_29.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_30.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_30.dif
new file mode 100644
index 0000000..2940f23
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_30.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_31.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_31.dif
new file mode 100644
index 0000000..85306f1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_31.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_32.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_32.dif
new file mode 100644
index 0000000..64a0fee
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_32.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_33.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_33.dif
new file mode 100644
index 0000000..51ced42
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_33.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_34.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_34.dif
new file mode 100644
index 0000000..92826e7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_34.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_35.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_35.dif
new file mode 100644
index 0000000..b799f88
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_35.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_36.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_36.dif
new file mode 100644
index 0000000..fcb83bb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_36.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_37.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_37.dif
new file mode 100644
index 0000000..aa46f48
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_37.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_38.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_38.dif
new file mode 100644
index 0000000..807cf17
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_38.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_39.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_39.dif
new file mode 100644
index 0000000..83a8f6e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_39.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_40.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_40.dif
new file mode 100644
index 0000000..5e53e1d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_40.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_41.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_41.dif
new file mode 100644
index 0000000..f208a72
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_41.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_44.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_44.dif
new file mode 100644
index 0000000..44f40dc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirt_44.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirto_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirto_1.dif
new file mode 100644
index 0000000..2185219
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirto_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_0.gif
new file mode 100644
index 0000000..970b45f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_10.gif
new file mode 100644
index 0000000..16eccd5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_3.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_5.gif
new file mode 100644
index 0000000..844ca48
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_6.gif
new file mode 100644
index 0000000..38ec9b4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_8.gif
new file mode 100644
index 0000000..95ad672
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@dirt@dirtom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire.ini
new file mode 100644
index 0000000..002b0c0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire.ini
@@ -0,0 +1,88 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0x006090
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0x006090,0x006090,0x800000,0x504040,0x402020,0x605050,0x807070,0xc00000,0x004060,0x20216e
+
+tiles = 64
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 8
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 8
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# lava - always animate
+frames_5 = 8
+anim_5 = 1
+type_5 = 5
+sound_5 = 8
+
+# animated part of exit - always animate
+frames_6 = 6
+anim_6 = 1
+type_6 = 0
+sound_6 = -1
+
+# fire pit trap - animate on trigger
+frames_7 = 8
+anim_7 = 1
+type_7 = 7
+sound_7 = 8
+
+# flame thrower left trap - animate on trigger
+frames_8 = 10
+anim_8 = 1
+type_8 = 7
+sound_8 = 8
+
+# blue flag - always animate
+frames_9 = 14
+anim_9 = 1
+type_9 = 0
+sound_10 = -1
+
+# flame thrower right trap - animate on trigger
+frames_10 = 10
+anim_10 = 1
+type_10 = 7
+sound_10 = 8
+
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_5.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_5.dif
new file mode 100644
index 0000000..2f8b0ef
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_5.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_6.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_6.dif
new file mode 100644
index 0000000..76bdca9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_6.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_7.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_7.dif
new file mode 100644
index 0000000..b91627a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_7.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_8.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_8.dif
new file mode 100644
index 0000000..71766c2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fire_8.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_1.dif
new file mode 100644
index 0000000..04803b0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_6.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_6.dif
new file mode 100644
index 0000000..d7742d8
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireo_6.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_0.gif
new file mode 100644
index 0000000..4e1544f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_10.gif
new file mode 100644
index 0000000..6ad2790
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_3.gif
new file mode 100644
index 0000000..862bb49
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_4.gif
new file mode 100644
index 0000000..862bb49
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_5.gif
new file mode 100644
index 0000000..f3840bd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_7.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_7.gif
new file mode 100644
index 0000000..5b9e39e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_7.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_8.gif
new file mode 100644
index 0000000..af747ac
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@fire@fireom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble.ini
new file mode 100644
index 0000000..20d6254
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble.ini
@@ -0,0 +1,81 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xc0b0f0
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xd0c0f0,0xc0b0f0,0xf0d0d0,0xa0a0f0,0xf0a0a0,0x8070c0,0xe08080,0xb05050,0x902020,0x5f5777
+
+tiles = 60
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# toxic water - always animate
+frames_5 = 8
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# animated part of exit - always animate
+frames_6 = 6
+anim_6 = 1
+type_6 = 0
+sound_6 = -1
+
+# blue flag - always animate
+frames_7 = 14
+anim_7 = 1
+type_7 = 0
+sound_7 = -1
+
+# squasher trap - animate on trigger
+frames_8 = 15
+anim_8 = 2
+type_8 = 6
+sound_8 = 20
+
+# spinning blade trap - always animate
+frames_9 = 16
+anim_9 = 1
+type_9 = 7
+sound_9 = 8
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_25.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_25.dif
new file mode 100644
index 0000000..77e85b9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_25.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_26.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_26.dif
new file mode 100644
index 0000000..3670df4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_26.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_46.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_46.dif
new file mode 100644
index 0000000..318298d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_46.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_55.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_55.dif
new file mode 100644
index 0000000..301f844
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_55.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_56.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_56.dif
new file mode 100644
index 0000000..b08a3d9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marble_56.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleo_1.dif
new file mode 100644
index 0000000..7f000dd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_0.gif
new file mode 100644
index 0000000..177c7a1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_3.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_5.gif
new file mode 100644
index 0000000..317eeb0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_8.gif
new file mode 100644
index 0000000..a33dd96
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_9.gif
new file mode 100644
index 0000000..a2e6d85
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@marble@marbleom_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillar.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillar.ini
new file mode 100644
index 0000000..5829d14
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillar.ini
@@ -0,0 +1,88 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xf0f060
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xf0f060,0xf0f060,0xd0b040,0xb08020,0xa05000,0x803010,0x800000,0x606070,0x404050,0x770000
+
+tiles = 62
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# water - always animate
+frames_5 = 8
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# animated part of exit - always animate
+frames_6 = 6
+anim_6 = 1
+type_6 = 0
+sound_6 = -1
+
+# blue flag - always animate
+frames_7 = 14
+anim_7 = 1
+type_7 = 0
+sound_7 = -1
+
+# wheel&rope trap - animate on trigger
+frames_8 = 37
+anim_8 = 2
+type_8 = 6
+sound_8 = 1
+
+# spikes left trap - animate on trigger
+frames_9 = 7
+anim_9 = 2
+type_9 = 7
+sound_9 = 16
+
+# spikes right trap - animate on trigger
+frames_10 = 7
+anim_10 = 2
+type_10 = 7
+sound_10 = 16
+
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_1.dif
new file mode 100644
index 0000000..545ffb4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_8.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_8.dif
new file mode 100644
index 0000000..6185b06
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillaro_8.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_0.gif
new file mode 100644
index 0000000..eaa967b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_10.gif
new file mode 100644
index 0000000..81dd104
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_3.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_5.gif
new file mode 100644
index 0000000..53cf307
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_8.gif
new file mode 100644
index 0000000..6541f7a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_9.gif
new file mode 100644
index 0000000..6483350
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@pillar@pillarom_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock.ini
new file mode 100644
index 0000000..379dfbd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock.ini
@@ -0,0 +1,95 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xe0d0b0
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xe0d0b0,0xe0d0b0,0x207000,0xA09070,0x635438,0x402f20,0x70d010,0xa04000,0x602000,0x766666
+
+tiles = 64
+
+# animated exit (atomic)
+frames_0 = 2
+anim_0 = 1
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# deady grass - always animate
+frames_5 = 4
+anim_5 = 1
+type_5 = 5
+sound_5 = 15
+
+# tentacle trap - animate on trigger
+frames_6 = 10
+anim_6 = 2
+type_6 = 6
+sound_6 = 15
+
+# chameleon body right
+frames_7 = 1
+anim_7 = 0
+type_7 = 0
+sound_7 = -1
+
+# chameleon tongue trap right - animate on trigger
+frames_8 = 5
+anim_8 = 2
+type_8 = 6
+sound_8 = 16
+
+# chameleon body left
+frames_9 = 1
+anim_9 = 0
+type_9 = 0
+sound_9 = -1
+
+# chameleon tongue trap right - animate on trigger
+frames_10 = 5
+anim_10 = 2
+type_10 = 6
+sound_10 = 16
+
+# blue flag - always animate
+frames_11 = 14
+anim_11 = 1
+type_11 = 0
+sound_11 = -1
+
+
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_10.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_10.dif
new file mode 100644
index 0000000..70de1ed
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_10.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_12.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_12.dif
new file mode 100644
index 0000000..f21a164
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_12.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_6.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_6.dif
new file mode 100644
index 0000000..fb41d64
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_6.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_9.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_9.dif
new file mode 100644
index 0000000..c4d58ed
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rock_9.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rocko_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rocko_1.dif
new file mode 100644
index 0000000..493fe08
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rocko_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_0.gif
new file mode 100644
index 0000000..da57d37
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_10.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_10.gif
new file mode 100644
index 0000000..896753a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_10.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_3.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_5.gif
new file mode 100644
index 0000000..cf379b7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_6.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_6.gif
new file mode 100644
index 0000000..ea7df7b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_6.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_8.gif
new file mode 100644
index 0000000..2ff4f29
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@rock@rockom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow.ini
new file mode 100644
index 0000000..da180b3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow.ini
@@ -0,0 +1,81 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xd0d090
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xd0d090,0xd0d090,0xa08040,0x803000,0xe0f0f0,0xb0e0f0,0x60c0d0,0x40a090,0x208050,0x306068
+
+tiles = 37
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# green flag - always animate
+frames_2 = 14
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
+
+# arrows left - always animate
+frames_3 = 7
+anim_3 = 1
+type_3 = 4
+sound_3 = -1
+
+# arrows right - always animate
+frames_4 = 7
+anim_4 = 1
+type_4 = 3
+sound_4 = -1
+
+# ice water - always animate
+frames_5 = 8
+anim_5 = 1
+type_5 = 5
+sound_5 = 9
+
+# red flag - always animate (animated part of exit)
+frames_6 = 5
+anim_6 = 1
+type_6 = 0
+sound_6 = -1
+
+# blue flag - always animate
+frames_7 = 14
+anim_7 = 1
+type_7 = 0
+sound_7 = -1
+
+# icicle trap - animate on trigger
+frames_8 = 14
+anim_8 = 2
+type_8 = 6
+sound_8 = 20
+
+# steam trap - always animate
+frames_9 = 11
+anim_9 = 1
+type_9 = 7
+sound_9 = 8
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_16.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_16.dif
new file mode 100644
index 0000000..f17e4e0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_16.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_20.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_20.dif
new file mode 100644
index 0000000..5adc81c
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_20.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_24.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_24.dif
new file mode 100644
index 0000000..26f7d92
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_24.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_34.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_34.dif
new file mode 100644
index 0000000..b5d308a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_34.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_35.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_35.dif
new file mode 100644
index 0000000..6f5989b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_35.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_36.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_36.dif
new file mode 100644
index 0000000..3e809da
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_36.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_7.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_7.dif
new file mode 100644
index 0000000..ec52af5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_7.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_8.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_8.dif
new file mode 100644
index 0000000..c0524d4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snow_8.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowo_1.dif
new file mode 100644
index 0000000..c9d7421
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_0.gif
new file mode 100644
index 0000000..56bc285
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_3.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_4.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_4.gif
new file mode 100644
index 0000000..73c6ddc
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_4.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_5.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_5.gif
new file mode 100644
index 0000000..d2a8cfd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_5.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_8.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_8.gif
new file mode 100644
index 0000000..d9b7f32
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_8.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_9.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_9.gif
new file mode 100644
index 0000000..dc441ca
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@snow@snowom_9.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special.ini b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special.ini
new file mode 100644
index 0000000..befe984
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special.ini
@@ -0,0 +1,39 @@
+# animation types
+# 0 - don't animate
+# 1 - animate continously
+# 2 - trap - animate on trigger - else show first pic
+# 3 - entry animation: animate once at level start
+
+# object types
+# 0 - passive
+# 3 - no digging to the left
+# 4 - no digging to the right
+# 5 - trap which makes lemmings drown (water/quick sand/mud)
+# 6 - trap which replaces lemming with death animation
+# 7 - trap which triggers lemming death animation
+# 8 - exit
+# 32 - entry
+
+bgColor = 0x000033
+debrisColor = 0xe08020
+particleColor = 0x4040e0,0x00b000,0xf0d0d0,0xf0f000,0xf02020,0x808080,0xe08020,0xe08020,0xc05010,0x902010,0x600010,0x404050,0x606070,0x709000,0x206020,0x787800
+
+tiles = 4
+
+# exit - non animated part
+frames_0 = 1
+anim_0 = 0
+type_0 = 8
+sound_0 = 14
+
+# entry - animate once
+frames_1 = 10
+anim_1 = 3
+type_1 = 32
+sound_1 = 5
+
+# animated part of exit - always animate
+frames_2 = 6
+anim_2 = 1
+type_2 = 0
+sound_2 = -1
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_0.gif
new file mode 100644
index 0000000..a3db574
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_1.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_1.gif
new file mode 100644
index 0000000..884ee18
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_1.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_2.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_2.gif
new file mode 100644
index 0000000..6573318
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_2.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_3.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_3.gif
new file mode 100644
index 0000000..a283821
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@special_3.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialo_1.dif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialo_1.dif
new file mode 100644
index 0000000..2185219
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialo_1.dif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialom_0.gif b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialom_0.gif
new file mode 100644
index 0000000..970b45f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/bin_copy/patch/styles@special@specialom_0.gif
Binary files differ
diff --git a/jeu-test/Lemmini/0.84/src/Extract/Diff.java b/jeu-test/Lemmini/0.84/src/Extract/Diff.java
new file mode 100644
index 0000000..8936128
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/Diff.java
@@ -0,0 +1,641 @@
+package Extract;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.Adler32;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple diff/patch algorithm for text or binary files
+ * In contrast to common line based diff utilities, this algorithm
+ * works byte based to create small binary difference files.
+ * However this very simple approach is only sensible for small files.
+ * It is by no way meant as rival to full featured approaches like XDelta.
+ *
+ * @author Volker Oth
+ */
+public class Diff {
+ /** insert n bytes */
+ private final static byte INSERT = 0;
+ /** delete n bytes */
+ private final static byte DELETE = 1;
+ /** replace n bytes with n bytes */
+ private final static byte REPLACE = 2;
+ /** substitute n bytes with m bytes */
+ private final static byte SUBSTITUTE = 3;
+
+ /** magic number for header ID */
+ private final static int HEADER_ID = 0xdeadbeef;
+ /** magic number for data ID */
+ private final static int DATA_ID = 0xfade0ff;
+
+ /** print info to System.out */
+ private static boolean verbatim = false;
+
+ /** re-synchronization length */
+ private static int resyncLength = 4;
+ /** re-synchronization window length */
+ private static int windowLength = 512;
+
+ /** target CRC */
+ public static int targetCRC = 0;
+
+ /**
+ * Set diff parameters
+ * @param winLen Length of windows to search for re-synchronization
+ * @param resyncLen Number of equal bytes needed for re-synchronization
+ */
+ public static void setParameters(final int winLen, final int resyncLen) {
+ resyncLength = resyncLen;
+ windowLength = winLen;
+ }
+
+ /**
+ * Create diff buffer from the differences between source and target buffer
+ * @param bsrc source buffer (the file to be patched)
+ * @param btrg target buffer (the file as it should be)
+ * @return buffer of differences
+ */
+ public static byte[] diffBuffers(final byte bsrc[], final byte btrg[]) {
+ ArrayList<Byte> patch = new ArrayList<Byte>();
+ Buffer src = new Buffer(bsrc);
+ Buffer trg = new Buffer(btrg);
+
+ // compare crcs
+ Adler32 crcSrc = new Adler32();
+ crcSrc.update(src.getData());
+ Adler32 crcTrg = new Adler32();
+ crcTrg.update(trg.getData());
+ targetCRC = (int)crcTrg.getValue();
+ if (crcTrg.getValue() == crcSrc.getValue())
+ return null;
+
+ // write header
+ setDWord(patch,HEADER_ID);
+ // write lengths to patch list
+ setLen(patch,src.length());
+ setLen(patch,trg.length());
+ // write crcs to patch list
+ setDWord(patch,(int)crcSrc.getValue());
+ setDWord(patch,(int)crcTrg.getValue());
+ setDWord(patch,DATA_ID);
+
+ // examine source buffer
+ int ofs = 0;
+ while (src.getIndex() < src.length()) {
+ // search for difference
+ int s = src.getByte();
+ int t = trg.getByte();
+ if (s == t) {
+ ofs++;
+ continue;
+ }
+ // reset indeces
+ src.setIndex(src.getIndex()-1);
+ trg.setIndex(trg.getIndex()-1);
+ // write offset
+ setLen(patch,ofs);
+ out("Offset: "+ofs);
+ ofs = 0;
+ // check for insert, delete, replace
+ int len, leni, lend, lenr, lens[];
+ int state = -1;
+
+ leni = checkInsert(src,trg);
+ lend = checkDelete(src,trg);
+ lenr = checkReplace(src,trg);
+ lens = checkSubstitute(src,trg);
+ len = Math.min(leni,lend);
+ len = Math.min(len,lenr);
+ len = Math.min(len,lens[1]);
+ if (len > windowLength) {
+ // completely lost synchronisation
+ int rs = src.length() - src.getIndex();
+ int rt = trg.length() - trg.getIndex();
+ if (rs==rt) {
+ len = rs;
+ state = REPLACE;
+ } else {
+ len = rt;
+ state = INSERT;
+ }
+ break;
+ }
+ if (len == leni)
+ state = INSERT;
+ else if (len == lend)
+ state = DELETE;
+ else if (len == lenr)
+ state = REPLACE;
+ else if (len == lens[1])
+ state = SUBSTITUTE;
+
+ switch (state) {
+ case INSERT :
+ // insert
+ out("Insert: "+len);
+ patch.add(new Byte(INSERT));
+ setLen(patch,len);
+ for (int i = 0; i<len; i++)
+ patch.add(new Byte((byte)trg.getByte()));
+ break;
+ case DELETE:
+ // delete
+ out("Delete: "+len);
+ patch.add(new Byte(DELETE));
+ setLen(patch,len);
+ src.setIndex(src.getIndex()+len);
+ break;
+ case REPLACE:
+ // replace
+ out("Replace: "+len);
+ patch.add(new Byte(REPLACE));
+ setLen(patch,len);
+ for (int i = 0; i<len; i++)
+ patch.add(new Byte((byte)trg.getByte()));
+ src.setIndex(src.getIndex()+len);
+ break;
+ case SUBSTITUTE:
+ // replace
+ out("Substitute: "+lens[0]+"/"+lens[1]);
+ patch.add(new Byte(SUBSTITUTE));
+ setLen(patch,lens[0]);
+ setLen(patch,lens[1]);
+ for (int i = 0; i<lens[1]; i++)
+ patch.add(new Byte((byte)trg.getByte()));
+ src.setIndex(src.getIndex()+lens[0]);
+ break;
+ }
+ }
+
+ // if the files end identically, the offset needs to be written
+ if (ofs != 0) {
+ out("Offset: "+ofs);
+ setLen(patch,ofs);
+ }
+
+ // check for stuff to insert in target
+ if (trg.getIndex() < trg.length()) {
+ patch.add(new Byte(INSERT));
+ int len = trg.length() - trg.getIndex();
+ out("Insert (End): "+len);
+ setLen(patch,len);
+ for (int i = 0; i<len; i++)
+ patch.add(new Byte((byte)trg.getByte()));
+ }
+
+ if (patch.size() == 0)
+ return null;
+
+ out("Patch length: "+patch.size());
+
+ // convert patch list to output byte array
+ byte retVal[] = new byte[patch.size()];
+ for (int i =0; i<retVal.length;i++)
+ retVal[i] = patch.get(i).byteValue();
+ return retVal;
+ }
+
+ /**
+ * Create a target buffer from a source buffer and a buffer of differences
+ * @param bsrc source buffer
+ * @param bpatch buffer containing differences
+ * @return target buffer created from a source buffer and a buffer of differences
+ * @throws DiffException
+ */
+ public static byte[] patchbuffers(final byte bsrc[], final byte bpatch[]) throws DiffException {
+ Buffer src = new Buffer(bsrc);
+ Buffer patch = new Buffer(bpatch);
+ // calculate src crc
+ Adler32 crc = new Adler32();
+ crc.update(src.getData());
+ // analyze header
+ if (patch.getDWord() != Diff.HEADER_ID)
+ throw new DiffException("No header id found in patch");
+ int lenSrc = getLen(patch);
+ if (lenSrc != src.length())
+ throw new DiffException("Size of source differs from that in patch header");
+ int lenTrg = getLen(patch);
+ int crcPatchSrc = patch.getDWord();
+ if (crcPatchSrc != (int)crc.getValue())
+ throw new DiffException("CRC of source (0x"+Integer.toHexString((int)crc.getValue())+
+ ") differs from that in patch header (0x"+Integer.toHexString(crcPatchSrc)+")");
+ int crcTrg = patch.getDWord();
+ if (patch.getDWord() != Diff.DATA_ID)
+ throw new DiffException("No data id found in patch header");
+
+ Buffer trg = new Buffer(lenTrg);
+
+ // step through patch buffer
+ try {
+ while (patch.getIndex()<patch.length()) {
+ int ofs = getLen(patch);
+ out("Offset: "+ofs);
+ // copy bytes from source buffer
+ for (int i=0; i<ofs; i++)
+ trg.setByte((byte)src.getByte());
+ // check for patch buffer empty
+ if (patch.getIndex()==patch.length())
+ break;
+ // now there must follow a command followed by a
+ int cmdIdx = patch.getIndex(); // just for exception
+ int cmd = patch.getByte();
+ int len = getLen(patch);
+ switch (cmd) {
+ case Diff.DELETE:
+ out("Delete: "+len);
+ src.setIndex(src.getIndex()+len);
+ break;
+ case Diff.REPLACE:
+ out("Replace/");
+ src.setIndex(src.getIndex()+len);
+ //$FALL-THROUGH$
+ case Diff.INSERT:
+ out("Insert: "+len);
+ for (int r=0; r<len;r++)
+ trg.setByte((byte)patch.getByte());
+ break;
+ case Diff.SUBSTITUTE: {
+ int lenT = getLen(patch);
+ out("Substitute: "+len+"/"+lenT);
+ src.setIndex(src.getIndex()+len);
+ for (int r=0; r<lenT;r++)
+ trg.setByte((byte)patch.getByte());
+ break; }
+ default:
+ throw new DiffException("Unknown command "+cmd+" at patch offset "+cmdIdx);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ throw new DiffException("Array index exceeds bounds. Patch file corrupt...");
+ }
+
+ // check length
+ if (trg.getIndex() != lenTrg)
+ throw new DiffException("Size of target differs from that in patch header");
+
+ // compare crc
+ crc.reset();
+ crc.update(trg.getData());
+ if (crcTrg != (int)crc.getValue())
+ throw new DiffException("CRC of target differs from that in patch");
+
+ return trg.getData();
+ }
+
+ /**
+ * Lengths/Offset are stored as 7bit values. The 8th bit is used as marker if the number
+ * is continued in the next byte.
+ * @param b Buffer from which to read the length/offset
+ * @return integer value of length/offset
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ private static int getLen(final Buffer b) throws ArrayIndexOutOfBoundsException {
+ int val = 0;
+ int v;
+ int shift = 0;
+ do {
+ v = b.getByte();
+ if ((v & 0x80) == 0) {
+ // no continue bit set
+ val += (v<<shift);
+ break;
+ }
+ // erase contine marker bit
+ v &= 0x7f;
+ val += (v<<shift);
+ shift += 7;
+ } while (true);
+ return val;
+ }
+
+ /**
+ * Store length/offset information in 7bit encoding. A set 8th bit means: continued in next byte
+ * So 127 is stored as 0x7f, but 128 is stored as 0x80 0x01 (where 0x80 means 0, highest bit is marker)
+ * @param l Patch list to add length/offset in 7bit encoding
+ * @param value Value to add in 7bit encoding
+ */
+ private static void setLen(final List<Byte> l, final int value) {
+ int val = value;
+ while ( val > 0x7f) {
+ l.add(new Byte((byte)(val & 0x7f | 0x80)));
+ val >>>= 7;
+ }
+ l.add(new Byte((byte)val));
+ }
+
+ /**
+ * Check for "insert" difference
+ * @param src source buffer
+ * @param trg target buffer
+ * @return number of bytes inserted
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ private static int checkInsert(final Buffer src, final Buffer trg) throws ArrayIndexOutOfBoundsException {
+ byte[] bs = src.getData();
+ int is = src.getIndex();
+ byte[] bt = trg.getData();
+ int it = trg.getIndex();
+ int len = windowLength;
+ if (is+len+resyncLength >= bs.length)
+ len = bs.length - is - resyncLength;
+ if (it+len+resyncLength >= bt.length)
+ len = bt.length - it - resyncLength;
+ for (int w=1; w<len; w++) {
+ int r;
+ for (r = 0; r<resyncLength; r++)
+ if (bs[is+r] != bt[it+w+r])
+ break;
+ if (r == resyncLength)
+ return w;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Check for "delete" difference
+ * @param src source buffer
+ * @param trg target buffer
+ * @return number of bytes deleted
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ private static int checkDelete(final Buffer src, final Buffer trg) throws ArrayIndexOutOfBoundsException {
+ byte[] bs = src.getData();
+ int is = src.getIndex();
+ byte[] bt = trg.getData();
+ int it = trg.getIndex();
+ int len = windowLength;
+ if (is+len+resyncLength >= bs.length)
+ len = bs.length - is - resyncLength;
+ if (it+len+resyncLength >= bt.length)
+ len = bt.length - it - resyncLength;
+ for (int w=1; w<len; w++) {
+ int r;
+ for (r = 0; r<resyncLength; r++)
+ if (bs[is+w+r] != bt[it+r])
+ break;
+ if (r == resyncLength)
+ return w;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Check for "replace" difference
+ * @param src source buffer
+ * @param trg target buffer
+ * @return number of bytes replaced
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ private static int checkReplace(final Buffer src, final Buffer trg) throws ArrayIndexOutOfBoundsException {
+ byte[] bs = src.getData();
+ int is = src.getIndex();
+ byte[] bt = trg.getData();
+ int it = trg.getIndex();
+ int len = windowLength;
+ if (is+len+resyncLength >= bs.length)
+ len = bs.length - is - resyncLength;
+ if (it+len+resyncLength >= bt.length)
+ len = bt.length - it - resyncLength;
+ for (int w=1; w<len; w++) {
+ int r;
+ for (r = 0; r<resyncLength; r++)
+ if (bs[is+w+r] != bt[it+w+r])
+ break;
+ if (r == resyncLength)
+ return w;
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Check for "substitute" difference
+ * @param src source buffer
+ * @param trg target buffer
+ * @return integer array: [0]: number of bytes to delete in source, [1]: number of bytes to insert in target
+ * @throws ArrayIndexOutOfBoundsException
+
+ */
+ private static int[] checkSubstitute(final Buffer src, final Buffer trg) throws ArrayIndexOutOfBoundsException {
+ byte[] bs = src.getData();
+ int is = src.getIndex();
+ byte[] bt = trg.getData();
+ int it = trg.getIndex();
+ int len = windowLength;
+ if (is+len+resyncLength >= bs.length)
+ len = bs.length - is - resyncLength;
+ if (it+len+resyncLength >= bt.length)
+ len = bt.length - it - resyncLength;
+
+ ArrayList<int[]> solutions = new ArrayList<int[]>();
+
+ for (int ws=1; ws<len; ws++) {
+ for (int wt=1; wt<len; wt++) {
+ int r;
+ for (r = 0; r<resyncLength; r++)
+ if (bs[is+ws+r] != bt[it+wt+r])
+ break;
+ if (r == resyncLength) {
+ int retVal[] = new int[2];
+ retVal[0] = ws;
+ retVal[1] = wt;
+ solutions.add(retVal);
+ }
+ }
+ }
+
+ if (solutions.size() == 0) {
+ // nothing found
+ int retVal[] = new int[2];
+ retVal[0] = Integer.MAX_VALUE;
+ retVal[1] = Integer.MAX_VALUE;
+ return retVal;
+ }
+
+ // search best solution
+ int sMinIdx = 0;
+ for (int i=1; i<solutions.size(); i++) {
+ int s[] = solutions.get(i);
+ int sMin[] = solutions.get(sMinIdx);
+ if (s[0]+s[1] < sMin[0]+sMin[1])
+ sMinIdx = i;
+ }
+ return solutions.get(sMinIdx);
+ }
+
+ /**
+ * Write DWord to difference list
+ * @param l difference list
+ * @param val DWord value
+ */
+ private static void setDWord(final List<Byte> l, final int val) {
+ l.add(new Byte((byte)val));
+ l.add(new Byte((byte)(val>>8)));
+ l.add(new Byte((byte)(val>>16)));
+ l.add(new Byte((byte)(val>>24)));
+ }
+
+ private static void out(final String s) {
+ if (verbatim)
+ System.out.println(s);
+ }
+}
+
+
+/**
+ * Buffer class that manages reading/writing from/to a byte buffer
+ *
+ * @author Volker Oth
+ */
+class Buffer {
+ /** array of byte which defines the data buffer */
+ private byte buffer[];
+ /** byte index in buffer */
+ private int index;
+
+ /**
+ * Constructor.
+ * @param size buffer size in bytes
+ */
+ Buffer(final int size) {
+ index = 0;
+ buffer = new byte[size];
+ }
+
+ /**
+ * Constructor.
+ * @param b array of byte to use as buffer
+ */
+ Buffer(final byte b[]) {
+ index = 0;
+ buffer = b;
+ }
+
+ /**
+ * Get size of buffer.
+ * @return size of buffer in bytes
+ */
+ int length() {
+ return buffer.length;
+ }
+
+ /**
+ * Get current byte index.
+ * @return current byte index
+ */
+ int getIndex() {
+ return index;
+ }
+
+ /**
+ * Get data buffer.
+ * @return data buffer
+ */
+ byte[] getData() {
+ return buffer;
+ }
+
+ /**
+ * Set index to new byte position.
+ * @param idx index to new byte position
+ */
+ void setIndex(final int idx) {
+ index = idx;
+ }
+
+ /**
+ * Get byte at current position.
+ * @return byte at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ int getByte() throws ArrayIndexOutOfBoundsException {
+ return buffer[index++] & 0xff;
+ }
+
+ /**
+ * Set byte at current position, increase index by 1.
+ * @param val byte value to write
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ void setByte(final byte val) throws ArrayIndexOutOfBoundsException {
+ buffer[index++] = val;
+ }
+
+ /**
+ * Get word (2 bytes, little endian) at current position.
+ * @return word at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ int getWord() throws ArrayIndexOutOfBoundsException {
+ return getByte() | (getByte()<<8);
+ }
+
+ /**
+ * Set word (2 bytes, little endian) at current position, increase index by 2.
+ * @param val word to write at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ void setWord(final int val) throws ArrayIndexOutOfBoundsException {
+ setByte((byte)val);
+ setByte((byte)(val>>8));
+ }
+
+ /**
+ * Get double word (4 bytes, little endian) at current position.
+ * @return dword at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ int getDWord() throws ArrayIndexOutOfBoundsException {
+ return getByte() | (getByte()<<8) | (getByte()<<16) | (getByte()<<24);
+ }
+
+ /**
+ * Set double word (4 bytes, little endian) at current position, increase index by 4.
+ * @param val dword to write at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ void setDWord(final int val) throws ArrayIndexOutOfBoundsException {
+ setByte((byte)val);
+ setByte((byte)(val>>8));
+ setByte((byte)(val>>16));
+ setByte((byte)(val>>24));
+ }
+}
+
+/**
+ * Generic Exception for Diff.
+ * @author Volker Oth
+ */
+class DiffException extends Exception {
+ private final static long serialVersionUID = 0x000000001;
+
+ /**
+ * Constructor.
+ */
+ public DiffException() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ * @param s Exception string
+ */
+ public DiffException(String s) {
+ super(s);
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Extract/Extract.java b/jeu-test/Lemmini/0.84/src/Extract/Extract.java
new file mode 100644
index 0000000..a99a584
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/Extract.java
@@ -0,0 +1,847 @@
+package Extract;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.zip.Adler32;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import Tools.Props;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Extraction of resources.
+ *
+ * @author Volker Oth
+ */
+public class Extract extends Thread {
+
+ /** file name of extraction configuration */
+ final private static String iniName = "extract.ini";
+ /** file name of patching configuration */
+ final private static String patchIniName = "patch.ini";
+ /** file name of resource CRCs (WINLEMM) */
+ final private static String crcIniName = "crc.ini";
+ /** allows to use this module for creation of the CRC.ini */
+ final private static boolean doCreateCRC = false;
+
+ /** index for files to be checked - static since multiple runs are possible */
+ private static int checkNo = 0;
+ /** index for CRCs - static since multiple runs are possible */
+ private static int crcNo = 0;
+ /** index for files to be extracted - static since multiple runs are possible */
+ private static int extractNo = 0;
+ /** index for files to be patched - static since multiple runs are possible */
+ private static int patchNo = 0;
+ /** array of extensions to be ignored - read from ini */
+ private static String ignoreExt[] = {null};
+ /** output dialog */
+ private static OutputDialog outputDiag;
+ /** monitor the files created without erasing the target dir */
+ private static HashMap<String,Object> createdFiles;
+ /** source path (WINLEMM) for extraction */
+ private static String sourcePath;
+ /** destination path (Lemmini resource) for extraction */
+ private static String destinationPath;
+ /** reference path for creation of DIF files */
+ private static String referencePath;
+ /** path of the DIF files */
+ private static String patchPath;
+ /** path of the CRC ini (without the file name) */
+ private static String crcPath;
+ /** exception caught in the thread */
+ private static ExtractException threadException = null;
+ /** static self reference to access thread from outside */
+ private static Thread thisThread;
+ /** reference to class loader */
+ private static ClassLoader loader;
+
+ /**
+ * Display an exception message box.
+ * @param ex Exception
+ */
+ private static void showException(final Throwable ex) {
+ String m;
+ m = "<html>";
+ m += ex.getClass().getName()+"<p>";
+ if (ex.getMessage() != null)
+ m += ex.getMessage() +"<p>";
+ StackTraceElement ste[] = ex.getStackTrace();
+ for (int i=0; i<ste.length; i++) {
+ m += ste[i].toString()+"<p>";
+ }
+ m += "</html>";
+ ex.printStackTrace();
+ JOptionPane.showMessageDialog( null, m, "Error", JOptionPane.ERROR_MESSAGE );
+ ex.printStackTrace();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Thread#run()
+ *
+ * Extraction running in a Thread.
+ */
+ @Override
+ public void run() {
+ createdFiles = new HashMap<String,Object>(); // to monitor the files created without erasing the target dir
+
+ try {
+ // read ini file
+ Props props = new Props();
+ URL fn = findFile(iniName);
+ if (fn==null || !props.load(fn))
+ throw new ExtractException("File " + iniName + " not found or error while reading");
+
+ ignoreExt = props.get("ignore_ext", ignoreExt);
+
+ // prolog_ check CRC
+ out("\nValidating WINLEMM");
+ URL fncrc = findFile(crcIniName);
+ Props cprops = new Props();
+ if (fncrc==null || !cprops.load(fncrc))
+ throw new ExtractException("File " + crcIniName + " not found or error while reading");
+ for (int i=0; true; i++) {
+ String crcbuf[] = {null,null,null};
+ // 0: name, 1:size, 2: crc
+ crcbuf = cprops.get("crc_"+Integer.toString(i),crcbuf);
+ if (crcbuf[0] == null)
+ break;
+ out(crcbuf[0]);
+ long len = new File(sourcePath+crcbuf[0]).length();
+ if (len != Long.parseLong(crcbuf[1]))
+ throw new ExtractException("CRC error for file "+sourcePath+crcbuf[0]+".\n");
+ byte src[] = readFile(sourcePath+crcbuf[0]);
+ Adler32 crc32 = new Adler32();
+ crc32.update(src);
+ if (Long.toHexString(crc32.getValue()).compareToIgnoreCase(crcbuf[2].substring(2))!=0)
+ throw new ExtractException("CRC error for file "+sourcePath+crcbuf[0]+".\n");
+ checkCancel();
+ }
+
+ // step one: extract the levels
+ out("\nExtracting levels");
+ for (int i=0; true; i++) {
+ String lvls[] = {null,null};
+ // 0: srcPath, 1: destPath
+ lvls = props.get("level_"+Integer.toString(i),lvls);
+ if (lvls[0] == null)
+ break;
+ extractLevels(sourcePath+lvls[0], destinationPath+lvls[1]);
+ checkCancel();
+ }
+
+ // step two: extract the styles
+ out("\nExtracting styles");
+ ExtractSPR sprite = new ExtractSPR();
+ for (int i=0; true; i++) {
+ String styles[] = {null,null,null,null};
+ // 0:SPR, 1:PAL, 2:path, 3:fname
+ styles = props.get("style_"+Integer.toString(i),styles);
+ if (styles[0] == null)
+ break;
+ out(styles[3]);
+ File dest = new File(destinationPath+styles[2]);
+ dest.mkdirs();
+ // load palette and sprite
+ sprite.loadPalette(sourcePath+styles[1]);
+ sprite.loadSPR(sourcePath+styles[0]);
+ String files[] = sprite.saveAll(destinationPath+addSeparator(styles[2])+styles[3], false);
+ for (int j=0; j<files.length; j++)
+ createdFiles.put(files[j].toLowerCase(),null);
+ checkCancel();
+ }
+
+ // step three: extract the objects
+ out("\nExtracting objects");
+ for (int i=0; true; i++) {
+ String object[] = {null,null,null,null};
+ // 0:SPR, 1:PAL, 2:resource, 3:path
+ object = props.get("objects_"+Integer.toString(i),object);
+ if (object[0] == null)
+ break;
+ out(object[0]);
+ File dest = new File(destinationPath+object[3]);
+ dest.mkdirs();
+ // load palette and sprite
+ sprite.loadPalette(sourcePath+object[1]);
+ sprite.loadSPR(sourcePath+object[0]);
+ for (int j=0; true; j++) {
+ String member[] = {null,null,null};
+ // 0:idx, 1:frames, 2:name
+ member = props.get(object[2]+"_"+Integer.toString(j),member);
+ if (member[0] == null)
+ break;
+ // save object
+ createdFiles.put((destinationPath+addSeparator(object[3])+member[2]).toLowerCase(),null);
+ sprite.saveAnim(destinationPath+addSeparator(object[3])+member[2],
+ Integer.parseInt(member[0]),Integer.parseInt(member[1]) );
+ checkCancel();
+ }
+ }
+
+ //if (false) { // debug only
+
+ // step four: create directories
+ out("\nCreate directories");
+ for (int i=0; true; i++) {
+ // 0: path
+ String path = props.get("mkdir_"+Integer.toString(i),"");
+ if (path.length() == 0)
+ break;
+ out(path);
+ File dest = new File(destinationPath+path);
+ dest.mkdirs();
+ checkCancel();
+ }
+
+ // step five: copy stuff
+ out("\nCopy files");
+ for (int i=0; true; i++) {
+ String copy[] = {null,null};
+ // 0: srcName, 1: destName
+ copy = props.get("copy_"+Integer.toString(i),copy);
+ if (copy[0] == null)
+ break;
+ try {
+ copyFile(sourcePath+copy[0], destinationPath+copy[1]);
+ createdFiles.put((destinationPath+copy[1]).toLowerCase(),null);
+ } catch (Exception ex) {
+ throw new ExtractException("Copying "+sourcePath+copy[0]+" to "+destinationPath+copy[1]+ " failed");
+ }
+ checkCancel();
+ }
+
+ // step five: clone files inside destination dir
+ out("\nClone files");
+ for (int i=0; true; i++) {
+ String clone[] = {null,null};
+ // 0: srcName, 1: destName
+ clone = props.get("clone_"+Integer.toString(i),clone);
+ if (clone[0] == null)
+ break;
+ try {
+ copyFile(destinationPath+clone[0], destinationPath+clone[1]);
+ createdFiles.put((destinationPath+clone[1]).toLowerCase(),null);
+ } catch (Exception ex) {
+ throw new ExtractException("Cloning "+destinationPath+clone[0]+" to "+destinationPath+clone[1]+ " failed");
+ }
+ checkCancel();
+ }
+
+ if (referencePath != null) {
+ // this is not needed by Lemmini, but to create the DIF files (and CRCs)
+ if (doCreateCRC) {
+ // create crc.ini
+ out("\nCreate CRC ini");
+ FileWriter fCRCList;
+ try {
+ fCRCList = new FileWriter(crcPath+crcIniName);
+ } catch (IOException ex) {
+ throw new ExtractException(crcPath+crcIniName+" coudn't be openend");
+ }
+ for (int i=0; true; i++) {
+ String ppath;
+ ppath = props.get("pcrc_"+Integer.toString(i),"");
+ if (ppath.length() == 0)
+ break;
+ createCRCs(sourcePath, ppath, fCRCList);
+ }
+ try {
+ fCRCList.close();
+ } catch (IOException ex) {
+ throw new ExtractException(crcPath+crcIniName+" coudn't be closed");
+ }
+ checkCancel();
+ }
+
+ // step seven: create patches and patch.ini
+ (new File(patchPath)).mkdirs();
+ out("\nCreate patch ini");
+ FileWriter fPatchList;
+ try {
+ fPatchList = new FileWriter(patchPath+patchIniName);
+ } catch (IOException ex) {
+ throw new ExtractException(patchPath+patchIniName+" coudn't be openend");
+ }
+ for (int i=0; true; i++) {
+ String ppath;
+ ppath = props.get("ppatch_"+Integer.toString(i),"");
+ if (ppath.length() == 0)
+ break;
+ createPatches(referencePath, destinationPath, ppath, patchPath, fPatchList);
+ }
+ try {
+ fPatchList.close();
+ } catch (IOException ex) {
+ throw new ExtractException(patchPath+patchIniName+" coudn't be closed");
+ }
+ checkCancel();
+ }
+
+ // step eight: use patch.ini to extract/patch all files
+ // read patch.ini file
+ Props pprops = new Props();
+ URL fnp = findFile(patchPath+patchIniName/*, this*/); // if it's in the JAR or local directory
+ if (!pprops.load(fnp))
+ throw new ExtractException("File " + patchIniName + " not found or error while reading");
+ // copy
+ out("\nExtract files");
+ for (int i=0; true; i++) {
+ String copy[] = {null,null};
+ // 0: name 1: crc
+ copy = pprops.get("extract_"+Integer.toString(i),copy);
+ if (copy[0] == null)
+ break;
+ out(copy[0]);
+ String fnDecorated = copy[0].replace('/', '@');
+ URL fnc = findFile(patchPath+fnDecorated /*, pprops*/);
+ try {
+ copyFile(fnc, destinationPath+copy[0]);
+ } catch (Exception ex) {
+ throw new ExtractException("Copying "+patchPath+getFileName(copy[0])+" to "+destinationPath+copy[0]+ " failed");
+ }
+ checkCancel();
+ }
+ // patch
+ out("\nPatch files");
+ for (int i=0; true; i++) {
+ String ppath[] = {null,null};
+ // 0: name 1: crc
+ ppath = pprops.get("patch_"+Integer.toString(i),ppath);
+ if (ppath[0] == null)
+ break;
+ out(ppath[0]);
+ String fnDif = ppath[0].replace('/', '@'); //getFileName(ppath[0]);
+ int pos = fnDif.toLowerCase().lastIndexOf('.');
+ if (pos == -1)
+ pos = fnDif.length();
+ fnDif = fnDif.substring(0,pos)+".dif";
+ URL urlDif = findFile(patchPath+fnDif);
+ if (urlDif == null)
+ throw new ExtractException("Patching of file "+destinationPath+ppath[0]+" failed.\n");
+ byte dif[] = readFile(urlDif);
+ byte src[] = readFile(destinationPath+ppath[0]);
+ try {
+ byte trg[] = Diff.patchbuffers(src, dif);
+ // write new file
+ writeFile(destinationPath+ppath[0],trg);
+ } catch (DiffException ex) {
+ throw new ExtractException("Patching of file "+destinationPath+ppath[0]+" failed.\n"+
+ ex.getMessage());
+ }
+ checkCancel();
+ }
+ //} // debug only
+
+ // finished
+ out("\nSuccessfully finished!");
+ } catch (ExtractException ex) {
+ threadException = ex;
+ out(ex.getMessage());
+ } catch (Exception ex) {
+ showException(ex);
+ System.exit(1);
+ } catch (Error ex) {
+ showException(ex);
+ System.exit(1);
+ }
+ outputDiag.enableOk();
+ }
+
+ /**
+ * Get source path (WINLEMM) for extraction.
+ * @return source path (WINLEMM) for extraction
+ */
+ public static String getSourcePath() {
+ return sourcePath;
+ }
+
+ /**
+ * Get destination path (Lemmini resource) for extraction.
+ * @return destination path (Lemmini resource) for extraction
+ */
+ public static String getResourcePath() {
+ return destinationPath;
+ }
+
+ /**
+ * Extract all resources and create patch.ini if referencePath is not null
+ * @param frame parent frame
+ * @param srcPath WINLEMM directory
+ * @param dstPath target (installation) directory. May also be a relative path inside JAR
+ * @param refPath the reference path with the original (wanted) files
+ * @param pPath the path to store the patch files to
+ * @throws ExtractException
+ */
+ public static void extract(final JFrame frame, final String srcPath, final String dstPath, final String refPath, final String pPath) throws ExtractException {
+
+ sourcePath = exchangeSeparators(addSeparator(srcPath));
+ destinationPath = exchangeSeparators(addSeparator(dstPath));
+ if (refPath != null)
+ referencePath = exchangeSeparators(addSeparator(refPath));
+ patchPath = exchangeSeparators(addSeparator(pPath));
+ crcPath = destinationPath; // ok, this is the wrong path, but this is executed once in a lifetime
+
+ loader = Extract.class.getClassLoader();
+
+ FolderDialog fDiag;
+ do {
+ fDiag = new FolderDialog(frame, true);
+ fDiag.setParameters(sourcePath, destinationPath);
+ fDiag.setVisible(true);
+ if (!fDiag.getSuccess())
+ throw new ExtractException("Extraction cancelled by user");
+ sourcePath = exchangeSeparators(addSeparator(fDiag.getSource()));
+ destinationPath = exchangeSeparators(addSeparator(fDiag.getTarget()));
+ // check if source path exists
+ File fSrc = new File(sourcePath);
+ if (fSrc.exists())
+ break;
+ JOptionPane.showMessageDialog(frame,"Source path "+sourcePath+" doesn't exist!","Error",JOptionPane.ERROR_MESSAGE);
+ } while (true);
+
+ // open output dialog
+ outputDiag = new OutputDialog(frame, true);
+
+ // start thread
+ threadException = null;
+ thisThread = new Thread(new Extract());
+ thisThread.start();
+
+ outputDiag.setVisible(true);
+ while (thisThread.isAlive()) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ex) {}
+ }
+ if (threadException != null)
+ throw threadException;
+ }
+
+ /**
+ * Extract the level INI files from LVL files
+ * @param r name of root folder (source of LVL files)
+ * @param dest destination folder for extraction (resource folder)
+ * @throws ExtractException
+ */
+ private static void extractLevels(final String r, final String destin) throws ExtractException {
+ // first extract the levels
+ File fRoot = new File(r);
+ FilenameFilter ff = new LvlFilter();
+
+ String root = addSeparator(r);
+ String destination = addSeparator(destin);
+ File dest = new File(destination);
+ dest.mkdirs();
+
+ File[] levels = fRoot.listFiles(ff);
+ if (levels == null)
+ throw new ExtractException("Path "+root+" doesn't exist or IO error occured.");
+ for (int i = 0; i< levels.length; i++) {
+ int pos;
+ String fIn = root + levels[i].getName();
+ String fOut = levels[i].getName();
+ pos = fOut.toLowerCase().indexOf(".lvl"); // MUST be there because of file filter
+ fOut = destination + (fOut.substring(0,pos)+".ini").toLowerCase();
+ createdFiles.put(fOut.toLowerCase(),null);
+ try {
+ out(levels[i].getName());
+ ExtractLevel.convertLevel(fIn, fOut);
+ } catch (Exception ex) {
+ String msg = ex.getMessage();
+ if (msg!=null && msg.length()>0)
+ out(ex.getMessage());
+ else
+ out(ex.toString());
+ throw new ExtractException(msg);
+ }
+ }
+ }
+
+ /**
+ * Create the DIF files from reference files and the extracted files (development).
+ * @param sPath The path with the original (wanted) files
+ * @param dPath The patch with the differing (to be patched) files
+ * @param subDir SubDir to create patches for
+ * @param pPath The patch to write the patches to
+ * @param fPatchList FileWriter to create patch.ini
+ * @throws ExtractException
+ */
+ private static void createPatches(final String sPath, final String dPath, final String sDir, final String pPath, final FileWriter fPatchList) throws ExtractException {
+ // add separators and create missing directories
+ sourcePath = addSeparator(sPath+sDir);
+ File fSource = new File(sourcePath);
+
+ String destPath = addSeparator(dPath+sDir);
+ File fDest = new File(destPath);
+ fDest.mkdirs();
+
+ String out;
+ patchPath = addSeparator(pPath);
+ File fPatch = new File(patchPath);
+ fPatch.mkdirs();
+
+ File[] files = fSource.listFiles();
+ if (files == null)
+ throw new ExtractException("Path "+sourcePath+" doesn't exist or IO error occured.");
+ Diff.setParameters(512,4);
+ String subDir = addSeparator(sDir);
+ String subDirDecorated = subDir.replace('/', '@');
+
+ outerLoop:
+ for (int i = 0; i< files.length; i++) {
+ int pos;
+ // ignore directories
+ if (files[i].isDirectory())
+ continue;
+ // check extension
+ pos = files[i].getName().lastIndexOf('.');
+ if (pos > -1) {
+ String ext = files[i].getName().substring(pos+1);
+ for (int n=0; n<ignoreExt.length; n++)
+ if (ignoreExt[n].equalsIgnoreCase(ext))
+ continue outerLoop;
+ }
+
+ String fnIn = sourcePath + files[i].getName();
+ String fnOut = destPath + files[i].getName();
+ String fnPatch = files[i].getName();
+
+ pos = fnPatch.toLowerCase().lastIndexOf('.');
+ if (pos == -1)
+ pos = fnPatch.length();
+ fnPatch = patchPath + subDirDecorated + (fnPatch.substring(0,pos)+".dif").toLowerCase();
+ try {
+ out(fnIn);
+ // read src file
+ byte src[] = readFile(fnIn);
+ byte trg[] = null;
+ // read target file
+ boolean fileExists;
+ if (createdFiles.containsKey(fnOut.toLowerCase()))
+ fileExists = true;
+ else
+ fileExists = false;
+ if (fileExists) {
+ try {
+ trg = readFile(fnOut);
+ } catch (ExtractException ex) {
+ fileExists = false;
+ }
+ }
+ if (!fileExists) {
+ // mark missing files: needs to be extracted from JAR
+ Adler32 crc = new Adler32();
+ crc.update(src);
+ out = subDir+files[i].getName()+", 0x"+Integer.toHexString((int)crc.getValue());
+ fPatchList.write("extract_"+(Integer.toString(extractNo++))+" = "+out+"\n");
+ // copy missing files to patch dir
+ copyFile(fnIn, patchPath + subDirDecorated + files[i].getName());
+ continue;
+ }
+ // create diff
+ byte patch[] = Diff.diffBuffers(trg,src);
+ int crc = Diff.targetCRC; // crc of target buffer
+ out = subDir+files[i].getName()+", 0x"+Integer.toHexString(crc);
+ if (patch == null) {
+ //out("src and trg are identical");
+ fPatchList.write("check_"+(Integer.toString(checkNo++))+" = "+out+"\n");
+ }
+ else {
+ // apply patch to test it's ok
+ Diff.patchbuffers(trg,patch);
+ // write patch file
+ writeFile( fnPatch, patch);
+ fPatchList.write("patch_"+(Integer.toString(patchNo++))+" = "+out+"\n");
+ }
+ } catch (Exception ex) {
+ String msg = ex.getMessage();
+ if (msg == null)
+ msg = ex.toString();
+ throw new ExtractException(ex.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Create CRCs for resources (development).
+ * @param rPath The root path with the files to create CRCs for
+ * @param sDir SubDir to create patches for
+ * @param fCRCList FileWriter to create crc.ini
+ * @throws ExtractException
+ */
+ private static void createCRCs(final String rPath, final String sDir, final FileWriter fCRCList) throws ExtractException {
+ // add separators and create missing directories
+ String rootPath = addSeparator(rPath+sDir);
+ File fSource = new File(rootPath);
+ String out;
+ File[] files = fSource.listFiles();
+ if (files == null)
+ throw new ExtractException("Path "+rootPath+" doesn't exist or IO error occured.");
+ String subDir = addSeparator(sDir);
+
+ outerLoop:
+ for (int i = 0; i< files.length; i++) {
+ int pos;
+ // ignore directories
+ if (files[i].isDirectory())
+ continue;
+ // check extension
+ pos = files[i].getName().lastIndexOf('.');
+ if (pos > -1) {
+ String ext = files[i].getName().substring(pos+1);
+ for (int n=0; n<ignoreExt.length; n++)
+ if (ignoreExt[n].equalsIgnoreCase(ext))
+ continue outerLoop;
+ }
+ String fnIn = rootPath + files[i].getName();
+ try {
+ out(fnIn);
+ // read src file
+ byte src[] = readFile(fnIn);
+ Adler32 crc32 = new Adler32();
+ crc32.update(src);
+ out = subDir+files[i].getName()+", "+src.length+", 0x"+Long.toHexString(crc32.getValue());
+ fCRCList.write("crc_"+(Integer.toString(crcNo++))+" = "+out+"\n");
+ } catch (Exception ex) {
+ String msg = ex.getMessage();
+ if (msg == null)
+ msg = ex.toString();
+ throw new ExtractException(ex.getMessage());
+ }
+ }
+ }
+
+
+ /**
+ * Add separator "/" to path name (if there isn't one yet)
+ * @param fName path name with or without separator
+ * @return path name with separator
+ */
+ private static String addSeparator(final String fName) {
+ int pos = fName.lastIndexOf(File.separator);
+ if (pos != fName.length()-1)
+ pos = fName.lastIndexOf("/");
+ if (pos != fName.length()-1)
+ return fName + "/";
+ else return fName;
+ }
+
+ /**
+ * Exchange all Windows style file separators ("\") with Unix style seaparators ("/")
+ * @param fName file name
+ * @return file name with only Unix style separators
+ */
+ private static String exchangeSeparators(final String fName) {
+ int pos;
+ StringBuffer sb = new StringBuffer(fName);
+ while ( (pos = sb.indexOf("\\")) != -1 )
+ sb.setCharAt(pos,'/');
+ return sb.toString();
+ }
+
+ /**
+ * Get only the name of the file from an absolute path.
+ * @param path absolute path of a file
+ * @return file name without the path
+ */
+ private static String getFileName(final String path) {
+ int p1 = path.lastIndexOf("/");
+ int p2 = path.lastIndexOf("\\");
+ if (p2 > p1)
+ p1 = p2;
+ if (p1 < 0)
+ p1 = 0;
+ else
+ p1++;
+ return path.substring(p1);
+ }
+
+ /**
+ * Copy a file.
+ * @param source URL of source file
+ * @param destination full destination file name including path
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private static void copyFile(final URL source, final String destination) throws FileNotFoundException, IOException {
+ InputStream fSrc = source.openStream();
+ FileOutputStream fDest = new FileOutputStream(destination);
+ byte buffer[] = new byte[4096];
+ int len;
+
+ while ( (len=fSrc.read(buffer)) != -1)
+ fDest.write(buffer,0,len);
+ fSrc.close();
+ fDest.close();
+ }
+
+ /**
+ * Copy a file.
+ * @param source full source file name including path
+ * @param destination full destination file name including path
+ * @throws FileNotFoundException
+ * @throws IOException
+ */
+ private static void copyFile(final String source, final String destination) throws FileNotFoundException, IOException {
+ FileInputStream fSrc = new FileInputStream(source);
+ FileOutputStream fDest = new FileOutputStream(destination);
+ byte buffer[] = new byte[4096];
+ int len;
+
+ while ( (len=fSrc.read(buffer)) != -1)
+ fDest.write(buffer,0,len);
+ fSrc.close();
+ fDest.close();
+ }
+
+ /**
+ * Read file into an array of byte.
+ * @param fname file name
+ * @return array of byte
+ * @throws ExtractException
+ */
+ private static byte[] readFile(final String fname) throws ExtractException {
+ byte buf[] = null;
+ try {
+ int len = (int)(new File(fname).length());
+ FileInputStream f = new FileInputStream(fname);
+ buf = new byte[ len ];
+ f.read( buf );
+ f.close();
+ return buf;
+ } catch (FileNotFoundException ex) {
+ throw new ExtractException("File "+fname+" not found");
+ } catch (IOException ex) {
+ throw new ExtractException("IO exception while reading file "+fname);
+ }
+ }
+
+ /**
+ * Read file into an array of byte.
+ * @param fname file name as URL
+ * @return array of byte
+ * @throws ExtractException
+ */
+ private static byte[] readFile(final URL fname) throws ExtractException {
+ byte buf[] = null;
+ try {
+ InputStream f = fname.openStream();
+ byte buffer[] = new byte[4096];
+ // URLs/InputStreams suck: we can't read a length
+ int len;
+ ArrayList<Byte> lbuf = new ArrayList<Byte>();
+
+ while ( (len=f.read(buffer)) != -1) {
+ for (int i=0; i<len; i++)
+ lbuf.add(new Byte(buffer[i]));
+ }
+ f.close();
+
+ // reconstruct byte array from ArrayList
+ buf = new byte[lbuf.size()];
+ for (int i=0; i<lbuf.size(); i++)
+ buf[i] = lbuf.get(i).byteValue();
+
+ return buf;
+ } catch (FileNotFoundException ex) {
+ throw new ExtractException("File "+fname+" not found");
+ } catch (IOException ex) {
+ throw new ExtractException("IO exception while reading file "+fname);
+ }
+ }
+
+ /**
+ * Write array of byte to file.
+ * @param fname file name
+ * @param buf array of byte
+ * @throws ExtractException
+ */
+ private static void writeFile(final String fname, final byte buf[]) throws ExtractException {
+ try {
+ FileOutputStream f = new FileOutputStream(fname);
+ f.write( buf );
+ f.close();
+ } catch (IOException ex) {
+ throw new ExtractException("IO exception while writing file "+fname);
+ }
+ }
+
+ /**
+ * Find a file.
+ * @param fname File name (without absolute path)
+ * @return URL to file
+ */
+ public static URL findFile(final String fname) {
+ URL retval = loader.getResource(fname);
+ try {
+ if (retval==null)
+ retval = new File(fname).toURI().toURL();
+ return retval;
+ } catch (MalformedURLException ex) {}
+ return null;
+ }
+
+ /**
+ * Print string to output dialog.
+ * @param s string to print
+ */
+ private static void out(final String s) {
+ // System.out.println(s);
+ if (outputDiag != null)
+ outputDiag.print(s+"\n");
+ }
+
+ /**
+ * Return cancel state of output dialog
+ * @throws ExtractException
+ */
+ private static void checkCancel() throws ExtractException {
+ if (outputDiag.isCancelled())
+ throw new ExtractException("Extraction cancelled by user");
+ }
+}
+
+/**
+ * File name filter for level files.
+ * @author Volker Oth
+ */
+class LvlFilter implements FilenameFilter {
+
+ /* (non-Javadoc)
+ * @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
+ */
+ public boolean accept(final File dir, final String name) {
+ if (name.toLowerCase().indexOf(".lvl") != -1)
+ return true;
+ else
+ return false;
+ }
+}
+
diff --git a/jeu-test/Lemmini/0.84/src/Extract/ExtractException.java b/jeu-test/Lemmini/0.84/src/Extract/ExtractException.java
new file mode 100644
index 0000000..ce5b88e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/ExtractException.java
@@ -0,0 +1,42 @@
+package Extract;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generic exception class for extraction errors.
+ *
+ * @author Volker Oth
+ */
+
+public class ExtractException extends Exception {
+ private final static long serialVersionUID = 0x000000001;
+
+ /**
+ * Constructor.
+ */
+ public ExtractException() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ * @param s Exception string
+ */
+ public ExtractException(final String s) {
+ super(s);
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Extract/ExtractLevel.java b/jeu-test/Lemmini/0.84/src/Extract/ExtractLevel.java
new file mode 100644
index 0000000..2aacb14
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/ExtractLevel.java
@@ -0,0 +1,412 @@
+package Extract;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Convert binary "Lemmings for Win95" level files into text format.
+ */
+public class ExtractLevel {
+ private final static int scale = 2; // Scale (to convert lowres levels into hires levels)
+ /** names for defautl styles */
+ private final static String styles[] = {"dirt", "fire", "marble", "pillar",
+ "crystal", "brick", "rock", "snow", "bubble" };
+
+ /** release rate : 0 is slowest, 0x0FA (250) is fastest */
+ private static int releaseRate;
+ /** number of Lemmings in this level (maximum 0x0072 in original LVL format) */
+ private static int numLemmings;
+ /** number of Lemmings to rescue : should be less than or equal to number of Lemmings */
+ private static int numToRescue;
+ static int timeLimit; // Time Limit : max 0x00FF, 0x0001 to 0x0009 works best
+ /** number of climbers in this level : max 0xfa (250) */
+ private static int numClimbers;
+ /** number of floaters in this level : max 0xfa (250) */
+ private static int numFloaters;
+ /** number of bombers in this level : max 0xfa (250) */
+ private static int numBombers;
+ /** number of blockers in this level : max 0xfa (250) */
+ private static int numBlockers;
+ /** number of builders in this level : max 0xfa (250) */
+ private static int numBuilders;
+ /** number of bashers in this level : max 0xfa (250) */
+ private static int numBashers;
+ /** number of miners in this level : max 0xfa (250) */
+ private static int numMiners;
+ /** number of diggers in this level : max 0xfa (250) */
+ private static int numDiggers;
+ /** start screen x pos : 0 - 0x04f0 (1264) rounded to modulo 8 */
+ private static int xPos;
+ /**
+ * 0x0000 is dirt, <br>0x0001 is fire, <br>0x0002 is squasher,<br>
+ * 0x0003 is pillar,<br>0x0004 is crystal,<br>0x0005 is brick, <br>
+ * 0x0006 is rock, <br>0x0007 is snow, <br>0x0008 is bubble
+ */
+ private static int style;
+ /** extended style: no used in windows version ? */
+ static int extStyle;
+ /** placeholder ? */
+ static int dummy;
+ /** objects like doors - 32 objects each consists of 8 bytes */
+ static ArrayList<LvlObject> objects;
+ /** terrain the Lemmings walk on etc. - 400 tiles, 4 bytes each */
+ static ArrayList<Terrain> terrain;
+ /** steel areas which are indestructible - 32 objects, 4 bytes each */
+ static ArrayList<Steel> steel;
+ /** 32 byte level name - filled with whitespaces */
+ static String lvlName;
+
+ /**
+ * Convert one binary LVL file into text file
+ * @param fnIn Name of binary LVL file
+ * @param fnOut Name of target text file
+ * @throws Exception
+ */
+ static public void convertLevel(final String fnIn, final String fnOut) throws Exception {
+ // read file into buffer
+ LevelBuffer b;
+ try {
+ File f = new File(fnIn);
+ if (f.length() != 2048)
+ throw new Exception("Lemmings level files must be 2048 bytes in size!");
+ FileInputStream fi = new FileInputStream(fnIn);
+ byte buffer[] = new byte[(int)f.length()];
+ fi.read(buffer);
+ b = new LevelBuffer(buffer);
+ fi.close();
+ } catch(FileNotFoundException e) {
+ throw new Exception("File "+fnIn+" not found");
+ }
+ catch(IOException e) {
+ throw new Exception("I/O error while reading "+fnIn);
+ }
+ // output file
+ FileWriter fo = new FileWriter(fnOut);
+ // add only file name without the path in the first line
+ int p1 = fnIn.lastIndexOf("/");
+ int p2 = fnIn.lastIndexOf("\\");
+ if (p2 > p1)
+ p1 = p2;
+ if (p1 < 0)
+ p1 = 0;
+ else
+ p1++;
+ String fn = fnIn.substring(p1);
+ // analyze buffer
+ fo.write("# LVL extracted by Lemmini # "+fn+"\n");
+ // read configuration in big endian word
+ releaseRate = b.getWord();
+ fo.write("releaseRate = "+releaseRate+"\n");
+ numLemmings = b.getWord();
+ fo.write("numLemmings = "+numLemmings+"\n");
+ numToRescue = b.getWord();
+ fo.write("numToRescue = "+numToRescue+"\n");
+ timeLimit = b.getWord();
+ fo.write("timeLimit = "+timeLimit+"\n");
+ numClimbers = b.getWord();
+ fo.write("numClimbers = "+numClimbers+"\n");
+ numFloaters = b.getWord();
+ fo.write("numFloaters = "+numFloaters+"\n");
+ numBombers = b.getWord();
+ fo.write("numBombers = "+numBombers+"\n");
+ numBlockers = b.getWord();
+ fo.write("numBlockers = "+numBlockers+"\n");
+ numBuilders = b.getWord();
+ fo.write("numBuilders = "+numBuilders+"\n");
+ numBashers = b.getWord();
+ fo.write("numBashers = "+numBashers+"\n");
+ numMiners = b.getWord();
+ fo.write("numMiners = "+numMiners+"\n");
+ numDiggers = b.getWord();
+ fo.write("numDiggers = "+numDiggers+"\n");
+ xPos = b.getWord();
+ // bugfix: in some levels, the position is negative (?)
+ if (xPos<0)
+ xPos = -xPos;
+ xPos *= scale;
+ fo.write("xPos = "+xPos+"\n");
+ style = b.getWord();
+ fo.write("style = "+styles[style]+"\n");
+ extStyle = b.getWord();
+ dummy = b.getWord();
+ // read objects
+ fo.write("\n# Objects"+"\n");
+ fo.write("# id, xpos, ypos, paint mode (), upside down (0,1)"+"\n");
+ fo.write("# paint modes: 8=VIS_ON_TERRAIN, 4=NO_OVERWRITE, 0=FULL (only one value possible)\n");
+ byte by[] = new byte[8];
+ objects = new ArrayList<LvlObject>();
+ int idx = 0;
+ for (int i=0; i<32; i++) {
+ int sum = 0;
+ for (int j=0; j<8; j++) {
+ by[j] = b.getByte();
+ sum += by[j] & 0xff;
+ }
+ if (sum != 0) {
+ LvlObject obj = new LvlObject(by, scale);
+ objects.add(obj);
+ fo.write("object_"+idx+" = "+obj.id+", "+obj.xPos+", "+obj.yPos+", "+obj.paintMode+", "+(obj.upsideDown?1:0)+"\n");
+ idx++;
+ }
+ }
+ // read terrain
+ fo.write("\n# Terrain"+"\n");
+ fo.write("# id, xpos, ypos, modifier"+"\n");
+ fo.write("# modifier: 8=NO_OVERWRITE, 4=UPSIDE_DOWN, 2=REMOVE (combining allowed, 0=FULL)\n");
+ terrain = new ArrayList<Terrain>();
+ idx = 0;
+ for (int i=0; i<400; i++) {
+ int mask = 0xff;
+ for (int j=0; j<4; j++) {
+ by[j] = b.getByte();
+ mask &= by[j];
+ }
+ if (mask != 0xff) {
+ Terrain ter = new Terrain(by, scale);
+ terrain.add(ter);
+ fo.write("terrain_"+idx+" = "+ter.id+", "+ter.xPos+", "+ter.yPos+", "+ter.modifier+"\n");
+ idx++;
+ }
+ }
+ // read steel blocks
+ fo.write("\n#Steel"+"\n");
+ fo.write("# id, xpos, ypos, width, height"+"\n");
+ steel = new ArrayList<Steel>();
+ idx =0;
+ for (int i=0; i<32; i++) {
+ int sum = 0;
+ for (int j=0; j<4; j++) {
+ by[j] = b.getByte();
+ sum += by[j] & 0xff;
+ }
+ if (sum != 0) {
+ Steel stl = new Steel(by, scale);
+ steel.add(stl);
+ fo.write("steel_"+idx+" = "+stl.xPos+", "+stl.yPos+", "+stl.width+", "+stl.height+"\n");
+ idx++;
+ }
+ }
+ // read name
+ fo.write("\n#Name"+"\n");
+ char cName[] = new char[32];
+ for (int j=0; j<32; j++) {
+ // replace wrong apostrophes
+ char c = (char)(b.getByte() & 0xff);
+ if (c == '´' || c == '`')
+ c = '\'';
+ cName[j] = c;
+ }
+
+ // int pos = fnIn.lastIndexOf("\\");
+ // if (pos == -1)
+ // pos = fnIn.lastIndexOf("/");
+ // if (pos > -1)
+ // fnIn = fnIn.substring(pos+1);
+ lvlName = String.valueOf(cName);
+ fo.write("name = "+lvlName+"\n");
+ fo.close();
+ }
+}
+
+/**
+ * Storage class for level objects.
+ * @author Volker Oth
+ */
+class LvlObject {
+ /** paint mode: only visible on a terrain pixel */
+ private static final int MODE_VIS_ON_TERRAIN = 8;
+ /** paint mode: don't overwrite terrain pixel in the original background image */
+ private static final int MODE_NO_OVERWRITE = 4;
+ /** paint mode: paint without any further checks */
+ private static final int MODE_FULL = 0;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** identifier */
+ int id;
+ /** paint mode */
+ int paintMode;
+ /** flag: paint object upsdie down */
+ boolean upsideDown;
+
+ /**
+ * Constructor.
+ * @param b buffer
+ * @param scale Scale (to convert lowres levels into hires levels)
+ */
+ LvlObject (final byte b[], final int scale) {
+ // x pos : min 0xFFF8, max 0x0638. 0xFFF8 = -24, 0x0000 = -16, 0x0008 = -8
+ // 0x0010 = 0, 0x0018 = 8, ... , 0x0638 = 1576 note: should be multiples of 8
+ xPos = (short)(((b[0] & 0xff)<<8) + (b[1] & 0xff)) - 16;
+ xPos *= scale;
+ // y pos : min 0xFFD7, max 0x009F. 0xFFD7 = -41, 0xFFF8 = -8, 0xFFFF = -1
+ // 0x0000 = 0, ... , 0x009F = 159. note: can be any value in the specified range
+ yPos = (short)(((b[2] & 0xff)<<8) + (b[3] & 0xff));
+ yPos *= scale;
+ // obj id : min 0x0000, max 0x000F. the object id is different in each
+ // graphics set, however 0x0000 is always an exit and 0x0001 is always a start.
+ id = ((b[4] & 0xff)<<8) + (b[5] & 0xff);
+ // modifier : first byte can be 80 (do not overwrite existing terrain) or 40
+ // (must have terrain underneath to be visible). 00 specifies always draw full graphic.
+ // second byte can be 8F (display graphic upside-down) or 0F (display graphic normally)
+ switch (b[6] & 0xff) {
+ case 0x80:
+ paintMode = MODE_NO_OVERWRITE;
+ break;
+ case 0x40:
+ case 0xc0: // bug in original level 36: overwrite AND visible on terrain: impossible
+ paintMode = MODE_VIS_ON_TERRAIN;
+ break;
+ default:
+ paintMode = MODE_FULL;
+ break;
+ }
+ upsideDown = ((b[7] & 0xff) == 0x8f);
+ }
+}
+
+/**
+ * Storage class for terrain tiles.
+ * @author Volker Oth
+ */
+class Terrain {
+ private final static long serialVersionUID = 0x01;
+
+ /** identifier */
+ int id;
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** modifier - must be one of the above MODEs */
+ int modifier;
+
+ /**
+ * Constructor.
+ * @param b buffer
+ * @param scale Scale (to convert lowres levels into hires levels)
+ */
+ Terrain (final byte b[], final int scale) {
+ // xpos: 0x0000..0x063F. 0x0000 = -16, 0x0008 = -8, 0x0010 = 0, 0x063f = 1583.
+ // note: the xpos also contains modifiers. the first nibble can be
+ // 8 (do no overwrite existing terrain), 4 (display upside-down), or
+ // 2 (remove terrain instead of add it). you can add them together.
+ // 0 indicates normal.
+ // eg: 0xC011 means draw at xpos=1, do not overwrite, upside-down.
+ modifier = (b[0] & 0xf0)>>4;
+ xPos = ((b[0] & 0x0f)<<8)+(b[1] & 0xff) - 16;
+ xPos *= scale;
+ // y pos : 9-bit value. min 0xEF0, max 0x518. 0xEF0 = -38, 0xEF8 = -37,
+ // 0x020 = 0, 0x028 = 1, 0x030 = 2, 0x038 = 3, ... , 0x518 = 159
+ // note: the ypos value bleeds into the next value since it is 9bits.
+ yPos = (((b[2] & 0xff)<<1) + ((b[3]&0x80)>>7));
+ if ((yPos & 256) != 0) // highest bit set -> negative
+ yPos -= 512;
+ yPos -= 4;
+ yPos *= scale;
+ // terrain id: min 0x00, max 0x3F. not all graphic sets have all 64 graphics.
+ id = b[3] & 0x3f;
+ }
+}
+
+/**
+ *
+ * Storage class for steel areas.
+ * @author Volker Oth
+ */
+class Steel {
+ private final static long serialVersionUID = 0x01;
+
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** width in pixels */
+ int width;
+ /** height in pixels */
+ int height;
+
+ /**
+ * Constructor.
+ * @param b buffer
+ * @param scale Scale (to convert lowres levels into hires levels)
+ */
+ Steel (final byte b[], final int scale) { // note: last byte is always 0
+ // xpos: 9-bit value: 0x000..0x178). 0x000 = -16, 0x178 = 1580
+ xPos = (((b[0] & 0xff)<<1) + ((b[1]&0x80)>>7))*4 - 16;
+ xPos *= scale;
+ // ypos: 0x00..0x27. 0x00 = 0, 0x27 = 156 - each hex value represents 4 pixels
+ yPos = (b[1] & 0x7f) * 4;
+ yPos *= scale;
+ // area: 0x00..max 0xFF. first nibble is the x-size, from 0..F (represents 4 pixels)
+ // second nibble is the y-size. 0x00 = (4,4), 0x11 = (8,8), 0x7F = (32,64)
+ width = ((b[2] & 0xf0) >>4) * 4 + 4;
+ width *= scale;
+ height = (b[2] & 0xf) * 4 + 4;
+ height *= scale;
+ }
+}
+
+/**
+ * Abstraction layer for binary level in byte buffer.
+ * @author Volker Oth
+ */
+class LevelBuffer {
+ /** data buffer */
+ private byte buffer[];
+ /** byte offset */
+ private int ofs;
+
+ private final static long serialVersionUID = 0x01;
+
+ /**
+ * Constructor.
+ * @param b array of byte to use as buffer
+ */
+ LevelBuffer(final byte b[]) {
+ buffer = b;
+ ofs = 0;
+ }
+
+ /**
+ * Get word (2 bytes, little endian) at current position.
+ * @return word at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ int getWord() throws ArrayIndexOutOfBoundsException {
+ return ((buffer[ofs++]&0xff)<<8) + buffer[ofs++];
+ }
+
+ /**
+ * Get byte at current position.
+ * @return byte at current position
+ * @throws ArrayIndexOutOfBoundsException
+ */
+ byte getByte() throws ArrayIndexOutOfBoundsException {
+ return buffer[ofs++];
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Extract/ExtractSPR.java b/jeu-test/Lemmini/0.84/src/Extract/ExtractSPR.java
new file mode 100644
index 0000000..45f5f3d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/ExtractSPR.java
@@ -0,0 +1,439 @@
+package Extract;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Extract graphics from "Lemming for Win95" SPR data files.
+ * @author Volker Oth
+ */
+public class ExtractSPR {
+ /** palette index of transparent color */
+ private final static int transparentIndex = 0;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** array of GIF images to store to disk */
+ private GIFImage images[];
+ /** color palette */
+ private Palette palette = null;
+ /** buffer used to compress the palette (remove double entries) to work around issues on MacOS */
+ private int lookupBuffer[];
+
+ /**
+ * Load palette.
+ * @param fname Name of palette file
+ * @return ColorModel representation of Palette
+ * @throws ExtractException
+ */
+ Palette loadPalette(final String fname) throws ExtractException {
+ byte buffer[];
+
+ // read file into buffer
+ int paletteSize = 0;
+ try {
+ File f = new File(fname);
+ FileInputStream fi = new FileInputStream(fname);
+ buffer = new byte[(int)f.length()];
+ fi.read(buffer);
+ fi.close();
+ } catch(FileNotFoundException e) {
+ throw new ExtractException("File "+fname+" not found");
+ }
+ catch(IOException e) {
+ throw new ExtractException("I/O error while reading "+fname);
+ }
+ // check header
+ if (buffer[0] != 0x20 || buffer[1] != 0x4c || buffer[2] != 0x41 || buffer[3] != 0x50 )
+ throw new ExtractException("File "+fname+" ist not a lemmings palette file");
+
+ paletteSize = unsigned(buffer[4]) + unsigned(buffer[5])*256; // number of palette entries
+
+ byte[] r = new byte[paletteSize];
+ byte[] g = new byte[paletteSize];
+ byte[] b = new byte[paletteSize];
+
+ int ofs = 6; // skip two bytes which contain number of palettes (?)
+ for (int idx = 0; idx < paletteSize; idx++) {
+ r[idx] = buffer[ofs++];
+ g[idx] = buffer[ofs++];
+ b[idx] = buffer[ofs++];
+ ofs++;
+ }
+
+ // search for double entries, create
+ // new palette without double entries
+ // and lookup table to fix the pixel values
+ byte compressedR[] = new byte[paletteSize];
+ byte compressedG[] = new byte[paletteSize];
+ byte compressedB[] = new byte[paletteSize];
+ lookupBuffer = new int[paletteSize];
+
+ Arrays.fill(lookupBuffer,-1); // mark all entries invalid
+ Arrays.fill(compressedR, (byte)0);
+ Arrays.fill(compressedG, (byte)0);
+ Arrays.fill(compressedB, (byte)0);
+
+ int compressedIndex = 0;
+ for (int i=0; i<paletteSize; i++) {
+
+ if (lookupBuffer[i] == -1) { // if -1, this value is no doublette of a lower index
+ compressedR[compressedIndex] = r[i]; // copy value to compressed buffer
+ compressedG[compressedIndex] = g[i];
+ compressedB[compressedIndex] = b[i];
+ if ( i != transparentIndex ) { // don't search doublettes of transparent color
+ // search for doublettes at higher indeces
+ for (int j=i+1; j<paletteSize; j++) {
+ if ( j == transparentIndex ) // transparent color can't be a doublette of another color
+ continue;
+ if ( (r[i] == r[j]) && (g[i] == g[j]) && (b[i] == b[j]) )
+ lookupBuffer[j] = compressedIndex; // mark double entry in lookupBuffer
+ }
+ }
+ lookupBuffer[i] = compressedIndex++;
+ }
+ }
+
+ if (paletteSize != compressedIndex) {
+ paletteSize = compressedIndex;
+ r = compressedR;
+ g = compressedG;
+ b = compressedB;
+ }
+
+ palette = new Palette(r, g, b);
+ return palette;
+ }
+
+ /**
+ * Convert byte in unsigned int
+ * @param b Byte to convert
+ * @return Unsigned value of byte
+ */
+ private static int unsigned(final byte b) {
+ return b & 0xFF;
+ }
+
+ /**
+ * Load SPR file. Load palette first!
+ * @param fname Name of SPR file
+ * @return Array of Images representing all images stored in the SPR file
+ * @throws ExtractException
+ */
+ GIFImage[] loadSPR(final String fname) throws ExtractException {
+ byte buffer[];
+
+ if (palette == null)
+ throw new ExtractException("Load Palette first!");
+
+ // read file into buffer
+ try {
+ File f = new File(fname);
+ FileInputStream fi = new FileInputStream(fname);
+ buffer = new byte[(int)f.length()];
+ fi.read(buffer);
+ fi.close();
+ } catch(FileNotFoundException e) {
+ throw new ExtractException("File "+fname+" not found");
+ }
+ catch(IOException e) {
+ throw new ExtractException("I/O error while reading "+fname);
+ }
+ // check header
+ if (buffer[0] != 0x53 || buffer[1] != 0x52 || buffer[2] != 0x4c || buffer[3] != 0x45 )
+ throw new ExtractException("File "+fname+" ist not a lemmings sprite file");
+ // get number of frames
+ int frames = unsigned(buffer[4]) + unsigned(buffer[5])*256;
+ int ofs = unsigned(buffer[6]) + unsigned(buffer[7])*256;
+
+ images = new GIFImage[frames];
+ byte b;
+ int lineOfs;
+
+ for (int frame=0; frame < frames; frame++) {
+ // get header info
+ int xOfs = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // x offset of data in output image
+ int yOfs = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // y offset of data in output image
+ int maxLen = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // maximum length of a data line
+ int lines = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // number of data lines
+ int width = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // width of output image
+ int height = unsigned(buffer[ofs++]) + unsigned(buffer[ofs++])*256; // height of output image
+
+ byte pixels[] = new byte[width*height];
+
+ for (int i=0; i<pixels.length; i++)
+ pixels[i] = transparentIndex;
+
+ int y = yOfs*width;
+
+ int pxOffset = 0; // additional offset for lines broken in several packets
+
+ for (int line = 0; line < lines; ) {
+ // read line
+ b = buffer[ofs++]; // start character including length (>= 0x80) or line offset (<0x80)
+ lineOfs = 0;
+ while (b == 0x7f) { // special line offset for large sprites
+ lineOfs += 0x7f;
+ b = buffer[ofs++];
+ }
+ if (!( (b & 0x80) == 0x80)) {
+ // additional line offset
+ lineOfs += (b & 0x7f);
+ b = buffer[ofs++]; // start character
+ }
+ // get line length
+ int len = (b & 0xff) - 0x80;
+ if (len < 0 || len > 0x7f || len > maxLen)
+ throw new ExtractException(
+ "Maximum data line length exceeded in line "+line+" of frame "+frame+" of "+fname+" (ofs:"+ofs+")");
+ if (len > 0) {
+ try {
+ for (int pixel = 0; pixel < len; pixel++) {
+ // none of the extracted images uses more than 128 colors (indeed much less)
+ // but some use higher indeces. Instead of mirroring the palette, just and every
+ // entry with 0x7f.
+ // The lookup table is needed to get new index in compresse palette
+ byte pixelVal = (byte)(lookupBuffer[buffer[ofs++] & 0x7f] & 0xff);
+ pixels[y+xOfs+lineOfs+pixel+pxOffset] = pixelVal;
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ throw new ExtractException(
+ "Index out of bounds in line "+line+" of frame "+frame+" of "+fname+" (ofs:"+ofs+")");
+ }
+ b = buffer[ofs++]; // end character must be 0x80
+ if ( (b & 0xff) != 0x80) {
+ // if this is not the end character, the line is continued after an offset
+ pxOffset += (lineOfs + len);
+ ofs--;
+ continue;
+ }
+ }
+ pxOffset = 0;
+ line++;
+ y += width;
+ }
+ // convert byte array into BufferedImage
+ images[frame] = new GIFImage(width, height, pixels, palette);
+ }
+
+ return images;
+ }
+
+ /**
+ * Save all images of currently loaded SPR file
+ * @param fname Filename of GIF files to export. "_N.gif" will be appended with N being the image number.
+ * @param keepAnims If true, consequently stored imaged with same size will be stored inside one GIF (one beneath the other)
+ * @return Array of all the filenames stored
+ * @throws ExtractException
+ */
+ String[] saveAll(final String fname, final boolean keepAnims) throws ExtractException {
+ int width = images[0].getWidth();
+ int height = images[0].getHeight();
+ int startIdx = 0;
+ int animNum = 0;
+ ArrayList<String> files = new ArrayList<String>();
+
+ for (int idx=1; idx <= images.length; idx++) {
+ // search for first image with different size
+ if (keepAnims)
+ if (idx < images.length)
+ if (images[idx].getWidth() == width && images[idx].getHeight() == height)
+ continue;
+ // now save all the images in one: one above the other
+ int num;
+ if (keepAnims)
+ num = idx - startIdx;
+ else num = 1;
+ byte pixels[] = new byte[width*num*height];
+ GIFImage anim = new GIFImage(width, num*height, pixels, palette);
+ for (int n = 0; n<num; n++)
+ System.arraycopy(images[startIdx+n].getPixels(), 0, pixels, n*height*width, images[startIdx+n].getPixels().length);
+
+ startIdx = idx;
+ // construct filename
+ String fn = fname+"_"+Integer.toString(animNum++)+".gif";
+ // save gif
+ saveGif(anim, fn);
+ files.add(fn.toLowerCase());
+ // remember new size
+ if (idx < images.length) {
+ width = images[idx].getWidth();
+ height = images[idx].getHeight();
+ }
+ }
+ String[] fileArray = new String[files.size()];
+ return files.toArray(fileArray);
+ }
+
+ /**
+ * Save a number of images of currently loaded SPR file into one GIF (one image beneath the other)
+ * @param fname Name of GIF file to create (".gif" will NOT be appended)
+ * @param startIdx Index of first image to store
+ * @param frames Number of frames to store
+ * @throws ExtractException
+ */
+ void saveAnim(final String fname, final int startIdx, final int frames) throws ExtractException {
+ int width = images[startIdx].getWidth();
+ int height = images[startIdx].getHeight();
+
+ byte pixels[] = new byte[width*frames*height];
+ GIFImage anim = new GIFImage(width, frames*height, pixels, palette);
+ for (int n = 0; n<frames; n++)
+ System.arraycopy(images[startIdx+n].getPixels(), 0, pixels, n*height*width, images[startIdx+n].getPixels().length);
+ // save gif
+ saveGif(anim, fname);
+ }
+
+ /**
+ * Save one image as GIF
+ * @param img Image object to save
+ * @param fname Name of GIF file to create (".gif" will NOT be appended)
+ * @throws ExtractException
+ */
+ public static void saveGif(final GIFImage img, final String fname) throws ExtractException {
+ GifEncoder gifEnc = new GifEncoder(img.getWidth(), img.getHeight(), img.getPixels(),
+ img.palette.getRed(), img.palette.getGreen(), img.getPalette().getBlue());
+ try {
+ FileOutputStream f = new FileOutputStream(fname);
+ gifEnc.setTransparentPixel(transparentIndex);
+ gifEnc.write(f);
+ f.close();
+ } catch(FileNotFoundException ex) {
+ throw new ExtractException("Can't open file "+fname+" for writing." );
+ }
+ catch(IOException ex) {
+ throw new ExtractException("I/O error while writing file "+fname);
+ }
+ }
+
+
+ /**
+ * Storage class for palettes.
+ * @author Volker Oth
+ */
+ class Palette {
+ /** byte array of red components */
+ private byte[] red;
+ /** byte array of green components */
+ private byte[] green;
+ /** byte array of blue components */
+ private byte[] blue;
+
+ /**
+ * Create palette from array of color components
+ * @param r byte array of red components
+ * @param g byte array of green components
+ * @param b byte array of blue components
+ */
+ Palette(final byte[] r, final byte[] g, final byte[] b) {
+ red = r;
+ green = g;
+ blue = b;
+ }
+
+ /**
+ * Get blue components.
+ * @return byte array of blue components
+ */
+ public byte[] getBlue() {
+ return blue;
+ }
+
+ /**
+ * Get green components.
+ * @return byte array of green components
+ */
+ public byte[] getGreen() {
+ return green;
+ }
+
+ /**
+ * Get red components.
+ * @return byte array of red components
+ */
+ public byte[] getRed() {
+ return red;
+ }
+ }
+
+ /**
+ * Stores GIF Image in RAM.
+ */
+ class GIFImage {
+ /** width in pixels */
+ private int width;
+ /** height in pixels */
+ private int height;
+ /** pixel data */
+ private byte[] pixels;
+ /** color palette */
+ private Palette palette;
+
+ /**
+ * Constructor.
+ * @param w width in pixels.
+ * @param h height in pixels.
+ * @param buf pixel data
+ * @param p color palette
+ */
+ GIFImage(final int w, final int h, final byte[] buf, final Palette p) {
+ width = w;
+ height = h;
+ pixels = buf;
+ palette = p;
+ }
+
+ /**
+ * Get pixel data.
+ * @return pixel data as array of bytes
+ */
+ public byte[] getPixels() {
+ return pixels;
+ }
+
+ /**
+ * Get width in pixels.
+ * @return width in pixels
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Get height in pixels.
+ * @return height in pixels
+ */
+ public int getHeight() {
+ return height;
+ }
+
+ /**
+ * Get color palette.
+ * @return color palette
+ */
+ public Palette getPalette() {
+ return palette;
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Extract/FolderDialog.java b/jeu-test/Lemmini/0.84/src/Extract/FolderDialog.java
new file mode 100644
index 0000000..6846bb5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/FolderDialog.java
@@ -0,0 +1,337 @@
+package Extract;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.awt.GraphicsEnvironment;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+
+/**
+ * Dialog to enter source and target paths for resource extraction.
+ *
+ * @author Volker Oth
+ */
+public class FolderDialog extends JDialog {
+
+ private javax.swing.JPanel jContentPane = null;
+ private JLabel jLabelTrg = null;
+ private JTextField jTextFieldTrg = null;
+ private JLabel jLabelSrc = null;
+ private JTextField jTextFieldSrc = null;
+ private JButton jButtonSrc = null;
+ private JButton jButtonTrg = null;
+ private JButton jButtonQuit = null;
+ private JButton jButtonExtract = null;
+ private JLabel jLabelHeader = null;
+
+ // own stuff
+ private final static long serialVersionUID = 0x01;
+
+ /** target (Lemmini resource) path for extraction */
+ private String targetPath;
+ /** source (WINLEMM) path for extraction */
+ private String sourcePath; // @jve:decl-index=0:
+ /** self reference to this dialog */
+ private JDialog thisDialog;
+ /** flag that tells whether to extract or not */
+ private boolean doExtract;
+
+
+ /**
+ * Constructor for modal dialog in parent frame
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public FolderDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ // own stuff
+ thisDialog = this;
+ doExtract = false;
+ if (frame != null) {
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ } else {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ Point p = ge.getCenterPoint();
+ p.x -= this.getWidth()/2;
+ p.y -= this.getHeight()/2;
+ this.setLocation(p);
+ }
+ }
+
+ /**
+ * Set parameters for text edit boxes.
+ * @param srcPath source (WINLEMM) path for extraction
+ * @param trgPath target (Lemmini resource) path for extraction
+ */
+ public void setParameters(final String srcPath, final String trgPath) {
+ jTextFieldSrc.setText( srcPath );
+ sourcePath = srcPath;
+ jTextFieldTrg.setText( trgPath );
+ targetPath = trgPath;
+ }
+
+ /**
+ * Get target (Lemmini resource) path for extraction.
+ * @return target (Lemmini resource) path for extraction
+ */
+ public String getTarget() {
+ if (targetPath != null)
+ return targetPath;
+ else
+ return "";
+ }
+
+ /**
+ * Get source (WINLEMM) path for extraction.
+ * @return source (WINLEMM) path for extraction
+ */
+ public String getSource() {
+ if (sourcePath != null)
+ return sourcePath;
+ else
+ return "";
+ }
+
+ /**
+ * Get extraction selection status.
+ * @return true if extraction was chosen, false otherwise
+ */
+ public boolean getSuccess() {
+ return doExtract;
+ }
+
+ /**
+ * Initialize manually generated resources.
+ */
+ private void initialize() {
+ this.setSize(457, 208);
+ this.setContentPane(getJContentPane());
+ this.setTitle("Lemmini Resource Extractor");
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private javax.swing.JPanel getJContentPane() {
+ if(jContentPane == null) {
+ GridBagConstraints gridBagLabelHeader = new GridBagConstraints();
+ gridBagLabelHeader.gridx = 0;
+ gridBagLabelHeader.gridwidth = 2;
+ gridBagLabelHeader.anchor = GridBagConstraints.NORTHWEST;
+ gridBagLabelHeader.insets = new Insets(10, 10, 10, 0);
+ gridBagLabelHeader.gridy = 0;
+ jLabelHeader = new JLabel();
+ jLabelHeader.setText("Extract the resources from Lemmings for Windows");
+ GridBagConstraints gridBagLabelTrg = new GridBagConstraints();
+ gridBagLabelTrg.gridx = 0;
+ gridBagLabelTrg.anchor = GridBagConstraints.WEST;
+ gridBagLabelTrg.insets = new Insets(10, 10, 0, 0);
+ gridBagLabelTrg.gridy = 5;
+ GridBagConstraints gridBagButtonTrg = new GridBagConstraints();
+ gridBagButtonTrg.gridx = 1;
+ gridBagButtonTrg.anchor = GridBagConstraints.EAST;
+ gridBagButtonTrg.insets = new Insets(0, 0, 0, 10);
+ gridBagButtonTrg.gridy = 6;
+ GridBagConstraints gridBagButtonSrc = new GridBagConstraints();
+ gridBagButtonSrc.gridx = 1;
+ gridBagButtonSrc.anchor = GridBagConstraints.EAST;
+ gridBagButtonSrc.insets = new Insets(0, 0, 0, 10);
+ gridBagButtonSrc.gridy = 4;
+ GridBagConstraints gridBagTextFieldSrc = new GridBagConstraints();
+ gridBagTextFieldSrc.fill = GridBagConstraints.BOTH;
+ gridBagTextFieldSrc.gridy = 4;
+ gridBagTextFieldSrc.weightx = 1.0;
+ gridBagTextFieldSrc.anchor = GridBagConstraints.WEST;
+ gridBagTextFieldSrc.insets = new Insets(0, 10, 0, 10);
+ gridBagTextFieldSrc.gridx = 0;
+ GridBagConstraints gridBagTextFieldTrg = new GridBagConstraints();
+ gridBagTextFieldTrg.fill = GridBagConstraints.BOTH;
+ gridBagTextFieldTrg.gridy = 6;
+ gridBagTextFieldTrg.weightx = 1.0;
+ gridBagTextFieldTrg.anchor = GridBagConstraints.WEST;
+ gridBagTextFieldTrg.insets = new Insets(0, 10, 0, 10);
+ gridBagTextFieldTrg.gridx = 0;
+ GridBagConstraints gridBagLabelSrc = new GridBagConstraints();
+ gridBagLabelSrc.gridx = 0;
+ gridBagLabelSrc.anchor = GridBagConstraints.WEST;
+ gridBagLabelSrc.insets = new Insets(0, 10, 0, 0);
+ gridBagLabelSrc.gridy = 3;
+ GridBagConstraints gridBagButtonExtract = new GridBagConstraints();
+ gridBagButtonExtract.gridx = 1;
+ gridBagButtonExtract.insets = new Insets(20, 0, 0, 10);
+ gridBagButtonExtract.anchor = GridBagConstraints.EAST;
+ gridBagButtonExtract.gridy = 7;
+ GridBagConstraints gridBagButtonQuit = new GridBagConstraints();
+ gridBagButtonQuit.gridx = 0;
+ gridBagButtonQuit.anchor = GridBagConstraints.WEST;
+ gridBagButtonQuit.insets = new Insets(20, 10, 0, 0);
+ gridBagButtonQuit.gridy = 7;
+ jLabelSrc = new JLabel();
+ jLabelTrg = new JLabel();
+ jContentPane = new javax.swing.JPanel();
+ jContentPane.setLayout(new GridBagLayout());
+ jLabelTrg.setText("Target Path");
+ jLabelSrc.setText("Source Path (\"WINLEMM\" directory)");
+ jLabelSrc.setComponentOrientation(java.awt.ComponentOrientation.UNKNOWN);
+ jContentPane.setComponentOrientation(java.awt.ComponentOrientation.UNKNOWN);
+ jContentPane.add(jLabelTrg, gridBagLabelTrg);
+ jContentPane.add(getJTextFieldTrg(), gridBagTextFieldTrg);
+ jContentPane.add(jLabelSrc, gridBagLabelSrc);
+ jContentPane.add(getJTextFieldSrc(), gridBagTextFieldSrc);
+ jContentPane.add(getJButtonSrc(), gridBagButtonSrc);
+ jContentPane.add(getJButtonTrg(), gridBagButtonTrg);
+ jContentPane.add(getJButtonQuit(), gridBagButtonQuit);
+ jContentPane.add(getJButtonExtract(), gridBagButtonExtract);
+ jContentPane.add(jLabelHeader, gridBagLabelHeader);
+ }
+ return jContentPane;
+ }
+ /**
+ * This method initializes jTextFieldTrg
+ *
+ * @return javax.swing.JTextField
+ */
+ private JTextField getJTextFieldTrg() {
+ if (jTextFieldTrg == null) {
+ jTextFieldTrg = new JTextField();
+ jTextFieldTrg.setPreferredSize(new java.awt.Dimension(100,19));
+ jTextFieldTrg.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ targetPath = jTextFieldTrg.getText();
+ }
+ });
+ }
+ return jTextFieldTrg;
+ }
+ /**
+ * This method initializes jTextFieldSrc
+ *
+ * @return javax.swing.JTextField
+ */
+ private JTextField getJTextFieldSrc() {
+ if (jTextFieldSrc == null) {
+ jTextFieldSrc = new JTextField();
+ jTextFieldSrc.setPreferredSize(new java.awt.Dimension(100,19));
+ jTextFieldSrc.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ sourcePath = jTextFieldSrc.getText();
+ }
+ });
+ }
+ return jTextFieldSrc;
+ }
+ /**
+ * This method initializes jButtonSrc
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonSrc() {
+ if (jButtonSrc == null) {
+ jButtonSrc = new JButton();
+ jButtonSrc.setText("Browse");
+ jButtonSrc.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ JFileChooser jf = new JFileChooser(sourcePath);
+ jf.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY );
+ int returnVal = jf.showOpenDialog(thisDialog);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ sourcePath = jf.getSelectedFile().getAbsolutePath();
+ jTextFieldSrc.setText(sourcePath);
+ }
+ }
+ });
+ }
+ return jButtonSrc;
+ }
+
+ /**
+ * This method initializes jButtonTrg
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonTrg() {
+ if (jButtonTrg == null) {
+ jButtonTrg = new JButton();
+ jButtonTrg.setText("Browse");
+ jButtonTrg.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ JFileChooser jf = new JFileChooser(targetPath);
+ jf.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY );
+ int returnVal = jf.showOpenDialog(thisDialog);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ targetPath = jf.getSelectedFile().getAbsolutePath();
+ jTextFieldTrg.setText(targetPath);
+ }
+ }
+ });
+ }
+ return jButtonTrg;
+ }
+ /**
+ * This method initializes jButtonQuit
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonQuit() {
+ if (jButtonQuit == null) {
+ jButtonQuit = new JButton();
+ jButtonQuit.setText("Quit");
+ jButtonQuit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ sourcePath = jTextFieldSrc.getText();
+ targetPath = jTextFieldTrg.getText();
+ dispose();
+ }
+ });
+ }
+ return jButtonQuit;
+ }
+ /**
+ * This method initializes jButtonExtract
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonExtract() {
+ if (jButtonExtract == null) {
+ jButtonExtract = new JButton();
+ jButtonExtract.setText("Extract");
+ jButtonExtract.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ sourcePath = jTextFieldSrc.getText();
+ targetPath = jTextFieldTrg.getText();
+ doExtract = true;
+ dispose();
+ }
+ });
+ }
+ return jButtonExtract;
+ }
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/jeu-test/Lemmini/0.84/src/Extract/GifEncoder.java b/jeu-test/Lemmini/0.84/src/Extract/GifEncoder.java
new file mode 100644
index 0000000..532efe5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/GifEncoder.java
@@ -0,0 +1,551 @@
+package Extract;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.IndexColorModel;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** GifEncoder - writes out an image as a GIF.
+ *
+ * Transparency handling and variable bit size courtesy of Jack Palevich.
+ *
+ * Some hacks for compatibility with JVM on MacOS by Volker Oth
+ *
+ * Copyright (C) 1996 by Jef Poskanzer <jef(at)acme.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Visit the ACME Labs Java page for up-to-date versions of this and other
+ * fine Java utilities: http://www.acme.com/java/
+ *
+ * @author Jef Poskanzer / Volker Oth
+ */
+
+public class GifEncoder {
+
+ private boolean interlace = false;
+ private int width, height;
+ private byte[] pixels;
+ private byte[] r, g, b; // the color look-up table
+ private int pixelIndex;
+ private int numPixels;
+ private int transparentPixel = -1; // hpm
+
+ /**
+ * Constructs a new GifEncoder.
+ * @param width The image width.
+ * @param height The image height.
+ * @param pixels The pixel data.
+ * @param r The red look-up table.
+ * @param g The green look-up table.
+ * @param b The blue look-up table.
+ */
+ public GifEncoder(final int width, final int height, final byte[] pixels, final byte[] r,final byte[] g, final byte[] b) {
+ this.width = width;
+ this.height = height;
+ this.pixels = pixels;
+ this.r = r; this.g = g; this.b = b;
+ interlace = false;
+ pixelIndex = 0;
+ numPixels = width*height;
+ }
+
+ /**
+ * Constructs a new GifEncoder using an 8-bit AWT Image.
+ * The image is assumed to be fully loaded.
+ * @param img Image
+ */
+ public GifEncoder(final BufferedImage img) {
+ width = img.getWidth(null);
+ height = img.getHeight(null);
+ pixels = new byte[width * height];
+ /* VO: Pixelgrabber seems to behave differently on MacOS (uses first of two identical
+ * palette entries instead of the original one.
+ * Therefore we need to "grab" the pixels manually */
+ ColorModel cm = img.getColorModel();
+ if (cm instanceof IndexColorModel) {
+ IndexColorModel icm = (IndexColorModel)cm;
+ setTransparentPixel(icm.getTransparentPixel());
+ } else
+ throw new IllegalArgumentException("Image must be 8-bit");
+
+ /* VO: manual pixel grabbing */
+ for (int y=0; y<height; y++) {
+ int line = y*width;
+ for (int x=0; x<width; x++) {
+ int colIdx = img.getRaster().getDataBuffer().getElem(x+line);
+ pixels[line+x] = (byte)colIdx;
+ }
+ }
+
+ IndexColorModel m = (IndexColorModel)cm;
+ int mapSize = m.getMapSize();
+ r = new byte[mapSize];
+ g = new byte[mapSize];
+ b = new byte[mapSize];
+ m.getReds(r);
+ m.getGreens(g);
+ m.getBlues(b);
+ interlace = false;
+ pixelIndex = 0;
+ numPixels = width*height;
+ }
+
+ /**
+ * Saves the image as a GIF file.
+ * @param out Output stream to write to
+ * @throws IOException
+ */
+ public void write(final OutputStream out) throws IOException {
+ // Figure out how many bits to use.
+ int numColors = r.length;
+ int BitsPerPixel;
+ if (numColors<=2)
+ BitsPerPixel = 1;
+ else if (numColors<=4)
+ BitsPerPixel = 2;
+ else if (numColors<=16)
+ BitsPerPixel = 4;
+ else
+ BitsPerPixel = 8;
+
+ int ColorMapSize = 1 << BitsPerPixel;
+ byte[] reds = new byte[ColorMapSize];
+ byte[] grns = new byte[ColorMapSize];
+ byte[] blus = new byte[ColorMapSize];
+ for (int i=0; i<numColors; i++) {
+ reds[i] = r[i];
+ grns[i] = g[i];
+ blus[i] = b[i];
+ }
+ // hpm
+ GIFEncode(out, width, height, interlace, (byte) 0,
+ getTransparentPixel(), BitsPerPixel, reds, grns, blus);
+ }
+
+ // hpm
+ /**
+ * Set transparent pixel color (palette index)
+ * @param pixel transparent pixel color (palette index)
+ */
+ public void setTransparentPixel(final int pixel) {
+ transparentPixel = pixel;
+ }
+
+ // hpm
+ /**
+ * Get transparent pixel color (palette index)
+ * @return transparent pixel color (palette index)
+ */
+ public int getTransparentPixel() {
+ return transparentPixel;
+ }
+
+ static void writeString(final OutputStream out, final String str) throws IOException {
+ byte[] buf = str.getBytes();
+ out.write(buf);
+ }
+
+ // Adapted from ppmtogif, which is based on GIFENCOD by David
+ // Rowley <mgardi@watdscu.waterloo.edu>. Lempel-Zim compression
+ // based on "compress".
+
+ void GIFEncode(final OutputStream outs, final int Width, final int Height, final boolean Interlace, final byte Background,
+ final int Transparent, final int BitsPerPixel, final byte[] Red, final byte[] Green, final byte[] Blue ) throws IOException {
+ byte B;
+ int LeftOfs, TopOfs;
+ int ColorMapSize;
+ int InitCodeSize;
+ int i;
+
+ ColorMapSize = 1 << BitsPerPixel;
+ LeftOfs = TopOfs = 0;
+
+ // The initial code size
+ if ( BitsPerPixel <= 1 )
+ InitCodeSize = 2;
+ else
+ InitCodeSize = BitsPerPixel;
+
+ // Write the Magic header
+ writeString( outs, "GIF89a" );
+
+ // Write out the screen width and height
+ Putword( Width, outs );
+ Putword( Height, outs );
+
+ // Indicate that there is a global colour map
+ B = (byte) 0x80; // Yes, there is a color map
+ // OR in the resolution
+ B |= (byte) ( ( 8 - 1 ) << 4 );
+ // Not sorted
+ // OR in the Bits per Pixel
+ B |= (byte) ( ( BitsPerPixel - 1 ) );
+
+ // Write it out
+ Putbyte( B, outs );
+
+ // Write out the Background colour
+ Putbyte( Background, outs );
+
+ // Pixel aspect ratio - 1:1.
+ //Putbyte( (byte) 49, outs );
+ // Java's GIF reader currently has a bug, if the aspect ratio byte is
+ // not zero it throws an ImageFormatException. It doesn't know that
+ // 49 means a 1:1 aspect ratio. Well, whatever, zero works with all
+ // the other decoders I've tried so it probably doesn't hurt.
+ Putbyte( (byte) 0, outs );
+
+ // Write out the Global Colour Map
+ for ( i = 0; i < ColorMapSize; ++i ) {
+ Putbyte( Red[i], outs );
+ Putbyte( Green[i], outs );
+ Putbyte( Blue[i], outs );
+ }
+
+ // Write out extension for transparent colour index, if necessary.
+ if ( Transparent != -1 ) {
+ Putbyte( (byte) '!', outs );
+ Putbyte( (byte) 0xf9, outs );
+ Putbyte( (byte) 4, outs );
+ Putbyte( (byte) 1, outs );
+ Putbyte( (byte) 0, outs );
+ Putbyte( (byte) 0, outs );
+ Putbyte( (byte) Transparent, outs );
+ Putbyte( (byte) 0, outs );
+ }
+
+ // Write an Image separator
+ Putbyte( (byte) ',', outs );
+
+ // Write the Image header
+ Putword( LeftOfs, outs );
+ Putword( TopOfs, outs );
+ Putword( Width, outs );
+ Putword( Height, outs );
+
+ // Write out whether or not the image is interlaced
+ if ( Interlace )
+ Putbyte( (byte) 0x40, outs );
+ else
+ Putbyte( (byte) 0x00, outs );
+
+ // Write out the initial code size
+ Putbyte( (byte) InitCodeSize, outs );
+
+ // Go and actually compress the data
+ compress( InitCodeSize+1, outs );
+
+ // Write out a Zero-length packet (to end the series)
+ Putbyte( (byte) 0, outs );
+
+ // Write the GIF file terminator
+ Putbyte( (byte) ';', outs );
+ }
+
+
+ static final int EOF = -1;
+
+ // Return the next pixel from the image
+ int GIFNextPixel() {
+ if (pixelIndex==numPixels)
+ return EOF;
+ else
+ return pixels[pixelIndex++] & 0xff;
+ }
+
+
+ // Write out a word to the GIF file
+ void Putword( final int w, final OutputStream outs ) throws IOException {
+ Putbyte( (byte) ( w & 0xff ), outs );
+ Putbyte( (byte) ( ( w >> 8 ) & 0xff ), outs );
+ }
+
+ // Write out a byte to the GIF file
+ void Putbyte( final byte b, final OutputStream outs ) throws IOException {
+ outs.write( b );
+ }
+
+
+ // GIFCOMPR.C - GIF Image compression routines
+ //
+ // Lempel-Ziv compression based on 'compress'. GIF modifications by
+ // David Rowley (mgardi@watdcsu.waterloo.edu)
+
+ // General DEFINEs
+
+ private static final int BITS = 12;
+
+ private static final int HSIZE = 5003; // 80% occupancy
+
+ // GIF Image compression - modified 'compress'
+ //
+ // Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ //
+ // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
+ // Jim McKie (decvax!mcvax!jim)
+ // Steve Davies (decvax!vax135!petsd!peora!srd)
+ // Ken Turkowski (decvax!decwrl!turtlevax!ken)
+ // James A. Woods (decvax!ihnp4!ames!jaw)
+ // Joe Orost (decvax!vax135!petsd!joe)
+
+ private int n_bits; // number of bits/code
+ private int maxbits = BITS; // user settable max # bits/code
+ private int maxcode; // maximum code, given n_bits
+ private int maxmaxcode = 1 << BITS; // should NEVER generate this code
+
+ final int MAXCODE( final int n_bits ) {
+ return ( 1 << n_bits ) - 1;
+ }
+
+ private int[] htab = new int[HSIZE];
+ private int[] codetab = new int[HSIZE];
+
+ private int hsize = HSIZE; // for dynamic table sizing
+
+ private int free_ent = 0; // first unused entry
+
+ // block compression parameters -- after all codes are used up,
+ // and compression rate changes, start over.
+ private boolean clear_flg = false;
+
+ // Algorithm: use open addressing double hashing (no chaining) on the
+ // prefix code / next character combination. We do a variant of Knuth's
+ // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ // secondary probe. Here, the modular division first probe is gives way
+ // to a faster exclusive-or manipulation. Also do block compression with
+ // an adaptive reset, whereby the code table is cleared when the compression
+ // ratio decreases, but after the table fills. The variable-length output
+ // codes are re-sized at this point, and a special CLEAR code is generated
+ // for the decompressor. Late addition: construct the table according to
+ // file size for noticeable speed improvement on small files. Please direct
+ // questions about this implementation to ames!jaw.
+
+ private int g_init_bits;
+
+ private int ClearCode;
+ private int EOFCode;
+
+ void compress( final int init_bits, final OutputStream outs ) throws IOException {
+ int fcode;
+ int i /* = 0 */;
+ int c;
+ int ent;
+ int disp;
+ int hsize_reg;
+ int hshift;
+
+ // Set up the globals: g_init_bits - initial number of bits
+ g_init_bits = init_bits;
+
+ // Set up the necessary values
+ clear_flg = false;
+ n_bits = g_init_bits;
+ maxcode = MAXCODE( n_bits );
+
+ ClearCode = 1 << ( init_bits - 1 );
+ EOFCode = ClearCode + 1;
+ free_ent = ClearCode + 2;
+
+ char_init();
+
+ ent = GIFNextPixel();
+
+ hshift = 0;
+ for ( fcode = hsize; fcode < 65536; fcode *= 2 )
+ ++hshift;
+ hshift = 8 - hshift; // set hash code range bound
+
+ hsize_reg = hsize;
+ cl_hash( hsize_reg ); // clear hash table
+
+ output( ClearCode, outs );
+
+ outer_loop:
+ while ( (c = GIFNextPixel()) != EOF ) {
+ fcode = ( c << maxbits ) + ent;
+ i = ( c << hshift ) ^ ent; // xor hashing
+
+ if ( htab[i] == fcode ) {
+ ent = codetab[i];
+ continue;
+ }
+ else if ( htab[i] >= 0 ) { // non-empty slot
+ disp = hsize_reg - i; // secondary hash (after G. Knott)
+ if ( i == 0 )
+ disp = 1;
+ do {
+ if ( (i -= disp) < 0 )
+ i += hsize_reg;
+
+ if ( htab[i] == fcode ) {
+ ent = codetab[i];
+ continue outer_loop;
+ }
+ }
+ while ( htab[i] >= 0 );
+ }
+ output( ent, outs );
+ ent = c;
+ if ( free_ent < maxmaxcode ) {
+ codetab[i] = free_ent++; // code -> hashtable
+ htab[i] = fcode;
+ } else
+ cl_block( outs );
+ }
+ // Put out the final code.
+ output( ent, outs );
+ output( EOFCode, outs );
+ }
+
+ // output
+ //
+ // OutputDialog the given code.
+ // Inputs:
+ // code: A n_bits-bit integer. If == -1, then EOF. This assumes
+ // that n_bits =< wordsize - 1.
+ // Outputs:
+ // Outputs code to the file.
+ // Assumptions:
+ // Chars are 8 bits long.
+ // Algorithm:
+ // Maintain a BITS character long buffer (so that 8 codes will
+ // fit in it exactly). Use the VAX insv instruction to insert each
+ // code in turn. When the buffer fills up empty it and start over.
+
+ private int cur_accum = 0;
+ private int cur_bits = 0;
+
+ private final int MASKS[] = {
+ 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
+ 0x001F, 0x003F, 0x007F, 0x00FF,
+ 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
+ 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
+ };
+
+ void output( final int code, final OutputStream outs ) throws IOException {
+ cur_accum &= MASKS[cur_bits];
+
+ if ( cur_bits > 0 )
+ cur_accum |= ( code << cur_bits );
+ else
+ cur_accum = code;
+
+ cur_bits += n_bits;
+
+ while ( cur_bits >= 8 ) {
+ char_out( (byte) ( cur_accum & 0xff ), outs );
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ // If the next entry is going to be too big for the code size,
+ // then increase it, if possible.
+ if ( free_ent > maxcode || clear_flg ) {
+ if ( clear_flg ) {
+ maxcode = MAXCODE(n_bits = g_init_bits);
+ clear_flg = false;
+ } else {
+ ++n_bits;
+ if ( n_bits == maxbits )
+ maxcode = maxmaxcode;
+ else
+ maxcode = MAXCODE(n_bits);
+ }
+ }
+
+ if ( code == EOFCode ) {
+ // At EOF, write the rest of the buffer.
+ while ( cur_bits > 0 ) {
+ char_out( (byte) ( cur_accum & 0xff ), outs );
+ cur_accum >>= 8;
+ cur_bits -= 8;
+ }
+
+ flush_char( outs );
+ }
+ }
+
+ // Clear out the hash table
+
+ // table clear for block compress
+ void cl_block( final OutputStream outs ) throws IOException {
+ cl_hash( hsize );
+ free_ent = ClearCode + 2;
+ clear_flg = true;
+
+ output( ClearCode, outs );
+ }
+
+ // reset code table
+ void cl_hash( final int hsize ) {
+ for ( int i = 0; i < hsize; ++i )
+ htab[i] = -1;
+ }
+
+ // GIF Specific routines
+
+ // Number of characters so far in this 'packet'
+ private int a_count;
+
+ // Set up the 'byte output' routine
+ void char_init() {
+ a_count = 0;
+ }
+
+ // Define the storage for the packet accumulator
+ private byte[] accum = new byte[256];
+
+ // Add a character to the end of the current packet, and if it is 254
+ // characters, flush the packet to disk.
+ void char_out(final byte c, final OutputStream outs ) throws IOException {
+ accum[a_count++] = c;
+ if ( a_count >= 254 )
+ flush_char( outs );
+ }
+
+ // Flush the packet to disk, and reset the accumulator
+ void flush_char( final OutputStream outs ) throws IOException {
+ if ( a_count > 0 ) {
+ outs.write( a_count );
+ outs.write( accum, 0, a_count );
+ a_count = 0;
+ }
+ }
+
+}
+
+class GifEncoderHashitem {
+ public int rgb;
+ public int count;
+ public int index;
+ public boolean isTransparent;
+
+ public GifEncoderHashitem(final int rgb, final int count, final int index, final boolean isTransparent) {
+ this.rgb = rgb;
+ this.count = count;
+ this.index = index;
+ this.isTransparent = isTransparent;
+ }
+
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Extract/OutputDialog.java b/jeu-test/Lemmini/0.84/src/Extract/OutputDialog.java
new file mode 100644
index 0000000..7895e1e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Extract/OutputDialog.java
@@ -0,0 +1,207 @@
+package Extract;
+
+import javax.swing.JPanel;
+import java.awt.GraphicsEnvironment;
+import java.awt.Point;
+
+import javax.swing.JDialog;
+import java.awt.GridBagLayout;
+import java.awt.GridBagConstraints;
+
+import javax.swing.JFrame;
+import javax.swing.JTextArea;
+import javax.swing.JScrollPane;
+import javax.swing.JButton;
+import java.awt.Insets;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dialog used to output extraction progress information.
+ * @author Volker Oth
+ */
+public class OutputDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private JPanel jContentPane = null;
+
+ private JButton jButtonOk = null;
+
+ private JScrollPane jScrollPaneOut = null;
+
+ private JButton jButtonCancel = null;
+
+ private JTextArea jTextAreaOut = null;
+
+ // own stuff
+ /** Extraction canceled? */
+ private boolean cancel = false;
+
+ /**
+ * Constructor for modal dialog in parent frame.
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public OutputDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ // own stuff
+ if (frame != null) {
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ } else {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ Point p = ge.getCenterPoint();
+ p.x -= this.getWidth()/2;
+ p.y -= this.getHeight()/2;
+ this.setLocation(p);
+ }
+ }
+
+ /**
+ * Print text to output console.
+ * @param txt text to print
+ */
+ public void print(final String txt) {
+ jTextAreaOut.insert(txt, jTextAreaOut.getDocument().getLength());
+ jTextAreaOut.setCaretPosition(jTextAreaOut.getDocument().getLength());
+ }
+
+ /**
+ * Return cancel state of extraction process.
+ * @return true if extraction was cancelled, else false
+ */
+ public boolean isCancelled() {
+ return cancel;
+ }
+
+ /**
+ * Enable the Ok button (if extraction was done successfully)
+ */
+ public void enableOk() {
+ jButtonOk.setEnabled(true);
+ }
+
+ /**
+ * Initialize manually generated resources.
+ */
+ private void initialize() {
+ this.setSize(500, 352);
+ this.setTitle("Lemmini Resource Extractor");
+ this.setContentPane(getJContentPane());
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ GridBagConstraints gridBagButtonCancel = new GridBagConstraints();
+ gridBagButtonCancel.gridx = 0;
+ gridBagButtonCancel.anchor = GridBagConstraints.WEST;
+ gridBagButtonCancel.insets = new Insets(5, 5, 5, 10);
+ gridBagButtonCancel.gridy = 1;
+ GridBagConstraints gridBagScrollPane = new GridBagConstraints();
+ gridBagScrollPane.fill = GridBagConstraints.BOTH;
+ gridBagScrollPane.gridy = 0;
+ gridBagScrollPane.weightx = 1.0;
+ gridBagScrollPane.weighty = 1.0;
+ gridBagScrollPane.gridwidth = 2;
+ gridBagScrollPane.gridx = 0;
+ GridBagConstraints gridBagButtonOk = new GridBagConstraints();
+ gridBagButtonOk.gridx = 1;
+ gridBagButtonOk.insets = new Insets(5, 5, 5, 5);
+ gridBagButtonOk.anchor = GridBagConstraints.EAST;
+ gridBagButtonOk.gridy = 1;
+ jContentPane = new JPanel();
+ jContentPane.setLayout(new GridBagLayout());
+ jContentPane.add(getJButtonOk(), gridBagButtonOk);
+ jContentPane.add(getJScrollPaneOut(), gridBagScrollPane);
+ jContentPane.add(getJButtonCancel(), gridBagButtonCancel);
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes jButtonOk
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonOk() {
+ if (jButtonOk == null) {
+ jButtonOk = new JButton();
+ jButtonOk.setText("Ok");
+ jButtonOk.setEnabled(false);
+ jButtonOk.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ dispose();
+ }
+ });
+ }
+ return jButtonOk;
+ }
+
+ /**
+ * This method initializes jScrollPaneOut
+ *
+ * @return javax.swing.JScrollPane
+ */
+ private JScrollPane getJScrollPaneOut() {
+ if (jScrollPaneOut == null) {
+ jScrollPaneOut = new JScrollPane();
+ jScrollPaneOut.setViewportView(getJTextAreaOut());
+ }
+ return jScrollPaneOut;
+ }
+
+ /**
+ * This method initializes jButtonCancel
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonCancel() {
+ if (jButtonCancel == null) {
+ jButtonCancel = new JButton();
+ jButtonCancel.setText("Cancel");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ cancel =true;
+ dispose();
+ }
+ });
+ }
+ return jButtonCancel;
+ }
+
+ /**
+ * This method initializes jTextAreaOut
+ *
+ * @return javax.swing.JTextArea
+ */
+ private JTextArea getJTextAreaOut() {
+ if (jTextAreaOut == null) {
+ jTextAreaOut = new JTextArea();
+ jTextAreaOut.setEditable(false);
+ }
+ return jTextAreaOut;
+ }
+
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/jeu-test/Lemmini/0.84/src/GUI/GainDialog.java b/jeu-test/Lemmini/0.84/src/GUI/GainDialog.java
new file mode 100644
index 0000000..b2598eb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GUI/GainDialog.java
@@ -0,0 +1,180 @@
+package GUI;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSlider;
+
+import Game.GameController;
+import Game.Music;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dialog for volume/gain control.
+ * @author Volker Oth
+ */
+public class GainDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private JPanel jContentPane = null;
+
+ private JSlider jSliderMusic = null;
+
+ private JLabel jLabelMusicGain = null;
+
+ private JLabel jLabelSoundGain = null;
+
+ private JSlider jSliderSound = null;
+
+ private JButton jButtonOK = null;
+
+ private JButton jButtonCancel = null;
+
+
+ /**
+ * Constructor for modal dialog in parent frame.
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public GainDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ //
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ jSliderSound.setValue((int)(100*GameController.sound.getGain()));
+ jSliderMusic.setValue((int)(100*Music.getGain()));
+ }
+
+ /**
+ * Automatically generated init.
+ */
+ private void initialize() {
+ this.setSize(300, 200);
+ this.setTitle("Volume Controls");
+ this.setContentPane(getJContentPane());
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ jLabelSoundGain = new JLabel();
+ jLabelSoundGain.setBounds(new Rectangle(15, 75, 101, 14));
+ jLabelSoundGain.setText("Sound Volume");
+ jLabelMusicGain = new JLabel();
+ jLabelMusicGain.setText("Music Volume");
+ jLabelMusicGain.setBounds(new Rectangle(15, 15, 106, 14));
+ jContentPane = new JPanel();
+ jContentPane.setLayout(null);
+ jContentPane.add(getJSliderMusic(), null);
+ jContentPane.add(jLabelMusicGain, null);
+ jContentPane.add(jLabelSoundGain, null);
+ jContentPane.add(getJSliderSound(), null);
+ jContentPane.add(getJButtonOK(), null);
+ jContentPane.add(getJButtonCancel(), null);
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes jSliderMusic
+ *
+ * @return javax.swing.JSlider
+ */
+ private JSlider getJSliderMusic() {
+ if (jSliderMusic == null) {
+ jSliderMusic = new JSlider();
+ jSliderMusic.setBounds(new Rectangle(15, 30, 256, 25));
+ jSliderMusic.setMaximum(100);
+ jSliderMusic.setMinimum(0);
+ jSliderMusic.setMajorTickSpacing(10);
+ jSliderMusic.setPaintTicks(true);
+ jSliderMusic.setValue(100);
+ }
+ return jSliderMusic;
+ }
+
+ /**
+ * This method initializes jSliderSound
+ *
+ * @return javax.swing.JSlider
+ */
+ private JSlider getJSliderSound() {
+ if (jSliderSound == null) {
+ jSliderSound = new JSlider();
+ jSliderSound.setBounds(new Rectangle(15, 90, 256, 25));
+ jSliderSound.setMaximum(100);
+ jSliderSound.setMinimum(0);
+ jSliderSound.setPaintTicks(true);
+ jSliderSound.setValue(100);
+ jSliderSound.setMajorTickSpacing(10);
+ }
+ return jSliderSound;
+ }
+
+ /**
+ * This method initializes jButtonOK
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonOK() {
+ if (jButtonOK == null) {
+ jButtonOK = new JButton();
+ jButtonOK.setBounds(new Rectangle(210, 135, 66, 25));
+ jButtonOK.setText(" Ok ");
+ jButtonOK.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ Music.setGain(jSliderMusic.getValue()/100.0);
+ GameController.sound.setGain(jSliderSound.getValue()/100.0);
+ dispose();
+ }
+ });
+ }
+ return jButtonOK;
+ }
+
+ /**
+ * This method initializes jButtonCancel
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonCancel() {
+ if (jButtonCancel == null) {
+ jButtonCancel = new JButton();
+ jButtonCancel.setBounds(new Rectangle(14, 136, 77, 23));
+ jButtonCancel.setText("Cancel");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ dispose();
+ }
+ });
+ }
+ return jButtonCancel;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/GUI/LegalDialog.java b/jeu-test/Lemmini/0.84/src/GUI/LegalDialog.java
new file mode 100644
index 0000000..c36fe1f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GUI/LegalDialog.java
@@ -0,0 +1,222 @@
+package GUI;
+
+import java.awt.Desktop;
+import java.awt.GraphicsEnvironment;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JEditorPane;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dialog with legal information.
+ * @author Volker Oth
+ */
+public class LegalDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private JPanel jContentPane = null;
+
+ private JButton jButtonCancel = null;
+
+ private JButton jButtonOk = null;
+
+ private JScrollPane jScrollPane = null;
+
+ private JEditorPane thisEditor = null;
+
+ // own stuff
+ private boolean ok = false;
+
+ private URL thisURL;
+
+ /**
+ * Initialize manually generated resources.
+ */
+ private void init() {
+ ClassLoader loader = LegalDialog.class.getClassLoader();
+ thisURL = loader.getResource("disclaimer.htm");
+ try {
+ thisEditor = new JEditorPane( thisURL );
+ thisEditor.setEditable( false );
+ // needed to open browser via clicking on a link
+ thisEditor.addHyperlinkListener(new HyperlinkListener() {
+ public void hyperlinkUpdate(HyperlinkEvent e) {
+ URL url = e.getURL();
+ if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
+ try {
+ if (url.sameFile(thisURL))
+ thisEditor.setPage(url);
+ else
+ Desktop.getDesktop().browse(url.toURI());
+ } catch (IOException ex) {
+ } catch (URISyntaxException ex) {
+ }
+ } else if (e.getEventType() == HyperlinkEvent.EventType.ENTERED) {
+ if (url.sameFile(thisURL))
+ thisEditor.setToolTipText(url.getRef());
+ else
+ thisEditor.setToolTipText(url.toExternalForm());
+ } else if (e.getEventType() == HyperlinkEvent.EventType.EXITED) {
+ thisEditor.setToolTipText(null);
+ }
+ }
+ });
+ jScrollPane.setViewportView(thisEditor); // Generated
+ } catch (IOException ex) {ex.printStackTrace();};
+ }
+
+
+ /**
+ * Constructor for modal dialog in parent frame.
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public LegalDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ // own stuff
+ if (frame != null) {
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ } else {
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ Point p = ge.getCenterPoint();
+ p.x -= this.getWidth()/2;
+ p.y -= this.getHeight()/2;
+ this.setLocation(p);
+ }
+ init();
+ }
+
+ /**
+ * Automatically generated init.
+ */
+ private void initialize() {
+ this.setSize(640, 450);
+ this.setTitle("Lemmini - Disclaimer");
+ this.setContentPane(getJContentPane());
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ GridBagConstraints gridBagScrollPane = new GridBagConstraints();
+ gridBagScrollPane.fill = GridBagConstraints.BOTH;
+ gridBagScrollPane.gridy = 0;
+ gridBagScrollPane.weightx = 1.0;
+ gridBagScrollPane.weighty = 1.0;
+ gridBagScrollPane.gridwidth = 2;
+ gridBagScrollPane.gridx = 0;
+ GridBagConstraints gridBagButtonOk = new GridBagConstraints();
+ gridBagButtonOk.gridx = 1;
+ gridBagButtonOk.anchor = GridBagConstraints.EAST;
+ gridBagButtonOk.insets = new Insets(5, 0, 5, 5);
+ gridBagButtonOk.gridy = 1;
+ GridBagConstraints gridBagButtonCancel = new GridBagConstraints();
+ gridBagButtonCancel.gridx = 0;
+ gridBagButtonCancel.insets = new Insets(5, 5, 5, 0);
+ gridBagButtonCancel.gridy = 1;
+ jContentPane = new JPanel();
+ jContentPane.setLayout(new GridBagLayout());
+ jContentPane.add(getJButtonCancel(), gridBagButtonCancel);
+ jContentPane.add(getJButtonOk(), gridBagButtonOk);
+ jContentPane.add(getJScrollPane(), gridBagScrollPane);
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes jButtonCancel
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonCancel() {
+ if (jButtonCancel == null) {
+ jButtonCancel = new JButton();
+ jButtonCancel.setText("I disagree");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ ok = false;
+ dispose();
+ }
+ });
+ }
+ return jButtonCancel;
+ }
+
+ /**
+ * This method initializes jButtonOk
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonOk() {
+ if (jButtonOk == null) {
+ jButtonOk = new JButton();
+ jButtonOk.setText("I agree");
+ jButtonOk.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ ok = true;
+ dispose();
+ }
+ });
+ }
+ return jButtonOk;
+ }
+
+ /**
+ * This method initializes jScrollPane
+ *
+ * @return javax.swing.JScrollPane
+ */
+ private JScrollPane getJScrollPane() {
+ if (jScrollPane == null) {
+ jScrollPane = new JScrollPane();
+ jScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
+ }
+ return jScrollPane;
+ }
+
+ /**
+ * Ok button was pressed.
+ * @return true: ok button was pressed.
+ */
+ public boolean isOk() {
+ return ok;
+ }
+
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/jeu-test/Lemmini/0.84/src/GUI/LevelCodeDialog.java b/jeu-test/Lemmini/0.84/src/GUI/LevelCodeDialog.java
new file mode 100644
index 0000000..30e0ffd
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GUI/LevelCodeDialog.java
@@ -0,0 +1,256 @@
+package GUI;
+
+import java.awt.Dimension;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+
+import Game.GameController;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dialog for entering level codes.
+ * @author Volker Oth
+ */
+public class LevelCodeDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private JPanel jContentPane = null;
+
+ private JLabel jLabelLvlPack = null;
+
+ private JComboBox jComboBoxLvlPack = null;
+
+ private JLabel jLabelCode = null;
+
+ private JTextField jTextFieldCode = null;
+
+ private JButton jButtonOk = null;
+
+ private JButton jButtonCancel = null;
+
+
+ // own stuff
+ private int levelPackIndex;
+ private String code; // @jve:decl-index=0:
+
+ /**
+ * Initialize manually generated resources.
+ */
+ private void init() {
+ // level pack 0 is the dummy level pack -> not selectable
+ for (int i=1; i<GameController.getLevelPackNum(); i++)
+ jComboBoxLvlPack.addItem(GameController.getLevelPack(i).getName());
+ int lpi = GameController.getCurLevelPackIdx();
+ if (lpi==0)
+ lpi = 1;
+ jComboBoxLvlPack.setSelectedIndex(lpi-1);
+
+ levelPackIndex = lpi;
+ jTextFieldCode.setText("");
+ }
+
+ /**
+ * Get entered level code.
+ * @return entered level code.
+ */
+ public String getCode() {
+ return code;
+ }
+
+ /**
+ * Get selected level pack.
+ * @return selected level pack
+ */
+ public int getLevelPack() {
+ return levelPackIndex;
+ }
+
+ /**
+ * Constructor for modal dialog in parent frame
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public LevelCodeDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ // own stuff
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ init();
+ }
+
+ /**
+ * Automatically generated init.
+ */
+ private void initialize() {
+ this.setSize(300, 153);
+ this.setTitle("Enter Level Code");
+ this.setContentPane(getJContentPane());
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ GridBagConstraints gridBagButtonCancel = new GridBagConstraints();
+ gridBagButtonCancel.gridx = 1;
+ gridBagButtonCancel.insets = new Insets(8, 24, 0, 4);
+ gridBagButtonCancel.anchor = GridBagConstraints.EAST;
+ gridBagButtonCancel.weightx = 1.0D;
+ gridBagButtonCancel.fill = GridBagConstraints.HORIZONTAL;
+ gridBagButtonCancel.gridy = 4;
+ GridBagConstraints gridBagButtonOk = new GridBagConstraints();
+ gridBagButtonOk.gridx = 0;
+ gridBagButtonOk.insets = new Insets(8, 4, 0, 24);
+ gridBagButtonOk.anchor = GridBagConstraints.WEST;
+ gridBagButtonOk.weightx = 1.0D;
+ gridBagButtonOk.fill = GridBagConstraints.HORIZONTAL;
+ gridBagButtonOk.gridy = 4;
+ GridBagConstraints gridBagTextFieldCode = new GridBagConstraints();
+ gridBagTextFieldCode.fill = GridBagConstraints.BOTH;
+ gridBagTextFieldCode.gridy = 3;
+ gridBagTextFieldCode.weightx = 1.0;
+ gridBagTextFieldCode.insets = new Insets(0, 4, 0, 4);
+ gridBagTextFieldCode.gridwidth = 2;
+ gridBagTextFieldCode.gridx = 0;
+ GridBagConstraints gridBagLabelCode = new GridBagConstraints();
+ gridBagLabelCode.gridx = 0;
+ gridBagLabelCode.anchor = GridBagConstraints.WEST;
+ gridBagLabelCode.insets = new Insets(8, 4, 0, 4);
+ gridBagLabelCode.fill = GridBagConstraints.HORIZONTAL;
+ gridBagLabelCode.gridwidth = 2;
+ gridBagLabelCode.gridy = 2;
+ jLabelCode = new JLabel();
+ jLabelCode.setText("Enter Level Code");
+ GridBagConstraints gridBagComboLvlPack = new GridBagConstraints();
+ gridBagComboLvlPack.fill = GridBagConstraints.BOTH;
+ gridBagComboLvlPack.gridy = 1;
+ gridBagComboLvlPack.weightx = 1.0;
+ gridBagComboLvlPack.anchor = GridBagConstraints.WEST;
+ gridBagComboLvlPack.insets = new Insets(0, 4, 6, 4);
+ gridBagComboLvlPack.gridwidth = 2;
+ gridBagComboLvlPack.gridx = 0;
+ GridBagConstraints gridBagLabelLvlPack = new GridBagConstraints();
+ gridBagLabelLvlPack.gridx = 0;
+ gridBagLabelLvlPack.anchor = GridBagConstraints.NORTHWEST;
+ gridBagLabelLvlPack.insets = new Insets(4, 4, 0, 4);
+ gridBagLabelLvlPack.fill = GridBagConstraints.HORIZONTAL;
+ gridBagLabelLvlPack.gridwidth = 2;
+ gridBagLabelLvlPack.gridy = 0;
+ jLabelLvlPack = new JLabel();
+ jLabelLvlPack.setText("Chose level pack");
+ jContentPane = new JPanel();
+ jContentPane.setLayout(new GridBagLayout());
+ jContentPane.add(jLabelLvlPack, gridBagLabelLvlPack);
+ jContentPane.add(getJComboBoxLvlPack(), gridBagComboLvlPack);
+ jContentPane.add(jLabelCode, gridBagLabelCode);
+ jContentPane.add(getJTextFieldCode(), gridBagTextFieldCode);
+ jContentPane.add(getJButtonOk(), gridBagButtonOk);
+ jContentPane.add(getJButtonCancel(), gridBagButtonCancel);
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes jComboBoxLvlPack
+ *
+ * @return javax.swing.JComboBox
+ */
+ private JComboBox getJComboBoxLvlPack() {
+ if (jComboBoxLvlPack == null) {
+ jComboBoxLvlPack = new JComboBox();
+ }
+ return jComboBoxLvlPack;
+ }
+
+ /**
+ * This method initializes jTextFieldCode
+ *
+ * @return javax.swing.JTextField
+ */
+ private JTextField getJTextFieldCode() {
+ if (jTextFieldCode == null) {
+ jTextFieldCode = new JTextField();
+ jTextFieldCode.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ code = jTextFieldCode.getText();
+ levelPackIndex = jComboBoxLvlPack.getSelectedIndex()+1;
+ dispose();
+ }
+ });
+ }
+ return jTextFieldCode;
+ }
+
+ /**
+ * This method initializes jButtonOk
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonOk() {
+ if (jButtonOk == null) {
+ jButtonOk = new JButton();
+ jButtonOk.setText("Ok");
+ jButtonOk.setPreferredSize(new Dimension(90, 50));
+ jButtonOk.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ code = jTextFieldCode.getText();
+ levelPackIndex = jComboBoxLvlPack.getSelectedIndex()+1;
+ dispose();
+ }
+ });
+ }
+ return jButtonOk;
+ }
+
+ /**
+ * This method initializes jButtonCancel
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonCancel() {
+ if (jButtonCancel == null) {
+ jButtonCancel = new JButton();
+ jButtonCancel.setText("Cancel");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ code = null;
+ levelPackIndex = -1;
+ dispose();
+ }
+ });
+ }
+ return jButtonCancel;
+ }
+
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/jeu-test/Lemmini/0.84/src/GUI/PlayerDialog.java b/jeu-test/Lemmini/0.84/src/GUI/PlayerDialog.java
new file mode 100644
index 0000000..bf718a4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GUI/PlayerDialog.java
@@ -0,0 +1,287 @@
+package GUI;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JButton;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import Game.Core;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Dialog for managing players.
+ * @author Volker Oth
+ */
+public class PlayerDialog extends JDialog {
+
+ private static final long serialVersionUID = 1L;
+
+ private JPanel jContentPane = null;
+
+ private JScrollPane jScrollPane = null;
+
+ private JList jList = null;
+
+ private JButton jButtonNew = null;
+
+ private JButton jButtonDelete = null;
+
+ private JButton jButtonOK = null;
+
+ private JButton jButtonCancel = null;
+
+ // own stuff
+ private Vector<String> players;
+
+
+ /**
+ * Get list of players.
+ * @return list of players.
+ */
+ public List<String> getPlayers() {
+ return players;
+ }
+
+ /**
+ * Get selected list index.
+ * @return selected list index
+ */
+ public int getSelection() {
+ return jList.getSelectedIndex();
+ }
+
+ /**
+ * Initialize manually generated resources.
+ */
+ private void init() {
+ players = new Vector<String>();
+ for (int i=0; i<Core.getPlayerNum(); i++)
+ players.add(Core.getPlayer(i));
+ jList = new JList(players);
+ jScrollPane.setViewportView(jList);
+ }
+
+ /**
+ * Constructor for modal dialog in parent frame.
+ * @param frame parent frame
+ * @param modal create modal dialog?
+ */
+ public PlayerDialog(final JFrame frame, final boolean modal) {
+ super(frame, modal);
+ initialize();
+
+ // own stuff
+ Point p = frame.getLocation();
+ this.setLocation(p.x+frame.getWidth()/2-getWidth()/2, p.y+frame.getHeight()/2-getHeight()/2);
+ init();
+ }
+
+ /**
+ * Automatically generated init.
+ */
+ private void initialize() {
+ this.setSize(442, 199);
+ this.setTitle("Manage Players");
+ this.setContentPane(getJContentPane());
+ }
+
+ /**
+ * This method initializes jContentPane
+ *
+ * @return javax.swing.JPanel
+ */
+ private JPanel getJContentPane() {
+ if (jContentPane == null) {
+ GridBagConstraints gridBagButtonNew = new GridBagConstraints();
+ gridBagButtonNew.fill = GridBagConstraints.HORIZONTAL;
+ gridBagButtonNew.insets = new Insets(0, 0, 0, 0);
+ GridBagConstraints gridBagButtonCancel = new GridBagConstraints();
+ gridBagButtonCancel.gridx = 0;
+ gridBagButtonCancel.insets = new Insets(0, 0, 0, 0);
+ gridBagButtonCancel.anchor = GridBagConstraints.WEST;
+ gridBagButtonCancel.gridy = 5;
+ GridBagConstraints gridBagButtonOk = new GridBagConstraints();
+ gridBagButtonOk.gridx = 1;
+ gridBagButtonOk.fill = GridBagConstraints.HORIZONTAL;
+ gridBagButtonOk.insets = new Insets(0, 0, 0, 0);
+ gridBagButtonOk.gridy = 5;
+ GridBagConstraints gridBagConstraints2 = new GridBagConstraints();
+ gridBagConstraints2.gridx = 1;
+ gridBagConstraints2.insets = new Insets(0, 2, 2, 2);
+ gridBagConstraints2.fill = GridBagConstraints.HORIZONTAL;
+ gridBagConstraints2.anchor = GridBagConstraints.NORTH;
+ gridBagConstraints2.gridy = 3;
+ GridBagConstraints gridBagButtonDelete = new GridBagConstraints();
+ gridBagButtonDelete.gridx = 1;
+ gridBagButtonDelete.insets = new Insets(0, 0, 0, 0);
+ gridBagButtonDelete.fill = GridBagConstraints.HORIZONTAL;
+ gridBagButtonDelete.gridy = 2;
+ GridBagConstraints gridBagScrollPane = new GridBagConstraints();
+ gridBagScrollPane.fill = GridBagConstraints.BOTH;
+ gridBagScrollPane.gridy = 0;
+ gridBagScrollPane.weightx = 1.0;
+ gridBagScrollPane.weighty = 1.0;
+ gridBagScrollPane.gridheight = 4;
+ gridBagScrollPane.insets = new Insets(0, 0, 0, 0);
+ gridBagScrollPane.gridx = 0;
+ jContentPane = new JPanel();
+ jContentPane.setLayout(new GridBagLayout());
+ jContentPane.add(getJScrollPane(), gridBagScrollPane);
+ jContentPane.add(getJButtonNew(), gridBagButtonNew);
+ jContentPane.add(getJButtonDelete(), gridBagButtonDelete);
+ jContentPane.add(getJButtonOK(), gridBagButtonOk);
+ jContentPane.add(getJButtonCancel(), gridBagButtonCancel);
+ }
+ return jContentPane;
+ }
+
+ /**
+ * This method initializes jScrollPane
+ *
+ * @return javax.swing.JScrollPane
+ */
+ private JScrollPane getJScrollPane() {
+ if (jScrollPane == null) {
+ jScrollPane = new JScrollPane();
+ jScrollPane.setViewportView(getJList());
+ }
+ return jScrollPane;
+ }
+
+ /**
+ * This method initializes jList
+ *
+ * @return javax.swing.JList
+ */
+ private JList getJList() {
+ if (jList == null) {
+ jList = new JList();
+ }
+ return jList;
+ }
+
+ /**
+ * This method initializes jButtonNew
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonNew() {
+ if (jButtonNew == null) {
+ jButtonNew = new JButton();
+ jButtonNew.setText("New Player");
+ jButtonNew.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String player = JOptionPane.showInputDialog(
+ Core.getCmp(), "Enter Player Name", "Input", JOptionPane.QUESTION_MESSAGE);
+ if (player != null) {
+ // check if this player already exists
+ // it it alread exists, reset the existing profile
+ boolean found= false;
+ for (int pli=0; pli < players.size(); pli++) {
+ if ( players.get(pli).equalsIgnoreCase(player) ) {
+ player = players.get(pli);
+ found = true;
+ break;
+ }
+ }
+ // really a new player
+ if (!found) {
+ players.add(player);
+ jList.setListData(players);
+ int i = players.size()-1;
+ if (i>=0)
+ jList.setSelectedIndex(i);
+ }
+ }
+ }
+ });
+ }
+ return jButtonNew;
+ }
+
+ /**
+ * This method initializes jButtonDelete
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonDelete() {
+ if (jButtonDelete == null) {
+ jButtonDelete = new JButton();
+ jButtonDelete.setText("Delete Player");
+ jButtonDelete.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ int idx =jList.getSelectedIndex();
+ if (idx != -1) {
+ players.remove(idx);
+ jList.setListData(players);
+ }
+ }
+ });
+ }
+ return jButtonDelete;
+ }
+
+ /**
+ * This method initializes jButtonOK
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonOK() {
+ if (jButtonOK == null) {
+ jButtonOK = new JButton();
+ jButtonOK.setText("Ok");
+ jButtonOK.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ dispose();
+ }
+ });
+ }
+ return jButtonOK;
+ }
+
+ /**
+ * This method initializes jButtonCancel
+ *
+ * @return javax.swing.JButton
+ */
+ private JButton getJButtonCancel() {
+ if (jButtonCancel == null) {
+ jButtonCancel = new JButton();
+ jButtonCancel.setText("Cancel");
+ jButtonCancel.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ players.clear();
+ players = null;
+ dispose();
+ }
+ });
+ }
+ return jButtonCancel;
+ }
+
+} // @jve:decl-index=0:visual-constraint="10,10"
diff --git a/jeu-test/Lemmini/0.84/src/Game/Core.java b/jeu-test/Lemmini/0.84/src/Game/Core.java
new file mode 100644
index 0000000..65fbb2b
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Core.java
@@ -0,0 +1,314 @@
+package Game;
+import java.awt.Component;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import Extract.Extract;
+import Extract.ExtractException;
+import GUI.LegalDialog;
+import Tools.Props;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Well, this started as some kind of core class to collect all global stuff
+ * Now lots of the functionality moved to GameController.
+ * Would need some cleaning up, maybe remove the whole thing?
+ * @author Volker Oth
+ */
+public class Core {
+ /** The revision string for resource compatibility - not necessarily the version number */
+ private final static String REVISION = "0.80";
+ /** name of the ini file */
+ private final static String INI_NAME = "lemmings.ini";
+ /** extensions accepted for level files in file dialog */
+ public final static String[] LEVEL_EXTENSIONS = {"ini", "lvl"};
+ /** extensions accepted for replay files in file dialog */
+ public final static String[] REPLAY_EXTENSIONS = {"rpl"};
+
+ /** program properties */
+ public static Props programProps;
+ /** path of (extracted) resources */
+ public static String resourcePath;
+ /** current player */
+ public static Player player;
+
+ /** parent component (main frame) */
+ private static Component cmp;
+ /** name of program properties file */
+ private static String programPropsFileStr;
+ /** name of player properties file */
+ private static String playerPropsFileStr;
+ /** player properties */
+ private static Props playerProps;
+ /** list of all players */
+ private static ArrayList<String> players;
+
+ /**
+ * Initialize some core elements.
+ * @param frame parent frame
+ * @param isWebstartApp true if this was started via Webstart, false otherwise
+ * @throws LemmException
+ */
+ public static void init(final JFrame frame, final boolean isWebstartApp) throws LemmException {
+ // get ini path
+ if (isWebstartApp) {
+ programPropsFileStr = ToolBox.exchangeSeparators(System.getProperty("user.home"));
+ programPropsFileStr = ToolBox.addSeparator(programPropsFileStr);
+ } else {
+ String s = frame.getClass().getName().replace('.','/') + ".class";
+ URL url = frame.getClass().getClassLoader().getResource(s);
+ int pos;
+ try {
+ programPropsFileStr = URLDecoder.decode(url.getPath(),"UTF-8");
+ } catch (UnsupportedEncodingException ex) {};
+ // special handling for JAR
+ if (( (pos=programPropsFileStr.toLowerCase().indexOf("file:")) != -1))
+ programPropsFileStr = programPropsFileStr.substring(pos+5);
+ if ( (pos=programPropsFileStr.toLowerCase().indexOf(s.toLowerCase())) != -1)
+ programPropsFileStr = programPropsFileStr.substring(0,pos);
+
+ /** @todo doesn't work if JAR is renamed...
+ * Maybe it would be a better idea to search only for ".JAR" and then
+ * for the first path separator...
+ */
+
+ s = (frame.getClass().getName().replace('.','/') + ".jar").toLowerCase();
+ if ( (pos=programPropsFileStr.toLowerCase().indexOf(s)) != -1)
+ programPropsFileStr = programPropsFileStr.substring(0,pos);
+ }
+ programPropsFileStr += INI_NAME;
+ // read main ini file
+ programProps = new Props();
+
+ if (!programProps.load(programPropsFileStr)) {// might exist or not - if not, it's created
+ LegalDialog ld = new LegalDialog(null,true);
+ ld.setVisible(true);
+ if (!ld.isOk())
+ throw new LemmException("User abort");
+ }
+
+ resourcePath = programProps.get("resourcePath", "");
+ String sourcePath = programProps.get("sourcePath", "");
+ String rev = programProps.get("revision", "");
+ GameController.setMusicOn(programProps.get("music", false));
+ GameController.setSoundOn(programProps.get("sound", true));
+ double gain;
+ gain = programProps.get("musicGain", 1.0);
+ GameController.setMusicGain(gain);
+ gain = programProps.get("soundGain", 1.0);
+ GameController.setSoundGain(gain);
+ GameController.setAdvancedSelect(programProps.get("advancedSelect", true));
+ if (resourcePath.length()==0 || !REVISION.equalsIgnoreCase(rev)) {
+ // extract resources
+ try {
+ Extract.extract(null, sourcePath, resourcePath, null, "patch");
+ resourcePath = Extract.getResourcePath();
+ programProps.set("resourcePath", ToolBox.addSeparator(Extract.getResourcePath()));
+ programProps.set("sourcePath", ToolBox.addSeparator(Extract.getSourcePath()));
+ programProps.set("revision", REVISION);
+ programProps.save(programPropsFileStr);
+ } catch (ExtractException ex) {
+ programProps.set("resourcePath", ToolBox.addSeparator(Extract.getResourcePath()));
+ programProps.set("sourcePath", ToolBox.addSeparator(Extract.getSourcePath()));
+ programProps.save(programPropsFileStr);
+ throw new LemmException("Ressource extraction failed\n"+ex.getMessage());
+ }
+ }
+ System.gc(); // force garbage collection here before the game starts
+
+ // read player names
+ playerPropsFileStr = Core.resourcePath+"players.ini";
+ playerProps = new Props();
+ playerProps.load(playerPropsFileStr);
+ String defaultPlayer = playerProps.get("defaultPlayer", "default");
+ players = new ArrayList<String>();
+ for (int idx=0; true; idx++) {
+ String p = playerProps.get("player_"+Integer.toString(idx), "");
+ if (p.length() == 0)
+ break;
+ players.add(p);
+ }
+ if (players.size() == 0) {
+ // no players yet, establish default player
+ players.add("default");
+ Core.playerProps.set("player_0", "default");
+ }
+ player = new Player(defaultPlayer);
+
+
+ cmp = frame;
+ }
+
+ /**
+ * Get parent component (main frame).
+ * @return parent component
+ */
+ public static Component getCmp() {
+ return cmp;
+ }
+
+ /**
+ * Get String to resource in resource path.
+ * @param fname file name (without path)
+ * @return absolute path to resource
+ */
+ public static String findResource(final String fname) {
+ return resourcePath+fname;
+ }
+
+ /**
+ * Store program properties.
+ */
+ public static void saveProgramProps() {
+ programProps.save(programPropsFileStr);
+ playerProps.set("defaultPlayer", Core.player.getName());
+ playerProps.save(playerPropsFileStr);
+ player.store();
+ }
+
+ /**
+ * Output error message box in case of a missing resource.
+ * @param rsrc name missing of resource.
+ */
+ public static void resourceError(final String rsrc) {
+ String out = "The resource "+rsrc+" is missing\n" +
+ "Please restart to extract all resources.";
+ JOptionPane.showMessageDialog(null,out,"Error",JOptionPane.ERROR_MESSAGE);
+ // invalidate resources
+ programProps.set("revision", "invalid");
+ programProps.save(programPropsFileStr);
+ System.exit(1);
+ }
+
+ /**
+ * Load an image from the resource path.
+ * @param tracker media tracker
+ * @param fName file name
+ * @return Image
+ * @throws ResourceException
+ */
+ public static Image loadImage(final MediaTracker tracker, final String fName) throws ResourceException {
+ String fileLoc = findResource(fName);
+ if (fileLoc == null)
+ return null;
+ return loadImage(tracker, fileLoc, false);
+ }
+
+ /**
+ * Load an image from either the resource path or from inside the JAR (or the directory of the main class).
+ * @param tracker media tracker
+ * @param fName file name
+ * @param jar true: load from the jar/class path, false: load from resource path
+ * @return Image
+ * @throws ResourceException
+ */
+ private static Image loadImage(final MediaTracker tracker, final String fName, final boolean jar) throws ResourceException {
+ Image image;
+ if (jar)
+ image = Toolkit.getDefaultToolkit().createImage(ToolBox.findFile(fName));
+ else
+ image = Toolkit.getDefaultToolkit().createImage(fName);
+ if (image != null) {
+ tracker.addImage(image, 0);
+ try {
+ tracker.waitForID(0);
+ if (tracker.isErrorAny()) {
+ image = null;
+ }
+ } catch (Exception ex) {
+ image = null;
+ }
+ }
+ if (image == null)
+ throw new ResourceException(fName);
+ return image;
+ }
+
+ /**
+ * Load an image from the resource path.
+ * @param fname file name
+ * @return Image
+ * @throws ResourceException
+ */
+ public static Image loadImage(final String fname) throws ResourceException {
+ MediaTracker tracker = new MediaTracker(Core.getCmp());
+ Image img = loadImage(tracker, fname);
+ if (img == null)
+ throw new ResourceException(fname);
+ return img;
+ }
+
+ /**
+ * Load an image from inside the JAR or the directory of the main class.
+ * @param fname
+ * @return Image
+ * @throws ResourceException
+ */
+ public static Image loadImageJar(final String fname) throws ResourceException {
+ MediaTracker tracker = new MediaTracker(Core.getCmp());
+ Image img = loadImage(tracker, fname, true);
+ if (img == null)
+ throw new ResourceException(fname);
+ return img;
+ }
+
+ /**
+ * Get player name via index.
+ * @param idx player index
+ * @return player name
+ */
+ public static String getPlayer(final int idx) {
+ return players.get(idx);
+ }
+
+ /**
+ * Get number of players.
+ * @return number of player.
+ */
+ public static int getPlayerNum() {
+ if (players == null)
+ return 0;
+ return players.size();
+ }
+
+ /**
+ * Reset list of players.
+ */
+ public static void clearPlayers() {
+ players.clear();
+ playerProps.clear();
+ }
+
+ /**
+ * Add player.
+ * @param name player name
+ */
+ public static void addPlayer(final String name) {
+ players.add(name);
+ playerProps.set("player_"+(players.size()-1), name);
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/Explosion.java b/jeu-test/Lemmini/0.84/src/Game/Explosion.java
new file mode 100644
index 0000000..94a242a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Explosion.java
@@ -0,0 +1,197 @@
+package Game;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handle the nuke/bomber particle explosion.
+ * @author Volker Oth
+ */
+public class Explosion {
+
+ /** number of particles per explosion */
+ private final static int PARTICLE_NUM = 24;
+ /** maximum step width (velocity) in X direction (pixels per step) */
+ private final static double MAX_DX = 1.5;
+ /** minimum step width (velocity) in X direction (pixels per step) */
+ private final static double MIN_DX = -1.5;
+ /** maximum step width (velocity) in Y direction (pixels per step) */
+ private final static double MAX_DY = 1;
+ /** minimum step width (velocity) in Y direction (pixels per step) */
+ private final static double MIN_DY = -4;
+ /** mean life time of a particle (in animation frames) */
+ private final static int LIFE_COUNTER = 64;
+ /** life time variance of a particle (in animation frames) */
+ private final static int LIFE_VARIANCE = 16;
+ /** factor used to simulate gravity (drags particles down) */
+ private final static double GRAVITY = 0.1;
+ /** Remove the explosion bitmaps after REMOVE_IMAGE_CTR animation steps */
+ private final static int REMOVE_IMAGE_CTR = 2;
+
+ /** array of particles */
+ private Particle particles[];
+ /** time/frame counter for explosion */
+ private int counter;
+ /** x position in pixels */
+ private int xExp;
+ /** y position in pixels */
+ private int yExp;
+ /** time/frame position when all particles are vanished */
+ private int maxCounter;
+ /** flag: explosion is finished */
+ private boolean finished;
+ /** explosion image used for the first few frames */
+ private static BufferedImage expImg;
+
+ /**
+ * Load explosion image as static resource.
+ * Mainly outside constructor for easier handling of ResourceException.
+ * @throws ResourceException
+ */
+ static void init() throws ResourceException {
+ expImg = ToolBox.ImageToBuffered(Core.loadImage("misc/explode.gif"), Transparency.BITMASK);
+ }
+
+ /**
+ * Constructor.
+ * @param x x position in pixels.
+ * @param y y position in pixels.
+ */
+ public Explosion(final int x, final int y) {
+ xExp = x - expImg.getWidth()/2;
+ yExp = y - expImg.getHeight()/2;
+ maxCounter = 0;
+ particles = new Particle[PARTICLE_NUM];
+ for (int i=0; i<PARTICLE_NUM; i++) {
+ double dx = (Math.random()*(MAX_DX-MIN_DX)+MIN_DX);
+ double dy = (Math.random()*(MAX_DY-MIN_DY)+MIN_DY);
+ int color = GameController.getLevel().getParticleCol()[(int)(Math.random()*Level.DEFAULT_PARTICLE_COLORS.length)];
+ int lifeCtr = LIFE_COUNTER+(int)(Math.random()*2*LIFE_VARIANCE)-LIFE_VARIANCE;
+ if (lifeCtr > maxCounter)
+ maxCounter = lifeCtr;
+ particles[i] = new Particle(x,y,dx,dy,color,lifeCtr);
+ }
+ counter = 0;
+ finished = false;
+ }
+
+ /**
+ * Update explosion (move particles etc.).
+ */
+ public void update() {
+ for (int i=0; i<PARTICLE_NUM; i++) {
+ Particle p = particles[i];
+ if (p != null) {
+ // calculate new position
+ p.x += p.dx;
+ p.y += p.dy + counter*GRAVITY;
+ // check life counter
+ if (p.lifeCtr >0)
+ p.lifeCtr--;
+ else
+ particles[i] = null;
+ }
+ }
+ if (++counter > maxCounter)
+ finished = true;
+ }
+
+ /**
+ * Draw explosion on graphics object.
+ * @param g
+ * @param width
+ * @param height
+ * @param xOfs
+ */
+ public void draw(final Graphics2D g, final int width, final int height, final int xOfs) {
+ if (!finished) {
+ int maxY = height-1;
+ int maxX = width-1;
+ // draw explosion bitmap
+ if (counter < REMOVE_IMAGE_CTR) {
+ int x = xExp-xOfs;
+ if (x>0 && x<maxX)
+ g.drawImage(expImg, xExp-xOfs, yExp, null);
+ }
+ // draw particles
+ for (int i=0; i<PARTICLE_NUM; i++) {
+ Particle p = particles[i];
+ if (p != null) {
+ // draw
+ int x = (int)p.x - xOfs;
+ int y = (int)p.y;
+ if (x>0 && x < maxX-1 && y>0 && y < maxY-1) {
+ g.setColor(p.color);
+ g.fillRect(x,y,2,2);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get finished state.
+ * @return true if the explosion is over, false otherwise
+ */
+ public boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * Storage class for a particle.
+ * @author Volker Oth
+ */
+ private class Particle {
+ /** x position in pixels */
+ double x;
+ /** y position in pixels */
+ double y;
+ /** x step width (velocity) in pixels per step */
+ double dx;
+ /** y step width (velocity) in pixels per step */
+ double dy;
+ /** particle color */
+ Color color;
+ /** life counter in steps (counting down) */
+ int lifeCtr;
+
+ /**
+ * Constructor
+ * @param x0 initial x position in pixels
+ * @param y0 initial y position in pixels
+ * @param dx0 x step width (velocity) in pixels per step
+ * @param dy0 y step width (velocity) in pixels per step
+ * @param col particle color
+ * @param lCtr life counter in steps (counting down)
+ */
+ Particle(int x0, int y0, double dx0, double dy0, int col, int lCtr) {
+ x = x0;
+ y = y0;
+ dx = dx0;
+ dy = dy0;
+ color = new Color(col);
+ lifeCtr = lCtr;
+ }
+ }
+
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/GameController.java b/jeu-test/Lemmini/0.84/src/Game/GameController.java
new file mode 100644
index 0000000..f37f97d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/GameController.java
@@ -0,0 +1,1733 @@
+package Game;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+
+import GameUtil.Fader;
+import GameUtil.KeyRepeat;
+import GameUtil.Sound;
+import GameUtil.Sprite;
+import Tools.MicrosecondTimer;
+import Tools.ToolBox;
+
+
+/**
+ * Game controller. Contains all the game logic.
+ * @author Volker Oth
+ */
+public class GameController {
+ /** game state */
+ public static enum State {
+ /** init state */
+ INIT,
+ /** display intro screen */
+ INTRO,
+ /** display level briefing screen */
+ BRIEFING,
+ /** display level */
+ LEVEL,
+ /** display debriefing screen */
+ DEBRIEFING,
+ /** fade out after level was finished */
+ LEVEL_END
+ }
+
+ /** Transition states */
+ public static enum TransitionState {
+ /** no fading */
+ NONE,
+ /** restart level: fade out, fade in briefing */
+ RESTART_LEVEL,
+ /** replay level: fade out, fade in briefing */
+ REPLAY_LEVEL,
+ /** load level: fade out, fade in briefing */
+ LOAD_LEVEL,
+ /** load replay: fade out, fade in briefing */
+ LOAD_REPLAY,
+ /** level finished: fade out */
+ END_LEVEL,
+ /** go to intro: fade in intro */
+ TO_INTRO,
+ /** go to briefing: fade in briefing */
+ TO_BRIEFING,
+ /** go to debriefing: fade in debriefing */
+ TO_DEBRIEFING,
+ /** go to level: fade in level */
+ TO_LEVEL
+ }
+
+ /** key repeat bitmask for icons */
+ public final static int KEYREPEAT_ICON = 1;
+ /** key repeat bitmask for keys */
+ public final static int KEYREPEAT_KEY = 2;
+
+ /** bang sound */
+ public final static int SND_BANG = 0;
+ /** brick wheel trap sound */
+ public final static int SND_CHAIN = 1;
+ /** setting new skill sound */
+ public final static int SND_CHANGE_OP = 2;
+ /** only some builder steps left sound */
+ public final static int SND_CHINK = 3;
+ /** dieing sound */
+ public final static int SND_DIE = 4;
+ /** trap door opening sound */
+ public final static int SND_DOOR = 5;
+ /** electric sound */
+ public final static int SND_ELECTRIC = 6;
+ /** explode sound */
+ public final static int SND_EXPLODE = 7;
+ /** fire sound */
+ public final static int SND_FIRE = 8;
+ /** drowning sound */
+ public final static int SND_GLUG = 9;
+ /** start of level sound */
+ public final static int SND_LETSGO = 10;
+ /** bear/twiner trap sound */
+ public final static int SND_MANTRAP = 11;
+ /** mouse clicked sound */
+ public final static int SND_MOUSEPRE = 12;
+ /** nuke command sound */
+ public final static int SND_OHNO = 13;
+ /** leaving exit sound */
+ public final static int SND_OING = 14;
+ /** scrape sound */
+ public final static int SND_SCRAPE = 15;
+ /** slicer sound */
+ public final static int SND_SLICER = 16;
+ /** splash sound */
+ public final static int SND_SPLASH = 17;
+ /** faller splat sound */
+ public final static int SND_SPLAT = 18;
+ /** ten tons sound, also pipe sucking lemmings in */
+ public final static int SND_TEN_TONS = 19;
+ /** icycle, brick stamper sound */
+ public final static int SND_THUD = 20;
+ /** thunk sound */
+ public final static int SND_THUNK = 21;
+ /** ting sound */
+ public final static int SND_TING = 22;
+ /** yipee sound */
+ public final static int SND_YIPEE = 23;
+
+ /** updates 5 frames instead of 1 in fast forward mode */
+ public final static int FAST_FWD_MULTI = 5;
+ /** updates 3 frames instead of 1 in Superlemming mode */
+ public final static int SUPERLEMM_MULTI = 3;
+ /** time per frame in microseconds - this is the timing everything else is based on */
+ public final static int MICROSEC_PER_FRAME = 30*1000;
+ /** resync if time difference greater than that (in microseconds)*/
+ public final static int MICROSEC_RESYNC = 5*30*1000;
+
+ /** redraw animated level obejcts every 3rd frame (about 100ms) */
+ private final static int MAX_ANIM_CTR = 100*1000/MICROSEC_PER_FRAME;
+ /** open Entry after about 1.5 seconds */
+ private final static int MAX_ENTRY_OPEN_CTR = 1500*1000/MICROSEC_PER_FRAME;
+ /** one second is 33.33 ticks (integer would cause error) */
+ private final static double MAX_SECOND_CTR = 1000.0*1000/MICROSEC_PER_FRAME;
+ /** maximum release rate */
+ private final static int MAX_RELEASE_RATE = 99;
+
+ /** nuke icon: maximum time between two mouse clicks for double click detection (in microseconds) */
+ private final static long MICROSEC_NUKE_DOUBLE_CLICK = 240*1000;
+ /** +/- icons: maximum time between two mouse clicks for double click detection (in microseconds) */
+ private final static long MICROSEC_RELEASE_DOUBLE_CLICK = 200*1000;
+ /** +/- icons: time for key repeat to kick in */
+ private final static long MICROSEC_KEYREPEAT_START = 250*1000;
+ /** +/- icons: time for key repeat rate */
+ private final static long MICROSEC_KEYREPEAT_REPEAT = 67*1000;
+
+ /** sound object */
+ public static Sound sound;
+
+ /** the background stencil */
+ private static Stencil stencil;
+ /** the background image */
+ private static BufferedImage bgImage;
+ /** flag: play music */
+ private static boolean musicOn;
+ /** flag: play sounds */
+ private static boolean soundOn;
+ /** flag: use advanced mouse selection methods */
+ private static boolean advancedSelect;
+ /** graphics object for the background image */
+ private static Graphics2D bgGfx;
+ /** color used to erase the background (black) */
+ private static Color blankColor = new Color(0xff,0,0,0);
+ /** flag: fast forward mode is active */
+ private static boolean fastForward;
+ /** flag: Superlemming mode is active */
+ private static boolean superLemming;
+ /** game state */
+ private static State gameState;
+ /** transition (fading) state */
+ private static TransitionState transitionState;
+ /** skill to assign to lemming (skill icon) */
+ private static Lemming.Type lemmSkill;
+ /** flag: entry is openend */
+ private static boolean entryOpened;
+ /** flag: nuke was acticated */
+ private static boolean nuke;
+ /** flag: game is paused */
+ private static boolean paused;
+ /** flag: cheat/debug mode is activated */
+ private static boolean cheat = false;
+ /** flag: cheat mode was activated during play */
+ private static boolean wasCheated = false;
+ /** frame counter for handling opening of entries */
+ private static int entryOpenCtr;
+ /** frame counter for handling time */
+ private static double secondCtr;
+ /** frame counter used to handle release of new Lemmings */
+ private static int releaseCtr;
+ /** threshold to release a new Lemming */
+ private static int releaseBase;
+ /** frame counter used to update animated sprite objects */
+ private static int animCtr;
+ /** level object */
+ private static Level level;
+ /** index of current difficulty level */
+ private static int curDiffLevel;
+ /** index of current level pack */
+ private static int curLevelPack;
+ /** index of current level */
+ private static int curLevelNumber;
+ /** index of next difficulty level */
+ private static int nextDiffLevel;
+ /** index of next level pack */
+ private static int nextLevelPack;
+ /** index of next level */
+ private static int nextLevelNumber;
+ /** list of all active Lemmings in the Level */
+ private static LinkedList<Lemming> lemmings;
+ /** list of all active explosions */
+ private static LinkedList<Explosion> explosions;
+ /** list of all Lemmings under the mouse cursor */
+ private static ArrayList<Lemming> lemmsUnderCursor;
+ /** array of available level packs */
+ private static LevelPack levelPack[];
+ /** small preview version of level used in briefing screen */
+ private static BufferedImage mapPreview;
+ /** timer used for nuking */
+ private static MicrosecondTimer timerNuke;
+ /** key repeat object for plus key/icon */
+ private static KeyRepeat plus;
+ /** key repeat object for minus key/icon */
+ private static KeyRepeat minus;
+ /** Lemming for which skill change is requested */
+ private static Lemming lemmSkillRequest;
+ /** horizontal scrolling offset for level */
+ private static int xPos;
+ /** replay stream used for handling replays */
+ private static ReplayStream replay;
+ /** frame counter used for handling replays */
+ private static int replayFrame;
+ /** old value of release rate */
+ private static int releaseRateOld;
+ /** old value of nuke flag */
+ private static boolean nukeOld;
+ /** old value of horizontal scrolling position */
+ private static int xPosOld;
+ /** old value of selected skill */
+ private static Lemming.Type lemmSkillOld;
+ /** flag: replay mode is active */
+ private static boolean replayMode;
+ /** flag: replay mode should be stopped */
+ private static boolean stopReplayMode;
+ /** listener to inform GUI of player's progress */
+ private static UpdateListener levelMenuUpdateListener;
+ /** number of Lemmings which left the level */
+ private static int numLeft;
+ /** release rate 0..99 */
+ private static int releaseRate;
+ /** number of Lemmings available */
+ private static int numLemmingsMax;
+ /** number of Lemmings who entered the level */
+ private static int numLemmingsOut;
+ /** number of Lemmings which have to be rescued to finish the level */
+ private static int numToRecue;
+ /** time left in seconds */
+ private static int time;
+ /** number of climber skills left to be assigned */
+ private static int numClimbers;
+ /** number of floater skills left to be assigned */
+ private static int numFloaters;
+ /** number of bomber skills left to be assigned */
+ private static int numBombers;
+ /** number of blocker skills left to be assigned */
+ private static int numBlockers;
+ /** number of builder skills left to be assigned */
+ private static int numBuilders;
+ /** number of basher skills left to be assigned */
+ private static int numBashers;
+ /** number of miner skills left to be assigned */
+ private static int numMiners;
+ /** number of digger skills left to be assigned */
+ private static int numDiggers;
+ /** free running update counter */
+ private static int updateCtr;
+ /** gain for sound 0..1.0 */
+ private static double soundGain = 1.0;
+ /** gain for music 0..1.0 */
+ private static double musicGain = 1.0;
+
+ /**
+ * Initialization.
+ * @throws ResourceException
+ */
+ public static void init() throws ResourceException {
+ bgImage = ToolBox.createImage(Level.WIDTH, Level.HEIGHT, Transparency.BITMASK);
+ bgGfx = bgImage.createGraphics();
+
+ gameState = State.INIT;
+ sound = new Sound(24,SND_MOUSEPRE);
+ sound.setGain(soundGain);
+ Icons.init(Core.getCmp());
+ Explosion.init();
+ Lemming.loadLemmings(Core.getCmp());
+ lemmings = new LinkedList<Lemming>();
+ explosions = new LinkedList<Explosion>();
+ lemmsUnderCursor = new ArrayList<Lemming>(10);
+ lemmSkillRequest = null;
+
+ LemmFont.init();
+ NumFont.init();
+ LemmCursor.init();
+ Music.init();
+ Music.setGain(musicGain);
+ MiscGfx.init();
+
+ plus = new KeyRepeat(MICROSEC_KEYREPEAT_START, MICROSEC_KEYREPEAT_REPEAT, MICROSEC_RELEASE_DOUBLE_CLICK);
+ minus = new KeyRepeat(MICROSEC_KEYREPEAT_START, MICROSEC_KEYREPEAT_REPEAT, MICROSEC_RELEASE_DOUBLE_CLICK);
+ timerNuke = new MicrosecondTimer();
+
+ level = new Level();
+ // read level packs
+
+ File dir = new File(Core.resourcePath+"levels");
+ File files[] = dir.listFiles();
+ // now get the names of the directories
+ ArrayList<String> dirs = new ArrayList<String>();
+ for (int i=0; i<files.length; i++)
+ if (files[i].isDirectory())
+ dirs.add(files[i].getName());
+ Collections.sort(dirs);
+
+ levelPack = new LevelPack[dirs.size()+1];
+ levelPack[0] = new LevelPack(); // dummy
+ for (int i=0; i<dirs.size(); i++) { // read levels
+ String lvlName = dirs.get(i);
+ levelPack[i+1] = new LevelPack(Core.findResource("levels/"+ToolBox.addSeparator(lvlName)+"levelpack.ini"));
+ }
+ curDiffLevel = 0;
+ curLevelPack = 1; // since 0 is dummy
+ curLevelNumber = 0;
+
+ replayFrame = 0;
+ replay = new ReplayStream();
+ replayMode = false;
+ stopReplayMode = false;
+
+ if (isCheat())
+ wasCheated = true;
+ else
+ wasCheated = false;
+ }
+
+ /**
+ * Calculate absolute level number from diff level and relative level number
+ * @param lvlPack level pack
+ * @param diffLevel difficulty level
+ * @param level relative level number
+ * @return absolute level number (0..127)
+ */
+ static int absLevelNum(final int lvlPack, final int diffLevel, final int level) {
+ LevelPack lpack = levelPack[lvlPack];
+ // calculate absolute level number
+ int absLvl = level;
+ for (int i=0; i<diffLevel; i++)
+ absLvl += lpack.getLevels(i).length;
+ return absLvl;
+ }
+
+ /**
+ * Calculate diffLevel and relative level number from absolute level number
+ * @param lvlPack level pack
+ * @param lvlAbs absolute level number
+ * @return { difficulty level, relative level number }
+ */
+ public static int[] relLevelNum(final int lvlPack, final int lvlAbs) {
+ int retval[] = new int[2];
+ LevelPack lpack = levelPack[lvlPack];
+ int diffLevels = lpack.getDiffLevels().length;
+ int lvl=0;
+ int diffLvl=0;
+ int maxLevels=30;
+ for (int i=0, ls = 0; i<diffLevels; i++) {
+ int ls_old = ls;
+ // add number of levels existing in this diff level
+ maxLevels = lpack.getLevels(i).length;
+ ls += maxLevels;
+ if (lvlAbs < ls) {
+ diffLvl = i;
+ lvl = lvlAbs - ls_old; // relative level mumber
+ break;
+ }
+ }
+ retval[0] = diffLvl;
+ retval[1] = lvl;
+ return retval;
+ }
+
+ /**
+ * Proceed to next level.
+ * @return true: ok, false: no more level in this difficulty level
+ */
+ public static synchronized boolean nextLevel() {
+ int num = curLevelNumber + 1;
+
+ if ( num < levelPack[curLevelPack].getLevels(curDiffLevel).length ) {
+ curLevelNumber = num;
+ return true;
+ } else
+ return false; // congrats - difficulty level done
+ }
+
+ /**
+ * Fade out at end of level.
+ */
+ public static synchronized void endLevel() {
+ transitionState = TransitionState.END_LEVEL;
+ gameState = State.LEVEL_END;
+ Fader.setState(Fader.State.OUT);
+ }
+
+ /**
+ * Level successfully finished, enter debriefing and tell GUI to enable next level.
+ */
+ static synchronized void finishLevel() {
+ Music.stop();
+ setFastForward(false);
+ setSuperLemming(false);
+ replayMode = false;
+
+ if (!wasLost()) {
+ if (curLevelPack!=0)
+ levelMenuUpdateListener.update();
+ // String pack = levelPack[curLevelPack].getName();
+ // String diff = levelPack[curLevelPack].getDiffLevels()[curDiffLevel];
+ // // get next level
+ // int num = curLevelNumber + 1;
+ // if ( num >= levelPack[curLevelPack].getLevels(curDiffLevel).length )
+ // num = curLevelNumber;
+ // // set next level as available
+ // GroupBitfield bf = Core.player.setAvailable(pack, diff, num);
+ // // update the menu
+ // if (curLevelPack!=0) // 0 is the dummy pack
+ // ((Lemmini)Core.getCmp()).updateLevelMenu(pack, diff, bf);
+ }
+ gameState = State.DEBRIEFING;
+ }
+
+ /**
+ * Hook for GUI to get informed when a level was successfully finished.
+ * @param l UpdateListener
+ */
+ public static void setLevelMenuUpdateListener(final UpdateListener l) {
+ levelMenuUpdateListener = l;
+ }
+
+ /**
+ * Restart level.
+ * @param doReplay true: replay, false: play
+ */
+ private static synchronized void restartLevel(final boolean doReplay) {
+ initLevel();
+ if (doReplay) {
+ replayMode = true;
+ replay.save(Core.resourcePath+"/replay.rpl");
+ replay.rewind();
+ } else {
+ replayMode = false;
+ replay.clear();
+ }
+ }
+
+ /**
+ * Initialize a level after it was loaded.
+ */
+ private static void initLevel() {
+ Music.stop();
+
+ setFastForward(false);
+ setPaused(false);
+ nuke = false;
+
+ lemmSkillRequest = null;
+
+ TextScreen.setMode(TextScreen.Mode.INIT);
+
+ bgGfx.setBackground(blankColor);
+ bgGfx.clearRect(0, 0, bgImage.getWidth(), bgImage.getHeight());
+
+ stencil = getLevel().paintLevel(bgImage, Core.getCmp(), stencil);
+
+ lemmings.clear();
+ explosions.clear();
+ Icons.reset();
+
+ TrapDoor.reset(getLevel().getEntryNum());
+ entryOpened = false;
+ entryOpenCtr = 0;
+ secondCtr = 0;
+ releaseCtr = 0;
+ lemmSkill = Lemming.Type.UNDEFINED;
+
+ plus.init();
+ minus.init();
+
+ numLeft = 0;
+ releaseRate = getLevel().getReleaseRate();
+ numLemmingsMax = getLevel().getNumLemmings();
+ numLemmingsOut = 0;
+ numToRecue = getLevel().getNumToRescue();
+ time = getLevel().getTimeLimitSeconds();
+ numClimbers = getLevel().getNumClimbers();
+ numFloaters = getLevel().getNumFloaters();
+ numBombers = getLevel().getNumBombers();
+ numBlockers = getLevel().getNumBlockers();
+ numBuilders = getLevel().getNumBuilders();
+ numBashers = getLevel().getNumBashers();
+ numMiners = getLevel().getNumMiners();
+ numDiggers = getLevel().getMumDiggers();
+ setxPos(getLevel().getXpos());
+
+ calcReleaseBase();
+
+ mapPreview = getLevel().createMiniMap(mapPreview, bgImage, 4, 4, false);
+
+ setSuperLemming(getLevel().isSuperLemming());
+
+ replayFrame = 0;
+ stopReplayMode = false;
+ releaseRateOld = releaseRate;
+ lemmSkillOld = lemmSkill;
+ nukeOld = false;
+ xPosOld = getLevel().getXpos();
+
+ gameState = State.BRIEFING;
+ }
+
+ /**
+ * Request the restart of this level.
+ * @param doReplay
+ */
+ public static synchronized void requestRestartLevel(final boolean doReplay) {
+ if (doReplay)
+ transitionState = TransitionState.REPLAY_LEVEL;
+ else
+ transitionState = TransitionState.RESTART_LEVEL;
+ Fader.setState(Fader.State.OUT);
+ }
+
+ /**
+ * Request a new level.
+ * @param lPack index of level pack
+ * @param dLevel index of difficulty level
+ * @param lNum level number
+ * @param doReplay true: replay, false: play
+ */
+ public static synchronized void requestChangeLevel(final int lPack, final int dLevel, final int lNum, final boolean doReplay) {
+ nextLevelPack = lPack;
+ nextDiffLevel = dLevel;
+ nextLevelNumber = lNum;
+
+ if (doReplay)
+ transitionState = TransitionState.LOAD_REPLAY;
+ else
+ transitionState = TransitionState.LOAD_LEVEL;
+ Fader.setState(Fader.State.OUT);
+ }
+
+ /**
+ * Start a new level.
+ * @param lPack index of level pack
+ * @param dLevel index of difficulty level
+ * @param lNum level number
+ * @param doReplay true: replay, false: play
+ */
+ static private synchronized Level changeLevel(final int lPack, final int dLevel, final int lNum, final boolean doReplay) throws ResourceException, LemmException {
+ //gameState = GAME_ST_INIT;
+ curLevelPack = lPack;
+ curDiffLevel = dLevel;
+ curLevelNumber = lNum;
+
+ String lvlPath = levelPack[curLevelPack].getInfo(curDiffLevel, curLevelNumber).getFileName();
+ // lemmings need to be reloaded to contain pink color
+ Lemming.loadLemmings(Core.getCmp());
+ // loading the level will patch pink lemmings pixels to correct color
+ getLevel().loadLevel(lvlPath);
+
+ // if with and height would be stored inside the level, the bgImage etc. would have to
+ // be recreated here
+ // bgImage = gc.createCompatibleImage(Level.width, Level.height, Transparency.BITMASK);
+ // bgGfx = bgImage.createGraphics();
+
+ initLevel();
+
+ if (doReplay) {
+ replayMode = true;
+ replay.rewind();
+ } else {
+ replayMode = false;
+ replay.clear();
+ }
+
+ return getLevel();
+ }
+
+ /**
+ * Get level lost state.
+ * @return true if level was lost, false otherwise
+ */
+ static synchronized boolean wasLost() {
+ if (gameState != State.LEVEL && numLeft >= numToRecue)
+ return false;
+ return true;
+ }
+
+ /**
+ * Get current replay image.
+ * @return current replay image
+ */
+ public synchronized static BufferedImage getReplayImage() {
+ if (!replayMode)
+ return null;
+ if ( (replayFrame & 0x3f) > 0x20)
+ return MiscGfx.getImage(MiscGfx.Index.REPLAY_1);
+ else
+ return MiscGfx.getImage(MiscGfx.Index.REPLAY_2);
+ }
+
+ /**
+ * Get a Lemming under the selection cursor.
+ * @param type cursor type
+ * @return fitting Lemming or null if none found
+ */
+ public static synchronized Lemming lemmUnderCursor(final LemmCursor.Type type) {
+ // search for level without the skill
+ for (int i=0; i<getLemmsUnderCursor().size(); i++) {
+ Lemming l = getLemmsUnderCursor().get(i);
+ // Walker only cursor: ignore non-walkers
+ if (type == LemmCursor.Type.WALKER && l.getSkill()!=Lemming.Type.WALKER)
+ continue;
+ if (type == LemmCursor.Type.LEFT && l.getDirection() != Lemming.Direction.LEFT)
+ continue;
+ if (type == LemmCursor.Type.RIGHT && l.getDirection() != Lemming.Direction.RIGHT)
+ continue;
+ switch (lemmSkill) {
+ case CLIMBER:
+ if (!l.canClimb())
+ return l;
+ break;
+ case FLOATER:
+ if (!l.canFloat())
+ return l;
+ break;
+ default:
+ if (l.canChangeSkill() && l.getSkill() != lemmSkill && l.getName().length() > 0) {
+ //System.out.println(l.getName());
+ return l;
+ }
+ }
+ break;
+ }
+ if (type == LemmCursor.Type.NORMAL && getLemmsUnderCursor().size() > 0) {
+ Lemming l = getLemmsUnderCursor().get(0);
+ if (l.getName().length() == 0)
+ return null;
+ //System.out.println(((Lemming)lemmsUnderCursor.get(0)).getName());
+ return l;
+ }
+ return null;
+ }
+
+ /**
+ * Lemming has left the Level.
+ */
+ static synchronized void increaseLeft() {
+ numLeft += 1;
+ }
+
+ /**
+ * Stop replay.
+ */
+ private static void stopReplayMode() {
+ if (replayMode)
+ stopReplayMode = true;
+ }
+
+ /**
+ * Return time as String "minutes-seconds"
+ * @return time as String "minutes-seconds"
+ */
+ public synchronized static String getTimeString() {
+ String t1 = Integer.toString(time/60);
+ String t2 = Integer.toString(time%60);
+ if (t2.length() < 2)
+ t2 = "0"+t2;
+ return t1 + "-" + t2;
+ }
+
+ /**
+ * Update the whole game state by one frame.
+ */
+ public static synchronized void update() {
+ if (gameState != State.LEVEL)
+ return;
+
+ updateCtr++;
+
+ if (!replayMode)
+ assignSkill(false); // first try to assign skill
+
+ // check +/- buttons also if paused
+ KeyRepeat.Event fired = plus.fired();
+ if (fired != KeyRepeat.Event.NONE) {
+ if (releaseRate < MAX_RELEASE_RATE) {
+ if (fired == KeyRepeat.Event.DOUBLE_CLICK)
+ releaseRate = MAX_RELEASE_RATE;
+ else
+ releaseRate += 1;
+ calcReleaseBase();
+ sound.playPitched(releaseRate);
+ } else sound.play(SND_TING);
+ }
+
+ fired = minus.fired();
+ if (fired != KeyRepeat.Event.NONE) {
+ if (releaseRate > getLevel().getReleaseRate()) {
+ if (fired == KeyRepeat.Event.DOUBLE_CLICK)
+ releaseRate = getLevel().getReleaseRate();
+ else
+ releaseRate -= 1;
+ calcReleaseBase();
+ sound.playPitched(releaseRate);
+ } else sound.play(SND_TING);
+ }
+
+
+ if (isPaused())
+ return;
+
+ // test for end of replay mode
+ if ( replayMode && stopReplayMode ) {
+ replay.clearFrom(replayFrame);
+ replayMode = false;
+ stopReplayMode = false;
+ }
+
+ if (!replayMode) {
+ if (!wasCheated) {
+ // replay: release rate changed?
+ if (releaseRate != releaseRateOld) {
+ replay.addReleaseRateEvent(replayFrame, releaseRate);
+ releaseRateOld = releaseRate;
+ }
+ // replay: nuked?
+ if (nuke != nukeOld) {
+ replay.addNukeEvent(replayFrame);
+ nukeOld = nuke;
+ }
+ // replay: xPos changed?
+ if (getxPos() != xPosOld) {
+ replay.addXPosEvent(replayFrame, getxPos());
+ xPosOld = getxPos();
+ }
+ // skill changed
+ if (lemmSkill != lemmSkillOld) {
+ replay.addSelectSkillEvent(replayFrame, lemmSkill);
+ lemmSkillOld = lemmSkill;
+ }
+ } else replay.clear();
+ } else {
+ // replay mode
+ ReplayEvent r;
+ while ( (r=replay.getNext(replayFrame)) != null) {
+ switch (r.type) {
+ case ReplayStream.ASSIGN_SKILL: {
+ ReplayAssignSkillEvent rs = (ReplayAssignSkillEvent)r;
+ synchronized (lemmings) {
+ Lemming l = lemmings.get(rs.lemming);
+ l.setSkill(rs.skill);
+ l.setSelected();
+ }
+ switch (rs.skill) {
+ case FLOATER:
+ numFloaters -= 1;
+ break;
+ case CLIMBER:
+ numClimbers -= 1;
+ break;
+ case BOMBER:
+ numBombers -= 1;
+ break;
+ case DIGGER:
+ numDiggers -= 1;
+ break;
+ case BASHER:
+ numBashers -= 1;
+ break;
+ case BUILDER:
+ numBuilders -= 1;
+ break;
+ case MINER:
+ numMiners -= 1;
+ break;
+ case STOPPER:
+ numBlockers -= 1;
+ break;
+ }
+ sound.play(SND_CHANGE_OP);
+ break;
+ }
+ case ReplayStream.SET_RELEASE_RATE:
+ ReplayReleaseRateEvent rr = (ReplayReleaseRateEvent)r;
+ releaseRate = rr.releaseRate;
+ calcReleaseBase();
+ sound.playPitched(releaseRate);
+ break;
+ case ReplayStream.NUKE:
+ nuke = true;
+ break;
+ case ReplayStream.MOVE_XPOS: {
+ ReplayMoveXPosEvent rx = (ReplayMoveXPosEvent)r;
+ setxPos(rx.xPos);
+ break;
+ }
+ case ReplayStream.SELECT_SKILL: {
+ ReplaySelectSkillEvent rs = (ReplaySelectSkillEvent)r;
+ lemmSkill = rs.skill;
+ switch (lemmSkill) {
+ case FLOATER:
+ Icons.press(Icons.Type.FLOAT);
+ break;
+ case CLIMBER:
+ Icons.press(Icons.Type.CLIMB);
+ break;
+ case BOMBER:
+ Icons.press(Icons.Type.BOMB);
+ break;
+ case DIGGER:
+ Icons.press(Icons.Type.DIG);
+ break;
+ case BASHER:
+ Icons.press(Icons.Type.BASH);
+ break;
+ case BUILDER:
+ Icons.press(Icons.Type.BUILD);
+ break;
+ case MINER:
+ Icons.press(Icons.Type.MINE);
+ break;
+ case STOPPER:
+ Icons.press(Icons.Type.BLOCK);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // replay: xpos changed
+
+ // store locally to avoid it's overwritten amidst function
+ boolean nukeTemp = nuke;
+
+ // time
+ secondCtr+=1.0;
+ if (secondCtr > MAX_SECOND_CTR) {
+ // one second passed
+ secondCtr -= MAX_SECOND_CTR;
+ time--;
+ if (!isCheat() && time == 0) {
+ // level failed
+ endLevel();
+ }
+ }
+ // release
+ if (entryOpened && !nukeTemp && !isPaused() && numLemmingsOut < getNumLemmingsMax() && ++releaseCtr >= releaseBase) {
+ releaseCtr = 0;
+ //LemmingResource ls = Lemming.getResource(Lemming.TYPE_FALLER);
+ try {
+ if (getLevel().getEntryNum() != 0) {
+ Entry e = getLevel().getEntry(TrapDoor.getNext());
+ Lemming l = new Lemming(e.xPos+2, e.yPos+20);
+ synchronized (lemmings) {
+ lemmings.add(l);
+ }
+ numLemmingsOut++;
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {}
+ }
+ // nuking
+ if (nukeTemp && ((updateCtr&1)==1)) {
+ synchronized (lemmings) {
+ Iterator<Lemming> it = lemmings.iterator();
+ while (it.hasNext()) {
+ Lemming l = it.next();
+ if (!l.nuke() && !l.hasDied() && !l.hasLeft()) {
+ l.setSkill(Lemming.Type.NUKE);
+ //System.out.println("nuked!");
+ break;
+ }
+ }
+ }
+ }
+ // open trap doors ?
+ if (!entryOpened) {
+ if (++entryOpenCtr == MAX_ENTRY_OPEN_CTR) {
+ for (int i=0; i<getLevel().getEntryNum(); i++)
+ getLevel().getSprObject(getLevel().getEntry(i).id).setAnimMode(Sprite.Animation.ONCE);
+ sound.play(SND_DOOR);
+ } else if (entryOpenCtr == MAX_ENTRY_OPEN_CTR+10*MAX_ANIM_CTR) {
+ //System.out.println("opened");
+ entryOpened = true;
+ releaseCtr = releaseBase; // first lemming to enter at once
+ if (musicOn)
+ Music.play();
+ }
+ }
+ // end of game conditions
+ if ((nukeTemp || numLemmingsOut == getNumLemmingsMax()) && explosions.size()==0 && lemmings.size()==0) {
+ endLevel();
+ }
+
+ synchronized (lemmings) {
+ Iterator<Lemming> it = lemmings.iterator();
+ while (it.hasNext()) {
+ Lemming l = it.next();
+ if (l.hasDied() || l.hasLeft()) {
+ it.remove();
+ continue;
+ }
+ l.animate();
+ }
+ }
+
+ synchronized (explosions) {
+ Iterator<Explosion> it = explosions.iterator();
+ while (it.hasNext()) {
+ Explosion e = it.next();
+ if (e.isFinished())
+ it.remove();
+ else
+ e.update();
+ }
+ }
+
+ // animate level objects
+ if (++animCtr > MAX_ANIM_CTR) {
+ animCtr -= MAX_ANIM_CTR;
+ for (int n=0; n<getLevel().getSprObjectNum(); n++) {
+ SpriteObject spr = getLevel().getSprObject(n);
+ spr.getImageAnim(); // just to animate
+ }
+ }
+
+ if (!replayMode)
+ assignSkill(true); // 2nd try to assign skill
+
+ replayFrame ++;
+ }
+
+ /**
+ * Request a skill change for a Lemming (currently selected skill).
+ * @param lemm Lemming
+ */
+ public synchronized static void requestSkill(final Lemming lemm) {
+ if (lemmSkill != Lemming.Type.UNDEFINED)
+ lemmSkillRequest = lemm;
+ stopReplayMode();
+ }
+
+ /**
+ * Assign the selected skill to the selected Lemming.
+ * @param delete flag: reset the current skill request
+ */
+ private synchronized static void assignSkill(final boolean delete) {
+ if (lemmSkillRequest == null || lemmSkill == Lemming.Type.UNDEFINED)
+ return;
+
+ Lemming lemm = lemmSkillRequest;
+ if (delete)
+ lemmSkillRequest = null;
+
+ boolean canSet = false;
+ stopReplayMode();
+
+ if (isCheat()) {
+ canSet = lemm.setSkill(lemmSkill);
+ } else {
+ switch (lemmSkill) {
+ case BASHER:
+ if (numBashers > 0 && lemm.setSkill(lemmSkill)) {
+ numBashers -= 1;
+ canSet = true;
+ }
+ break;
+ case BOMBER:
+ if (numBombers > 0 && lemm.setSkill(lemmSkill)) {
+ numBombers -= 1;
+ canSet = true;
+ }
+ break;
+ case BUILDER:
+ if (numBuilders > 0 && lemm.setSkill(lemmSkill)) {
+ numBuilders -= 1;
+ canSet = true;
+ }
+ break;
+ case CLIMBER:
+ if (numClimbers > 0 && lemm.setSkill(lemmSkill)) {
+ numClimbers -= 1;
+ canSet = true;
+ }
+ break;
+ case DIGGER:
+ if (numDiggers > 0 && lemm.setSkill(lemmSkill)) {
+ numDiggers -= 1;
+ canSet = true;
+ }
+ break;
+ case FLOATER:
+ if (numFloaters > 0 && lemm.setSkill(lemmSkill)) {
+ numFloaters -= 1;
+ canSet = true;
+ }
+ break;
+ case MINER:
+ if (numMiners > 0 && lemm.setSkill(lemmSkill)) {
+ numMiners -= 1;
+ canSet = true;
+ }
+ break;
+ case STOPPER:
+ if (numBlockers > 0 && lemm.setSkill(lemmSkill)) {
+ numBlockers -= 1;
+ canSet = true;
+ }
+ break;
+ }
+ }
+ if (canSet) {
+ lemmSkillRequest = null; // erase request
+ sound.play(SND_MOUSEPRE);
+ if (isPaused()) {
+ setPaused(false);
+ Icons.press(Icons.Type.PAUSE);
+ }
+ // add to replay stream
+ if (!wasCheated)
+ synchronized (lemmings) {
+ for (int i=0; i<lemmings.size(); i++)
+ if (lemmings.get(i) == lemm) // if 2nd try (delete==true) assign to next frame
+ replay.addAssignSkillEvent(replayFrame+((delete) ? 1:0), lemmSkill, i);
+ }
+ } else if (delete)
+ sound.play(SND_TING);
+ }
+
+ /**
+ * Calculate the counter threshold for releasing a new Lemmings.
+ */
+ static private void calcReleaseBase() {
+ // the original formula is: release lemming every 4+(99-speed)/2 time steps
+ // where one step is 60ms (3s/50) or 66ms (4s/60).
+ // Lemmini runs at 30ms/33ms, so the term has to be multiplied by 2
+ // 8+(99-releaseRate) should be correct
+ releaseBase = 8 + (99 - releaseRate);
+ }
+
+ /**
+ * Handle pressing of an icon button.
+ * @param type icon type
+ */
+ public static synchronized void handleIconButton(final Icons.Type type) {
+ Lemming.Type lemmSkillOld = lemmSkill;
+ boolean ok = false;
+ switch (type) {
+ case FLOAT:
+ if (isCheat() || numFloaters>0)
+ lemmSkill = Lemming.Type.FLOATER;
+ stopReplayMode();
+ break;
+ case CLIMB:
+ if (isCheat() || numClimbers>0)
+ lemmSkill = Lemming.Type.CLIMBER;
+ stopReplayMode();
+ break;
+ case BOMB:
+ if (isCheat() || numBombers>0)
+ lemmSkill = Lemming.Type.BOMBER;
+ stopReplayMode();
+ break;
+ case DIG:
+ if (isCheat() || numDiggers>0)
+ lemmSkill = Lemming.Type.DIGGER;
+ stopReplayMode();
+ break;
+ case BASH:
+ if (isCheat() || numBashers>0)
+ lemmSkill = Lemming.Type.BASHER;
+ stopReplayMode();
+ break;
+ case BUILD:
+ if (isCheat() || numBuilders>0)
+ lemmSkill = Lemming.Type.BUILDER;
+ stopReplayMode();
+ break;
+ case MINE:
+ if (isCheat() || numMiners>0)
+ lemmSkill = Lemming.Type.MINER;
+ stopReplayMode();
+ break;
+ case BLOCK:
+ if (isCheat() || numBlockers>0)
+ lemmSkill = Lemming.Type.STOPPER;
+ stopReplayMode();
+ break;
+ case NUKE: {
+ ok = true;
+ stopReplayMode();
+ if (timerNuke.delta() < MICROSEC_NUKE_DOUBLE_CLICK) {
+ if (!nuke) {
+ nuke = true;
+ sound.play(SND_OHNO);
+ }
+ } else timerNuke.deltaUpdate();
+ break;}
+ case PAUSE:
+ setPaused(!isPaused());
+ ok = true;
+ break;
+ case FFWD:
+ setFastForward(!isFastForward());
+ ok = true;
+ break;
+ case PLUS:
+ ok = true; // supress sound
+ plus.pressed(KEYREPEAT_ICON);
+ stopReplayMode();
+ break;
+ case MINUS:
+ ok = true; // supress sound
+ minus.pressed(KEYREPEAT_ICON);
+ stopReplayMode();
+ break;
+ }
+ if (ok || lemmSkill != lemmSkillOld) {
+ switch (type) {
+ case PLUS:
+ case MINUS:
+ break; // supress sound
+ default:
+ sound.play(SND_CHANGE_OP);
+ }
+ Icons.press(type);
+ } else
+ sound.play(SND_TING);
+ }
+
+ /**
+ * Fade in/out.
+ * @param g graphics object
+ */
+ public static void fade(final Graphics g) {
+ if (Fader.getState() == Fader.State.OFF && transitionState != TransitionState.NONE) {
+ switch (transitionState) {
+ case END_LEVEL:
+ finishLevel();
+ break;
+ case TO_BRIEFING:
+ gameState = State.BRIEFING;
+ break;
+ case TO_DEBRIEFING:
+ gameState = State.DEBRIEFING;
+ break;
+ case TO_INTRO:
+ gameState = State.INTRO;
+ break;
+ case TO_LEVEL:
+ GameController.sound.play(SND_LETSGO);
+ try {
+ Music.load("music/"+GameController.levelPack[GameController.curLevelPack].getInfo(GameController.curDiffLevel,
+ GameController.curLevelNumber).getMusic());
+ } catch (ResourceException ex) {
+ Core.resourceError(ex.getMessage());
+ } catch (LemmException ex) {
+ JOptionPane.showMessageDialog( null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
+ System.exit(1);
+ }
+ gameState = State.LEVEL;
+ break;
+ case RESTART_LEVEL:
+ case REPLAY_LEVEL:
+ restartLevel(transitionState == TransitionState.REPLAY_LEVEL);
+ break;
+ case LOAD_LEVEL:
+ case LOAD_REPLAY:
+ try {
+ changeLevel(nextLevelPack, nextDiffLevel, nextLevelNumber, transitionState == TransitionState.LOAD_REPLAY);
+ ((JFrame)Core.getCmp()).setTitle("Lemmini - "+getLevel().getLevelName());
+ } catch (ResourceException ex) {
+ Core.resourceError(ex.getMessage());
+ } catch (LemmException ex) {
+ JOptionPane.showMessageDialog( null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
+ System.exit(1);
+ }
+ break;
+ }
+ Fader.setState(Fader.State.IN);
+ transitionState = TransitionState.NONE;
+ }
+ Fader.fade(g);
+ }
+
+ /**
+ * Draw the explosions
+ * @param g graphics object
+ * @param width width of screen in pixels
+ * @param height height of screen in pixels
+ * @param xOfs horizontal level offset in pixels
+ */
+ public static void drawExplosions(final Graphics2D g, final int width, final int height, final int xOfs) {
+ synchronized (explosions) {
+ Iterator<Explosion> it = explosions.iterator();
+ while (it.hasNext()) {
+ Explosion e = it.next();
+ e.draw(g, width, height, xOfs);
+ }
+ }
+ }
+
+ /**
+ * Add a new explosion.
+ * @param x x coordinate in pixels.
+ * @param y y coordinate in pixels.
+ */
+ public static void addExplosion(final int x, final int y) {
+ // create particle explosion
+ synchronized (GameController.explosions) {
+ GameController.explosions.add(new Explosion(x, y));
+ }
+ }
+
+ /**
+ * Draw icon bar.
+ * @param g graphics object
+ * @param x x coordinate in pixels
+ * @param y y coordinate in pixels
+ */
+ public static void drawIcons(final Graphics2D g, final int x, final int y) {
+ g.drawImage(Icons.getImg(),x,y,null);
+ }
+
+ /**
+ * Draw the skill/release rate values
+ * @param g graphics object
+ * @param y y offset in pixels
+ */
+ public static void drawCounters(final Graphics2D g, final int y) {
+ // draw counters
+ int val = 0;
+ for (int i=0; i<10;i++) {
+ switch (i) {
+ case 0:
+ val = level.getReleaseRate();
+ break;
+ case 1:
+ val = releaseRate;
+ break;
+ case 2:
+ val = numClimbers;
+ break;
+ case 3:
+ val = numFloaters;
+ break;
+ case 4:
+ val = numBombers;
+ break;
+ case 5:
+ val = numBlockers;
+ break;
+ case 6:
+ val = numBuilders;
+ break;
+ case 7:
+ val = numBashers;
+ break;
+ case 8:
+ val = numMiners;
+ break;
+ case 9:
+ val = numDiggers;
+ break;
+ }
+ g.drawImage(NumFont.numImage(val),Icons.WIDTH*i+8,y,null);
+ }
+
+ }
+
+ /**
+ * Get index of current level pack.
+ * @return index of current level pack
+ */
+ public static int getCurLevelPackIdx() {
+ return curLevelPack;
+ }
+
+ /**
+ * Get current level pack.
+ * @return current level pack
+ */
+ public static LevelPack getCurLevelPack() {
+ return levelPack[curLevelPack];
+ }
+
+ /**
+ * get number of level packs
+ * @return number of level packs
+ */
+ public static int getLevelPackNum() {
+ return levelPack.length;
+ }
+
+ /**
+ * Get level pack via index.
+ * @param i index of level pack
+ * @return LevelPack
+ */
+ public static LevelPack getLevelPack(final int i) {
+ return levelPack[i];
+ }
+
+ /**
+ * Get index of current difficulty level.
+ * @return index of current difficulty level
+ */
+ public static int getCurDiffLevel() {
+ return curDiffLevel;
+ }
+
+ /**
+ * Get number of current level.
+ * @return number of current leve
+ */
+ public static int getCurLevelNumber() {
+ return curLevelNumber;
+ }
+
+
+ /**
+ * Set horizontal scrolling offset.
+ * @param x horizontal scrolling offset in pixels
+ */
+ public static void setxPos(final int x) {
+ xPos = x;
+ }
+
+ /**
+ * Get horizontal scrolling offset.
+ * @return horizontal scrolling offset in pixels
+ */
+ public static int getxPos() {
+ return xPos;
+ }
+
+ /**
+ * Set game state.
+ * @param s new game state
+ */
+ public static void setGameState(final State s) {
+ gameState = s;
+ }
+
+ /**
+ * Get game state.
+ * @return game state
+ */
+ public static State getGameState() {
+ return gameState;
+ }
+
+ /**
+ * Enable/disable cheat mode.
+ * @param c true: enable, false: disable
+ */
+ public static void setCheat(final boolean c) {
+ cheat = c;
+ }
+
+ /**
+ * Get state of cheat mode.
+ * @return true if cheat mode enabled, false otherwise
+ */
+ public static boolean isCheat() {
+ return cheat;
+ }
+
+ /**
+ * Set transition state.
+ * @param ts TransitionState
+ */
+ public static void setTransition(final TransitionState ts) {
+ transitionState = ts;
+ }
+
+
+ /**
+ * Load a replay.
+ * @param fn file name
+ * @return replay level info object
+ */
+ public static ReplayLevelInfo loadReplay(final String fn) {
+ return replay.load(fn);
+ }
+
+ /**
+ * Save a replay.
+ * @param fn file name
+ * @return true if saved successfully, false otherwise
+ */
+ public static boolean saveReplay(final String fn) {
+ return replay.save(fn);
+ }
+
+ /**
+ * Activate/deactivate Superlemming mode.
+ * @param sl true: activate, false: deactivate
+ */
+ public static void setSuperLemming(final boolean sl) {
+ superLemming = sl;
+ }
+
+ /**
+ * Get Superlemming state.
+ * @return true is Superlemming mode is active, false otherwise
+ */
+ public static boolean isSuperLemming() {
+ return superLemming;
+ }
+
+ /**
+ * Set cheated detection.
+ * @param c true: cheat mode was activated, false otherwise
+ */
+ public static void setWasCheated(final boolean c) {
+ wasCheated = c;
+ }
+
+ /**
+ * Enable pause mode.
+ * @param p true: pause is active, false otherwise
+ */
+ public static void setPaused(final boolean p) {
+ paused = p;
+ }
+
+ /**
+ * Get pause state.
+ * @return true if pause is active, false otherwise
+ */
+ public static boolean isPaused() {
+ return paused;
+ }
+
+ /**
+ * Enable fast forward mode.
+ * @param ff true: fast forward is active, false otherwise
+ */
+ public static void setFastForward(final boolean ff) {
+ fastForward = ff;
+ }
+
+ /**
+ * Get fast forward state.
+ * @return true if fast forward is active, false otherwise
+ */
+ public static boolean isFastForward() {
+ return fastForward;
+ }
+
+ /** get number of lemmings left in the game
+ * @return number of lemmings left in the game
+ */
+ public static int getNumLeft() {
+ return numLeft;
+ }
+
+ /**
+ * Set number of Lemmings left in the game.
+ * @param n number of Lemmings left in the game
+ */
+ public static void setNumLeft(final int n) {
+ numLeft = n;
+ }
+
+ /**
+ * Get level object.
+ * @return level object
+ */
+ public static Level getLevel() {
+ return level;
+ }
+
+ /**
+ * Get maximum number of Lemmings for this level.
+ * @return maximum number of Lemmings for this level
+ */
+ public static int getNumLemmingsMax() {
+ return numLemmingsMax;
+ }
+
+ /**
+ * Get icon type from x position.
+ * @param x x position in pixels
+ * @return icon type
+ */
+ public static Icons.Type getIconType(final int x) {
+ return Icons.getType(x);
+ }
+
+ /**
+ * Icon was pressed.
+ * @param t icon type
+ */
+ public static void pressIcon(final Icons.Type t) {
+ Icons.press(t);
+ }
+
+ /**
+ * Icon was released.
+ * @param t icon type
+ */
+ public static void releaseIcon(final Icons.Type t) {
+ Icons.release(t);
+ }
+
+ /**
+ * Plus was pressed.
+ * @param d bitmask: key or icon
+ */
+ public static void pressPlus(final int d) {
+ plus.pressed(d);
+ }
+
+ /**
+ * Plus was released.
+ * @param d bitmask: key or icon
+ */
+ public static void releasePlus(final int d) {
+ plus.released(d);
+ }
+
+ /**
+ * Minus was pressed.
+ * @param d bitmask: key or icon
+ */
+ public static void pressMinus(final int d) {
+ minus.pressed(d);
+ }
+
+ /**
+ * Minus was released.
+ * @param d bitmask: key or icon
+ */
+ public static void releaseMinus(final int d) {
+ minus.released(d);
+ }
+
+ /**
+ * Get list of all Lemmings under the mouse cursor.
+ * @return list of all Lemmings under the mouse cursor
+ */
+ public static ArrayList<Lemming> getLemmsUnderCursor() {
+ return lemmsUnderCursor;
+ }
+
+ /**
+ * Get list of all Lemmings in this level.
+ * @return list of all Lemmings in this level
+ */
+ public static LinkedList<Lemming> getLemmings() {
+ return lemmings;
+ }
+
+ /**
+ * Set sound gain.
+ * @param g gain (0..1.0)
+ */
+ public static void setSoundGain(final double g) {
+ soundGain = g;
+ if (sound != null)
+ sound.setGain(soundGain);
+ }
+
+ /**
+ * Set music gain.
+ * @param g gain (0..1.0)
+ */
+ public static void setMusicGain(final double g) {
+ musicGain = g;
+ if (Music.getType() != null)
+ Music.setGain(musicGain);
+ }
+
+ /**
+ * Set advanced mouse selection mode.
+ * @param sel true: advanced selection mode active, false otherwise
+ */
+ public static void setAdvancedSelect(final boolean sel) {
+ advancedSelect = sel;
+ }
+
+ /**
+ * Get state of advanced mouse selection mode.
+ * @return true if advanced selection mode activated, false otherwise
+ */
+ public static boolean isAdvancedSelect() {
+ return advancedSelect;
+ }
+
+ /**
+ * Get background image of level.
+ * @return background image of level
+ */
+ public static BufferedImage getBgImage() {
+ return bgImage;
+ }
+
+ /**
+ * Get background stencil of level.
+ * @return background stencil of level
+ */
+ public static Stencil getStencil() {
+ return stencil;
+ }
+
+ /**
+ * Enable music.
+ * @param on true: music on, false otherwise
+ */
+ public static void setMusicOn(final boolean on) {
+ musicOn = on;
+ }
+
+ /**
+ * Get music enable state.
+ * @return true: music is on, false otherwise
+ */
+ public static boolean isMusicOn() {
+ return musicOn;
+ }
+
+ /**
+ * Enable sound.
+ * @param on true: sound on, false otherwise
+ */
+ public static void setSoundOn(final boolean on) {
+ soundOn = on;
+ }
+
+ /**
+ * Get sound enable state.
+ * @return true: sound is on, false otherwise
+ */
+ public static boolean isSoundOn() {
+ return soundOn;
+ }
+
+ /**
+ * Get small preview image of level.
+ * @return small preview image of level
+ */
+ public static BufferedImage getMapPreview() {
+ return mapPreview;
+ }
+
+ /**
+ * Get number of Lemmings to rescue.
+ * @return number of Lemmings to rescue
+ */
+ public static int getNumToRecue() {
+ return numToRecue;
+ }
+
+ /**
+ * Get time left in seconds.
+ * @return time left in seconds
+ */
+ public static int getTime() {
+ return time;
+ }
+}
+
+
+/**
+ * Trapdoor/Entry class
+ * Trapdoor logic: for numbers >1, just take the next door for each lemming and wrap around to 1 when
+ * the last one is reached.
+ * Special rule for 3 trapdoors: the order is 1, 2, 3, 2 (loop), not 1, 2, 3 (loop)
+ *
+ * @author Volker Oth
+ */
+class TrapDoor {
+ /** pattern for three entries */
+ private final static int[] PATTERN3 = {0,1,2,1};
+
+ /** number of entries */
+ private static int entries;
+ /** entry counter */
+ private static int counter;
+
+ /**
+ * Reset to new number of entries.
+ * @param e number of entries
+ */
+ static void reset(final int e) {
+ entries = e;
+ counter = 0;
+ }
+
+ /**
+ * Get index of next entry.
+ * @return index of next entry
+ */
+ static int getNext() {
+ int retVal = counter;
+ counter++;
+ if (entries != 3) {
+ if (counter >= entries)
+ counter = 0;
+ return retVal;
+ }
+ // special case: 3
+ if (counter >= 4)
+ counter = 0;
+ return PATTERN3[retVal];
+ }
+
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/GroupBitfield.java b/jeu-test/Lemmini/0.84/src/Game/GroupBitfield.java
new file mode 100644
index 0000000..1e17fe1
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/GroupBitfield.java
@@ -0,0 +1,47 @@
+package Game;
+
+import java.math.BigInteger;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Wrapper class which uses BigInteger to implement a large bitfield
+ * in which single bits can be set via testBit and setBit.
+ */
+public class GroupBitfield extends BigInteger {
+
+ /** define "1" to avoid multiple instances */
+ final static GroupBitfield ONE = new GroupBitfield("1");
+
+ private final static long serialVersionUID = 1;
+
+ /**
+ * Constructor with numerical value as string
+ * @param s numerical value as string
+ */
+ public GroupBitfield(final String s) {
+ super(s);
+ }
+
+ /**
+ * Constructor with numerical value as BigInteger
+ * @param i numerical value as BigInteger
+ */
+ public GroupBitfield(final BigInteger i) {
+ super(i.toString());
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/Icons.java b/jeu-test/Lemmini/0.84/src/Game/Icons.java
new file mode 100644
index 0000000..56b8929
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Icons.java
@@ -0,0 +1,224 @@
+package Game;
+
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+import GameUtil.Sprite;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handle the control icons.
+ *
+ * @author Volker Oth
+ */
+public class Icons {
+
+ /** icon width in pixels */
+ public final static int WIDTH = 32;
+ /** icon height in pixels */
+ public final static int HEIGHT = 40;
+
+ /** Icon types */
+ public static enum Type {
+ /** minus icon */
+ MINUS,
+ /** plus icon */
+ PLUS,
+ /** climber icon */
+ CLIMB,
+ /** floater icon */
+ FLOAT,
+ /** bomber icon */
+ BOMB,
+ /** blocker icon */
+ BLOCK,
+ /** builder icon */
+ BUILD,
+ /** basher icon */
+ BASH,
+ /** miner icon */
+ MINE,
+ /** digger icon */
+ DIG,
+ /** pause icon */
+ PAUSE,
+ /** nuke icon */
+ NUKE,
+ /** fast forward icon */
+ FFWD,
+ /** an empty icon (not used) */
+ EMPTY,
+ /** invalid no such icon */
+ INVALID;
+
+ private static final Map<Integer,Type> lookup = new HashMap<Integer,Type>();
+
+ static {
+ for(Type s : EnumSet.allOf(Type.class))
+ lookup.put(s.ordinal(), s);
+ }
+
+ /**
+ * Reverse lookup implemented via hashtable.
+ * @param val Ordinal value
+ * @return Parameter with ordinal value val
+ */
+ public static Type get(final int val) {
+ return lookup.get(val);
+ }
+ }
+
+ /** 1st radio button */
+ private final static int FIRST_RADIO = Type.CLIMB.ordinal();
+ /** last radio button */
+ private final static int LAST_RADIO = Type.DIG.ordinal();
+ /** last icon to be drawn */
+ private final static int LAST_DRAWN = Type.FFWD.ordinal();
+
+ /** array of Sprites that contains the icons */
+ private static Sprite icons[];
+ /** buffered image that contains the whole icon bar in its current state */
+ private static BufferedImage iconImg;
+ /** graphics object used to draw on iconImg */
+ private static Graphics2D iconGfx;
+
+
+ /**
+ * Initialization.
+ * @param cmp parent component
+ * @throws ResourceException
+ */
+ public static void init(final Component cmp) throws ResourceException {
+ iconImg = ToolBox.createImage(WIDTH*(1+LAST_DRAWN),HEIGHT,Transparency.OPAQUE);
+ iconGfx = iconImg.createGraphics();
+ MediaTracker tracker = new MediaTracker(cmp);
+ icons = new Sprite[15];
+ for (int i=0; i<14; i++) {
+ Image sourceImg = Core.loadImage(tracker, "misc/icon_"+i+".gif");
+ try {
+ tracker.waitForAll();
+ } catch (InterruptedException ex) {}
+ icons[i] = new Sprite(sourceImg, (i==Type.EMPTY.ordinal())?1:2);
+ if (i<=LAST_DRAWN)
+ iconGfx.drawImage(icons[i].getImage(),WIDTH*i,0,null);
+ }
+ }
+
+ /**
+ * Get Icon type by x position.
+ * @param x x position inside bar in pixels
+ * @return Icon type
+ */
+ public static Type getType(final int x) {
+ if (x>=(LAST_DRAWN+1)*WIDTH)
+ return Type.INVALID; // invalid
+ return Type.get(x/WIDTH);
+ }
+
+ /**
+ * Get buffered image that contains the whole icon bar in its current state.
+ * @return image of icon bar
+ */
+ public static BufferedImage getImg() {
+ return iconImg;
+ }
+
+ /**
+ * Get pressed state of the given Icon
+ * @param type
+ * @return
+ */
+ static boolean isPressed(Type type) {
+ int idx = type.ordinal();
+ if (idx > LAST_DRAWN)
+ return false;
+ return (icons[idx].getFrameIdx() == 1);
+ }
+
+ /**
+ * Press down icon.
+ * @param type Icon Type
+ */
+ static void press(final Type type) {
+ int idx = type.ordinal();
+ switch (type) {
+ case PAUSE:
+ case FFWD:
+ icons[idx].setFrameIdx((icons[idx].getFrameIdx()==0)?1:0); // toggle
+ if (idx <= LAST_DRAWN)
+ iconGfx.drawImage(icons[idx].getImage(),WIDTH*idx,0,null);
+ break;
+ case CLIMB:
+ case FLOAT:
+ case BOMB:
+ case BLOCK:
+ case BUILD:
+ case BASH:
+ case MINE:
+ case DIG:
+ for (int i=FIRST_RADIO; i<=LAST_RADIO; i++)
+ if (i!=idx) {
+ icons[i].setFrameIdx(0);
+ iconGfx.drawImage(icons[i].getImage(),WIDTH*i,0,null);
+ }
+ //$FALL-THROUGH$
+ case MINUS:
+ case PLUS:
+ case NUKE:
+ icons[idx].setFrameIdx(1); // set "pressed" frame
+ if (idx <= LAST_DRAWN)
+ iconGfx.drawImage(icons[idx].getImage(),WIDTH*idx,0,null);
+ break;
+ }
+ }
+
+ /**
+ * Release Icon.
+ * @param type Icon Type
+ */
+ static void release(final Type type) {
+ int idx = type.ordinal();
+ switch (type) {
+ case MINUS:
+ case PLUS:
+ case NUKE:
+ icons[idx].setFrameIdx(0); // set "released" frame
+ if (idx <= LAST_DRAWN)
+ iconGfx.drawImage(icons[idx].getImage(),WIDTH*idx,0,null);
+ break;
+ }
+ }
+
+ /**
+ * Reset Icon bar.
+ */
+ static void reset() {
+ for (int i=0; i<=LAST_DRAWN; i++) {
+ icons[i].setFrameIdx(0);
+ iconGfx.drawImage(icons[i].getImage(),WIDTH*i,0,null);
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/LemmCursor.java b/jeu-test/Lemmini/0.84/src/Game/LemmCursor.java
new file mode 100644
index 0000000..9ace85f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LemmCursor.java
@@ -0,0 +1,209 @@
+package Game;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the Lemmini selection cursor.
+ *
+ * @author Volker Oth
+ */
+public class LemmCursor {
+
+ /** distance from center of cursor to be used to detect Lemmings under the cursor */
+ private final static int HIT_DISTANCE = 12;
+
+ /** cursor type */
+ public enum Type {
+ /** normal cursor */
+ NORMAL,
+ /** select left cursor */
+ LEFT,
+ /** select right cursor */
+ RIGHT,
+ /** select walkers cursor */
+ WALKER,
+ /** normal cursor with selection box */
+ BOX_NORMAL,
+ /** select left cursor with selection box */
+ BOX_LEFT,
+ /** select right cursor with selection box */
+ BOX_RIGHT,
+ /** select walkers cursor with selection box */
+ BOX_WALKER
+ }
+
+ /** x position in pixels */
+ private static int x;
+ /** y position in pixels */
+ private static int y;
+ /** current cursor type */
+ private static Type type;
+ /** array of images - one for each cursor type */
+ private static BufferedImage img[];
+ /** array of AWT cursor Objects */
+ private static Cursor cursor[];
+
+ /**
+ * Initialization.
+ * @throws ResourceException
+ */
+ public static void init() throws ResourceException {
+ img = ToolBox.getAnimation(Core.loadImage("misc/cursor.gif"), 8, Transparency.BITMASK);
+ cursor = new Cursor[4];
+ int w = getImage(Type.NORMAL).getWidth()/2;
+ int h = getImage(Type.NORMAL).getHeight()/2;
+ cursor[Type.NORMAL.ordinal()] = Toolkit.getDefaultToolkit().createCustomCursor( LemmCursor.getImage(Type.NORMAL), new Point(w,h), "" );
+ cursor[Type.LEFT.ordinal()] = Toolkit.getDefaultToolkit().createCustomCursor( LemmCursor.getImage(Type.LEFT), new Point(w,h), "" );
+ cursor[Type.RIGHT.ordinal()] = Toolkit.getDefaultToolkit().createCustomCursor( LemmCursor.getImage(Type.RIGHT), new Point(w,h), "" );
+ cursor[Type.WALKER.ordinal()] = Toolkit.getDefaultToolkit().createCustomCursor( LemmCursor.getImage(Type.WALKER), new Point(w,h), "" );
+
+ type = Type.NORMAL;
+ setX(0);
+ setY(0);
+ }
+
+ /**
+ * Get image for a certain cursor type.
+ * @param t cursor type
+ * @return image for the given cursor type
+ */
+ public static BufferedImage getImage(final Type t) {
+ return img[t.ordinal()];
+ }
+
+ /**
+ * Get image for current cursor type.
+ * @return image for current cursor type
+ */
+ public static BufferedImage getImage() {
+ return getImage(type);
+ }
+
+ /**
+ * Get boxed version of image for the current cursor type.
+ * @return boxed version of image for the current cursor type
+ */
+ public static BufferedImage getBoxImage() {
+ Type t;
+ switch (type) {
+ case NORMAL:
+ t = Type.BOX_NORMAL;
+ break;
+ case LEFT:
+ t = Type.BOX_LEFT;
+ break;
+ case RIGHT:
+ t = Type.BOX_RIGHT;
+ break;
+ case WALKER:
+ t = Type.BOX_WALKER;
+ break;
+ default:
+ t = type; // should never happen
+ }
+ return getImage(t);
+ }
+
+ /**
+ * Get current cursor as AWT cursor object.
+ * @return current cursor as AWT cursor object
+ */
+ public static Cursor getCursor() {
+ return cursor[type.ordinal()];
+ }
+
+ /**
+ * Get current cursor type.
+ * @return current cursor type
+ */
+ public static Type getType() {
+ return type;
+ }
+
+ /**
+ * Set current cursor type.
+ * @param t cursor type
+ */
+ public static void setType(final Type t) {
+ type = t;
+ }
+
+ /**
+ * Check if a Lemming is under the cursor.
+ * @param l Lemming to check
+ * @param xOfs screen x offset
+ * @return true if the Lemming is under the Cursor, else false.
+ */
+ public static boolean doesCollide(final Lemming l, final int xOfs) {
+ // get center of lemming
+ int lx = l.midX() - xOfs;
+ int ly = l.midY();
+
+ // calculate center of cursor
+ int cx = getX();
+ int cy = getY();
+
+ // calculate distance
+ int dx = Math.abs(lx-cx);
+ int dy = Math.abs(ly-cy);
+
+ if (dx <= HIT_DISTANCE && dy <= HIT_DISTANCE)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Set x position in pixels.
+ * @param x x position in pixels.
+ */
+ public static void setX(final int x) {
+ LemmCursor.x = x;
+ }
+
+ /**
+ * Get x position in pixels.
+ * @return x position in pixels
+ */
+ public static int getX() {
+ return x;
+ }
+
+ /**
+ * Set y position in pixels.
+ * @param y y position in pixels
+ */
+ public static void setY(final int y) {
+ LemmCursor.y = y;
+ }
+
+ /**
+ * Get y position in pixels.
+ * @return y position in pixels
+ */
+ public static int getY() {
+ return y;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/LemmException.java b/jeu-test/Lemmini/0.84/src/Game/LemmException.java
new file mode 100644
index 0000000..e89cc45
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LemmException.java
@@ -0,0 +1,41 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generic exception class.
+ *
+ * @author Volker Oth
+ */
+public class LemmException extends Exception {
+ private final static long serialVersionUID = 0x000000001;
+
+ /**
+ * Constructor.
+ */
+ public LemmException() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ * @param s Exception string
+ */
+ public LemmException(final String s) {
+ super(s);
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/LemmFont.java b/jeu-test/Lemmini/0.84/src/Game/LemmFont.java
new file mode 100644
index 0000000..53e0bf5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LemmFont.java
@@ -0,0 +1,184 @@
+package Game;
+
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handle the main bitmap font.
+ *
+ * @author Volker Oth
+ */
+public class LemmFont {
+
+ /** Colors */
+ public static enum Color {
+ /** green color */
+ GREEN,
+ /** blue color */
+ BLUE,
+ /** red color */
+ RED,
+ /** brown/yellow color */
+ BROWN,
+ /** turquoise/cyan color */
+ TURQUOISE,
+ /** violet color */
+ VIOLET
+ }
+
+ /** default width of one character in pixels */
+ private final static int SPACING = 18;
+ /** character map */
+ private final static String CHARS = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_´abcdefghijklmnopqrstuvwxyz{|}~";
+
+ /** width of one character in pixels */
+ private static int width;
+ /** height of one character pixels */
+ private static int height;
+ /** array of array of images [color,character] */
+ static private BufferedImage img[][];
+
+ /**
+ * Initialization.
+ * @throws ResourceException
+ */
+ static public void init() throws ResourceException {
+ BufferedImage sourceImg = ToolBox.ImageToBuffered(Core.loadImage("misc/lemmfont.gif"),Transparency.BITMASK);
+
+ width = SPACING; //sourceImg.getWidth(null);
+ height = sourceImg.getHeight(null)/CHARS.length();
+ BufferedImage redImg = ToolBox.createImage(sourceImg.getWidth(), sourceImg.getHeight(),Transparency.BITMASK);
+ BufferedImage blueImg = ToolBox.createImage(sourceImg.getWidth(), sourceImg.getHeight(),Transparency.BITMASK);
+ BufferedImage turquoiseImg = ToolBox.createImage(sourceImg.getWidth(), sourceImg.getHeight(),Transparency.BITMASK);
+ BufferedImage brownImg = ToolBox.createImage(sourceImg.getWidth(), sourceImg.getHeight(),Transparency.BITMASK);
+ BufferedImage violetImg = ToolBox.createImage(sourceImg.getWidth(), sourceImg.getHeight(),Transparency.BITMASK);
+ img = new BufferedImage[7][];
+ img[0] = ToolBox.getAnimation(sourceImg,CHARS.length(),Transparency.BITMASK,width);
+ for (int xp=0; xp<sourceImg.getWidth(null); xp++)
+ for (int yp=0; yp<sourceImg.getHeight(null); yp++) {
+ int col = sourceImg.getRGB(xp, yp); // A R G B
+ int a = col & 0xff000000; // transparent part
+ int r = (col >> 16) & 0xff;
+ int g = (col >> 8) & 0xff;
+ int b = col & 0xff;
+ // patch image to red version by swapping red and green components
+ col = a | (g<<16) | (r<<8) | b;
+ redImg.setRGB(xp, yp, col);
+ // patch image to blue version by swapping blue and green components
+ col = a | (r<<16) | (b<<8) | g;
+ blueImg.setRGB(xp, yp, col);
+ // patch image to turquoise version by setting blue component to value of green component
+ col = a | (r<<16) | (g<<8) | g;
+ turquoiseImg.setRGB(xp, yp, col);
+ // patch image to yellow version by setting red component to value of green component
+ col = a | (g<<16) | (g<<8) | b;
+ brownImg.setRGB(xp, yp, col);
+ // patch image to violet version by exchanging red and blue with green
+ col = a | (g<<16) | (((r+b)<<7)&0xff00) | g;
+ violetImg.setRGB(xp, yp, col);
+ }
+ img[Color.RED.ordinal()] = ToolBox.getAnimation(redImg,CHARS.length(),Transparency.BITMASK,width);
+ img[Color.BLUE.ordinal()] = ToolBox.getAnimation(blueImg,CHARS.length(),Transparency.BITMASK,width);
+ img[Color.TURQUOISE.ordinal()] = ToolBox.getAnimation(turquoiseImg,CHARS.length(),Transparency.BITMASK,width);
+ img[Color.BROWN.ordinal()] = ToolBox.getAnimation(brownImg,CHARS.length(),Transparency.BITMASK,width);
+ img[Color.VIOLET.ordinal()] = ToolBox.getAnimation(violetImg,CHARS.length(),Transparency.BITMASK,width);
+ }
+
+ /**
+ * Draw string into graphics object in given color.
+ * @param g graphics object to draw to.
+ * @param s string to draw.
+ * @param sx x coordinate in pixels
+ * @param sy y coordinate in pixels
+ * @param color Color
+ */
+ static public void strImage(final Graphics2D g, final String s, final int sx, final int sy, final Color color) {
+ for (int i=0, x = sx; i<s.length();i++,x+=SPACING) {
+ char c = s.charAt(i);
+ if (c==' ')
+ continue;
+ int pos = CHARS.indexOf(c);
+ if (pos > -1 && pos < CHARS.length()) {
+ g.drawImage(img[color.ordinal()][pos], x, sy, null);
+ }
+ }
+ return;
+ }
+
+ /**
+ * Draw string into graphics object in given color.
+ * @param g graphics object to draw to.
+ * @param s string to draw.
+ * @param color Color
+ */
+ static public void strImage(final Graphics2D g, final String s, final Color color) {
+ strImage(g, s, 0, 0, color);
+ return;
+ }
+
+ /**
+ * Create image of string in given color.
+ * @param s string to draw
+ * @param color Color
+ * @return a buffered image of the needed size that contains an image of the given string
+ */
+ static public BufferedImage strImage(final String s, final Color color) {
+ BufferedImage image = ToolBox.createImage(width*s.length(), height, Transparency.BITMASK);
+ strImage(image.createGraphics(), s, color);
+ return image;
+ }
+
+ /**
+ * Create image of string in default color (green).
+ * @param s string to draw
+ * @return a buffered image of the needed size that contains an image of the given string
+ */
+ static public BufferedImage strImage(final String s) {
+ return strImage(s, Color.GREEN);
+ }
+
+ /**
+ * Draw string into graphics object in default color (green).
+ * @param g graphics object to draw to.
+ * @param s string to draw.
+ */
+ static public void strImage(final Graphics2D g, final String s) {
+ strImage(g, s, Color.GREEN);
+ }
+
+ /**
+ * Get width of one character in pixels.
+ * @return width of one character in pixels
+ */
+ public static int getWidth() {
+ return width;
+ }
+
+ /**
+ * Get height of one character in pixels.
+ * @return height of one character in pixels
+ */
+ public static int getHeight() {
+ return height;
+ }
+
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/Lemming.java b/jeu-test/Lemmini/0.84/src/Game/Lemming.java
new file mode 100644
index 0000000..abebe48
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Lemming.java
@@ -0,0 +1,1691 @@
+package Game;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+import Tools.Props;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implements a Lemming.
+ *
+ * @author Volker Oth
+ */
+public class Lemming {
+ /** name of the configuration file */
+ private final static String LEMM_INI_STR = "misc/lemming.ini";
+ /** number of resources (animations/names) */
+ private final static int NUM_RESOURCES = 17;
+
+ /** Lemming skill type */
+ public static enum Type {
+ /** the typical Lemming */
+ WALKER,
+ /** a falling Lemming */
+ FALLER,
+ /** a climbing Lemming */
+ CLIMBER,
+ /** a climbing Lemming returning to the ground */
+ CLIMBER_TO_WALKER,
+ /** a Lemming on a parachute */
+ FLOATER,
+ /** a Lemming dieing from a fall */
+ SPLAT,
+ /** a Lemming blocking the way for the other Lemmings */
+ STOPPER,
+ /** a Lemming drowning in the water */
+ DROWNING,
+ /** a Lemming killed by a trap */
+ TRAPPED,
+ /** a Lemming existing the level */
+ EXITING,
+ /** a Lemming blowing itself up */
+ BOMBER,
+ /** a Lemming building stairs */
+ BUILDER,
+ /** a builder Lemmings with no more steps in his backpack */
+ BUILDER_END,
+ /** a Lemming digging a hole in the ground */
+ DIGGER,
+ /** a Lemming bashing the ground before it */
+ BASHER,
+ /** a Lemming digging a mine with a pick */
+ MINER,
+ /** a Lemming jumping over a small obstacle */
+ JUMPER,
+ /* types without a separate animation */
+ /** a Lemming that is nuked */
+ NUKE,
+ /** a stopper that is told to explode */
+ BOMBER_STOPPER,
+ /** a floater before the parachute opened completely */
+ FLOATER_START,
+ /** undefined */
+ UNDEFINED;
+
+ private static final Map<Integer,Type> lookup = new HashMap<Integer,Type>();
+
+ static {
+ for(Type s : EnumSet.allOf(Type.class))
+ lookup.put(s.ordinal(), s);
+ }
+
+ /**
+ * Reverse lookup implemented via hashtable.
+ * @param val Ordinal value
+ * @return Parameter with ordinal value val
+ */
+ public static Type get(final int val) {
+ return lookup.get(val);
+ }
+ }
+
+ /** Lemming heading */
+ static enum Direction {
+ RIGHT,
+ LEFT,
+ NONE;
+
+ private static final Map<Integer,Direction> lookup = new HashMap<Integer,Direction>();
+
+ static {
+ for(Direction s : EnumSet.allOf(Direction.class))
+ lookup.put(s.ordinal(), s);
+ }
+
+ /**
+ * Reverse lookup implemented via hashtable.
+ * @param val Ordinal value
+ * @return Parameter with ordinal value val
+ */
+ public static Direction get(final int val) {
+ return lookup.get(val);
+ }
+
+ }
+
+ /** animation type */
+ static enum Animation {
+ NONE,
+ LOOP,
+ ONCE
+ }
+
+ /** display string for skills/types. order must be the same as in the enum! */
+ private final static String LEMM_NAMES[] = {
+ "WALKER", "FALLER", "CLIMBER", "CLIMBER",
+ "FLOATER", "", "BLOCKER", "DROWNING",
+ "", "", "BOMBER", "BUILDER",
+ "BUILDER", "DIGGER", "BASHER", "MINER", "WALKER"
+ };
+
+ /** a walker walks one pixel per frame */
+ private final static int WALKER_STEP = 1;
+ /** a climber climbs up 1 pixel every 2nd frame */
+ private final static int CLIMBER_STEP = 1;
+ /** at this height a walker will turn around */
+ private final static int WALKER_OBSTACLE_HEIGHT = 14;
+ /** check N pixels above the lemming's feet */
+ private final static int BASHER_CHECK_STEP = 12;
+ /** from this on a basher will become a faller */
+ private final static int BASHER_FALL_DISTANCE = 6;
+ /** from this on a miner will become a faller */
+ private final static int MINER_FALL_DISTANCE = 4;
+ /** a faller falls down three pixels per frame */
+ private final static int FALLER_STEP = 3;
+ /** a floater falls down two pixels per frame */
+ private final static int FLOATER_STEP = 2;
+ /** a jumper moves up two pixels per frame */
+ private final static int JUMPER_STEP = 2;
+ /** if a walker jumps up 6 pixels, it becomes a jumper */
+ private final static int JUMPER_JUMP = 4;
+ /** pixels a floater falls before the parachute begins to open */
+ private final static int FALL_DISTANCE_FLOAT = 32;
+ /** number of free pixels below needed to convert a lemming to a faller */
+ private final static int FALL_DISTANCE_FALL = 8;
+ /** used as "free below" value to convert most skills into a faller */
+ private final static int FALL_DISTANCE_FORCE_FALL = 2*FALL_DISTANCE_FALL;
+ /** number of steps a builder can build */
+ private final static int STEPS_MAX = 12;
+ /** number of steps before the warning sound is played */
+ private final static int STEPS_WARNING = 9;
+ /** Lemmini runs with 50fps instead of 25fps */
+ private final static int TIME_SCALE = 2;
+ /** explosion counter is decreased every second */
+ private final static int MAX_EXPLODE_CTR = 1000000/GameController.MICROSEC_PER_FRAME;
+
+ /** resource (animation etc.) for the current Lemming */
+ private LemmingResource lemRes;
+ /** animation frame */
+ private int frameIdx;
+ /** x coordinate of foot in pixels */
+ private int x;
+ /** y coordinate of foot in pixels */
+ private int y;
+ /** x coordinate for mask in pixels */
+ private int maskX;
+ /** y coordinate for mask in pixels */
+ private int maskY;
+ /** Lemming's heading */
+ private Direction dir;
+ /** Lemming's skill/type */
+ private Type type;
+ /** counter used for internal state changes */
+ private int counter;
+ /** another counter used for internal state changes */
+ private int counter2;
+ /** explosion counter when nuked */
+ private int explodeNumCtr;
+ /** Lemming can float */
+ private boolean canFloat;
+ /** Lemming can climb */
+ private boolean canClimb;
+ /** Lemming can change its skill */
+ private boolean canChangeSkill;
+ /** Lemming is to be nuked */
+ private boolean nuke;
+ /** Lemming has died */
+ private boolean hasDied;
+ /** Lemming has left the level */
+ private boolean hasLeft;
+ /** counter used to manage the explosion */
+ private int explodeCtr;
+ /** counter used to display the select image in replay mode */
+ private int selectCtr;
+
+ /** static array of resources for each Lemming skill/type */
+ private static LemmingResource lemmings[];
+ /** font used for the explosion counter */
+ private static ExplodeFont explodeFont;
+
+ /**
+ * Constructor: Create Lemming
+ * @param sx x coordinate of foot
+ * @param sy y coordinate of foot
+ */
+ public Lemming(final int sx, final int sy) {
+ frameIdx = 0;
+ type = Type.FALLER; // always start with a faller
+ lemRes = lemmings[getOrdinal(type)];
+ counter = 0;
+ explodeNumCtr = 0;
+ selectCtr = 0;
+ dir = Direction.RIGHT; // always start walking to the right
+ x = sx;
+ y = sy;
+ //insideStopper = false;
+ canFloat = false; // new lemming can't float
+ canClimb = false; // new lemming can't climb
+ canChangeSkill = false; // a faller can not change the skill to e.g. builder
+ hasDied = false; // not yet
+ hasLeft = false; // not yet
+ nuke = false;
+ }
+
+ /**
+ * Get number of Lemming type in internal resource array.
+ * @param t Type
+ * @return resource number for type
+ */
+ public static int getOrdinal(final Type t) {
+ switch (t) {
+ case BOMBER_STOPPER:
+ return Type.BOMBER.ordinal();
+ case FLOATER_START:
+ return Type.FLOATER.ordinal();
+ default:
+ return t.ordinal();
+ }
+ }
+
+ /**
+ * Update animation, move Lemming, check state transitions.
+ */
+ public void animate() {
+ int free;
+ Type oldType = type;
+ Type newType = type;
+ int oldX = x;
+ boolean explode = false;
+ // first check explode state
+ if (explodeNumCtr != 0) {
+ if (++explodeCtr >= MAX_EXPLODE_CTR) {
+ explodeCtr -= MAX_EXPLODE_CTR;
+ explodeNumCtr--;
+ if (explodeNumCtr==0)
+ explode = true;
+ }
+ }
+ if (selectCtr>0)
+ selectCtr--;
+ flipDirBorder();
+ // lemming state machine
+ switch (type) {
+
+ case CLIMBER_TO_WALKER:
+ if (explode) {
+ explode();
+ break;
+ }
+ break;
+
+ case FALLER:
+ if (explode) {
+ explode();
+ break;
+ }
+ free = freeBelow(FALLER_STEP);
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FALLER_STEP;
+ else
+ y += free; // max: FALLER_STEP
+ if (!crossedLowerBorder()) {
+ counter += free; // fall counter
+ // check conversion to floater
+ if (canFloat && counter >= FALL_DISTANCE_FLOAT) {
+ newType = Type.FLOATER_START;
+ counter2 = 0; // used for parachute opening "jump" up
+ } else if (free == 0) { // check ground hit
+ //System.out.println(counter);
+ if (counter > GameController.getLevel().getMaxFallDistance())
+ newType = Type.SPLAT;
+ else {
+ newType = Type.WALKER;
+ counter = 0;
+ }
+ }
+ }
+ break;
+
+ case JUMPER: {
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check collision with stopper
+ if (turnedByStopper())
+ break;
+ int levitation = aboveGround();
+ if (levitation > JUMPER_STEP)
+ y -= JUMPER_STEP;
+ else {
+ // conversion to walker
+ y -= levitation;
+ newType = Type.WALKER;
+ }
+ break;
+ }
+
+ case WALKER: {
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check collision with stopper
+ if (turnedByStopper())
+ break;
+ if (dir == Direction.RIGHT)
+ x += WALKER_STEP;
+ else if (dir == Direction.LEFT)
+ x -= WALKER_STEP;
+ // check
+ free = freeBelow(FALL_DISTANCE_FALL);
+ if (free >= FALL_DISTANCE_FALL)
+ y += FALLER_STEP;
+ else {
+ y += free;
+ counter = free;
+ // if (free == 0)
+ // counter = 0; // reset fall counter
+ }
+ int levitation = aboveGround();
+ // check for flip direction
+ if (levitation < WALKER_OBSTACLE_HEIGHT && (y+lemRes.height/2)>0) {
+ //y -= levitation;
+ if (levitation >= JUMPER_JUMP) {
+ y -= JUMPER_STEP;
+ newType = Type.JUMPER;
+ break;
+ } else y -= levitation;
+ } else {
+ x = oldX;
+ //y = oldY;
+ if (canClimb) {
+ newType = Type.CLIMBER;
+ break;
+ } else {
+ dir = (dir==Direction.RIGHT) ? Direction.LEFT : Direction.RIGHT;
+ }
+ }
+ if (free>0) {
+ // check for conversion to faller
+ counter += FALLER_STEP; // @check: is this ok? increasing counter, but using free???
+ if (free >= FALL_DISTANCE_FALL)
+ newType = Type.FALLER;
+ }
+ break; }
+
+ case FLOATER_START:
+ if (explode) {
+ explode();
+ break;
+ }
+ switch (counter2++) {
+ case 0:
+ case 1: // keep falling with faller speed
+ case 2: y += FALLER_STEP-FLOATER_STEP;
+ break;
+ case 3: y -= FLOATER_STEP-1; // decelerate a little
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7: y -= FLOATER_STEP; // decelerate some more
+ break;
+ default:
+ type = Type.FLOATER;
+ }
+ //$FALL-THROUGH$
+ case FLOATER:
+ if (explode) {
+ explode();
+ break;
+ }
+ free = freeBelow(FLOATER_STEP);
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FLOATER_STEP;
+ else
+ y += free; // max: FLOATER_STEP
+ if (!crossedLowerBorder()) {
+ counter += free; // fall counter
+ // check ground hit
+ if (free == 0) {
+ newType = Type.WALKER;
+ counter = 0;
+ }
+ }
+ break;
+
+ case CLIMBER:
+ if (explode) {
+ explode();
+ break;
+ }
+ if ( (++counter & 1) == 1) // only every other step
+ y -= CLIMBER_STEP;
+ if (midY() < 0 || freeAbove(2)<2) {
+ dir = (dir==Direction.RIGHT) ? Direction.LEFT : Direction.RIGHT;
+ newType = Type.FALLER;
+ counter = 0;
+ } else if (reachedPlateau()) {
+ counter = 0;
+ newType = Type.CLIMBER_TO_WALKER;
+ }
+ break;
+
+ case SPLAT:
+ if (explode) {
+ explode();
+ break;
+ }
+ if (frameIdx ==0) // looped once
+ GameController.sound.play(GameController.SND_SPLAT);
+ break;
+
+ case DIGGER:
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ break;
+
+ case BASHER: {
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check for conversion to faller
+ // check collision with stopper
+ if (turnedByStopper()) {
+ newType = Type.WALKER;
+ break;
+ }
+ free = freeBelow(FLOATER_STEP);
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FALLER_STEP;
+ else
+ y += free;
+ if (free != 0) {
+ counter += free;
+ if (counter >= BASHER_FALL_DISTANCE)
+ newType = Type.FALLER;
+ } else counter = 0;
+ Mask m;
+ int checkMask;
+ int idx = frameIdx+1;
+ if (idx >= lemRes.frames*TIME_SCALE)
+ idx=0;
+ switch (idx) {
+ case 2*TIME_SCALE:
+ case 3*TIME_SCALE:
+ case 4*TIME_SCALE:
+ case 5*TIME_SCALE: {
+ // bash mask should have the same height as the lemming
+ m = lemRes.getMask(dir);
+ int sx =screenX();
+ int sy =screenY();
+ checkMask = Stencil.MSK_STEEL|((dir==Direction.LEFT) ? Stencil.MSK_NO_DIG_LEFT : Stencil.MSK_NO_DIG_RIGHT);
+ m.eraseMask(sx,sy,idx/TIME_SCALE-2, checkMask);
+ // check for conversion to walker because there are indestructible pixels
+ if (lemRes.getImask(dir).checkType(sx, sy, 0, checkMask)) {
+ GameController.sound.play(GameController.SND_CHINK);
+ newType = Type.WALKER;
+ }
+ if (idx == 5*TIME_SCALE)
+ // check for conversion to walker because there are no bricks left
+ if (!canBash()) {
+ // no bricks any more
+ newType = Type.WALKER;
+ }
+ break;}
+ case 18*TIME_SCALE:
+ case 19*TIME_SCALE:
+ case 20*TIME_SCALE:
+ case 21*TIME_SCALE: {
+ // bash mask should have the same height as the lemming
+ m = lemRes.getMask(dir);;
+ int sx =screenX();
+ int sy =screenY();
+ checkMask = Stencil.MSK_STEEL|((dir==Direction.LEFT) ? Stencil.MSK_NO_DIG_LEFT : Stencil.MSK_NO_DIG_RIGHT);
+ m.eraseMask(sx,sy,idx/TIME_SCALE-18, checkMask);
+ // check for conversion to walker because there are indestructible pixels
+ if (lemRes.getImask(dir).checkType(sx, sy, 0, checkMask)) {
+ GameController.sound.play(GameController.SND_CHINK);
+ newType = Type.WALKER;
+ }
+ break; }
+ case 10*TIME_SCALE:
+ case 11*TIME_SCALE:
+ case 12*TIME_SCALE:
+ case 13*TIME_SCALE:
+ case 14*TIME_SCALE:
+ case 26*TIME_SCALE:
+ case 27*TIME_SCALE:
+ case 28*TIME_SCALE:
+ case 29*TIME_SCALE:
+ case 30*TIME_SCALE:
+ if (dir == Direction.RIGHT)
+ x += 2;
+ else
+ x -= 2;
+ break;
+
+ }
+ break; }
+
+ case MINER: {
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check collision with stopper
+ if (turnedByStopper())
+ break;
+ Mask m;
+ int sx;
+ int sy;
+ int idx = frameIdx+1;
+ if (idx >= lemRes.frames*TIME_SCALE)
+ idx=0;
+ switch (idx) {
+ case 1*TIME_SCALE:
+ case 2*TIME_SCALE:
+ // check for steel in mask
+ m = lemRes.getMask(dir);
+ sx = screenX();
+ sy = screenY();
+ int checkMask = Stencil.MSK_STEEL|((dir==Direction.LEFT) ? Stencil.MSK_NO_DIG_LEFT : Stencil.MSK_NO_DIG_RIGHT);
+ m.eraseMask(sx,sy,idx/TIME_SCALE-1,checkMask);
+ if (lemRes.getImask(dir).checkType(sx, sy, 0, checkMask)) {
+ GameController.sound.play(GameController.SND_CHINK);
+ newType = Type.WALKER;
+ }
+ break;
+ case 3*TIME_SCALE:
+ case 15*TIME_SCALE:
+ if (dir == Direction.RIGHT)
+ x += 4;
+ else
+ x -= 4;
+ // check for conversion to faller
+ free = freeBelow(MINER_FALL_DISTANCE);
+ if (free >= MINER_FALL_DISTANCE) {
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FALLER_STEP;
+ else
+ y += free;
+ newType = Type.FALLER;
+ break;
+ }
+ if (idx == 15*TIME_SCALE)
+ y += 4;
+ break;
+ //case 23*TIME_SCALE:
+ // break;
+ }
+ break; }
+
+ case BUILDER_END:
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ }
+ break;
+ case BUILDER: {
+ if (explode) {
+ newType = Type.BOMBER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check collision with stopper
+ if (turnedByStopper())
+ break;
+ int idx = frameIdx+1;
+ if (idx >= lemRes.frames*TIME_SCALE) {
+ // step created -> move up
+ idx=0;
+ counter++; // step counter;
+ if (dir == Direction.RIGHT)
+ x += 4; // step forward
+ else
+ x -= 4;
+ y-=2; // step up
+ int levitation = aboveGround(); // should be 0, if not, we built into a wall -> stop
+ // check for conversion to walker
+ int fa = freeAbove(8); // check if builder is too close to ceiling
+ if (fa < 8 || levitation > 0) {
+ newType = Type.WALKER;
+ // a lemming can jump through the ceiling like in Mayhem2-Boiler Room
+ if (levitation >= WALKER_OBSTACLE_HEIGHT) {
+ // avoid getting stuck
+ x = oldX;
+ y += 2;
+ }
+ dir = (dir==Direction.RIGHT) ? Direction.LEFT : Direction.RIGHT;
+ break;
+ }
+ // check for last step used
+ if (counter >= STEPS_MAX) {
+ newType = Type.BUILDER_END;
+ break;
+ }
+ } else if (idx == 9*TIME_SCALE) {
+ // stair mask is the same heigth as a lemming
+ Mask m;
+ m = lemRes.getMask(dir);;
+ int sx = screenX();
+ int sy = screenY();
+ m.paintStep(sx,sy,0,GameController.getLevel().getDebrisColor());
+ if (counter >= STEPS_WARNING)
+ GameController.sound.play(GameController.SND_TING);
+ }
+ break; }
+
+ case STOPPER: {
+ if (explode) {
+ // don't erase stopper mask!
+ newType = Type.BOMBER_STOPPER;
+ if (!nuke)
+ GameController.sound.play(GameController.SND_OHNO);
+ break;
+ }
+ // check for conversion to faller
+ free = freeBelow(FLOATER_STEP);
+ if (free > 0) {
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FALLER_STEP;
+ else
+ y += free;
+ counter += free;
+ if (counter >= FALL_DISTANCE_FALL)
+ newType = Type.FALLER;
+ else
+ newType = Type.WALKER;
+ // conversion to faller or walker -> erase stopper mask
+ Mask m = lemmings[getOrdinal(Type.STOPPER)].getMask(dir);
+ m.clearType(maskX,maskY,0,Stencil.MSK_STOPPER);
+ } else counter = 0;
+ break; }
+
+ case BOMBER_STOPPER:
+ // don't erase stopper mask before stopper finally explodes or falls
+ free = freeBelow(FLOATER_STEP);
+ if (free>0) {
+ // stopper falls -> erase mask and convert to normal stopper.
+ Mask m = lemmings[getOrdinal(Type.STOPPER)].getMask(dir);
+ m.clearType(maskX,maskY,0,Stencil.MSK_STOPPER);
+ type = Type.BOMBER;
+ // fall through
+ } else break;
+ //$FALL-THROUGH$
+ case BOMBER:
+ free = freeBelow(FLOATER_STEP);
+ if (free == FALL_DISTANCE_FORCE_FALL)
+ y += FALLER_STEP;
+ else
+ y += free;
+ crossedLowerBorder();
+ break;
+
+ default:
+ // all cases not explicitly checked above should at least explode
+ if (explode) {
+ explode();
+ break;
+ }
+ break;
+
+ }
+ // check collision with exit and traps
+ int s = stencilMid();
+ switch (s & (Stencil.MSK_TRAP|Stencil.MSK_EXIT)) {
+ case Stencil.MSK_TRAP_DROWN:
+ if (type != Type.DROWNING) {
+ newType = Type.DROWNING;
+ SpriteObject spr = GameController.getLevel().getSprObject(Stencil.getObjectID(s));
+ GameController.sound.play(spr.getSound());
+ }
+ break;
+ case Stencil.MSK_TRAP_DIE:
+ if (type != Type.TRAPPED) {
+ SpriteObject spr = GameController.getLevel().getSprObject(Stencil.getObjectID(s));
+ if (spr.canBeTriggered()) {
+ if (spr.trigger()) {
+ GameController.sound.play(spr.getSound());
+ newType = Type.TRAPPED;
+ }
+ } else {
+ GameController.sound.play(spr.getSound());
+ newType = Type.TRAPPED;
+ }
+ if ( type == Type.STOPPER || type == Type.BOMBER_STOPPER) {
+ // erase stopper mask
+ Mask m = lemmings[getOrdinal(Type.STOPPER)].getMask(dir);
+ m.clearType(maskX,maskY,0,Stencil.MSK_STOPPER);
+ }
+ }
+ break;
+ case Stencil.MSK_TRAP_REPLACE: {
+ SpriteObject spr = GameController.getLevel().getSprObject(Stencil.getObjectID(s));
+ if (spr.canBeTriggered()) {
+ if (spr.trigger()) {
+ GameController.sound.play(spr.getSound());
+ hasDied = true;
+ }
+ } else {
+ GameController.sound.play(spr.getSound());
+ hasDied = true;
+ }
+ if ( type == Type.STOPPER || type == Type.BOMBER_STOPPER) {
+ // erase stopper mask
+ Mask m = lemmings[getOrdinal(Type.STOPPER)].getMask(dir);
+ m.clearType(maskX,maskY,0,Stencil.MSK_STOPPER);
+ }
+ break; }
+ case Stencil.MSK_EXIT:
+ switch (type) {
+ case WALKER:
+ case JUMPER:
+ case BASHER:
+ case MINER:
+ case BUILDER:
+ case DIGGER:
+ SpriteObject spr = GameController.getLevel().getSprObject(Stencil.getObjectID(s));
+ newType = Type.EXITING;
+ GameController.sound.play(spr.getSound());
+ break;
+ }
+ break;
+ }
+ // animate
+ if (oldType == newType) {
+ boolean trigger = false;
+ switch (lemRes.animMode) {
+ case LOOP:
+ if(++frameIdx >= lemRes.frames*TIME_SCALE)
+ frameIdx = 0;
+ if (lemRes.maskStep > 0 && frameIdx % (lemRes.maskStep*TIME_SCALE) == 0)
+ trigger = true;
+ break;
+ case ONCE:
+ if (frameIdx < lemRes.frames*TIME_SCALE-1)
+ frameIdx++;
+ else
+ trigger = true;
+ break;
+ }
+ if (trigger) {
+ // Trigger condition reached?
+ switch (type) {
+ case BOMBER_STOPPER: {
+ Mask m = lemmings[getOrdinal(Type.STOPPER)].getMask(dir);
+ m.clearType(maskX,maskY,0,Stencil.MSK_STOPPER);}
+ //$FALL-THROUGH$
+ case BOMBER:
+ explode();
+ break;
+ case SPLAT:
+ case DROWNING:
+ case TRAPPED:
+ hasDied = true;
+ break;
+ case EXITING:
+ hasLeft = true;
+ GameController.increaseLeft();
+ break;
+ case FLOATER_START:
+ type = Type.FLOATER; // should never happen
+ //$FALL-THROUGH$
+ case FLOATER:
+ frameIdx -= 5*TIME_SCALE; // rewind 5 frames
+ break;
+ case CLIMBER_TO_WALKER:
+ newType = Type.WALKER;
+ y -= 10; // why is this needed? could be done via foot coordinates?
+ break;
+ case DIGGER:
+ // the dig mask must be applied to the bottom of the lemming
+ {Mask m = lemRes.getMask(dir);
+ int sx = screenX();
+ int sy = screenY();
+ m.eraseMask(sx,sy,0,Stencil.MSK_STEEL);
+
+ // check for conversion to walker when hitting steel
+ if (lemRes.getImask(dir).checkType(sx, sy, 0, Stencil.MSK_STEEL)) {
+ GameController.sound.play(GameController.SND_CHINK);
+ newType = Type.WALKER;
+ } else
+ y += 2; // move down
+
+ // check for conversion to faller
+ int freeMin = Integer.MAX_VALUE;
+ free = 0;
+ int xOld = x;
+ for (int i=-6; i<6; i++) { // should be 14 pixels, here it's more like 12
+ x = xOld+i;
+ if (x<0)
+ x=0;
+ else if (x>=Level.WIDTH)
+ x=Level.WIDTH;
+ if ((free = freeBelow(FLOATER_STEP)) < freeMin)
+ freeMin = free;
+ }
+ x = xOld;
+ free = freeMin;
+ if (free > 0) {
+ //convert to faller or walker
+ if (free >= FALL_DISTANCE_FALL)
+ newType = Type.FALLER;
+ else
+ newType = Type.FALLER;
+ if (free >= FALLER_STEP)
+ y += FALLER_STEP;
+ else
+ y += free;
+ }
+
+ break;}
+ case BUILDER_END:
+ newType = Type.WALKER;
+ break;
+ }
+ }
+ }
+ changeType(oldType, newType);
+ }
+
+ /**
+ * Check if a Lemming is to be turned by a stopper/blocker.
+ * @return true if Lemming is to be turned, false otherwise
+ */
+ private boolean turnedByStopper() {
+ int s = (stencilMid()&Stencil.MSK_STOPPER);
+
+ if (s == Stencil.MSK_STOPPER_LEFT && dir==Direction.RIGHT) {
+ dir = Direction.LEFT;
+ return true;
+ }
+ if (s == Stencil.MSK_STOPPER_RIGHT && dir==Direction.LEFT) {
+ dir = Direction.RIGHT;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Change skill/type.
+ * @param oldType old skill/type of Lemming
+ * @param newType new skill/type of Lemming
+ */
+ private void changeType(final Type oldType, final Type newType) {
+ if (oldType != newType) {
+ type = newType;
+ lemRes = lemmings[getOrdinal(type)];
+ if (newType == Type.DIGGER)
+ frameIdx = lemRes.frames*TIME_SCALE-1; // start digging immediately
+ else
+ frameIdx = 0;
+
+ // some types can't change the skill - check this
+ switch (newType) {
+ case WALKER:
+ //insideStopper = (stencilMid()&Stencil.MSK_STOPPER) != 0;
+ case BASHER:
+ case BUILDER:
+ case BUILDER_END:
+ case DIGGER:
+ case MINER:
+ canChangeSkill = true;
+ break;
+ default:
+ canChangeSkill = false;
+ }
+ }
+ }
+
+ /**
+ * Let the Lemming explode.
+ */
+ private void explode () {
+ GameController.sound.play(GameController.SND_EXPLODE);
+ // create particle explosion
+ GameController.addExplosion(midX(), midY());
+ hasDied = true;
+ changeType(type, Type.BOMBER);
+ // consider height difference between lemming and mask
+ Mask m = lemRes.getMask(Direction.RIGHT);
+ // check if lemming is standing on steel
+ int sy = y+1;
+ if (x > 0 && x < Level.WIDTH && sy > 0 && sy < Level.HEIGHT)
+ m.eraseMask(x-m.getWidth()/2,midY()-m.getHeight()/2+3,0,Stencil.MSK_STEEL);
+ // if ((Core.stencil.get(x + sy*Level.width) & Stencil.MSK_STEEL) == 0)
+ // m.eraseMask(x-m.width/2,midY()-m.height/2,0,0);
+ }
+
+
+ /**
+ * Get stencil value from the middle of the lemming
+ * @return stencil value from the middle of the lemming
+ */
+ private int stencilMid() {
+ int xm = x;
+ int ym = y-lemRes.size;
+ int retval;
+ if (xm>0 && xm<Level.WIDTH && ym > 0 && ym < Level.HEIGHT)
+ retval = GameController.getStencil().get(xm+Level.WIDTH*ym);
+ else
+ retval = Stencil.MSK_EMPTY;
+ return retval;
+ }
+
+ /**
+ * Check if bashing is possible.
+ * @return true if bashing is possible, false otherwise.
+ */
+ private boolean canBash() {
+ int xm = midX();
+ int ypos = Level.WIDTH*(y-BASHER_CHECK_STEP) ;
+ int xb;
+ int bricks = 0;
+ for (int i=16; i<25; i++) {
+ if (dir == Direction.RIGHT)
+ xb = xm+i;
+ else
+ xb = xm-i;
+ int sval = GameController.getStencil().get(xb+ypos);
+ if ((sval & Stencil.MSK_NO_DIG_LEFT) != 0 && dir == Direction.LEFT)
+ return false;
+ if ((sval & Stencil.MSK_NO_DIG_RIGHT) != 0 && dir == Direction.RIGHT)
+ return false;
+ if ((sval & Stencil.MSK_STEEL) != 0)
+ return false;
+ if ((sval & Stencil.MSK_WALK_ON) == Stencil.MSK_BRICK)
+ bricks++;
+ }
+ if (bricks>0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Check if digging is possible.
+ * @return true if digging is possible, false otherwise.
+ */
+ private boolean canDig() {
+ int ypos = Level.WIDTH*(y+1);
+ int xm = x;
+ int sval = GameController.getStencil().get(xm+ypos);
+ if ((sval & Stencil.MSK_WALK_ON) == Stencil.MSK_BRICK)
+ return true;
+ return false;
+ }
+
+ /**
+ * Check if mining is possible.
+ * @return true if mining is possible, false otherwise.
+ */
+ private boolean canMine() {
+ int ypos = Level.WIDTH*(y+1);
+ int bricks= 0;
+ int xMin;
+ int xMax;
+ if (dir == Direction.RIGHT) {
+ xMin = x;
+ xMax = x-lemRes.footX+lemRes.width;
+ } else {
+ xMin = x-lemRes.footX;
+ xMax = x;
+ }
+ for (int xb = xMin; xb < xMax; xb++) {
+ int sval = GameController.getStencil().get(xb+ypos);
+ if ((sval & Stencil.MSK_NO_DIG_LEFT) != 0 && dir == Direction.LEFT)
+ return false;
+ if ((sval & Stencil.MSK_NO_DIG_RIGHT) != 0 && dir == Direction.RIGHT)
+ return false;
+ if ((sval & Stencil.MSK_STEEL) != 0)
+ return false;
+ if ((sval & Stencil.MSK_WALK_ON) == Stencil.MSK_BRICK)
+ bricks++;
+ }
+ if (bricks>0)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Get number of free pixels below the lemming (max of step is checked).
+ * @return number of free pixels below the lemming
+ */
+ private int freeBelow(final int step) {
+ if (x<0 || x >= Level.WIDTH)
+ return 0;
+ int free = 0;
+ int pos = x;
+ Stencil stencil = GameController.getStencil();
+ int yb = y+1;
+ pos = x+yb*Level.WIDTH; // line below the lemming
+ for (int i=0; i<step; i++) {
+ if (yb+i >= Level.HEIGHT)
+ return FALL_DISTANCE_FORCE_FALL; // convert most skill to faller
+ int s = stencil.get(pos);
+ if ((s & Stencil.MSK_WALK_ON) == Stencil.MSK_EMPTY)
+ free++;
+ else break;
+ pos += Level.WIDTH;
+ }
+ return free;
+ }
+
+ /**
+ * Check if Lemming reached the left or right border of the level and was turned.
+ * @return true if lemming was turned, false otherwise.
+ */
+ private boolean flipDirBorder() {
+ boolean flip = false;
+ if (lemRes.dirs >1) {
+ if (x<0) {
+ x=0;
+ flip = true;
+ } else if (x >= Level.WIDTH) {
+ x = Level.WIDTH-1;
+ flip = true;
+ }
+ }
+ if (flip)
+ dir = (dir==Direction.RIGHT) ? Direction.LEFT : Direction.RIGHT;
+ return flip;
+ }
+
+ /**
+ * Get number of free pixels above the lemming (max of step is checked).
+ * @return number of free pixels above the lemming
+ */
+ private int freeAbove(final int step) {
+ if (x<0 || x >= Level.WIDTH)
+ return 0;
+
+ int free = 0;
+ int pos;
+ int ym = midY();
+ Stencil stencil = GameController.getStencil();
+ pos = x + ym*Level.WIDTH;
+ for (int i=0; i<step; i++) {
+ if (ym-i <= 0)
+ return -1; // splat
+ if ((stencil.get(pos) & Stencil.MSK_WALK_ON) == Stencil.MSK_EMPTY
+ /*|| (stencil.get(pos+1) & Stencil.MSK_WALK_ON) == Stencil.MSK_EMPTY */ )
+ free++;
+ else break;
+ pos -= Level.WIDTH;
+ }
+ return free;
+ }
+
+ /**
+ * Check if Lemming has fallen to/through the bottom of the level.
+ * @return true if Lemming has fallen to/through the bottom of the level, false otherwise
+ */
+ private boolean crossedLowerBorder() {
+ if (y >= Level.HEIGHT) {
+ hasDied = true;
+ GameController.sound.play(GameController.SND_DIE);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the number of pixels of walkable ground above the Lemmings foot.
+ * @return number of pixels of walkable ground above the Lemmings foot.
+ */
+ private int aboveGround() {
+ if (x<0 || x >= Level.WIDTH)
+ return Level.HEIGHT-1;
+
+ int ym = y;
+ if (ym >= Level.HEIGHT)
+ return Level.HEIGHT-1;
+ int pos = x;
+ Stencil stencil = GameController.getStencil();
+ pos += ym*Level.WIDTH;
+ int levitation;
+ for (levitation=0; levitation<WALKER_OBSTACLE_HEIGHT; levitation++, pos -= Level.WIDTH, ym--) {
+ if (ym<0)
+ return WALKER_OBSTACLE_HEIGHT+1; // forbid leaving level to the top
+ if ( (stencil.get(pos) & Stencil.MSK_WALK_ON) == Stencil.MSK_EMPTY)
+ break;
+ }
+ return levitation;
+ }
+
+ /**
+ * Check if climber reached a plateau he can walk on.
+ * @return true if climber reached a plateau he can walk on, false otherwise
+ */
+ private boolean reachedPlateau() {
+ if (x<2 || x >= Level.WIDTH-2)
+ return false;
+ int ym = midY();
+ if (ym>= Level.HEIGHT || ym<0)
+ return false;
+ int pos = x;
+ if (dir == Direction.LEFT)
+ pos -= 2;
+ else
+ pos += 2;
+ pos += ym*Level.WIDTH;
+ if ((GameController.getStencil().get(pos) & Stencil.MSK_WALK_ON) == Stencil.MSK_EMPTY)
+ return true;
+ else
+ return false;
+ }
+
+ /**
+ * Replace a color in the animation frame with another color.
+ * Used to patch the color of debris from pink color to a level specific color.
+ * @param findCol color to find
+ * @param replaceCol color to replace with
+ */
+ public static void patchColors(final int findCol, final int replaceCol) {
+ for (int l = 0; l<NUM_RESOURCES; l++) { // go through all the lemmings
+ LemmingResource lr = lemmings[l];
+ for (int f = 0; f<lr.frames; f++) // go through all frames
+ for (int d=0; d<lr.dirs; d++) // go though all dirs
+ for (int xp=0; xp<lr.width; xp++)
+ for (int yp=0; yp<lr.height; yp++) {
+ BufferedImage i = lr.getImage(Direction.get(d),f);
+ if (i.getRGB(xp,yp) == findCol)
+ i.setRGB(xp,yp,replaceCol);
+ }
+ }
+ }
+
+ /**
+ * Load images used for Lemming animations.
+ * @param cmp parent component
+ * @throws ResourceException
+ */
+ public static void loadLemmings(final Component cmp) throws ResourceException {
+ explodeFont = new ExplodeFont(cmp);
+ MediaTracker tracker = new MediaTracker(cmp);
+ // read lemmings definition file
+ String fn = Core.findResource(LEMM_INI_STR);
+ Props p = new Props();
+ if (!p.load(fn))
+ throw new ResourceException(LEMM_INI_STR);
+ lemmings = new LemmingResource[NUM_RESOURCES];
+ // read lemmings
+ int def[] = {-1};
+ for (int i = 0; true; i++) {
+ int[] val = p.get("lemm_"+i,def);
+ int type;
+ if (val.length == 3) {
+ // frames, directions, animation type
+ type = i;
+ if (lemmings[type] == null) {
+ BufferedImage sourceImg = ToolBox.ImageToBuffered(
+ Core.loadImage(tracker, "misc/lemm_"+i+".gif"), Transparency.BITMASK);
+ try {
+ tracker.waitForAll();
+ } catch (InterruptedException ex) {}
+ lemmings[type] = new LemmingResource(sourceImg, val[0], val[1]);
+ lemmings[type].animMode = (val[2]==0) ? Animation.LOOP : Animation.ONCE;
+ }
+ } else break;
+ // read mask
+ val = p.get("mask_"+i,def);
+ if (val.length == 3) {
+ // mask_Y: frames, directions, step
+ type = i;
+ Image sourceImg = Core.loadImage(tracker, "misc/mask_"+i+".gif");
+ Mask mask = new Mask(ToolBox.ImageToBuffered(sourceImg, Transparency.BITMASK), val[0]);
+ lemmings[type].setMask(Direction.RIGHT, mask);
+ int dirs = val[1];
+ if (dirs > 1) {
+ Mask maskLeft = new Mask(ToolBox.flipImageX(ToolBox.ImageToBuffered(sourceImg,Transparency.BITMASK)), val[0]);
+ lemmings[type].setMask(Direction.LEFT, maskLeft);
+ }
+ lemmings[type].maskStep = val[2];
+ }
+ // read indestructible mask
+ val = p.get("imask_"+i,def);
+ if (val.length == 2) {
+ // mask_Y: type, frames, directions, step
+ type = i;
+ Image sourceImg = Core.loadImage(tracker, "misc/imask_"+i+".gif");
+ Mask mask = new Mask(ToolBox.ImageToBuffered(sourceImg, Transparency.BITMASK), val[0]);
+ lemmings[type].setImask(Direction.RIGHT, mask);
+ int dirs = val[1];
+ if (dirs > 1) {
+ Mask maskLeft = new Mask(ToolBox.flipImageX(ToolBox.ImageToBuffered(sourceImg,Transparency.BITMASK)), val[0]);
+ lemmings[type].setImask(Direction.LEFT, maskLeft);
+ }
+ }
+ // read foot position and size
+ val = p.get("pos_"+i,def);
+ if (val.length == 3) {
+ lemmings[type].footX = val[0];
+ lemmings[type].footY = val[1];
+ lemmings[type].size = val[2];
+ } else break;
+ }
+ }
+
+ /**
+ * Get display name of this Lemming.
+ * @return display name of this Lemming
+ */
+ public String getName() {
+ Type t;
+ switch (type) {
+ case BOMBER_STOPPER:
+ t = Type.BOMBER;
+ break;
+ case FLOATER_START:
+ t = Type.FLOATER;
+ break;
+ default:
+ t = type;
+ }
+ String n = LEMM_NAMES[getOrdinal(t)];
+ if (n.length()>0) {
+ if (canFloat) {
+ if (canClimb)
+ n += "(A)";
+ else if (t != Type.FLOATER)
+ n += "(F)";
+ } else {
+ if (canClimb && t != Type.CLIMBER)
+ n += "(C)";
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Get current skill/type of this Lemming.
+ * @return current skill/type of this Lemming
+ */
+ public Type getSkill() {
+ return type;
+ }
+
+ /**
+ * Set new skill/type of this Lemming.
+ * @param skill new skill/type
+ * @return true if a change was possible, false otherwise
+ */
+ public boolean setSkill(final Type skill) {
+ if (skill == type || hasDied)
+ return false;
+ // check types which can't even get an additional skill anymore
+ switch (type) {
+ case DROWNING:
+ case EXITING:
+ case SPLAT:
+ case TRAPPED:
+ case BOMBER:
+ if (skill == Type.NUKE) {
+ if (nuke)
+ return false;
+ nuke = true;
+ if (explodeNumCtr==0) {
+ explodeNumCtr = 5;
+ explodeCtr = 0;
+ return true;
+ } else return false;
+ }
+ return false;
+ }
+ // check additional skills
+ switch (skill) {
+ case CLIMBER:
+ if (canClimb)
+ return false;
+ canClimb = true;
+ return true;
+ case FLOATER:
+ if (canFloat)
+ return false;
+ canFloat = true;
+ return true;
+ case NUKE: // special case: nuke request
+ if (nuke)
+ return false;
+ nuke = true;
+ //$FALL-THROUGH$
+ case BOMBER:
+ if (explodeNumCtr==0) {
+ explodeNumCtr = 5;
+ explodeCtr = 0;
+ return true;
+ } else return false;
+ }
+ // check main skills
+ if (canChangeSkill) {
+ switch (skill) {
+ case DIGGER:
+ if (canDig()) {
+ //y += DIGGER_GND_OFFSET;
+ changeType(type, skill);
+ counter = 0;
+ return true;
+ } else return false;
+ case MINER:
+ if (canMine()) {
+ //y += 2;
+ changeType(type, skill);
+ counter = 0;
+ return true;
+ } else return false;
+ case BASHER:
+ //if (canBash(true)) {
+ changeType(type, skill);
+ counter = 0;
+ return true;
+ //} else return false;
+ case BUILDER: {
+ //int fa = freeAbove(4);
+ int fb = freeBelow(FALLER_STEP);
+ if (/*fa <= 0 ||*/ fb != 0)
+ return false;
+ //x = x & ~1; // start building at even positions
+ changeType(type, skill);
+ counter = 0;
+ return true; }
+ case STOPPER: {
+ Mask m = Lemming.getResource(Type.STOPPER).getMask(Direction.LEFT);
+ maskX = screenX();
+ maskY = screenY();
+ if (m.checkType(maskX, maskY, 0, Stencil.MSK_STOPPER))
+ return false; // overlaps existing stopper
+ changeType(type, skill);
+ counter = 0;
+ // set stopper mask
+ m.setStopperMask(maskX,maskY,x);
+ return true;}
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get width of animation frame in pixels.
+ * @return width of animation frame in pixels
+ */
+ public int width() {
+ return lemRes.width;
+ }
+
+ /**
+ * Get height of animation frame in pixels.
+ * @return height of animation frame in pixels
+ */
+ public int height() {
+ return lemRes.height;
+ }
+
+ /**
+ * Get static resource for a skill/type
+ * @param type skill/type
+ * @return static resource for this skill/type
+ */
+ private static LemmingResource getResource(final Type type) {
+ return lemmings[getOrdinal(type)];
+ }
+
+ /**
+ * Get X coordinate of upper left corner of animation frame.
+ * @return X coordinate of upper left corner of animation frame
+ */
+ public int screenX() {
+ if (lemRes.dirs == 1 || dir == Direction.RIGHT) {
+ return x - lemRes.footX;
+ } else {
+ return x - lemRes.width + lemRes.footX;
+ }
+ }
+
+ /**
+ * Get Y coordinate of upper left corner of animation frame
+ * @return Y coordinate of upper left corner of animation frame
+ */
+ public int screenY() {
+ return y - lemRes.footY;
+ }
+
+ /**
+ * Get X coordinate of collision position in pixels.
+ * @return X coordinate of collision position in pixels.
+ */
+ public int midX() {
+ return x;
+ }
+
+ /**
+ * Collision position
+ * @return Position inside lemming which is used for collisions
+ */
+ public int midY() {
+ return y - lemRes.size;
+ }
+
+ /**
+ * Get heading of Lemming.
+ * @return heading of Lemming
+ */
+ public Direction getDirection() {
+ return dir;
+ }
+
+ /**
+ * Get current animation frame for this Lemming.
+ * @return current animation frame for this Lemming
+ */
+ public BufferedImage getImage() {
+ return lemRes.getImage(dir,frameIdx/TIME_SCALE);
+ }
+
+ /**
+ * Get image for explosion countdown.
+ * @return image for explosion countdown (or null if no explosion countdown)
+ */
+ public BufferedImage getCountdown() {
+ if (explodeNumCtr==0)
+ return null;
+ else return explodeFont.getImage(explodeNumCtr-1);
+ }
+
+ /**
+ * Used for replay: start to display the selection image.
+ */
+ public void setSelected() {
+ selectCtr = 20;
+ }
+
+ /**
+ * Get the selection image for replay.
+ * @return the selection image (or null if no selection displayed)
+ */
+ public BufferedImage getSelectImg() {
+ if (selectCtr==0)
+ return null;
+ else return MiscGfx.getImage(MiscGfx.Index.SELECT);
+ }
+
+ /**
+ * Get: Lemming has died.
+ * @return true if Lemming has died, false otherwise
+ */
+ public boolean hasDied() {
+ return hasDied;
+ }
+
+ /**
+ * Get: Lemming has left the level.
+ * @return true if Lemming has left the level, false otherwise
+ */
+ public boolean hasLeft() {
+ return hasLeft;
+ }
+
+ /**
+ * Get: Lemming is to be nuked.
+ * @return true if Lemming is to be nuked, false otherwise
+ */
+ public boolean nuke() {
+ return nuke;
+ }
+
+ /**
+ * Get: Lemming can float.
+ * @return true if Lemming can float, false otherwise
+ */
+ public boolean canFloat() {
+ return canFloat;
+ }
+
+ /**
+ * Get: Lemming can climb.
+ * @return true if Lemming can climb, false otherwise
+ */
+ public boolean canClimb() {
+ return canClimb;
+ }
+
+ /**
+ * Get: Lemming can get a new skill.
+ * @return true if Lemming can get a new skill, false otherwise
+ */
+ public boolean canChangeSkill() {
+ return canChangeSkill;
+ }
+
+}
+
+/**
+ * Storage class for a Lemming.
+ * @author Volker Oth
+ */
+class LemmingResource {
+ /** relative foot X position in pixels inside bitmap */
+ int footX;
+ /** relative foot Y position in pixels inside bitmap */
+ int footY;
+ /** mask collision ("mid") position above foot in pixels */
+ int size;
+ /** width of image in pixels */
+ int width;
+ /** height of image in pixels */
+ int height;
+ /** number of animation frames */
+ int frames;
+ /** animation mode */
+ Lemming.Animation animMode;
+ /** number of directions (1 or 2) */
+ int dirs;
+ int maskStep;
+ /** array of images to store the animation [Direction][AnimationFrame] */
+ private BufferedImage img[][];
+ /** array of removal masks used for digging/bashing/mining/explosions etc. [Direction] */
+ private Mask mask[];
+ /** array of check masks for indestructible pixels [Direction] */
+ private Mask iMask[];
+
+ /**
+ * Constructor.
+ * @param sourceImg image containing animation frames (one above the other)
+ * @param animFrames number of animation frames.
+ * @param directions number of directions (1 or 2)
+ */
+ LemmingResource(final BufferedImage sourceImg, final int animFrames, final int directions) {
+ img = new BufferedImage[directions][];
+ mask = new Mask[directions];
+ iMask = new Mask[directions];
+ frames = animFrames;
+ width = sourceImg.getWidth(null);
+ height = sourceImg.getHeight(null)/animFrames;
+ dirs = directions;
+ animMode = Lemming.Animation.NONE;
+ img[Lemming.Direction.RIGHT.ordinal()] = ToolBox.getAnimation(sourceImg, animFrames, Transparency.BITMASK);
+ if (dirs>1)
+ img[Lemming.Direction.LEFT.ordinal()] = ToolBox.getAnimation(ToolBox.flipImageX(sourceImg), animFrames, Transparency.BITMASK);
+ }
+
+ /**
+ * Get the mask for stencil manipulation.
+ * @param dir Direction
+ * @return mask for stencil manipulation
+ */
+ Mask getMask(final Lemming.Direction dir) {
+ if (dirs > 1)
+ return mask[dir.ordinal()];
+ else
+ return mask[0];
+ }
+
+ /**
+ * Set the mask for stencil manipulation.
+ * @param dir Direction
+ * @param m mask for stencil manipulation
+ */
+ void setMask(final Lemming.Direction dir, final Mask m) {
+ if (dirs > 1)
+ mask[dir.ordinal()] = m;
+ else
+ mask[0] = m;
+ }
+
+ /**
+ * Get the mask for checking of indestructible pixels.
+ * @param dir Direction
+ * @return mask for checking of indestructible pixels
+ */
+ Mask getImask(final Lemming.Direction dir) {
+ if (dirs > 1)
+ return iMask[dir.ordinal()];
+ else
+ return iMask[0];
+ }
+
+ /**
+ * Set the mask for checking of indestructible pixels
+ * @param dir Direction
+ * @param m mask for checking of indestructible pixels
+ */
+ void setImask(final Lemming.Direction dir, final Mask m) {
+ if (dirs > 1)
+ iMask[dir.ordinal()] = m;
+ else
+ iMask[0] = m;
+ }
+
+ /**
+ * Get specific animation frame.
+ * @param dir Direction.
+ * @param frame Index of animation frame.
+ * @return specific animation frame
+ */
+ BufferedImage getImage(final Lemming.Direction dir, final int frame) {
+ if (dirs > 1)
+ return img[dir.ordinal()][frame];
+ else
+ return img[0][frame];
+ }
+}
+
+
+/**
+ * Used to manage the font for the explosion counter.
+ * @author Volker Oth
+ */
+class ExplodeFont {
+
+ /**
+ * Constructor.
+ * @param cmp parent component
+ * @throws ResourceException
+ */
+ ExplodeFont(final Component cmp) throws ResourceException {
+ Image sourceImg = Core.loadImage("misc/countdown.gif");
+ img = ToolBox.getAnimation(sourceImg,5,Transparency.BITMASK);
+ }
+
+ /**
+ * Get image for a counter value (0..9)
+ * @param num counter value (0..9)
+ * @return
+ */
+ BufferedImage getImage(final int num) {
+ return img[num];
+ }
+
+ /** array of images for each counter value */
+ private BufferedImage img[];
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/Level.java b/jeu-test/Lemmini/0.84/src/Game/Level.java
new file mode 100644
index 0000000..55e9cfb
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Level.java
@@ -0,0 +1,1026 @@
+package Game;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Transparency;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.awt.image.PixelGrabber;
+import java.awt.image.WritableRaster;
+import java.util.ArrayList;
+
+import GameUtil.Sprite;
+import Tools.Props;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Load a level, paint level, create background stencil.
+ *
+ * @author Volker Oth
+ */
+public class Level {
+ /** maximum width of level */
+ public final static int WIDTH = 1664*2;
+ /** maximum height of level */
+ public final static int HEIGHT = 160*2;
+ /** array of default ARGB colors for particle effects */
+ public final static int DEFAULT_PARTICLE_COLORS[] = { 0xff00ff00, 0xff0000ff, 0xffffffff, 0xffffffff, 0xffff0000 };
+
+ /** array of default styles */
+ private final static String STYLES[] = { "dirt", "fire", "marble", "pillar",
+ "crystal", "brick", "rock", "snow", "Bubble", "special" };
+ /** template color to be replaced with debris color */
+ private final static int TEMPLATE_COLOR = 0xffff00ff;
+
+ /** array of normal sprite objects - no transparency, drawn behind background image */
+ private SpriteObject sprObjBehind[];
+ /** array of special sprite objects - with transparency, drawn above background image */
+ private SpriteObject sprObjFront[];
+ /** array of all sprite objects (in front and behind) */
+ private SpriteObject sprObjects[];
+ /** array of level entries */
+ private Entry entries[];
+ /** release rate : 0 is slowest, 0x0FA (250) is fastest */
+ private int releaseRate;
+ /** number of Lemmings in this level (maximum 0x0072 in original LVL format) */
+ private int numLemmings;
+ /** number of Lemmings to rescue : should be less than or equal to number of Lemmings */
+ private int numToRescue;
+ /** time limit in seconds */
+ private int timeLimitSeconds;
+ /** number of climbers in this level : max 0xfa (250) */
+ private int numClimbers;
+ /** number of floaters in this level : max 0xfa (250) */
+ private int numFloaters;
+ /** number of bombers in this level : max 0xfa (250) */
+ private int numBombers;
+ /** number of blockers in this level : max 0xfa (250) */
+ private int numBlockers;
+ /** number of builders in this level : max 0xfa (250) */
+ private int numBuilders;
+ /** number of bashers in this level : max 0xfa (250) */
+ private int numBashers;
+ /** number of miners in this level : max 0xfa (250) */
+ private int numMiners;
+ /** number of diggers in this level : max 0xfa (250) */
+ private int numDiggers;
+ /** start screen x pos : 0 - 0x04f0 (1264) rounded to modulo 8 */
+ private int xPos;
+ /** background color as ARGB */
+ private int bgCol;
+ /** background color */
+ private Color bgColor;
+ /** color used for steps and debris */
+ private int debrisCol;
+ /** array of ARGB colors used for particle effects */
+ private int particleCol[];
+ /** maximum safe fall distance */
+ private int maxFallDistance;
+ /** this level is a SuperLemming level (runs faster) */
+ private boolean superlemming;
+ /** level is completely loaded */
+ private boolean ready = false;
+ /** objects like doors - originally 32 objects where each consists of 8 bytes */
+ private ArrayList<LvlObject> objects;
+ /** background tiles - every pixel in them is interpreted as brick in the stencil */
+ private Image tiles[];
+ /** sprite objects of all sprite objects available in this style */
+ private SpriteObject sprObjAvailable[];
+ /** terrain the Lemmings walk on etc. - originally 400 tiles, 4 bytes each */
+ private ArrayList<Terrain> terrain;
+ /** steel areas which are indestructible - originally 32 objects, 4 bytes each */
+ private ArrayList<Steel> steel; //
+ /** level name - originally 32 bytes ASCII - filled with whitespaces */
+ private String lvlName;
+ /** used to read in the configuration file */
+ private Props props;
+
+ /**
+ * Load a level and all level resources.
+ * @param fname file name
+ * @throws ResourceException
+ * @throws LemmException
+ */
+ void loadLevel(final String fname) throws ResourceException, LemmException {
+ ready = false;
+ // read level properties from file
+ Props p = new Props();
+ if (!p.load(fname))
+ throw new ResourceException(fname);
+
+ // read name
+ lvlName = p.get("name","");
+ //out(fname + " - " + lvlName);
+ maxFallDistance = p.get("maxFallDistance", GameController.getCurLevelPack().getMaxFallDistance());
+ // read configuration in big endian word
+ releaseRate = p.get("releaseRate",-1);
+ //out("releaseRate = " + releaseRate);
+ numLemmings = p.get("numLemmings",-1);
+ //out("numLemmings = " + numLemmings);
+ numToRescue = p.get("numToRescue",-1);
+ //out("numToRescue = " + numToRescue);
+ timeLimitSeconds = p.get("timeLimitSeconds",-1);
+ if (timeLimitSeconds == -1) {
+ int timeLimit = p.get("timeLimit",-1);
+ timeLimitSeconds = timeLimit*60;
+ }
+
+ //out("timeLimit = " + timeLimit);
+ numClimbers = p.get("numClimbers",-1);
+ //out("numClimbers = " + numClimbers);
+ numFloaters = p.get("numFloaters",-1);
+ //out("numFloaters = " + numFloaters);
+ numBombers = p.get("numBombers",-1);
+ //out("numBombers = " + numBombers);
+ numBlockers = p.get("numBlockers",-1);
+ //out("numBlockers = " + numBlockers);
+ numBuilders = p.get("numBuilders",-1);
+ //out("numBuilders = " + numBuilders);
+ numBashers = p.get("numBashers",-1);
+ //out("numBashers = " + numBashers);
+ numMiners = p.get("numMiners",-1);
+ //out("numMiners = " + numMiners);
+ numDiggers = p.get("numDiggers",-1);
+ //out("numDiggers = " + numDiggers);
+ xPos = p.get("xPos",-1);
+ //out("xPos = " + xPos);
+ String strStyle = p.get("style","");
+ int style;
+ style = -1;
+ for (int i=0; i<STYLES.length; i++)
+ if (strStyle.equalsIgnoreCase(STYLES[i])) {
+ style = i;
+ break;
+ }
+ //out("style = " + styles[style]);
+ superlemming = p.get("superlemming", false);
+
+ // read objects
+ //out("\n[Objects]");
+ objects = new ArrayList<LvlObject>();
+ int def[] = {-1};
+ for (int i = 0; true /*i < 32*/; i++) {
+ int[] val = p.get("object_"+i,def);
+ if (val.length == 5) {
+ LvlObject obj = new LvlObject(val);
+ objects.add(obj);
+ //out("" + obj.id + ", " + obj.xPos + ", " + obj.yPos + ", "+ obj.paintMode + ", " + obj.upsideDown);
+ } else break;
+ }
+ // read terrain
+ //out("\n[Terrain]");
+ terrain = new ArrayList<Terrain>();
+ for (int i = 0; true /*i < 400*/; i++) {
+ int[] val = p.get("terrain_"+i,def);
+ if (val.length == 4) {
+ Terrain ter = new Terrain(val);
+ terrain.add(ter);
+ //out("" + ter.id + ", " + ter.xPos + ", " + ter.yPos + ", " + ter.modifier);
+ } else break;
+ }
+ // read steel blocks
+ //out("\n[Steel]");
+ steel = new ArrayList<Steel>();
+ for (int i = 0; true/*i < 32*/; i++) {
+ int[] val = p.get("steel_"+i,def);
+ if (val.length == 4) {
+ Steel stl = new Steel(val);
+ steel.add(stl);
+ //out("" + stl.xPos + ", " + stl.yPos + ", " + stl.width + ", " + stl.height);
+ } else break;
+ }
+ // load objects
+ sprObjAvailable = null;
+ // first load the data from object descriptor file xxx.ini
+ String fnames = Core.findResource("styles/"+strStyle + "/" + strStyle + ".ini");
+ props = new Props();
+ if (!props.load(fnames)) {
+ if (style != -1)
+ throw new ResourceException(fnames);
+ else
+ throw new LemmException("Style "+strStyle+ " not existing.");
+ }
+ // load blockset
+ tiles = loadTileSet(strStyle, Core.getCmp());
+ sprObjAvailable = loadObjects(strStyle, Core.getCmp());
+ ready = true;
+ }
+
+ /**
+ * Paint a level.
+ * @param bgImage background image to draw into
+ * @param cmp parent component
+ * @param s stencil to reuse
+ * @return stencil of this level
+ */
+ Stencil paintLevel(final BufferedImage bgImage, final Component cmp, final Stencil s) {
+ // flush all resources
+ sprObjFront = null;
+ sprObjBehind = null;
+ sprObjects = null;
+ entries = null;
+ System.gc();
+ // the screenBuffer should be big enough to hold the level
+ // returns stencil buffer;
+ int bgWidth = bgImage.getWidth();
+ int bgHeight = bgImage.getHeight();
+ // try to reuse old stencil
+ Stencil stencil;
+ if (s!= null && s.getWidth() == bgWidth && s.getHeight() == bgImage.getHeight()) {
+ s.clear();
+ stencil = s;
+ } else
+ stencil = new Stencil(bgWidth,bgImage.getHeight());
+ // paint terrain
+ for (int n = 0; n < terrain.size(); n++) {
+ Terrain t = terrain.get(n);
+ Image i = tiles[t.id];
+ int width = i.getWidth(null);
+ int height = i.getHeight(null);
+
+ int source[] = new int[width * height];
+ PixelGrabber pixelgrabber = new PixelGrabber(i, 0, 0, width,
+ height, source, 0, width);
+ try {
+ pixelgrabber.grabPixels();
+ } catch (InterruptedException interruptedexception) {}
+ int tx = t.xPos;
+ int ty = t.yPos;
+ boolean upsideDown = (t.modifier & Terrain.MODE_UPSIDE_DOWN) != 0;
+ boolean overwrite = (t.modifier & Terrain.MODE_NO_OVERWRITE) == 0;
+ boolean remove = (t.modifier & Terrain.MODE_REMOVE) != 0;
+ try {
+ for (int y = 0; y < height; y++) {
+ if (y + ty < 0 || y + ty >= bgHeight)
+ continue;
+ int yLineStencil = (y + ty) * bgWidth;
+ int yLine;
+ if (upsideDown)
+ yLine = (height - y - 1) * width;
+ else
+ yLine = y * width;
+ for (int x = 0; x < width; x++) {
+ if (x + tx < 0 || x + tx >= bgWidth)
+ continue;
+ int col = source[yLine + x];
+ // ignore transparent pixels
+ if ((col & 0xff000000) == 0)
+ continue;
+ boolean paint = false;
+ if (!overwrite) {
+ // don't overwrite -> only paint if background is transparent
+ if (stencil.get(yLineStencil + tx + x) == Stencil.MSK_EMPTY)
+ paint = true;
+ } else if (remove) {
+ bgImage.setRGB(x + tx, y + ty, 0 /*bgCol*/);
+ stencil.set(yLineStencil + tx + x, Stencil.MSK_EMPTY);
+ } else
+ paint = true;
+ if (paint) {
+ bgImage.setRGB(x + tx, y + ty, col);
+ stencil.set(yLineStencil + tx + x,Stencil.MSK_BRICK);
+ }
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ }
+ }
+
+ // now for the animated objects
+ ArrayList<SpriteObject> oCombined = new ArrayList<SpriteObject>(64);
+ ArrayList<SpriteObject> oBehind = new ArrayList<SpriteObject>(64);
+ ArrayList<SpriteObject> oFront = new ArrayList<SpriteObject>(4);
+ ArrayList<Entry> entry = new ArrayList<Entry>(4);
+ AffineTransform tx;
+ for (int n = 0; n < objects.size(); n++) {
+ try {
+ LvlObject o = objects.get(n);
+ //if (sprObjAvailable[o.id].animMode != Sprite.ANIM_NONE) {
+ SpriteObject spr = new SpriteObject(sprObjAvailable[o.id]);
+ spr.setX(o.xPos);
+ spr.setY(o.yPos);
+ // affine transform for flipping
+ tx = AffineTransform.getScaleInstance(1, -1);
+ tx.translate(0, -spr.getHeight());
+ AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ BufferedImage imgSpr;
+ // check for entries (ignore upside down entries)
+ if (spr.getType() == SpriteObject.Type.ENTRY && !o.upsideDown) {
+ Entry e = new Entry(o.xPos+spr.getWidth()/2, o.yPos);
+ e.id = oCombined.size();
+ entry.add(e);
+ spr.setAnimMode(Sprite.Animation.NONE);
+ }
+ // animated
+ boolean drawOnVis = o.paintMode == LvlObject.MODE_VIS_ON_TERRAIN;
+ boolean noOverwrite = o.paintMode == LvlObject.MODE_NO_OVERWRITE;
+ boolean inFront = (drawOnVis || !noOverwrite);
+ boolean drawFull = o.paintMode == LvlObject.MODE_FULL;
+
+ if (inFront)
+ oFront.add(spr);
+ else
+ oBehind.add(spr);
+ oCombined.add(spr);
+
+ // draw stencil (only for objects that are not upside down)
+ if (!o.upsideDown) {
+ for (int y = 0; y < spr.getHeight(); y++) {
+ if (y + spr.getY() < 0 || y + spr.getY() >= bgHeight)
+ continue;
+ int yLineStencil = (y + spr.getY()) * bgWidth;
+ for (int x = 0; x < spr.getWidth(); x++) {
+ //boolean pixOverdraw = false;
+ if (x + spr.getX() < 0 || x + spr.getX() >= bgWidth)
+ continue;
+ // manage collision mask
+ // now read stencil
+ int stencilVal;
+ stencilVal = stencil.get(yLineStencil + spr.getX()+ x);
+ // store object type in mask and idx in higher byte
+ if (/*paint &&*/ spr.getType() != SpriteObject.Type.ENTRY && spr.getType() != SpriteObject.Type.PASSIVE)
+ if ((spr.getMask(x,y) & 0xff000000) != 0) { // not transparent
+ // avoid two objects on the same stencil position
+ // overlap makes it impossible to delete pixels in objects (mask operations)
+ if (Stencil.getObjectID(stencilVal) == 0) {
+ stencil.or(yLineStencil + spr.getX() + x, spr.getMaskType() | Stencil.createObjectID(n));
+ } // else: overlap - erased later in object instance
+ }
+ }
+ }
+ }
+ // remove invisible pixels from all object frames that are "in front"
+ // for upside down objects, just create the upside down copy
+ if (o.upsideDown || inFront) {
+ for (int frame = 0; frame < spr.getNumFrames(); frame++) {
+ imgSpr = ToolBox.createImage(spr.getWidth(),spr.getHeight(), Transparency.BITMASK);
+ // get flipped or normal version
+ if (o.upsideDown) {
+ // flip the image vertically
+ imgSpr = op.filter(spr.getImage(frame), imgSpr);
+ } else {
+ WritableRaster rImgSpr = imgSpr.getRaster();
+ rImgSpr.setRect(spr.getImage(frame).getRaster()); // just copy
+ }
+ // for "in front" objects the really drawn pixels have to be determined
+ if (inFront) {
+ for (int y = 0; y < spr.getHeight(); y++) {
+ if (y + spr.getY() < 0 || y + spr.getY() >= bgHeight)
+ continue;
+ int yLineStencil = (y + spr.getY()) * bgWidth;
+ for (int x = 0; x < spr.getWidth(); x++) {
+ if (x + spr.getX() < 0 || x + spr.getX() >= bgWidth)
+ continue;
+ // now read stencil
+ int stencilVal = stencil.get(yLineStencil + spr.getX()+ x);
+ int stencilValMasked = stencilVal & Stencil.MSK_WALK_ON;
+ boolean paint =
+ drawFull || (stencilValMasked != 0 && drawOnVis) || (stencilValMasked == 0 && noOverwrite);
+ // hack for overlap:
+ int id = Stencil.getObjectID(stencilVal);
+ // check if a different interactive object was already entered at this pixel position
+ // however: exits must always be painted
+ // also: passive objects will always be painted
+ if ( inFront && spr.getType() != SpriteObject.Type.PASSIVE && spr.getType() != SpriteObject.Type.EXIT && id!=0 && id != n)
+ paint = false;
+ // sprite screenBuffer pixel
+ int imgCol = imgSpr.getRGB(x,y);
+ if ((imgCol & 0xff000000) == 0)
+ continue;
+ if (!paint)
+ imgSpr.setRGB(x,y,imgCol&0xffffff); // set transparent
+ }
+ }
+ }
+ //spr.img[frame].flush(); // will be overwritten -> flush data
+ spr.setImage(frame,imgSpr);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ //System.out.println("Array out of bounds");
+ }
+ }
+
+ entries = new Entry[entry.size()];
+ entries = entry.toArray(entries);
+
+ // paint steel tiles into stencil
+ for (int n = 0; n < steel.size(); n++) {
+ Steel stl = steel.get(n);
+ int sx = stl.xPos;
+ int sy = stl.yPos;
+ for (int y = 0; y < stl.height; y++) {
+ if (y + sy < 0 || y + sy >= bgHeight)
+ continue;
+ int yLineStencil = (y + sy) * bgWidth;
+ for (int x = 0; x < stl.width; x++) {
+ if (x + sx < 0 || x + sx >= bgWidth)
+ continue;
+ int stencilVal = stencil.get(yLineStencil + x + sx);
+ // only allow steel on brick
+ if ((stencilVal & Stencil.MSK_BRICK) != 0) {
+ stencilVal &= ~Stencil.MSK_BRICK;
+ stencilVal |= Stencil.MSK_STEEL;
+ stencil.set(yLineStencil + x + sx, stencilVal);
+ }
+ }
+ }
+ }
+ // flush tiles
+ // if (tiles != null)
+ // for (int i=0; i < tiles.length; i++)
+ // tiles[i].flush();
+
+ sprObjects = new SpriteObject[oCombined.size()];
+ sprObjects = oCombined.toArray(sprObjects);
+ sprObjFront = new SpriteObject[oFront.size()];
+ sprObjFront = oFront.toArray(sprObjFront);
+ sprObjBehind = new SpriteObject[oBehind.size()];
+ sprObjBehind = oBehind.toArray(sprObjBehind);
+ System.gc();
+
+ return stencil;
+ }
+
+ /**
+ * Draw opaque objects behind background image.
+ * @param g graphics object to draw on
+ * @param width width of screen
+ * @param xOfs level offset position
+ */
+ public void drawBehindObjects(final Graphics2D g, final int width, final int xOfs) {
+ // draw "behind" objects
+ if (sprObjBehind != null) {
+ for (int n=0; n<sprObjBehind.length; n++) {
+ try {
+ SpriteObject spr = sprObjBehind[n];
+ BufferedImage img = spr.getImage();
+ if (spr.getX()+spr.getWidth() > xOfs && spr.getX() < xOfs+width)
+ g.drawImage(img,spr.getX()-xOfs,spr.getY(),null);
+ //spr.drawHidden(offImg,xOfsTemp);
+ } catch (ArrayIndexOutOfBoundsException ex) {}
+ }
+ }
+ }
+
+ /**
+ * Draw transparent objects in front of background image.
+ * @param g graphics object to draw on
+ * @param width width of screen
+ * @param xOfs level offset position
+ */
+ public void drawInFrontObjects(final Graphics2D g, final int width, final int xOfs) {
+ // draw "in front" objects
+ if (sprObjFront != null) {
+ for (int n=0; n<sprObjFront.length; n++) {
+ try {
+ SpriteObject spr = sprObjFront[n];
+ BufferedImage img = spr.getImage();
+ if (spr.getX()+spr.getWidth() > xOfs && spr.getX() < xOfs+width)
+ g.drawImage(img,spr.getX()-xOfs,spr.getY(),null);
+ } catch (ArrayIndexOutOfBoundsException ex) {}
+ }
+ }
+ }
+
+ // /**
+ // * Debug output.
+ // * @param o string to print
+ // */
+ // private static void out(final String o) {
+ // System.out.println(o);
+ // }
+
+ /**
+ * Load tile set from a styles folder.
+ * @param set name of the style
+ * @param cmp parent component
+ * @return array of images where each image contains one tile
+ * @throws ResourceException
+ */
+ private Image[] loadTileSet(final String set, final Component cmp) throws ResourceException {
+ ArrayList<Image> images = new ArrayList<Image>(64);
+ MediaTracker tracker = new MediaTracker(cmp);
+ int tiles = props.get("tiles", 64);
+ for (int n = 0; n < tiles; n++) {
+ String fName = "styles/" + set + "/" + set + "_" + Integer.toString(n) + ".gif";
+ Image img = Core.loadImage(tracker, fName);
+ images.add(img);
+ }
+ try {
+ tracker.waitForAll();
+ } catch (InterruptedException ex) {}
+ Image ret[] = new Image[images.size()];
+ ret = images.toArray(ret);
+ images = null;
+ return ret;
+ }
+
+
+ /**
+ * Load level sprite objects.
+ * @param set name of the style
+ * @param cmp parent component
+ * @return array of images where each image contains one tile
+ * @throws ResourceException
+ */
+ private SpriteObject[] loadObjects(final String set, final Component cmp) throws ResourceException {
+ //URLClassLoader urlLoader = (URLClassLoader) this.getClass().getClassLoader();
+ MediaTracker tracker = new MediaTracker(cmp);
+ // first some global settings
+ bgCol = props.get("bgColor",0x000000) | 0xff000000;
+ bgColor = new Color(bgCol);
+ debrisCol = props.get("debrisColor",0xffffff) | 0xff000000;
+ // replace pink color with debris color
+ Lemming.patchColors(TEMPLATE_COLOR,debrisCol);
+ particleCol = props.get("particleColor", DEFAULT_PARTICLE_COLORS);
+ for (int i=0; i<particleCol.length; i++)
+ particleCol[i] |= 0xff000000;
+ // go through all the entries (shouldn't be more than 64)
+ ArrayList<SpriteObject> sprites = new ArrayList<SpriteObject>(64);
+ int idx;
+ for (idx = 0; true; idx++) {
+ // get number of animations
+ String sIdx = Integer.toString(idx);
+ int frames = props.get("frames_" + sIdx, -1);
+ if (frames < 0)
+ break;
+ // load screenBuffer
+ String fName = "styles/"+set + "/" + set + "o_" + Integer.toString(idx)+ ".gif";
+ Image img = Core.loadImage(tracker, fName);
+ try {
+ tracker.waitForAll();
+ } catch (InterruptedException ex) {}
+ // get animation mode
+ int anim = props.get("anim_" + sIdx, -1);
+ if (anim < 0)
+ break;
+ SpriteObject sprite = new SpriteObject(img, frames);
+ switch (anim) {
+ case 0: // dont' animate
+ sprite.setAnimMode(Sprite.Animation.NONE);
+ break;
+ case 1: // loop mode
+ sprite.setAnimMode(Sprite.Animation.LOOP);
+ break;
+ case 2: // triggered animation - for the moment handle like loop
+ sprite.setAnimMode(Sprite.Animation.TRIGGERED);
+ break;
+ case 3: // entry animation
+ sprite.setAnimMode(Sprite.Animation.ONCE);
+ break;
+ }
+ // get object type
+ int type = props.get("type_" + sIdx, -1);
+ if (type < 0)
+ break;
+ sprite.setType(SpriteObject.getType(type));
+ switch (sprite.getType()) {
+ case EXIT:
+ case NO_DIG_LEFT:
+ case NO_DIG_RIGHT:
+ case TRAP_DIE:
+ case TRAP_REPLACE:
+ case TRAP_DROWN:
+ // load mask
+ fName = "styles/"+set + "/" + set + "om_" + Integer.toString(idx)+ ".gif";
+ img = Core.loadImage(tracker, fName);
+ sprite.setMask(img);
+ break;
+ }
+ // get sound
+ int sound = props.get("sound_" + sIdx, -1);
+ sprite.setSound(sound);
+
+ sprites.add(sprite);
+ }
+ SpriteObject ret[] = new SpriteObject[sprites.size()];
+ ret = sprites.toArray(ret);
+ sprites = null;
+ return ret;
+ }
+
+ /**
+ * Create a mini map for this level.
+ * @param image image to re-use (if null or wrong size, it will be recreated)
+ * @param bgImage background image used as source for the mini map
+ * @param scaleX integer X scaling factor (2 -> half width)
+ * @param scaleY integer Y scaling factor (2 -> half height)
+ * @param tint apply a greenish color tint
+ * @return image with mini map
+ */
+ public BufferedImage createMiniMap(final BufferedImage image, final BufferedImage bgImage, final int scaleX, final int scaleY, final boolean tint) {
+ Level level = GameController.getLevel();
+ int bgCol;
+ int width = bgImage.getWidth()/scaleX;
+ int height = bgImage.getHeight()/scaleY;
+ BufferedImage img;
+
+ if (image == null || image.getWidth() != width || image.getHeight() != height)
+ img = ToolBox.createImage(width,height,Transparency.OPAQUE);
+ else
+ img = image;
+ Graphics2D gx = img.createGraphics();
+ // clear background
+ gx.setBackground(bgColor);
+ gx.clearRect(0, 0, width, height);
+ // read back background color to avoid problems with 16bit mode
+ // (bgColor written can be slightly different from the one read)
+ bgCol = img.getRGB(0,0);
+ // draw "behind" objects
+ if (level != null && level.sprObjBehind != null) {
+ for (int n=0; n<level.sprObjBehind.length; n++) {
+ try {
+ SpriteObject spr = level.sprObjBehind[n];
+ BufferedImage sprImg = spr.getImage();
+ gx.drawImage(sprImg,spr.getX()/scaleX,spr.getY()/scaleY,
+ spr.getWidth()/scaleX, spr.getHeight()/scaleY, null);
+ } catch (ArrayIndexOutOfBoundsException ex) {}
+ }
+ }
+ gx.drawImage(bgImage,0,0,width,height,0,0,bgImage.getWidth(),bgImage.getHeight(),null);
+ // draw "in front" objects
+ if (level != null && level.sprObjFront != null) {
+ for (int n=0; n<level.sprObjFront.length; n++) {
+ try {
+ SpriteObject spr = level.sprObjFront[n];
+ BufferedImage sprImg = spr.getImage();
+ gx.drawImage(sprImg,spr.getX()/scaleX,spr.getY()/scaleY,
+ spr.getWidth()/scaleX, spr.getHeight()/scaleY, null);
+ } catch (ArrayIndexOutOfBoundsException ex) {}
+ }
+ }
+ gx.dispose();
+ // now tint in green
+ if (tint) {
+ for (int y=0; y<img.getHeight(); y++)
+ for (int x=0; x<img.getWidth(); x++) {
+ int c = img.getRGB(x,y);
+ if (c == bgCol)
+ c = 0xff000000; // make backgroud black instead of dark green (easier mask operations)
+ else {
+ int sum = 0;
+ for (int i =0; i<3; i++,c>>=8)
+ sum += (c&0xff);
+ sum /= 3; // mean value
+ if (sum != 0)
+ sum += 0x60;
+ //sum *= 3; // make lighter
+ if (sum > 0xff)
+ sum = 0xff;
+ c = 0xff000000 + /*((sum<<16)&0xff0000)*/ + ((sum<<8)&0xff00) /*+ sum*/;
+ }
+ img.setRGB(x,y,c);
+ }
+ }
+ return img;
+ }
+
+ /**
+ * Get level sprite object via index.
+ * @param idx index
+ * @return level sprite object
+ */
+ public SpriteObject getSprObject(final int idx) {
+ return sprObjects[idx];
+ }
+
+ /**
+ * Get number of level sprite objects.
+ * @return number of level sprite objects
+ */
+ public int getSprObjectNum() {
+ if (sprObjects == null)
+ return 0;
+ return sprObjects.length;
+ }
+
+ /**
+ * Get level Entry via idx.
+ * @param idx index
+ * @return level Entry
+ */
+ public Entry getEntry(final int idx) {
+ return entries[idx];
+ }
+
+ /**
+ * Get number of entries for this level.
+ * @return number of entries.
+ */
+ public int getEntryNum() {
+ if (entries == null)
+ return 0;
+ return entries.length;
+ }
+
+ /**
+ * Get background color.
+ * @return background color.
+ */
+ public Color getBgColor() {
+ return bgColor;
+ }
+
+ /**
+ * Get ready state of level.
+ * @return true if level is completely loaded.
+ */
+ public boolean isReady() {
+ return ready;
+ }
+
+ /**
+ * Get maximum safe fall distance.
+ * @return maximum safe fall distance
+ */
+ public int getMaxFallDistance() {
+ return maxFallDistance;
+ }
+
+ /**
+ * Get array of ARGB colors used for particle effects.
+ * @return array of ARGB colors used for particle effects
+ */
+ public int[] getParticleCol() {
+ return particleCol;
+ }
+
+ /**
+ * Get start screen x position : 0 - 0x04f0 (1264) rounded to modulo 8,
+ * @return start screen x position
+ */
+ public int getXpos() {
+ return xPos;
+ }
+
+ /**
+ * Get number of climbers in this level : max 0xfa (250).
+ * @return number of climbers in this level
+ */
+ public int getNumClimbers () {
+ return numClimbers;
+ }
+
+ /**
+ * Get number of floaters in this level : max 0xfa (250).
+ * @return number of floaters in this level
+ */
+ public int getNumFloaters () {
+ return numFloaters;
+ }
+
+ /**
+ * Get number of bombers in this level : max 0xfa (250).
+ * @return number of bombers in this level
+ */
+ public int getNumBombers () {
+ return numBombers;
+ }
+
+ /**
+ * Get number of blockers in this level : max 0xfa (250).
+ * @return number of blockers in this level
+ */
+ public int getNumBlockers () {
+ return numBlockers;
+ }
+
+ /**
+ * Get number of builders in this level : max 0xfa (250).
+ * @return number of builders in this level
+ */
+ public int getNumBuilders () {
+ return numBuilders;
+ }
+
+ /**
+ * Get number of bashers in this level : max 0xfa (250).
+ * @return number of bashers in this level
+ */
+ public int getNumBashers () {
+ return numBashers;
+ }
+
+ /**
+ * Get number of miners in this level : max 0xfa (250).
+ * @return number of miners in this level
+ */
+ public int getNumMiners () {
+ return numMiners;
+ }
+
+
+ /**
+ * Get number of diggers in this level : max 0xfa (250).
+ * @return number of diggers in this level
+ */
+ public int getMumDiggers () {
+ return numDiggers;
+ }
+
+ /**
+ * Get time limit in seconds
+ * @return time limit in seconds
+ */
+ public int getTimeLimitSeconds() {
+ return timeLimitSeconds;
+ }
+
+ /**
+ * Get number of Lemmings to rescue : should be less than or equal to number of Lemmings.
+ * @return number of Lemmings to rescue
+ */
+ public int getNumToRescue() {
+ return numToRescue;
+ }
+
+ /**
+ * Get number of Lemmings in this level (maximum 0x0072 = 114 in original LVL format).
+ * @return number of Lemmings in this level
+ */
+ public int getNumLemmings() {
+ return numLemmings;
+ }
+
+ /**
+ * Get color of debris pixels (to be replaced with level color).
+ * @return color of debris pixels as ARGB
+ */
+ public int getDebrisColor() {
+ return debrisCol;
+ }
+
+ /**
+ * Get release rate : 0 is slowest, 0x0FA (250) is fastest
+ * @return release rate : 0 is slowest, 0x0FA (250) is fastest
+ */
+ public int getReleaseRate() {
+ return releaseRate;
+ }
+
+ /**
+ * Check if this is a SuperLemming level (runs faster).
+ * @return true if this is a SuperLemming level, false otherwise
+ */
+ public boolean isSuperLemming() {
+ return superlemming;
+ }
+
+ /**
+ * Get level name.
+ * @return level name
+ */
+ public String getLevelName() {
+ return lvlName;
+ }
+}
+
+/**
+ * Storage class for a level object.
+ * @author Volker Oth
+ */
+class LvlObject {
+ /** paint mode: only visible on a terrain pixel */
+ static final int MODE_VIS_ON_TERRAIN = 8;
+ /** paint mode: don't overwrite terrain pixel in the original background image */
+ static final int MODE_NO_OVERWRITE = 4;
+ /** paint mode: don't overwrite terrain pixel in the current (!) background image.
+ special NO_OVERWRITE case for objects hidden behind terrain. */
+ static final int MODE_HIDDEN = 5;
+ /** paint mode: paint without any further checks */
+ static final int MODE_FULL = 0;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** identifier */
+ int id;
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** paint mode - must be one of the MODEs above */
+ int paintMode;
+ /** flag: paint the object upside down */
+ boolean upsideDown;
+
+ /**
+ * Constructor
+ * @param val three values as array [identifier, x position, y position]
+ */
+ public LvlObject(final int[] val) {
+ id = val[0];
+ xPos = val[1];
+ yPos = val[2];
+ paintMode = val[3];
+ upsideDown = val[4] != 0;
+ }
+}
+
+/**
+ * Storage class for a terrain/background tiles.
+ * @author Volker Oth
+ */
+class Terrain {
+ /** paint mode: don't overwrite existing terrain pixel */
+ static final int MODE_NO_OVERWRITE = 8;
+ /** paint mode: upside down */
+ static final int MODE_UPSIDE_DOWN = 4;
+ /** paint mode: remove existing terrain pixels instead of overdrawing them */
+ static final int MODE_REMOVE = 2;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** identifier */
+ int id;
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** modifier - must be one of the above MODEs */
+ int modifier;
+
+ /**
+ * Constructor.
+ * @param val three values as array [identifier, x position, y position]
+ */
+ public Terrain(final int[] val) {
+ id = val[0];
+ xPos = val[1];
+ yPos = val[2];
+ modifier = val[3];
+ }
+}
+
+/**
+ * Storage class for steel tiles.
+ * @author Volker Oth
+ */
+class Steel {
+ private final static long serialVersionUID = 0x01;
+
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+ /** width in pixels */
+ int width;
+ /** height in pixels */
+ int height;
+
+ /**
+ * Constructor.
+ * @param val four values as array [x position, y position, width, height]
+ */
+ public Steel(final int[] val) {
+ xPos = val[0];
+ yPos = val[1];
+ width = val[2];
+ height = val[3];
+ }
+}
+
+/**
+ * Storage class for level Entries.
+ * @author Volker Oth
+ */
+class Entry {
+ /** identifier */
+ int id;
+ /** x position in pixels */
+ int xPos;
+ /** y position in pixels */
+ int yPos;
+
+
+ /**
+ * Constructor.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ */
+ Entry(final int x, final int y) {
+ xPos = x;
+ yPos = y;
+ }
+
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/LevelCode.java b/jeu-test/Lemmini/0.84/src/Game/LevelCode.java
new file mode 100644
index 0000000..bfc707e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LevelCode.java
@@ -0,0 +1,120 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @author Volker Oth
+ * Create and evaluate lemmings level codes
+ * Based on the documentation and Basic code of Herman Perk (LemGen)
+ */
+public class LevelCode {
+ // nibble 0 | nibble 1 | nibble 2 | nibble 3 | nibble 4 | nibble 5 | nibble 6
+ // ----------|----------|----------|-----------|----------|----------|---------
+ // 3 2 1 0| 3 2 1 0|3 2 1 0| 3 2 1 0| 3 2 1 0|3 2 1 0|3 2 1 0
+ // ----------|----------|----------|-----------|----------|----------|---------
+ // L0 %0 S0 0|S1 L1 0 %1|0 L2 %2 S2|S4 S3 %3 L3|%4 0 L4 S5|0 %5 S6 L5|0 L6 0 %6
+
+ /* magic: S*/
+ private final static int MMASK[] = { 1, 2, 4, 24, 32, 64, 0};
+ private final static int MSHIFTL[] = { 1, 2, 0, 0, 0, 0, 0};
+ private final static int MSHIFTR[] = { 0, 0, 2, 1, 5, 5, 0};
+ /* level: L */
+ private final static int LMASK[] = { 1, 2, 4, 8, 16, 32, 64};
+ private final static int LSHIFTL[] = { 3, 1, 0, 0, 0, 0, 0};
+ private final static int LSHIFTR[] = { 0, 0, 0, 3, 3, 5, 4};
+ /* percent: % */
+ private final static int PMASK[] = { 1, 2, 4, 8, 16, 32, 64};
+ private final static int PSHIFTL[] = { 2, 0, 0, 0, 0, 0, 0};
+ private final static int PSHIFTR[] = { 0, 1, 1, 2, 1, 3, 6};
+
+ private final static int MAX_LVL_NUM = 127;
+
+
+ /**
+ * Create a level code from the given parameters
+ * @param seed The seed string used as base for the level code
+ * @param lvl The level number (0..127)
+ * @param percent Percentage of levels saved in the level won to get this code
+ * @param magic A "magic" number with more or less unknown sense
+ * @param offset Used to get a higher code for the first level
+ * @return String containing level code
+ */
+ public static String create(final String seed, int lvl, final int percent, final int magic, final int offset) {
+ if (lvl > MAX_LVL_NUM || percent > 127 || magic > 127 || seed==null || seed.length() != 10)
+ return null;
+ byte bi[] = seed.getBytes();
+ byte bo[] = new byte[bi.length];
+
+ // add offset and wrap around
+ int level = lvl + offset;
+ level %= (MAX_LVL_NUM+1);
+
+ // create first 7 bytes
+ int sum = 0;
+ for (int i=0; i<7; i++) {
+ bi[i] += (byte)(((magic & MMASK[i]) << MSHIFTL[i]) >>> MSHIFTR[i]);
+ bi[i] += (byte)(((level & LMASK[i]) << LSHIFTL[i]) >>> LSHIFTR[i]);
+ bi[i] += (byte)(((percent & PMASK[i]) << PSHIFTL[i]) >>> PSHIFTR[i]);
+ bo[(i + 8 - (level % 8)) % 7] = bi[i]; // rotate
+ sum += bi[i] & 0xff; // checksum
+ }
+ // create bytes 8th and 9th byte (level)
+ bo[7] = (byte)(bi[7] + (level & 0xf));
+ bo[8] = (byte)(bi[8] + ((level & 0xf0) >> 4));
+ sum += (bo[7] + bo[8]) & 0xff;
+ // create 10th byte (checksum)
+ bo[9] = (byte)(bi[9] + (sum & 0x0f));
+ return new String(bo);
+ }
+
+ /**
+ * Extract the level number from the level code and seed
+ * @param seed The seed string used as base for the level code
+ * @param code Code that contains the level number (amongst other things)
+ * @param offset Used to get a higher code for the first level
+ * @return Level number extracted from the level code (-1 in case of error)
+ */
+ public static int getLevel(final String seed, final String code, final int offset) {
+ byte bs[] = seed.getBytes();
+ byte bi[] = code.getBytes();
+ byte bo[] = new byte[bi.length];
+
+ if (seed.length() != 10 || code.length() != 10)
+ return -1;
+
+ int level = ((bi[7]-bs[7]) & 0xf) + (((bi[8]-bs[8])& 0xf)<<4);
+ // unrotate
+ for (int j=0; j<7; j++)
+ bo[(j + 6 + (level % 8)) % 7] = bi[j];
+ //decode
+ int level_ = 0;
+ int percent = 0;
+ for (int i=0; i<7; i++) {
+ int nibble = (bo[i] - bs[i]) & 0xff; // reconstruct nibble stored
+ level_ += ((nibble << LSHIFTR[i]) >> LSHIFTL[i]) & LMASK[i];
+ percent += ((nibble << PSHIFTR[i]) >> PSHIFTL[i]) & PMASK[i];
+ }
+ if (level != level_ || percent > 100)
+ return -1;
+
+ level -= offset;
+ while (level < 0)
+ level += MAX_LVL_NUM;
+
+ return level;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/LevelInfo.java b/jeu-test/Lemmini/0.84/src/Game/LevelInfo.java
new file mode 100644
index 0000000..6cc1d5d
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LevelInfo.java
@@ -0,0 +1,79 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Storage class to store level info.
+ *
+ * @author Volker Oth
+ */
+public class LevelInfo {
+ /** level name */
+ private String name;
+ /** name of music for this level */
+ private String music;
+ /** file name of the INI file containing the level information */
+ private String fileName;
+
+ /**
+ * Set the file name
+ * @param fileName file name
+ */
+ public void setFileName(final String fileName) {
+ this.fileName = fileName;
+ }
+
+ /**
+ * Get the file name.
+ * @return file name
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Set name of music.
+ * @param music name of music
+ */
+ public void setMusic(final String music) {
+ this.music = music;
+ }
+
+ /**
+ * Get name of music.
+ * @return name of music.
+ */
+ public String getMusic() {
+ return music;
+ }
+
+ /**
+ * Set level name.
+ * @param name level name
+ */
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get level name.
+ * @return level name
+ */
+ public String getName() {
+ return name;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/LevelPack.java b/jeu-test/Lemmini/0.84/src/Game/LevelPack.java
new file mode 100644
index 0000000..4534878
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/LevelPack.java
@@ -0,0 +1,208 @@
+package Game;
+
+import java.util.ArrayList;
+
+import Tools.Props;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handling of a level pack.
+ *
+ * @author Volker Oth
+ */
+public class LevelPack {
+
+ /** name of the level pack */
+ private String name;
+ /** seed used to generate the level codes */
+ private String codeSeed;
+ /** array containing names of difficulty levels (easiest first, hardest last) */
+ private String diffLevels[];
+ /** array of array of level info - [difficulty][level number] */
+ private LevelInfo lvlInfo[][];
+ /** path of level pack - where the INI files for the level are located */
+ private String path;
+ /** maximum number of pixels a Lemming can fall before he dies */
+ private int maxFallDistance;
+ /** offset to apply in level code algorithm */
+ private int codeOffset;
+
+ /**
+ * Constructor for dummy level pack. Needed for loading single levels.
+ */
+ public LevelPack() {
+ name = "test";
+ path = "";
+ codeSeed = "AAAAAAAAAA";
+ maxFallDistance = 126;
+ codeOffset = 0;
+
+ diffLevels = new String[1];
+ diffLevels[0] = "test";
+
+ lvlInfo = new LevelInfo[1][1];
+ lvlInfo[0][0] = new LevelInfo();
+ //lvlInfo[0][0].code = "..........";
+ lvlInfo[0][0].setMusic("tim1.mod");
+ lvlInfo[0][0].setName("test");
+ lvlInfo[0][0].setFileName("");
+ }
+
+ /**
+ * Constructor for loading a level pack.
+ * @param fname file name of level pack ini
+ * @throws ResourceException
+ */
+ public LevelPack(final String fname) throws ResourceException {
+ // extract path from descriptor file
+ path = ToolBox.getPathName(fname);
+ // load the descriptor file
+ Props props = new Props();
+ if (!props.load(fname))
+ throw new ResourceException(fname);
+ // read name
+ name = props.get("name","");
+ // read code seed
+ codeSeed = props.get("codeSeed","").trim().toUpperCase();
+ // read code level offset
+ codeOffset = props.get("codeOffset",0);
+ // read max falling distance
+ maxFallDistance = props.get("maxFallDistance", 126);
+ // read levels of difficulty
+ ArrayList<String> difficulty = new ArrayList<String>(); // <String>
+ int idx = 0;
+ String diffLevel;
+ do {
+ diffLevel = props.get("level_"+Integer.toString(idx++),"");
+ if (diffLevel.length() > 0)
+ difficulty.add(diffLevel);
+ } while (diffLevel.length() > 0);
+ diffLevels = new String[difficulty.size()];
+ diffLevels = difficulty.toArray(diffLevels);
+ // read music files
+ ArrayList<String> music = new ArrayList<String>(); // <String>
+ String track;
+ idx = 0;
+ do {
+ track = props.get("music_"+Integer.toString(idx++),"");
+ if (track.length() > 0)
+ music.add(track);
+ } while (track.length() > 0);
+ // read levels
+ lvlInfo = new LevelInfo[difficulty.size()][];
+ String levelStr[];
+ String def[] = {""};
+ for (int diff=0; diff<difficulty.size(); diff++) {
+ idx = 0;
+ ArrayList<LevelInfo> levels = new ArrayList<LevelInfo>(); // <LevelInfo>
+ diffLevel = difficulty.get(diff);
+ do {
+ levelStr = props.get(diffLevel.toLowerCase()+"_"+Integer.toString(idx),def);
+ // filename, music number
+ if (levelStr.length == 2) {
+ // get name from ini file
+ Props lvlProps = new Props();
+ lvlProps.load(path+"/"/*+lvlPath+"/"*/+levelStr[0]);
+ // Now put everything together
+ LevelInfo info = new LevelInfo();
+ info.setFileName(path+"/"/*+lvlPath+"/"*/+levelStr[0]);
+ info.setMusic(music.get(Integer.parseInt(levelStr[1/*2*/])));
+ //info.code = levelStr[1];
+ info.setName(lvlProps.get("name","")); // only used in menu
+ levels.add(info);
+ }
+ idx++;
+ } while (levelStr.length == 2);
+ lvlInfo[diff] = new LevelInfo[levels.size()];
+ lvlInfo[diff] = levels.toArray(lvlInfo[diff]);
+ }
+ }
+
+ /**
+ * Assemble level pack and difficulty level to string.
+ * @param pack level pack
+ * @param diff name of difficulty level
+ * @return String formed from level pack and difficulty level
+ */
+ public static String getID(final String pack, final String diff) {
+ return pack.toLowerCase()+"-"+diff.toLowerCase();
+ }
+
+ /**
+ * Return levels of difficulty as string array.
+ * @return levels of difficulty as string array
+ */
+ public String[] getDiffLevels() {
+ return diffLevels;
+ }
+
+ /**
+ * Get name of level pack.
+ * @return name of level pack
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get code seed.
+ * @return code seed.
+ */
+ public String getCodeSeed() {
+ return codeSeed;
+ }
+
+ /**
+ * Get maximum fall distance.
+ * @return maximum fall distance
+ */
+ public int getMaxFallDistance() {
+ return maxFallDistance;
+ }
+
+ /**
+ * Get offset to apply in level code algorithm.
+ * @return offset to apply in level code algorithm
+ */
+ public int getCodeOffset() {
+ return codeOffset;
+ }
+
+ /**
+ * Get level info for a certain level.
+ * @param diffLvl difficulty level
+ * @param level level number
+ * @return LevelInfo for the given level
+ */
+ public LevelInfo getInfo(final int diffLvl, final int level) {
+ return lvlInfo[diffLvl][level];
+ }
+
+ /**
+ * Return all levels for a given difficulty
+ * @param diffLevel number of difficulty level
+ * @return level names as string array
+ */
+ public String[] getLevels(final int diffLevel) {
+ String names[] = new String[lvlInfo[diffLevel].length];
+ for (int i=0; i<lvlInfo[diffLevel].length; i++)
+ names[i] = lvlInfo[diffLevel][i].getName();
+ return names;
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/Mask.java b/jeu-test/Lemmini/0.84/src/Game/Mask.java
new file mode 100644
index 0000000..62d1671
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Mask.java
@@ -0,0 +1,288 @@
+package Game;
+
+import java.awt.image.BufferedImage;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Masks are used to erase or draw elements in the background stencil.
+ * E.g. digger, bashers and explosions erase elements from the stencil via a mask.
+ * Also stairs created by the builder are handled via a mask.
+ *
+ * @author Volker Oth
+ */
+public class Mask {
+ /** number of pixels in mask that may be indestructible before action is stopped */
+ private int maxMaskPixels[];
+ /** width of mask in pixels */
+ private int width;
+ /** height of mask in pixels */
+ private int height;
+ /** array of byte arrays. Note: masks may be animated and thus contain multiple frames. */
+ private byte mask[][];
+
+ /**
+ * Constructor.
+ * @param img image which may contain several animation frames one above each other
+ * @param frames number of animation frames
+ */
+ public Mask(final BufferedImage img, final int frames) {
+ width = img.getWidth(null);
+ height = img.getHeight(null)/frames;
+ mask = new byte[frames][];
+ maxMaskPixels = new int[frames];
+ for (int i=0; i<frames; i++) {
+ int pos = 0;
+ int y0 = i*height;
+ maxMaskPixels[i] = 0;
+ mask[i] = new byte[width * height];
+ for (int y=y0; y<y0+height; y++,pos+=width) {
+ for (int x=0; x<width; x++) {
+ int rgba = img.getRGB(x,y);
+ if (((rgba & 0xff000000)!=0)) {
+ mask[i][pos+x] = (byte)1;
+ maxMaskPixels[i]++;
+ } else
+ mask[i][pos+x] = (byte)0;
+ }
+ }
+ /* now maxMaskPixels[i] contains the exact amount of active pixels in the mask
+ * however, it works better to stop a mask action already if there are only about
+ * a third of the pixels indestructible, so divide by 3
+ * critical: for diggers, the mask is 34 pixels in size but a maximum of
+ * 17 pixels is in the mask. */
+ maxMaskPixels[i] /= 3;
+ }
+ }
+
+ /**
+ * Apply erase mask (to background image, MiniMap and Stencil).
+ * @param x0 x position in pixels
+ * @param y0 y position in pixels
+ * @param maskNum index of mask if there are multiple animation frames, else 0
+ * @param checkMask Stencil bitmask with attributes that make the pixel indestructible
+ * @return true if the number of indestructible pixels was > than maxMaskPixels
+ */
+ public boolean eraseMask(final int x0, final int y0, final int maskNum, final int checkMask) {
+ int ctrIndestructable = 0;
+ BufferedImage bgImage = GameController.getBgImage();
+ BufferedImage bgImageSmall = MiniMap.getImage();
+ Stencil stencil = GameController.getStencil();
+ byte m[] = mask[maskNum];
+ int sPos = y0*bgImage.getWidth();
+ int pos = 0;
+ int scaleX = bgImage.getWidth()/bgImageSmall.getWidth();
+ int scaleY = bgImage.getHeight()/bgImageSmall.getHeight();
+ int yMax = y0+height;
+ if (yMax >= bgImage.getHeight())
+ yMax = bgImage.getHeight();
+ int xMax = x0+width;
+ if (xMax >= bgImage.getWidth())
+ xMax = bgImage.getWidth();
+
+ int bgCol = 0 /*GameController.level.bgCol*/;
+
+ for (int y=y0; y<yMax; y++,pos+=width,sPos+=bgImage.getWidth()) {
+ if (y<0) continue;
+ boolean drawSmallY = (y%scaleY) == 0;
+ for (int x=x0; x<xMax; x++) {
+ if (x<0) continue;
+ boolean drawSmallX = (x%scaleX) == 0;
+ int s = stencil.get(sPos+x);
+ if (m[pos+x-x0] != 0) {
+ if ((s & checkMask) == 0) {
+ // special handling for objects with "NO DIG" stencil (basically arrows)
+ if ((s & Stencil.MSK_NO_DIG) !=0) {
+ // get object
+ SpriteObject spr = GameController.getLevel().getSprObject(Stencil.getObjectID(s));
+ // remove pixel from all object images
+ spr.setPixel(x-spr.getX(),y-spr.getY(),0);
+ }
+ // erase pixel
+ stencil.set(sPos+x, s & Stencil.MSK_ERASE); // erase brick in stencil
+ bgImage.setRGB(x,y,bgCol); // erase pixel in bgIMage
+ if (drawSmallX && drawSmallY)
+ bgImageSmall.setRGB(x/scaleX,y/scaleY,0xff000000/*bgCol*/); // erase pixel in bgIMageSmall
+ } else // don't erase pixel
+ ctrIndestructable++;
+ }
+ }
+ }
+ return ctrIndestructable > maxMaskPixels[maskNum]; // to be checked
+ }
+
+ /**
+ * Paint one step (of a stair created by a Builder
+ * @param x0 x position in pixels
+ * @param y0 y position in pixels
+ * @param maskNum index of mask if there are multiple animation frames, else 0
+ * @param color Color to use to paint the step in the background image
+ */
+ public void paintStep(final int x0, final int y0, final int maskNum, final int color) {
+ BufferedImage bgImage = GameController.getBgImage();
+ BufferedImage bgImageSmall = MiniMap.getImage();
+ Stencil stencil = GameController.getStencil();
+ byte m[] = mask[maskNum];
+ int sPos = y0*bgImage.getWidth();
+ int pos = 0;
+ int scaleX = bgImage.getWidth()/bgImageSmall.getWidth();
+ int scaleY = bgImage.getHeight()/bgImageSmall.getHeight();
+ int yMax = y0+height;
+ if (yMax >= bgImage.getHeight())
+ yMax = bgImage.getHeight();
+ int xMax = x0+width;
+ if (xMax >= bgImage.getWidth())
+ xMax = bgImage.getWidth();
+
+ for (int y=y0; y<yMax; y++,pos+=width,sPos+=bgImage.getWidth()) {
+ boolean drawSmallY = (y%scaleY) == 0;
+ if (y<0) continue;
+ for (int x=x0; x<xMax; x++) {
+ if (x<0) continue;
+ boolean drawSmallX = (x%scaleX) == 0;
+ int s = stencil.get(sPos+x);
+ if (m[pos+x-x0] != 0 /*&& (s & Stencil.MSK_WALK_ON) == 0*/) {
+ // mask pixel set
+ if ((s & Stencil.MSK_WALK_ON) == 0)
+ s |= Stencil.MSK_BRICK;
+ stencil.set(sPos+x, s | Stencil.MSK_STAIR); // set type in stencil
+ bgImage.setRGB(x,y,color);
+ if (drawSmallX && drawSmallY)
+ bgImageSmall.setRGB(x/scaleX,y/scaleY,color & 0xff00ff00); // green pixel in bgIMageSmall
+ }
+ }
+ }
+ }
+
+ /**
+ * Create stopper mask in the Stencil only (Lemming is assigned a Blocker/Stopper
+ * @param x0 x position in pixels
+ * @param y0 y position in pixels
+ * @param xMid x position of Lemming's foot
+ */
+ public void setStopperMask(final int x0, final int y0, final int xMid) {
+ BufferedImage bgImage = GameController.getBgImage();
+ Stencil stencil = GameController.getStencil();
+ byte m[] = mask[0];
+ int sPos = y0*bgImage.getWidth();
+ int pos = 0;
+ int yMax = y0+height;
+ if (yMax >= bgImage.getHeight())
+ yMax = bgImage.getHeight();
+ int xMax = x0+width;
+ if (xMax >= bgImage.getWidth())
+ xMax = bgImage.getWidth();
+
+ for (int y=y0; y<yMax; y++,pos+=width,sPos+=bgImage.getWidth()) {
+ if (y<0) continue;
+ for (int x=x0; x<xMax; x++) {
+ if (x<0) continue;
+ if (m[pos+x-x0] != 0) {
+ int s = stencil.get(sPos+x);
+ if (x <= xMid)
+ stencil.set(sPos+x, s | Stencil.MSK_STOPPER_LEFT); // set type in stencil
+ else
+ stencil.set(sPos+x, s | Stencil.MSK_STOPPER_RIGHT); // set type in stencil
+ //bgImage.setRGB(x, y, 0xff00ff00); // debug
+ }
+ }
+ }
+ }
+
+ /**
+ * Use mask to check bitmask properties of Stencil.
+ * @param x0 x position in pixels
+ * @param y0 y position in pixels
+ * @param maskNum index of mask if there are multiple animation frames, else 0
+ * @param type Stencil bitmask to check (may contain several attributes)
+ * @return true if at least one pixel with one of the given attributes is found
+ */
+ public boolean checkType(final int x0, final int y0, final int maskNum, final int type) {
+ Stencil stencil = GameController.getStencil();
+ byte m[] = mask[maskNum];
+ int sPos = y0*stencil.getWidth();
+ int pos = 0;
+ int yMax = y0+height;
+ if (yMax >= stencil.getHeight())
+ yMax = stencil.getHeight();
+ int xMax = x0+width;
+ if (xMax >= stencil.getWidth())
+ xMax = stencil.getWidth();
+
+ for (int y=y0; y<yMax; y++,pos+=width,sPos+=stencil.getWidth()) {
+ if (y<0) continue;
+ for (int x=x0; x<xMax; x++) {
+ if (x<0) continue;
+ if (m[pos+x-x0] != 0) {
+ int s = stencil.get(sPos+x);
+ if ((s & type) != 0)
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Erase certain properties from Stencil bitmask.
+ * @param x0 x position in pixels
+ * @param y0 y position in pixels
+ * @param maskNum index of mask if there are multiple animation frames, else 0
+ * @param type Stencil bitmask to erase (may contain several attributes)
+ */
+ public void clearType(final int x0, final int y0, final int maskNum, final int type) {
+ BufferedImage bgImage = GameController.getBgImage();
+ Stencil stencil = GameController.getStencil();
+ byte m[] = mask[maskNum];
+ int sPos = y0*bgImage.getWidth();
+ int pos = 0;
+ int yMax = y0+height;
+ if (yMax >= bgImage.getHeight())
+ yMax = bgImage.getHeight();
+ int xMax = x0+width;
+ if (xMax >= bgImage.getWidth())
+ xMax = bgImage.getWidth();
+
+ for (int y=y0; y<yMax; y++,pos+=width,sPos+=bgImage.getWidth()) {
+ if (y<0) continue;
+ for (int x=x0; x<xMax; x++) {
+ if (x<0) continue;
+ if (m[pos+x-x0] != 0) {
+ int s = stencil.get(sPos+x);
+ stencil.set(sPos+x, s & ~type); // erase type in stencil
+ //bgImage.setRGB(x, y, 0xffff0000); // debug
+ }
+ }
+ }
+ }
+
+ /**
+ * Get width.
+ * @return width in pixels.
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Get height.
+ * @return height in pixels.
+ */
+ public int getHeight() {
+ return height;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/MidiMusic.java b/jeu-test/Lemmini/0.84/src/Game/MidiMusic.java
new file mode 100644
index 0000000..4906faf
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/MidiMusic.java
@@ -0,0 +1,135 @@
+package Game;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.sound.midi.InvalidMidiDataException;
+import javax.sound.midi.MetaEventListener;
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiChannel;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Sequencer;
+import javax.sound.midi.Synthesizer;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class to play Midi music.
+ *
+ * @author Volker Oth
+ */
+public class MidiMusic {
+
+ /** Midi sequencer */
+ private Sequencer sequencer;
+ /** flag: initialization is finished and midi file can be played */
+ private boolean canPlay;
+
+ /**
+ * Constructor.
+ * @param fName file name
+ * @throws ResourceException
+ * @throws LemmException
+ */
+ public MidiMusic(final String fName) throws ResourceException, LemmException {
+ try {
+ FileInputStream f = new FileInputStream(Core.findResource(fName));
+ canPlay = false;
+ sequencer = MidiSystem.getSequencer();
+ if (sequencer == null) {
+ throw new LemmException("Midi not supported");
+ } else {
+ // Acquire resources and make operational.
+ sequencer.open();
+ }
+ Sequence mySeq = MidiSystem.getSequence(f);
+ if (sequencer != null) {
+ setGain(Music.getGain());
+ sequencer.setSequence(mySeq);
+ canPlay = true;
+ sequencer.addMetaEventListener(new MetaEventListener() {
+ public void meta(MetaMessage event) {
+ int type = event.getType();
+ //System.out.println("midi message: "+type+" "+event.toString());
+ if (type == 47) {
+ sequencer.setTickPosition(0);
+ sequencer.start();
+ }
+ }
+ });
+ }
+ } catch (MidiUnavailableException ex) {
+ throw new LemmException("Midi not supported");
+ } catch (InvalidMidiDataException ex) {
+ throw new ResourceException(fName+" (Invalid midi data)");
+ } catch (FileNotFoundException ex) {
+ throw new ResourceException(fName);
+ } catch (IOException ex) {
+ throw new ResourceException(fName+" (IO exception)");
+ }
+
+ }
+
+ /**
+ * Play current midi file.
+ */
+ public void play() {
+ if (canPlay)
+ sequencer.start();
+ }
+
+ /**
+ * Stop current midi file.
+ */
+ public void stop() {
+ if (canPlay)
+ sequencer.stop();
+ }
+
+ /**
+ * Close current midi file.
+ */
+ public void close() {
+ stop();
+ if (sequencer != null)
+ sequencer.close();
+ }
+
+ /**
+ * Set gain (volume) of midi output
+ * @param gn gain factor: 0.0 (off) .. 1.0 (full volume)
+ */
+ public void setGain(double gn) {
+ double gain;
+ if (gn > 1.0)
+ gain = 1.0;
+ else if (gn < 0)
+ gain = 0;
+ else
+ gain = gn;
+ if (sequencer != null && sequencer instanceof Synthesizer) {
+ Synthesizer synthesizer = (Synthesizer)sequencer;
+ MidiChannel[] channels = synthesizer.getChannels();
+
+ for (int i=0; i<channels.length; i++) {
+ channels[i].controlChange(7, (int)(gain * 127.0));
+ }
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/MiniMap.java b/jeu-test/Lemmini/0.84/src/Game/MiniMap.java
new file mode 100644
index 0000000..bae3d87
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/MiniMap.java
@@ -0,0 +1,121 @@
+package Game;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handles the mini map.
+ * @author Volker Oth
+ */
+public class MiniMap {
+
+ /** color of Lemmings in mini map */
+ final private static Color LEMM_COLOR = Color.RED;
+ /** color of screen frame in mini map */
+ final private static Color BORDER_COLOR = Color.YELLOW;
+
+ /** image used for mini map */
+ private static BufferedImage img;
+ /** X position in main gfx */
+ private static int xPos;
+ /** Y position in main gfx */
+ private static int yPos;
+ /** X scale */
+ private static int scaleX;
+ /** Y scale */
+ private static int scaleY;
+ /** height of mini map */
+ private static int height;
+ /** width of mini map */
+ private static int width;
+
+ /**
+ * init
+ * @param x X position in main gfx used in drawLemming() and move()
+ * @param y Y position in main gfx used in drawLemming() and move()
+ * @param sx X Scale (2 -> 0.5)
+ * @param sy Y Scale (3 -> 0.333)
+ * @param tint true: apply greenish tint, false: use original colors
+ */
+ public static void init(final int x, final int y, final int sx, final int sy, final boolean tint) {
+ xPos = x;
+ yPos = y;
+ scaleX = sx;
+ scaleY = sy;
+ Level level = GameController.getLevel();
+ BufferedImage bgImage = GameController.getBgImage();
+ img = level.createMiniMap(img, bgImage, scaleX, scaleY, tint);
+ width = img.getWidth();
+ height = img.getHeight();
+ }
+
+ /**
+ * Draw mini map.
+ * @param g Graphics object to draw on
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param xOfs horizontal level offset
+ */
+ public static void draw(final Graphics2D g, final int x, final int y, final int xOfs) {
+ int wWidth = Core.getCmp().getWidth();
+ g.drawImage(img,x,y,null);
+ g.setColor(BORDER_COLOR);
+ g.drawRect(x+xOfs/scaleX,y,wWidth/scaleX,img.getHeight()-1);
+ }
+
+ /**
+ * Draw Lemming in mini map.
+ * @param g Graphics object to draw on
+ * @param lx original lemming x position in pixels
+ * @param ly original lemming y position in pixels
+ */
+ public static void drawLemming(final Graphics2D g, final int lx, final int ly) {
+ int x = xPos + (lx+scaleX/2)/scaleX;
+ int y = yPos + (ly+scaleY/2)/scaleY;
+ g.setColor(LEMM_COLOR);
+ g.fillRect(x,y,2,2);
+ }
+
+ /**
+ * Return current image.
+ * @return current image.
+ */
+ public static BufferedImage getImage() {
+ return img;
+ }
+
+ /**
+ * Move screen frame via mini map.
+ * @param x cursor x position in original gfx.
+ * @param y cursor y position in original gfx.
+ * @param swidth
+ * @return new horizontal screen offset
+ */
+ public static int move(final int x, final int y, final int swidth) {
+ if (y < yPos || y >= yPos+height || x < xPos || x >= xPos+width)
+ return -1; // cursor outside the mini map
+ int xOfs = (x-xPos)*scaleX-swidth/2;
+ if (xOfs < 0)
+ xOfs = 0;
+ if (xOfs > Level.WIDTH - swidth)
+ xOfs = Level.WIDTH - swidth-1;
+ return xOfs;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/MiscGfx.java b/jeu-test/Lemmini/0.84/src/Game/MiscGfx.java
new file mode 100644
index 0000000..e1268d3
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/MiscGfx.java
@@ -0,0 +1,108 @@
+package Game;
+
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Wrapper class for additional images which don't fit anywhere else.
+ *
+ * @author Volker Oth
+ */
+public class MiscGfx {
+
+ /** Index of images */
+ public static enum Index {
+ /** border for the mini map */
+ BORDER,
+ /** Lemmin i logo */
+ LEMMINI,
+ /** green background tile */
+ TILE_GREEN,
+ /** brows background tile */
+ TILE_BROWN,
+ /** replay sign 1 */
+ REPLAY_1,
+ /** replay sign 2 */
+ REPLAY_2,
+ /** selection marker for replay */
+ SELECT
+ }
+
+ /** array of images */
+ private static BufferedImage image[];
+
+ /**
+ * Initialization.
+ * @throws ResourceException
+ */
+ public static void init() throws ResourceException {
+ ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
+ BufferedImage img;
+ /* 0: BORDER */
+ img = ToolBox.ImageToBuffered(Core.loadImage("misc/border.gif"),Transparency.OPAQUE);
+ images.add(img);
+ /* 1: LEMMINI */
+ img = ToolBox.ImageToBuffered(Core.loadImageJar("lemmini.png"), Transparency.TRANSLUCENT);
+ images.add(img);
+ /* 2: TILE_GREEN */
+ img = ToolBox.ImageToBuffered(Core.loadImageJar("background.gif"), Transparency.OPAQUE);
+ images.add(img);
+ /* 3: TILE_BROWN */
+ //patch brown version of tile
+ BufferedImage brownImg = ToolBox.createImage(img.getWidth(), img.getHeight(),Transparency.BITMASK);
+ for (int xp=0; xp<img.getWidth(null); xp++)
+ for (int yp=0; yp<img.getHeight(null); yp++) {
+ int col = img.getRGB(xp, yp); // A R G B
+ int a = col & 0xff000000; // transparent part
+ int r = (col >> 16) & 0xff;
+ int g = (col >> 8) & 0xff;
+ int b = col & 0xff;
+ // patch image to brown version by manipulating red component
+ r = (r*2)&0xff;
+ b = b/2;
+ col = a | (g<<16) | (r<<8) | b;
+ brownImg.setRGB(xp, yp, col);
+ }
+ images.add(brownImg);
+ /* 4: REPLAY_1 */
+ BufferedImage anim[] = ToolBox.getAnimation(Core.loadImage("misc/replay.gif"),2,Transparency.BITMASK);
+ images.add(anim[0]);
+ /* 5: REPLAY_2 */
+ images.add(anim[1]);
+ /* 6: SELECT */
+ img = ToolBox.ImageToBuffered(Core.loadImage("misc/select.gif"),Transparency.BITMASK);
+ images.add(img);
+
+ image = new BufferedImage[images.size()];
+ image = images.toArray(image);
+ }
+
+ /**
+ * Get image.
+ * @param idx Index
+ * @return image of the given index
+ */
+ public static BufferedImage getImage(Index idx) {
+ return image[idx.ordinal()];
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/ModMusic.java b/jeu-test/Lemmini/0.84/src/Game/ModMusic.java
new file mode 100644
index 0000000..2735949
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/ModMusic.java
@@ -0,0 +1,187 @@
+package Game;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.SourceDataLine;
+
+import micromod.Micromod;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class to play MOD music.
+ *
+ * @author Volker Oth
+ */
+public class ModMusic implements Runnable {
+ /** sample frequency */
+ private final static int SAMPLE_RATE = 44100;
+
+ /** object to play MODs */
+ private Micromod micromod;
+ /** flag: loop the song */
+ private boolean songloop;
+ /** flag: currently playing */
+ private boolean play;
+ /** thread for playing */
+ private Thread mmThread;
+ /** data line used to play samples */
+ private SourceDataLine line;
+
+ /**
+ * Load MOD file, initialize player.
+ * @param fn file name
+ * @throws ResourceException
+ */
+ public void load(final String fn) throws ResourceException {
+ if (mmThread != null)
+ close();
+ String fName = Core.findResource(fn);
+ int datalen = (int)(new File(fName).length());
+ if( datalen < 0 ) throw new ResourceException(fName);
+ try {
+ FileInputStream f = new FileInputStream(fName);
+ byte[] songdata = new byte[ datalen ];
+ f.read( songdata );
+ f.close();
+ micromod = new Micromod( songdata, SAMPLE_RATE );
+ setloop( true );
+ } catch (FileNotFoundException ex) {
+ throw new ResourceException(fName);
+ } catch (IOException ex) {
+ throw new ResourceException(fName+" (IO exception)");
+ }
+ mmThread = new Thread( this );
+ mmThread.start();
+ }
+
+ /**
+ * Set whether the song is to loop continuously or not. The default is to loop.
+ * @param loop true: loop, false: playe only once
+ */
+ public void setloop( final boolean loop ) {
+ songloop = loop;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ *
+ * Begin playback.
+ * This method will return once the song has finished, or stop has been called.
+ */
+ public void run() {
+ int buflen = 2048;
+ int[] lbuf = new int[ buflen ];
+ int[] rbuf = new int[ buflen ];
+ byte[] obuf = new byte[ buflen << 2 ];
+ try {
+ AudioFormat af = new AudioFormat( SAMPLE_RATE, 16, 2, true, false );
+ DataLine.Info lineInfo = new DataLine.Info( SourceDataLine.class, af );
+ //SourceDataLine line = (SourceDataLine)AudioSystem.getLine(lineInfo);
+ line = (SourceDataLine)GameController.sound.getLine(lineInfo);
+ line.open();
+ line.start();
+ setGain(Music.getGain());
+ int songlen = micromod.getlen();
+ int remain = songlen;
+ while( remain > 0 && Thread.currentThread() == mmThread) {
+ if (play) {
+ int count = buflen;
+ if ( count > remain ) count = remain;
+ micromod.mix( lbuf, rbuf, 0, count );
+ for( int ix = 0; ix < count; ix++ ) {
+ int ox = ix << 2;
+ obuf[ ox ] = ( byte ) ( lbuf[ ix ] & 0xFF );
+ obuf[ ox + 1 ] = ( byte ) ( lbuf[ ix ] >> 8 );
+ obuf[ ox + 2 ] = ( byte ) ( rbuf[ ix ] & 0xFF );
+ obuf[ ox + 3 ] = ( byte ) ( rbuf[ ix ] >> 8 );
+ lbuf[ ix ] = rbuf[ ix ] = 0;
+ }
+ line.write( obuf, 0, count << 2 );
+ remain -= count;
+ if( remain == 0 && songloop ) remain = songlen;
+ Thread.yield();
+ } else {
+ try {
+ line.flush();
+ Thread.sleep(40);
+ } catch (InterruptedException ex) {}
+ }
+ }
+ line.flush();
+ line.close();
+ } catch( LineUnavailableException e ) {
+ e.printStackTrace();
+ return;
+ }
+ }
+
+ /**
+ * Instruct the run() method to finish playing and return.
+ */
+ public void stop() {
+ if (mmThread != null)
+ mmThread.interrupt();
+ play = false;
+ }
+
+ /**
+ * Instruct the run() method to resume playing.
+ */
+ public void play() {
+ if (mmThread != null)
+ mmThread.interrupt();
+ play = true;
+ }
+
+ /**
+ * Kills the thread.
+ */
+ public void close() {
+ Thread moribund = mmThread;
+ mmThread = null;
+ try {
+ moribund.interrupt();
+ moribund.join();
+ } catch (InterruptedException ex) {}
+ }
+
+ /**
+ * Set gain (volume) of MOD output
+ * @param gn gain factor: 0.0 (off) .. 1.0 (full volume)
+ */
+ public void setGain(final double gn) {
+ double gain;
+ if (gn > 1.0)
+ gain = 1.0;
+ else if (gn < 0)
+ gain = 0;
+ else
+ gain = gn;
+ if (line != null)
+ GameController.sound.setLineGain(line, gain);
+ }
+
+}
+
+
diff --git a/jeu-test/Lemmini/0.84/src/Game/Music.java b/jeu-test/Lemmini/0.84/src/Game/Music.java
new file mode 100644
index 0000000..729a1f9
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Music.java
@@ -0,0 +1,212 @@
+package Game;
+import java.io.File;
+import java.io.FileFilter;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Play background music. Abstraction layer for ModMusic and MidiMusic.
+ *
+ * @author Volker Oth
+ */
+public class Music {
+
+ /** music type */
+ private static enum Type {
+ /** no type */
+ NONE,
+ /** midi music */
+ MIDI,
+ /** MOD music */
+ MOD
+ }
+
+ /** music type */
+ private static Type type;
+ /** currently playing? */
+ private static boolean playing;
+ /** MOD music object */
+ private static ModMusic modMusic;
+ /** Midi music object */
+ private static MidiMusic midiMusic;
+ /** music gain */
+ private static double gain = 1.0;
+ /** array of file names */
+ private static String musicFiles[];
+
+
+ /**
+ * Initialization.
+ */
+ public static void init() {
+ type = Type.NONE;
+ modMusic = new ModMusic();
+
+ // read available musicfiles for random mode
+ File dir = new File(Core.resourcePath+"music");
+ File files[] = dir.listFiles(new MusicFileFilter());
+ musicFiles = new String[files.length];
+ for (int i=0; i<files.length; i++)
+ musicFiles[i] = files[i].getName();
+ }
+
+ /**
+ * Load music file.
+ * @param fName file name
+ * @throws ResourceException
+ * @throws LemmException
+ */
+ public static void load(final String fName) throws ResourceException, LemmException {
+ if (fName.toLowerCase().indexOf(".mid") != -1) {
+ // MIDI
+ midiMusic = new MidiMusic(fName);
+ if (type == Type.MOD)
+ modMusic.close();
+ type = Type.MIDI;
+ } else if (fName.toLowerCase().indexOf(".mod") != -1) {
+ // MOD
+ modMusic.load(fName);
+ if (type == Type.MIDI)
+ midiMusic.close();
+ type = Type.MOD;
+ }
+ playing = false;
+ }
+
+ /**
+ * Get file name of a random track.
+ * @return file name of a random track
+ */
+ public static String getRandomTrack() {
+ double r = Math.random()*musicFiles.length;
+ return musicFiles[(int)r];
+ }
+
+ /**
+ * Play music.
+ */
+ public static void play() {
+ switch (type) {
+ case MIDI:
+ midiMusic.play();
+ playing = true;
+ break;
+ case MOD:
+ modMusic.play();
+ playing = true;
+ break;
+ }
+ }
+
+ /**
+ * Stop music.
+ */
+ public static void stop() {
+ switch (type) {
+ case MIDI:
+ midiMusic.stop();
+ playing = false;
+ break;
+ case MOD:
+ modMusic.stop();
+ playing = false;
+ break;
+ }
+ }
+
+ /**
+ * Close music.
+ */
+ public static void close() {
+ switch (type) {
+ case MIDI:
+ midiMusic.close();
+ playing = false;
+ break;
+ case MOD:
+ modMusic.close();
+ playing = false;
+ break;
+ }
+ }
+
+ /**
+ * Check if music is currently playing
+ * @return true if music is currently playing, else false
+ */
+ public static boolean isPlaying() {
+ return playing;
+ }
+
+ /**
+ * Get current music gain (1.0=100%)
+ * @return current music gain (1.0=100%)
+ */
+ public static double getGain() {
+ return gain;
+ }
+
+ /**
+ * Set music gain
+ * @param gn gain (1.0=100%)
+ */
+ public static void setGain(final double gn) {
+ if (gn > 1.0)
+ gain = 1.0;
+ else if (gn < 0)
+ gain = 0;
+ else
+ gain = gn;
+ switch (type) {
+ case MIDI:
+ midiMusic.setGain(gain);
+ break;
+ case MOD:
+ modMusic.setGain(gain);
+ break;
+ }
+ Core.programProps.set("musicGain", gain);
+ }
+
+ /**
+ * Get current music type.
+ * @return music type
+ */
+ public static Type getType() {
+ return type;
+ }
+}
+
+/**
+ * File filter for music files.
+ * @author Volker Oth
+ */
+class MusicFileFilter implements FileFilter {
+ /* (non-Javadoc)
+ * @see java.io.FileFilter#accept(java.io.File)
+ */
+ public boolean accept(final File f) {
+ if (!f.isFile())
+ return false;
+ if (f.getName().toLowerCase().indexOf(".mid") != -1)
+ return true;
+ if (f.getName().toLowerCase().indexOf(".mod") != -1)
+ return true;
+ return false;
+ }
+}
+
diff --git a/jeu-test/Lemmini/0.84/src/Game/NumFont.java b/jeu-test/Lemmini/0.84/src/Game/NumFont.java
new file mode 100644
index 0000000..def6293
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/NumFont.java
@@ -0,0 +1,74 @@
+package Game;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handle small number font.
+ * Meant to print out values between 0 and 99.
+ *
+ * @author Volker Oth
+ */
+public class NumFont {
+
+ /** width in pixels */
+ private static int width;
+ /** height in pixels */
+ private static int height;
+ /** array of images - one for each cipher 0..9 */
+ private static BufferedImage numImg[];
+
+ /**
+ * Load and initialize the font.
+ * @throws ResourceException
+ */
+ public static void init() throws ResourceException {
+ Image sourceImg = Core.loadImage("misc/numfont.gif");
+ BufferedImage img[] = ToolBox.getAnimation(sourceImg,10,Transparency.OPAQUE);
+ width = sourceImg.getWidth(null);
+ height = sourceImg.getHeight(null)/10;
+ numImg = new BufferedImage[100];
+ for (int i=0; i<100; i++) {
+ numImg[i] = ToolBox.createImage(width*2, height, Transparency.OPAQUE);
+ Graphics2D g = numImg[i].createGraphics();
+ g.drawImage(img[i/10], 0, 0, null);
+ g.drawImage(img[i%10], width, 0, null);
+ g.dispose();
+ }
+ }
+
+ /**
+ * Get an image for a number between 0 and 99
+ * @param n number (0..99)
+ * @return image of the number
+ */
+ public static BufferedImage numImage(final int n) {
+ int num;
+ if (n>99)
+ num = 99;
+ else if (n<0)
+ num = 0;
+ else num = n;
+ return numImg[num];
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/Player.java b/jeu-test/Lemmini/0.84/src/Game/Player.java
new file mode 100644
index 0000000..05778e0
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Player.java
@@ -0,0 +1,178 @@
+package Game;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+import Tools.Props;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Stores player progress.
+ * @author Volker Oth
+ */
+public class Player {
+
+ /** property class to store player settings persistently */
+ private Props props;
+ /** name of the INI file used for persistence */
+ private String iniFileStr;
+ /** used to store level progress */
+ private HashMap<String,GroupBitfield> lvlGroup;
+ /** cheat mode enabled? */
+ private boolean cheat;
+ /** player's name */
+ private String name;
+
+ /**
+ * Constructor.
+ * @param n player's name
+ */
+ public Player(final String n) {
+ name = n;
+ lvlGroup = new HashMap<String,GroupBitfield>();
+ // read main ini file
+ props = new Props();
+ // create players directory if it doesn't exist
+ File dest = new File(Core.resourcePath+"players");
+ dest.mkdirs();
+ iniFileStr = Core.resourcePath+"players/"+name+".ini";
+
+ if (props.load(iniFileStr)) {// might exist or not - if not, it's created
+ // file existed, now extract entries
+ String sdef[] = { null, null};
+ for (int idx = 0;true;idx++) {
+ String s[] = props.get("group"+Integer.toString(idx), sdef);
+ if (s == null || s.length != 2 || s[0] == null )
+ break;
+ // first string is the level group key identifier
+ // second string is a GroupBitfield used as bitfield to store won levels
+ lvlGroup.put(s[0], new GroupBitfield(s[1]));
+ }
+ }
+
+ // cheat mode
+ cheat = false;
+ }
+
+ /**
+ * Enable cheat mode for this player.
+ */
+ public void enableCheatMode() {
+ cheat = true;
+ }
+
+ /**
+ * Store player's progress.
+ */
+ public void store() {
+ Set<String> k = lvlGroup.keySet();
+ Iterator<String> it = k.iterator();
+ int idx=0;
+ while (it.hasNext()) {
+ String s = it.next();
+ GroupBitfield bf = lvlGroup.get(s);
+ String sout = s+", "+bf.toString();
+ props.set("group"+Integer.toString(idx++), sout);
+ }
+ props.save(iniFileStr);
+ }
+
+ /**
+ * Allow a level to be played.
+ * @param pack level pack
+ * @param diff difficulty level
+ * @param num level number
+ * @return updated bitfield
+ */
+ public GroupBitfield setAvailable(final String pack, final String diff, final int num) {
+ // get current bitfield
+ String id = LevelPack.getID(pack, diff);
+ GroupBitfield bf = lvlGroup.get(id);
+ if (bf==null)
+ bf = GroupBitfield.ONE; // first level is always available
+ bf = new GroupBitfield(bf.setBit(num)); // set bit in bitfield (just overwrite existing bit)
+ // store new value
+ lvlGroup.put(id, bf);
+ return bf;
+ }
+
+ /**
+ * Check if player is allowed to play a level.
+ * @param pack level pack
+ * @param diff difficulty level
+ * @param num level number
+ * @return true if allowed, false if not
+ */
+ public boolean isAvailable(final String pack, final String diff, final int num) {
+ if (isCheat())
+ return true;
+ // get current bitfield
+ String id = LevelPack.getID(pack, diff);
+ GroupBitfield bf = lvlGroup.get(id);
+ if (bf==null)
+ bf = GroupBitfield.ONE; // first level is always available
+ return (bf.testBit(num));
+ }
+
+ /**
+ * Check if player is allowed to play a level.
+ * @param bf bitfield containing the approval information for all levels of this pack/difficulty
+ * @param num number of level
+ * @return true if allowed, false if not
+ */
+ public boolean isAvailable(final GroupBitfield bf, final int num) {
+ if (isCheat())
+ return true;
+ return (bf.testBit(num));
+ }
+
+ /**
+ * Get bitfield containing the approval information for all levels of this pack/difficulty.
+ * @param pack level pack
+ * @param diff difficulty level
+ * @return bitfield containing the approval information for all levels of this pack/difficulty
+ */
+ public GroupBitfield getBitField(final String pack, final String diff) {
+ if (isCheat())
+ return new GroupBitfield("18446744073709551615"); // 0xffffffffffffffff (8 bytes with all bits set)
+
+ String id = LevelPack.getID(pack, diff);
+ GroupBitfield bf = lvlGroup.get(id);
+ if (bf==null)
+ return GroupBitfield.ONE;
+ return bf;
+ }
+
+ /**
+ * Get player's name.
+ * @return player's name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get cheat state.
+ * @return true if cheat is enabled
+ */
+ public boolean isCheat() {
+ return cheat;
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/ReplayLevelInfo.java b/jeu-test/Lemmini/0.84/src/Game/ReplayLevelInfo.java
new file mode 100644
index 0000000..40a4c1f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/ReplayLevelInfo.java
@@ -0,0 +1,77 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Storage class for replay level info.
+ * @author Volker Oth
+ */
+public class ReplayLevelInfo {
+ /** name of level pack */
+ private String levelPack;
+ /** difficulty level */
+ private int diffLevel;
+ /** level number */
+ private int lvlNumber;
+
+ /**
+ * Set name of level pack.
+ * @param levelPack name of level pack
+ */
+ public void setLevelPack(final String levelPack) {
+ this.levelPack = levelPack;
+ }
+ /**
+ * Get name of level pack.
+ * @return name of level pack
+ */
+ public String getLevelPack() {
+ return levelPack;
+ }
+
+ /**
+ * Set difficulty level.
+ * @param diffLevel difficulty level
+ */
+ public void setDiffLevel(final int diffLevel) {
+ this.diffLevel = diffLevel;
+ }
+
+ /**
+ * Get difficulty level.
+ * @return difficulty level
+ */
+ public int getDiffLevel() {
+ return diffLevel;
+ }
+
+ /**
+ * Set level number.
+ * @param lvlNumber level number
+ */
+ public void setLvlNumber(final int lvlNumber) {
+ this.lvlNumber = lvlNumber;
+ }
+
+ /**
+ * Get level number.
+ * @return level number
+ */
+ public int getLvlNumber() {
+ return lvlNumber;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/ReplayStream.java b/jeu-test/Lemmini/0.84/src/Game/ReplayStream.java
new file mode 100644
index 0000000..ead4561
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/ReplayStream.java
@@ -0,0 +1,382 @@
+package Game;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Handle replays.
+ * @author Volker Oth
+ */
+public class ReplayStream {
+ // event types
+ final static int ASSIGN_SKILL = 0;
+ final static int MOVE_XPOS = 1;
+ final static int SELECT_SKILL = 2;
+ final static int SET_RELEASE_RATE = 3;
+ final static int NUKE = 4;
+
+ private ArrayList<ReplayEvent> events;
+ private int replayIndex;
+
+ /**
+ * Constructor.
+ */
+ public ReplayStream() {
+ events = new ArrayList<ReplayEvent>(); // <events>
+ replayIndex = 0;
+ }
+
+ /**
+ * Rewind replay to start position.
+ */
+ public void rewind() {
+ replayIndex = 0;
+ }
+
+ /**
+ * Get next replay event
+ * @param ctr frame counter
+ * @return replay event
+ */
+ public ReplayEvent getNext(final int ctr) {
+ if (replayIndex >= events.size())
+ return null;
+ ReplayEvent r = events.get(replayIndex);
+ /* Note: there can be multiple replay events for one frame.
+ * return the next stored event if was stored for a frame
+ * smaller or equal to the given frame counter.
+ */
+ if (ctr >= r.frameCtr) {
+ replayIndex++;
+ return r;
+ }
+ return null; /* no more events for this frame */
+ }
+
+ /**
+ * Clear the replay buffer.
+ */
+ public void clear() {
+ events.clear();
+ }
+
+ /**
+ * Clear the replay buffer from a certain frame counter.
+ * @param ctr frame counter
+ */
+ public void clearFrom(final int ctr) {
+ /* Note: there can be multiple replay events for one frame. */
+ for (int i=events.size()-1; i>0; i--) {
+ ReplayEvent r = events.get(i);
+ if (r.frameCtr > ctr // clearly behind ctr -> erase
+ || r.frameCtr == ctr && i > replayIndex) // equal to ctr, but after replayIndex -> erase
+ events.remove(i);
+ else break;
+ }
+ replayIndex = 0;
+ }
+
+ /**
+ * Load replay buffer from file.
+ * @param fname file name
+ * @return replay information
+ */
+ public ReplayLevelInfo load(final String fname) {
+ try {
+ ArrayList<ReplayEvent> ev = new ArrayList<ReplayEvent>();
+ BufferedReader f = new BufferedReader(new FileReader(fname));
+ String line = f.readLine();
+ if (!line.equals("#REPLAY"))
+ return null;
+ // read level info
+ line = f.readLine();
+ String e[] = line.split(",");
+ for (int j=0; j<e.length; j++)
+ e[j] = e[j].trim();
+ ReplayLevelInfo rli = new ReplayLevelInfo();
+ if (e[0].charAt(0) != '#')
+ return null;
+ rli.setLevelPack(e[0].substring(1));
+ rli.setDiffLevel(Integer.parseInt(e[1]));
+ rli.setLvlNumber(Integer.parseInt(e[2]));
+ // read events
+ while ( (line=f.readLine()) != null) {
+ e = line.split(",");
+ int i[] = new int[e.length];
+ for (int j=0; j<e.length; j++)
+ i[j] = Integer.parseInt(e[j].trim());
+
+ switch (i[1] /* type*/) {
+ case ASSIGN_SKILL:
+ ev.add(new ReplayAssignSkillEvent(i[0], Lemming.Type.get(i[2]), i[3]));
+ break;
+ case MOVE_XPOS:
+ ev.add(new ReplayMoveXPosEvent(i[0], i[2]));
+ break;
+ case SELECT_SKILL:
+ ev.add(new ReplaySelectSkillEvent(i[0], Lemming.Type.get(i[2])));
+ break;
+ case SET_RELEASE_RATE:
+ ev.add(new ReplayReleaseRateEvent(i[0], i[2]));
+ break;
+ case NUKE:
+ ev.add(new ReplayEvent(i[0], NUKE));
+ break;
+ default:
+ return null;
+ }
+ }
+ f.close();
+ events = ev;
+ return rli;
+ } catch(FileNotFoundException e) {
+ return null;
+ }
+ catch(IOException e) {
+ return null;
+ }
+ catch(NumberFormatException e) {
+ return null;
+ }
+ catch(ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Store replay info in a file.
+ * @param fname file name
+ * @return true if save ok, false otherwise
+ */
+ public boolean save(final String fname) {
+ try {
+ FileWriter f = new FileWriter(new File(fname));
+ f.write("#REPLAY\n");
+ LevelPack lp = GameController.getCurLevelPack();
+ f.write("#"+lp.getName()+", "+GameController.getCurDiffLevel()+", "+GameController.getCurLevelNumber()+"\n");
+ for (int i=0; i < events.size(); i++) {
+ ReplayEvent r = events.get(i);
+ f.write(r.toString()+"\n"); // will use toString of the correct child object
+ }
+ f.close();
+
+ return true;
+ } catch(FileNotFoundException e) {
+ return false;
+ }
+ catch(IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Add a NUKE event (all lemmings nuked).
+ * @param ctr frame counter
+ */
+ public void addNukeEvent(final int ctr) {
+ ReplayEvent event = new ReplayEvent(ctr, NUKE);
+ events.add(event);
+ }
+
+ /**
+ * Add ASSIGN_SKILL event (one lemming was assigned a skill).
+ * @param ctr frame counter
+ * @param skill skill assigned
+ * @param lemming Lemming the skill was assigned to
+ */
+ public void addAssignSkillEvent(final int ctr, final Lemming.Type skill, final int lemming ) {
+ ReplayAssignSkillEvent event = new ReplayAssignSkillEvent(ctr, skill, lemming);
+ events.add(event);
+ }
+
+ /**
+ * Add SELECT_SKILL event (skill selection button was pressed).
+ * @param ctr frame counter
+ * @param skill skill selected
+ */
+ public void addSelectSkillEvent(final int ctr, final Lemming.Type skill) {
+
+ ReplaySelectSkillEvent event = new ReplaySelectSkillEvent(ctr, skill);
+ events.add(event);
+ }
+
+ /**
+ * Add MOVE_XPOS event (screen moved left/right).
+ * @param ctr frame counter
+ * @param xPos new screen position
+ */
+ public void addXPosEvent(final int ctr, final int xPos ) {
+ ReplayMoveXPosEvent event = new ReplayMoveXPosEvent(ctr, xPos);
+ events.add(event);
+ }
+
+ /**
+ * Add SET_RELEASE_RATE event (release rate was changed).
+ * @param ctr frame counter
+ * @param releaserate new release rate
+ */
+ public void addReleaseRateEvent(final int ctr, final int releaserate ) {
+ ReplayReleaseRateEvent event = new ReplayReleaseRateEvent(ctr, releaserate);
+ events.add(event);
+ }
+}
+
+/**
+ * Storage class for one replay event.
+ * @author Volker Oth
+ */
+class ReplayEvent {
+ /** frame counter */
+ int frameCtr;
+ /** event type */
+ int type;
+
+ /**
+ * Constructor
+ * @param ctr frame counter
+ * @param t type
+ */
+ public ReplayEvent(final int ctr, final int t) {
+ frameCtr = ctr;
+ type = t;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return ""+frameCtr+", "+type;
+ }
+}
+
+/**
+ * Storage class for ASSIGN_SKILL event
+ * @author Volker Oth
+ */
+class ReplayAssignSkillEvent extends ReplayEvent {
+ /** skill */
+ Lemming.Type skill;
+ /** Lemming */
+ int lemming;
+
+ /**
+ * Skill assigned
+ * @param ctr Frame counter
+ * @param s skill selected
+ * @param lem lemming no. that the skill was assigned
+ */
+ public ReplayAssignSkillEvent(final int ctr, final Lemming.Type s, final int lem) {
+ super(ctr, ReplayStream.ASSIGN_SKILL);
+ skill = s;
+ lemming = lem;
+ }
+
+ /* (non-Javadoc)
+ * @see Game.ReplayEvent#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString()+", "+skill.ordinal()+", "+lemming;
+ }
+}
+
+/**
+ * Storage class for SELECT_SKILL event.
+ * @author Volker Oth
+ */
+class ReplaySelectSkillEvent extends ReplayEvent {
+ Lemming.Type skill;
+
+ /**
+ * Skill selected
+ * @param ctr Frame counter
+ * @param s skill selected
+ */
+ public ReplaySelectSkillEvent(final int ctr, final Lemming.Type s) {
+ super(ctr, ReplayStream.SELECT_SKILL);
+ skill = s;
+ }
+
+ /* (non-Javadoc)
+ * @see Game.ReplayEvent#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString()+", "+skill.ordinal();
+ }
+}
+
+/**
+ * Storage class for MOVE_XPOS event.
+ * @author Volker Oth
+ */
+class ReplayMoveXPosEvent extends ReplayEvent {
+ /** screen x position */
+ int xPos;
+
+ /**
+ * Screen X position changed event
+ * @param ctr Frame counter
+ * @param x release x position
+ */
+ public ReplayMoveXPosEvent(final int ctr, final int x) {
+ super(ctr, ReplayStream.MOVE_XPOS);
+ xPos = x;
+ }
+
+ /* (non-Javadoc)
+ * @see Game.ReplayEvent#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString()+", "+xPos;
+ }
+}
+
+/**
+ * Storage class for SET_RELEASE_RATE event.
+ * @author Volker Oth
+ */
+class ReplayReleaseRateEvent extends ReplayEvent {
+ int releaseRate;
+
+ /**
+ * Release Rate changed event
+ * @param ctr Frame counter
+ * @param rate release rate value
+ */
+ public ReplayReleaseRateEvent(final int ctr, final int rate) {
+ super(ctr, ReplayStream.SET_RELEASE_RATE);
+ releaseRate = rate;
+ }
+
+ /* (non-Javadoc)
+ * @see Game.ReplayEvent#toString()
+ */
+ @Override
+ public String toString() {
+ return super.toString()+", "+releaseRate;
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/ResourceException.java b/jeu-test/Lemmini/0.84/src/Game/ResourceException.java
new file mode 100644
index 0000000..57d00c7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/ResourceException.java
@@ -0,0 +1,41 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Generic exception class for problems during resource extraction.
+ *
+ * @author Volker Oth
+ */
+public class ResourceException extends Exception {
+ private final static long serialVersionUID = 0x000000001;
+
+ /**
+ * Constructor.
+ */
+ public ResourceException() {
+ super();
+ }
+
+ /**
+ * Constructor.
+ * @param s Exception string
+ */
+ public ResourceException(final String s) {
+ super(s);
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/SpriteObject.java b/jeu-test/Lemmini/0.84/src/Game/SpriteObject.java
new file mode 100644
index 0000000..2e9453a
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/SpriteObject.java
@@ -0,0 +1,202 @@
+package Game;
+
+import java.awt.Image;
+import java.awt.image.PixelGrabber;
+
+import GameUtil.Sprite;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Extension of {@link Sprite} to define animated level objects as exits etc.
+ * @author Volker Oth
+ */
+public class SpriteObject extends Sprite {
+
+ /** Type of level object */
+ public static enum Type {
+ /** no influence on gameplay */
+ PASSIVE,
+ /** right arrows - no digging to the left */
+ NO_DIG_LEFT,
+ /** left arrows - no digging to the right */
+ NO_DIG_RIGHT,
+ /** trap triggering drowning animation */
+ TRAP_DROWN,
+ /** trap triggering a replacement with special death animation */
+ TRAP_REPLACE,
+ /** trap triggering default death animation */
+ TRAP_DIE,
+ /** level exit (active part!) */
+ EXIT,
+ /** level entry */
+ ENTRY
+ }
+
+ /** x position in pixels */
+ private int x;
+ /** y position in pixels */
+ private int y;
+ /** Type of level object */
+ private Type type;
+ /** collision mask - only this part is copied into the stencil */
+ private int mask[];
+
+ /**
+ * Get Type depending on integer value from INI.
+ * @param t integer type
+ * @return Type
+ */
+ public static Type getType(final int t) {
+ switch (t) {
+ case 3:
+ return Type.NO_DIG_LEFT;
+ case 4:
+ return Type.NO_DIG_RIGHT;
+ case 5:
+ return Type.TRAP_DROWN;
+ case 6:
+ return Type.TRAP_REPLACE;
+ case 7:
+ return Type.TRAP_DIE;
+ case 8:
+ return Type.EXIT;
+ case 32:
+ return Type.ENTRY;
+ default:
+ return Type.PASSIVE;
+ }
+ }
+
+ /**
+ * Constructor.
+ * @param sourceImg Image containing animation frames one above each other.
+ * @param animFrames number of frames.
+ */
+ public SpriteObject(final Image sourceImg, final int animFrames) {
+ super(sourceImg, animFrames);
+ type = Type.PASSIVE;
+ setX(0);
+ setY(0);
+ }
+
+ /**
+ * Constructor. Create Sprite from other Sprite.
+ * @param src Sprite to clone.
+ */
+ public SpriteObject(final SpriteObject src) {
+ super(src);
+ setX(src.getX());
+ setY(src.getY());
+ type = src.type;
+ mask = src.mask; // flat copy - no deep copy needed!
+ }
+
+ /**
+ * Set the collision mask.
+ * @param imgMask image containing the collision mask.
+ */
+ void setMask(final Image imgMask) {
+ int w = imgMask.getWidth(null);
+ int h = imgMask.getHeight(null);
+ mask = new int[w*h];
+ PixelGrabber grab = new PixelGrabber(imgMask,0,0,w,h,mask,0,w);
+ try {
+ grab.grabPixels();
+ } catch (InterruptedException interruptedexception) {}
+ }
+
+ /**
+ * Get type of level object.
+ * @return mask as used in Stencil
+ */
+ int getMaskType() {
+ switch (type) {
+ case NO_DIG_LEFT:
+ return Stencil.MSK_NO_DIG_LEFT;
+ case NO_DIG_RIGHT:
+ return Stencil.MSK_NO_DIG_RIGHT;
+ case TRAP_DROWN:
+ return Stencil.MSK_TRAP_DROWN;
+ case TRAP_REPLACE:
+ return Stencil.MSK_TRAP_REPLACE;
+ case TRAP_DIE:
+ return Stencil.MSK_TRAP_DIE;
+ case EXIT:
+ return Stencil.MSK_EXIT;
+ }
+ return -1;
+ }
+
+ /**
+ * Set x position.
+ * @param xi x position in pixels
+ */
+ public void setX(final int xi) {
+ x = xi;
+ }
+
+ /**
+ * Get x position.
+ * @return x position in pixels
+ */
+ public int getX() {
+ return x;
+ }
+
+ /**
+ * Set y position.
+ * @param yi y position in pixels
+ */
+ public void setY(final int yi) {
+ y = yi;
+ }
+
+ /**
+ * Get y position.
+ * @return y position in pixels
+ */
+ public int getY() {
+ return y;
+ }
+
+ /**
+ * Set type of level object.
+ * @param t type of level object
+ */
+ public void setType(final Type t) {
+ type = t;
+ }
+
+ /**
+ * Get type of level object.
+ * @return type of level object
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * Get mask value.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @return mask value (ARGB).
+ */
+ public int getMask(final int x, final int y) {
+ return mask[y*width+x];
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/Stencil.java b/jeu-test/Lemmini/0.84/src/Game/Stencil.java
new file mode 100644
index 0000000..3035efa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/Stencil.java
@@ -0,0 +1,248 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Defines the stencil which defines the properties of each pixel of the level.
+ * @author Volker Oth
+ */
+public class Stencil {
+ /* Each pixel is represented by a 32bit value in the stencil.
+ * The lower part is used for a bitmask that contains stencil properties.
+ * The higher part (val << ID_SHIFT_VAL) can contain an identifier for
+ * a special trap, exit etc.
+ * Note: the lowe part is a bitmask, mainly to save space, but also
+ * since some combinations of properties are possible.
+ * Yet of course not all combinations are possible or make sense.
+ */
+ /** empty space - background is visible */
+ public final static int MSK_EMPTY = 0;
+ /** brick - can be destroyed, Lemmings can walk on it */
+ public final static int MSK_BRICK = 1;
+ /** steel - can't be destroyed, Lemmings can walk on it */
+ public final static int MSK_STEEL = 2;
+ /** Lemmings can either walk on steel or on brick */
+ public final static int MSK_WALK_ON = MSK_BRICK|MSK_STEEL;
+ /** stair build by a builder - note that this is just an additional attribute - brick is also needed to walk on it */
+ public final static int MSK_STAIR = 4;
+ /** right side of stopper mask - reflects to the right */
+ public final static int MSK_STOPPER_RIGHT = 8;
+ /** left side of stopper mask - reflects to the left */
+ public final static int MSK_STOPPER_LEFT = 16;
+ /** stopper mask (either left or right) */
+ public final static int MSK_STOPPER = MSK_STOPPER_RIGHT|MSK_STOPPER_LEFT;
+ /** arrow to the right - no digging to the left */
+ public final static int MSK_NO_DIG_LEFT = 32;
+ /** arrow to the left - no digging to the right */
+ public final static int MSK_NO_DIG_RIGHT = 64;
+ /** no digging - either left or right */
+ public final static int MSK_NO_DIG = MSK_NO_DIG_LEFT|MSK_NO_DIG_RIGHT;
+
+ /** mask used to erase stencil properties when a pixel is erased */
+ public final static int MSK_ERASE = ~(Stencil.MSK_WALK_ON|Stencil.MSK_STAIR|Stencil.MSK_NO_DIG);
+
+ /** a trap triggering the drowning animation - i.e. water */
+ public final static int MSK_TRAP_DROWN = 128;
+ /** a trap that replaces the Lemming with a special death animation */
+ public final static int MSK_TRAP_REPLACE = 256;
+ /** a trap that triggers the normal death animation */
+ public final static int MSK_TRAP_DIE = 512;
+ /** a trap (either DROWN, REPLACE or DIE) */
+ public final static int MSK_TRAP = MSK_TRAP_DROWN|MSK_TRAP_REPLACE|MSK_TRAP_DIE;
+ /** the level exit */
+ public final static int MSK_EXIT = 1024;
+
+ /** number of bits the identifier is shifter up (below is the bitmask part) */
+ private final static int ID_SHIFT_VAL = 16;
+
+ /** array which represents the stencil buffer */
+ private int stencil[];
+ /** width of stencil (=width of level) */
+ private int width;
+ /** height of stencil (=height of level) */
+ private int height;
+
+
+ /**
+ * Constructor.
+ * @param w width in pixels
+ * @param h height in pixels
+ */
+ public Stencil(final int w, final int h) {
+ width = w;
+ height = h;
+ stencil = new int[width * height];
+ }
+
+ /**
+ * Clear stencil (fill with MSK_EMPTY).
+ */
+ public void clear() {
+ int size = width*height;
+ for (int idx=0; idx<size; idx++)
+ stencil[idx] = MSK_EMPTY;
+ }
+
+ /**
+ * Set given value at given position.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param val stencil value
+ */
+ public void set(final int x, final int y, final int val) {
+ stencil[x+y*width] = val;
+ }
+
+ /**
+ * Set given value at given position.
+ * @param pos position (x*width+y)
+ * @param val stencil value
+ */
+ public void set(final int pos, final int val) {
+ stencil[pos] = val;
+ }
+
+ /**
+ * Get stencil value at given position.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @return stencil value
+ */
+ public int get(final int x, final int y) {
+ return stencil[x+y*width];
+ }
+
+ /**
+ * Get stencil value at given position.
+ * @param pos position (x*width+y)
+ * @return stencil value
+ */
+ public int get(final int pos) {
+ return stencil[pos];
+ }
+
+ /**
+ * AND given value with existing value at given position.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param val stencil value
+ */
+ public void and(final int x, final int y, final int val) {
+ int pos = x+y*width;
+ stencil[pos] = (stencil[pos] & val);
+ }
+
+ /**
+ * AND given value with existing value at given position.
+ * @param pos position (x*width+y)
+ * @param val stencil value
+ */
+ public void and(final int pos, final int val) {
+ stencil[pos] = (stencil[pos] & val);
+ }
+
+ /**
+ * OR given value with existing value at given position.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param val stencil value
+ */
+ public void or(final int x, final int y, final int val) {
+ int pos = x+y*width;
+ stencil[pos] = (stencil[pos] | val);
+ }
+
+ /**
+ * OR given value with existing value at given position.
+ * @param pos position (x*width+y)
+ * @param val stencil value
+ */
+ public void or(final int pos, final int val) {
+ stencil[pos] = (stencil[pos] | val);
+ }
+
+ /**
+ * Set only the ID without changing the lower bitmask part.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param id identifier (must not exceed 16bit)
+ */
+ public void setID(final int x, final int y, final int id) {
+ stencil[x+y*width] |= (id << ID_SHIFT_VAL);
+ }
+
+ /**
+ * Set only the ID without changing the lower bitmask part.
+ * @param pos position (x*width+y)
+ * @param id identifier (must not exceed 16bit)
+ */
+ public void setID(final int pos, final int id) {
+ stencil[pos] |= (id << ID_SHIFT_VAL);
+ }
+
+ /**
+ * Get the identifier from the stencil.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @return identifier
+ */
+ public int getID(final int x, final int y) {
+ return (stencil[x+y*width] >> ID_SHIFT_VAL);
+ }
+
+ /**
+ * Get the identifier from the stencil.
+ * @param pos position (x*width+y)
+ * @return identifier
+ */
+ public int getID(final int pos) {
+ return (stencil[pos] >> ID_SHIFT_VAL);
+ }
+
+ /**
+ * Get identifier (upper part) from full stencil value.
+ * @param sval stencil value
+ * @return identifier
+ */
+ public static int getObjectID(final int sval) {
+ return sval >> ID_SHIFT_VAL;
+ }
+
+ /**
+ * Create the numerical value used in the stencil from the identifier.
+ * @param id identifier
+ * @return numerical value of the identifier as used in the stencil
+ */
+ static int createObjectID(final int id) {
+ return id << ID_SHIFT_VAL;
+ }
+
+ /** Get width of stencil.
+ * @return width of stencil
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Get height of stencil.
+ * @return height of stencil
+ */
+ public int getHeight() {
+ return height;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/TextDialog.java b/jeu-test/Lemmini/0.84/src/Game/TextDialog.java
new file mode 100644
index 0000000..973b06e
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/TextDialog.java
@@ -0,0 +1,439 @@
+package Game;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class to create text screens which can be navigated with the mouse.
+ * Uses {@link LemmFont} as bitmap font.
+ *
+ * @author Volker Oth
+ */
+public class TextDialog {
+ /** list of buttons */
+ private ArrayList<Button> buttons;
+ /** image used as screen buffer */
+ private BufferedImage screenBuffer;
+ /** graphics object to draw in screen buffer */
+ private Graphics2D gScreen;
+ /** image used as 2nd screen buffer for offscreen drawing */
+ private BufferedImage backBuffer;
+ /** graphics object to draw in 2nd (offscreen) screen buffer */
+ private Graphics2D gBack;
+ /** width of screen in pixels */
+ private int width;
+ /** height of screen in pixels */
+ private int height;
+ /** horizontal center of the screen in pixels */
+ private int centerX;
+ /** vertical center of the screen in pixels */
+ private int centerY;
+
+ /**
+ * Create dialog text screen.
+ * @param w Width of screen to create
+ * @param h Height of screen to create
+ */
+ public TextDialog(final int w, final int h) {
+ width = w;
+ height = h;
+ centerX = width/2;
+ centerY = height/2;
+ screenBuffer = ToolBox.createImage(w, h, Transparency.OPAQUE);
+ gScreen = screenBuffer.createGraphics();
+ gScreen.setClip(0, 0, width, height);
+ backBuffer = ToolBox.createImage(w, h, Transparency.OPAQUE);
+ gBack = backBuffer.createGraphics();
+ gBack.setClip(0, 0, width, height);
+ buttons = new ArrayList<Button>();
+ }
+
+ /**
+ * Initialize/reset the text screen.
+ */
+ public void init() {
+ buttons.clear();
+ gScreen.setBackground(Color.BLACK);
+ gScreen.clearRect(0, 0, width, height);
+ }
+
+ /**
+ * Get image containing current (on screen) screenbuffer.
+ * @return image containing current (on screen) screenbuffer
+ */
+ public BufferedImage getScreen() {
+ return screenBuffer;
+ }
+
+ /**
+ * Fill brackground with tiles.
+ * @param tile Image used as tile
+ */
+ public void fillBackground(final BufferedImage tile) {
+ for (int x=0; x<width; x+=tile.getWidth()) {
+ for (int y=0; y<width; y+= tile.getHeight())
+ gBack.drawImage(tile,x,y,null);
+ }
+ gScreen.drawImage(backBuffer,0,0,null);
+ }
+
+ /**
+ * Copy back buffer to front buffer.
+ */
+ public void copyToBackBuffer() {
+ gBack.drawImage(screenBuffer,0,0,null);
+ }
+
+ /**
+ * Set Image as background. The image will appear centered.
+ * @param image Image to use as background
+ */
+ public void setBackground(final BufferedImage image) {
+ int x= (width-image.getWidth())/2;
+ int y= (height-image.getHeight())/2;
+ gBack.setBackground(Color.BLACK);
+ gBack.clearRect(0, 0, width, height);
+ gBack.drawImage(image,x,y,null);
+ gScreen.drawImage(backBuffer,0,0,null);
+ }
+
+ /**
+ * Restore whole background from back buffer.
+ */
+ public void restore() {
+ gScreen.drawImage(backBuffer,0,0,null);
+ }
+
+ /**
+ * Restore a rectangle of the background from backbuffer.
+ * @param x x position of upper left corner of rectangle
+ * @param y y position of upper left corner of rectangle
+ * @param width width of rectangle
+ * @param height height of rectangle
+ */
+ public void restoreRect(final int x, final int y, final int width, final int height) {
+ gScreen.drawImage(backBuffer,x,y,x+width,y+height,x,y,x+width,y+height,null);
+ }
+
+ /**
+ * Restore a rectangle of the background from backbuffer that might be invalidated by
+ * a text starting at x,y and having a length of len characters.
+ * @param x0 x position of upper left corner of rectangle expressed in character widths
+ * @param y0 y position of upper left corner of rectangle expressed in character heights
+ * @param l Length of text
+ */
+ public void restoreText(final int x0, final int y0, final int l) {
+ int x = x0*LemmFont.getWidth();
+ int y = y0*(LemmFont.getHeight()+4);
+ int len = l*LemmFont.getWidth();
+ int h = LemmFont.getHeight()+4;
+ gScreen.drawImage(backBuffer,x,y,x+len,y+h,x,y,x+len,y+h,null);
+ }
+
+ /**
+ * Draw string.
+ * @param s String
+ * @param x0 X position relative to center expressed in character widths
+ * @param y0 Y position relative to center expressed in character heights
+ * @param col LemmFont color
+ */
+ public void print(final String s, final int x0, final int y0, final LemmFont.Color col) {
+ int x = x0*LemmFont.getWidth();
+ int y = y0*(LemmFont.getHeight()+4);
+ LemmFont.strImage(gScreen,s, centerX+x, centerY+y, col);
+ }
+
+ /**
+ * Draw string.
+ * @param s String
+ * @param x X position relative to center expressed in character widths
+ * @param y Y position relative to center expressed in character heights
+ */
+ public void print(final String s, final int x, final int y) {
+ print(s,x,y,LemmFont.Color.GREEN);
+ }
+
+ /**
+ * Draw string horizontally centered.
+ * @param s String
+ * @param y0 Y position relative to center expressed in character heights
+ * @param col LemmFont color
+ * @return Absolute x position
+ */
+ public int printCentered(final String s, final int y0, final LemmFont.Color col) {
+ int y = y0*(LemmFont.getHeight()+4);
+ int x = centerX-s.length()*LemmFont.getWidth()/2;
+ LemmFont.strImage(gScreen,s, x, centerY+y, col);
+ return x;
+ }
+
+ /**
+ * Draw string horizontally centered.
+ * @param s String
+ * @param y Y position relative to center expressed in character heights
+ * @return Absolute x position
+ */
+ public int printCentered(final String s, final int y) {
+ return printCentered(s,y,LemmFont.Color.GREEN);
+ }
+
+ /**
+ * Draw Image.
+ * @param img Image
+ * @param x X position relative to center
+ * @param y Y position relative to center
+ */
+ public void drawImage(final BufferedImage img, final int x, final int y) {
+ gScreen.drawImage(img,centerX+x,centerY+y,null);
+ }
+
+ /**
+ * Draw Image horizontally centered.
+ * @param img Image
+ * @param y Y position relative to center
+ */
+ public void drawImage(final BufferedImage img, final int y) {
+ int x = centerX-img.getWidth()/2;
+ gScreen.drawImage(img,x,centerY+y,null);
+ }
+
+ /**
+ * Add Button.
+ * @param x X position relative to center in pixels
+ * @param y Y position relative to center in pixels
+ * @param img Button image
+ * @param imgSelected Button selected image
+ * @param id Button ID
+ */
+ public void addButton(final int x, final int y, final BufferedImage img, final BufferedImage imgSelected, final int id) {
+ Button b = new Button(centerX+x,centerY+y,id);
+ b.SetImage(img);
+ b.SetImageSelected(imgSelected);
+ buttons.add(b);
+ }
+
+ /**
+ * Add text button.
+ * @param x0 X position relative to center (in characters)
+ * @param y0 Y position relative to center (in characters)
+ * @param id Button ID
+ * @param t Button text
+ * @param ts Button selected text
+ * @param col Button text color
+ * @param cols Button selected text color
+ */
+ public void addTextButton(final int x0, final int y0, final int id, final String t, final String ts, final LemmFont.Color col, final LemmFont.Color cols) {
+ int x = x0*LemmFont.getWidth();
+ int y = y0*(LemmFont.getHeight()+4);
+ TextButton b = new TextButton(centerX+x,centerY+y,id);
+ b.setText(t, col);
+ b.setTextSelected(ts, cols);
+ buttons.add(b);
+ }
+
+ /**
+ * React on left click.
+ * @param x Absolute x position in pixels
+ * @param y Absolute y position in pixels
+ * @return Button ID if button clicked, else -1
+ */
+ public int handleLeftClick(final int x, final int y) {
+ for (int i=0; i<buttons.size(); i++) {
+ Button b = buttons.get(i);
+ if (b.inside(x, y))
+ return b.id;
+ }
+ return -1;
+ }
+
+ /**
+ * React on mouse hover.
+ * @param x Absolute x position
+ * @param y Absolute y position
+ */
+ public void handleMouseMove(final int x, final int y) {
+ for (int i=0; i<buttons.size(); i++) {
+ Button b = buttons.get(i);
+ if (b.inside(x, y))
+ b.selected = true;
+ else
+ b.selected = false;
+ }
+ }
+
+ /**
+ * Draw buttons on screen.
+ */
+ public void drawButtons() {
+ for (int i=0; i<buttons.size(); i++)
+ buttons.get(i).draw(gScreen);
+ }
+
+ /**
+ * React on right click.
+ * @param x Absolute x position
+ * @param y Absolute y position
+ * @return Button ID if button clicked, else -1
+ */
+ public int handleRightClick(final int x, final int y) {
+ for (int i=0; i<buttons.size(); i++) {
+ Button b = buttons.get(i);
+ if (b.inside(x, y))
+ return b.id;
+ }
+ return -1;
+ }
+
+}
+
+/**
+ * Button class for TextDialog.
+ * @author Volker Oth
+ */
+class Button {
+ /** x coordinate in pixels */
+ private int x;
+ /** y coordinate in pixels */
+ private int y;
+ /** width in pixels */
+ protected int width;
+ /** height in pixels */
+ protected int height;
+ /** button identifier */
+ protected int id;
+ /** true if button is selected */
+ protected boolean selected;
+ /** normal button image */
+ protected BufferedImage image;
+ /** selected button image */
+ protected BufferedImage imgSelected;
+
+ /**
+ * Constructor
+ * @param xi x position in pixels
+ * @param yi y position in pixels
+ * @param idi identifier
+ */
+ Button(final int xi, final int yi, final int idi) {
+ x = xi;
+ y = yi;
+ id = idi;
+ }
+
+ /**
+ * Set normal button image.
+ * @param img image
+ */
+ void SetImage(final BufferedImage img) {
+ image = img;
+ if (image.getHeight() > height)
+ height = image.getHeight();
+ if (image.getWidth() > width)
+ width = image.getWidth();
+ }
+
+ /**
+ * Set selected button image.
+ * @param img image
+ */
+ void SetImageSelected(final BufferedImage img) {
+ imgSelected = img;
+ if (imgSelected.getHeight() > height)
+ height = imgSelected.getHeight();
+ if (imgSelected.getWidth() > width)
+ width = imgSelected.getWidth();
+ }
+
+ /**
+ * Return current button image (normal or selected, depending on state).
+ * @return current button image
+ */
+ BufferedImage getImage() {
+ if (selected)
+ return imgSelected;
+ else
+ return image;
+ }
+
+ /**
+ * Draw the button.
+ * @param g graphics object to draw on
+ */
+ void draw(final Graphics2D g) {
+ g.drawImage(getImage(),x,y,null);
+ }
+
+ /**
+ * Check if a (mouse) position is inside this button.
+ * @param xi
+ * @param yi
+ * @return true if the coordinates are inside this button, false if not
+ */
+ boolean inside(final int xi, final int yi) {
+ return (xi>=x && xi<x+width && yi>=y && yi<y+height);
+ }
+
+}
+
+/**
+ * Button class for TextDialog.
+ * @author Volker Oth
+ */
+class TextButton extends Button {
+ /**
+ * Constructor
+ * @param xi x position in pixels
+ * @param yi y position in pixels
+ * @param idi identifier
+ */
+ TextButton(final int xi, final int yi, final int idi) {
+ super(xi, yi, idi);
+ }
+
+ /**
+ * Set text which is used as button.
+ * @param s String which contains the button text
+ * @param color Color of the button (LemmFont color!)
+ */
+ void setText(final String s, final LemmFont.Color color) {
+ image = LemmFont.strImage(s, color);
+ if (image.getHeight() > height)
+ height = image.getHeight();
+ if (image.getWidth() > width)
+ width = image.getWidth();
+ }
+
+ /**
+ * Set text for selected button.
+ * @param s String which contains the selected button text
+ * @param color Color of the button (LemmFont color!)
+ */
+ void setTextSelected(final String s, final LemmFont.Color color) {
+ imgSelected = LemmFont.strImage(s, color);
+ if (imgSelected.getHeight() > height)
+ height = imgSelected.getHeight();
+ if (imgSelected.getWidth() > width)
+ width = imgSelected.getWidth();
+ }
+} \ No newline at end of file
diff --git a/jeu-test/Lemmini/0.84/src/Game/TextScreen.java b/jeu-test/Lemmini/0.84/src/Game/TextScreen.java
new file mode 100644
index 0000000..d66fa6f
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/TextScreen.java
@@ -0,0 +1,377 @@
+package Game;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+import static Game.LemmFont.Color.*;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Class to print text screens which can be navigated with the mouse.
+ * Uses {@link TextDialog}
+ *
+ * @author Volker Oth
+ */
+public class TextScreen {
+
+ /** Mode (type of screen to present) */
+ public static enum Mode {
+ /** initial state */
+ INIT,
+ /** main introduction screen */
+ INTRO,
+ /** level briefing screen */
+ BRIEFING,
+ /** level debriefing screen */
+ DEBRIEFING
+ }
+
+ /** Button: continue */
+ public final static int BUTTON_CONTINUE = 0;
+ /** Button: restart level */
+ public final static int BUTTON_RESTART = 1;
+ /** Button: back to menu */
+ public final static int BUTTON_MENU = 2;
+ /** Button: replay level */
+ public final static int BUTTON_REPLAY = 3;
+ /** Button: save replay */
+ public final static int BUTTON_SAVEREPLAY = 4;
+
+ /** y position of scroll text - pixels relative to center */
+ private final static int SCROLL_Y = 150;
+ /** width of scroll text in characters */
+ private final static int SCROLL_WIDTH = 39;
+ /** height of scroll text in pixels */
+ private final static int SCROLL_HEIGHT = LemmFont.getHeight()*2;
+ /** step width of scroll text in pixels */
+ private final static int SCROLL_STEP = 2;
+ /** scroll text */
+ private final static String SCROLL_TEXT =
+ " "+
+ "Lemmini - a game engine for Lemmings (tm) in Java. "+
+ "Thanks to Martin Cameron for his MicroMod Library, "+
+ "Jef Poskanzer for his GifEncoder Library, "+
+ "Mindless for his MOD conversions of the original Amiga Lemmings tunes, "+
+ "the guys of DMA Design for writing the original Lemmings, "+
+ "ccexplore and the other nice folks at the Lemmingswelt Forum for discussion and advice "+
+ "and to Sun for maintaining Java and providing the community with a free development environment.";
+
+ /** TextDialog used as base component */
+ private static TextDialog textScreen;
+ /** factor used for the rotation animation */
+ private static double rotFact = 1.0;
+ /** delta used for the rotation animation */
+ private static double rotDelta;
+ /** source image for rotation animation */
+ private static BufferedImage imgSrc;
+ /** target image for rotation animation */
+ private static BufferedImage imgTrg;
+ /** graphics for rotation animation */
+ private static Graphics2D imgGfx;
+ /** flip state for rotation: true - image is flipped in Y direction */
+ private static boolean flip;
+ /** affine transformation used for rotation animation */
+ private static AffineTransform at;
+ /** counter used to trigger the rotation animation (in animation update frames) */
+ private static int rotCtr;
+ /** counter threshold used to trigger the rotation animation (in animation update frames) */
+ private static final int maxRotCtr = 99;
+ /** used to stop the rotation only after it was flipped twice -> original direction */
+ private static int flipCtr;
+ /** counter for scrolled characters */
+ private static int scrollCharCtr;
+ /** counter for scrolled pixels */
+ private static int scrollPixCtr;
+ /** image used for scroller */
+ private static BufferedImage scrollerImg;
+ /** graphics used for scroller */
+ private static Graphics2D scrollerGfx;
+ /** screen type to display */
+ private static Mode mode;
+ /** sychronization monitor */
+ private static Object monitor = new Object();
+
+ /**
+ * Set mode.
+ * @param m mode.
+ */
+ public static void setMode(final Mode m) {
+ synchronized (monitor) {
+ if (mode != m) {
+ switch (m) {
+ case INTRO:
+ textScreen.init();
+ textScreen.fillBackground(MiscGfx.getImage(MiscGfx.Index.TILE_BROWN));
+ textScreen.printCentered("A game engine for Lemmings(tm) in Java", 0, RED);
+ textScreen.printCentered("Release 0.84 1/2010", 1, BLUE);
+ textScreen.printCentered("Coded by Volker Oth 2005-2010", 2, VIOLET);
+ textScreen.printCentered("www.lemmini.de", 3, GREEN);
+ textScreen.copyToBackBuffer();
+ //textScreen.addTextButton(-4, 3, 1, " Start ", "Let's go", BLUE, RED);
+ break;
+ case BRIEFING:
+ initBriefing();
+ break;
+ case DEBRIEFING:
+ initDebriefing();
+ break;
+ }
+ }
+ mode = m;
+ }
+ }
+
+ /**
+ * Initialize the briefing dialog.
+ */
+ static void initBriefing() {
+ textScreen.init();
+ textScreen.fillBackground(MiscGfx.getImage(MiscGfx.Index.TILE_GREEN));
+ Level level = GameController.getLevel();
+ //LevelInfo li;
+ textScreen.restore();
+ //li = GameController.levelPack[GameController.curLevelPack].getInfo(GameController.curDiffLevel, GameController.curLevelNumber);
+ String rating = GameController.getCurLevelPack().getDiffLevels()[GameController.getCurDiffLevel()];
+ textScreen.drawImage(GameController.getMapPreview(), -200);
+ textScreen.printCentered("Level "+(GameController.getCurLevelNumber()+1)+" "+level.getLevelName(), -2, RED);
+ textScreen.print("Number of Lemmings "+level.getNumLemmings(), -9, 0, BLUE);
+ textScreen.print(""+(level.getNumToRescue()*100/level.getNumLemmings())+"% to be saved", -9, 1, GREEN);
+ textScreen.print("Release Rate "+level.getReleaseRate(), -9, 2, BROWN);
+ int minutes = level.getTimeLimitSeconds() / 60;
+ int seconds = level.getTimeLimitSeconds() % 60;
+ if (seconds == 0)
+ textScreen.print("Time "+minutes+" Minutes", -9, 3, TURQUOISE);
+ else
+ textScreen.print("Time "+minutes+"-"+seconds+" Minutes", -9, 3, TURQUOISE);
+ textScreen.print("Rating "+rating, -9, 4, VIOLET);
+ textScreen.copyToBackBuffer(); // though not really needed
+ }
+
+ /**
+ * Initialize the debriefing dialog.
+ */
+ static void initDebriefing() {
+ textScreen.init();
+ textScreen.fillBackground(MiscGfx.getImage(MiscGfx.Index.TILE_GREEN));
+ int toRescue = GameController.getNumToRecue()*100/GameController.getNumLemmingsMax(); // % to rescue of total number
+ int rescued = GameController.getNumLeft()*100/GameController.getNumLemmingsMax(); // % rescued of total number
+ int rescuedOfToRescue = GameController.getNumLeft()*100/GameController.getNumToRecue(); // % rescued of no. to rescue
+ textScreen.restore();
+ if (GameController.getTime()==0)
+ textScreen.printCentered("Time is up.", -6, TURQUOISE);
+ else
+ textScreen.printCentered("All lemmings accounted for.", -6, TURQUOISE);
+ textScreen.print("You needed: "+Integer.toString(toRescue)+"%", -7, -4, VIOLET);
+ textScreen.print("You rescued: "+Integer.toString(rescued)+"%", -7, -3, VIOLET);
+ if (GameController.wasLost()) {
+ if (rescued == 0) {
+ textScreen.printCentered("ROCK BOTTOM! I hope for your sake", -1, RED);
+ textScreen.printCentered("that you nuked that level", 0, RED);
+ } else if (rescuedOfToRescue < 50){
+ textScreen.printCentered("Better rethink your strategy before", -1, RED);
+ textScreen.printCentered("you try this level again!", 0, RED);
+ } else if (rescuedOfToRescue < 95){
+ textScreen.printCentered("A little more practice on this level", -1, RED);
+ textScreen.printCentered("is definitely recommended.", 0, RED);
+ } else {
+ textScreen.printCentered("You got pretty close that time.", -1, RED);
+ textScreen.printCentered("Now try again for that few % extra.", 0, RED);
+ }
+ textScreen.addTextButton(-2, 6, BUTTON_RESTART, "Retry", "Retry", BLUE, BROWN);
+ } else {
+ if (rescued == 100) {
+ textScreen.printCentered("Superb! You rescued every lemming on", -1, RED);
+ textScreen.printCentered("that level. Can you do it again....?", 0, RED);
+ } else if (rescued > toRescue) {
+ textScreen.printCentered("You totally stormed that level!", -1, RED);
+ textScreen.printCentered("Let's see if you can storm the next...", 0, RED);
+ } else if (rescued == toRescue) {
+ textScreen.printCentered("SPOT ON. You can't get much closer", -1, RED);
+ textScreen.printCentered("than that. Let's try the next....", 0, RED);
+ } else {
+ textScreen.printCentered("That level seemed no problem to you on", -1, RED);
+ textScreen.printCentered("that attempt. Onto the next.... ", 0, RED);
+ }
+ LevelPack lp = GameController.getCurLevelPack();
+ int ln = GameController.getCurLevelNumber();
+ if (lp.getLevels(GameController.getCurDiffLevel()).length > ln+1) {
+ textScreen.printCentered("Your access code for level "+(ln+2), 2, BROWN);
+ int absLevel = GameController.absLevelNum(GameController.getCurLevelPackIdx(), GameController.getCurDiffLevel(), ln+1);
+ String code = LevelCode.create(lp.getCodeSeed(), absLevel, rescued, 0,lp.getCodeOffset());
+ textScreen.printCentered("is "+code, 3, BROWN);
+ textScreen.addTextButton(-4, 6, BUTTON_CONTINUE, "Continue", "Continue", BLUE, BROWN);
+ } else {
+ textScreen.printCentered("Congratulations!", 2, BROWN);
+ textScreen.printCentered("You finished all the "+lp.getDiffLevels()[GameController.getCurDiffLevel()]+" levels!",3, GREEN);
+ }
+ }
+ textScreen.copyToBackBuffer(); // though not really needed
+ textScreen.addTextButton(-12, 5, BUTTON_REPLAY, "Replay", "Replay", BLUE, BROWN);
+ if (GameController.getCurLevelPackIdx() != 0) // not for single levels started via "load level"
+ textScreen.addTextButton( -4, 5, BUTTON_SAVEREPLAY, "Save Replay", "Save Replay", BLUE, BROWN);
+ textScreen.addTextButton( 9, 5, BUTTON_MENU, "Menu", "Menu", BLUE, BROWN);
+ }
+
+ /**
+ * Get text dialog.
+ * @return text dialog.
+ */
+ public static TextDialog getDialog() {
+ synchronized (monitor) {
+ return textScreen;
+ }
+ }
+
+ /**
+ * Initialize text screen.
+ * @param width width in pixels
+ * @param height height in pixels
+ */
+ public static void init(final int width, final int height) {
+ rotFact = 1.0;
+ rotDelta = -0.1;
+ imgSrc = MiscGfx.getImage(MiscGfx.Index.LEMMINI);
+ at = new AffineTransform();
+ flip = false;
+ rotCtr = 0 ;
+ flipCtr = 0;
+ imgTrg = ToolBox.createImage(imgSrc.getWidth(),imgSrc.getHeight(), Transparency.TRANSLUCENT);
+ imgGfx = imgTrg.createGraphics();
+ imgGfx.setBackground(new Color(0,0,0,0)); // invisible
+ scrollCharCtr = 0;
+ scrollPixCtr = 0;
+
+ scrollerImg = ToolBox.createImage(LemmFont.getWidth()*(1+SCROLL_WIDTH),SCROLL_HEIGHT, Transparency.BITMASK);
+ scrollerGfx = scrollerImg.createGraphics();
+ scrollerGfx.setBackground(new Color(0,0,0,0));
+
+ textScreen = new TextDialog(width, height);
+
+ }
+
+ /**
+ * Update the text screen (for animations)
+ */
+ public static void update() {
+ synchronized (monitor) {
+ textScreen.restore();
+ switch (mode) {
+ case INTRO:
+ update_intro();
+ break;
+ case BRIEFING:
+ update_briefing();
+ break;
+ case DEBRIEFING:
+ update_debriefing();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Update the into screen.
+ */
+ private static void update_intro() {
+ // manage logo rotation
+ if (++rotCtr > maxRotCtr) {
+ // animate
+ rotFact += rotDelta;
+ if (rotFact <= 0.0) {
+ // minimum size reached -> flip and increase again
+ rotFact = 0.1;
+ rotDelta = -rotDelta;
+ flip = !flip;
+ } else if (rotFact > 1.0) {
+ // maximum size reached -> decrease again
+ rotFact = 1.0;
+ rotDelta = -rotDelta;
+ // reset only after two rounds (flipped back)
+ if (++flipCtr > 1)
+ rotCtr = 0;
+ }
+ if (flip) {
+ at.setToScale(1, -rotFact);
+ at.translate(1,-imgSrc.getHeight());
+ } else at.setToScale(1, rotFact);
+ AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ imgGfx.clearRect(0, 0, imgTrg.getWidth(), imgTrg.getHeight());
+ op.filter(imgSrc, imgTrg);
+ textScreen.drawImage(imgTrg, -120 - (int)(imgSrc.getHeight()/2*Math.abs(rotFact)+0.5));
+ } else {
+ // display original image
+ flipCtr=0;
+ textScreen.drawImage(imgSrc, -120 - imgSrc.getHeight()/2);
+ }
+ // manage scroller
+ String out;
+ boolean wrapAround = false;
+ int endIdx = scrollCharCtr+SCROLL_WIDTH+1;
+ if (endIdx > SCROLL_TEXT.length()) {
+ endIdx = SCROLL_TEXT.length();
+ wrapAround = true;
+ }
+ out = SCROLL_TEXT.substring(scrollCharCtr, endIdx);
+ if (wrapAround)
+ out += SCROLL_TEXT.substring(0,scrollCharCtr+SCROLL_WIDTH+1-SCROLL_TEXT.length());
+ scrollerGfx.clearRect(0, 0, scrollerImg.getWidth(), scrollerImg.getHeight());
+ LemmFont.strImage(scrollerGfx, out, BLUE);
+ int w = SCROLL_WIDTH*LemmFont.getWidth();
+ int dx = (textScreen.getScreen().getWidth()-w)/2;
+ int dy = (textScreen.getScreen().getHeight()/2)+SCROLL_Y;
+ textScreen.getScreen().createGraphics().drawImage(
+ scrollerImg, dx, dy, dx+w, dy+SCROLL_HEIGHT, scrollPixCtr,0,scrollPixCtr+w,SCROLL_HEIGHT/2, null
+ );
+
+ scrollPixCtr+=SCROLL_STEP;
+ if (scrollPixCtr >= LemmFont.getWidth()) {
+ scrollCharCtr++;
+ scrollPixCtr = 0;
+ if (scrollCharCtr >= SCROLL_TEXT.length())
+ scrollCharCtr = 0;
+ }
+ }
+
+ /**
+ * Update the briefing screen.
+ */
+ private static void update_briefing() {
+ }
+
+ /**
+ * Update the debriefing screen.
+ */
+ private static void update_debriefing() {
+ textScreen.drawButtons();
+ }
+
+ /**
+ * Get image of text screen
+ * @return image of text screen
+ */
+ public static BufferedImage getScreen() {
+ synchronized (monitor) {
+ return textScreen.getScreen();
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Game/UpdateListener.java b/jeu-test/Lemmini/0.84/src/Game/UpdateListener.java
new file mode 100644
index 0000000..a6f4cb5
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Game/UpdateListener.java
@@ -0,0 +1,29 @@
+package Game;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Listener interface to inform GUI about updates
+ *
+ * @author Volker Oth
+ */
+public interface UpdateListener {
+ /**
+ * Action to perform if Listener is called
+ */
+ public void update();
+}
diff --git a/jeu-test/Lemmini/0.84/src/GameUtil/Fader.java b/jeu-test/Lemmini/0.84/src/GameUtil/Fader.java
new file mode 100644
index 0000000..d77cfd4
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GameUtil/Fader.java
@@ -0,0 +1,189 @@
+package GameUtil;
+
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple fader class.
+ * instead of doing painfully slow pixel wise gamma manipulation,
+ * use a square with transparency with is drawn over the whole graphic context.
+ *
+ * @author Volker Oth
+ */
+public class Fader {
+ /** width of square to use for fading */
+ private final static int WIDTH = 64;
+ /** height of square to use for fading */
+ private final static int HEIGHT = 64;
+ /** maximum alpha (opaque) */
+ private final static int MAX_ALPHA = 0xff;
+
+ /** Fader state */
+ public static enum State {
+ /** don't fade */
+ OFF,
+ /** fade in */
+ IN,
+ /** fade out */
+ OUT
+ }
+
+ /** current alpha value */
+ private static int fadeValue;
+ /** current fade state */
+ private static State fadeState = State.OFF;
+ /** step size for fading */
+ private static int fadeStep = 14;
+ /** color of the fading rectangle */
+ private static int color = 0; // black
+ /** alpha value of the fading rectangle */
+ private static int alpha = 0x80; // half transparent
+ /** width of faded area */
+ private static int width;
+ /** height of faded area */
+ private static int height;
+ /** the image used as fading rectangle */
+ private static BufferedImage alphaImg = null;
+ /** the graphics used as fading rectangle (static to avoid multiple allocation) */
+ private static Graphics2D alphaGfx;
+
+ /**
+ * Set color to be used for fading.
+ * @param c RGB color
+ */
+ public static synchronized void setColor(final int c) {
+ color = c & 0xffffff;
+ init();
+ }
+
+ /**
+ * Set alpha value to be used for fading.
+ * @param a 8bit alpha value
+ */
+ public static synchronized void setAlpha(final int a) {
+ alpha = a & 0xff;
+ init();
+ }
+
+ /**
+ * Set bounds of fading area.
+ * @param w width in pixels
+ * @param h height pixels
+ */
+ public static synchronized void setBounds(final int w, final int h) {
+ width = w;
+ height = h;
+ }
+
+ /**
+ * Initialize fader.
+ */
+ private static void init() {
+ Color fillColor; /* ARGB color of the fading rectangle composed from alpha and color */
+ // create alpha image if needed
+ if (alphaImg == null) {
+ alphaImg = ToolBox.createImage(WIDTH, HEIGHT, Transparency.TRANSLUCENT);
+ alphaGfx = alphaImg.createGraphics();
+ }
+ // fill with alpha blended color
+ fillColor = new Color((color>>16)&0xff, (color>>8)&0xff, color&0xff, alpha);
+ alphaGfx.setBackground(fillColor);
+ alphaGfx.clearRect(0, 0, WIDTH, HEIGHT);
+ }
+
+ /**
+ * Apply fader without changing the fader state.
+ * @param g graphics to apply fader to
+ */
+ public static synchronized void apply(final Graphics g) {
+ for (int y=0; y<height; y+= HEIGHT)
+ for (int x=0; x<width; x+= WIDTH)
+ g.drawImage(alphaImg,x,y,null);
+ }
+
+ /**
+ * Set fader state.
+ * @param s state
+ */
+ public static synchronized void setState(final State s) {
+ fadeState = s;
+ switch (fadeState) {
+ case IN:
+ fadeValue = MAX_ALPHA; // opaque
+ setAlpha(fadeValue);
+ break;
+ case OUT:
+ fadeValue = 0; // transparent
+ setAlpha(fadeValue);
+ break;
+ }
+ }
+
+ /**
+ * Get fader state.
+ * @return fader state.
+ */
+ public static synchronized State getState() {
+ return fadeState;
+ }
+
+ /**
+ * Set step size.
+ * @param step
+ */
+ public static void setStep(final int step) {
+ fadeStep = step & 0xff;
+ }
+
+ /**
+ * Fade.
+ * @param g graphics to fade
+ */
+ public static synchronized void fade(final Graphics g) {
+ switch (fadeState) {
+ case IN:
+ if (fadeValue >= fadeStep)
+ fadeValue -= fadeStep;
+ else {
+ fadeValue = 0;
+ fadeState = State.OFF;
+ }
+ Fader.setAlpha(fadeValue);
+ Fader.apply(g);
+ // System.out.println(fadeValue);
+ break;
+ case OUT:
+ if (fadeValue <= MAX_ALPHA-fadeStep)
+ fadeValue += fadeStep;
+ else {
+ fadeValue = MAX_ALPHA;
+ fadeState = State.OFF;
+ }
+ Fader.setAlpha(fadeValue);
+ Fader.apply(g);
+ // System.out.println(fadeValue);
+ break;
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/GameUtil/KeyRepeat.java b/jeu-test/Lemmini/0.84/src/GameUtil/KeyRepeat.java
new file mode 100644
index 0000000..eb548e7
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GameUtil/KeyRepeat.java
@@ -0,0 +1,178 @@
+package GameUtil;
+
+import Tools.MicrosecondTimer;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * A more or less generic approach to key repeat function.<br>
+ * - if a key is pressed and released, a single SINGLE_CLICK is returned
+ * - if a key is pressed for longer than timeFirstPress but not released,
+ * a SINGLE_CLICK event is returned. Then if the key is still not released,
+ * each timeRepeat microseconds, a new SINGLE_CLICK is returned.
+ * - if a key is pressed, released and pressed again within timeDoubleClick
+ * microseconds, a DOUBLE_CLICK is fired.
+ *
+ * @author Volker Oth
+ */
+public class KeyRepeat {
+
+ /** key repeat event */
+ public static enum Event {
+ /** no state reached yet */
+ NONE,
+ /** single click detected */
+ SINGLE_CLICK,
+ /** double click detected */
+ DOUBLE_CLICK
+ }
+
+ /** repeat state */
+ private enum State {
+ /** off state */
+ OFF,
+ /** wait for timeFirstPress to pass */
+ DETECT_FIRST,
+ /** wait for timeRepeat to pass */
+ REPEAT
+ }
+
+ /** double click state */
+ private enum DcState {
+ /** not pressed */
+ OFF,
+ /** pressed and released once */
+ PRESSED_ONCE
+ }
+
+ /** timer used as time base */
+ private MicrosecondTimer timer;
+ /** internal debounce state */
+ private State state;
+ /** double click state */
+ private DcState ddstate;
+ /** 32bit debounce mask - up to 32 triggers can used for the same event */
+ private int mask;
+ /** time after which a pressed (and not released) key is recognized as key press (microseconds) */
+ private long timeFirstPress;
+ /** time after which a pressed key fires repeatedly (microseconds) */
+ private long timeRepeat;
+ /** the maximum time between two clicks to be recognized as double click (microseconds) */
+ private long timeDoubleClick;
+ /** repeat event */
+ private Event event;
+
+ /**
+ * Constructor.
+ * @param tfirst time after which a pressed (and not released) key is recognized as key press (microseconds)
+ * @param trep time for repeat function (microseconds)
+ * @param tdc maximum time between two clicks to be recognized as double click (microseconds)
+ */
+ public KeyRepeat(final long tfirst, final long trep, final long tdc) {
+ timer = new MicrosecondTimer();
+ timeFirstPress = tfirst;
+ timeRepeat = trep;
+ timeDoubleClick = tdc;
+ init();
+ }
+
+ /**
+ * Initialize/Reset all states.
+ */
+ public void init() {
+ state = State.OFF;
+ ddstate = DcState.OFF;
+ mask = 0;
+ timer.update();
+ }
+
+ /**
+ * Button/Icon was released.
+ * @param m trigger mask
+ */
+ public synchronized void pressed(final int m) {
+ // check repeat states
+ if ( (mask & m) == m) // already pressed - possible for keys -> ignore
+ return;
+ switch (ddstate) {
+ case OFF:
+ // first press -> start timer and remember event
+ ddstate = DcState.PRESSED_ONCE;
+ event = Event.SINGLE_CLICK;
+ break;
+ case PRESSED_ONCE:
+ // clicked and released before
+ if (!timer.timePassed(timeDoubleClick)) {
+ // double click
+ ddstate = DcState.OFF;
+ event = Event.DOUBLE_CLICK;
+ } else
+ // new single click
+ event = Event.SINGLE_CLICK;
+ break;
+ }
+ mask |= m;
+ state = State.DETECT_FIRST;
+ timer.update(); // set up lock timer
+ }
+
+ /**
+ * Button/Icon was released.
+ * @param m trigger mask
+ */
+ public synchronized void released(final int m) {
+ mask &= ~m;
+ if (mask == 0)
+ state = State.OFF;
+ }
+
+ /**
+ * Poll the last event.
+ * @return repeat event
+ */
+ public synchronized Event fired() {
+ // check if there is a pending event stored
+ if (event != Event.NONE) {
+ // return event, reset internal state
+ Event temp = event;
+ event = Event.NONE;
+ return temp;
+ }
+
+ // no button pressed, no event stored -> return none
+ if (mask == 0)
+ return Event.NONE;
+
+ // no event stored, but button still pressed: check repeat states
+ switch (state) {
+ case DETECT_FIRST:
+ if (timer.timePassedUpdate(timeFirstPress)) {
+ // first event occured
+ state = State.REPEAT;
+ ddstate = DcState.OFF;
+ return Event.SINGLE_CLICK;
+ }
+ break;
+ case REPEAT:
+ if (timer.timePassedUpdate(timeRepeat)) // new repeat event occured
+ return Event.SINGLE_CLICK;
+ break;
+ }
+ return Event.NONE;
+ }
+}
+
diff --git a/jeu-test/Lemmini/0.84/src/GameUtil/Sound.java b/jeu-test/Lemmini/0.84/src/GameUtil/Sound.java
new file mode 100644
index 0000000..d275bb2
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GameUtil/Sound.java
@@ -0,0 +1,369 @@
+package GameUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.FloatControl;
+import javax.sound.sampled.Line;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.Mixer;
+
+import Game.Core;
+import Game.GameController;
+import Game.ResourceException;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Used to play a number of sounds.
+ * Supports upsampling and one pitched sample.
+ * @author Volker Oth
+ */
+public class Sound {
+
+ /** default sampling frequency */
+ final private static float DEFAULT_FREQUENCY = 22050;
+ /** allow upsampling for lower frequencies */
+ final private static boolean ALLOW_UPSAMLING = true;
+ /** number of pitch levels */
+ final private static int NUMBER_PITCHED = 100;
+ /** fade in the first n samples when calculating the pitched buffers */
+ final private static int PITCH_FADE_IN = 20;
+ /** maximum number of sounds played in parallel */
+ final private static int MAX_SIMUL_SOUNDS = 6;
+
+ /** line listener to be called after sample was played */
+ private LineListener defaultListener;
+ /** sound buffers to store the samples */
+ private byte soundBuffer[][];
+ /** pitch buffers to store all pitched samples */
+ private byte pitchBuffers[][];
+ /** audio formats for normal samples (one for each sample) */
+ private AudioFormat format[];
+ /** audio format for pitched samples */
+ private AudioFormat pitchFormat;
+ /** audio format for upsampling */
+ private AudioFormat defaultFormat;
+ /** line info for each sample */
+ private DataLine.Info info[];
+ /** line info for the pitched sample */
+ private DataLine.Info pitchInfo;
+ /** line info for upsampling */
+ private DataLine.Info defaultInfo;
+ /** gain/volume: 1.0 = 100% */
+ private double gain;
+ /** number of sounds currently played */
+ static int simulSounds;
+ /** selected mixer index */
+ static int mixerIdx;
+ /** array of available mixers */
+ static Mixer mixers[];
+ /** number of samples to be used */
+ static int sampleNum;
+
+ /**
+ * Constructor.
+ * @param snum number of samples to use
+ * @param pitchID ID of the pitched sample (-1 for none)
+ * @throws ResourceException
+ */
+ public Sound(final int snum, final int pitchID) throws ResourceException {
+ String fName="";
+ sampleNum = snum;
+ soundBuffer = new byte[sampleNum][];
+ format = new AudioFormat[sampleNum];
+ info = new DataLine.Info[sampleNum];
+
+ gain = 1.0;
+ simulSounds = 0;
+ defaultListener = new DefaultListener();
+
+ // upsampling to default frequency (more compatible for weird sample frequencies)
+ if (ALLOW_UPSAMLING) {
+ defaultFormat = new AudioFormat( DEFAULT_FREQUENCY, 8, 1, false, false);
+ defaultInfo = new DataLine.Info(Clip.class, defaultFormat);
+ }
+
+ int maxLen = 0;
+ try {
+ for (int i = 0; i<sampleNum; i++) {
+ fName = "sound/sound_"+Integer.toString(i)+".wav";
+ File fs = new File(Core.findResource(fName));
+ AudioInputStream f = AudioSystem.getAudioInputStream(fs.toURI().toURL());
+ format[i] = f.getFormat();
+ info[i] = new DataLine.Info(Clip.class, format[i]);
+ soundBuffer[i] = new byte[(int)f.getFrameLength()*format[i].getFrameSize()];
+ f.read(soundBuffer[i]);
+ f.close();
+
+ if (ALLOW_UPSAMLING) {
+ // convert samples with frequencies < 8kHz no work around bug in JDK6
+ float sr = format[i].getSampleRate();
+ if ( (sr < DEFAULT_FREQUENCY) && (sr!=8000) && (sr!=11025) ) {
+ soundBuffer[i] = convertToDefault(soundBuffer[i], format[i].getSampleRate());
+ format[i] = defaultFormat;
+ info[i] = defaultInfo;
+ }
+ }
+
+ if (soundBuffer[i].length > maxLen)
+ maxLen = soundBuffer[i].length;
+ }
+ } catch (Exception ex) {
+ throw new ResourceException(fName);
+ }
+
+ if (pitchID >= 0) {
+ // create buffers for pitching
+ // note that bit size (8) and channels (1) have to be the same for all pitched buffers
+ pitchFormat = new AudioFormat( 44100, 8, 1, false, false);
+ pitchInfo = new DataLine.Info(Clip.class, pitchFormat);
+ pitchBuffers = new byte[NUMBER_PITCHED][];
+ for (int i=0; i<NUMBER_PITCHED; i++)
+ pitchBuffers[i] = createPitched(pitchID, i);
+ }
+
+ // get all available mixers
+ Mixer.Info[] mixInfo = AudioSystem.getMixerInfo();
+ ArrayList<Mixer> mix = new ArrayList<Mixer>();
+ for (int i=0; i<mixInfo.length; i++) {
+ Mixer mixer = AudioSystem.getMixer(mixInfo[i]);
+ Line.Info info = new Line.Info(Clip.class);
+ int num = mixer.getMaxLines(info);
+ if (num != 0)
+ mix.add(mixer);
+ }
+ mixers = new Mixer[mix.size()];
+ mixers = mix.toArray(mixers);
+ }
+
+ /**
+ * Get an array of available mixer names.
+ * @return array of available mixer names
+ */
+ public String[] getMixers() {
+ if (mixers==null)
+ return null;
+ String s[] = new String[mixers.length];
+ for (int i=0; i<mixers.length; i++)
+ s[i] = mixers[i].getMixerInfo().getName();
+ return s;
+ }
+
+ /**
+ * Set mixer to be used for sound output.
+ * @param idx index of mixer
+ */
+ public void setMixer(final int idx) {
+ if (idx > mixers.length)
+ mixerIdx = 0;
+ else
+ mixerIdx = idx;
+ }
+
+ /**
+ * Return a data line to play a sample.
+ * @param info line info with requirements
+ * @return data line to play a sample
+ */
+ public Line getLine(final DataLine.Info info) {
+ try {
+ return mixers[mixerIdx].getLine(info);
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Play a given sound.
+ * @param idx index of the sound to be played
+ */
+ public synchronized void play(final int idx) {
+ if (!GameController.isSoundOn() || simulSounds >= MAX_SIMUL_SOUNDS /*|| clips==null*/)
+ return;
+
+ try {
+ Clip c = (Clip)mixers[mixerIdx].getLine(info[idx]);
+ // Add a listener for line events
+ c.addLineListener(defaultListener);
+ c.open(format[idx],soundBuffer[idx],0,soundBuffer[idx].length);
+ setLineGain(c, gain);
+ c.start();
+ simulSounds++;
+ } catch (Exception ex) {}
+ }
+
+ /**
+ * Convert sampling rate to default sampling rate.
+ * @param buffer byte array containing source sample
+ * @param frqSource sampling frequency of source sample
+ * @return sample converted to DEFAULT_FREQUENCY stored in byte array
+ */
+ public synchronized byte[] convertToDefault(final byte buffer[], final float frqSource) {
+ // sample up low frequency files to a DEFAULT_FREQUENCY to work around sound bug in JDK6
+ double scale = DEFAULT_FREQUENCY/frqSource;
+ int len = (int)(buffer.length*scale); // length of target buffer in samples
+ byte buf[] = new byte[len];
+
+ // create scaled buffer
+ for (int i=0; i<len; i++ ) {
+ int pos = (int)(i/scale);
+ double ofs = i/scale - pos;
+ if (pos >= buffer.length)
+ pos = buffer.length-1;
+ if (ofs < 0.1 || pos == buffer.length-1)
+ buf[i] = buffer[pos];
+ else {
+ // interpolate between sample points
+ buf[i] = (byte)( (buffer[pos]&0xff)*(1.0-ofs) + (buffer[pos+1]&0xff)*(ofs));
+ }
+ }
+ return buf;
+ }
+
+ /**
+ * Create a pitched version of a sample.
+ * @param idx index of the sample to be pitched
+ * @param pitch pitch value as percent (0..100)
+ * @return pitched sample as array of byte
+ */
+ public synchronized byte[] createPitched(final int idx, final int pitch) {
+ // the idea is to sample up to 44KHz
+ // then increase the sample rate virtually by creating a buffer which contains only
+ // every Nth sample
+
+ if (format[idx].getFrameSize() > 1) // only 8bit supported at the moment
+ return null;
+ double scale = pitchFormat.getSampleRate()/format[idx].getSampleRate();
+ double dpitch = (1.0+((pitch-1)*0.0204));
+ if (dpitch < 1.0)
+ dpitch = 1.0;
+ double fact = dpitch/scale;
+ boolean signed = pitchFormat.getEncoding() == AudioFormat.Encoding.PCM_SIGNED;
+ int len = (int)(soundBuffer[idx].length/fact); // length of target buffer in samples
+ byte buf[] = new byte[len];
+ // create scaled buffer
+ for (int i=0; i<len; i++) {
+ int pos = (int)(i*fact+0.5);
+ if (pos >= soundBuffer[idx].length)
+ pos = soundBuffer[idx].length-1;
+ byte b = soundBuffer[idx][pos/**bytes+k*/];
+ double val;
+ if (signed)
+ val = b;
+ else
+ val = (b & 0xff);
+ // fade in
+ if (i<PITCH_FADE_IN)
+ val *= 1.0 - (PITCH_FADE_IN-1-i)/10.0;
+ buf[i] = (byte)val;
+ }
+ return buf;
+ }
+
+
+
+ /**
+ * Play the pitched sample.
+ * @param pitch pitch value 0..99
+ */
+ public synchronized void playPitched(final int pitch) {
+ if (!GameController.isSoundOn() || simulSounds >= MAX_SIMUL_SOUNDS)
+ return;
+
+ try {
+ Clip c = (Clip)mixers[mixerIdx].getLine(pitchInfo);
+ // Add a listener for line events
+ c.addLineListener(defaultListener);
+ c.open(pitchFormat,pitchBuffers[pitch],0,pitchBuffers[pitch].length);
+ setLineGain(c,gain);
+ c.start();
+ simulSounds++;
+ } catch (Exception ex) {
+ }
+ }
+
+ /**
+ * Set gain of a line.
+ * @param line line
+ * @param gn gain (1.0 = 100%)
+ */
+ public void setLineGain(final Line line, double gn) {
+ if (line != null) {
+ try {
+ double g;
+ FloatControl control = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN);
+ double maxGain = Math.pow(10, control.getMaximum()/20);
+ if (gn == 0)
+ g = 0.001;
+ else
+ g = gn;
+ float fgain = 20*(float)Math.log10(g*maxGain);
+ control.setValue(fgain);
+ } catch (IllegalArgumentException ex) {}
+ }
+ }
+
+ /**
+ * Get gain.
+ * @return gain (1.0 == 100%)
+ */
+ public double getGain() {
+ return gain;
+ }
+
+ /**
+ * Set gain.
+ * @param gn gain (1.0 == 100%)
+ */
+ public void setGain(final double gn) {
+ if (gn > 1.0)
+ gain = 1.0;
+ else if (gn < 0)
+ gain = 0;
+ else
+ gain = gn;
+ Core.programProps.set("soundGain", gain);
+ }
+
+}
+
+/**
+ * Default Line Listener. Called after sample was played.
+ * @author Volker Oth
+ */
+class DefaultListener implements LineListener {
+ /* (non-Javadoc)
+ * @see javax.sound.sampled.LineListener#update(javax.sound.sampled.LineEvent)
+ */
+ public synchronized void update(final LineEvent event) {
+ if (event.getType().equals(LineEvent.Type.STOP)) {
+ Clip c = (Clip)event.getLine();
+ if (c.isOpen()){
+ c.close();
+ if (--Sound.simulSounds < 0)
+ Sound.simulSounds = 0;
+ }
+ }
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/GameUtil/Sprite.java b/jeu-test/Lemmini/0.84/src/GameUtil/Sprite.java
new file mode 100644
index 0000000..fbef9fa
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/GameUtil/Sprite.java
@@ -0,0 +1,270 @@
+package GameUtil;
+
+import java.awt.Image;
+import java.awt.Transparency;
+import java.awt.image.BufferedImage;
+
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Simple sprite class.
+ *
+ * @author Volker Oth
+ */
+public class Sprite {
+
+ /**
+ * Animation style.
+ */
+ public static enum Animation {
+ /** not animated */
+ NONE,
+ /** endless animation loop */
+ LOOP,
+ /** triggered animation */
+ TRIGGERED,
+ /** one animation cycle */
+ ONCE
+ };
+
+ /** sprite width in pixels */
+ protected int width;
+ /** sprite height in pixels */
+ protected int height;
+ /** number of animation frames */
+ private int numframes;
+ /** index of current animation frame */
+ private int frameIdx;
+ /** animation mode */
+ private Animation animMode;
+ /** index for a sound */
+ private int sound;
+ /** boolean flag: animation was triggered */
+ private boolean triggered;
+ /** array of animation frames */
+ private BufferedImage frames[];
+
+ /**
+ * Constructor.
+ * @param sourceImg Image containing animation frames one above each other.
+ * @param animFrames number of frames.
+ */
+ public Sprite(final Image sourceImg, final int animFrames) {
+ init(sourceImg, animFrames);
+ }
+
+ /**
+ * Constructor. Create Sprite from other Sprite.
+ * @param src Sprite to clone.
+ */
+ public Sprite(final Sprite src) {
+ copyFrom(src);
+ }
+
+ /**
+ * Copy all class attributes from another Sprite to this one.
+ * @param src Sprite to copy from.
+ */
+ private void copyFrom(final Sprite src) {
+ numframes = src.numframes;
+ width = src.width;
+ height = src.height;
+ frameIdx = src.frameIdx;
+ animMode = src.animMode;
+ sound = src.sound;
+ triggered = false;
+ frames = src.frames.clone();
+ }
+
+ /**
+ * Initialized sprite with new animation.
+ * @param sourceImg Image containing animation frames one above each other.
+ * @param animFrames number of frames.
+ */
+ private void init(final Image sourceImg, final int animFrames) {
+ numframes = animFrames;
+ width = sourceImg.getWidth(null);
+ height = sourceImg.getHeight(null)/numframes;
+ frameIdx = 0;
+ animMode = Animation.NONE;
+ triggered = false;
+ // animation frames stored one above the other - now separate them into single images
+ frames = ToolBox.getAnimation(sourceImg, animFrames, Transparency.BITMASK);
+ }
+
+ /**
+ * Get given animation frame.
+ * @param idx index of animation frame
+ * @return animation frame at position idx.
+ */
+ public BufferedImage getImage(final int idx) {
+ return frames[idx];
+ }
+
+ /**
+ * Get current animation frame.
+ * @return current animation frame.
+ */
+ public BufferedImage getImage() {
+ return frames[frameIdx];
+ }
+
+ /**
+ * Replace one animation frame.
+ * Note: replacing with an image of different size will create problems.
+ * @param idx index of frame to replace
+ * @param img image to use for this animation frame
+ */
+ public void setImage(final int idx, final BufferedImage img) {
+ frames[idx] = img;
+ }
+
+ /**
+ * Get current animation frame and animate.
+ * @return current animation frame (before increasing the animation step).
+ */
+ public BufferedImage getImageAnim() {
+ BufferedImage i = frames[frameIdx];
+ switch (animMode) {
+ case LOOP:
+ if(++frameIdx >= numframes)
+ frameIdx = 0;
+ break;
+ case ONCE:
+ if (frameIdx < numframes-1)
+ frameIdx++;
+ break;
+ case TRIGGERED:
+ if (triggered) {
+ if (frameIdx < numframes-1)
+ frameIdx++;
+ else {
+ triggered = false;
+ frameIdx = 0;
+ }
+ } else frameIdx = 0;
+ break;
+ }
+ return i;
+ }
+
+ /**
+ * Set Pixel in all animation frames.
+ * @param x x position
+ * @param y y position
+ * @param color color as TYPE_INT_ARGB
+ */
+ public void setPixel(final int x, final int y, final int color) {
+ if (x>=0 && x<width && y>=0 && y<height)
+ for (int i=0; i<numframes; i++)
+ frames[i].setRGB(x,y,color);
+ }
+
+ /**
+ * Get animation mode.
+ * @return animation mode.
+ */
+ public Animation getAnimMode() {
+ return animMode;
+ }
+
+ /**
+ * Set animation mode.
+ * @param mode animation mode
+ */
+ public void setAnimMode(final Animation mode) {
+ animMode = mode;
+ }
+
+ /**
+ * Check if the animation is a triggered type.
+ * @return true if the animation is Animation.TRIGGERED.
+ */
+ public boolean canBeTriggered() {
+ return (animMode == Animation.TRIGGERED);
+ }
+
+ /**
+ * Trigger a triggered animation
+ * @return true if not yet triggered, false if already triggered
+ */
+ public boolean trigger() {
+ if (triggered == true)
+ return false;
+ triggered = true;
+ frameIdx = 1;
+ return true;
+ }
+
+ /**
+ * Get current animation frame index.
+ * @return current animation frame index
+ */
+ public int getFrameIdx() {
+ return frameIdx;
+ }
+
+ /** Set current animation frame index.
+ * @param frameIdx current animation frame index
+ */
+ public void setFrameIdx(final int frameIdx) {
+ this.frameIdx = frameIdx;
+ }
+
+ /**
+ * Get number of animation frames.
+ * @return number of animation frames
+ */
+ public int getNumFrames() {
+ return numframes;
+ }
+
+ /**
+ * Get sound index.
+ * @return sound index
+ */
+ public int getSound() {
+ return sound;
+ }
+
+ /**
+ * Set sound index.
+ * @param s sound index
+ */
+ public void setSound(final int s) {
+ sound = s;
+ }
+
+ /**
+ * Get width of Sprite in pixels.
+ * @return width of Sprite in pixels
+ */
+ public int getWidth() {
+ return width;
+ }
+
+ /**
+ * Get height of Sprite in pixels.
+ * @return height of Sprite in pixels
+ */
+ public int getHeight() {
+ return height;
+ }
+
+}
diff --git a/jeu-test/Lemmini/0.84/src/Lemmini.java b/jeu-test/Lemmini/0.84/src/Lemmini.java
new file mode 100644
index 0000000..7af1447
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Lemmini.java
@@ -0,0 +1,1688 @@
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Toolkit;
+import java.awt.Transparency;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.image.BufferedImage;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.imageio.ImageIO;
+import javax.jnlp.ServiceManager;
+import javax.jnlp.UnavailableServiceException;
+import javax.swing.ButtonGroup;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.UIManager;
+
+import GUI.GainDialog;
+import GUI.LevelCodeDialog;
+import GUI.PlayerDialog;
+import Game.Core;
+import Game.GameController;
+import Game.GroupBitfield;
+import Game.Icons;
+import Game.LemmCursor;
+import Game.LemmException;
+import Game.LemmFont;
+import Game.Lemming;
+import Game.Level;
+import Game.LevelCode;
+import Game.LevelPack;
+import Game.MiniMap;
+import Game.MiscGfx;
+import Game.Music;
+import Game.Player;
+import Game.ReplayLevelInfo;
+import Game.ResourceException;
+import Game.Stencil;
+import Game.TextScreen;
+import Game.UpdateListener;
+import GameUtil.Fader;
+import Tools.MicrosecondTimer;
+import Tools.ToolBox;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Lemmini - a game engine for Lemmings.<br>
+ * This is the main window including input handling. The game logic is located in
+ * {@link GameController}, some core components are in {@link Core}.<br>
+ * <br>
+ * Note: this was developed for JRE1.4 and only ported to JRE1.5 after it was finished.
+ * Also the design evolved during two years of development and thus isn't nearly as clean
+ * as it should be. During the porting to 1.5, I cleaned up some things here and there,
+ * but didn't want to redesign the whole thing from scratch.
+ *
+ * @author Volker Oth
+ */
+
+public class Lemmini extends JFrame implements KeyListener {
+ /** minimum sleep duration in milliseconds - values too small may cause system clock shift under WinXP etc. */
+ final static int MIN_SLEEP = 10;
+ /** threshold for sleep - don't sleep if time to wait is shorter than this as sleep might return too late */
+ final static int THR_SLEEP = 16;
+ /** height of menu and icon bar in pixels */
+ private final static int WIN_OFS = 120;
+ /** flag: started as Webstart application */
+ private static boolean isWebstartApp = true;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** self reference */
+ static JFrame thisFrame;
+
+ /** path for loading single level files */
+ private String lvlPath;
+ /** HashMap to store menu items for difficulty levels */
+ private HashMap<String,ArrayList<LvlMenuItem>> diffLevelMenus;
+ /** panel for the game graphics */
+ private GraphicsPane gp;
+
+ // Swing stuff
+
+ private JMenuBar jMenuBar = null;
+ private JMenu jMenuLevel = null;
+ private JMenuItem jMenuItemRestart = null;
+ private JMenuItem jMenuItemLevelCode = null;
+ private JMenuItem jMenuSelect = null;
+ private JMenu jMenuFile = null;
+ private JMenu jMenuPlayer = null;
+ private JMenu jMenuSelectPlayer = null;
+ private JMenu jMenuSound = null;
+ private JMenu jMenuSFX = null;
+ private JMenuItem jMenuItemVolume = null;
+ private JMenu jMenuOptions = null;
+ private JMenuItem jMenuItemCursor = null;
+ private JMenuItem jMenuItemExit = null;
+ private JMenuItem jMenuItemManagePlayer = null;
+ private JMenuItem jMenuItemLoad = null;
+ private JMenuItem jMenuItemReplay = null;
+ private JCheckBoxMenuItem jMenuItemMusic = null;
+ private JCheckBoxMenuItem jMenuItemSound = null;
+ private ButtonGroup playerGroup = null;
+
+
+ /**
+ * Constructor of the main frame.
+ */
+ Lemmini() {
+ try {
+ Core.init(this,isWebstartApp); // initialize Core object
+ GameController.init();
+ GameController.setLevelMenuUpdateListener(new LevelMenuUpdateListener());
+ } catch (ResourceException ex) {
+ Core.resourceError(ex.getMessage());
+ } catch (LemmException ex) {
+ JOptionPane.showMessageDialog( null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
+ System.exit(1);
+ } catch (Exception ex) {
+ ToolBox.showException(ex);
+ System.exit(1);
+ } catch (Error ex) {
+ ToolBox.showException(ex);
+ System.exit(1);
+ }
+ // read frame props
+ int width, height, posX, posY;
+ width = 800 ; //Core.programProps.get("frameWidth", 800);
+ height = Level.HEIGHT+WIN_OFS+60; //Core.programProps.get("frameHeight", Level.height+winOfs+60);
+ this.setSize(width,height);
+ this.setResizable(false); // at least for the moment: forbid resize
+ Point p = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint();
+ p.x -= this.getWidth()/2;
+ p.y -= this.getHeight()/2;
+ posX = Core.programProps.get("framePosX", p.x > 0 ? p.x : 0);
+ posY = Core.programProps.get("framePosY", p.y > 0 ? p.y : 0);
+ this.setLocation(posX, posY);
+ this.validate(); // force redraw
+ this.setTitle("Lemmini");
+
+ ClassLoader loader = Lemmini.class.getClassLoader();
+ Image img = Toolkit.getDefaultToolkit().getImage(loader.getResource("icon_32.png"));
+ setIconImage(img);
+
+ // set component pane
+ gp = new GraphicsPane();
+ gp.setDoubleBuffered(false);
+ this.setContentPane(gp);
+
+ this.validate(); // force redraw
+ this.setTitle("Lemmini");
+
+
+ // create Menu
+ jMenuItemExit = new JMenuItem("Exit");
+ jMenuItemExit.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ exit();
+ }
+ });
+
+ jMenuFile = new JMenu("File");
+ jMenuFile.add(jMenuItemExit);
+
+ // Player Menu
+ jMenuItemManagePlayer = new JMenuItem("Manage Players");
+ jMenuItemManagePlayer.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ Core.player.store(); // save player in case it is changed
+ PlayerDialog d = new PlayerDialog((JFrame)Core.getCmp(), true);
+ d.setVisible(true);
+ // blocked until dialog returns
+ List<String> players = d.getPlayers();
+ if (players != null) {
+ String player = Core.player.getName(); // old player
+ int playerIdx = d.getSelection();
+ if (playerIdx != -1)
+ player = players.get(playerIdx); // remember selected player
+ // check for players to delete
+ for (int i=0; i<Core.getPlayerNum(); i++) {
+ String p = Core.getPlayer(i);
+ if (!players.contains(p)) {
+ File f = new File(Core.resourcePath+"players/"+p+".ini");
+ f.delete();
+ if (p.equals(player))
+ player = "default";
+ }
+ }
+ // rebuild players list
+ Core.clearPlayers();
+ // add default player if missing
+ if (!players.contains("default"))
+ players.add("default");
+ // now copy all player and create properties
+ for (int i=0; i<players.size(); i++) {
+ Core.addPlayer(players.get(i));
+ }
+
+ // select new default player
+ Core.player = new Player(player);
+
+ // rebuild players menu
+ playerGroup = new ButtonGroup();
+ jMenuSelectPlayer.removeAll();
+ for (int idx=0; idx < Core.getPlayerNum(); idx++) {
+ JCheckBoxMenuItem item = addPlayerItem(Core.getPlayer(idx));
+ if ( Core.player.getName().equals(Core.getPlayer(idx)) )
+ item.setSelected(true);
+ }
+ updateLevelMenus();
+ }
+ }
+ });
+
+
+ jMenuSelectPlayer = new JMenu("Select Player");
+ playerGroup = new ButtonGroup();
+ for (int idx=0; idx < Core.getPlayerNum(); idx++) {
+ JCheckBoxMenuItem item = addPlayerItem(Core.getPlayer(idx));
+ if ( Core.player.getName().equals(Core.getPlayer(idx)) )
+ item.setSelected(true);
+ }
+ jMenuPlayer = new JMenu("Player");
+ jMenuPlayer.add(jMenuItemManagePlayer);
+ jMenuPlayer.add(jMenuSelectPlayer);
+
+
+ // load level packs and create Level menu
+ java.awt.event.ActionListener lvlListener = new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ LvlMenuItem item = (LvlMenuItem)e.getSource();
+ GameController.requestChangeLevel(item.levelPack, item.diffLevel, item.level, false);
+ }
+ };
+ diffLevelMenus = new HashMap<String,ArrayList<LvlMenuItem>>(); // store menus to access them later
+ jMenuSelect = new JMenu("Select Level");
+ for (int lp=1; lp<GameController.getLevelPackNum(); lp++) { // skip dummy level pack
+ LevelPack lPack = GameController.getLevelPack(lp);
+ JMenu jMenuPack = new JMenu(lPack.getName());
+ String difficulties[] = lPack.getDiffLevels();
+ for (int i=0; i<difficulties.length; i++) {
+ // get activated levels for this group
+ GroupBitfield bf = Core.player.getBitField(lPack.getName(), difficulties[i]);
+ String names[] = lPack.getLevels(i);
+ JMenu jMenuDiff = new JMenu(difficulties[i]);
+ // store menus to access them later
+ ArrayList<LvlMenuItem> menuItems = new ArrayList<LvlMenuItem>();
+ for (int n=0; n<names.length; n++) {
+ LvlMenuItem jMenuLvl = new LvlMenuItem(names[n],lp,i,n);
+ jMenuLvl.addActionListener(lvlListener);
+ if (Core.player.isAvailable(bf, n))
+ jMenuLvl.setEnabled(true);
+ else
+ jMenuLvl.setEnabled(false);
+ jMenuDiff.add(jMenuLvl);
+ menuItems.add(jMenuLvl);
+ }
+ jMenuPack.add(jMenuDiff);
+ // store menus to access them later
+ diffLevelMenus.put(LevelPack.getID(lPack.getName(), difficulties[i]), menuItems);
+ }
+ jMenuSelect.add(jMenuPack);
+ }
+
+ jMenuItemRestart = new JMenuItem();
+ jMenuItemRestart.setText("Restart Level");
+ jMenuItemRestart.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ if (!GameController.getLevel().isReady())
+ GameController.requestChangeLevel(GameController.getCurLevelPackIdx(), GameController.getCurDiffLevel(), GameController.getCurLevelNumber(), false);
+ else
+ GameController.requestRestartLevel(false);
+ }
+ });
+
+
+ jMenuItemLoad = new JMenuItem();
+ jMenuItemLoad.setText("Load Level");
+ jMenuItemLoad.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String p = ToolBox.getFileName(thisFrame,lvlPath,Core.LEVEL_EXTENSIONS,true);
+ if (p != null) {
+ try {
+ if (ToolBox.getExtension(p).equalsIgnoreCase("lvl")) {
+ Extract.ExtractLevel.convertLevel(p, Core.resourcePath+"/temp.ini");
+ p = Core.resourcePath+"/temp.ini";
+ }
+ if (ToolBox.getExtension(p).equalsIgnoreCase("ini")) {
+ String id = new String(ToolBox.getFileID(p,5));
+ if (id.equalsIgnoreCase("# LVL")) {
+ // this is a hack - maybe find a better way
+ GameController.getLevelPack(0).getInfo(0, 0).setFileName(p);
+ GameController.getLevelPack(0).getInfo(0, 0).setMusic(Music.getRandomTrack());
+ GameController.requestChangeLevel(0,0,0,false);
+ lvlPath = p;
+ return;
+ }
+ }
+ JOptionPane.showMessageDialog(Core.getCmp(), "Wrong format!", "Loading level failed", JOptionPane.INFORMATION_MESSAGE);
+ } catch (Exception ex) {
+ ToolBox.showException(ex);
+ }
+
+ }
+ }
+ });
+
+ jMenuItemReplay = new JMenuItem();
+ jMenuItemReplay.setText("Load Replay");
+ jMenuItemReplay.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String replayPath = ToolBox.getFileName(thisFrame,Core.resourcePath,Core.REPLAY_EXTENSIONS,true);
+ if (replayPath != null) {
+ try {
+ if (ToolBox.getExtension(replayPath).equalsIgnoreCase("rpl")) {
+ ReplayLevelInfo rli = GameController.loadReplay(replayPath);
+ if (rli != null) {
+ int lpn = -1;
+ for (int i=0; i<GameController.getLevelPackNum(); i++)
+ if (GameController.getLevelPack(i).getName().equals(rli.getLevelPack()))
+ lpn = i;
+ if (lpn > -1) {
+ GameController.requestChangeLevel(lpn,rli.getDiffLevel(), rli.getLvlNumber(), true);
+ return; // success
+ }
+ }
+ }
+ // else: no success
+ JOptionPane.showMessageDialog(Core.getCmp(), "Wrong format!", "Loading replay failed", JOptionPane.INFORMATION_MESSAGE);
+ } catch (Exception ex) {
+ ToolBox.showException(ex);
+ }
+ }
+ }
+ });
+
+
+ jMenuItemLevelCode = new JMenuItem("Enter Level Code");
+ jMenuItemLevelCode.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ LevelCodeDialog lcd = new LevelCodeDialog((JFrame)Core.getCmp(), true);
+ lcd.setVisible(true);
+ String levelCode = lcd.getCode();
+ int lvlPack = lcd.getLevelPack();
+ if (levelCode != null && levelCode.length() != 0 && lvlPack > 0) {
+
+ levelCode = levelCode.trim();
+ // cheat mode
+ if (levelCode.equals("0xdeadbeef")) {
+ JOptionPane.showMessageDialog(Core.getCmp(), "All levels and debug mode enabled", "Cheater!", JOptionPane.INFORMATION_MESSAGE);
+ Core.player.enableCheatMode();
+ updateLevelMenus();
+ return;
+ }
+
+ // real level code -> get absolute level
+ levelCode = levelCode.toUpperCase();
+ LevelPack lpack = GameController.getLevelPack(lvlPack);
+ int lvlAbs = LevelCode.getLevel(lpack.getCodeSeed(), levelCode, lpack.getCodeOffset());
+ if (lvlAbs != -1) {
+ // calculate level pack and relative levelnumber from absolute number
+ int l[] = GameController.relLevelNum(lvlPack, lvlAbs);
+ int diffLvl = l[0];
+ int lvlRel = l[1];
+ Core.player.setAvailable(lpack.getName(), lpack.getDiffLevels()[diffLvl], lvlRel);
+ GameController.requestChangeLevel(lvlPack, diffLvl, lvlRel, false);
+ updateLevelMenus();
+ return;
+ }
+ }
+ // not found
+ JOptionPane.showMessageDialog(Core.getCmp(), "Invalid Level Code", "Error", JOptionPane.WARNING_MESSAGE);
+ }
+ });
+
+ jMenuLevel = new JMenu("Level");
+ jMenuLevel.add(jMenuSelect);
+ jMenuLevel.add(jMenuItemRestart);
+ jMenuLevel.add(jMenuItemLoad);
+ jMenuLevel.add(jMenuItemReplay);
+ jMenuLevel.add(jMenuItemLevelCode);
+
+ jMenuItemMusic = new JCheckBoxMenuItem("Music", false);
+ jMenuItemMusic.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ boolean selected = jMenuItemMusic.isSelected();
+ //jMenuItemMusic.setSelected(selected);
+ if (selected)
+ GameController.setMusicOn(true);
+ else
+ GameController.setMusicOn(false);
+ Core.programProps.set("music", GameController.isMusicOn());
+ if (GameController.getLevel() != null) // to be improved: level is running (game state)
+ if (GameController.isMusicOn())
+ Music.play();
+ else
+ Music.stop();
+ }
+ });
+ jMenuItemMusic.setSelected(GameController.isMusicOn());
+
+ jMenuItemSound = new JCheckBoxMenuItem("Sound", false);
+ jMenuItemSound.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ boolean selected = jMenuItemSound.isSelected();
+ if (selected)
+ GameController.setSoundOn(true);
+ else
+ GameController.setSoundOn(false);
+ Core.programProps.set("sound", GameController.isSoundOn());
+ }
+ });
+ jMenuItemSound.setSelected(GameController.isSoundOn());
+
+ jMenuSFX = new JMenu("SFX Mixer");
+ String mixerNames[] = GameController.sound.getMixers();
+ ButtonGroup mixerGroup = new ButtonGroup();
+ String lastMixerName = Core.programProps.get("mixerName", "Java Sound Audio Engine");
+
+ // special handling of mixer from INI that doesn't exist (any more)
+ boolean found = false;
+ for (int i=0; i<mixerNames.length; i++) {
+ if (mixerNames[i].equals(lastMixerName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ lastMixerName = "Java Sound Audio Engine";
+
+ for (int i=0; i<mixerNames.length; i++) {
+ JCheckBoxMenuItem item = new JCheckBoxMenuItem();
+ item.setText(mixerNames[i]);
+ item.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ String mixerNames[] = GameController.sound.getMixers();
+ String mixerName = e.getActionCommand();
+ for (int i=0; i<mixerNames.length; i++) {
+ if (mixerNames[i].equals(mixerName)) {
+ GameController.sound.setMixer(i);
+ Core.programProps.set("mixerName", mixerName);
+ break;
+ }
+ }
+ }
+ });
+ if (mixerNames[i].equals(lastMixerName)) { // default setting
+ item.setState(true);
+ GameController.sound.setMixer(i);
+ }
+
+ jMenuSFX.add(item);
+ mixerGroup.add(item);
+ }
+
+ jMenuItemVolume = new JMenuItem("Volume Control");
+ jMenuItemVolume.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ GainDialog v = new GainDialog((JFrame)Core.getCmp(), true);
+ v.setVisible(true);
+ }
+ });
+
+
+ jMenuSound = new JMenu();
+ jMenuSound.setText("Sound");
+ jMenuSound.add(jMenuItemVolume);
+ jMenuSound.add(jMenuItemMusic);
+ jMenuSound.add(jMenuItemSound);
+ jMenuSound.add(jMenuSFX);
+
+ jMenuItemCursor = new JCheckBoxMenuItem("Advanced select", false);
+ jMenuItemCursor.addActionListener(new java.awt.event.ActionListener() {
+ /* (non-Javadoc)
+ * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
+ */
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ boolean selected = jMenuItemCursor.isSelected();
+ if (selected)
+ GameController.setAdvancedSelect(true);
+ else {
+ GameController.setAdvancedSelect(false);
+ gp.setCursor(LemmCursor.Type.NORMAL);
+ }
+ Core.programProps.set("advancedSelect", GameController.isAdvancedSelect());
+ }
+ });
+ jMenuItemCursor.setSelected(GameController.isAdvancedSelect());
+
+ jMenuOptions = new JMenu();
+ jMenuOptions.setText("Options");
+ jMenuOptions.add(jMenuItemCursor);
+
+ jMenuBar = new JMenuBar();
+ jMenuBar.add(jMenuFile);
+ jMenuBar.add(jMenuPlayer);
+ jMenuBar.add(jMenuLevel);
+ jMenuBar.add(jMenuSound);
+ jMenuBar.add(jMenuOptions);
+ this.setJMenuBar(jMenuBar);
+
+ this.addWindowListener(new java.awt.event.WindowAdapter() {
+ @Override
+ public void windowClosing(java.awt.event.WindowEvent e) {
+ exit();
+ }
+
+ @Override
+ public void windowClosed(java.awt.event.WindowEvent e) {
+ exit();
+ }
+ });
+ this.setVisible(true);
+ gp.init();
+ Thread t = new Thread(gp);
+
+ lvlPath = ".";
+
+ addKeyListener(this);
+
+ t.start();
+ }
+
+ /**
+ * Add a menu item for a player.
+ * @param name player name
+ * @return JCheckBoxMenuItem
+ */
+ private JCheckBoxMenuItem addPlayerItem(final String name) {
+ JCheckBoxMenuItem item = new JCheckBoxMenuItem(name);
+ item.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent e) {
+ Core.player.store(); // save player in case it is changed
+ JMenuItem item = (JMenuItem)e.getSource();
+ String player = item.getText();
+ Player p = new Player(player);
+ Core.player = p; // default player
+ item.setSelected(true);
+ updateLevelMenus();
+ }
+ });
+ playerGroup.add(item);
+ jMenuSelectPlayer.add(item);
+ return item;
+ }
+
+ /**
+ * Convert String to int.
+ * @param s String with decimal integer value
+ * @return integer value (0 if no valid number)
+ */
+ private static int getInt(final String s) {
+ try {
+ return Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ return 0;
+ }
+ }
+
+ /**
+ * The main function. Entry point of the program.
+ * @param args
+ */
+ public static void main(final String[] args) {
+ /*
+ * Set "Look and Feel" to system default
+ */
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) { /* don't care */}
+ /*
+ * Apple menu bar for MacOS
+ */
+ System.setProperty("com.apple.macos.useScreenMenuBar", "true");
+
+ /*
+ * Check JVM version
+ */
+ String jreStr = System.getProperty("java.version");
+ String vs[] = jreStr.split("[._]");
+ double vnum;
+ if (vs.length >= 3) {
+ vnum = (getInt(vs[0]))
+ + (getInt(vs[1])) * 0.1
+ + (getInt(vs[2])) * 0.01;
+ if (vnum < 1.5) {
+ JOptionPane.showMessageDialog(null,"Run this with JVM >= 1.5","Error",JOptionPane.ERROR_MESSAGE);
+ System.exit(1);
+ }
+ }
+
+ // check free memory
+ long free = Runtime.getRuntime().maxMemory();
+ if (free < 60*1024*1024) { // 64MB doesn't seem to work even if set with -Xmx64M
+ JOptionPane.showMessageDialog(null,"You need at least 64MB of heap","Error",JOptionPane.ERROR_MESSAGE);
+ System.exit(1);
+ }
+
+ // detect webstart
+ try {
+ ServiceManager.lookup("javax.jnlp.BasicService");
+ } catch (UnavailableServiceException ex) {
+ isWebstartApp = false;
+ };
+
+ // workaround to adjust time base to 1ms under XP
+ // see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6435126
+ new Thread() {
+ { this.setDaemon(true); this.start(); }
+ @Override
+ public void run() {
+ while(true) {
+ try {
+ Thread.sleep(Integer.MAX_VALUE);
+ }
+ catch(InterruptedException ex) {
+ }
+ }
+ }
+ };
+
+ Toolkit.getDefaultToolkit().setDynamicLayout(true);
+ thisFrame = new Lemmini();
+ }
+
+ /**
+ * Update the level menus according to the progress of the current player.
+ */
+ private void updateLevelMenus() {
+ // update level menus
+ for (int lp=1; lp<GameController.getLevelPackNum(); lp++) { // skip dummy level pack
+ LevelPack lPack = GameController.getLevelPack(lp);
+ String difficulties[] = lPack.getDiffLevels();
+ for (int i=0; i<difficulties.length; i++) {
+ // get activated levels for this group
+ GroupBitfield bf = Core.player.getBitField(lPack.getName(), difficulties[i]);
+ updateLevelMenu(lPack.getName(), difficulties[i], bf);
+ }
+ }
+ }
+
+ /**
+ * Update the level menus according to the given progress information.
+ * @param pack name of level pack
+ * @param diff name of difficulty level
+ * @param bf bitmap containing availability flags for each level
+ */
+ private void updateLevelMenu(final String pack, final String diff, final GroupBitfield bf) {
+ ArrayList<LvlMenuItem> menuItems = diffLevelMenus.get(LevelPack.getID(pack, diff));
+ for (int k=0; k<menuItems.size(); k++) {
+ // select level, e.g. "All fall down"
+ JMenuItem level = menuItems.get(k);
+ if (k == 0 || Core.player.isAvailable(bf, k))
+ level.setEnabled(true);
+ else
+ level.setEnabled(false);
+ }
+
+ }
+
+
+ /**
+ * Development function: patch current level x offset in the level configuration file.
+ * Works only in cheat mode.
+ * @param lvlPath path of level configuration files
+ */
+ private void patchLevel(final String lvlPath) {
+ try {
+ ArrayList<String> lines = new ArrayList<String>();
+ BufferedReader r = new BufferedReader(new FileReader(lvlPath));
+ String l;
+ while ( (l = r.readLine()) != null)
+ lines.add(l);
+ r.close();
+ FileWriter sw = new FileWriter(lvlPath);
+ for (int i=0; i<lines.size(); i++) {
+ String s = lines.get(i);
+ if (s.startsWith("xPos =")) {
+ sw.write("xPos = "+Integer.toString(GameController.getxPos())+"\n");
+ } else
+ sw.write(s+"\n");
+ }
+ sw.close();
+ } catch (FileNotFoundException ex) {}
+ catch (IOException ex) {}
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
+ */
+ public void keyPressed(final KeyEvent keyevent) {
+ int code = keyevent.getKeyCode();
+ if (GameController.getGameState() == GameController.State.LEVEL) {
+ switch (code) {
+ case KeyEvent.VK_1:
+ case KeyEvent.VK_F3:
+ GameController.handleIconButton(Icons.Type.CLIMB);
+ break;
+ case KeyEvent.VK_2:
+ case KeyEvent.VK_F4:
+ GameController.handleIconButton(Icons.Type.FLOAT);
+ break;
+ case KeyEvent.VK_3:
+ case KeyEvent.VK_F5:
+ GameController.handleIconButton(Icons.Type.BOMB);
+ break;
+ case KeyEvent.VK_4:
+ case KeyEvent.VK_F6:
+ GameController.handleIconButton(Icons.Type.BLOCK);
+ break;
+ case KeyEvent.VK_5:
+ case KeyEvent.VK_F7:
+ GameController.handleIconButton(Icons.Type.BUILD);
+ break;
+ case KeyEvent.VK_6:
+ case KeyEvent.VK_F8:
+ GameController.handleIconButton(Icons.Type.BASH);
+ break;
+ case KeyEvent.VK_7:
+ case KeyEvent.VK_F9:
+ GameController.handleIconButton(Icons.Type.MINE);
+ break;
+ case KeyEvent.VK_8:
+ case KeyEvent.VK_F10:
+ GameController.handleIconButton(Icons.Type.DIG);
+ break;
+ case KeyEvent.VK_D:
+ if (GameController.isCheat())
+ gp.setDebugDraw(!gp.getDebugDraw());
+ break;
+ case KeyEvent.VK_W:
+ if (GameController.isCheat()) {
+ GameController.setNumLeft(GameController.getNumLemmingsMax());
+ GameController.endLevel();
+ }
+ break;
+ case KeyEvent.VK_L: // print current level on the console
+ if (GameController.isCheat())
+ System.out.println(GameController.getLevelPack(GameController.getCurLevelPackIdx()).getInfo(GameController.getCurDiffLevel(), GameController.getCurLevelNumber()).getFileName());
+ break;
+ case KeyEvent.VK_S: // superlemming on/off
+ if (GameController.isCheat())
+ GameController.setSuperLemming(!GameController.isSuperLemming());
+ else {
+ try{
+ File file = new File(Core.resourcePath+"/level.png");
+ BufferedImage tmp = GameController.getLevel().createMiniMap(null,GameController.getBgImage(), 1, 1, false);
+ ImageIO.write(tmp, "png", file);
+ } catch (Exception ex) {}
+ }
+ break;
+ case KeyEvent.VK_C:
+ if (Core.player.isCheat()) {
+ GameController.setCheat(!GameController.isCheat());
+ if (GameController.isCheat())
+ GameController.setWasCheated(true);
+ }
+ else
+ GameController.setCheat(false);
+ break;
+ case KeyEvent.VK_F11:
+ case KeyEvent.VK_P:
+ GameController.setPaused(!GameController.isPaused());
+ GameController.pressIcon(Icons.Type.PAUSE);
+ break;
+ case KeyEvent.VK_F:
+ case KeyEvent.VK_ENTER:
+ GameController.setFastForward(!GameController.isFastForward());
+ GameController.pressIcon(Icons.Type.FFWD);
+ break;
+ case KeyEvent.VK_X:
+ if (GameController.isCheat())
+ patchLevel(GameController.getLevelPack(GameController.getCurLevelPackIdx()).getInfo(GameController.getCurDiffLevel(), GameController.getCurLevelNumber()).getFileName());
+ break;
+ case KeyEvent.VK_RIGHT /*39*/: {
+ if (GameController.isAdvancedSelect())
+ gp.setCursor(LemmCursor.Type.RIGHT);
+ else {
+ int xOfsTemp = GameController.getxPos() + ((gp.isShiftPressed()) ? GraphicsPane.X_STEP_FAST : GraphicsPane.X_STEP);
+ if (xOfsTemp < Level.WIDTH-this.getWidth())
+ GameController.setxPos(xOfsTemp);
+ else
+ GameController.setxPos(Level.WIDTH-this.getWidth());
+ }
+ break; }
+ case KeyEvent.VK_LEFT /*37*/: {
+ if (GameController.isAdvancedSelect())
+ gp.setCursor(LemmCursor.Type.LEFT);
+ else {
+ int xOfsTemp = GameController.getxPos() - ((gp.isShiftPressed()) ? GraphicsPane.X_STEP_FAST : GraphicsPane.X_STEP);
+ if (xOfsTemp > 0)
+ GameController.setxPos(xOfsTemp);
+ else
+ GameController.setxPos(0);
+ }
+ break; }
+ case KeyEvent.VK_UP: {
+ gp.setCursor(LemmCursor.Type.WALKER);
+ break;}
+ case KeyEvent.VK_SHIFT:
+ gp.setShiftPressed(true);
+ break;
+ case KeyEvent.VK_SPACE:
+ if (GameController.isCheat()) {
+ Lemming l = new Lemming(gp.getCursorX(), gp.getCursorY());
+ synchronized (GameController.getLemmings()) {
+ GameController.getLemmings().add(l);
+ }
+ }
+ break;
+ case KeyEvent.VK_PLUS:
+ case KeyEvent.VK_ADD:
+ case KeyEvent.VK_F2:
+ GameController.pressPlus(GameController.KEYREPEAT_KEY);
+ break;
+ case KeyEvent.VK_MINUS:
+ case KeyEvent.VK_SUBTRACT:
+ case KeyEvent.VK_F1:
+ GameController.pressMinus(GameController.KEYREPEAT_KEY);
+ break;
+ case KeyEvent.VK_F12:
+ GameController.handleIconButton(Icons.Type.NUKE);
+ break;
+ }
+ keyevent.consume();
+ }
+ //System.out.println(keyevent.getKeyCode());
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
+ */
+ public void keyReleased(final KeyEvent keyevent) {
+ int code = keyevent.getKeyCode();
+ if (GameController.getGameState() == GameController.State.LEVEL) {
+ switch (code) {
+ case KeyEvent.VK_SHIFT:
+ gp.setShiftPressed(false);
+ break;
+ case KeyEvent.VK_PLUS:
+ case KeyEvent.VK_ADD:
+ case KeyEvent.VK_F2:
+ GameController.releasePlus(GameController.KEYREPEAT_KEY);
+ break;
+ case KeyEvent.VK_MINUS:
+ case KeyEvent.VK_SUBTRACT:
+ case KeyEvent.VK_F1:
+ GameController.releaseMinus(GameController.KEYREPEAT_KEY);
+ break;
+ case KeyEvent.VK_F12:
+ GameController.releaseIcon(Icons.Type.NUKE);
+ break;
+ case KeyEvent.VK_LEFT:
+ if (LemmCursor.getType() == LemmCursor.Type.LEFT)
+ gp.setCursor(LemmCursor.Type.NORMAL);
+ break;
+ case KeyEvent.VK_RIGHT:
+ if (LemmCursor.getType() == LemmCursor.Type.RIGHT)
+ gp.setCursor(LemmCursor.Type.NORMAL);
+ break;
+ case KeyEvent.VK_UP:
+ if (LemmCursor.getType() == LemmCursor.Type.WALKER)
+ gp.setCursor(LemmCursor.Type.NORMAL);
+ break;
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
+ */
+ public void keyTyped(final KeyEvent keyevent) {
+ }
+
+ /**
+ * Common exit method to use in exit events.
+ */
+ private void exit() {
+ // store width and height
+ Dimension d = this.getSize();
+ Core.programProps.set("frameWidth", d.width);
+ Core.programProps.set("frameHeight", d.height);
+ // store frame pos
+ Point p = this.getLocation();
+ Core.programProps.set("framePosX", p.x);
+ Core.programProps.set("framePosY", p.y);
+ //
+ Core.saveProgramProps();
+ System.exit(0);
+ }
+
+
+ /**
+ * Listener to inform the GUI of the player's progress.
+ * @author Volker Oth
+ */
+ class LevelMenuUpdateListener implements UpdateListener {
+ /* (non-Javadoc)
+ * @see Game.UpdateListener#update()
+ */
+ public void update() {
+ if (GameController.getCurLevelPackIdx()!=0) { // 0 is the dummy pack
+ LevelPack lvlPack = GameController.getLevelPack(GameController.getCurLevelPackIdx());
+ String pack = lvlPack.getName();
+ String diff = lvlPack.getDiffLevels()[GameController.getCurDiffLevel()];
+ // get next level
+ int num = GameController.getCurLevelNumber() + 1;
+ if ( num >= lvlPack.getLevels(GameController.getCurDiffLevel()).length )
+ num = GameController.getCurLevelNumber();
+ // set next level as available
+ GroupBitfield bf = Core.player.setAvailable(pack, diff, num);
+ // update the menu
+ updateLevelMenu(pack, diff, bf);
+ }
+ }
+ }
+
+
+ /**
+ * Specialized menu item for level selection menus.
+ * @author Volker Oth
+ */
+ class LvlMenuItem extends JMenuItem {
+ private final static long serialVersionUID = 0x01;
+
+ /** index of level pack */
+ int levelPack;
+ /** index of difficulty level */
+ int diffLevel;
+ /** level number */
+ int level;
+
+ /**
+ * Constructor
+ * @param text level name
+ * @param pack index level pack
+ * @param diff index of difficulty level
+ * @param lvl level number
+ */
+ LvlMenuItem(final String text, final int pack, final int diff, final int lvl) {
+ super(text);
+ levelPack = pack;
+ diffLevel = diff;
+ level = lvl;
+ }
+ }
+}
+
+
+
+
+/**
+ * A graphics panel in which the actual game contents is displayed.
+ * @author Volker Oth
+ */
+class GraphicsPane extends JPanel implements Runnable, MouseListener, MouseMotionListener {
+ /** step size in pixels for horizontal scrolling */
+ final static int X_STEP = 4;
+ /** step size in pixels for fast horizontal scrolling */
+ final static int X_STEP_FAST = 8;
+ /** size of auto scrolling range in pixels (from the left and right border) */
+ final static int AUTOSCROLL_RANGE = 20;
+ /** y coordinate of score display in pixels */
+ final static int scoreY = Level.HEIGHT;
+ /** y coordinate of counter displays in pixels */
+ final static int counterY = scoreY+40;
+ /** y coordinate of icons in pixels */
+ final static int iconsY = counterY+14;
+ /** x coordinate of minimap in pixels */
+ final static int smallX = 640-16/*-32*/-200;
+ /** y coordinate of minimap in pixels */
+ final static int smallY = iconsY;
+
+ private final static long serialVersionUID = 0x01;
+
+ /** start position of mouse drag (for mouse scrolling) */
+ private int mouseDragStartX;
+ /** x position of cursor in level */
+ private int xMouse;
+ /** x position of cursor on screen */
+ private int xMouseScreen;
+ /** y position of cursor in level */
+ private int yMouse;
+ /** y position of cursor on screen */
+ private int yMouseScreen;
+ /** mouse drag length in x direction (pixels) */
+ private int mouseDx;
+ /** mouse drag length in y direction (pixels) */
+ private int mouseDy;
+ /** flag: Shift key is pressed */
+ private boolean shiftPressed;
+ /** flag: left mouse button is currently pressed */
+ private boolean leftMousePressed;
+ /** flag: debug draw is active */
+ private boolean draw;
+ /** image for information string display */
+ private BufferedImage outStrImg;
+ /** graphics object for information string display */
+ private Graphics2D outStrGfx;
+ /** array of offscreen images (one is active, one is passive) */
+ private BufferedImage offImage[];
+ /** graphics objects for the two offscreen images */
+ private Graphics2D offGraphics[];
+ /** index of the active buffer in the image buffer */
+ private int activeBuffer;
+ /** monitoring object used for synchronized painting */
+ private Object paintSemaphore;
+
+ /**
+ * Constructor.
+ */
+ public GraphicsPane() {
+ super();
+
+ paintSemaphore = new Object();
+ this.requestFocus();
+ this.setCursor(LemmCursor.getCursor());
+ this.addMouseListener(this);
+ this.addMouseMotionListener(this);
+ }
+
+ /**
+ * Set cursor type.
+ * @param c Cursor
+ */
+ public void setCursor(final LemmCursor.Type c) {
+ LemmCursor.setType(c);
+ this.setCursor(LemmCursor.getCursor());
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.JComponent#paint(java.awt.Graphics)
+ */
+ @Override
+ public void paint(final Graphics g) {
+ //super.paint(iconGfx);
+ synchronized (paintSemaphore) {
+ if (offImage != null)
+ g.drawImage(offImage[activeBuffer],0,0,null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see javax.swing.JComponent#update(java.awt.Graphics)
+ */
+ @Override
+ public void update(final Graphics g) {
+ //super.update(iconGfx);
+ synchronized (paintSemaphore) {
+ if (offImage != null)
+ g.drawImage(offImage[activeBuffer],0,0,null);
+ }
+ }
+
+ /**
+ * Initialization.
+ */
+ public void init() {
+ offImage = new BufferedImage[2];
+ offGraphics = new Graphics2D[2];
+ offImage[0] = ToolBox.createImage(this.getWidth(), this.getHeight(), Transparency.OPAQUE);
+ offImage[1] = ToolBox.createImage(this.getWidth(), this.getHeight(), Transparency.OPAQUE);
+ offGraphics[0] = offImage[0].createGraphics();
+ offGraphics[1] = offImage[1].createGraphics();
+
+ outStrImg = ToolBox.createImage(this.getWidth(), LemmFont.getHeight(), Transparency.BITMASK);
+ outStrGfx = outStrImg.createGraphics();
+ outStrGfx.setBackground(new Color(0,0,0));
+
+ TextScreen.init(this.getWidth(), this.getHeight());
+ GameController.setGameState(GameController.State.INTRO);
+ GameController.setTransition(GameController.TransitionState.NONE);
+ Fader.setBounds(this.getWidth(), this.getHeight());
+ Fader.setState(Fader.State.IN);
+
+ shiftPressed = false;
+ }
+
+ /**
+ * redraw the offscreen image, then flip buffers and force repaint.
+ */
+ private void redraw() {
+ int drawBuffer;
+ Graphics2D offGfx;
+
+ synchronized (paintSemaphore) {
+ if (offImage == null)
+ init();
+ drawBuffer = (activeBuffer == 0) ? 1:0;
+ offGfx = offGraphics[drawBuffer];
+ }
+
+ BufferedImage bgImage = GameController.getBgImage();
+ switch (GameController.getGameState()) {
+ case INTRO:
+ TextScreen.setMode(TextScreen.Mode.INTRO);
+ TextScreen.update();
+ offGfx.drawImage(TextScreen.getScreen(), 0,0,null);
+ //offGfx.drawImage(LemmCursor.getImage(LemmCursor.TYPE_NORMAL), LemmCursor.x, LemmCursor.y, null);
+ break;
+ case BRIEFING:
+ TextScreen.setMode(TextScreen.Mode.BRIEFING);
+ TextScreen.update();
+ offGfx.drawImage(TextScreen.getScreen(), 0,0,null);
+ //offGfx.drawImage(LemmCursor.getImage(LemmCursor.TYPE_NORMAL), LemmCursor.x, LemmCursor.y, null);
+ break;
+ case DEBRIEFING:
+ TextScreen.setMode(TextScreen.Mode.DEBRIEFING);
+ TextScreen.update();
+ offGfx.drawImage(TextScreen.getScreen(), 0,0,null);
+ TextScreen.getDialog().handleMouseMove(xMouseScreen, yMouseScreen);
+ //offGfx.drawImage(LemmCursor.getImage(LemmCursor.TYPE_NORMAL), LemmCursor.x, LemmCursor.y, null);
+ break;
+ case LEVEL:
+ case LEVEL_END:
+ if (bgImage != null) {
+ GameController.update();
+ // mouse movement
+ if (yMouseScreen > 40 && yMouseScreen <scoreY) { // avoid scrolling if menu is selected
+ int xOfsTemp;
+ if (xMouseScreen > this.getWidth() - AUTOSCROLL_RANGE) {
+ xOfsTemp = GameController.getxPos() + ((shiftPressed) ? X_STEP_FAST : X_STEP);
+ if (xOfsTemp < Level.WIDTH-this.getWidth())
+ GameController.setxPos(xOfsTemp);
+ else
+ GameController.setxPos(Level.WIDTH-this.getWidth());
+ } else if (xMouseScreen < AUTOSCROLL_RANGE) {
+ xOfsTemp = GameController.getxPos() - ((shiftPressed) ? X_STEP_FAST : X_STEP);
+ if (xOfsTemp > 0)
+ GameController.setxPos(xOfsTemp);
+ else
+ GameController.setxPos(0);
+ }
+ }
+ // store local copy of xOfs to avoid sync problems with AWT threads
+ // (scrolling by dragging changes xOfs as well)
+ int xOfsTemp = GameController.getxPos();
+
+ //timeBaseRedraw +=GameController.timePerFrame;
+ int w = this.getWidth();
+ int h = Level.HEIGHT;
+ if (h>this.getHeight())
+ h = this.getHeight();
+
+ Level level = GameController.getLevel();
+ if (level != null) {
+
+ // clear screen
+ offGfx.setClip(0,0,w,h);
+ offGfx.setBackground(level.getBgColor());
+ offGfx.clearRect(0, 0, w, h);
+
+ // draw "behind" objects
+ GameController.getLevel().drawBehindObjects(offGfx, w, xOfsTemp);
+
+ // draw background
+ offGfx.drawImage(bgImage, 0, 0, w, h, xOfsTemp, 0, xOfsTemp+w, h, this);
+
+ // draw "in front" objects
+ GameController.getLevel().drawInFrontObjects(offGfx, w, xOfsTemp);
+ }
+ // clear parts of the screen for menu etc.
+ offGfx.setClip(0,Level.HEIGHT,w,this.getHeight());
+ offGfx.setBackground(Color.BLACK);
+ offGfx.clearRect(0,scoreY,w,this.getHeight());
+ // draw counter, icons, small level pic
+ // draw menu
+ //Icons icons = GameController.getIcons();
+ GameController.drawIcons(offGfx, 0, iconsY);
+ offGfx.drawImage(MiscGfx.getImage(MiscGfx.Index.BORDER), smallX-4, smallY-4, null);
+ MiniMap.draw(offGfx, smallX, smallY, xOfsTemp);
+ // draw counters
+ GameController.drawCounters(offGfx,counterY);
+
+ // draw lemmings
+ offGfx.setClip(0,0,w,h);
+ GameController.getLemmsUnderCursor().clear();
+ List<Lemming> lemmings = GameController.getLemmings();
+ synchronized (GameController.getLemmings()) {
+ Iterator<Lemming> it = lemmings.iterator();
+ while (it.hasNext()) {
+ Lemming l = it.next();
+ int lx = l.screenX();
+ int ly = l.screenY();
+ int mx = l.midX()-16;
+ if (lx+l.width() > xOfsTemp && lx < xOfsTemp+w) {
+ offGfx.drawImage(l.getImage(),lx-xOfsTemp,ly,null);
+ if (LemmCursor.doesCollide(l, xOfsTemp)) {
+ GameController.getLemmsUnderCursor().add(l);
+ }
+ BufferedImage cd = l.getCountdown();
+ if (cd!=null)
+ offGfx.drawImage(cd,mx-xOfsTemp,ly-cd.getHeight(),null);
+
+ BufferedImage sel = l.getSelectImg();
+ if (sel!=null)
+ offGfx.drawImage(sel,mx-xOfsTemp,ly-sel.getHeight(),null);
+
+ }
+ }
+ // draw pixels in mini map
+ offGfx.setClip(0,0,w,this.getHeight());
+ it = lemmings.iterator();
+ while (it.hasNext()) {
+ Lemming l = it.next();
+ int lx = l.screenX();
+ int ly = l.screenY();
+ // draw pixel in mini map
+ MiniMap.drawLemming(offGfx, lx, ly);
+ }
+ }
+ Lemming lemmUnderCursor = GameController.lemmUnderCursor(LemmCursor.getType());
+ offGfx.setClip(0,0,w,h);
+ // draw explosions
+ GameController.drawExplosions(offGfx,offImage[0].getWidth(), Level.HEIGHT, xOfsTemp);
+ offGfx.setClip(0,0,w,this.getHeight());
+
+ // draw info string
+ outStrGfx.clearRect(0, 0, outStrImg.getWidth(), outStrImg.getHeight());
+ if (GameController.isCheat()) {
+ Stencil stencil = GameController.getStencil();
+ if (stencil != null) {
+ int stencilVal = stencil.get(xMouse+yMouse*Level.WIDTH);
+ String test = "x: "+xMouse+", y: "+yMouse+", mask: "+(stencilVal&0xffff)+" "+Stencil.getObjectID(stencilVal);
+ LemmFont.strImage(outStrGfx, test);
+ offGfx.drawImage(outStrImg,4,Level.HEIGHT+8,null);
+ }
+ } else {
+ StringBuffer sb = new StringBuffer();
+ sb.append("OUT ");
+ String s = Integer.toString(GameController.getLemmings().size());
+ sb.append(s);
+ if (s.length()==1)
+ sb.append(" ");
+ sb.append(" IN ");
+ s = Integer.toString(GameController.getNumLeft()*100/GameController.getNumLemmingsMax());
+ if (s.length()==1)
+ sb.append("0");
+ sb.append(s);
+ sb.append("% TIME ").append(GameController.getTimeString());
+ //BufferedImage iout = LemmFont.strImage(out);
+ String n=null;
+ if (lemmUnderCursor != null) {
+ n = lemmUnderCursor.getName();
+ // display also the total number of lemmings under the cursor
+ int num = GameController.getLemmsUnderCursor().size();
+ if (num > 1)
+ n = n + " " + Integer.toString(num);
+ }
+ if (n!=null) {
+ int ln = n.length();
+ if (ln>14)
+ ln = 14;
+ sb.insert(0," ".substring(0, 14-ln));
+ sb.insert(0,n);
+ } else
+ sb.insert(0," ");
+ LemmFont.strImage(outStrGfx, sb.toString());
+ offGfx.drawImage(outStrImg,4,Level.HEIGHT+8,null);
+ }
+ // replay icon
+ BufferedImage replayImage = GameController.getReplayImage();
+ if (replayImage != null)
+ offGfx.drawImage(replayImage,this.getWidth()-2*replayImage.getWidth(),replayImage.getHeight(),null);
+ // draw cursor
+ if (lemmUnderCursor != null) {
+ int lx = lemmUnderCursor.midX()-xOfsTemp;
+ int ly = lemmUnderCursor.midY();
+ BufferedImage cursorImg = LemmCursor.getBoxImage();
+ lx -= cursorImg.getWidth()/2;
+ ly -= cursorImg.getHeight()/2;
+ offGfx.drawImage(cursorImg,lx,ly,null);
+ }
+ //offGfx.drawImage(LemmCursor.getImage(0), LemmCursor.x, LemmCursor.y, null);
+ }
+ }
+
+ // fader
+ GameController.fade(offGfx);
+ // and all onto screen
+ activeBuffer = drawBuffer;
+
+ repaint();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Runnable#run()
+ */
+ public void run() {
+ Thread.currentThread().setPriority(Thread.NORM_PRIORITY+1);
+ MicrosecondTimer timerRepaint = new MicrosecondTimer();
+ try {
+ while (true) {
+ GameController.State gameState = GameController.getGameState();
+ // Try to keep the Amiga timing. Note that no frames are skipped
+ // If one frame is too late, the next one will be a little earlier
+ // to compensate. No frames are skipped though!
+ // On a slow CPU this might slow down gameplay...
+ if (timerRepaint.timePassedAdd(GameController.MICROSEC_PER_FRAME)) {
+ // time passed -> redraw necessary
+ redraw();
+ // special handling for fast forward or super lemming mode only during real gameplay
+ if (gameState == GameController.State.LEVEL) {
+ // in fast forward or super lemming modes, update the game mechanics
+ // multiple times per (drawn) frame
+ if (GameController.isFastForward())
+ for (int f=0; f<GameController.FAST_FWD_MULTI-1;f++)
+ GameController.update();
+ else if (GameController.isSuperLemming())
+ for (int f=0; f<GameController.SUPERLEMM_MULTI-1;f++)
+ GameController.update();
+ }
+ } else {
+ try {
+ // determine time until next frame
+ long diff = GameController.MICROSEC_PER_FRAME - timerRepaint.delta();
+ if (diff > GameController.MICROSEC_RESYNC) {
+ timerRepaint.update(); // resync to time base
+ System.out.println("Resynced, diff was "+(diff/1000)+" millis");
+ } else if (diff > Lemmini.THR_SLEEP*1000)
+ Thread.sleep(Lemmini.MIN_SLEEP);
+ } catch (InterruptedException ex) {}
+ }
+ }
+ } catch (Exception ex) {
+ ToolBox.showException(ex);
+ System.exit(1);
+ } catch (Error ex) {
+ ToolBox.showException(ex);
+ System.exit(1);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
+ */
+ public void mouseReleased(final MouseEvent mouseevent) {
+ int x = mouseevent.getX();
+ int y = mouseevent.getY();
+ mouseDx = 0;
+ mouseDy = 0;
+ if (mouseevent.getButton() == MouseEvent.BUTTON1)
+ leftMousePressed = false;
+
+ switch (GameController.getGameState()) {
+ case LEVEL:
+ if (y > iconsY && y < iconsY+Icons.HEIGHT) {
+ Icons.Type type = GameController.getIconType(x);
+ if (type != Icons.Type.INVALID)
+ GameController.releaseIcon(type);
+ }
+ // always release icons which don't stay pressed
+ // this is to avoid the icons get stuck when they're pressed,
+ // the the mouse is dragged out and released outside
+ GameController.releasePlus(GameController.KEYREPEAT_ICON);
+ GameController.releaseMinus(GameController.KEYREPEAT_ICON);
+ GameController.releaseIcon(Icons.Type.MINUS);
+ GameController.releaseIcon(Icons.Type.PLUS);
+ GameController.releaseIcon(Icons.Type.NUKE);
+ mouseevent.consume();
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
+ */
+ public void mouseClicked(final MouseEvent mouseevent) {
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
+ */
+ public void mousePressed(final MouseEvent mouseevent) {
+ int x = mouseevent.getX();
+ int y = mouseevent.getY();
+ mouseDx = 0;
+ mouseDy = 0;
+ if (mouseevent.getButton() == MouseEvent.BUTTON1)
+ leftMousePressed = true;
+
+ if (Fader.getState() != Fader.State.OFF)
+ return;
+
+ switch (GameController.getGameState()) {
+ case BRIEFING:
+ MiniMap.init(smallX, smallY, 16, 8, true);
+ GameController.setTransition(GameController.TransitionState.TO_LEVEL);
+ Fader.setState(Fader.State.OUT);
+ mouseevent.consume();
+ break;
+ case DEBRIEFING:
+ int button = TextScreen.getDialog().handleLeftClick(x,y);
+ switch (button) {
+ case TextScreen.BUTTON_CONTINUE:
+ GameController.nextLevel(); // continue to next level
+ GameController.requestChangeLevel(GameController.getCurLevelPackIdx(), GameController.getCurDiffLevel(),
+ GameController.getCurLevelNumber(), false);
+ break;
+ case TextScreen.BUTTON_RESTART:
+ GameController.requestRestartLevel(false);
+ break;
+ case TextScreen.BUTTON_MENU:
+ GameController.setTransition(GameController.TransitionState.TO_INTRO);
+ Fader.setState(Fader.State.OUT);
+ ((JFrame)Core.getCmp()).setTitle("Lemmini");
+ break;
+ case TextScreen.BUTTON_REPLAY:
+ GameController.requestRestartLevel(true);
+ break;
+ case TextScreen.BUTTON_SAVEREPLAY:
+ String replayPath = ToolBox.getFileName(Lemmini.thisFrame,Core.resourcePath,Core.REPLAY_EXTENSIONS,false);
+ if (replayPath != null) {
+ try {
+ String ext = ToolBox.getExtension(replayPath);
+ if (ext == null)
+ replayPath += ".rpl";
+ if (GameController.saveReplay(replayPath))
+ return;
+ // else: no success
+ JOptionPane.showMessageDialog(Core.getCmp(), "Error!", "Saving replay failed", JOptionPane.INFORMATION_MESSAGE);
+ } catch (Exception ex) {
+ ToolBox.showException(ex);
+ }
+ }
+ break;
+ }
+ mouseevent.consume();
+ break;
+ case LEVEL:
+ // debug drawing
+ debugDraw(x,y,leftMousePressed);
+ if (leftMousePressed) {
+ if (y > iconsY && y < iconsY+Icons.HEIGHT) {
+ Icons.Type type = GameController.getIconType(x);
+ if (type != Icons.Type.INVALID) {
+ GameController.handleIconButton(type);
+ }
+ } else {
+ Lemming l = GameController.lemmUnderCursor(LemmCursor.getType());
+ if (l != null)
+ GameController.requestSkill(l);
+ }
+ // check minimap mouse move
+ int ofs = MiniMap.move(x,y,this.getWidth());
+ if (ofs != -1)
+ GameController.setxPos(ofs);
+ mouseevent.consume();
+ }
+ }
+ }
+
+ /**
+ * Debug routine to draw terrain pixels in stencil and background image.
+ * @param x x position in pixels
+ * @param y y position in pixels
+ * @param doDraw true: draw, false: erase
+ */
+ private void debugDraw(final int x, final int y, final boolean doDraw) {
+ if (draw && GameController.isCheat()) {
+ int rgbVal = (doDraw) ? 0xffffffff : 0x0;
+ int maskVal = (doDraw) ? Stencil.MSK_BRICK : Stencil.MSK_EMPTY;
+ int xOfs = GameController.getxPos();
+ if (x+xOfs > 0 && x+xOfs<Level.WIDTH-1 && y >0 && y < Level.HEIGHT-1) {
+ GameController.getBgImage().setRGB(x+xOfs, y, rgbVal);
+ GameController.getStencil().set(x+xOfs, y, maskVal);
+ GameController.getBgImage().setRGB(x+xOfs+1, y, rgbVal);
+ GameController.getStencil().set(x+xOfs+1, y, maskVal);
+ GameController.getBgImage().setRGB(x+xOfs, y+1, rgbVal);
+ GameController.getStencil().set(x+xOfs, y+1, maskVal);
+ GameController.getBgImage().setRGB(x+xOfs+1, y+1, rgbVal);
+ GameController.getStencil().set(x+xOfs+1, y+1, maskVal);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
+ */
+ public void mouseEntered(final MouseEvent mouseevent) {
+ mouseDx = 0;
+ mouseDy = 0;
+ int x = mouseevent.getX()/*-LemmCursor.width/2*/;
+ int y = mouseevent.getY()/*-LemmCursor.height/2*/;
+ LemmCursor.setX(x/*-LemmCursor.width/2*/);
+ LemmCursor.setY(y/*-LemmCursor.height/2*/);
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
+ */
+ public void mouseExited(final MouseEvent mouseevent) {
+ int x = xMouseScreen + mouseDx;
+ switch (GameController.getGameState()) {
+ case BRIEFING:
+ case DEBRIEFING:
+ case LEVEL:
+ if (x>=this.getWidth())
+ x = this.getWidth()-1;
+ if (x<0)
+ x = 0;
+ xMouseScreen = x;
+ x += GameController.getxPos();
+ if (x>=Level.WIDTH)
+ x = Level.WIDTH-1;
+ xMouse = x;
+ LemmCursor.setX(xMouseScreen/*-LemmCursor.width/2*/);
+
+ int y = yMouseScreen + mouseDy;
+ if (y >= this.getHeight())
+ y = this.getHeight()-1;
+ if (y<0)
+ y = 0;
+ yMouseScreen = y;
+
+ y = yMouse + mouseDy;
+ if (y >= Level.HEIGHT)
+ y = Level.HEIGHT-1;
+ if (y<0)
+ y = 0;
+ yMouse = y;
+ LemmCursor.setY(yMouseScreen/*-LemmCursor.height/2*/);
+ mouseevent.consume();
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
+ */
+ public void mouseDragged(final MouseEvent mouseevent) {
+ mouseDx = 0;
+ mouseDy = 0;
+ // check minimap mouse move
+ switch (GameController.getGameState()) {
+ case LEVEL:
+ int x = mouseevent.getX();
+ int y = mouseevent.getY();
+ if (leftMousePressed) {
+ int ofs = MiniMap.move(x,y,this.getWidth());
+ if (ofs != -1)
+ GameController.setxPos(ofs);
+ } else {
+ int xOfsTemp = GameController.getxPos() + (x-mouseDragStartX);
+ if (xOfsTemp < 0)
+ xOfsTemp = 0;
+ else if (xOfsTemp >= Level.WIDTH-this.getWidth())
+ GameController.setxPos(Level.WIDTH-this.getWidth());
+ else GameController.setxPos(xOfsTemp);
+ }
+ // debug drawing
+ debugDraw(x,y,leftMousePressed);
+ mouseMoved(mouseevent);
+ mouseevent.consume();
+ break;
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
+ */
+ public void mouseMoved(final MouseEvent mouseevent) {
+ //long t = System.currentTimeMillis();
+ int x,y;
+ int oldX = xMouse;
+ int oldY = yMouse;
+
+ x = (mouseevent.getX() + GameController.getxPos());
+ y = mouseevent.getY();
+ if (x>=Level.WIDTH)
+ x = Level.WIDTH-1;
+ if (y >= Level.HEIGHT)
+ y = Level.HEIGHT-1;
+ xMouse = x;
+ yMouse = y;
+ // LemmCursor
+ xMouseScreen = mouseevent.getX();
+ if (xMouseScreen>=this.getWidth())
+ xMouseScreen = this.getWidth();
+ else if (xMouseScreen <0)
+ xMouseScreen = 0;
+ yMouseScreen = mouseevent.getY();
+ if (yMouseScreen>=this.getHeight())
+ yMouseScreen = this.getHeight();
+ else if (yMouseScreen <0)
+ yMouseScreen = 0;
+ LemmCursor.setX(xMouseScreen/*-LemmCursor.width/2*/);
+ LemmCursor.setY(yMouseScreen/*-LemmCursor.height/2*/);
+
+ switch (GameController.getGameState()) {
+ case INTRO:
+ case BRIEFING:
+ case DEBRIEFING:
+ TextScreen.getDialog().handleMouseMove(xMouseScreen, yMouseScreen);
+ //$FALL-THROUGH$
+ case LEVEL:
+ mouseDx = (xMouse - oldX);
+ mouseDy = (yMouse - oldY);
+ mouseDragStartX = mouseevent.getX();
+ mouseevent.consume();
+ break;
+ }
+ }
+
+ /**
+ * Get cursor x position in pixels.
+ * @return cursor x position in pixels
+ */
+ int getCursorX() {
+ return xMouse;
+ }
+
+ /**
+ * Get cursor y position in pixels.
+ * @return cursor y position in pixels
+ */
+ int getCursorY() {
+ return yMouse;
+ }
+
+ /**
+ * Get flag: Shift key is pressed?
+ * @return true if Shift key is pressed, false otherwise
+ */
+ boolean isShiftPressed() {
+ return shiftPressed;
+ }
+
+ /**
+ * Set flag: Shift key is pressed.
+ * @param p true: Shift key is pressed,false otherwise
+ */
+ void setShiftPressed(final boolean p) {
+ shiftPressed = p;
+ }
+
+ /**
+ * Get state of debug draw option.
+ * @return true: debug draw is active, false otherwise
+ */
+ boolean getDebugDraw() {
+ return draw;
+ }
+
+ /**
+ * Set state of debug draw option.
+ * @param d true: debug draw is active, false otherwise
+ */
+ void setDebugDraw(final boolean d) {
+ draw = d;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Tools/JFileFilter.java b/jeu-test/Lemmini/0.84/src/Tools/JFileFilter.java
new file mode 100644
index 0000000..20ae3b6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Tools/JFileFilter.java
@@ -0,0 +1,293 @@
+package Tools;
+
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * -Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * -Redistribution in binary form must reproduct the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
+ * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
+ * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
+ * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
+ * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
+ * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
+ * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
+ * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed, licensed or intended for
+ * use in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+/*
+ * @(#)JFileFilter.java 1.14 03/01/23
+ */
+
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.swing.filechooser.FileFilter;
+
+/**
+ * <p>A convenience implementation of FileFilter that filters out
+ * all files except for those type extensions that it knows about.</p>
+ *
+ * <p>Extensions are of the type ".foo", which is typically found on
+ * Windows and Unix boxes, but not on Macinthosh. Case is ignored.</p>
+ *
+ * <p>Example - create a new filter that filerts out all files
+ * but gif and jpg image files:</p>
+ *
+ * <p><code>JFileChooser chooser = new JFileChooser();
+ * JFileFilter filter = new JFileFilter(
+ * new String{"gif", "jpg"}, "JPEG & GIF Images")
+ * chooser.addChoosableFileFilter(filter);
+ * chooser.showOpenDialog(this);</code></p>
+ *
+ * @version 1.14 01/23/03
+ * @author Jeff Dinkins
+ */
+
+public class JFileFilter extends FileFilter {
+
+ private Hashtable<String,JFileFilter> filters = null;
+ private String description = null;
+ private String fullDescription = null;
+ private boolean useExtensionsInDescription = true;
+
+ /**
+ * Creates a file filter. If no filters are added, then all
+ * files are accepted.
+ *
+ * @see #addExtension(String)
+ */
+ public JFileFilter() {
+ this.filters = new Hashtable<String,JFileFilter>();
+ }
+
+ /**
+ * Creates a file filter that accepts files with the given extension.
+ * Example: new JFileFilter("jpg");
+ *
+ * @param extension string containing file extension
+ * @see #addExtension(String)
+ */
+ public JFileFilter(final String extension) {
+ this(extension,null);
+ }
+
+ /**
+ * Creates a file filter that accepts the given file type.
+ * Example: new JFileFilter("jpg", "JPEG Image Images");
+ *
+ * Note that the "." before the extension is not needed. If
+ * provided, it will be ignored.
+ *
+ * @param extension string containing file extension
+ * @param description string containing file description
+ *
+ * @see #addExtension(String)
+ */
+ public JFileFilter(final String extension, final String description) {
+ this();
+ if(extension!=null) addExtension(extension);
+ if(description!=null) setDescription(description);
+ }
+
+ /**
+ * Creates a file filter from the given string array.
+ * Example: new JFileFilter(String {"gif", "jpg"});
+ *
+ * Note that the "." before the extension is not needed adn
+ * will be ignored.
+ *
+ * @param filters string array containing extensions
+ *
+ * @see #addExtension(String)
+ */
+ public JFileFilter(final String[] filters) {
+ this(filters, null);
+ }
+
+ /**
+ * Creates a file filter from the given string array and description.
+ * Example: new JFileFilter(String {"gif", "jpg"}, "Gif and JPG Images");
+ *
+ * Note that the "." before the extension is not needed and will be ignored.
+ *
+ * @param filters string array containing extensions
+ * @param description string containing file description
+ *
+ * @see #addExtension(String)
+ */
+ public JFileFilter(final String[] filters, final String description) {
+ this();
+ for (int i = 0; i < filters.length; i++) {
+ // add filters one by one
+ addExtension(filters[i]);
+ }
+ if(description!=null) setDescription(description);
+ }
+
+ /**
+ * Return true if this file should be shown in the directory pane,
+ * false if it shouldn't.
+ *
+ * Files that begin with "." are ignored.
+ *
+ * @see #getExtension(File)
+ * @see #accept(File)
+ */
+ @Override
+ public boolean accept(final File f) {
+ if(f != null) {
+ if(f.isDirectory()) {
+ return true;
+ }
+ String extension = getExtension(f);
+ if(extension != null && filters.get(getExtension(f)) != null) {
+ return true;
+ };
+ }
+ return false;
+ }
+
+ /**
+ * Return the extension portion of the file's name .
+ *
+ * @see #getExtension(File)
+ * @see #accept(File)
+ * @param f File handle to get extension for
+ * @return extension as string (excluding the '.')
+ */
+ public String getExtension(final File f) {
+ if(f != null) {
+ String filename = f.getName();
+ int i = filename.lastIndexOf('.');
+ if(i>0 && i<filename.length()-1) {
+ return filename.substring(i+1).toLowerCase();
+ };
+ }
+ return null;
+ }
+
+ /**
+ * Adds a file type "dot" extension to filter against.
+ *
+ * For example: the following code will create a filter that filters
+ * out all files except those that end in ".jpg" and ".tif":
+ *
+ * JFileFilter filter = new JFileFilter();
+ * filter.addExtension("jpg");
+ * filter.addExtension("tif");
+ *
+ * Note that the "." before the extension is not needed and will be ignored.
+ * @param extension file extension (e.g. '.sup')
+ */
+ public void addExtension(final String extension) {
+ if(filters == null) {
+ filters = new Hashtable<String,JFileFilter>(5);
+ }
+ filters.put(extension.toLowerCase(), this);
+ fullDescription = null;
+ }
+
+
+ /**
+ * Returns the human readable description of this filter. For
+ * example: "JPEG and GIF Image Files (*.jpg, *.gif)"
+ *
+ * @see #setDescription(String)
+ * @see #setExtensionListInDescription(boolean)
+ * @see #isExtensionListInDescription()
+ * @see #getDescription()
+ */
+ @Override
+ public String getDescription() {
+ if(fullDescription == null) {
+ if(description == null || isExtensionListInDescription()) {
+ fullDescription = description==null ? "(" : description + " (";
+ // build the description from the extension list
+ Enumeration<String> extensions = filters.keys();
+ if(extensions != null) {
+ fullDescription += "." + extensions.nextElement();
+ while (extensions.hasMoreElements()) {
+ fullDescription += ", ." + extensions.nextElement();
+ }
+ }
+ fullDescription += ")";
+ } else {
+ fullDescription = description;
+ }
+ }
+ return fullDescription;
+ }
+
+ /**
+ * Sets the human readable description of this filter. For
+ * example: filter.setDescription("Gif and JPG Images");
+ *
+ * @param description string containing file description
+ *
+ * @see #setDescription(String)
+ * @see #setExtensionListInDescription(boolean)
+ * @see #isExtensionListInDescription()
+ */
+ public void setDescription(final String description) {
+ this.description = description;
+ fullDescription = null;
+ }
+
+ /**
+ * Determines whether the extension list (.jpg, .gif, etc) should
+ * show up in the human readable description.
+ *
+ * Only relevant if a description was provided in the constructor
+ * or using setDescription();
+ *
+ * @param b true if the extension list should show up in the human readable description
+ *
+ * @see #getDescription()
+ * @see #setDescription(String)
+ * @see #isExtensionListInDescription()
+ */
+ public void setExtensionListInDescription(final boolean b) {
+ useExtensionsInDescription = b;
+ fullDescription = null;
+ }
+
+ /**
+ * Returns whether the extension list (.jpg, .gif, etc) should
+ * show up in the human readable description.
+ *
+ * Only relevant if a description was provided in the constructor
+ * or using setDescription();
+ *
+ * @return true if the extension list should show up in the human readable description
+ *
+ * @see #getDescription()
+ * @see #setDescription(String)
+ * @see #setExtensionListInDescription(boolean)
+ */
+ public boolean isExtensionListInDescription() {
+ return useExtensionsInDescription;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Tools/MicrosecondTimer.java b/jeu-test/Lemmini/0.84/src/Tools/MicrosecondTimer.java
new file mode 100644
index 0000000..261ad86
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Tools/MicrosecondTimer.java
@@ -0,0 +1,126 @@
+package Tools;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Wrapper class for direct timer access. Implements a microsecond timer.
+ *
+ * @author Volker Oth
+ */
+public class MicrosecondTimer {
+
+ private long timeBase;
+
+ /**
+ * Constructor.
+ */
+ public MicrosecondTimer() {
+ timeBase = System.nanoTime()/1000;
+ }
+
+ /**
+ * Return delta time since last update.
+ * @return delta time since last update.
+ */
+ public long delta() {
+ long t = System.nanoTime()/1000;
+ long delta = t - timeBase;
+ // check for inconsistency: external clock setting (e.g. to internet server)
+ // might cause t to jump "back" in time
+ if (delta < 0) {
+ System.out.println("MicrosecondTimer inconsistency detected: "+(-delta)+"us");
+ timeBase = t;
+ delta = 0;
+ }
+ return delta;
+ }
+
+ /**
+ * Return delta time since last update and perform an update.
+ * @return delta time since last update.
+ */
+ public long deltaUpdate() {
+ long t = System.nanoTime()/1000;
+ long delta = t - timeBase;
+ // check for inconsistency: external clock setting (e.g. to internet server)
+ // might cause t to jump "back" in time
+ if (delta < 0) {
+ System.out.println("MicrosecondTimer inconsistency detected: "+(-delta)+"us");
+ delta = 0;
+ }
+ timeBase = t;
+ return delta;
+ }
+
+ /**
+ * Returns true if the given time has passed since the last update.
+ * @param dt time delta in microseconds
+ * @return true if the given time has passed since the last update.
+ */
+ public boolean timePassed(final long dt) {
+ long delta = delta();
+ if (delta>=dt)
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns true if the given time has passed since the last update and performs an update.
+ * @param dt time delta in microseconds
+ * @return true if the given time has passed since the last update.
+ */
+ public boolean timePassedUpdate(final long dt) {
+ long t = System.nanoTime()/1000;
+ long delta = delta();
+ if (delta>=dt) {
+ timeBase = t;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the given time has passed since the last update and adds the time delta.
+ * @param dt time delta
+ * @return true if the given time has passed since the last update.
+ */
+ public boolean timePassedAdd(final long dt) {
+ long delta = delta();
+ if (delta>=dt) {
+ timeBase+= dt;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Updates the internal time base to the current timer value.
+ */
+ public void update() {
+ long t = System.nanoTime()/1000;
+ timeBase = t;
+ }
+
+ /**
+ * Adds a time delta to the internal time base.
+ * @param delta time delta in microseconds
+ */
+ public void update(long delta) {
+ timeBase += delta;
+ }
+
+}
diff --git a/jeu-test/Lemmini/0.84/src/Tools/Props.java b/jeu-test/Lemmini/0.84/src/Tools/Props.java
new file mode 100644
index 0000000..7c7d063
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Tools/Props.java
@@ -0,0 +1,314 @@
+package Tools;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Properties;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Property class to ease use of ini files to save/load properties.
+ *
+ * @author Volker Oth
+ */
+public class Props {
+
+ private final static long serialVersionUID = 0x01;
+
+ /** extended hash to store properties */
+ private final Properties hash;
+ /** header string */
+ private String header;
+
+ /**
+ * Constructor
+ */
+ public Props() {
+ hash = new Properties();
+ header = new String();
+ }
+
+ /**
+ * Set the property file header
+ * @param h String containing Header information
+ */
+ public void setHeader(final String h) {
+ header = h;
+ }
+
+ /**
+ * Clear all properties
+ */
+ public void clear() {
+ hash.clear();
+ }
+
+ /**
+ * Remove key
+ * @param key Name of key
+ */
+ public void remove(final String key) {
+ hash.remove(key);
+ }
+
+ /**
+ * Set string property
+ * @param key Name of the key to set value for
+ * @param value Value to set
+ */
+ public void set(final String key, final String value) {
+ hash.setProperty(key, value);
+ }
+
+ /**
+ * Set integer property
+ * @param key Name of the key to set value for
+ * @param value Value to set
+ */
+ public void set(final String key, final int value) {
+ hash.setProperty(key, String.valueOf(value));
+ }
+
+ /**
+ * Set boolean property
+ * @param key Name of the key to set value for
+ * @param value Value to set
+ */
+ public void set(final String key, final boolean value) {
+ hash.setProperty(key, String.valueOf(value));
+ }
+
+ /**
+ * Set double property
+ * @param key Name of the key to set value for
+ * @param value Value to set
+ */
+ public void set(final String key, final double value) {
+ hash.setProperty(key, String.valueOf(value));
+ }
+
+ /**
+ * Get string property
+ * @param key Name of the key to get value for
+ * @param def Default value in case key is not found
+ * @return Value of key as String
+ */
+ public String get(final String key, final String def) {
+ String s = hash.getProperty(key,def);
+ return removeComment(s);
+ }
+
+ /**
+ * Get integer property
+ * @param key Name of the key to get value for
+ * @param def Default value in case key is not found
+ * @return Value of key as int
+ */
+ public int get(final String key, final int def) {
+ String s = hash.getProperty(key);
+ if (s == null)
+ return def;
+ s = removeComment(s);
+ return parseString(s);
+ }
+
+ /**
+ * Get integer array property
+ * @param key Name of the key to get value for
+ * @param def Default value in case key is not found
+ * @return Value of key as array of int
+ */
+ public int[] get(final String key, final int def[]) {
+ String s = hash.getProperty(key);
+ if (s == null)
+ return def;
+ s = removeComment(s);
+ String members[] = s.split(",");
+ // remove trailing and leading spaces
+ for (int i=0; i<members.length; i++)
+ members[i] = trim(members[i]);
+
+ int ret[];
+ ret = new int[members.length];
+ for (int i=0; i<members.length; i++)
+ ret[i] = parseString(members[i]);
+
+ return ret;
+ }
+
+ /**
+ * Get string array property
+ * @param key Name of the key to get value for
+ * @param def Default value in case key is not found
+ * @return Value of key as array of string
+ */
+ public String[] get(final String key, final String def[]) {
+ String s = hash.getProperty(key);
+ if (s == null)
+ return def;
+ s = removeComment(s);
+ String members[] = s.split(",");
+ // remove trailing and leading spaces
+ for (int i=0; i<members.length; i++)
+ members[i] = trim(members[i]);
+
+ return members;
+ }
+
+ /**
+ * Get boolean property
+ * @param key Name of the key to get value for
+ * @param def Default value in case key is not found
+ * @return Value of key as boolean
+ */
+ public boolean get(final String key, final boolean def) {
+ String s = hash.getProperty(key);
+ if (s == null)
+ return def;
+ s = removeComment(s);
+ return Boolean.valueOf(s).booleanValue();
+ }
+
+ /**
+ * Get double property
+ * @param key Name of the key to get value for
+ * @param def default value in case key is not found
+ * @return value of key as double
+ */
+ public double get(final String key, final double def) {
+ String s = hash.getProperty(key);
+ if (s == null)
+ return def;
+ s = removeComment(s);
+ return Double.valueOf(s).doubleValue();
+ }
+
+ /**
+ * Save property file
+ * @param fname File name of property file
+ * @return True if ok, false if exception occured
+ */
+ public boolean save(final String fname) {
+ try {
+ FileOutputStream f = new FileOutputStream(fname);
+ hash.store(f, header);
+ return true;
+ } catch(FileNotFoundException e) {
+ return false;
+ } catch(IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Load property file
+ * @param file File handle of property file
+ * @return True if OK, false if exception occurred
+ */
+ public boolean load(final URL file) {
+ try {
+ InputStream f = file.openStream();
+ hash.load(f);
+ f.close();
+ return true;
+ } catch(FileNotFoundException e) {
+ return false;
+ } catch(IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Load property file
+ * @param fname File name of property file
+ * @return True if OK, false if exception occurred
+ */
+ public boolean load(final String fname) {
+ try {
+ FileInputStream f = new FileInputStream(fname);
+ hash.load(f);
+ f.close();
+ return true;
+ } catch(FileNotFoundException e) {
+ return false;
+ } catch(IOException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Parse hex, binary or octal number
+ * @param s String that contains one number
+ * @return Integer value of string
+ */
+ private static int parseString(final String s) {
+ if (s==null || s.length()==0)
+ return -1;
+ if (s.charAt(0) == '0') {
+ if (s.length() == 1)
+ return 0;
+ else if (s.length() > 2 && s.charAt(1) == 'x')
+ return Integer.parseInt(s.substring(2),16); // hex
+ else if (s.charAt(1) == 'b') // binary
+ return Integer.parseInt(s.substring(2),2);
+ else return Integer.parseInt(s.substring(0),8); // octal
+ }
+ int retval;
+ try {
+ retval = Integer.parseInt(s);
+ } catch (NumberFormatException ex) {
+ retval = 0;
+ }
+ return retval;
+ }
+
+ /**
+ * Remove comment from line. Comment character is "#".
+ * Everything behind (including "#") will be removed
+ * @param s String to search for comment
+ * @return String without comment
+ */
+ private String removeComment(final String s) {
+ int pos = s.indexOf('#');
+ if (pos != -1)
+ return s.substring(0,pos);
+ else
+ return s;
+ }
+
+ /**
+ * Remove trailing and leading spaces from a string
+ * @param s String to process
+ * @return String without leading and trailing spaces
+ */
+ private static String trim(final String s) {
+ // search first character that is not a space
+ int spos;
+ for (spos=0; spos<s.length(); spos++)
+ if (s.charAt(spos) != ' ')
+ break;
+ int epos;
+ for (epos=s.length()-1; epos > spos; epos--)
+ if (s.charAt(epos) != ' ')
+ break;
+ return s.substring(spos, epos+1);
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/Tools/ToolBox.java b/jeu-test/Lemmini/0.84/src/Tools/ToolBox.java
new file mode 100644
index 0000000..a95a136
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/Tools/ToolBox.java
@@ -0,0 +1,274 @@
+package Tools;
+
+
+import java.awt.Component;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.geom.AffineTransform;
+import java.awt.image.AffineTransformOp;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.URL;
+import java.util.ArrayList;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+/*
+ * Copyright 2009 Volker Oth
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Selection of utility functions.
+ *
+ * @author Volker Oth
+ */
+public class ToolBox {
+
+ private static GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
+
+ /**
+ * Create a compatible buffered image.
+ * @param width width of image in pixels
+ * @param height height of image in pixels
+ * @param transparency {@link java.awt.Transparency}
+ * @return compatible buffered image
+ */
+ public static BufferedImage createImage(final int width, final int height, final int transparency) {
+ BufferedImage b = gc.createCompatibleImage(width, height, transparency);
+ return b;
+ }
+
+ /**
+ * Create a compatible buffered image from an image.
+ * @param img existing {@link java.awt.Image}
+ * @param transparency {@link java.awt.Transparency}
+ * @return compatible buffered image
+ */ public static BufferedImage ImageToBuffered(final Image img, final int transparency) {
+ BufferedImage bImg = createImage(img.getWidth(null), img.getHeight(null), transparency);
+ Graphics2D g = bImg.createGraphics();
+ g.drawImage(img, 0, 0, null);
+ g.dispose();
+ return bImg;
+ }
+
+ /**
+ * Return an array of buffered images which contain an animation.
+ * @param img image containing all the frames one above each other
+ * @param frames number of frames
+ * @param transparency {@link java.awt.Transparency}
+ * @return an array of buffered images which contain an animation
+ */
+ public static BufferedImage[] getAnimation(final Image img, final int frames, final int transparency) {
+ int width = img.getWidth(null);
+ return getAnimation(img, frames, transparency, width);
+ }
+
+ /**
+ * Return an array of buffered images which contain an animation.
+ * @param img image containing all the frames one above each other
+ * @param frames number of frames
+ * @param transparency {@link java.awt.Transparency}
+ * @param width image width
+ * @return an array of buffered images which contain an animation
+ */
+ public static BufferedImage[] getAnimation(final Image img, final int frames, final int transparency, final int width) {
+ int height = img.getHeight(null)/frames;
+ // characters stored one above the other - now separate them into single images
+ ArrayList<BufferedImage> arrImg = new ArrayList<BufferedImage>(frames);
+ int y0 = 0;
+ for (int i=0; i<frames; i++, y0+=height) {
+ BufferedImage frame = createImage(width, height, transparency);
+ Graphics2D g = frame.createGraphics();
+ g.drawImage(img, 0, 0, width, height, 0, y0, width, y0+height, null);
+ arrImg.add(frame);
+ g.dispose();
+ }
+ BufferedImage images[] = new BufferedImage[arrImg.size()];
+ return arrImg.toArray(images);
+ }
+
+ /**
+ * Flip image in X direction.
+ * @param img image to flip
+ * @return flipped image
+ */
+ public static BufferedImage flipImageX(final BufferedImage img) {
+ BufferedImage trg = createImage(img.getWidth(), img.getHeight(), img.getColorModel().getTransparency());
+ // affine transform for flipping
+ AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
+ tx.translate(-img.getWidth(), 0);
+ AffineTransformOp op = new AffineTransformOp(tx,AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ return op.filter(img, trg);
+ }
+
+ /**
+ * Use the Loader to find a file.
+ * @param fname file name
+ * @return URL of the file
+ */
+ public static URL findFile(final String fname) {
+ ClassLoader loader = ToolBox.class.getClassLoader();
+ return loader.getResource(fname);
+ }
+
+ /**
+ * Add (system default) path separator to string (if there isn't one already).
+ * @param fName String containing path name
+ * @return String that ends with the (system default) path separator for sure
+ */
+ public static String addSeparator(final String fName) {
+ int pos = fName.lastIndexOf(File.separator);
+ if (pos != fName.length()-1)
+ pos = fName.lastIndexOf("/");
+ if (pos != fName.length()-1)
+ return fName + "/";
+ else return fName;
+ }
+
+ /**
+ * Exchange any DOS style path separator ("\") with a Unix style separator ("/").
+ * @param fName String containing file/path name
+ * @return String with only Unix style path separators
+ */
+ public static String exchangeSeparators(final String fName) {
+ int pos;
+ StringBuffer sb = new StringBuffer(fName);
+ while ( (pos = sb.indexOf("\\")) != -1 )
+ sb.setCharAt(pos,'/');
+ return sb.toString();
+ }
+
+ /**
+ * Return file name from path.
+ * @param path String of a path with a file name
+ * @return String containing only the file name
+ */
+ public static String getFileName(final String path) {
+ int p1 = path.lastIndexOf("/");
+ int p2 = path.lastIndexOf("\\");
+ if (p2 > p1)
+ p1 = p2;
+ if (p1 < 0)
+ p1 = 0;
+ else
+ p1++;
+ return path.substring(p1);
+ }
+
+ /**
+ * Returns the extension (".XXX") of a filename without the dot.
+ * @param path String containing file name
+ * @return String containing only the extension (without the dot) or null (if no extension found)
+ */
+ public static String getExtension(final String path) {
+ int p1 = path.lastIndexOf("/");
+ int p2 = path.lastIndexOf("\\");
+ int p = path.lastIndexOf(".");
+ if (p==-1 || p<p1 || p<p2)
+ return null;
+ return path.substring(p+1);
+ }
+
+
+ /**
+ * Returns the first few bytes of a file to check its type.
+ * @param fname Filename of the file
+ * @param num Number of bytes to return
+ * @return Array of bytes (size num) from the beginning of the file
+ */
+ public static byte[] getFileID(final String fname, final int num) {
+ byte buf[] = new byte[num];
+ File f = new File(fname);
+ if (f.length() < num)
+ return null;
+ try {
+ FileInputStream fi = new FileInputStream(fname);
+ fi.read(buf);
+ fi.close();
+ } catch (Exception ex) {
+ return null;
+ }
+ return buf;
+ }
+
+ /**
+ * Get path name from absolute file name.
+ * @param path absolute file name
+ * @return path name without the separator
+ */
+ public static String getPathName(final String path) {
+ int p1 = path.lastIndexOf("/");
+ int p2 = path.lastIndexOf("\\");
+ if (p2 > p1)
+ p1 = p2;
+ if (p1 < 0)
+ p1 = 0;
+ return path.substring(0,p1);
+ }
+
+ /**
+ * Show exception message box.
+ * @param ex exception
+ */
+ public static void showException(final Throwable ex) {
+ String m;
+ m = "<html>";
+ m += ex.getClass().getName()+"<p>";
+ if (ex.getMessage() != null)
+ m += ex.getMessage() +"<p>";
+ StackTraceElement ste[] = ex.getStackTrace();
+ for (int i=0; i<ste.length; i++)
+ m += ste[i].toString()+"<p>";
+ m += "</html>";
+ ex.printStackTrace();
+ JOptionPane.showMessageDialog( null, m, "Error", JOptionPane.ERROR_MESSAGE );
+ ex.printStackTrace();
+ }
+
+ /**
+ * Open file dialog.
+ * @param parent parent frame
+ * @param path default file name
+ * @param ext array of allowed extensions
+ * @param load true: load, false: save
+ * @return absolute file name of selected file or null
+ */
+ public static String getFileName(final Component parent, final String path, final String ext[], final boolean load) {
+ String p = path;
+ if (p.length() == 0)
+ p = ".";
+ JFileChooser jf = new JFileChooser(p);
+ if (ext != null) {
+ JFileFilter filter = new JFileFilter();
+ for (int i=0; i<ext.length; i++)
+ filter.addExtension(ext[i]);
+ jf.setFileFilter(filter);
+ }
+ jf.setFileSelectionMode(JFileChooser.FILES_ONLY );
+ if (!load)
+ jf.setDialogType(JFileChooser.SAVE_DIALOG);
+ int returnVal = jf.showDialog(parent,null);
+ if(returnVal == JFileChooser.APPROVE_OPTION) {
+ File f = jf.getSelectedFile();
+ if (f != null)
+ return f.getAbsolutePath();
+ }
+ return null;
+ }
+}
diff --git a/jeu-test/Lemmini/0.84/src/micromod/Micromod.java b/jeu-test/Lemmini/0.84/src/micromod/Micromod.java
new file mode 100644
index 0000000..45e52d6
--- /dev/null
+++ b/jeu-test/Lemmini/0.84/src/micromod/Micromod.java
@@ -0,0 +1,560 @@
+package micromod;
+
+/**
+ * micromod/e fast module player (rev b)
+ */
+public class Micromod {
+ private final int
+ FP_SHIFT = 13,
+ FP_ONE = 1 << FP_SHIFT,
+ IN_STRUCT_LEN = 0x05,
+ IN_SAMPLE_INDEX = 0x00,
+ IN_LOOP_START = 0x01,
+ IN_LOOP_END = 0x02,
+ IN_VOLUME = 0x03,
+ IN_FINETUNE = 0x04,
+ CH_STRUCT_LEN = 0x16,
+ CH_SPOS = 0x00,
+ CH_STEP = 0x01,
+ CH_AMPL = 0x02,
+ CH_INSTRUMENT = 0x03,
+ CH_ASSIGNED = 0x04,
+ CH_VOLUME = 0x05,
+ CH_FINETUNE = 0x06,
+ CH_PERIOD = 0x07,
+ CH_PORTA_PERIOD = 0x08,
+ CH_PORTA_PARAM = 0x09,
+ CH_PANNING = 0x0A,
+ CH_ARPEGGIO = 0x0B,
+ CH_VIBR_PERIOD = 0x0C,
+ CH_VIBR_PARAM = 0x0D,
+ CH_VIBR_COUNT = 0x0E,
+ CH_TREM_VOLUME = 0x0F,
+ CH_TREM_PARAM = 0x10,
+ CH_PAT_LOOP_ROW = 0x11,
+ CH_NOTE_PERIOD = 0x12,
+ CH_NOTE_INSTRU = 0x13,
+ CH_NOTE_EFFECT = 0x14,
+ CH_NOTE_EPARAM = 0x15,
+ FX_ARPEGGIO = 0x00,
+ FX_PORTA_UP = 0x01,
+ FX_PORTA_DOWN = 0x02,
+ FX_TONE_PORTA = 0x03,
+ FX_VIBRATO = 0x04,
+ FX_TPORTA_VOL = 0x05,
+ FX_VIBRATO_VOL = 0x06,
+ FX_TREMOLO = 0x07,
+ FX_SET_PANNING = 0x08,
+ FX_SET_SPOS = 0x09,
+ FX_VOLUME_SLIDE = 0x0A,
+ FX_PAT_JUMP = 0x0B,
+ FX_SET_VOLUME = 0x0C,
+ FX_PAT_BREAK = 0x0D,
+ FX_EXTENDED = 0x0E,
+ FX_SET_SPEED = 0x0F,
+ EX_FINE_PORT_UP = 0x10,
+ EX_FINE_PORT_DN = 0x20,
+ EX_SET_GLISS = 0x30,
+ EX_SET_VIBR_WAV = 0x40,
+ EX_SET_FINETUNE = 0x50,
+ EX_PAT_LOOP = 0x60,
+ EX_SET_TREM_WAV = 0x70,
+ EX_SET_PANNING = 0x80,
+ EX_RETRIG = 0x90,
+ EX_FINE_VOL_UP = 0xA0,
+ EX_FINE_VOL_DN = 0xB0,
+ EX_NOTE_CUT = 0xC0,
+ EX_NOTE_DELAY = 0xD0,
+ EX_PAT_DELAY = 0xE0,
+ EX_INVERT_LOOP = 0xF0;
+
+ private final int[] arptable = new int[] {
+ 8192, 8679, 9195, 9742, 10321, 10935, 11585, 12274,
+ 13004, 13777, 14596, 15464, 16384, 17358, 18390, 19484
+ };
+
+ private final int[] fttable = new int[] {
+ 15464, 15576, 15689, 15803, 15918, 16033, 16149, 16266,
+ 16384, 16503, 16622, 16743, 16864, 16986, 17109, 17233
+ };
+
+ private final int[] sintable = new int[] {
+ 0, 24 , 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253,
+ 255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24
+ };
+
+ private byte[] mod;
+ private boolean amiga;
+ private int numchan, songlen, restart;
+ private int pat, npat, row, nrow, tick, tempo, bpm;
+ private int fcount, loopcount, loopchan;
+ private int[] instruments = new int[ IN_STRUCT_LEN * 32 ];
+ private int[] channels = new int[ CH_STRUCT_LEN * 32 ];
+ private int samplerate, tickremain;
+
+ /* constructor ( mod - module data ) */
+ /**
+ * Constructor
+ * @param mod byte buffer containing MOD data
+ * @param samplerate sample rate in Hertz
+ */
+ public Micromod( final byte[] mod, final int samplerate ) {
+ this.mod = mod;
+ this.samplerate = samplerate;
+ //System.out.println( "micromod/e (c)2005 mumart@gmail.com" );
+ songlen = mod[ 950 ] & 0x7F;
+ restart = mod[ 951 ] & 0x7F;
+ if( restart >= songlen ) restart = 0;
+ int numpatterns = 0;
+ for( int n = 0; n < 128; n++ ) {
+ int pat = mod[ 952 + n ] & 0x7F;
+ if( pat >= numpatterns ) numpatterns = pat + 1;
+ }
+ switch( ( mod[ 1082 ] << 8 ) | mod[ 1083 ] ) {
+ case 0x4b2e: // M.K.
+ case 0x4b21: // M!K!
+ case 0x5434: // FLT4
+ numchan = 4;
+ amiga = true;
+ break;
+ case 0x484e: // xCHN
+ numchan = mod[ 1080 ] - 48;
+ amiga = false;
+ break;
+ case 0x4348: // xxCH
+ numchan = ( ( mod[ 1080 ] - 48 ) * 10 ) + ( mod[ 1081 ] - 48 );
+ amiga = false;
+ break;
+ default:
+ throw new IllegalArgumentException( "MOD Format not recognised!" );
+ }
+ int sampleidx = 1084 + 4 * numchan * 64 * numpatterns;
+ for( int inst = 0; inst < 31; inst++ ) {
+ int slen = ushortbe( mod, inst * 30 + 42 ) << 1;
+ int fine = mod[ inst * 30 + 44 ] & 0xF;
+ if( fine > 7 ) fine -= 16;
+ int vol = mod[ inst * 30 + 45 ] & 0x7F;
+ if( vol > 64 ) vol = 64;
+ int lsta = ushortbe( mod, inst * 30 + 46 ) << 1;
+ int llen = ushortbe( mod, inst * 30 + 48 ) << 1;
+ if( sampleidx + slen - 1 >= mod.length ) {
+ System.out.println( "Module is truncated!" );
+ slen = mod.length - sampleidx;
+ if( slen < 0 ) slen = 0;
+ }
+ if( llen < 4 || lsta >= slen ) {
+ lsta = slen - 1;
+ llen = 1;
+ }
+ int lend = lsta + llen - 1;
+ if( lend >= slen ) lend = slen - 1;
+ int ioffset = ( inst + 1 ) * IN_STRUCT_LEN;
+ instruments[ ioffset + IN_SAMPLE_INDEX ] = sampleidx;
+ instruments[ ioffset + IN_LOOP_START ] = lsta;
+ instruments[ ioffset + IN_LOOP_END ] = lend;
+ instruments[ ioffset + IN_FINETUNE ] = fine;
+ instruments[ ioffset + IN_VOLUME ] = vol;
+ sampleidx += slen;
+ }
+ reset();
+ }
+
+ /**
+ * return the song length in samples
+ * @return song length in samples
+ */
+ public int getlen() {
+ reset();
+ int len = getticklen();
+ while( !tick() ) len += getticklen();
+ reset();
+ return len;
+ }
+
+ /**
+ * Mix 16 bit stereo audio into the buffers.
+ * @param l left
+ * @param r right
+ * @param ofs offset
+ * @param length length
+ */
+ public void mix( final int[] l, final int[] r, final int ofs, final int length ) {
+ int len = length;
+ int offset = ofs;
+ while( len > 0 ) {
+ int count = tickremain;
+ if( count > len ) count = len;
+ for( int chan = 0; chan < numchan ; chan++ ) {
+ int coffset = chan * CH_STRUCT_LEN;
+ int ampl = channels[ coffset + CH_AMPL ];
+ int pann = channels[ coffset + CH_PANNING ] << FP_SHIFT - 8;
+ int lamp = ampl * ( FP_ONE - pann ) >> FP_SHIFT;
+ int ramp = ampl * pann >> FP_SHIFT;
+ int inst = channels[ coffset + CH_INSTRUMENT ];
+ int ioffset = inst * IN_STRUCT_LEN;
+ int sidx = instruments[ ioffset + IN_SAMPLE_INDEX ];
+ int lsta = instruments[ ioffset + IN_LOOP_START ] << FP_SHIFT;
+ int lep1 = instruments[ ioffset + IN_LOOP_END ] + 1 << FP_SHIFT;
+ int spos = channels[ coffset + CH_SPOS ];
+ int step = channels[ coffset + CH_STEP ];
+ int llen = lep1 - lsta;
+ boolean dontmix = llen <= FP_ONE && spos >= lsta;
+ if( !dontmix ) for( int x = 0; x < count; x++ ) {
+ while( spos >= lep1 ) spos -= llen;
+ int sample = mod[ sidx + ( spos >> FP_SHIFT ) ] << 8;
+ l[ offset + x ] += sample * lamp >> FP_SHIFT;
+ r[ offset + x ] += sample * ramp >> FP_SHIFT;
+ spos += step;
+ }
+ channels[ coffset + CH_SPOS ] = spos;
+ }
+ tickremain -= count;
+ if( tickremain == 0 ) {
+ tick();
+ tickremain = getticklen();
+ }
+ offset += count;
+ len -= count;
+ }
+ }
+
+ private void reset() {
+ pat = npat = 0;
+ row = nrow = 0;
+ tick = tempo = 6;
+ bpm = 125;
+ loopcount = loopchan = 0;
+ for( int n = 0; n < channels.length; n++ ) channels[ n ] = 0;
+ for( int chan = 0; chan < numchan; chan++ ) {
+ int p = 128;
+ switch( chan & 0x3 ) {
+ case 0: p = 64; break;
+ case 1: p = 192; break;
+ case 2: p = 192; break;
+ case 3: p = 64; break;
+ }
+ channels[ chan * CH_STRUCT_LEN + CH_PANNING ] = p;
+ }
+ row();
+ tickremain = getticklen();
+ }
+
+ private boolean tick() {
+ tick--;
+ if( tick <= 0 ) {
+ tick = tempo;
+ return row();
+ }
+ // Update channel fx
+ for( int chan = 0; chan < numchan; chan++ ) {
+ int coffset = chan * CH_STRUCT_LEN;
+ int effect = channels[ coffset + CH_NOTE_EFFECT ];
+ int eparam = channels[ coffset + CH_NOTE_EPARAM ];
+ switch( effect ) {
+ case FX_ARPEGGIO:
+ switch( fcount % 3 ) {
+ case 0: channels[ coffset + CH_ARPEGGIO ] = 0; break;
+ case 1: channels[ coffset + CH_ARPEGGIO ] = ( eparam & 0xF0 ) >> 4; break;
+ case 2: channels[ coffset + CH_ARPEGGIO ] = eparam & 0x0F; break;
+ }
+ break;
+ case FX_PORTA_UP:
+ channels[ coffset + CH_PERIOD ] -= eparam;
+ break;
+ case FX_PORTA_DOWN:
+ channels[ coffset + CH_PERIOD ] += eparam;
+ break;
+ case FX_TONE_PORTA:
+ toneporta( coffset );
+ break;
+ case FX_VIBRATO:
+ vibrato( coffset );
+ break;
+ case FX_TPORTA_VOL:
+ volslide( coffset, eparam );
+ toneporta( coffset );
+ break;
+ case FX_VIBRATO_VOL:
+ volslide( coffset, eparam );
+ vibrato( coffset );
+ break;
+ case FX_TREMOLO:
+ tremolo( coffset );
+ break;
+ case FX_VOLUME_SLIDE:
+ volslide( coffset, eparam );
+ break;
+ case FX_EXTENDED:
+ switch( eparam & 0xF0 ) {
+ case EX_RETRIG:
+ int rtparam = eparam & 0x0F;
+ if( rtparam == 0 ) rtparam = 1;
+ if( fcount % rtparam == 0 ) channels[ coffset + CH_SPOS ] = 0;
+ break;
+ case EX_NOTE_CUT:
+ if( ( eparam & 0x0F ) == fcount ) channels[ coffset + CH_VOLUME ] = 0;
+ break;
+ case EX_NOTE_DELAY:
+ if( ( eparam & 0x0F ) == fcount ) trigger( coffset );
+ break;
+ }
+ break;
+ }
+ channels[ coffset + CH_VIBR_COUNT ]++;
+ }
+ mixupdate();
+ fcount++;
+ return false;
+ }
+
+ private boolean row() {
+ // Decide whether to restart.
+ boolean songend = false;
+ if( npat < pat ) songend = true;
+ if( npat == pat && nrow <= row && loopcount <= 0 ) songend = true;
+ // Jump to next row
+ pat = npat;
+ row = nrow;
+ // Decide next row.
+ nrow = row + 1;
+ if( nrow == 64 ) {
+ npat = pat + 1;
+ nrow = 0;
+ }
+ // Load channels and process fx
+ fcount = 0;
+ int poffset = mod[ 952 + pat ] & 0x7F;
+ int roffset = 1084 + ( poffset * 64 * numchan * 4 ) + ( row * numchan * 4 );
+ for( int chan = 0; chan < numchan; chan++ ) {
+ int coffset = chan * CH_STRUCT_LEN;
+ int noffset = roffset + ( chan * 4 );
+ channels[ coffset + CH_NOTE_PERIOD ] = ( mod[ noffset + 1 ] & 0xFF ) | ( ( mod[ noffset ] & 0x0F ) << 8 );
+ channels[ coffset + CH_NOTE_INSTRU ] = ( ( mod[ noffset + 2 ] & 0xF0 ) >> 4 ) | ( mod[ noffset ] & 0x10 );
+ channels[ coffset + CH_NOTE_EFFECT ] = mod[ noffset + 2 ] & 0x0F;
+ channels[ coffset + CH_NOTE_EPARAM ] = mod[ noffset + 3 ] & 0xFF;
+ int effect = channels[ coffset + CH_NOTE_EFFECT ];
+ int eparam = channels[ coffset + CH_NOTE_EPARAM ];
+ if( !( effect == FX_EXTENDED && ( ( eparam & 0xF0 ) == EX_NOTE_DELAY ) ) ) trigger( coffset );
+ channels[ coffset + CH_ARPEGGIO ] = 0;
+ channels[ coffset + CH_VIBR_PERIOD ] = 0;
+ channels[ coffset + CH_TREM_VOLUME ] = 0;
+ switch( effect ) {
+ case FX_ARPEGGIO:
+ break;
+ case FX_PORTA_UP:
+ break;
+ case FX_PORTA_DOWN:
+ break;
+ case FX_TONE_PORTA:
+ if( eparam != 0 ) channels[ coffset + CH_PORTA_PARAM ] = eparam;
+ break;
+ case FX_VIBRATO:
+ if( eparam != 0 ) channels[ coffset + CH_VIBR_PARAM ] = eparam;
+ vibrato( coffset );
+ break;
+ case FX_TPORTA_VOL:
+ break;
+ case FX_VIBRATO_VOL:
+ vibrato( coffset );
+ break;
+ case FX_TREMOLO:
+ if( eparam != 0 ) channels[ coffset + CH_TREM_PARAM ] = eparam;
+ tremolo( coffset );
+ break;
+ case FX_SET_PANNING:
+ if( !amiga ) channels[ coffset + CH_PANNING ] = eparam;
+ break;
+ case FX_SET_SPOS:
+ channels[ coffset + CH_SPOS ] = eparam << FP_SHIFT + 8;
+ break;
+ case FX_VOLUME_SLIDE:
+ break;
+ case FX_PAT_JUMP:
+ if( loopcount <= 0 ) {
+ npat = eparam;
+ nrow = 0;
+ }
+ break;
+ case FX_SET_VOLUME:
+ channels[ coffset + CH_VOLUME ] = ( eparam > 64 ) ? 64 : eparam;
+ break;
+ case FX_PAT_BREAK:
+ if( loopcount <= 0 ) {
+ npat = pat + 1;
+ nrow = ( ( eparam & 0xF0 ) >> 4 ) * 10 + ( eparam & 0x0F );
+ }
+ break;
+ case FX_EXTENDED:
+ switch( eparam & 0xF0 ) {
+ case EX_FINE_PORT_UP:
+ channels[ coffset + CH_PERIOD ] -= ( eparam & 0x0F );
+ break;
+ case EX_FINE_PORT_DN:
+ channels[ coffset + CH_PERIOD ] += ( eparam & 0x0F );
+ break;
+ case EX_SET_GLISS:
+ break;
+ case EX_SET_VIBR_WAV:
+ break;
+ case EX_SET_FINETUNE:
+ int ftval = eparam & 0x0F;
+ if( ftval > 7 ) ftval -= 16;
+ channels[ coffset + CH_FINETUNE ] = ftval;
+ break;
+ case EX_PAT_LOOP:
+ int plparam = eparam & 0x0F;
+ if( plparam == 0 ) channels[ coffset + CH_PAT_LOOP_ROW ] = row;
+ if( plparam > 0 && channels[ coffset + CH_PAT_LOOP_ROW ] < row ) {
+ if( loopcount <= 0 ) {
+ loopcount = plparam;
+ loopchan = chan;
+ nrow = channels[ coffset + CH_PAT_LOOP_ROW ];
+ npat = pat;
+ } else if( loopchan == chan ) {
+ if( loopcount == 1 ) {
+ channels[ coffset + CH_PAT_LOOP_ROW ] = row + 1;
+ } else {
+ nrow = channels[ coffset + CH_PAT_LOOP_ROW ];
+ npat = pat;
+ }
+ loopcount--;
+ }
+ }
+ break;
+ case EX_SET_TREM_WAV:
+ break;
+ case EX_SET_PANNING:
+ break;
+ case EX_RETRIG:
+ break;
+ case EX_FINE_VOL_UP:
+ int fvolup = channels[ coffset + CH_VOLUME ] + ( eparam & 0x0F );
+ if( fvolup > 64 ) fvolup = 64;
+ channels[ coffset + CH_VOLUME ] = fvolup;
+ break;
+ case EX_FINE_VOL_DN:
+ int fvoldn = channels[ coffset + CH_VOLUME ] - ( eparam & 0x0F );
+ if( fvoldn > 64 ) fvoldn = 0;
+ channels[ coffset + CH_VOLUME ] = fvoldn;
+ break;
+ case EX_NOTE_CUT:
+ if( ( eparam & 0x0F ) == fcount ) channels[ coffset + CH_VOLUME ] = 0;
+ break;
+ case EX_NOTE_DELAY:
+ if( ( eparam & 0x0F ) == fcount ) trigger( coffset );
+ break;
+ case EX_PAT_DELAY:
+ tick = tempo + tempo * ( eparam & 0x0F );
+ break;
+ case EX_INVERT_LOOP:
+ break;
+ }
+ break;
+ case FX_SET_SPEED:
+ if( eparam < 32 ) tick = tempo = eparam;
+ else bpm = eparam;
+ break;
+ }
+ }
+ mixupdate();
+ fcount++;
+ if( npat >= songlen ) npat = restart;
+ if( nrow >= 64 ) nrow = 0;
+ return songend;
+ }
+
+ private void trigger( final int coffset ) {
+ int period = channels[ coffset + CH_NOTE_PERIOD ];
+ int instru = channels[ coffset + CH_NOTE_INSTRU ];
+ int effect = channels[ coffset + CH_NOTE_EFFECT ];
+ if( instru != 0 ) {
+ channels[ coffset + CH_ASSIGNED ] = instru;
+ int ioffset = instru * IN_STRUCT_LEN;
+ channels[ coffset + CH_VOLUME ] = instruments[ ioffset + IN_VOLUME ];
+ channels[ coffset + CH_FINETUNE ] = instruments[ ioffset + IN_FINETUNE ];
+ if( amiga ) {
+ int atlsta = instruments[ ioffset + IN_LOOP_START ];
+ int atlend = instruments[ ioffset + IN_LOOP_END ];
+ if( atlend > atlsta ) channels[ coffset + CH_INSTRUMENT ] = instru;
+ }
+ }
+ if( period != 0 ) {
+ channels[ coffset + CH_INSTRUMENT ] = channels[ coffset + CH_ASSIGNED ];
+ channels[ coffset + CH_PORTA_PERIOD ] = period;
+ if( effect != FX_TONE_PORTA && effect != FX_TPORTA_VOL ) {
+ channels[ coffset + CH_PERIOD ] = period;
+ channels[ coffset + CH_SPOS ] = 0;
+ }
+ channels[ coffset + CH_VIBR_COUNT ] = 0;
+ }
+ }
+
+ private void volslide( final int coffset, final int eparam ) {
+ int vol = channels[ coffset + CH_VOLUME ];
+ vol += ( eparam & 0xF0 ) >> 4;
+ vol -= eparam & 0x0F;
+ if( vol > 64 ) vol = 64;
+ if( vol < 0 ) vol = 0;
+ channels[ coffset + CH_VOLUME ] = vol;
+ }
+
+ private void toneporta( final int coffset ) {
+ int sp = channels[ coffset + CH_PERIOD ];
+ int dp = channels[ coffset + CH_PORTA_PERIOD ];
+ if( sp < dp ) {
+ sp += channels[ coffset + CH_PORTA_PARAM ];
+ if( sp > dp ) sp = dp;
+ }
+ if( sp > dp ) {
+ sp -= channels[ coffset + CH_PORTA_PARAM ];
+ if( sp < dp ) sp = dp;
+ }
+ channels[ coffset + CH_PERIOD ] = sp;
+ }
+
+ private void vibrato( final int coffset ) {
+ int vparam = channels[ coffset + CH_VIBR_PARAM ];
+ int vspeed = ( vparam & 0xF0 ) >> 4;
+ int vdepth = vparam & 0x0F;
+ int vibpos = vspeed * channels[ coffset + CH_VIBR_COUNT ];
+ int tval = sintable[ vibpos & 0x1F ];
+ if( ( vibpos & 0x20 ) > 0 ) tval = -tval;
+ channels[ coffset + CH_VIBR_PERIOD ] = tval * vdepth >> 7;
+ }
+
+ private void tremolo( final int coffset ) {
+ int tparam = channels[ coffset + CH_TREM_PARAM ];
+ int tspeed = ( tparam & 0xF0 ) >> 4;
+ int tdepth = tparam & 0x0F;
+ int trempos = tspeed * channels[ coffset + CH_VIBR_COUNT ];
+ int tval = sintable[ trempos & 0x1F ];
+ if( ( trempos & 0x20 ) > 0 ) tval = -tval;
+ channels[ coffset + CH_TREM_VOLUME ] = tval * tdepth >> 7;
+ }
+
+ private void mixupdate() {
+ for( int chan = 0; chan < numchan; chan++ ) {
+ int coffset = chan * CH_STRUCT_LEN;
+ int a = channels[ coffset + CH_VOLUME ] + channels[ coffset + CH_TREM_VOLUME ];
+ if( a < 0 ) a = 0;
+ if( a > 64 ) a = 64;
+ channels[ coffset + CH_AMPL ] = a << FP_SHIFT - 8;
+ int p = channels[ coffset + CH_PERIOD ] + channels[ coffset + CH_VIBR_PERIOD ];
+ if( p < 27 ) p = 27;
+ int clk = amiga ? 3546894 : 3579364;
+ int s = ( clk / p << FP_SHIFT ) / samplerate;
+ s = s * fttable[ channels[ coffset + CH_FINETUNE ] + 8 ] >> 14;
+ s = s * arptable[ channels[ coffset + CH_ARPEGGIO ] ] >> 13;
+ channels[ coffset + CH_STEP ] = s;
+ }
+ }
+
+ private int getticklen() {
+ return ( ( samplerate << 1 ) + ( samplerate >> 1 ) ) / bpm;
+ }
+
+ private int ushortbe( final byte[] buf, final int offset ) {
+ return ( ( buf[ offset ] & 0xFF ) << 8 ) | ( buf[ offset + 1 ] & 0xFF );
+ }
+}
+