cosmetix
[dd2d.git] / d2dparts.d
blob28465c677ad456e85320c638c886247011542125
1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module d2dparts is aliced;
19 private:
21 import arsd.color;
23 import glutils;
24 import console;
25 import wadarc;
27 import d2dmap;
28 import d2dgfx;
29 import dengapi : map, unsyncrandu31;
32 // ////////////////////////////////////////////////////////////////////////// //
33 // "dead" blood dots will stay on image
34 //TODO: "dormant" blood should fall down if map was modified
37 public void dotInit () {
38 imgParts = new TrueColorImage(map.width*8, map.height*8);
39 imgPartsOld = new TrueColorImage(map.width*8, map.height*8);
40 foreach (int y; 0..map.height*8) {
41 foreach (int x; 0..map.width*8) {
42 putPixel(x, y, Color(0, 0, 0, 0));
43 putPixelOld(x, y, Color(0, 0, 0, 0));
46 texParts = new Texture(imgParts, Texture.Option.Nearest, Texture.Option.Clamp);
48 foreach (int y; 0..map.height*8) {
49 foreach (int x; 0..map.width*8) {
50 putPixel(x, y, Color(0, 200, 0, 255));
54 //texParts.setFromImage(imgParts, 0, 0);
58 // ////////////////////////////////////////////////////////////////////////// //
59 __gshared public Texture texParts; // texture for particles; can be blended on bg
60 __gshared TrueColorImage imgParts, imgPartsOld;
63 void putPixelOld (int x, int y, Color clr) {
64 pragma(inline, true);
65 if (/*clr.a != 0 &&*/ x >= 0 && y >= 0 && x < imgPartsOld.width && y < imgPartsOld.height) {
66 imgPartsOld.imageData.colors.ptr[y*imgPartsOld.width+x] = clr;
71 void putPixel (int x, int y, Color clr) {
72 pragma(inline, true);
73 if (/*clr.a != 0 &&*/ x >= 0 && y >= 0 && x < imgParts.width && y < imgParts.height) {
74 imgParts.imageData.colors.ptr[y*imgParts.width+x] = clr;
79 Color getPixel (int x, int y) {
80 pragma(inline, true);
81 return (x >= 0 && y >= 0 && x < imgParts.width && y < imgParts.height ? imgParts.imageData.colors.ptr[y*imgParts.width+x] : Color(0, 0, 0, 0));
85 // ////////////////////////////////////////////////////////////////////////// //
86 enum BL_XV = 4;
87 enum BL_YV = 4;
88 //enum BL_MINT = 10;
89 //enum BL_MAXT = 14;
91 enum SP_V = 2;
92 enum SP_MINT = 5;
93 enum SP_MAXT = 7;
96 // ////////////////////////////////////////////////////////////////////////// //
97 struct DotParticle {
98 int prevX, prevY;
99 int lastX, lastY;
100 int x, y, xv, yv, vx, vy;
101 ubyte time;
102 ubyte color; // from D2D palette
103 Color drawColor;
104 //Color prevColor; // on image; a=0: was empty
106 @disable this (this); // no copy
110 // ////////////////////////////////////////////////////////////////////////// //
111 __gshared DotParticle[1024] dots;
112 __gshared uint dotsUsed;
115 void dotRemove (usize idx, bool leftOnPic) {
116 if (idx >= dotsUsed) return;
117 import core.stdc.string : memmove;
118 auto dt = dots.ptr+idx;
119 if (leftOnPic) putPixelOld(dt.x, dt.y, dt.drawColor);
121 auto pclr = (leftOnPic ? dt.drawColor : dt.prevColor);
122 bool found = false;
123 int px = (leftOnPic ? dt.x : dt.lastX);
124 int py = (leftOnPic ? dt.y : dt.lastY);
125 // if it has color, reassign this color to next dot at this coords
126 foreach (ref dp; dots[0..dotsUsed]) {
127 if (dp.lastX == px && dp.lastY == py) {
128 dp.prevColor = pclr;
129 found = true;
130 break;
133 // if not found, just draw it
134 if (!found) putPixel(px, py, pclr);
136 // move dots
137 --dotsUsed;
138 if (idx < dotsUsed) memmove(dots.ptr+idx, dots.ptr+idx+1, (dotsUsed-idx)*dots[0].sizeof);
142 DotParticle* dotAlloc () {
143 if (dotsUsed == dots.length) dotRemove(0, false);
144 auto res = dots.ptr+dotsUsed;
145 ++dotsUsed;
146 *res = DotParticle.init;
147 return res;
151 void dotAdd (int x, int y, int xv, int yv, ubyte color, ubyte time) {
152 //conwriteln("dotAdd: x=", x, "; y=", y, "; xv=", xv, "; yv=", yv, "; color=", color, "; time=", time);
153 if (x < 0 || y < 0 || x >= map.width*8 || y >= map.height*8) return;
154 if (!Z_canfit(x, y)) return;
155 auto dot = dotAlloc();
156 if (dot is null) return;
157 dot.x = dot.prevX = dot.lastX = x;
158 dot.y = dot.prevY = dot.lastY = y;
159 dot.xv = xv;
160 dot.yv = yv;
161 dot.color = color;
162 dot.time = time;
163 dot.vx = dot.vy = 0;
164 //dot.prevColor = getPixel(x, y);
165 dot.drawColor = d2dpal[color];
166 dot.drawColor.a = 180;
170 // ////////////////////////////////////////////////////////////////////////// //
171 public void dotDraw (float itp) {
172 import core.stdc.math : roundf;
173 // remove dots from image (in reverse order)
174 //foreach_reverse (ref dt; dots[0..dotsUsed]) putPixel(dt.lastX, dt.lastY, dt.prevColor);
175 imgParts.imageData.colors[] = imgPartsOld.imageData.colors[];
176 //imgParts.imageData.colors[] = Color(0, 0, 0, 0);
177 // draw dots
178 foreach (ref dt; dots[0..dotsUsed]) {
179 int nx = cast(int)(dt.x+roundf((dt.x-dt.prevX)*itp));
180 int ny = cast(int)(dt.y+roundf((dt.y-dt.prevY)*itp));
181 dt.lastX = nx;
182 dt.lastY = ny;
183 //dt.prevColor = getPixel(nx, ny);
184 putPixel(nx, ny, dt.drawColor);
186 // update texture
187 texParts.setFromImage(imgParts, 0, 0);
191 // ////////////////////////////////////////////////////////////////////////// //
192 public void dotThink () {
193 // now move dots
194 int idx = 0;
195 auto dt = dots.ptr;
196 while (idx < dotsUsed) {
197 if (dt.time) {
198 dt.prevX = dt.x;
199 dt.prevY = dt.y;
200 int xv = dt.xv+dt.vx;
201 int yv = dt.yv+dt.vy;
202 if (dt.time < 254) {
203 if ((--dt.time) == 0) {
204 dotRemove(idx, false);
205 continue;
208 int st = Z_moveobj(ref *dt);
209 if (st&(Z_HITWATER|Z_FALLOUT)) {
210 // blood?
211 //if (dt.time == 255) putPixel(dt.x, dt.y, d2dpal.ptr[dt.color]);
212 dotRemove(idx, (dt.time == 255));
213 dt.time = 0;
214 continue;
216 if (st&Z_HITLAND) {
217 if (!dt.xv) {
218 if (yv > 2) {
219 dt.vx = (xv ? Z_sign(dt.vx) : (unsyncrandu31&0x01 ? -1 : 1));
220 if (unsyncrandu31%yv == 0) dt.vx *= 2;
221 dt.yv = yv-2;
224 dt.xv = 0;
225 if (dt.time > 4 && dt.time != 255) dt.time = 4;
227 if (st&Z_HITWALL) {
228 dt.vx = Z_sign(xv)*2;
229 dt.yv = Z_sign(dt.yv);
230 if (dt.yv >= 0 && (unsyncrandu31&0x03)) --dt.yv;
231 if (dt.yv >= 0 && (unsyncrandu31&0x01)) --dt.yv;
233 if (st&Z_HITCEIL) {
234 dt.xv = 0;
235 dt.yv = (unsyncrandu31%100 ? -2 : 0);
237 } else {
238 dotRemove(idx, false);
239 continue;
241 ++idx;
242 ++dt;
247 // ////////////////////////////////////////////////////////////////////////// //
248 public void dotAddBlood (int x, int y, int xv, int yv, int n) {
249 while (n-- > 0) {
250 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
251 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
252 int dxv = cast(int)unsyncrandu31%(BL_XV*2+1)-BL_XV+Z_dec(xv, 3);
253 int dyv = -cast(int)unsyncrandu31%(BL_YV)+Z_dec(yv, 3)-3;
254 ubyte clr = cast(ubyte)(0xB0+unsyncrandu31%16);
255 dotAdd(dx, dy, dxv, dyv, clr, 255);
260 public void dotAddSpark (int x, int y, int xv, int yv, int n) {
261 while (n-- > 0) {
262 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
263 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
264 int dxv = cast(int)unsyncrandu31%(SP_V*2+1)-SP_V-xv/4;
265 int dyv = cast(int)unsyncrandu31%(SP_V*2+1)-SP_V-yv/4;
266 ubyte clr = cast(ubyte)(0xA0+unsyncrandu31%6);
267 ubyte time = cast(ubyte)(unsyncrandu31%(SP_MAXT-SP_MINT+1)+SP_MINT);
268 dotAdd(dx, dy, dxv, dyv, clr, time);
273 public void dotAddWater (int x, int y, int xv, int yv, int n, int color) {
274 static immutable ubyte[3] ct = [0xC0, 0x70, 0xB0];
275 if (color >= 0 && color < 3) {
276 while (n-- > 0) {
277 import std.math : abs;
278 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
279 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
280 int dxv = cast(int)unsyncrandu31%(BL_XV*2+1)-BL_XV+Z_dec(xv, 3);
281 int dyv = -cast(int)unsyncrandu31%(BL_YV)-abs(yv);
282 ubyte clr = cast(ubyte)(unsyncrandu31%16+ct.ptr[color]);
283 dotAdd(dx, dy, dxv, dyv, clr, 254);
289 // ////////////////////////////////////////////////////////////////////////// //
290 int clamp7 (int v) { /*pragma(inline, true);*/ import std.math : abs; return (abs(v) <= 7 ? v : (v > 0 ? 7 : -7)); }
293 int Z_sign (int n) { pragma(inline, true); return (n < 0 ? -1 : (n > 0 ? 1 : 0)); }
296 int Z_dec (int n, int delta) {
297 import std.math : abs;
298 if (abs(n) > delta) {
299 if (n > 0) return n-delta;
300 if (n < 0) return n+delta;
302 return 0;
306 int Z_checkArea (int x, int y, scope int delegate (ubyte tt) dg) {
307 int tx = x/8;
308 int ty = y/8;
309 if (tx < 0 || ty < 0 || tx >= map.width || ty >= map.height) return 0;
310 if (auto res = dg(map.tiles.ptr[LevelMap.Type].ptr[ty*map.width+tx])) return res;
311 return 0;
315 bool Z_checkAreaFit (int x, int y, scope int delegate (ubyte tt) dg) {
316 if (Z_checkArea(x, y, dg)) {
317 int tx = x/8;
318 int ty = y/8;
319 auto fgt = map.tiles.ptr[LevelMap.Front].ptr[ty*map.width+tx];
320 if (fgt >= map.walltypes.length) return false;
321 if (map.walltypes[fgt]) return true;
322 auto bgt = map.tiles.ptr[LevelMap.Back].ptr[ty*map.width+tx];
323 if (bgt >= map.walltypes.length) return false;
324 if (map.walltypes[bgt]&0x02) return true;
326 return false;
330 bool Z_canfit (int x, int y) {
332 int tx = x/8;
333 int ty = y/8;
334 if (tx < 0 || ty < 0 || tx >= map.width || ty >= map.height) return true;
335 auto tt = map.tiles.ptr[LevelMap.Type].ptr[ty*map.width+tx];
336 if (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC) {
337 fgt = map.tiles.ptr[LevelMap.Front].ptr[ty*map.width+tx];
338 if (map.walltype[fgt]) return false;
339 bgt = map.tiles.ptr[LevelMap.Back].ptr[ty*map.width+tx];
340 if (map.walltype[bgt]&0x02) return false;
342 return true;
344 return !Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC ? 1 : 0));
348 bool Z_hitceil (int x, int y) {
349 return Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC ? 1 : 0));
353 bool Z_canstand (int x, int y) {
354 return Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC || tt == LevelMap.TILE_STEP ? 1 : 0));
358 // 0: not; 1: up; 2: down
359 int Z_inlift (int x, int y) {
360 return Z_checkArea(x, y, (tt) {
361 if (tt == LevelMap.TILE_LIFTU) return 1;
362 if (tt == LevelMap.TILE_LIFTD) return 2;
363 return 0;
368 // 0: not; >0: water
369 int Z_inwater (int x, int y) {
370 return Z_checkArea(x, y, (tt) {
371 if (tt == LevelMap.TILE_WATER) return 1;
372 if (tt == LevelMap.TILE_ACID1) return 2;
373 if (tt == LevelMap.TILE_ACID2) return 3;
374 return 0;
379 int Z_moveobj (ref DotParticle dot) {
380 enum r = 0;
381 enum h = 1;
382 enum CELW = 8;
383 enum CELH = 8;
384 immutable int FLDW = map.width*CELW;
385 immutable int FLDH = map.height*CELH;
386 int xv, yv, lx, ly;
387 int inw;
389 int st = 0;
390 int x = dot.x;
391 int y = dot.y;
393 switch (Z_inlift(x, y)) {
394 case 0:
395 if (++dot.yv > MAX_YV) --dot.yv;
396 break;
397 case 1:
398 if (--dot.yv < -5) ++dot.yv;
399 break;
400 case 2:
401 if (dot.yv > 5) --dot.yv; else ++dot.yv;
402 break;
403 default:
406 inw = Z_inwater(x, y);
407 if (inw != 0) {
408 import std.math : abs;
409 st |= Z_INWATER;
410 if ((xv = abs(dot.xv)+1) > 5) dot.xv = Z_dec(dot.xv, xv/2-2);
411 if ((xv = abs(dot.yv)+1) > 5) dot.yv = Z_dec(dot.yv, xv/2-2);
412 if ((xv = abs(dot.vx)+1) > 5) dot.vx = Z_dec(dot.vx, xv/2-2);
413 if ((xv = abs(dot.vy)+1) > 5) dot.vy = Z_dec(dot.vy, xv/2-2);
416 dot.vx = Z_dec(dot.vx, 1);
417 dot.vy = Z_dec(dot.vy, 1);
419 xv = dot.xv+dot.vx;
420 yv = dot.yv+dot.vy;
422 while (xv || yv) {
423 if (x < -100 || x >= FLDW*CELW+100 || y < -100 || y >= FLDH*CELH+100) {
424 // out of map
425 st |= Z_FALLOUT;
428 lx = x;
429 x += clamp7(xv);
431 if (!Z_canfit(x, y)) {
432 if (xv == 0) x = lx;
433 else if (xv < 0) x = ((lx-r)&0xFFF8)+r;
434 else x = ((lx+r)&0xFFF8)-r+7;
435 xv = dot.xv = dot.vx = 0;
436 st |= Z_HITWALL;
438 xv -= clamp7(xv);
440 ly = y;
441 y += clamp7(yv);
442 if (yv >= 8) --y;
443 // moving up and hit the ceiling
444 if (yv < 0 && Z_hitceil(x, y)) {
445 y = ((ly-h+1)&0xFFF8)+h-1;
446 yv = dot.vy = 1;
447 dot.yv = 0;
448 st |= Z_HITCEIL;
450 if (yv > 0 && Z_canstand(x, y)) {
451 y = ((y+1)&0xFFF8)-1;
452 yv = dot.yv = dot.vy = 0;
453 st |= Z_HITLAND;
455 yv -= clamp7(yv);
458 dot.x = x;
459 dot.y = y;
461 if (Z_inwater(x, y)) {
462 st |= Z_INWATER;
463 if (!inw) st |= Z_HITWATER;
464 } else if (inw) {
465 st |= Z_HITAIR;
468 return st;
472 // ////////////////////////////////////////////////////////////////////////// //
473 enum MAX_YV = 30;
476 enum {
477 Z_HITWALL = 1<<0,
478 Z_HITCEIL = 1<<1,
479 Z_HITLAND = 1<<2,
480 Z_FALLOUT = 1<<3,
481 Z_INWATER = 1<<4,
482 Z_HITWATER = 1<<5,
483 Z_HITAIR = 1<<6,
484 Z_BLOCK = 1<<7,