slightly better console management
[dd2d.git] / d2dadefs.d
blobaed4351dfae30344b1a0599005945513242a8d9d
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 iv.glbinds;
22 import glutils;
23 import console;
24 import dacs;
25 import d2dimage;
26 import d2dsprite;
29 // ////////////////////////////////////////////////////////////////////////// //
30 private enum BIT(ubyte n) = (1U<<n);
33 // ////////////////////////////////////////////////////////////////////////// //
34 // thing flags
35 public enum {
36 AF_NOCOLLISION = BIT!(0),
37 AF_NOGRAVITY = BIT!(1),
38 AF_NOTHINK = BIT!(2),
39 AF_NOONTOUCH = BIT!(3), // `onTouch` will never be called with `me` for this actor
40 AF_NODRAW = BIT!(4), // don't draw sprite
41 AF_NOLIGHT = BIT!(5), // no attached light
42 AF_NOANIMATE = BIT!(6), // don't do animation
43 AF_PIXEL = BIT!(7), // draw pixel, color is `s`
44 AF_TELEPORT = BIT!(8), // don't interpolate camera; will be autoreset on next frame
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 public bool processCL (ref string[] args) {
162 static string h() (const(void)[] src) {
163 static immutable uint[5] g = [0x31c84b1, 0x95eed, 0x1c39, 0x55, 0x1];
164 uint a = 0; int c = 0; auto d = cast(const(ubyte)[])src; string e; ubyte f = 0x5a;
165 foreach (immutable b; d) { a += (b-0x21)*g[c++]; if (c == 5) { foreach (immutable _; 0..4) { e ~= ((a>>24)^(f^=0xff))&0xff; a <<= 8; } a = 0; c = 0; } }
166 if (c > 1) { a += g[--c]; foreach (immutable _; 0..c) { e ~= ((a>>24)^(f^=0xff))&0xff; a <<= 8; } }
167 return e;
170 for (usize idx = 1; idx < args.length; ++idx) {
171 string s = null;
172 if (args[idx] == h("Lj-`b^coRZ")) {
173 s = h("l86;\"KeCe2M0e21^coRZJglC6au@O$_Ks*Wf6Z40cqs6ff0@&oKfmL2f62ib^b4#!f6YOr`\\"~
174 "E/latM@m_)9@`Y8kb;^ikD\\bT_mi_C3>^KfauKf.Y-\"__^+jY6huq^bgmo^b`h_d<:3aau%:'K"~
175 "dkD2gj7<sKf6k%^iZaobV[+YKeC.tM0eJPdm)H1ei(<.eU#.s`&#SYMfc-");
176 } else if (args[idx] == h("Lj.0,a#Lj_dP]$!g]")) {
177 s = h("`=C<Xep>@bbVmR!_KLVpb<CV4bs-n2aYV=\"gj7@*dm)D[KmM]6_0WsCf0=sNb<CVCbW!R#^j="~
178 "3]KeCV7Ke:5&^gO>baYni3bW\")tKe:(g^j=B`^j=$Yf/:E&dm)E,YBkmAb<CV4bs-n@au@+^f/"~
179 "qA8ep>Ild66)Ca$5JYY>Buk^b*JQ^j=B`^j>H2a#Lj_dP]$!M0d5fKdk/$KM[AKnM7n+cTBQ(Ke"~
180 "CV3d<`hhd6EJ6d5&L``\\taeep>_!`%Ha(`%K%bKM^hrdQ?&FKeCV7Kb2TjcS*=ba\"u=2M0d)s_"~
181 "0XZfcnqqPKau?lKfms?a>:slep?L@b<E]iehk;t`\\N3lY>FUNKeCM*c?d_kep>e&f.+]sKdkG-f"~
182 "04mEbBhDpb<DOpKmMu@b<:P3bW!=`d5&L`a?.Qhau@+^__o5Ubs-n2atLUed<^UmY6i?tf0@h=^cmH"~
183 "-^j=$GKeCV7KauHgd4`Red3udpM,8nPj>=]!eh5*&_0X$ObqmI+Kepnud5&it_D9M\\Y>F::_01Mo"~
184 "ep>OsKeL>+bULe_d5&it_D9Mbbs-n2d5B'1M,8nPnM7n+mlSQL^aRVg`\\E<rdPK9+Kg4?Wep>J$Kf"~
185 "mI=a?PMXju^XKKe:P*_`Z$sb<:P3bp^^f_D6ftbs-n7co/a*gj7C+b!(YYKmMf:_0X-Tf/Ul:_0X$KK"~
186 "eq,&`]M7+KM\\ieb<Li0d4NLfKb_cqM0dQ$b'Sg]b<CV4bp^^f_D6g,b<CV4bp^^f_D8<@Kau?lKe1@"~
187 "peN:m!a#:k\"^j=Enb:\\Jtb;7-m^aP!fb<*]uKM_&#Kf6_/d<a\"hKg*:6_01Mob<CV3bV%.dbV%Im"~
188 "bs0-1YBkm8^j=Enb:\\K3`&>t$eM>?*_Ks6a`\\EBJYB]+b_01Mf`$pBmbr*joei(H&bBhPk^j>H2a"~
189 "#Lj_dP]$!RS\\]aju(4EKeCMoa<QC#f/M9(Kf@e5a$7L$bULe_a#2Y!Y8)aRf/q#,atM=l_)pG!Kc."~
190 "feaYh3rKR2cBaYq'^KRL%MKbD1I`\\P[bd5Jd(KepnubqmpscSaL\"Y>DD`nL_[.KfI[?aYq@+M0d)s_"~
191 "0XKa^j=QibV%-bKeCM!^aZI5Kc\\H%Kg\"3>^j<pU_0WsSa>h:+Kepnud5&L`dQ>K6Sl\"K+aa2#aa=5n"~
192 "jdQc>JKeCV7KfI4'b!!lu^aY+:KmMf:_0XKa^j='Za?PA,`%K:`Kf.40_C3>^^iZa");
193 } else if (args[idx] == h("Lj.<*f.+]s") || args[idx] == h("Lj.<*f.+]seh,#m_#") || args[idx] == h("Lj.<*f.+]sa#:k\"^]")) {
194 s = h("r&)iI_)9Dad<a&!dm&Nrbs-mjasbe%aa3,.`'1tYY5");
195 } else if (args[idx] == h("Lj.E7dm)\"")) {
196 s = h("p-7\"jbBh)T^j=B`^a\"\\^ej%VJbBh)T^j=B`^a\"M/lnjDKbs-nBbra**^c$m(_g9]c^j<g]^"
197 "ab$e^b!8XKfdmNKeCV7gj?4<bBhPk^j=To_)'\"d`]o/7_g9Ql`%8\\\\Kdjeg^bga,f0=sAbs1!1Y7o**d5&"~
198 "L``\\PIhb<O*paukA1bs-nG^d$@qbVY#8Kdt#!`&YVZ_C32OKeCV7gj7<sMbrV!aZkJ3bs1!0`$iEIb;I[jKfm"~
199 ".0`&>DWd5&K^KfdmNKf$q)MbrasKeCV7Kf@4>eh2Y6_g9]c^j<g]^ab%\"f0=sAbs0E#Kdt\"nbpeQInh\\0JK"~
200 "eCV7gj74,dm&OCatANsehtWsd5&K^KegRt^j=0]d<)Q,f06tuKfdIAKe^e._)/qVep>Uuat8<\"bs1!0b;I[hY"~
201 "6rlCgcsD:`&,;Veh>Q'aYUpud5/p%KegG*f6Yh\"^a\"OiatATKnM7n+`]MX6f//1;bBhPk^j='Zcns-3bVY#+"~
202 "bs1!0^bjG'f6\"2,b:^q`m6AO8`$iEId5&K^Kege\"d5/m/Mbrq!^j=B`f/:`9LcE13f0@k9Kf-bod5&L`_DT."~
203 "YKfdmNKeCV7giT_@bULpmf/Uu2_KsTb^j=*YaWlKqbBhPk^j=Bcf-nEkd4'c2f0=sAbs1!0ej%Q(Y8G29^c]t%"~
204 "KfdmNKeCV7gieB2`&>Cnco-S>f0=sAbs1!*Y6huq^bga,f0=sAbs1!,Kdjeg^bga,f0=sAbs1!,Kdjeg^bg`td"~
205 "PJm-Kfd.5bVY#8f0=sAbs1!*Y8>,%KeCV7KdkP.f/(f'Kf-bobs0,t^bg`n^c0%fa$81!Kf?ppKeCV7KdkP.f/"~
206 "(f'Kf-bo^cU.#c$Iu+a?Ra*^aP+;");
207 } else if (args[idx] == h("Lj-lsf0'")) {
208 s = h("niqgif0=sAbs-n!asb,KKbhf_^aOm]atM@mehYH6aZe:%`&,;Veh>W'^biklf6Z40cmXSsbqmm!Mbrt/"~
209 "Ke:M,aa1lh_0XlocmXT-bVdL\"_DB1naa1l^_0XKn`\\P^paYgpk`]M@i_)'XpbV[<lbUgOd^a[i\\d5?V8cmXT*"~
210 "dPQkYr&)hLKfRL<d4a*9Ke:M,aa1lh`%'/\\d5?V,^aRYf`]o/1eo\\)$^a[JVd6#N3^j=3RKfm^7bVm7YY5");
211 } else if (args[idx] == h("Lj-]nauI0bec") || args[idx] == h("Lcrm<a$80)")) {
213 if (s.length) {
214 import core.stdc.stdio : printf;
215 printf("%.*s", cast(uint)s.length, s.ptr);
216 foreach (immutable c; idx+1..args.length) args[c-1] = args[c];
217 args.length = args.length-1;
218 --idx;
219 return true;
222 return false;
226 // ////////////////////////////////////////////////////////////////////////// //
227 public final class ActorDef {
228 StrId classtype;
229 StrId classname;
231 // animation sequences for stated; keyed by state strid
232 StrId[][][uint] anims; // [statestrid][dir][pos]
234 FuncPool.FuncInfo fiAnimInit;
235 FuncPool.FuncInfo fiInit;
236 FuncPool.FuncInfo fiThink;
238 ImgSprite[][][uint] animSprites;
240 // load actor graphics
241 void loadGraphics () {
242 //conwriteln("loading graphics for '", fullname, "'; anims.length=", anims.length");
243 if (anims.length == 0) return; // no graphics here
244 // load sprites
245 foreach (auto anma2; anims.byKeyValue) {
246 //if (anma2.key !in animSprites) animSprites[anma2.key] = new ImgSprite[][](2); // 2 dirs
247 //auto aspx = animSprites[anma2.key];
248 if (anma2.key in animSprites) continue;
249 ImgSprite[][] aspx;
250 foreach (immutable dir, auto anma; anma2.value) {
251 ImgSprite[] dsp;
252 foreach (StrId ssid; anma) {
253 //if (dir) { conwriteln("dir: ", dir, "; [", ssid.get, "]"); }
254 dsp ~= loadSprite(ssid.get);
256 aspx ~= dsp;
258 animSprites[anma2.key] = aspx;
262 // unload actor graphics
263 void releaseGraphics () {
264 animSprites = null;
267 int nextAnimIdx (StrId state, uint dir, int curidx) {
268 if (++curidx <= 0) return 0;
269 if (auto spra = state.id in animSprites) {
270 if (dir != 0) dir = 1;
271 return (curidx < (*spra).ptr[dir].length ? curidx : 0);
273 return 0;
276 ImgSprite animSpr (StrId state, uint dir, int curidx) {
277 if (curidx < 0) return null;
278 if (auto spra = state.id in animSprites) {
279 if (dir != 0) dir = 1;
280 auto px = (*spra).ptr[dir];
281 return (curidx < px.length ? px.ptr[curidx] : null);
283 return null;
286 this (string ctype, string cname) {
287 classtype = StrPool.intern(ctype);
288 classname = StrPool.intern(cname);
291 void clearAllFrames (StrId state) {
292 anims.clear();
295 void clearFrames (StrId state) {
296 anims.remove(state.id);
299 void addFrame (StrId state, uint dir, StrId sprname) {
300 //conwriteln("new frame for '", fullname, "'; state=", state.get, "; sprname=", sprname.get, "; dir=", dir);
301 if (dir > 1) throw new Exception("addFrame: invalid dir");
302 if (state.id !in anims) anims[state.id] = new StrId[][](2);
303 auto aa = anims[state.id];
304 aa[dir] ~= sprname;
307 void setAnimInitFunc (FuncPool.FuncInfo fi) {
308 if (fi is null) return;
309 if (fi.mgtype != ":void") throw new Exception("invalid actor animation init function");
310 fiAnimInit = fi;
313 void setInitFunc (FuncPool.FuncInfo fi) {
314 if (fi is null) return;
315 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor init function");
316 fiInit = fi;
319 void setThinkFunc (FuncPool.FuncInfo fi) {
320 if (fi is null) return;
321 if (fi.mgtype != ":void:Actor") throw new Exception("invalid actor think function");
322 fiThink = fi;
325 void callAnimInit () {
326 if (fiAnimInit !is null) fiAnimInit();
329 void callInit (ActorId aid) {
330 if (fiInit is null || !aid.valid) return;
331 fiInit(aid);
334 void callThink (ActorId aid) {
335 if (fiThink is null || !aid.valid) return;
336 fiThink(aid);
339 static:
340 // return `true` to stop
341 bool forEach (bool delegate (ActorDef d) dg) {
342 foreach (ActorDef adef; actordefs.byValue) if (dg(adef)) return true;
343 return false;
346 void forEach (void delegate (ActorDef d) dg) {
347 foreach (ActorDef adef; actordefs.byValue) dg(adef);
352 // ////////////////////////////////////////////////////////////////////////// //
353 public ActorDef findD2DActorDef (ushort mapid) {
354 if (mapid == 0) assert(0);
355 if (mapid == 1 || mapid == 2) return findActorDef("monster", "Player");
356 if (auto dad = mapid in d2dactordefsById) {
357 auto adef = findActorDef(dad.classtype, dad.classname);
358 if (adef is null) throw new Exception("can't find DACS actor definition for D2D actor '"~dad.classtype.get~":"~dad.classname.get~"'");
359 return adef;
361 import std.conv : to;
362 throw new Exception("can't find D2D ActorDef for mapid "~to!string(mapid));
366 // ////////////////////////////////////////////////////////////////////////// //
367 import dacs.actor;
369 __gshared ActorDef[ulong] actordefs; // by (name<<32)|type
371 mixin(Actor.FieldGetMixin!("classtype", StrId)); // fget_classtype
372 mixin(Actor.FieldGetMixin!("classname", StrId)); // fget_classname
375 ulong ctn2idx (uint ctid, uint cnid) { pragma(inline, true); return ((cast(ulong)cnid)<<32)|ctid; }
376 ulong ctn2idx (StrId ct, StrId cn) { pragma(inline, true); return ctn2idx(ct.id, cn.id); }
379 public ActorDef findActorDef (ActorId aid) {
380 pragma(inline, true);
381 if (!aid.valid) return null;
382 return findActorDef(aid.fget_classtype, aid.fget_classname);
386 public ActorDef findActorDef (StrId ctype, StrId cname) { /*pragma(inline, true);*/ return actordefs.get(ctn2idx(ctype.id, cname.id), null); }
389 public ActorDef findActorDef (const(char)[] ctype, const(char)[] cname) {
390 auto ctid = ctype in StrPool;
391 auto cnid = cname in StrPool;
392 if (!ctid || !cnid) return null;
393 return actordefs.get(ctn2idx(ctid, cnid), null);
397 public ActorDef registerActorDef (string classtype, string classname) {
398 auto ct = StrPool.intern(classtype);
399 auto cn = StrPool.intern(classname);
400 if (findActorDef(ct, cn) is null) actordefs[ctn2idx(ct, cn)] = new ActorDef(classtype, classname);
401 return actordefs[ctn2idx(ct, cn)];