slightly better float output
[dd2d.git] / d2dparts.d
blobe418f624b2dca0435dd683c811147faf71c2f15d
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 iv.glbinds;
22 //public enum dotsAtTexture = false;
24 import arsd.color;
26 import glutils;
27 import console;
28 import wadarc;
30 import d2dmap;
31 import d2dimage;
32 import dengapi : map, unsyncrandu31;
35 // ////////////////////////////////////////////////////////////////////////// //
36 enum BL_XV = 4;
37 enum BL_YV = 4;
38 //enum BL_MINT = 10;
39 //enum BL_MAXT = 14;
41 enum SP_V = 2;
42 enum SP_MINT = 5;
43 enum SP_MAXT = 7;
46 // ////////////////////////////////////////////////////////////////////////// //
47 struct DotParticle {
48 int prevX, prevY;
49 int lastX, lastY;
50 int x, y, xv, yv, vx, vy;
51 ubyte time;
52 Color drawColor;
54 //@disable this (this); // no copy
56 @property ulong dotHashId () pure nothrow @safe @nogc {
57 pragma(inline, true);
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 () {
84 DotParticle tmp;
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
88 tmp.x = dot.x;
89 tmp.y = dot.y;
90 tmp.xv = 0;
91 tmp.yv = 1;
92 tmp.vx = dot.vy = 0;
93 Z_moveobj(ref tmp);
94 if (tmp.x != dot.x || tmp.y != dot.y) {
95 auto nd = dotAlloc(true); // force alloc
96 if (nd !is null) *nd = dot;
99 dotsDormant.clear();
103 // ////////////////////////////////////////////////////////////////////////// //
104 public void dotInit () {
105 dotsUsed = 0;
106 dotsDormant.clear();
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);
117 // move dots
118 --dotsUsed;
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;
127 ++dotsUsed;
128 *res = DotParticle.init;
129 return res;
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;
140 dot.xv = xv;
141 dot.yv = yv;
142 dot.time = time;
143 dot.vx = dot.vy = 0;
144 dot.drawColor = d2dpal[color];
145 dot.drawColor.a = 180;
146 return dot;
150 // ////////////////////////////////////////////////////////////////////////// //
151 public void dotDraw (float itp) {
152 Color lastc;
153 if (dotsUsed == 0) return;
154 bindTexture(0);
156 auto dt = dots.ptr;
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);
160 glBegin(GL_POINTS);
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));
165 dt.lastX = nx;
166 dt.lastY = ny;
167 if (dt.drawColor != lastc) {
168 // color changed, we should start new batch
169 glEnd();
170 glColor4f(dt.drawColor.r/255.0f, dt.drawColor.g/255.0f, dt.drawColor.b/255.0f, dt.drawColor.a/255.0f);
171 glBegin(GL_POINTS);
172 lastc = dt.drawColor;
174 glVertex2i(nx, ny);
176 glEnd();
177 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
181 // ////////////////////////////////////////////////////////////////////////// //
182 public void dotThink () {
183 // now move dots
184 int idx = 0;
185 auto dt = dots.ptr;
186 while (idx < dotsUsed) {
187 if (dt.time) {
188 dt.prevX = dt.x;
189 dt.prevY = dt.y;
190 int xv = dt.xv+dt.vx;
191 int yv = dt.yv+dt.vy;
192 if (dt.time < 254) {
193 if ((--dt.time) == 0) {
194 dotRemove(idx, false);
195 continue;
198 int st = Z_moveobj(ref *dt);
199 if (st&(Z_HITWATER|Z_FALLOUT)) {
200 dotRemove(idx, (dt.time == 255)); // blood should stay
201 dt.time = 0;
202 continue;
204 if (st&Z_HITLAND) {
205 if (!dt.xv) {
206 if (yv > 2) {
207 dt.vx = (xv ? Z_sign(dt.vx) : (unsyncrandu31&0x01 ? -1 : 1));
208 if (unsyncrandu31%yv == 0) dt.vx *= 2;
209 dt.yv = yv-2;
212 dt.xv = 0;
213 if (dt.time > 4 && dt.time != 255) dt.time = 4;
215 if (st&Z_HITWALL) {
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;
221 if (st&Z_HITCEIL) {
222 dt.xv = 0;
223 dt.yv = (unsyncrandu31%100 ? -2 : 0);
225 } else {
226 dotRemove(idx, false);
227 continue;
229 ++idx;
230 ++dt;
235 // ////////////////////////////////////////////////////////////////////////// //
236 public void dotAddBlood (int x, int y, int xv, int yv, int n) {
237 while (n-- > 0) {
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) {
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%(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) {
264 while (n-- > 0) {
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;
290 return 0;
294 int Z_checkArea (int x, int y, scope int delegate (ubyte tt) dg) {
295 int tx = x/TileSize;
296 int ty = y/TileSize;
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;
299 return 0;
303 bool Z_checkAreaFit (int x, int y, scope int delegate (ubyte tt) dg) {
304 if (Z_checkArea(x, y, dg)) {
305 int tx = x/TileSize;
306 int ty = y/TileSize;
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;
314 return false;
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;
338 return 0;
343 // 0: not; >0: water
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;
349 return 0;
354 int Z_moveobj (ref DotParticle dot) {
355 enum r = 0;
356 enum h = 1;
357 enum CELW = TileSize;
358 enum CELH = TileSize;
359 immutable int FLDW = map.width*CELW;
360 immutable int FLDH = map.height*CELH;
361 int xv, yv, lx, ly;
362 int inw;
364 int st = 0;
365 int x = dot.x;
366 int y = dot.y;
368 switch (Z_inlift(x, y)) {
369 case 0:
370 if (++dot.yv > MAX_YV) --dot.yv;
371 break;
372 case 1:
373 if (--dot.yv < -5) ++dot.yv;
374 break;
375 case 2:
376 if (dot.yv > 5) --dot.yv; else ++dot.yv;
377 break;
378 default:
381 inw = Z_inwater(x, y);
382 if (inw != 0) {
383 import std.math : abs;
384 st |= Z_INWATER;
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);
394 xv = dot.xv+dot.vx;
395 yv = dot.yv+dot.vy;
397 while (xv || yv) {
398 if (x < -100 || x >= FLDW*CELW+100 || y < -100 || y >= FLDH*CELH+100) {
399 // out of map
400 st |= Z_FALLOUT;
403 lx = x;
404 x += clamp7(xv);
406 if (!Z_canfit(x, y)) {
407 if (xv == 0) x = lx;
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;
411 st |= Z_HITWALL;
413 xv -= clamp7(xv);
415 ly = y;
416 y += clamp7(yv);
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;
421 yv = dot.vy = 1;
422 dot.yv = 0;
423 st |= Z_HITCEIL;
425 if (yv > 0 && Z_canstand(x, y)) {
426 y = ((y+1)&0xFFF8)-1;
427 yv = dot.yv = dot.vy = 0;
428 st |= Z_HITLAND;
430 yv -= clamp7(yv);
433 dot.x = x;
434 dot.y = y;
436 if (Z_inwater(x, y)) {
437 st |= Z_INWATER;
438 if (!inw) st |= Z_HITWATER;
439 } else if (inw) {
440 st |= Z_HITAIR;
443 return st;
447 // ////////////////////////////////////////////////////////////////////////// //
448 enum MAX_YV = 30;
451 enum {
452 Z_HITWALL = 1<<0,
453 Z_HITCEIL = 1<<1,
454 Z_HITLAND = 1<<2,
455 Z_FALLOUT = 1<<3,
456 Z_INWATER = 1<<4,
457 Z_HITWATER = 1<<5,
458 Z_HITAIR = 1<<6,
459 Z_BLOCK = 1<<7,