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 d2dadefs
is aliced
;
27 //version = tatlas_dump;
30 // ////////////////////////////////////////////////////////////////////////// //
31 private enum BIT(ubyte n
) = (1U<<n
);
34 // ////////////////////////////////////////////////////////////////////////// //
37 AF_NOCOLLISION
= BIT
!(0),
38 AF_NOGRAVITY
= BIT
!(1),
40 AF_NOONTOUCH
= BIT
!(3), // `onTouch` will never be called with `me` for this actor
41 AF_NODRAW
= BIT
!(4), // don't draw sprite
42 AF_NOLIGHT
= BIT
!(5), // no attached light
43 AF_NOANIMATE
= BIT
!(6), // don't do animation
47 // ////////////////////////////////////////////////////////////////////////// //
48 // known D2D actors (and pseudoactors)
52 ushort mapid
; // thing id in map
54 this (string ctype
, string cname
, ushort thid
=0) {
55 classtype
= StrPool
.intern(ctype
);
56 classname
= StrPool
.intern(cname
);
60 string
toString () const {
61 import std
.string
: format
;
62 return "ActorDefD2D(%s, %s, %s)".format(classtype
.get
, classname
.get
, mapid
);
66 immutable ActorDefD2D
[] d2dactordefs
;
67 public immutable ActorDefD2D
[ushort] d2dactordefsById
; // by mapid
69 shared static this () {
71 ActorDefD2D("playerstart", "Player1", 1),
72 ActorDefD2D("playerstart", "Player2", 2),
73 ActorDefD2D("playerstart", "DMStart", 3),
74 ActorDefD2D("item", "Clip", 100),
75 ActorDefD2D("item", "Shell", 101),
76 ActorDefD2D("item", "Rocket", 102),
77 ActorDefD2D("item", "Cell", 103),
78 ActorDefD2D("item", "Ammo", 104),
79 ActorDefD2D("item", "ShellBox", 105),
80 ActorDefD2D("item", "RocketBox", 106),
81 ActorDefD2D("item", "CellPack", 107),
82 ActorDefD2D("item", "StimPack", 108),
83 ActorDefD2D("item", "MediKit", 109),
84 ActorDefD2D("item", "BackPack", 110),
85 ActorDefD2D("item", "Chainsaw", 111),
86 ActorDefD2D("item", "Shotgun", 112),
87 ActorDefD2D("item", "SuperShotgun", 113),
88 ActorDefD2D("item", "MachineGun", 114),
89 ActorDefD2D("item", "RocketLauncher", 115),
90 ActorDefD2D("item", "Plasmagun", 116),
91 ActorDefD2D("item", "BFG9000", 117),
92 ActorDefD2D("item", "ArmorGreen", 118),
93 ActorDefD2D("item", "ArmorBlue", 119),
94 ActorDefD2D("item", "MegaSphere", 120),
95 ActorDefD2D("item", "Invulnerability", 121),
96 ActorDefD2D("item", "Aqualung", 122),
97 ActorDefD2D("item", "KeyRed", 123),
98 ActorDefD2D("item", "KeyGreen", 124),
99 ActorDefD2D("item", "KeyBlue", 125),
100 ActorDefD2D("item", "ProtectionSuit", 126),
101 ActorDefD2D("item", "Super", 127),
102 ActorDefD2D("item", "TorchRed", 128),
103 ActorDefD2D("item", "TorchGreen", 129),
104 ActorDefD2D("item", "TorchBlue", 130),
105 ActorDefD2D("item", "Gor1", 131),
106 ActorDefD2D("item", "FCan", 132),
107 ActorDefD2D("item", "Gun2", 133),
108 ActorDefD2D("monster", "Demon", 200),
109 ActorDefD2D("monster", "Imp", 201),
110 ActorDefD2D("monster", "Zombie", 202),
111 ActorDefD2D("monster", "Sergeant", 203),
112 ActorDefD2D("monster", "Cyberdemon", 204),
113 ActorDefD2D("monster", "Chaingunner", 205),
114 ActorDefD2D("monster", "BaronOfHell", 206),
115 ActorDefD2D("monster", "HellKnight", 207),
116 ActorDefD2D("monster", "Cacodemon", 208),
117 ActorDefD2D("monster", "LostSoul", 209),
118 ActorDefD2D("monster", "PainElemental", 210),
119 ActorDefD2D("monster", "SpiderMastermind", 211),
120 ActorDefD2D("monster", "Arachnotron", 212),
121 ActorDefD2D("monster", "Mancubus", 213),
122 ActorDefD2D("monster", "Revenant", 214),
123 ActorDefD2D("monster", "Archvile", 215),
124 ActorDefD2D("monster", "Fish", 216),
125 ActorDefD2D("monster", "Barrel", 217),
126 ActorDefD2D("monster", "Robot", 218),
127 ActorDefD2D("monster", "Man", 219),
129 foreach (const ref ActorDefD2D d2da
; d2dactordefs
) {
130 if (d2da
.mapid
) d2dactordefsById
[d2da
.mapid
] = d2da
;
135 // ////////////////////////////////////////////////////////////////////////// //
136 public string
getD2DSwitchClassName (uint swtype
) {
138 case 1: return "Exit"; // SW_EXIT
139 case 2: return "ExitSecret"; // SW_EXITS
140 case 3: return "DoorOpen"; // SW_OPENDOOR
141 case 4: return "DoorClose"; // SW_SHUTDOOR
142 case 5: return "TrapClose"; // SW_SHUTTRAP
143 case 6: return "Door"; // SW_DOOR
144 case 7: return "Door5"; // SW_DOOR5
145 case 8: return "Press"; // SW_PRESS
146 case 9: return "Teleport"; // SW_TELE
147 case 10: return "Secret"; // SW_SECRET
148 case 11: return "LiftUp"; // SW_LIFTUP
149 case 12: return "LiftDown"; // SW_LIFTDOWN
150 case 13: return "Trap"; // SW_TRAP
151 case 14: return "Lift"; // SW_LIFT
152 case 64: return "WinGame"; // SW_WINGAME
159 // ////////////////////////////////////////////////////////////////////////// //
161 __gshared TexAtlas
[] atlases
;
162 __gshared ImgSprite
[string
] sprImages
;
165 public void realiseSpriteAtlases () {
166 foreach (immutable idx
, TexAtlas a
; atlases
) {
168 import std
.string
: format
;
170 version(tatlas_dump
) writePng("_za%02s.png".format(idx
), a
.img
);
175 public struct ImgSprite
{
179 float tx0
, tx1
, ty0
, ty1
;
183 //@disable this (this);
186 if (atlas
!is null) return;
189 foreach (TexAtlas a
; atlases
) {
190 auto rc
= a
.insert(vga
);
191 if (rc
.valid
) { arc
= rc
; aa
= a
; break; }
194 int w
= (vga
.width
< 1024 ?
1024 : vga
.width
);
195 int h
= (vga
.height
< 1024 ?
1024 : vga
.height
);
196 aa
= new TexAtlas(w
, h
);
197 arc
= aa
.insert(vga
);
203 tx0
= cast(float)arc
.x
/cast(float)(aa
.width
);
204 ty0
= cast(float)arc
.y
/cast(float)(aa
.height
);
205 tx1
= cast(float)(arc
.x
+arc
.w
)/cast(float)(aa
.width
);
206 ty1
= cast(float)(arc
.y
+arc
.h
)/cast(float)(aa
.height
);
209 void drawAtXY (int x
, int y
, bool mirrorX
=false, bool mirrorY
=false) {
211 if (atlas
is null ||
!atlas
.hasTexture
) return;
215 int h
= y
+vga
.height
;
216 if (mirrored
) mirrorX
= !mirrorX
;
217 if (mirrorX
) { int tmp
= x
; x
= w
; w
= tmp
; }
218 if (mirrorY
) { int tmp
= y
; y
= h
; h
= tmp
; }
219 glBindTexture(GL_TEXTURE_2D
, atlas
.tex
.tid
);
221 glTexCoord2f(tx0
, ty0
); glVertex2i(x
, y
); // top-left
222 glTexCoord2f(tx1
, ty0
); glVertex2i(w
, y
); // top-right
223 glTexCoord2f(tx1
, ty1
); glVertex2i(w
, h
); // bottom-right
224 glTexCoord2f(tx0
, ty1
); glVertex2i(x
, h
); // bottom-left
230 // ////////////////////////////////////////////////////////////////////////// //
231 ImgSprite
* loadSprite (string spnamef
, bool checkMirror
=true) {
232 if (spnamef
.length
== 0) return null;
233 import std
.algorithm
: endsWith
;
234 if (auto im
= spnamef
in sprImages
) {
235 //conwriteln("cached sprite '", spnamef, "'");
238 string spname
= spnamef
;
239 bool mirrored
= false;
240 if (checkMirror
&& spname
.endsWith("_mirrored.vga")) {
242 spname
= spname
[0..$-13]~".vga";
244 // for mirrored, load normal sprite and create mirrored one
246 auto im
= spname
in sprImages
;
247 if (im
is null) im
= loadSprite(spname
, false);
248 if (im
is null) return null;
249 auto imm
= new ImgSprite();
252 sprImages
[spnamef
] = *imm
;
256 auto im
= new ImgSprite();
257 im
.vga
= new D2DImage(spname
);
259 sprImages
[spnamef
] = *im
;
261 return spnamef
in sprImages
;
265 // ////////////////////////////////////////////////////////////////////////// //
266 public final class ActorDef
{
270 // animation sequences for stated; keyed by state strid
271 StrId
[][][uint] anims
; // [statestrid][dir][pos]
273 FuncPool
.FuncInfo
[] fiAnimInit
;
274 FuncPool
.FuncInfo fiInit
;
275 FuncPool
.FuncInfo fiThink
;
277 ImgSprite
*[][][uint] animSprites
;
278 //__gshared ImgSprite*[][] animCur;
280 // load actor graphics
281 void loadGraphics () {
282 //conwriteln("loading graphics for '", fullname, "'; anims.length=", anims.length");
283 if (anims
.length
== 0) return; // no graphics here
285 foreach (auto anma2
; anims
.byValue
) {
286 foreach (auto anma
; anma2
) {
287 foreach (StrId ssid
; anma
) {
288 //conwriteln(fullname, ": ssid=", ssid.id, "; is ", ssid.get);
289 if (ssid
.id
!= 0) loadSprite(ssid
.get
);
293 // now fill animSprites
294 foreach (auto anma2
; anims
.byKeyValue
) {
295 if (anma2
.key
!in animSprites
) animSprites
[anma2
.key
] = new ImgSprite
*[][](2); // 2 dirs
296 auto aspx
= animSprites
[anma2
.key
];
297 foreach (immutable dir
, auto anma
; anma2
.value
) {
298 foreach (StrId ssid
; anma
) {
299 ImgSprite
* spi
= ssid
.get
in sprImages
;
306 // unload actor graphics
307 void releaseGraphics () {
311 int nextAnimIdx (StrId state
, uint dir
, int curidx
) {
312 if (dir
!= 0) dir
= 1;
313 if (++curidx
<= 0) return 0;
314 if (auto spra
= state
.id
in animSprites
) {
315 if (curidx
< (*spra
).ptr
[dir
].length
) return curidx
;
321 ImgSprite
* animSpr (StrId state
, uint dir
, int curidx
) {
322 if (dir
!= 0) dir
= 1;
323 if (curidx
< 0) return null;
324 if (auto spra
= state
.id
in animSprites
) {
325 if (curidx
>= (*spra
).ptr
[dir
].length
) return null;
326 return (*spra
).ptr
[dir
].ptr
[curidx
];
331 this (string ctype
, string cname
) {
332 classtype
= StrPool
.intern(ctype
);
333 classname
= StrPool
.intern(cname
);
336 void clearAllFrames (StrId state
) {
340 void clearFrames (StrId state
) {
341 anims
.remove(state
.id
);
344 void addFrame (StrId state
, uint dir
, StrId sprname
) {
345 //conwriteln("new frame for '", fullname, "'; state=", state.get, "; sprname=", sprname.get, "; dir=", dir);
346 if (dir
> 1) throw new Exception("addFrame: invalid dir");
347 if (state
.id
!in anims
) anims
[state
.id
] = new StrId
[][](2);
348 auto aa
= anims
[state
.id
];
352 void setAnimInitFunc (FuncPool
.FuncInfo fi
) {
353 if (fi
is null) return;
354 if (fi
.mgtype
!= ":void") throw new Exception("invalid actor animation init function");
358 void setInitFunc (FuncPool
.FuncInfo fi
) {
359 if (fi
is null) return;
360 if (fi
.mgtype
!= ":void:Actor") throw new Exception("invalid actor init function");
364 void setThinkFunc (FuncPool
.FuncInfo fi
) {
365 if (fi
is null) return;
366 if (fi
.mgtype
!= ":void:Actor") throw new Exception("invalid actor think function");
370 void callAnimInit () {
371 foreach (auto fi
; fiAnimInit
) if (fi
!is null) fi();
374 void callInit (ActorId aid
) {
375 if (fiInit
is null ||
!aid
.valid
) return;
379 void callThink (ActorId aid
) {
380 if (fiThink
is null ||
!aid
.valid
) return;
385 // return `true` to stop
386 bool forEach (bool delegate (ActorDef d
) dg
) {
387 foreach (ActorDef adef
; actordefs
.byValue
) if (dg(adef
)) return true;
391 void forEach (void delegate (ActorDef d
) dg
) {
392 foreach (ActorDef adef
; actordefs
.byValue
) dg(adef
);
397 // ////////////////////////////////////////////////////////////////////////// //
398 public ActorDef
findD2DActorDef (ushort mapid
) {
399 if (mapid
== 0) assert(0);
400 if (mapid
== 1 || mapid
== 2) return findActorDef("monster", "Player");
401 if (auto dad
= mapid
in d2dactordefsById
) {
402 auto adef
= findActorDef(dad
.classtype
, dad
.classname
);
403 if (adef
is null) throw new Exception("can't find DACS actor definition for D2D actor '"~dad
.classtype
.get
~":"~dad
.classname
.get
~"'");
406 import std
.conv
: to
;
407 throw new Exception("can't find D2D ActorDef for mapid "~to
!string(mapid
));
411 // ////////////////////////////////////////////////////////////////////////// //
414 __gshared ActorDef
[ulong] actordefs
; // by (name<<32)|type
416 mixin(Actor
.FieldGetMixin
!("classtype", StrId
)); // fget_classtype
417 mixin(Actor
.FieldGetMixin
!("classname", StrId
)); // fget_classname
420 ulong ctn2idx (uint ctid
, uint cnid
) { pragma(inline
, true); return ((cast(ulong)cnid
)<<32)|ctid
; }
421 ulong ctn2idx (StrId ct
, StrId cn
) { pragma(inline
, true); return ctn2idx(ct
.id
, cn
.id
); }
424 public ActorDef
findActorDef (ActorId aid
) {
425 pragma(inline
, true);
426 if (!aid
.valid
) return null;
427 return findActorDef(aid
.fget_classtype
, aid
.fget_classname
);
431 public ActorDef
findActorDef (StrId ctype
, StrId cname
) { /*pragma(inline, true);*/ return actordefs
.get(ctn2idx(ctype
.id
, cname
.id
), null); }
434 public ActorDef
findActorDef (const(char)[] ctype
, const(char)[] cname
) {
435 auto ctid
= ctype
in StrPool
;
436 auto cnid
= cname
in StrPool
;
437 if (!ctid ||
!cnid
) return null;
438 return actordefs
.get(ctn2idx(ctid
, cnid
), null);
442 public ActorDef
registerActorDef (string classtype
, string classname
) {
443 auto ct
= StrPool
.intern(classtype
);
444 auto cn
= StrPool
.intern(classname
);
445 if (findActorDef(ct
, cn
) is null) actordefs
[ctn2idx(ct
, cn
)] = new ActorDef(classtype
, classname
);
446 return actordefs
[ctn2idx(ct
, cn
)];