diff --git a/src/Cube.mm b/src/Cube.mm index 7d02bf5..a35899b 100644 --- a/src/Cube.mm +++ b/src/Cube.mm @@ -94,6 +94,7 @@ VARP(minmillis, 0, 5, 1000); if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0) fatal(@"Unable to initialize SDL"); + initEntities(); initPlayers(); log(@"net"); diff --git a/src/Entity.h b/src/Entity.h new file mode 100644 index 0000000..3cc4a5b --- /dev/null +++ b/src/Entity.h @@ -0,0 +1,5 @@ +#import "PersistentEntity.h" + +@interface Entity: PersistentEntity +@property (nonatomic) bool spawned; // the only dynamic state of a map entity +@end diff --git a/src/Entity.m b/src/Entity.m new file mode 100644 index 0000000..77e1d39 --- /dev/null +++ b/src/Entity.m @@ -0,0 +1,4 @@ +#import "Entity.h" + +@implementation Entity +@end diff --git a/src/PersistentEntity.h b/src/PersistentEntity.h new file mode 100644 index 0000000..accf8bd --- /dev/null +++ b/src/PersistentEntity.h @@ -0,0 +1,11 @@ +#import + +// map entity +@interface PersistentEntity: OFObject +@property (nonatomic) short x, y, z; // cube aligned position +@property (nonatomic) short attr1; +@property (nonatomic) unsigned char type; // type is one of the above +@property (nonatomic) unsigned char attr2, attr3, attr4; + ++ (instancetype)entity; +@end diff --git a/src/PersistentEntity.m b/src/PersistentEntity.m new file mode 100644 index 0000000..be145be --- /dev/null +++ b/src/PersistentEntity.m @@ -0,0 +1,8 @@ +#import "PersistentEntity.h" + +@implementation PersistentEntity ++ (instancetype)entity +{ + return [[self alloc] init]; +} +@end diff --git a/src/clientgame.mm b/src/clientgame.mm index 49e1755..970d79b 100644 --- a/src/clientgame.mm +++ b/src/clientgame.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" #import "OFString+Cube.h" int nextmode = 0; // nextmode becomes gamemode after next map load diff --git a/src/clients2c.mm b/src/clients2c.mm index 207d8d5..654aaa3 100644 --- a/src/clients2c.mm +++ b/src/clients2c.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" extern int clientnum; extern bool c2sinit, senditemstoserver; @@ -280,7 +281,7 @@ localservertoclient(uchar *buf, int len) case SV_ITEMSPAWN: { uint i = getint(p); setspawn(i, true); - if (i >= (uint)ents.length()) + if (i >= (uint)ents.count) break; OFVector3D v = OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z); @@ -328,8 +329,13 @@ localservertoclient(uchar *buf, int len) case SV_EDITENT: // coop edit of ent { uint i = getint(p); - while ((uint)ents.length() <= i) - ents.add().type = NOTUSED; + + while ((uint)ents.count <= i) { + Entity *e = [Entity entity]; + e.type = NOTUSED; + [ents addObject:e]; + } + int to = ents[i].type; ents[i].type = getint(p); ents[i].x = getint(p); diff --git a/src/cube.h b/src/cube.h index 7388cef..0fcb6b7 100644 --- a/src/cube.h +++ b/src/cube.h @@ -10,6 +10,7 @@ #define _MAXDEFSTR 260 +@class Entity; @class DynamicEntity; @interface Cube: OFObject @@ -76,18 +77,6 @@ enum { MAXENTTYPES }; -// map entity -struct persistent_entity { - short x, y, z; // cube aligned position - short attr1; - uchar type; // type is one of the above - uchar attr2, attr3, attr4; -}; - -struct entity: public persistent_entity { - bool spawned; // the only dynamic state of a map entity -}; - #define MAPVERSION 5 // bump if map format changes, see worldio.cpp // map file format header @@ -264,7 +253,7 @@ extern DynamicEntity *player1; // all the other clients (in multiplayer) extern OFMutableArray *players; extern bool editmode; -extern vector ents; // map entities +extern OFMutableArray *ents; // map entities extern OFVector3D worldpos; // current target of the crosshair in the world extern int lastmillis; // last time extern int curtime; // current frame time diff --git a/src/entities.mm b/src/entities.mm index 078fede..480b90a 100644 --- a/src/entities.mm +++ b/src/entities.mm @@ -3,9 +3,10 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" #import "MapModelInfo.h" -vector ents; +OFMutableArray *ents; static OFString *entmdlnames[] = { @"shells", @@ -23,7 +24,13 @@ static OFString *entmdlnames[] = { int triggertime = 0; void -renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0, +initEntities() +{ + ents = [[OFMutableArray alloc] init]; +} + +void +renderent(Entity *e, OFString *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) { rendermodel(mdlname, frame, numf, 0, 1.1f, @@ -36,9 +43,8 @@ renderentities() { if (lastmillis > triggertime + 1000) triggertime = 0; - loopv(ents) - { - entity &e = ents[i]; + + for (Entity *e in ents) { if (e.type == MAPMODEL) { MapModelInfo *mmi = getmminfo(e.attr2); if (mmi == nil) @@ -299,21 +305,24 @@ checkitems() { if (editmode) return; - loopv(ents) - { - entity &e = ents[i]; + + [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) { if (e.type == NOTUSED) - continue; - if (!ents[i].spawned && e.type != TELEPORT && e.type != JUMPPAD) - continue; + return; + + if (!e.spawned && e.type != TELEPORT && e.type != JUMPPAD) + return; + if (OUTBORD(e.x, e.y)) - continue; + return; + OFVector3D v = OFMakeVector3D( e.x, e.y, (float)S(e.x, e.y)->floor + player1.eyeheight); vdist(dist, t, player1.o, v); + if (dist < (e.type == TELEPORT ? 4 : 2.5)) pickup(i, player1); - } + }]; } void @@ -329,22 +338,24 @@ checkquad(int time) void putitems(uchar *&p) // puts items in network stream and also spawns them locally { - loopv(ents) if ((ents[i].type >= I_SHELLS && ents[i].type <= I_QUAD) || - ents[i].type == CARROT) - { - putint(p, i); - ents[i].spawned = true; - } + [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) { + if ((e.type >= I_SHELLS && e.type <= I_QUAD) || + e.type == CARROT) { + putint(p, i); + e.spawned = true; + } + }]; } void resetspawns() { - loopv(ents) ents[i].spawned = false; + for (Entity *e in ents) + e.spawned = false; } void setspawn(uint i, bool on) { - if (i < (uint)ents.length()) + if (i < (uint)ents.count) ents[i].spawned = on; } diff --git a/src/meson.build b/src/meson.build index 3d2e1bf..41f7394 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,6 +6,7 @@ executable('client', 'ConsoleLine.m', 'Cube.mm', 'DynamicEntity.mm', + 'Entity.m', 'Identifier.m', 'KeyMapping.m', 'MD2.mm', @@ -13,6 +14,7 @@ executable('client', 'Menu.m', 'MenuItem.m', 'OFString+Cube.mm', + 'PersistentEntity.m', 'Projectile.m', 'ResolverResult.mm', 'ResolverThread.mm', diff --git a/src/monster.mm b/src/monster.mm index 8aedf95..2a56f5a 100644 --- a/src/monster.mm +++ b/src/monster.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" static OFMutableArray *monsters; static int nextmonster, spawnremain, numkilled, monstertotal, mtimestart; @@ -121,11 +122,14 @@ monsterclear() monstertotal = spawnremain = gamemode < 0 ? skill * 10 : 0; } else if (m_classicsp) { mtimestart = lastmillis; - loopv(ents) if (ents[i].type == MONSTER) - { - DynamicEntity *m = basicmonster( - ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0); - m.o = OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z); + + for (Entity *e in ents) { + if (e.type != MONSTER) + continue; + + DynamicEntity *m = + basicmonster(e.attr2, e.attr1, M_SLEEP, 100, 0); + m.o = OFMakeVector3D(e.x, e.y, e.z); entinmap(m); monstertotal++; } @@ -377,13 +381,13 @@ monsterthink() endsp(true); // equivalent of player entity touch, but only teleports are used - loopv(ents) - { - entity &e = ents[i]; + [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) { if (e.type != TELEPORT) - continue; + return; + if (OUTBORD(e.x, e.y)) - continue; + return; + OFVector3D v = OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor); for (DynamicEntity *monster in monsters) { @@ -396,11 +400,12 @@ monsterthink() v.z += monster.eyeheight; vdist(dist, t, monster.o, v); v.z -= monster.eyeheight; + if (dist < 4) - teleport((int)(&e - &ents[0]), monster); + teleport(i, monster); } } - } + }]; for (DynamicEntity *monster in monsters) if (monster.state == CS_ALIVE) diff --git a/src/physics.mm b/src/physics.mm index fff6faf..8aa2759 100644 --- a/src/physics.mm +++ b/src/physics.mm @@ -7,6 +7,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" #import "MapModelInfo.h" // collide with player or monster @@ -58,18 +59,19 @@ cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, void mmcollide(DynamicEntity *d, float &hi, float &lo) // collide with a mapmodel { - loopv(ents) - { - entity &e = ents[i]; + for (Entity *e in ents) { if (e.type != MAPMODEL) continue; + MapModelInfo *mmi = getmminfo(e.attr2); if (mmi == nil || !mmi.h) continue; + const float r = mmi.rad + d.radius; if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) { float mmz = (float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3); + if (d.o.z - d.eyeheight < mmz) { if (mmz < hi) hi = mmz; diff --git a/src/protos.h b/src/protos.h index 17ba55a..4438c76 100644 --- a/src/protos.h +++ b/src/protos.h @@ -121,7 +121,7 @@ extern int findentity(int type, int index = 0); extern void trigger(int tag, int type, bool savegame); extern void resettagareas(); extern void settagareas(); -extern entity *newentity( +extern Entity *newentity( int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4); // worldlight @@ -227,7 +227,7 @@ extern void putint(uchar *&p, int n); extern int getint(uchar *&p); extern void sendstring(OFString *t, uchar *&p); extern void startintermission(); -extern void restoreserverstate(vector &ents); +extern void restoreserverstate(OFArray *ents); extern uchar *retrieveservers(uchar *buf, int buflen); extern char msgsizelookup(int msg); extern void serverms(int mode, int numplayers, int minremain, @@ -257,6 +257,7 @@ extern void monsterpain(DynamicEntity *m, int damage, DynamicEntity *d); extern void endsp(bool allkilled); // entities +extern void initEntities(); extern void renderents(); extern void putitems(uchar *&p); extern void checkquad(int time); diff --git a/src/renderextras.mm b/src/renderextras.mm index 54a3be7..4b5abce 100644 --- a/src/renderextras.mm +++ b/src/renderextras.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" void line(int x1, int y1, float z1, int x2, int y2, float z2) @@ -175,23 +176,26 @@ OFString *entnames[] = { @"?", }; +// show sparkly thingies for map entities in edit mode void -renderents() // show sparkly thingies for map entities in edit mode +renderents() { closeent = @""; + if (!editmode) return; - loopv(ents) - { - entity &e = ents[i]; + + for (Entity *e in ents) { if (e.type == NOTUSED) continue; + OFVector3D v = OFMakeVector3D(e.x, e.y, e.z); particle_splash(2, 2, 40, v); } + int e = closestent(); if (e >= 0) { - entity &c = ents[e]; + Entity *c = ents[e]; closeent = [OFString stringWithFormat:@"closest entity = %@ (%d, %d, " @"%d, %d), selection = (%d, %d)", diff --git a/src/savegamedemo.mm b/src/savegamedemo.mm index 498114c..0b374eb 100644 --- a/src/savegamedemo.mm +++ b/src/savegamedemo.mm @@ -4,6 +4,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" #ifdef OF_BIG_ENDIAN static const int islittleendian = 0; @@ -109,8 +110,9 @@ savestate(OFIRI *IRI) min(getclientmap().UTF8StringLength, _MAXDEFSTR - 1)); gzwrite(f, map, _MAXDEFSTR); gzputi(gamemode); - gzputi(ents.length()); - loopv(ents) gzputc(f, ents[i].spawned); + gzputi(ents.count); + for (Entity *e in ents) + gzputc(f, e.spawned); gzwrite(f, data.items, data.count); OFArray *monsters = getmonsters(); gzputi(monsters.count); @@ -205,14 +207,16 @@ loadgamerest() if (demoplayback || !f) return; - if (gzgeti() != ents.length()) + if (gzgeti() != ents.count) return loadgameout(); - loopv(ents) - { - ents[i].spawned = gzgetc(f) != 0; - if (ents[i].type == CARROT && !ents[i].spawned) - trigger(ents[i].attr1, ents[i].attr2, true); + + for (Entity *e in ents) { + e.spawned = (gzgetc(f) != 0); + + if (e.type == CARROT && !e.spawned) + trigger(e.attr1, e.attr2, true); } + restoreserverstate(ents); OFMutableData *data = diff --git a/src/server.mm b/src/server.mm index eb30738..587b543 100644 --- a/src/server.mm +++ b/src/server.mm @@ -4,6 +4,7 @@ #include "cube.h" #import "Client.h" +#import "Entity.h" enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; @@ -24,9 +25,9 @@ vector sents; bool notgotitems = true; int mode = 0; +// hack: called from savegame code, only works in SP void -restoreserverstate( - vector &ents) // hack: called from savegame code, only works in SP +restoreserverstate(OFArray *ents) { loopv(sents) { diff --git a/src/world.mm b/src/world.mm index a2a32d6..b6a66a7 100644 --- a/src/world.mm +++ b/src/world.mm @@ -3,6 +3,7 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" extern OFString *entnames[]; // lookup from map entities above to strings @@ -53,7 +54,11 @@ void settagareas() { settag(0, 1); - loopv(ents) if (ents[i].type == CARROT) setspawn(i, true); + + [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) { + if (ents[i].type == CARROT) + setspawn(i, true); + }]; } // set for playing void @@ -256,21 +261,22 @@ closestent() // used for delent and edit mode ent display { if (noteditmode()) return -1; - int best; - float bdist = 99999; - loopv(ents) - { - entity &e = ents[i]; + + __block int best; + __block float bdist = 99999; + [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) { if (e.type == NOTUSED) - continue; + return; + OFVector3D v = OFMakeVector3D(e.x, e.y, e.z); vdist(dist, t, player1.o, v); if (dist < bdist) { best = i; bdist = dist; } - } - return bdist == 99999 ? -1 : best; + }]; + + return (bdist == 99999 ? -1 : best); } void @@ -319,12 +325,21 @@ findtype(OFString *what) return NOTUSED; } -entity * +Entity * newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4) { int type = findtype(what); - persistent_entity e = { (short)x, (short)y, (short)z, (short)v1, - (uchar)type, (uchar)v2, (uchar)v3, (uchar)v4 }; + + PersistentEntity *e = [PersistentEntity entity]; + e.x = x; + e.y = y; + e.z = z; + e.attr1 = v1; + e.type = type; + e.attr2 = v2; + e.attr3 = v3; + e.attr4 = v4; + switch (type) { case LIGHT: if (v1 > 32) @@ -345,59 +360,63 @@ newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4) e.attr1 = (int)player1.yaw; break; } - addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1, + addmsg(1, 10, SV_EDITENT, ents.count, type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); - ents.add(*((entity *)&e)); // unsafe! + + [ents addObject:e]; // unsafe! + if (type == LIGHT) calclight(); - return &ents.last(); + + return e; } void clearents(OFString *name) { int type = findtype(name); + if (noteditmode() || multiplayer()) return; - loopv(ents) - { - entity &e = ents[i]; + + for (Entity *e in ents) if (e.type == type) e.type = NOTUSED; - } + if (type == LIGHT) calclight(); } COMMAND(clearents, ARG_1STR) -void -scalecomp(uchar &c, int intens) +static uchar +scalecomp(uchar c, int intens) { int n = c * intens / 100; if (n > 255) n = 255; - c = n; + return n; } void scalelights(int f, int intens) { - loopv(ents) - { - entity &e = ents[i]; + for (Entity *e in ents) { if (e.type != LIGHT) continue; + e.attr1 = e.attr1 * f / 100; if (e.attr1 < 2) e.attr1 = 2; if (e.attr1 > 32) e.attr1 = 32; + if (intens) { - scalecomp(e.attr2, intens); - scalecomp(e.attr3, intens); - scalecomp(e.attr4, intens); + e.attr2 = scalecomp(e.attr2, intens); + e.attr3 = scalecomp(e.attr3, intens); + e.attr4 = scalecomp(e.attr4, intens); } } + calclight(); } COMMAND(scalelights, ARG_2INT) @@ -405,7 +424,7 @@ COMMAND(scalelights, ARG_2INT) int findentity(int type, int index) { - for (int i = index; i < ents.length(); i++) + for (int i = index; i < ents.count; i++) if (ents[i].type == type) return i; loopj(index) if (ents[j].type == type) return j; @@ -474,10 +493,10 @@ empty_world(int factor, bool force) *S(x + ssize / 4, y + ssize / 4) = *SWS(oldworld, x, y, ssize / 2); } - loopv(ents) - { - ents[i].x += ssize / 4; - ents[i].y += ssize / 4; + + for (Entity *e in ents) { + e.x += ssize / 4; + e.y += ssize / 4; } } else { char buffer[128] = "Untitled Map by Unknown"; @@ -485,7 +504,7 @@ empty_world(int factor, bool force) hdr.waterlevel = -100000; loopi(15) hdr.reserved[i] = 0; loopk(3) loopi(256) hdr.texlists[k][i] = i; - ents.setsize(0); + [ents removeAllObjects]; block b = { 8, 8, ssize - 16, ssize - 16 }; edittypexy(SPACE, b); } diff --git a/src/worldio.mm b/src/worldio.mm index 943f745..b8ef9bb 100644 --- a/src/worldio.mm +++ b/src/worldio.mm @@ -2,6 +2,15 @@ #include "cube.h" +#import "Entity.h" + +struct persistent_entity { + short x, y, z; // cube aligned position + short attr1; + uchar type; // type is one of the above + uchar attr2, attr3, attr4; +}; + void backup(OFString *name, OFString *backupname) { @@ -173,15 +182,17 @@ save_world(OFString *mname) } hdr.version = MAPVERSION; hdr.numents = 0; - loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++; + for (Entity *e in ents) + if (e.type != NOTUSED) + hdr.numents++; header tmp = hdr; endianswap(&tmp.version, sizeof(int), 4); endianswap(&tmp.waterlevel, sizeof(int), 16); gzwrite(f, &tmp, sizeof(header)); - loopv(ents) - { - if (ents[i].type != NOTUSED) { - entity tmp = ents[i]; + for (Entity *e in ents) { + if (e.type != NOTUSED) { + struct persistent_entity tmp = { e.x, e.y, e.z, e.attr1, + e.type, e.attr2, e.attr3, e.attr4 }; endianswap(&tmp, sizeof(short), 4); gzwrite(f, &tmp, sizeof(persistent_entity)); } @@ -273,13 +284,24 @@ load_world(OFString *mname) // still supports all map formats that have existed } else { hdr.waterlevel = -100000; } - ents.setsize(0); + [ents removeAllObjects]; loopi(hdr.numents) { - entity &e = ents.add(); - gzread(f, &e, sizeof(persistent_entity)); - endianswap(&e, sizeof(short), 4); - e.spawned = false; + struct persistent_entity tmp; + gzread(f, &tmp, sizeof(persistent_entity)); + endianswap(&tmp, sizeof(short), 4); + + Entity *e = [Entity entity]; + e.x = tmp.x; + e.y = tmp.y; + e.z = tmp.z; + e.attr1 = tmp.attr1; + e.type = tmp.type; + e.attr2 = tmp.attr2; + e.attr3 = tmp.attr3; + e.attr4 = tmp.attr4; + [ents addObject:e]; + if (e.type == LIGHT) { if (!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2 diff --git a/src/worldlight.mm b/src/worldlight.mm index 58fe2ff..95a32e4 100644 --- a/src/worldlight.mm +++ b/src/worldlight.mm @@ -3,14 +3,16 @@ #include "cube.h" #import "DynamicEntity.h" +#import "Entity.h" +#import "PersistentEntity.h" extern bool hasoverbright; VAR(lightscale, 1, 4, 100); +// done in realtime, needs to be fast void -lightray(float bx, float by, - persistent_entity &light) // done in realtime, needs to be fast +lightray(float bx, float by, PersistentEntity *light) { float lx = light.x + (rnd(21) - 10) * 0.1f; float ly = light.y + (rnd(21) - 10) * 0.1f; @@ -121,7 +123,7 @@ lightray(float bx, float by, } void -calclightsource(persistent_entity &l) +calclightsource(PersistentEntity *l) { int reach = l.attr1; int sx = l.x - reach; @@ -175,12 +177,9 @@ calclight() s->r = s->g = s->b = 10; } - loopv(ents) - { - entity &e = ents[i]; + for (Entity *e in ents) if (e.type == LIGHT) calclightsource(e); - } block b = { 1, 1, ssize - 2, ssize - 2 }; postlightarea(b); @@ -236,8 +235,13 @@ dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach, int strength, block *copy = blockcopy(b); [dlights addItem:©]; - persistent_entity l = { (short)v.x, (short)v.y, (short)v.z, - (short)reach, LIGHT, (uchar)strength, 0, 0 }; + PersistentEntity *l = [Entity entity]; + l.x = v.x; + l.y = v.y; + l.z = v.z; + l.attr1 = reach; + l.type = LIGHT; + l.attr2 = strength; calclightsource(l); postlightarea(b); }