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
44 AF_CAMERACHICK
= BIT
!(7), // camera will follow this actor; if we have more than one camera chick... well, who knows
48 // ////////////////////////////////////////////////////////////////////////// //
49 // known D2D actors (and pseudoactors)
53 ushort mapid
; // thing id in map
55 this (string ctype
, string cname
, ushort thid
=0) {
56 classtype
= StrPool
.intern(ctype
);
57 classname
= StrPool
.intern(cname
);
61 string
toString () const {
62 import std
.string
: format
;
63 return "ActorDefD2D(%s, %s, %s)".format(classtype
.get
, classname
.get
, mapid
);
67 immutable ActorDefD2D
[] d2dactordefs
;
68 public immutable ActorDefD2D
[ushort] d2dactordefsById
; // by mapid
70 shared static this () {
72 ActorDefD2D("playerstart", "Player1", 1),
73 ActorDefD2D("playerstart", "Player2", 2),
74 ActorDefD2D("playerstart", "DMStart", 3),
75 ActorDefD2D("item", "Clip", 100),
76 ActorDefD2D("item", "Shell", 101),
77 ActorDefD2D("item", "Rocket", 102),
78 ActorDefD2D("item", "Cell", 103),
79 ActorDefD2D("item", "Ammo", 104),
80 ActorDefD2D("item", "ShellBox", 105),
81 ActorDefD2D("item", "RocketBox", 106),
82 ActorDefD2D("item", "CellPack", 107),
83 ActorDefD2D("item", "StimPack", 108),
84 ActorDefD2D("item", "MediKit", 109),
85 ActorDefD2D("item", "BackPack", 110),
86 ActorDefD2D("item", "Chainsaw", 111),
87 ActorDefD2D("item", "Shotgun", 112),
88 ActorDefD2D("item", "SuperShotgun", 113),
89 ActorDefD2D("item", "MachineGun", 114),
90 ActorDefD2D("item", "RocketLauncher", 115),
91 ActorDefD2D("item", "Plasmagun", 116),
92 ActorDefD2D("item", "BFG900", 117),
93 ActorDefD2D("item", "ArmorGreen", 118),
94 ActorDefD2D("item", "ArmorBlue", 119),
95 ActorDefD2D("item", "MegaSphere", 120),
96 ActorDefD2D("item", "Invulnerability", 121),
97 ActorDefD2D("item", "Aqualung", 122),
98 ActorDefD2D("item", "KeyRed", 123),
99 ActorDefD2D("item", "KeyGreen", 124),
100 ActorDefD2D("item", "KeyBlue", 125),
101 ActorDefD2D("item", "ProtectionSuit", 126),
102 ActorDefD2D("item", "Super", 127),
103 ActorDefD2D("item", "TorchRed", 128),
104 ActorDefD2D("item", "TorchGreen", 129),
105 ActorDefD2D("item", "TorchBlue", 130),
106 ActorDefD2D("item", "Gor1", 131),
107 ActorDefD2D("item", "FCan", 132),
108 ActorDefD2D("item", "Gun2", 133),
109 ActorDefD2D("monster", "Demon", 200),
110 ActorDefD2D("monster", "Imp", 201),
111 ActorDefD2D("monster", "Zombie", 202),
112 ActorDefD2D("monster", "Sergeant", 203),
113 ActorDefD2D("monster", "Cyberdemon", 204),
114 ActorDefD2D("monster", "Chaingunner", 205),
115 ActorDefD2D("monster", "BaronOfHell", 206),
116 ActorDefD2D("monster", "HellKnight", 207),
117 ActorDefD2D("monster", "Cacodemon", 208),
118 ActorDefD2D("monster", "LostSoul", 209),
119 ActorDefD2D("monster", "PainElemental", 210),
120 ActorDefD2D("monster", "SpiderMastermind", 211),
121 ActorDefD2D("monster", "Arachnotron", 212),
122 ActorDefD2D("monster", "Mancubus", 213),
123 ActorDefD2D("monster", "Revenant", 214),
124 ActorDefD2D("monster", "Archvile", 215),
125 ActorDefD2D("monster", "Fish", 216),
126 ActorDefD2D("monster", "Barrel", 217),
127 ActorDefD2D("monster", "Robot", 218),
128 ActorDefD2D("monster", "Man", 219),
130 foreach (const ref ActorDefD2D d2da
; d2dactordefs
) {
131 if (d2da
.mapid
) d2dactordefsById
[d2da
.mapid
] = d2da
;
136 // ////////////////////////////////////////////////////////////////////////// //
137 public string
getD2DSwitchClassName (uint swtype
) {
139 case 1: return "Exit"; // SW_EXIT
140 case 2: return "ExitSecret"; // SW_EXITS
141 case 3: return "DoorOpen"; // SW_OPENDOOR
142 case 4: return "DoorClose"; // SW_SHUTDOOR
143 case 5: return "TrapClose"; // SW_SHUTTRAP
144 case 6: return "Door"; // SW_DOOR
145 case 7: return "Door5"; // SW_DOOR5
146 case 8: return "Press"; // SW_PRESS
147 case 9: return "Tele"; // SW_TELE
148 case 10: return "Secret"; // SW_SECRET
149 case 11: return "LiftUp"; // SW_LIFTUP
150 case 12: return "LiftDown"; // SW_LIFTDOWN
151 case 13: return "Trap"; // SW_TRAP
152 case 14: return "Lift"; // SW_LIFT
153 case 64: return "WinGame"; // SW_WINGAME
160 // ////////////////////////////////////////////////////////////////////////// //
162 __gshared TexAtlas
[] atlases
;
163 __gshared ImgSprite
[string
] sprImages
;
166 public void realiseSpriteAtlases () {
167 foreach (immutable idx
, TexAtlas a
; atlases
) {
169 import std
.string
: format
;
171 version(tatlas_dump
) writePng("_za%02s.png".format(idx
), a
.img
);
176 public struct ImgSprite
{
180 float tx0
, tx1
, ty0
, ty1
;
184 //@disable this (this);
187 if (atlas
!is null) return;
190 foreach (TexAtlas a
; atlases
) {
191 auto rc
= a
.insert(vga
);
192 if (rc
.valid
) { arc
= rc
; aa
= a
; break; }
195 int w
= (vga
.width
< 1024 ?
1024 : vga
.width
);
196 int h
= (vga
.height
< 1024 ?
1024 : vga
.height
);
197 aa
= new TexAtlas(w
, h
);
198 arc
= aa
.insert(vga
);
204 tx0
= cast(float)arc
.x
/cast(float)(aa
.width
);
205 ty0
= cast(float)arc
.y
/cast(float)(aa
.height
);
206 tx1
= cast(float)(arc
.x
+arc
.w
)/cast(float)(aa
.width
);
207 ty1
= cast(float)(arc
.y
+arc
.h
)/cast(float)(aa
.height
);
210 void drawAtXY (int x
, int y
, bool mirrorX
=false, bool mirrorY
=false) {
212 if (atlas
is null ||
!atlas
.hasTexture
) return;
216 int h
= y
+vga
.height
;
217 if (mirrored
) mirrorX
= !mirrorX
;
218 if (mirrorX
) { int tmp
= x
; x
= w
; w
= tmp
; }
219 if (mirrorY
) { int tmp
= y
; y
= h
; h
= tmp
; }
220 glBindTexture(GL_TEXTURE_2D
, atlas
.tex
.tid
);
222 glTexCoord2f(tx0
, ty0
); glVertex2i(x
, y
); // top-left
223 glTexCoord2f(tx1
, ty0
); glVertex2i(w
, y
); // top-right
224 glTexCoord2f(tx1
, ty1
); glVertex2i(w
, h
); // bottom-right
225 glTexCoord2f(tx0
, ty1
); glVertex2i(x
, h
); // bottom-left
231 // ////////////////////////////////////////////////////////////////////////// //
232 ImgSprite
* loadSprite (string spnamef
, bool checkMirror
=true) {
233 if (spnamef
.length
== 0) return null;
234 import std
.algorithm
: endsWith
;
235 if (auto im
= spnamef
in sprImages
) {
236 //conwriteln("cached sprite '", spnamef, "'");
239 string spname
= spnamef
;
240 bool mirrored
= false;
241 if (checkMirror
&& spname
.endsWith("_mirrored.vga")) {
243 spname
= spname
[0..$-13]~".vga";
245 // for mirrored, load normal sprite and create mirrored one
247 auto im
= spname
in sprImages
;
248 if (im
is null) im
= loadSprite(spname
, false);
249 if (im
is null) return null;
250 auto imm
= new ImgSprite();
253 sprImages
[spnamef
] = *imm
;
257 auto im
= new ImgSprite();
258 im
.vga
= new D2DImage(spname
);
260 sprImages
[spnamef
] = *im
;
262 return spnamef
in sprImages
;
266 // ////////////////////////////////////////////////////////////////////////// //
267 public final class ActorDef
{
271 // animation sequences for stated; keyed by state strid
272 StrId
[][][uint] anims
; // [statestrid][dir][pos]
274 FuncPool
.FuncInfo
[] fiAnimInit
;
275 FuncPool
.FuncInfo fiInit
;
276 FuncPool
.FuncInfo fiThink
;
278 ImgSprite
*[][][uint] animSprites
;
279 //__gshared ImgSprite*[][] animCur;
281 // load actor graphics
282 void loadGraphics () {
283 //conwriteln("loading graphics for '", fullname, "'; anims.length=", anims.length");
284 if (anims
.length
== 0) return; // no graphics here
286 foreach (auto anma2
; anims
.byValue
) {
287 foreach (auto anma
; anma2
) {
288 foreach (StrId ssid
; anma
) {
289 //conwriteln(fullname, ": ssid=", ssid.id, "; is ", ssid.get);
290 if (ssid
.id
!= 0) loadSprite(ssid
.get
);
294 // now fill animSprites
295 foreach (auto anma2
; anims
.byKeyValue
) {
296 if (anma2
.key
!in animSprites
) animSprites
[anma2
.key
] = new ImgSprite
*[][](2); // 2 dirs
297 auto aspx
= animSprites
[anma2
.key
];
298 foreach (immutable dir
, auto anma
; anma2
.value
) {
299 foreach (StrId ssid
; anma
) {
300 ImgSprite
* spi
= ssid
.get
in sprImages
;
307 // unload actor graphics
308 void releaseGraphics () {
312 int nextAnimIdx (StrId state
, uint dir
, int curidx
) {
313 if (dir
!= 0) dir
= 1;
314 if (++curidx
<= 0) return 0;
315 if (auto spra
= state
.id
in animSprites
) {
316 if (curidx
< (*spra
).ptr
[dir
].length
) return curidx
;
322 ImgSprite
* animSpr (StrId state
, uint dir
, int curidx
) {
323 if (dir
!= 0) dir
= 1;
324 if (curidx
< 0) return null;
325 if (auto spra
= state
.id
in animSprites
) {
326 if (curidx
>= (*spra
).ptr
[dir
].length
) return null;
327 return (*spra
).ptr
[dir
].ptr
[curidx
];
332 this (string ctype
, string cname
) {
333 classtype
= StrPool
.intern(ctype
);
334 classname
= StrPool
.intern(cname
);
337 void clearAllFrames (StrId state
) {
341 void clearFrames (StrId state
) {
342 anims
.remove(state
.id
);
345 void addFrame (StrId state
, uint dir
, StrId sprname
) {
346 //conwriteln("new frame for '", fullname, "'; state=", state.get, "; sprname=", sprname.get, "; dir=", dir);
347 if (dir
> 1) throw new Exception("addFrame: invalid dir");
348 if (state
.id
!in anims
) anims
[state
.id
] = new StrId
[][](2);
349 auto aa
= anims
[state
.id
];
353 void setAnimInitFunc (FuncPool
.FuncInfo fi
) {
354 if (fi
is null) return;
355 if (fi
.mgtype
!= ":void") throw new Exception("invalid actor animation init function");
359 void setInitFunc (FuncPool
.FuncInfo fi
) {
360 if (fi
is null) return;
361 if (fi
.mgtype
!= ":void:Actor") throw new Exception("invalid actor init function");
365 void setThinkFunc (FuncPool
.FuncInfo fi
) {
366 if (fi
is null) return;
367 if (fi
.mgtype
!= ":void:Actor") throw new Exception("invalid actor think function");
371 void callAnimInit () {
372 foreach (auto fi
; fiAnimInit
) if (fi
!is null) fi();
375 void callInit (ActorId aid
) {
376 if (fiInit
is null ||
!aid
.valid
) return;
380 void callThink (ActorId aid
) {
381 if (fiThink
is null ||
!aid
.valid
) return;
386 // return `true` to stop
387 bool forEach (bool delegate (ActorDef d
) dg
) {
388 foreach (ActorDef adef
; actordefs
.byValue
) if (dg(adef
)) return true;
392 void forEach (void delegate (ActorDef d
) dg
) {
393 foreach (ActorDef adef
; actordefs
.byValue
) dg(adef
);
398 // ////////////////////////////////////////////////////////////////////////// //
399 public ActorDef
findD2DActorDef (ushort mapid
) {
400 if (mapid
== 0) assert(0);
401 if (mapid
== 1 || mapid
== 2) return findActorDef("monster", "Player");
402 if (auto dad
= mapid
in d2dactordefsById
) {
403 auto adef
= findActorDef(dad
.classtype
, dad
.classname
);
404 if (adef
is null) throw new Exception("can't find DACS actor definition for D2D actor '"~dad
.classtype
.get
~":"~dad
.classname
.get
~"'");
407 import std
.conv
: to
;
408 throw new Exception("can't find D2D ActorDef for mapid "~to
!string(mapid
));
412 // ////////////////////////////////////////////////////////////////////////// //
415 __gshared ActorDef
[ulong] actordefs
; // by (name<<32)|type
417 mixin(Actor
.FieldGetMixin
!("classtype", StrId
)); // fget_classtype
418 mixin(Actor
.FieldGetMixin
!("classname", StrId
)); // fget_classname
421 ulong ctn2idx (uint ctid
, uint cnid
) { pragma(inline
, true); return ((cast(ulong)cnid
)<<32)|ctid
; }
422 ulong ctn2idx (StrId ct
, StrId cn
) { pragma(inline
, true); return ctn2idx(ct
.id
, cn
.id
); }
425 public ActorDef
findActorDef (ActorId aid
) {
426 pragma(inline
, true);
427 if (!aid
.valid
) return null;
428 return findActorDef(aid
.fget_classtype
, aid
.fget_classname
);
432 public ActorDef
findActorDef (StrId ctype
, StrId cname
) { /*pragma(inline, true);*/ return actordefs
.get(ctn2idx(ctype
.id
, cname
.id
), null); }
435 public ActorDef
findActorDef (const(char)[] ctype
, const(char)[] cname
) {
436 auto ctid
= ctype
in StrPool
;
437 auto cnid
= cname
in StrPool
;
438 if (!ctid ||
!cnid
) return null;
439 return actordefs
.get(ctn2idx(ctid
, cnid
), null);
443 public ActorDef
registerActorDef (string classtype
, string classname
) {
444 auto ct
= StrPool
.intern(classtype
);
445 auto cn
= StrPool
.intern(classname
);
446 if (findActorDef(ct
, cn
) is null) actordefs
[ctn2idx(ct
, cn
)] = new ActorDef(classtype
, classname
);
447 return actordefs
[ctn2idx(ct
, cn
)];