using Actor mixins to cache field offsets
[dd2d.git] / d2dadefs.d
blobaab9afdee9e98d93caacb44f69578b5091cd82ae
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;
27 // ////////////////////////////////////////////////////////////////////////// //
28 private enum BIT(ubyte n) = (1U<<n);
31 // ////////////////////////////////////////////////////////////////////////// //
32 // thing flags
33 public enum {
34 AF_NOCOLLISION = BIT!(0),
35 AF_NOGRAVITY = BIT!(1),
36 AF_NOTHINK = BIT!(2),
37 AF_NOONTOUCH = BIT!(3), // `onTouch` will never be called with `me` for this actor
38 AF_NODRAW = BIT!(4), // don't draw sprite
39 AF_NOLIGHT = BIT!(5), // no attached light
40 AF_NOANIMATE = BIT!(6), // don't do animation
43 // ////////////////////////////////////////////////////////////////////////// //
44 // known D2D actors (and pseudoactors)
45 struct ActorDefD2D {
46 string classtype;
47 string classname;
48 string fullname;
49 ushort mapid; // thing id in map
51 this (string ctype, string cname, ushort thid) { classtype = ctype; classname = cname; fullname = classtype~":"~classname; mapid = thid; }
53 string toString () const {
54 import std.string : format;
55 return "ActorDefD2D(%s, %s, %s)".format(classtype/*.quote*/, classname/*.quote*/, mapid);
59 immutable ActorDefD2D[$] d2dactordefs = [
60 ActorDefD2D("playerstart", "Player1", 1),
61 ActorDefD2D("playerstart", "Player2", 2),
62 ActorDefD2D("playerstart", "DMStart", 3),
63 ActorDefD2D("item", "Clip", 100),
64 ActorDefD2D("item", "Shell", 101),
65 ActorDefD2D("item", "Rocket", 102),
66 ActorDefD2D("item", "Cell", 103),
67 ActorDefD2D("item", "Ammo", 104),
68 ActorDefD2D("item", "ShellBox", 105),
69 ActorDefD2D("item", "RocketBox", 106),
70 ActorDefD2D("item", "CellPack", 107),
71 ActorDefD2D("item", "StimPack", 108),
72 ActorDefD2D("item", "MediKit", 109),
73 ActorDefD2D("item", "BackPack", 110),
74 ActorDefD2D("item", "Chainsaw", 111),
75 ActorDefD2D("item", "Shotgun", 112),
76 ActorDefD2D("item", "SuperShotgun", 113),
77 ActorDefD2D("item", "MachineGun", 114),
78 ActorDefD2D("item", "RocketLauncher", 115),
79 ActorDefD2D("item", "Plasmagun", 116),
80 ActorDefD2D("item", "BFG900", 117),
81 ActorDefD2D("item", "ArmorGreen", 118),
82 ActorDefD2D("item", "ArmorBlue", 119),
83 ActorDefD2D("item", "MegaSphere", 120),
84 ActorDefD2D("item", "Invulnerability", 121),
85 ActorDefD2D("item", "Aqualung", 122),
86 ActorDefD2D("item", "KeyRed", 123),
87 ActorDefD2D("item", "KeyGreen", 124),
88 ActorDefD2D("item", "KeyBlue", 125),
89 ActorDefD2D("item", "ProtectionSuit", 126),
90 ActorDefD2D("item", "Super", 127),
91 ActorDefD2D("item", "TorchRed", 128),
92 ActorDefD2D("item", "TorchGreen", 129),
93 ActorDefD2D("item", "TorchBlue", 130),
94 ActorDefD2D("item", "Gor1", 131),
95 ActorDefD2D("item", "FCan", 132),
96 ActorDefD2D("item", "Gun2", 133),
97 ActorDefD2D("monster", "Demon", 200),
98 ActorDefD2D("monster", "Imp", 201),
99 ActorDefD2D("monster", "Zombie", 202),
100 ActorDefD2D("monster", "Sergeant", 203),
101 ActorDefD2D("monster", "Cyberdemon", 204),
102 ActorDefD2D("monster", "Chaingunner", 205),
103 ActorDefD2D("monster", "BaronOfHell", 206),
104 ActorDefD2D("monster", "HellKnight", 207),
105 ActorDefD2D("monster", "Cacodemon", 208),
106 ActorDefD2D("monster", "LostSoul", 209),
107 ActorDefD2D("monster", "PainElemental", 210),
108 ActorDefD2D("monster", "SpiderMastermind", 211),
109 ActorDefD2D("monster", "Arachnotron", 212),
110 ActorDefD2D("monster", "Mancubus", 213),
111 ActorDefD2D("monster", "Revenant", 214),
112 ActorDefD2D("monster", "Archvile", 215),
113 ActorDefD2D("monster", "Fish", 216),
114 ActorDefD2D("monster", "Barrel", 217),
115 ActorDefD2D("monster", "Robot", 218),
116 ActorDefD2D("monster", "Man", 219),
120 public immutable ActorDefD2D[ushort] d2dactordefsById;
121 //immutable ActorDefD2D[string] d2dactordefsByTN; // by "type:name"
123 shared static this () {
124 foreach (const ref ActorDefD2D d2da; d2dactordefs) {
125 d2dactordefsById[d2da.mapid] = d2da;
126 //d2dactordefsByTN[d2da.fullname] = d2da;
131 // ////////////////////////////////////////////////////////////////////////// //
132 public struct ImgSprite {
133 D2DImage vga;
134 Texture tex;
135 bool mirrored;
137 @disable this (this);
141 public final class ActorDef {
142 string classtype;
143 string classname;
144 string fullname;
145 uint id; // actor's unique id
147 // animation sequences for stated; keyed by state strid
148 StrId[][][uint] anims; // [statestrid][dir][pos]
150 FuncPool.FuncInfo[] fiAnimInit;
151 FuncPool.FuncInfo fiInit;
152 FuncPool.FuncInfo fiThink;
154 __gshared ImgSprite[uint] allSprites; // to avoid loading sprites twice they are keyed by namestrid
155 ImgSprite*[][][uint] animSprites;
156 //__gshared ImgSprite*[][] animCur;
158 // load actor graphics
159 void loadGraphics () {
160 //conwriteln("loading graphics for '", fullname, "'; anims.length=", anims.length, "; allSprites.length=", allSprites.length);
161 if (anims.length == 0) return; // no graphics here
162 //if (allSprites.length > 0) return;
163 // load sprites
164 foreach (auto anma2; anims.byValue) {
165 foreach (auto anma; anma2) {
166 foreach (StrId ssid; anma) {
167 //conwriteln(fullname, ": ssid=", ssid.id, "; is ", ssid.get);
168 ImgSprite* spi = null;
169 if (ssid.id != 0) {
170 spi = ssid.id in allSprites;
171 if (spi is null) {
172 //conwriteln("loading sprite '", ssid.get, "'");
173 D2DImage vga = new D2DImage(ssid.get);
174 allSprites[ssid.id] = ImgSprite();
175 spi = ssid.id in allSprites;
176 assert(spi !is null);
177 spi.vga = vga;
178 spi.tex = new Texture(spi.vga.asTCImage, Texture.Option.Nearest);
179 } else {
180 //conwriteln("cached sprite '", ssid.get, "'");
186 // now fill animSprites
187 foreach (auto anma2; anims.byKeyValue) {
188 if (anma2.key !in animSprites) animSprites[anma2.key] = new ImgSprite*[][](2); // 2 dirs
189 auto aspx = animSprites[anma2.key];
190 foreach (immutable dir, auto anma; anma2.value) {
191 foreach (StrId ssid; anma) {
192 ImgSprite* spi = null;
193 if (ssid.id != 0) spi = ssid.id in allSprites;
194 aspx[dir] ~= spi;
200 // unload actor graphics
201 void releaseGraphics () {
202 animSprites = null;
203 //allSprites.clear();
206 int nextAnimIdx (StrId state, uint dir, int curidx) {
207 if (dir != 0) dir = 1;
208 if (++curidx <= 0) return 0;
209 if (auto spra = state.id in animSprites) {
210 if (curidx < (*spra).ptr[dir].length) return curidx;
211 return 0;
213 return 0;
216 ImgSprite* animSpr (StrId state, uint dir, int curidx) {
217 if (dir != 0) dir = 1;
218 if (curidx < 0) return null;
219 if (auto spra = state.id in animSprites) {
220 if (curidx >= (*spra).ptr[dir].length) return null;
221 return (*spra).ptr[dir].ptr[curidx];
223 return null;
226 private __gshared uint nextId = 1;
228 this (string ctype, string cname) {
229 classtype = ctype;
230 classname = cname;
231 fullname = classtype~":"~classname;
232 id = nextId++;
235 void clearAllFrames (StrId state) {
236 anims.clear();
239 void clearFrames (StrId state) {
240 anims.remove(state.id);
243 void addFrame (StrId state, uint dir, StrId sprname) {
244 //conwriteln("new frame for '", fullname, "'; state=", state.get, "; sprname=", sprname.get, "; dir=", dir);
245 if (dir > 1) throw new Exception("addFrame: invalid dir");
246 if (state.id !in anims) anims[state.id] = new StrId[][](2);
247 auto aa = anims[state.id];
248 aa[dir] ~= sprname;
251 void setAnimInitFunc (FuncPool.FuncInfo fi) {
252 if (fi is null) return;
253 if (fi.mgtype != ":void") throw new Exception("invalid actor animation init function");
254 fiAnimInit ~= fi;
257 void setInitFunc (FuncPool.FuncInfo fi) {
258 if (fi is null) return;
259 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor init function");
260 fiInit = fi;
263 void setThinkFunc (FuncPool.FuncInfo fi) {
264 if (fi is null) return;
265 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor think function");
266 fiThink = fi;
269 void callAnimInit () {
270 foreach (auto fi; fiAnimInit) if (fi !is null) fi();
273 void callInit (ActorId aid) {
274 if (fiInit is null || !aid.valid) return;
275 fiInit(aid);
278 void callThink (ActorId aid) {
279 if (fiThink is null || !aid.valid) return;
280 fiThink(aid);
285 // ////////////////////////////////////////////////////////////////////////// //
286 public __gshared ActorDef[string] actordefs; // by fullname
289 public ActorDef findD2DActorDef (ushort mapid) {
290 if (mapid == 0) assert(0);
291 if (mapid == 1 || mapid == 2) return findActorDefByFullName("monster:Player");
292 if (auto dad = mapid in d2dactordefsById) {
293 auto adef = findActorDefByFullName(dad.fullname);
294 if (adef is null) throw new Exception("can't find DACS actor definition for D2D actor '"~dad.fullname~"'");
295 return adef;
297 import std.conv : to;
298 throw new Exception("can't find D2D ActorDef for mapid "~to!string(mapid));
302 // ////////////////////////////////////////////////////////////////////////// //
303 import dacs.actor;
305 mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype
306 mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname
309 public ActorDef findActorDef (ActorId aid) {
310 if (!aid.valid) return null;
311 //conwriteln("findActorDef(", aid.id, ") '", aid.fget_classtype.get, ":", aid.fget_classname.get, "'");
312 return findActorDef(aid.fget_classtype, aid.fget_classname);
316 public ActorDef findActorDefByFullName (const(char)[] fullname) {
317 if (auto fd = fullname in actordefs) return *fd;
318 return null;
322 public ActorDef findActorDef (const(char)[] ctype, const(char)[] cname) {
323 if (ctype.length+cname.length > 1024) assert(0);
324 char[1025] buf;
325 buf[0..ctype.length] = ctype[];
326 buf[ctype.length] = ':';
327 buf[ctype.length+1..ctype.length+1+cname.length] = cname[];
328 if (auto fd = buf[0..ctype.length+1+cname.length] in actordefs) return *fd;
329 return null;
333 public ActorDef findActorDef (StrId ctype, StrId cname) { return findActorDef(ctype.get, cname.get); }
336 public ActorDef registerActorDef (string classtype, string classname) {
337 string fullname = classtype~":"~classname;
338 if (fullname !in actordefs) actordefs[fullname] = new ActorDef(classtype, classname);
339 if (auto fd = fullname in actordefs) return *fd;
340 assert(0);