cosmetix
[dd2d.git] / d2dadefs.d
bloba6d1eebcaf9eb90fa40e6fad8b39716f40023f8f
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;
20 private:
21 import glutils;
22 import console;
23 import dacs;
24 import d2dgfx;
25 import tatlas;
27 //version = tatlas_dump;
30 // ////////////////////////////////////////////////////////////////////////// //
31 private enum BIT(ubyte n) = (1U<<n);
34 // ////////////////////////////////////////////////////////////////////////// //
35 // thing flags
36 public enum {
37 AF_NOCOLLISION = BIT!(0),
38 AF_NOGRAVITY = BIT!(1),
39 AF_NOTHINK = BIT!(2),
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)
50 struct ActorDefD2D {
51 StrId classtype;
52 StrId classname;
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);
58 mapid = thid;
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 () {
71 d2dactordefs = [
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", "BFG9000", 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) {
138 switch (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 "Teleport"; // 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
154 default:
156 return null;
160 // ////////////////////////////////////////////////////////////////////////// //
161 // sprite atlases
162 __gshared TexAtlas[] atlases;
163 __gshared ImgSprite[string] sprImages;
166 public void realiseSpriteAtlases () {
167 foreach (immutable idx, TexAtlas a; atlases) {
168 import arsd.png;
169 import std.string : format;
170 a.updateTexture();
171 version(tatlas_dump) writePng("_za%02s.png".format(idx), a.img);
176 public struct ImgSprite {
177 D2DImage vga;
178 //Texture tex;
179 bool mirrored;
180 float tx0, tx1, ty0, ty1;
181 TexAtlas atlas;
182 TexAtlas.Rect arect;
184 //@disable this (this);
186 void putToAtlas () {
187 if (atlas !is null) return;
188 TexAtlas.Rect arc;
189 TexAtlas aa;
190 foreach (TexAtlas a; atlases) {
191 auto rc = a.insert(vga);
192 if (rc.valid) { arc = rc; aa = a; break; }
194 if (!arc.valid) {
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);
199 assert(arc.valid);
200 atlases ~= aa;
202 atlas = aa;
203 arect = arc;
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) {
211 import iv.glbinds;
212 if (atlas is null || !atlas.hasTexture) return;
213 x -= vga.sx;
214 y -= vga.sy;
215 int w = x+vga.width;
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);
221 glBegin(GL_QUADS);
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
226 glEnd();
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, "'");
237 return im;
239 string spname = spnamef;
240 bool mirrored = false;
241 if (checkMirror && spname.endsWith("_mirrored.vga")) {
242 mirrored = true;
243 spname = spname[0..$-13]~".vga";
245 // for mirrored, load normal sprite and create mirrored one
246 if (mirrored) {
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();
251 *imm = *im;
252 imm.mirrored = true;
253 sprImages[spnamef] = *imm;
254 } else {
255 // new sprite
256 assert(!mirrored);
257 auto im = new ImgSprite();
258 im.vga = new D2DImage(spname);
259 im.putToAtlas();
260 sprImages[spnamef] = *im;
262 return spnamef in sprImages;
266 // ////////////////////////////////////////////////////////////////////////// //
267 public final class ActorDef {
268 StrId classtype;
269 StrId classname;
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
285 // load sprites
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;
301 aspx[dir] ~= spi;
307 // unload actor graphics
308 void releaseGraphics () {
309 animSprites = null;
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;
317 return 0;
319 return 0;
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];
329 return null;
332 this (string ctype, string cname) {
333 classtype = StrPool.intern(ctype);
334 classname = StrPool.intern(cname);
337 void clearAllFrames (StrId state) {
338 anims.clear();
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];
350 aa[dir] ~= sprname;
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");
356 fiAnimInit ~= fi;
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");
362 fiInit = fi;
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");
368 fiThink = fi;
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;
377 fiInit(aid);
380 void callThink (ActorId aid) {
381 if (fiThink is null || !aid.valid) return;
382 fiThink(aid);
385 static:
386 // return `true` to stop
387 bool forEach (bool delegate (ActorDef d) dg) {
388 foreach (ActorDef adef; actordefs.byValue) if (dg(adef)) return true;
389 return false;
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~"'");
405 return adef;
407 import std.conv : to;
408 throw new Exception("can't find D2D ActorDef for mapid "~to!string(mapid));
412 // ////////////////////////////////////////////////////////////////////////// //
413 import dacs.actor;
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)];