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
;
25 public enum dotsAtTexture
= false;
27 public enum dotsAtTexture
= true;
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
) {
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
) {
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
) {
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 // ////////////////////////////////////////////////////////////////////////// //
105 // ////////////////////////////////////////////////////////////////////////// //
109 int x
, y
, xv
, yv
, vx
, vy
;
111 ubyte color
; // from D2D palette
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);
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) {
142 // if not found, just draw it
143 if (!found) putPixel(px, py, pclr);
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
;
155 *res
= DotParticle
.init
;
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
;
173 //dot.prevColor = getPixel(x, y);
174 dot
.drawColor
= d2dpal
[color
];
175 dot
.drawColor
.a
= 180;
179 // ////////////////////////////////////////////////////////////////////////// //
180 public void dotDraw (float itp
) {
184 if (dotsUsed
== 0) return;
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);
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);
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
));
203 //dt.prevColor = getPixel(nx, ny);
205 if (dt.drawColor
!= lastc
) {
207 glColor4f(dt.drawColor
.r
/255.0f, dt.drawColor
.g
/255.0f, dt.drawColor
.b
/255.0f, dt.drawColor
.a
/255.0f);
209 lastc
= dt.drawColor
;
213 putPixel(nx
, ny
, dt.drawColor
);
218 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
221 texParts
.setFromImage(imgParts
, 0, 0);
226 // ////////////////////////////////////////////////////////////////////////// //
227 public void dotThink () {
231 while (idx
< dotsUsed
) {
235 int xv
= dt.xv
+dt.vx
;
236 int yv
= dt.yv
+dt.vy
;
238 if ((--dt.time
) == 0) {
239 dotRemove(idx
, false);
243 int st
= Z_moveobj(ref *dt);
244 if (st
&(Z_HITWATER|Z_FALLOUT
)) {
246 //if (dt.time == 255) putPixel(dt.x, dt.y, d2dpal.ptr[dt.color]);
247 dotRemove(idx
, (dt.time
== 255));
254 dt.vx
= (xv ?
Z_sign(dt.vx
) : (unsyncrandu31
&0x01 ?
-1 : 1));
255 if (unsyncrandu31
%yv
== 0) dt.vx
*= 2;
260 if (dt.time
> 4 && dt.time
!= 255) dt.time
= 4;
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
;
270 dt.yv
= (unsyncrandu31
%100 ?
-2 : 0);
273 dotRemove(idx
, false);
282 // ////////////////////////////////////////////////////////////////////////// //
283 public void dotAddBlood (int x
, int y
, int xv
, int yv
, int n
) {
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
) {
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) {
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
;
341 int Z_checkArea (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
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
;
350 bool Z_checkAreaFit (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
351 if (Z_checkArea(x
, y
, dg
)) {
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;
365 bool Z_canfit (int x
, int y
) {
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;
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;
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;
414 int Z_moveobj (ref DotParticle dot
) {
417 enum CELW
= TileSize
;
418 enum CELH
= TileSize
;
419 immutable int FLDW
= map
.width
*CELW
;
420 immutable int FLDH
= map
.height
*CELH
;
428 switch (Z_inlift(x
, y
)) {
430 if (++dot
.yv
> MAX_YV
) --dot
.yv
;
433 if (--dot
.yv
< -5) ++dot
.yv
;
436 if (dot
.yv
> 5) --dot
.yv
; else ++dot
.yv
;
441 inw
= Z_inwater(x
, y
);
443 import std
.math
: abs
;
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);
458 if (x
< -100 || x
>= FLDW
*CELW
+100 || y
< -100 || y
>= FLDH
*CELH
+100) {
466 if (!Z_canfit(x
, y
)) {
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;
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;
485 if (yv
> 0 && Z_canstand(x
, y
)) {
486 y
= ((y
+1)&0xFFF8)-1;
487 yv
= dot
.yv
= dot
.vy
= 0;
496 if (Z_inwater(x
, y
)) {
498 if (!inw
) st |
= Z_HITWATER
;
507 // ////////////////////////////////////////////////////////////////////////// //