initial work for switch actors
[dd2d.git] / d2dadefs.d
blobcf1f5d3cc8df813e192bd3afde456ce5d5cc59a0
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
44 // ////////////////////////////////////////////////////////////////////////// //
45 // known D2D actors (and pseudoactors)
46 struct ActorDefD2D {
47 StrId classtype;
48 StrId classname;
49 ushort mapid; // thing id in map
51 this (string ctype, string cname, ushort thid=0) {
52 classtype = StrPool.intern(ctype);
53 classname = StrPool.intern(cname);
54 mapid = thid;
57 string toString () const {
58 import std.string : format;
59 return "ActorDefD2D(%s, %s, %s)".format(classtype.get, classname.get, mapid);
63 immutable ActorDefD2D[] d2dactordefs;
64 public immutable ActorDefD2D[ushort] d2dactordefsById; // by mapid
66 shared static this () {
67 d2dactordefs = [
68 ActorDefD2D("playerstart", "Player1", 1),
69 ActorDefD2D("playerstart", "Player2", 2),
70 ActorDefD2D("playerstart", "DMStart", 3),
71 ActorDefD2D("item", "Clip", 100),
72 ActorDefD2D("item", "Shell", 101),
73 ActorDefD2D("item", "Rocket", 102),
74 ActorDefD2D("item", "Cell", 103),
75 ActorDefD2D("item", "Ammo", 104),
76 ActorDefD2D("item", "ShellBox", 105),
77 ActorDefD2D("item", "RocketBox", 106),
78 ActorDefD2D("item", "CellPack", 107),
79 ActorDefD2D("item", "StimPack", 108),
80 ActorDefD2D("item", "MediKit", 109),
81 ActorDefD2D("item", "BackPack", 110),
82 ActorDefD2D("item", "Chainsaw", 111),
83 ActorDefD2D("item", "Shotgun", 112),
84 ActorDefD2D("item", "SuperShotgun", 113),
85 ActorDefD2D("item", "MachineGun", 114),
86 ActorDefD2D("item", "RocketLauncher", 115),
87 ActorDefD2D("item", "Plasmagun", 116),
88 ActorDefD2D("item", "BFG900", 117),
89 ActorDefD2D("item", "ArmorGreen", 118),
90 ActorDefD2D("item", "ArmorBlue", 119),
91 ActorDefD2D("item", "MegaSphere", 120),
92 ActorDefD2D("item", "Invulnerability", 121),
93 ActorDefD2D("item", "Aqualung", 122),
94 ActorDefD2D("item", "KeyRed", 123),
95 ActorDefD2D("item", "KeyGreen", 124),
96 ActorDefD2D("item", "KeyBlue", 125),
97 ActorDefD2D("item", "ProtectionSuit", 126),
98 ActorDefD2D("item", "Super", 127),
99 ActorDefD2D("item", "TorchRed", 128),
100 ActorDefD2D("item", "TorchGreen", 129),
101 ActorDefD2D("item", "TorchBlue", 130),
102 ActorDefD2D("item", "Gor1", 131),
103 ActorDefD2D("item", "FCan", 132),
104 ActorDefD2D("item", "Gun2", 133),
105 ActorDefD2D("monster", "Demon", 200),
106 ActorDefD2D("monster", "Imp", 201),
107 ActorDefD2D("monster", "Zombie", 202),
108 ActorDefD2D("monster", "Sergeant", 203),
109 ActorDefD2D("monster", "Cyberdemon", 204),
110 ActorDefD2D("monster", "Chaingunner", 205),
111 ActorDefD2D("monster", "BaronOfHell", 206),
112 ActorDefD2D("monster", "HellKnight", 207),
113 ActorDefD2D("monster", "Cacodemon", 208),
114 ActorDefD2D("monster", "LostSoul", 209),
115 ActorDefD2D("monster", "PainElemental", 210),
116 ActorDefD2D("monster", "SpiderMastermind", 211),
117 ActorDefD2D("monster", "Arachnotron", 212),
118 ActorDefD2D("monster", "Mancubus", 213),
119 ActorDefD2D("monster", "Revenant", 214),
120 ActorDefD2D("monster", "Archvile", 215),
121 ActorDefD2D("monster", "Fish", 216),
122 ActorDefD2D("monster", "Barrel", 217),
123 ActorDefD2D("monster", "Robot", 218),
124 ActorDefD2D("monster", "Man", 219),
126 foreach (const ref ActorDefD2D d2da; d2dactordefs) {
127 if (d2da.mapid) d2dactordefsById[d2da.mapid] = d2da;
132 // ////////////////////////////////////////////////////////////////////////// //
133 public string getD2DSwitchClassName (uint swtype) {
134 switch (swtype) {
135 case 1: return "Exit"; // SW_EXIT
136 case 2: return "ExitSecret"; // SW_EXITS
137 case 3: return "DoorOpen"; // SW_OPENDOOR
138 case 4: return "DoorClose"; // SW_SHUTDOOR
139 case 5: return "TrapClose"; // SW_SHUTTRAP
140 case 6: return "Door"; // SW_DOOR
141 case 7: return "Door5"; // SW_DOOR5
142 case 8: return "Press"; // SW_PRESS
143 case 9: return "Tele"; // SW_TELE
144 case 10: return "Secret"; // SW_SECRET
145 case 11: return "LiftUp"; // SW_LIFTUP
146 case 12: return "LiftDown"; // SW_LIFTDOWN
147 case 13: return "Trap"; // SW_TRAP
148 case 14: return "Lift"; // SW_LIFT
149 case 64: return "WinGame"; // SW_WINGAME
150 default:
152 return null;
156 // ////////////////////////////////////////////////////////////////////////// //
157 public struct ImgSprite {
158 D2DImage vga;
159 Texture tex;
160 bool mirrored;
162 @disable this (this);
166 // ////////////////////////////////////////////////////////////////////////// //
167 public final class ActorDef {
168 StrId classtype;
169 StrId classname;
171 // animation sequences for stated; keyed by state strid
172 StrId[][][uint] anims; // [statestrid][dir][pos]
174 FuncPool.FuncInfo[] fiAnimInit;
175 FuncPool.FuncInfo fiInit;
176 FuncPool.FuncInfo fiThink;
178 __gshared ImgSprite[uint] allSprites; // to avoid loading sprites twice they are keyed by namestrid
179 ImgSprite*[][][uint] animSprites;
180 //__gshared ImgSprite*[][] animCur;
182 // load actor graphics
183 void loadGraphics () {
184 //conwriteln("loading graphics for '", fullname, "'; anims.length=", anims.length, "; allSprites.length=", allSprites.length);
185 if (anims.length == 0) return; // no graphics here
186 //if (allSprites.length > 0) return;
187 // load sprites
188 foreach (auto anma2; anims.byValue) {
189 foreach (auto anma; anma2) {
190 foreach (StrId ssid; anma) {
191 //conwriteln(fullname, ": ssid=", ssid.id, "; is ", ssid.get);
192 ImgSprite* spi = null;
193 if (ssid.id != 0) {
194 spi = ssid.id in allSprites;
195 if (spi is null) {
196 //conwriteln("loading sprite '", ssid.get, "'");
197 D2DImage vga = new D2DImage(ssid.get);
198 allSprites[ssid.id] = ImgSprite();
199 spi = ssid.id in allSprites;
200 assert(spi !is null);
201 spi.vga = vga;
202 spi.tex = new Texture(spi.vga.asTCImage, Texture.Option.Nearest);
203 } else {
204 //conwriteln("cached sprite '", ssid.get, "'");
210 // now fill animSprites
211 foreach (auto anma2; anims.byKeyValue) {
212 if (anma2.key !in animSprites) animSprites[anma2.key] = new ImgSprite*[][](2); // 2 dirs
213 auto aspx = animSprites[anma2.key];
214 foreach (immutable dir, auto anma; anma2.value) {
215 foreach (StrId ssid; anma) {
216 ImgSprite* spi = null;
217 if (ssid.id != 0) spi = ssid.id in allSprites;
218 aspx[dir] ~= spi;
224 // unload actor graphics
225 void releaseGraphics () {
226 animSprites = null;
227 //allSprites.clear();
230 int nextAnimIdx (StrId state, uint dir, int curidx) {
231 if (dir != 0) dir = 1;
232 if (++curidx <= 0) return 0;
233 if (auto spra = state.id in animSprites) {
234 if (curidx < (*spra).ptr[dir].length) return curidx;
235 return 0;
237 return 0;
240 ImgSprite* animSpr (StrId state, uint dir, int curidx) {
241 if (dir != 0) dir = 1;
242 if (curidx < 0) return null;
243 if (auto spra = state.id in animSprites) {
244 if (curidx >= (*spra).ptr[dir].length) return null;
245 return (*spra).ptr[dir].ptr[curidx];
247 return null;
250 this (string ctype, string cname) {
251 classtype = StrPool.intern(ctype);
252 classname = StrPool.intern(cname);
255 void clearAllFrames (StrId state) {
256 anims.clear();
259 void clearFrames (StrId state) {
260 anims.remove(state.id);
263 void addFrame (StrId state, uint dir, StrId sprname) {
264 //conwriteln("new frame for '", fullname, "'; state=", state.get, "; sprname=", sprname.get, "; dir=", dir);
265 if (dir > 1) throw new Exception("addFrame: invalid dir");
266 if (state.id !in anims) anims[state.id] = new StrId[][](2);
267 auto aa = anims[state.id];
268 aa[dir] ~= sprname;
271 void setAnimInitFunc (FuncPool.FuncInfo fi) {
272 if (fi is null) return;
273 if (fi.mgtype != ":void") throw new Exception("invalid actor animation init function");
274 fiAnimInit ~= fi;
277 void setInitFunc (FuncPool.FuncInfo fi) {
278 if (fi is null) return;
279 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor init function");
280 fiInit = fi;
283 void setThinkFunc (FuncPool.FuncInfo fi) {
284 if (fi is null) return;
285 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor think function");
286 fiThink = fi;
289 void callAnimInit () {
290 foreach (auto fi; fiAnimInit) if (fi !is null) fi();
293 void callInit (ActorId aid) {
294 if (fiInit is null || !aid.valid) return;
295 fiInit(aid);
298 void callThink (ActorId aid) {
299 if (fiThink is null || !aid.valid) return;
300 fiThink(aid);
303 static:
304 // return `true` to stop
305 bool forEach (bool delegate (ActorDef d) dg) {
306 foreach (ActorDef adef; actordefs.byValue) if (dg(adef)) return true;
307 return false;
310 void forEach (void delegate (ActorDef d) dg) {
311 foreach (ActorDef adef; actordefs.byValue) dg(adef);
316 // ////////////////////////////////////////////////////////////////////////// //
317 public ActorDef findD2DActorDef (ushort mapid) {
318 if (mapid == 0) assert(0);
319 if (mapid == 1 || mapid == 2) return findActorDef("monster", "Player");
320 if (auto dad = mapid in d2dactordefsById) {
321 auto adef = findActorDef(dad.classtype, dad.classname);
322 if (adef is null) throw new Exception("can't find DACS actor definition for D2D actor '"~dad.classtype.get~":"~dad.classname.get~"'");
323 return adef;
325 import std.conv : to;
326 throw new Exception("can't find D2D ActorDef for mapid "~to!string(mapid));
330 // ////////////////////////////////////////////////////////////////////////// //
331 import dacs.actor;
333 __gshared ActorDef[ulong] actordefs; // by (name<<32)|type
335 mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype
336 mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname
339 ulong ctn2idx (uint ctid, uint cnid) { pragma(inline, true); return ((cast(ulong)cnid)<<32)|ctid; }
340 ulong ctn2idx (StrId ct, StrId cn) { pragma(inline, true); return ctn2idx(ct.id, cn.id); }
343 public ActorDef findActorDef (ActorId aid) {
344 pragma(inline, true);
345 if (!aid.valid) return null;
346 return findActorDef(aid.fget_classtype, aid.fget_classname);
350 public ActorDef findActorDef (StrId ctype, StrId cname) { /*pragma(inline, true);*/ return actordefs.get(ctn2idx(ctype.id, cname.id), null); }
353 public ActorDef findActorDef (const(char)[] ctype, const(char)[] cname) {
354 auto ctid = ctype in StrPool;
355 auto cnid = cname in StrPool;
356 if (!ctid || !cnid) return null;
357 return actordefs.get(ctn2idx(ctid, cnid), null);
361 public ActorDef registerActorDef (string classtype, string classname) {
362 auto ct = StrPool.intern(classtype);
363 auto cn = StrPool.intern(classname);
364 if (findActorDef(ct, cn) is null) actordefs[ctn2idx(ct, cn)] = new ActorDef(classtype, classname);
365 return actordefs[ctn2idx(ct, cn)];