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
;
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
) {
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
) {
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
) {
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 // ////////////////////////////////////////////////////////////////////////// //
96 // ////////////////////////////////////////////////////////////////////////// //
100 int x
, y
, xv
, yv
, vx
, vy
;
102 ubyte color
; // from D2D palette
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);
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) {
133 // if not found, just draw it
134 if (!found) putPixel(px, py, pclr);
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
;
146 *res
= DotParticle
.init
;
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
;
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);
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
));
183 //dt.prevColor = getPixel(nx, ny);
184 putPixel(nx
, ny
, dt.drawColor
);
187 texParts
.setFromImage(imgParts
, 0, 0);
191 // ////////////////////////////////////////////////////////////////////////// //
192 public void dotThink () {
196 while (idx
< dotsUsed
) {
200 int xv
= dt.xv
+dt.vx
;
201 int yv
= dt.yv
+dt.vy
;
203 if ((--dt.time
) == 0) {
204 dotRemove(idx
, false);
208 int st
= Z_moveobj(ref *dt);
209 if (st
&(Z_HITWATER|Z_FALLOUT
)) {
211 //if (dt.time == 255) putPixel(dt.x, dt.y, d2dpal.ptr[dt.color]);
212 dotRemove(idx
, (dt.time
== 255));
219 dt.vx
= (xv ?
Z_sign(dt.vx
) : (unsyncrandu31
&0x01 ?
-1 : 1));
220 if (unsyncrandu31
%yv
== 0) dt.vx
*= 2;
225 if (dt.time
> 4 && dt.time
!= 255) dt.time
= 4;
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
;
235 dt.yv
= (unsyncrandu31
%100 ?
-2 : 0);
238 dotRemove(idx
, false);
247 // ////////////////////////////////////////////////////////////////////////// //
248 public void dotAddBlood (int x
, int y
, int xv
, int yv
, int n
) {
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
) {
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) {
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
;
306 int Z_checkArea (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
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
;
315 bool Z_checkAreaFit (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
316 if (Z_checkArea(x
, y
, dg
)) {
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;
330 bool Z_canfit (int x
, int y
) {
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;
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;
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;
379 int Z_moveobj (ref DotParticle dot
) {
384 immutable int FLDW
= map
.width
*CELW
;
385 immutable int FLDH
= map
.height
*CELH
;
393 switch (Z_inlift(x
, y
)) {
395 if (++dot
.yv
> MAX_YV
) --dot
.yv
;
398 if (--dot
.yv
< -5) ++dot
.yv
;
401 if (dot
.yv
> 5) --dot
.yv
; else ++dot
.yv
;
406 inw
= Z_inwater(x
, y
);
408 import std
.math
: abs
;
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);
423 if (x
< -100 || x
>= FLDW
*CELW
+100 || y
< -100 || y
>= FLDH
*CELH
+100) {
431 if (!Z_canfit(x
, y
)) {
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;
443 // moving up and hit the ceiling
444 if (yv
< 0 && Z_hitceil(x
, y
)) {
445 y
= ((ly
-h
+1)&0xFFF8)+h
-1;
450 if (yv
> 0 && Z_canstand(x
, y
)) {
451 y
= ((y
+1)&0xFFF8)-1;
452 yv
= dot
.yv
= dot
.vy
= 0;
461 if (Z_inwater(x
, y
)) {
463 if (!inw
) st |
= Z_HITWATER
;
472 // ////////////////////////////////////////////////////////////////////////// //