switched to GPLv3 ONLY, because i don't trust FSF anymore
[knightmare.git] / mesactor.d
blob58bd96dd6725c270494a11f597088250c61f8653
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module mesactor;
17 private:
19 import iv.cmdcon;
20 import iv.strex;
21 import iv.vfs.io;
22 import mesengine : Loc, TypeBase, TypeEnum, typeBool, typeInt, typeActor;
25 // ////////////////////////////////////////////////////////////////////////// //
26 public struct ActorId {
27 uint id; // 0: empty; low word is index, high word is counter
28 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return ((id&0xffff) == 0); }
29 @property bool alive () const nothrow @trusted @nogc { pragma(inline, true); return ((id&0xffff) > 0 && (id&0xffff) < actors.length && id == actors.ptr[id&0xffff][0]); }
31 // this is how MES does it
32 //@property bool opCast(T) () const nothrow @safe @nogc if (is(T == bool)) { pragma(inline, true); return (id != 0); }
36 public struct ActorField {
37 TypeBase type;
38 int ofs;
39 string name;
40 Loc loc;
42 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (type !is null); }
46 // ////////////////////////////////////////////////////////////////////////// //
47 __gshared int[][] actors; // first is always index; if bit 15 is set, this slot is free
48 __gshared int[string] actfields; // in actfieldlist
49 __gshared ActorField[] actfieldlist;
50 __gshared int actfieldsused = 1; // cells
51 __gshared int aliveActorCount = 0;
54 // ////////////////////////////////////////////////////////////////////////// //
55 public int NumObjects () nothrow @trusted @nogc { pragma(inline, true); return aliveActorCount; }
58 // ////////////////////////////////////////////////////////////////////////// //
59 public int[] actorGetById() (in ActorId aid) nothrow @trusted @nogc {
60 pragma(inline, true);
61 return (aid.alive ? actors.ptr[aid.id&0xffff] : null);
65 public int[] actorGetByIndex() (in int idx) nothrow @trusted @nogc {
66 pragma(inline, true);
67 return (idx >= 0 && idx < actors.length && (actors.ptr[idx].ptr[0]&0x8000) == 0 ? actors.ptr[idx] : null);
71 public ActorField actorFindField (const(char)[] name) {
72 if (auto aip = name in actfields) return actfieldlist[*aip];
73 return ActorField.init;
76 public ActorField* actorFindFieldPtr (const(char)[] name) {
77 if (auto aip = name in actfields) return &actfieldlist[*aip];
78 return null;
82 public ActorField actorCreateField(T:const(char)[]) (T name, TypeBase atype, in auto ref Loc loc) {
83 assert(atype !is null);
84 assert(atype.cellSize > 0);
85 assert(name.length);
86 if (auto aip = name in actfields) {
87 if (actfieldlist[*aip].type.same(atype)) return actfieldlist[*aip];
88 return ActorField.init;
90 ActorField af;
91 af.type = atype;
92 af.ofs = actfieldsused;
93 af.loc = loc;
94 static if (is(T == string)) af.name = name; else af.name = name.idup;
95 actfieldsused += atype.cellSize;
96 auto aidx = cast(int)actfieldlist.length;
97 actfieldlist ~= af;
98 actfields[af.name] = aidx;
99 return af;
103 // ////////////////////////////////////////////////////////////////////////// //
104 public ActorId spawnActor () {
105 // no actors, create actor pool
106 if (actors.length == 0) {
107 // [0] is reserved
108 actors.reserve(32768);
109 actors.length = 80; // arbitrary number
110 foreach (immutable idx, ref int[] aa; actors) {
111 aa = new int[](actfieldsused);
112 // set index
113 aa[0] = (cast(int)idx)|0x8000; // "free flag"
115 actors[0][0] = 0xffff_0000; // just in case
117 //TODO: use some kind of list or hash table instead of linear search
118 foreach (immutable idx, int[] aa; actors) {
119 if (idx && (aa.ptr[0]&0x8000) != 0) {
120 if ((aa.ptr[0]>>16) == 0xffff) aa.ptr[0] &= 0xffff; else aa.ptr[0] += 0x0001_0000;
121 aa.ptr[0] &= 0xffff_7fff; // clear "free" flag
122 aa[1..$] = 0; // clear instance
123 ++aliveActorCount;
124 return ActorId(aa[0]);
127 // add new actor
128 if (actors.length >= ushort.max/2-1) throw new Exception("too many alive actors");
129 int[] aa = new int[](actfieldsused);
130 aa[] = 0;
131 // set index
132 aa[0] = (cast(int)actors.length)|0x0001_0000;
133 actors ~= aa;
134 ++aliveActorCount;
135 return ActorId(aa[0]);
139 // mark actor as dead
140 public void killActor (ActorId aid) nothrow @trusted @nogc {
141 if (aid.alive) {
142 assert(aliveActorCount > 0);
143 int[] aa = actors.ptr[aid.id&0xffff];
144 assert(aa.length > 0);
145 assert((aa.ptr[0]&0x8000) == 0);
146 aa.ptr[0] |= 0x8000;
147 --aliveActorCount;
152 // move all alive actors to top, reset indicies
153 public void actorsPack () {
154 int dpos = 1;
155 foreach (immutable aidx, int[] aa; actors) {
156 if (aidx == 0) continue;
157 if ((aa.ptr[0]&0x8000) != 0) {
158 // set index
159 aa[0] = cast(int)aidx|0x8000; // "free flag"
160 continue;
162 // move alive actor to top
163 assert(dpos <= aidx);
164 aa[0] = dpos|0x0001; // reset index
165 if (dpos != aidx) {
166 actors.ptr[dpos][] = aa[]; // move data
167 aa[0] = cast(int)aidx|0x8000; // "free flag"
169 ++dpos;
171 assert(dpos-1 == aliveActorCount);
172 if (actors.length > 80 && dpos < 80) {
173 actors.length = 80;
174 actors.assumeSafeAppend;
179 public void actorsGC () {
180 int lastUsedActor = 0;
181 foreach (immutable aidx, int[] aa; actors) {
182 if (aidx == 0 || (aa.ptr[0]&0x8000) != 0) continue;
183 lastUsedActor = cast(int)aidx;
185 if (actors.length > 80 && lastUsedActor < 80) {
186 actors.length = 80;
187 actors.assumeSafeAppend;
192 // ////////////////////////////////////////////////////////////////////////// //
193 // must return `ActorId.init` when there are no more actors to iterate
194 public ActorId actorIterStart () nothrow @trusted @nogc {
195 foreach (immutable aidx, int[] aa; actors) {
196 if (aidx == 0 || (aa.ptr[0]&0x8000) != 0) continue;
197 return ActorId(aa.ptr[0]);
199 return ActorId.init;
203 // must return `ActorId.init` when there are no more actors to iterate
204 public ActorId actorIterNext (ActorId aid) nothrow @trusted @nogc {
205 if (aid.empty) return ActorId.init;
206 int aidx = cast(int)(aid.id&0xffff)+1;
207 while (aidx < actors.length && (actors.ptr[aidx].ptr[0]&0x8000) != 0) ++aidx;
208 if (aidx >= actors.length) return ActorId.init;
209 return ActorId(actors.ptr[aidx].ptr[0]);