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
;
22 //public enum dotsAtTexture = false;
32 import dengapi
: map
, unsyncrandu31
;
35 // ////////////////////////////////////////////////////////////////////////// //
46 // ////////////////////////////////////////////////////////////////////////// //
50 int x
, y
, xv
, yv
, vx
, vy
;
54 //@disable this (this); // no copy
56 @property ulong dotHashId () pure nothrow @safe @nogc {
58 return cast(ulong)(cast(uint)y
)|
((cast(ulong)(cast(uint)x
))<<32);
63 // ////////////////////////////////////////////////////////////////////////// //
64 enum AbsoluteMaxDots
= 2048; // plus "awoken"
66 __gshared DotParticle
[] dots
;
67 __gshared
uint dotsUsed
;
70 // ////////////////////////////////////////////////////////////////////////// //
71 // "dead" blood dots will stay in this AA
72 // after map modification one should call `dotsAwake()` to make 'em fall
73 DotParticle
[ulong] dotsDormant
; // hash: (y<<32)|x
76 void dotAddDormant() (auto ref DotParticle dot
) {
77 dotsDormant
[dot
.dotHashId
] = dot
; // simply overwrite it
81 // ////////////////////////////////////////////////////////////////////////// //
82 // awake all dormants dot (if they should fall)
83 public void dotsAwake () {
85 foreach (ref dot
; dotsDormant
.byValue
) {
86 if (!Z_canfit(dot
.x
, dot
.y
)) return; // this dot made dead
87 // check if we can fall
94 if (tmp
.x
!= dot
.x || tmp
.y
!= dot
.y
) {
95 auto nd
= dotAlloc(true); // force alloc
96 if (nd
!is null) *nd
= dot
;
103 // ////////////////////////////////////////////////////////////////////////// //
104 public void dotInit () {
107 if (dots
.length
< AbsoluteMaxDots
) dots
.length
= AbsoluteMaxDots
;
111 // ////////////////////////////////////////////////////////////////////////// //
112 void dotRemove (usize idx
, bool leftOnPic
) {
113 if (idx
>= dotsUsed
) return;
114 import core
.stdc
.string
: memmove
;
115 auto dt = dots
.ptr
+idx
;
116 if (leftOnPic
) dotAddDormant(*dt);
119 if (idx
< dotsUsed
) memmove(dots
.ptr
+idx
, dots
.ptr
+idx
+1, (dotsUsed
-idx
)*dots
[0].sizeof
);
123 DotParticle
* dotAlloc (bool force
=false) {
124 while (!force
&& dotsUsed
>= AbsoluteMaxDots
) dotRemove(0, false); //FIXME: make this faster
125 if (dotsUsed
>= dots
.length
) dots
.assumeSafeAppend
.length
= dotsUsed
+128;
126 auto res
= dots
.ptr
+dotsUsed
;
128 *res
= DotParticle
.init
;
133 DotParticle
* dotAdd (int x
, int y
, int xv
, int yv
, ubyte color
, ubyte time
) {
134 if (x
< 0 || y
< 0 || x
>= map
.width
*TileSize || y
>= map
.height
*TileSize
) return null;
135 if (!Z_canfit(x
, y
)) return null;
136 auto dot
= dotAlloc();
137 if (dot
is null) return null;
138 dot
.x
= dot
.prevX
= dot
.lastX
= x
;
139 dot
.y
= dot
.prevY
= dot
.lastY
= y
;
144 dot
.drawColor
= d2dpal
[color
];
145 dot
.drawColor
.a
= 180;
150 // ////////////////////////////////////////////////////////////////////////// //
151 public void dotDraw (float itp
) {
153 if (dotsUsed
== 0) return;
157 lastc
= dt.drawColor
;
158 glColor4f(dt.drawColor
.r
/255.0f, dt.drawColor
.g
/255.0f, dt.drawColor
.b
/255.0f, dt.drawColor
.a
/255.0f);
161 foreach (ref dt; dots
[0..dotsUsed
]) {
162 import core
.stdc
.math
: roundf
;
163 int nx
= cast(int)(dt.x
+roundf((dt.x
-dt.prevX
)*itp
));
164 int ny
= cast(int)(dt.y
+roundf((dt.y
-dt.prevY
)*itp
));
167 if (dt.drawColor
!= lastc
) {
168 // color changed, we should start new batch
170 glColor4f(dt.drawColor
.r
/255.0f, dt.drawColor
.g
/255.0f, dt.drawColor
.b
/255.0f, dt.drawColor
.a
/255.0f);
172 lastc
= dt.drawColor
;
177 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
181 // ////////////////////////////////////////////////////////////////////////// //
182 public void dotThink () {
186 while (idx
< dotsUsed
) {
190 int xv
= dt.xv
+dt.vx
;
191 int yv
= dt.yv
+dt.vy
;
193 if ((--dt.time
) == 0) {
194 dotRemove(idx
, false);
198 int st
= Z_moveobj(ref *dt);
199 if (st
&(Z_HITWATER|Z_FALLOUT
)) {
200 dotRemove(idx
, (dt.time
== 255)); // blood should stay
207 dt.vx
= (xv ?
Z_sign(dt.vx
) : (unsyncrandu31
&0x01 ?
-1 : 1));
208 if (unsyncrandu31
%yv
== 0) dt.vx
*= 2;
213 if (dt.time
> 4 && dt.time
!= 255) dt.time
= 4;
216 dt.vx
= Z_sign(xv
)*2;
217 dt.yv
= Z_sign(dt.yv
);
218 if (dt.yv
>= 0 && (unsyncrandu31
&0x03)) --dt.yv
;
219 if (dt.yv
>= 0 && (unsyncrandu31
&0x01)) --dt.yv
;
223 dt.yv
= (unsyncrandu31
%100 ?
-2 : 0);
226 dotRemove(idx
, false);
235 // ////////////////////////////////////////////////////////////////////////// //
236 public void dotAddBlood (int x
, int y
, int xv
, int yv
, int n
) {
238 int dx
= x
+cast(int)unsyncrandu31
%(2*2+1)-2;
239 int dy
= y
+cast(int)unsyncrandu31
%(2*2+1)-2;
240 int dxv
= cast(int)unsyncrandu31
%(BL_XV
*2+1)-BL_XV
+Z_dec(xv
, 3);
241 int dyv
= -cast(int)unsyncrandu31
%(BL_YV
)+Z_dec(yv
, 3)-3;
242 ubyte clr
= cast(ubyte)(0xB0+unsyncrandu31
%16);
243 dotAdd(dx
, dy
, dxv
, dyv
, clr
, 255);
248 public void dotAddSpark (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
%(SP_V
*2+1)-SP_V
-xv
/4;
253 int dyv
= cast(int)unsyncrandu31
%(SP_V
*2+1)-SP_V
-yv
/4;
254 ubyte clr
= cast(ubyte)(0xA0+unsyncrandu31
%6);
255 ubyte time
= cast(ubyte)(unsyncrandu31
%(SP_MAXT
-SP_MINT
+1)+SP_MINT
);
256 dotAdd(dx
, dy
, dxv
, dyv
, clr
, time
);
261 public void dotAddWater (int x
, int y
, int xv
, int yv
, int n
, int color
) {
262 static immutable ubyte[3] ct
= [0xC0, 0x70, 0xB0];
263 if (color
>= 0 && color
< 3) {
265 import std
.math
: abs
;
266 int dx
= x
+cast(int)unsyncrandu31
%(2*2+1)-2;
267 int dy
= y
+cast(int)unsyncrandu31
%(2*2+1)-2;
268 int dxv
= cast(int)unsyncrandu31
%(BL_XV
*2+1)-BL_XV
+Z_dec(xv
, 3);
269 int dyv
= -cast(int)unsyncrandu31
%(BL_YV
)-abs(yv
);
270 ubyte clr
= cast(ubyte)(unsyncrandu31
%16+ct
.ptr
[color
]);
271 dotAdd(dx
, dy
, dxv
, dyv
, clr
, 254);
277 // ////////////////////////////////////////////////////////////////////////// //
278 int clamp7 (int v
) { /*pragma(inline, true);*/ import std
.math
: abs
; return (abs(v
) <= 7 ? v
: (v
> 0 ?
7 : -7)); }
281 int Z_sign (int n
) { pragma(inline
, true); return (n
< 0 ?
-1 : (n
> 0 ?
1 : 0)); }
284 int Z_dec (int n
, int delta
) {
285 import std
.math
: abs
;
286 if (abs(n
) > delta
) {
287 if (n
> 0) return n
-delta
;
288 if (n
< 0) return n
+delta
;
294 int Z_checkArea (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
297 if (tx
< 0 || ty
< 0 || tx
>= map
.width || ty
>= map
.height
) return 0;
298 if (auto res
= dg(map
.tiles
.ptr
[LevelMap
.Type
].ptr
[ty
*map
.width
+tx
])) return res
;
303 bool Z_checkAreaFit (int x
, int y
, scope int delegate (ubyte tt
) dg
) {
304 if (Z_checkArea(x
, y
, dg
)) {
307 auto fgt
= map
.tiles
.ptr
[LevelMap
.Front
].ptr
[ty
*map
.width
+tx
];
308 if (fgt
>= map
.walltypes
.length
) return false;
309 if (map
.walltypes
[fgt
]) return true;
310 auto bgt
= map
.tiles
.ptr
[LevelMap
.Back
].ptr
[ty
*map
.width
+tx
];
311 if (bgt
>= map
.walltypes
.length
) return false;
312 if (map
.walltypes
[bgt
]&0x02) return true;
318 bool Z_canfit (int x
, int y
) {
319 return !Z_checkAreaFit(x
, y
, (tt
) => (tt
== LevelMap
.TILE_WALL || tt
== LevelMap
.TILE_DOORC ?
1 : 0));
323 bool Z_hitceil (int x
, int y
) {
324 return Z_checkAreaFit(x
, y
, (tt
) => (tt
== LevelMap
.TILE_WALL || tt
== LevelMap
.TILE_DOORC ?
1 : 0));
328 bool Z_canstand (int x
, int y
) {
329 return Z_checkAreaFit(x
, y
, (tt
) => (tt
== LevelMap
.TILE_WALL || tt
== LevelMap
.TILE_DOORC || tt
== LevelMap
.TILE_STEP ?
1 : 0));
333 // 0: not; 1: up; 2: down
334 int Z_inlift (int x
, int y
) {
335 return Z_checkArea(x
, y
, (tt
) {
336 if (tt
== LevelMap
.TILE_LIFTU
) return 1;
337 if (tt
== LevelMap
.TILE_LIFTD
) return 2;
344 int Z_inwater (int x
, int y
) {
345 return Z_checkArea(x
, y
, (tt
) {
346 if (tt
== LevelMap
.TILE_WATER
) return 1;
347 if (tt
== LevelMap
.TILE_ACID1
) return 2;
348 if (tt
== LevelMap
.TILE_ACID2
) return 3;
354 int Z_moveobj (ref DotParticle dot
) {
357 enum CELW
= TileSize
;
358 enum CELH
= TileSize
;
359 immutable int FLDW
= map
.width
*CELW
;
360 immutable int FLDH
= map
.height
*CELH
;
368 switch (Z_inlift(x
, y
)) {
370 if (++dot
.yv
> MAX_YV
) --dot
.yv
;
373 if (--dot
.yv
< -5) ++dot
.yv
;
376 if (dot
.yv
> 5) --dot
.yv
; else ++dot
.yv
;
381 inw
= Z_inwater(x
, y
);
383 import std
.math
: abs
;
385 if ((xv
= abs(dot
.xv
)+1) > 5) dot
.xv
= Z_dec(dot
.xv
, xv
/2-2);
386 if ((xv
= abs(dot
.yv
)+1) > 5) dot
.yv
= Z_dec(dot
.yv
, xv
/2-2);
387 if ((xv
= abs(dot
.vx
)+1) > 5) dot
.vx
= Z_dec(dot
.vx
, xv
/2-2);
388 if ((xv
= abs(dot
.vy
)+1) > 5) dot
.vy
= Z_dec(dot
.vy
, xv
/2-2);
391 dot
.vx
= Z_dec(dot
.vx
, 1);
392 dot
.vy
= Z_dec(dot
.vy
, 1);
398 if (x
< -100 || x
>= FLDW
*CELW
+100 || y
< -100 || y
>= FLDH
*CELH
+100) {
406 if (!Z_canfit(x
, y
)) {
408 else if (xv
< 0) x
= ((lx
-r
)&0xFFF8)+r
;
409 else x
= ((lx
+r
)&0xFFF8)-r
+7;
410 xv
= dot
.xv
= dot
.vx
= 0;
417 if (yv
>= TileSize
) --y
;
418 // moving up and hit the ceiling
419 if (yv
< 0 && Z_hitceil(x
, y
)) {
420 y
= ((ly
-h
+1)&0xFFF8)+h
-1;
425 if (yv
> 0 && Z_canstand(x
, y
)) {
426 y
= ((y
+1)&0xFFF8)-1;
427 yv
= dot
.yv
= dot
.vy
= 0;
436 if (Z_inwater(x
, y
)) {
438 if (!inw
) st |
= Z_HITWATER
;
447 // ////////////////////////////////////////////////////////////////////////// //