max light radius is 255 now. no, really!
[dd2d.git] / d2dadefs.d
blob615ee47e8b32a6b73247ca2831dc142672c431c2
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
47 // ////////////////////////////////////////////////////////////////////////// //
48 // known D2D actors (and pseudoactors)
49 struct ActorDefD2D {
50 StrId classtype;
51 StrId classname;
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);
57 mapid = thid;
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 () {
70 d2dactordefs = [
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) {
137 switch (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
153 default:
155 return null;
159 // ////////////////////////////////////////////////////////////////////////// //
160 // sprite atlases
161 __gshared TexAtlas[] atlases;
162 __gshared ImgSprite[string] sprImages;
165 public void realiseSpriteAtlases () {
166 foreach (immutable idx, TexAtlas a; atlases) {
167 import arsd.png;
168 import std.string : format;
169 a.updateTexture();
170 version(tatlas_dump) writePng("_za%02s.png".format(idx), a.img);
175 public struct ImgSprite {
176 D2DImage vga;
177 //Texture tex;
178 bool mirrored;
179 float tx0, tx1, ty0, ty1;
180 TexAtlas atlas;
181 TexAtlas.Rect arect;
183 //@disable this (this);
185 void putToAtlas () {
186 if (atlas !is null) return;
187 TexAtlas.Rect arc;
188 TexAtlas aa;
189 foreach (TexAtlas a; atlases) {
190 auto rc = a.insert(vga);
191 if (rc.valid) { arc = rc; aa = a; break; }
193 if (!arc.valid) {
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);
198 assert(arc.valid);
199 atlases ~= aa;
201 atlas = aa;
202 arect = arc;
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) {
210 import iv.glbinds;
211 if (atlas is null || !atlas.hasTexture) return;
212 x -= vga.sx;
213 y -= vga.sy;
214 int w = x+vga.width;
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);
220 glBegin(GL_QUADS);
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
225 glEnd();
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, "'");
236 return im;
238 string spname = spnamef;
239 bool mirrored = false;
240 if (checkMirror && spname.endsWith("_mirrored.vga")) {
241 mirrored = true;
242 spname = spname[0..$-13]~".vga";
244 // for mirrored, load normal sprite and create mirrored one
245 if (mirrored) {
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();
250 *imm = *im;
251 imm.mirrored = true;
252 sprImages[spnamef] = *imm;
253 } else {
254 // new sprite
255 assert(!mirrored);
256 auto im = new ImgSprite();
257 im.vga = new D2DImage(spname);
258 im.putToAtlas();
259 sprImages[spnamef] = *im;
261 return spnamef in sprImages;
265 // ////////////////////////////////////////////////////////////////////////// //
266 public final class ActorDef {
267 StrId classtype;
268 StrId classname;
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
284 // load sprites
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;
300 aspx[dir] ~= spi;
306 // unload actor graphics
307 void releaseGraphics () {
308 animSprites = null;
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;
316 return 0;
318 return 0;
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];
328 return null;
331 this (string ctype, string cname) {
332 classtype = StrPool.intern(ctype);
333 classname = StrPool.intern(cname);
336 void clearAllFrames (StrId state) {
337 anims.clear();
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];
349 aa[dir] ~= sprname;
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");
355 fiAnimInit ~= fi;
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");
361 fiInit = fi;
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");
367 fiThink = fi;
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;
376 fiInit(aid);
379 void callThink (ActorId aid) {
380 if (fiThink is null || !aid.valid) return;
381 fiThink(aid);
384 static:
385 // return `true` to stop
386 bool forEach (bool delegate (ActorDef d) dg) {
387 foreach (ActorDef adef; actordefs.byValue) if (dg(adef)) return true;
388 return false;
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~"'");
404 return adef;
406 import std.conv : to;
407 throw new Exception("can't find D2D ActorDef for mapid "~to!string(mapid));
411 // ////////////////////////////////////////////////////////////////////////// //
412 import dacs.actor;
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)];