some mode useless code
[dd2d.git] / d2dparts.d
blobddca38e89813ad34a95ac2ddb3de265eb58a969c
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 version = ogl_dots;
23 version(ogl_dots) {
24 import iv.glbinds;
25 public enum dotsAtTexture = false;
26 } else {
27 public enum dotsAtTexture = true;
30 import arsd.color;
32 import glutils;
33 import console;
34 import wadarc;
36 import d2dmap;
37 import d2dgfx;
38 import dengapi : map, unsyncrandu31;
41 // ////////////////////////////////////////////////////////////////////////// //
42 // "dead" blood dots will stay on image
43 //TODO: "dormant" blood should fall down if map was modified
46 public void dotInit () {
47 imgParts = new TrueColorImage(map.width*TileSize, map.height*TileSize);
48 imgPartsOld = new TrueColorImage(map.width*TileSize, map.height*TileSize);
49 foreach (int y; 0..map.height*TileSize) {
50 foreach (int x; 0..map.width*TileSize) {
51 putPixel(x, y, Color(0, 0, 0, 0));
52 putPixelOld(x, y, Color(0, 0, 0, 0));
55 texParts = new Texture(imgParts, Texture.Option.Nearest, Texture.Option.Clamp);
57 foreach (int y; 0..map.height*TileSize) {
58 foreach (int x; 0..map.width*TileSize) {
59 putPixel(x, y, Color(0, 200, 0, 255));
63 //texParts.setFromImage(imgParts, 0, 0);
67 // ////////////////////////////////////////////////////////////////////////// //
68 __gshared public Texture texParts; // texture for particles; can be blended on bg
69 __gshared TrueColorImage imgParts, imgPartsOld;
72 void putPixelOld (int x, int y, Color clr) {
73 pragma(inline, true);
74 if (/*clr.a != 0 &&*/ x >= 0 && y >= 0 && x < imgPartsOld.width && y < imgPartsOld.height) {
75 imgPartsOld.imageData.colors.ptr[y*imgPartsOld.width+x] = clr;
80 void putPixel (int x, int y, Color clr) {
81 pragma(inline, true);
82 if (/*clr.a != 0 &&*/ x >= 0 && y >= 0 && x < imgParts.width && y < imgParts.height) {
83 imgParts.imageData.colors.ptr[y*imgParts.width+x] = clr;
88 Color getPixel (int x, int y) {
89 pragma(inline, true);
90 return (x >= 0 && y >= 0 && x < imgParts.width && y < imgParts.height ? imgParts.imageData.colors.ptr[y*imgParts.width+x] : Color(0, 0, 0, 0));
94 // ////////////////////////////////////////////////////////////////////////// //
95 enum BL_XV = 4;
96 enum BL_YV = 4;
97 //enum BL_MINT = 10;
98 //enum BL_MAXT = 14;
100 enum SP_V = 2;
101 enum SP_MINT = 5;
102 enum SP_MAXT = 7;
105 // ////////////////////////////////////////////////////////////////////////// //
106 struct DotParticle {
107 int prevX, prevY;
108 int lastX, lastY;
109 int x, y, xv, yv, vx, vy;
110 ubyte time;
111 ubyte color; // from D2D palette
112 Color drawColor;
113 //Color prevColor; // on image; a=0: was empty
115 @disable this (this); // no copy
119 // ////////////////////////////////////////////////////////////////////////// //
120 __gshared DotParticle[1024] dots;
121 __gshared uint dotsUsed;
124 void dotRemove (usize idx, bool leftOnPic) {
125 if (idx >= dotsUsed) return;
126 import core.stdc.string : memmove;
127 auto dt = dots.ptr+idx;
128 if (leftOnPic) putPixelOld(dt.x, dt.y, dt.drawColor);
130 auto pclr = (leftOnPic ? dt.drawColor : dt.prevColor);
131 bool found = false;
132 int px = (leftOnPic ? dt.x : dt.lastX);
133 int py = (leftOnPic ? dt.y : dt.lastY);
134 // if it has color, reassign this color to next dot at this coords
135 foreach (ref dp; dots[0..dotsUsed]) {
136 if (dp.lastX == px && dp.lastY == py) {
137 dp.prevColor = pclr;
138 found = true;
139 break;
142 // if not found, just draw it
143 if (!found) putPixel(px, py, pclr);
145 // move dots
146 --dotsUsed;
147 if (idx < dotsUsed) memmove(dots.ptr+idx, dots.ptr+idx+1, (dotsUsed-idx)*dots[0].sizeof);
151 DotParticle* dotAlloc () {
152 if (dotsUsed == dots.length) dotRemove(0, false);
153 auto res = dots.ptr+dotsUsed;
154 ++dotsUsed;
155 *res = DotParticle.init;
156 return res;
160 void dotAdd (int x, int y, int xv, int yv, ubyte color, ubyte time) {
161 //conwriteln("dotAdd: x=", x, "; y=", y, "; xv=", xv, "; yv=", yv, "; color=", color, "; time=", time);
162 if (x < 0 || y < 0 || x >= map.width*TileSize || y >= map.height*TileSize) return;
163 if (!Z_canfit(x, y)) return;
164 auto dot = dotAlloc();
165 if (dot is null) return;
166 dot.x = dot.prevX = dot.lastX = x;
167 dot.y = dot.prevY = dot.lastY = y;
168 dot.xv = xv;
169 dot.yv = yv;
170 dot.color = color;
171 dot.time = time;
172 dot.vx = dot.vy = 0;
173 //dot.prevColor = getPixel(x, y);
174 dot.drawColor = d2dpal[color];
175 dot.drawColor.a = 180;
179 // ////////////////////////////////////////////////////////////////////////// //
180 public void dotDraw (float itp) {
181 version(ogl_dots) {
182 Color lastc;
184 if (dotsUsed == 0) return;
185 glBindTexture(GL_TEXTURE_2D, 0);
186 auto dt = dots.ptr;
187 lastc = dt.drawColor;
188 glColor4f(dt.drawColor.r/255.0f, dt.drawColor.g/255.0f, dt.drawColor.b/255.0f, dt.drawColor.a/255.0f);
189 glBegin(GL_POINTS);
192 import core.stdc.math : roundf;
193 // remove dots from image (in reverse order)
194 //foreach_reverse (ref dt; dots[0..dotsUsed]) putPixel(dt.lastX, dt.lastY, dt.prevColor);
195 imgParts.imageData.colors[] = imgPartsOld.imageData.colors[];
196 //imgParts.imageData.colors[] = Color(0, 0, 0, 0);
197 // draw dots
198 foreach (ref dt; dots[0..dotsUsed]) {
199 int nx = cast(int)(dt.x+roundf((dt.x-dt.prevX)*itp));
200 int ny = cast(int)(dt.y+roundf((dt.y-dt.prevY)*itp));
201 dt.lastX = nx;
202 dt.lastY = ny;
203 //dt.prevColor = getPixel(nx, ny);
204 version(ogl_dots) {
205 if (dt.drawColor != lastc) {
206 glEnd();
207 glColor4f(dt.drawColor.r/255.0f, dt.drawColor.g/255.0f, dt.drawColor.b/255.0f, dt.drawColor.a/255.0f);
208 glBegin(GL_POINTS);
209 lastc = dt.drawColor;
211 glVertex2i(nx, ny);
212 } else {
213 putPixel(nx, ny, dt.drawColor);
216 version(ogl_dots) {
217 glEnd();
218 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
219 } else {
220 // update texture
221 texParts.setFromImage(imgParts, 0, 0);
226 // ////////////////////////////////////////////////////////////////////////// //
227 public void dotThink () {
228 // now move dots
229 int idx = 0;
230 auto dt = dots.ptr;
231 while (idx < dotsUsed) {
232 if (dt.time) {
233 dt.prevX = dt.x;
234 dt.prevY = dt.y;
235 int xv = dt.xv+dt.vx;
236 int yv = dt.yv+dt.vy;
237 if (dt.time < 254) {
238 if ((--dt.time) == 0) {
239 dotRemove(idx, false);
240 continue;
243 int st = Z_moveobj(ref *dt);
244 if (st&(Z_HITWATER|Z_FALLOUT)) {
245 // blood?
246 //if (dt.time == 255) putPixel(dt.x, dt.y, d2dpal.ptr[dt.color]);
247 dotRemove(idx, (dt.time == 255));
248 dt.time = 0;
249 continue;
251 if (st&Z_HITLAND) {
252 if (!dt.xv) {
253 if (yv > 2) {
254 dt.vx = (xv ? Z_sign(dt.vx) : (unsyncrandu31&0x01 ? -1 : 1));
255 if (unsyncrandu31%yv == 0) dt.vx *= 2;
256 dt.yv = yv-2;
259 dt.xv = 0;
260 if (dt.time > 4 && dt.time != 255) dt.time = 4;
262 if (st&Z_HITWALL) {
263 dt.vx = Z_sign(xv)*2;
264 dt.yv = Z_sign(dt.yv);
265 if (dt.yv >= 0 && (unsyncrandu31&0x03)) --dt.yv;
266 if (dt.yv >= 0 && (unsyncrandu31&0x01)) --dt.yv;
268 if (st&Z_HITCEIL) {
269 dt.xv = 0;
270 dt.yv = (unsyncrandu31%100 ? -2 : 0);
272 } else {
273 dotRemove(idx, false);
274 continue;
276 ++idx;
277 ++dt;
282 // ////////////////////////////////////////////////////////////////////////// //
283 public void dotAddBlood (int x, int y, int xv, int yv, int n) {
284 while (n-- > 0) {
285 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
286 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
287 int dxv = cast(int)unsyncrandu31%(BL_XV*2+1)-BL_XV+Z_dec(xv, 3);
288 int dyv = -cast(int)unsyncrandu31%(BL_YV)+Z_dec(yv, 3)-3;
289 ubyte clr = cast(ubyte)(0xB0+unsyncrandu31%16);
290 dotAdd(dx, dy, dxv, dyv, clr, 255);
295 public void dotAddSpark (int x, int y, int xv, int yv, int n) {
296 while (n-- > 0) {
297 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
298 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
299 int dxv = cast(int)unsyncrandu31%(SP_V*2+1)-SP_V-xv/4;
300 int dyv = cast(int)unsyncrandu31%(SP_V*2+1)-SP_V-yv/4;
301 ubyte clr = cast(ubyte)(0xA0+unsyncrandu31%6);
302 ubyte time = cast(ubyte)(unsyncrandu31%(SP_MAXT-SP_MINT+1)+SP_MINT);
303 dotAdd(dx, dy, dxv, dyv, clr, time);
308 public void dotAddWater (int x, int y, int xv, int yv, int n, int color) {
309 static immutable ubyte[3] ct = [0xC0, 0x70, 0xB0];
310 if (color >= 0 && color < 3) {
311 while (n-- > 0) {
312 import std.math : abs;
313 int dx = x+cast(int)unsyncrandu31%(2*2+1)-2;
314 int dy = y+cast(int)unsyncrandu31%(2*2+1)-2;
315 int dxv = cast(int)unsyncrandu31%(BL_XV*2+1)-BL_XV+Z_dec(xv, 3);
316 int dyv = -cast(int)unsyncrandu31%(BL_YV)-abs(yv);
317 ubyte clr = cast(ubyte)(unsyncrandu31%16+ct.ptr[color]);
318 dotAdd(dx, dy, dxv, dyv, clr, 254);
324 // ////////////////////////////////////////////////////////////////////////// //
325 int clamp7 (int v) { /*pragma(inline, true);*/ import std.math : abs; return (abs(v) <= 7 ? v : (v > 0 ? 7 : -7)); }
328 int Z_sign (int n) { pragma(inline, true); return (n < 0 ? -1 : (n > 0 ? 1 : 0)); }
331 int Z_dec (int n, int delta) {
332 import std.math : abs;
333 if (abs(n) > delta) {
334 if (n > 0) return n-delta;
335 if (n < 0) return n+delta;
337 return 0;
341 int Z_checkArea (int x, int y, scope int delegate (ubyte tt) dg) {
342 int tx = x/TileSize;
343 int ty = y/TileSize;
344 if (tx < 0 || ty < 0 || tx >= map.width || ty >= map.height) return 0;
345 if (auto res = dg(map.tiles.ptr[LevelMap.Type].ptr[ty*map.width+tx])) return res;
346 return 0;
350 bool Z_checkAreaFit (int x, int y, scope int delegate (ubyte tt) dg) {
351 if (Z_checkArea(x, y, dg)) {
352 int tx = x/TileSize;
353 int ty = y/TileSize;
354 auto fgt = map.tiles.ptr[LevelMap.Front].ptr[ty*map.width+tx];
355 if (fgt >= map.walltypes.length) return false;
356 if (map.walltypes[fgt]) return true;
357 auto bgt = map.tiles.ptr[LevelMap.Back].ptr[ty*map.width+tx];
358 if (bgt >= map.walltypes.length) return false;
359 if (map.walltypes[bgt]&0x02) return true;
361 return false;
365 bool Z_canfit (int x, int y) {
367 int tx = x/TileSize;
368 int ty = y/TileSize;
369 if (tx < 0 || ty < 0 || tx >= map.width || ty >= map.height) return true;
370 auto tt = map.tiles.ptr[LevelMap.Type].ptr[ty*map.width+tx];
371 if (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC) {
372 fgt = map.tiles.ptr[LevelMap.Front].ptr[ty*map.width+tx];
373 if (map.walltype[fgt]) return false;
374 bgt = map.tiles.ptr[LevelMap.Back].ptr[ty*map.width+tx];
375 if (map.walltype[bgt]&0x02) return false;
377 return true;
379 return !Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC ? 1 : 0));
383 bool Z_hitceil (int x, int y) {
384 return Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC ? 1 : 0));
388 bool Z_canstand (int x, int y) {
389 return Z_checkAreaFit(x, y, (tt) => (tt == LevelMap.TILE_WALL || tt == LevelMap.TILE_DOORC || tt == LevelMap.TILE_STEP ? 1 : 0));
393 // 0: not; 1: up; 2: down
394 int Z_inlift (int x, int y) {
395 return Z_checkArea(x, y, (tt) {
396 if (tt == LevelMap.TILE_LIFTU) return 1;
397 if (tt == LevelMap.TILE_LIFTD) return 2;
398 return 0;
403 // 0: not; >0: water
404 int Z_inwater (int x, int y) {
405 return Z_checkArea(x, y, (tt) {
406 if (tt == LevelMap.TILE_WATER) return 1;
407 if (tt == LevelMap.TILE_ACID1) return 2;
408 if (tt == LevelMap.TILE_ACID2) return 3;
409 return 0;
414 int Z_moveobj (ref DotParticle dot) {
415 enum r = 0;
416 enum h = 1;
417 enum CELW = TileSize;
418 enum CELH = TileSize;
419 immutable int FLDW = map.width*CELW;
420 immutable int FLDH = map.height*CELH;
421 int xv, yv, lx, ly;
422 int inw;
424 int st = 0;
425 int x = dot.x;
426 int y = dot.y;
428 switch (Z_inlift(x, y)) {
429 case 0:
430 if (++dot.yv > MAX_YV) --dot.yv;
431 break;
432 case 1:
433 if (--dot.yv < -5) ++dot.yv;
434 break;
435 case 2:
436 if (dot.yv > 5) --dot.yv; else ++dot.yv;
437 break;
438 default:
441 inw = Z_inwater(x, y);
442 if (inw != 0) {
443 import std.math : abs;
444 st |= Z_INWATER;
445 if ((xv = abs(dot.xv)+1) > 5) dot.xv = Z_dec(dot.xv, xv/2-2);
446 if ((xv = abs(dot.yv)+1) > 5) dot.yv = Z_dec(dot.yv, xv/2-2);
447 if ((xv = abs(dot.vx)+1) > 5) dot.vx = Z_dec(dot.vx, xv/2-2);
448 if ((xv = abs(dot.vy)+1) > 5) dot.vy = Z_dec(dot.vy, xv/2-2);
451 dot.vx = Z_dec(dot.vx, 1);
452 dot.vy = Z_dec(dot.vy, 1);
454 xv = dot.xv+dot.vx;
455 yv = dot.yv+dot.vy;
457 while (xv || yv) {
458 if (x < -100 || x >= FLDW*CELW+100 || y < -100 || y >= FLDH*CELH+100) {
459 // out of map
460 st |= Z_FALLOUT;
463 lx = x;
464 x += clamp7(xv);
466 if (!Z_canfit(x, y)) {
467 if (xv == 0) x = lx;
468 else if (xv < 0) x = ((lx-r)&0xFFF8)+r;
469 else x = ((lx+r)&0xFFF8)-r+7;
470 xv = dot.xv = dot.vx = 0;
471 st |= Z_HITWALL;
473 xv -= clamp7(xv);
475 ly = y;
476 y += clamp7(yv);
477 if (yv >= TileSize) --y;
478 // moving up and hit the ceiling
479 if (yv < 0 && Z_hitceil(x, y)) {
480 y = ((ly-h+1)&0xFFF8)+h-1;
481 yv = dot.vy = 1;
482 dot.yv = 0;
483 st |= Z_HITCEIL;
485 if (yv > 0 && Z_canstand(x, y)) {
486 y = ((y+1)&0xFFF8)-1;
487 yv = dot.yv = dot.vy = 0;
488 st |= Z_HITLAND;
490 yv -= clamp7(yv);
493 dot.x = x;
494 dot.y = y;
496 if (Z_inwater(x, y)) {
497 st |= Z_INWATER;
498 if (!inw) st |= Z_HITWATER;
499 } else if (inw) {
500 st |= Z_HITAIR;
503 return st;
507 // ////////////////////////////////////////////////////////////////////////// //
508 enum MAX_YV = 30;
511 enum {
512 Z_HITWALL = 1<<0,
513 Z_HITCEIL = 1<<1,
514 Z_HITLAND = 1<<2,
515 Z_FALLOUT = 1<<3,
516 Z_INWATER = 1<<4,
517 Z_HITWATER = 1<<5,
518 Z_HITAIR = 1<<6,
519 Z_BLOCK = 1<<7,