Convert entity to a class

FossilOrigin-Name: 4b002822f954056510cbb1f85d7687784e26782f63c5995f01ac6488d0632d80
This commit is contained in:
Jonathan Schleifer 2025-03-20 16:04:35 +00:00
parent 34b31eb77f
commit d42f82f1ec
19 changed files with 222 additions and 122 deletions

View file

@ -94,6 +94,7 @@ VARP(minmillis, 0, 5, 1000);
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0) if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0)
fatal(@"Unable to initialize SDL"); fatal(@"Unable to initialize SDL");
initEntities();
initPlayers(); initPlayers();
log(@"net"); log(@"net");

5
src/Entity.h Normal file
View file

@ -0,0 +1,5 @@
#import "PersistentEntity.h"
@interface Entity: PersistentEntity
@property (nonatomic) bool spawned; // the only dynamic state of a map entity
@end

4
src/Entity.m Normal file
View file

@ -0,0 +1,4 @@
#import "Entity.h"
@implementation Entity
@end

11
src/PersistentEntity.h Normal file
View file

@ -0,0 +1,11 @@
#import <ObjFW/ObjFW.h>
// 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

8
src/PersistentEntity.m Normal file
View file

@ -0,0 +1,8 @@
#import "PersistentEntity.h"
@implementation PersistentEntity
+ (instancetype)entity
{
return [[self alloc] init];
}
@end

View file

@ -3,6 +3,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
#import "OFString+Cube.h" #import "OFString+Cube.h"
int nextmode = 0; // nextmode becomes gamemode after next map load int nextmode = 0; // nextmode becomes gamemode after next map load

View file

@ -3,6 +3,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
extern int clientnum; extern int clientnum;
extern bool c2sinit, senditemstoserver; extern bool c2sinit, senditemstoserver;
@ -280,7 +281,7 @@ localservertoclient(uchar *buf, int len)
case SV_ITEMSPAWN: { case SV_ITEMSPAWN: {
uint i = getint(p); uint i = getint(p);
setspawn(i, true); setspawn(i, true);
if (i >= (uint)ents.length()) if (i >= (uint)ents.count)
break; break;
OFVector3D v = OFVector3D v =
OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z); 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 case SV_EDITENT: // coop edit of ent
{ {
uint i = getint(p); 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; int to = ents[i].type;
ents[i].type = getint(p); ents[i].type = getint(p);
ents[i].x = getint(p); ents[i].x = getint(p);

View file

@ -10,6 +10,7 @@
#define _MAXDEFSTR 260 #define _MAXDEFSTR 260
@class Entity;
@class DynamicEntity; @class DynamicEntity;
@interface Cube: OFObject <OFApplicationDelegate> @interface Cube: OFObject <OFApplicationDelegate>
@ -76,18 +77,6 @@ enum {
MAXENTTYPES 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 #define MAPVERSION 5 // bump if map format changes, see worldio.cpp
// map file format header // map file format header
@ -264,7 +253,7 @@ extern DynamicEntity *player1;
// all the other clients (in multiplayer) // all the other clients (in multiplayer)
extern OFMutableArray *players; extern OFMutableArray *players;
extern bool editmode; extern bool editmode;
extern vector<entity> ents; // map entities extern OFMutableArray<Entity *> *ents; // map entities
extern OFVector3D worldpos; // current target of the crosshair in the world extern OFVector3D worldpos; // current target of the crosshair in the world
extern int lastmillis; // last time extern int lastmillis; // last time
extern int curtime; // current frame time extern int curtime; // current frame time

View file

@ -3,9 +3,10 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
#import "MapModelInfo.h" #import "MapModelInfo.h"
vector<entity> ents; OFMutableArray<Entity *> *ents;
static OFString *entmdlnames[] = { static OFString *entmdlnames[] = {
@"shells", @"shells",
@ -23,7 +24,13 @@ static OFString *entmdlnames[] = {
int triggertime = 0; int triggertime = 0;
void 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) int numf = 1, int basetime = 0, float speed = 10.0f)
{ {
rendermodel(mdlname, frame, numf, 0, 1.1f, rendermodel(mdlname, frame, numf, 0, 1.1f,
@ -36,9 +43,8 @@ renderentities()
{ {
if (lastmillis > triggertime + 1000) if (lastmillis > triggertime + 1000)
triggertime = 0; triggertime = 0;
loopv(ents)
{ for (Entity *e in ents) {
entity &e = ents[i];
if (e.type == MAPMODEL) { if (e.type == MAPMODEL) {
MapModelInfo *mmi = getmminfo(e.attr2); MapModelInfo *mmi = getmminfo(e.attr2);
if (mmi == nil) if (mmi == nil)
@ -299,21 +305,24 @@ checkitems()
{ {
if (editmode) if (editmode)
return; return;
loopv(ents)
{ [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
entity &e = ents[i];
if (e.type == NOTUSED) if (e.type == NOTUSED)
continue; return;
if (!ents[i].spawned && e.type != TELEPORT && e.type != JUMPPAD)
continue; if (!e.spawned && e.type != TELEPORT && e.type != JUMPPAD)
return;
if (OUTBORD(e.x, e.y)) if (OUTBORD(e.x, e.y))
continue; return;
OFVector3D v = OFMakeVector3D( OFVector3D v = OFMakeVector3D(
e.x, e.y, (float)S(e.x, e.y)->floor + player1.eyeheight); e.x, e.y, (float)S(e.x, e.y)->floor + player1.eyeheight);
vdist(dist, t, player1.o, v); vdist(dist, t, player1.o, v);
if (dist < (e.type == TELEPORT ? 4 : 2.5)) if (dist < (e.type == TELEPORT ? 4 : 2.5))
pickup(i, player1); pickup(i, player1);
} }];
} }
void void
@ -329,22 +338,24 @@ checkquad(int time)
void void
putitems(uchar *&p) // puts items in network stream and also spawns them locally 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 enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
ents[i].type == CARROT) if ((e.type >= I_SHELLS && e.type <= I_QUAD) ||
{ e.type == CARROT) {
putint(p, i); putint(p, i);
ents[i].spawned = true; e.spawned = true;
} }
}];
} }
void void
resetspawns() resetspawns()
{ {
loopv(ents) ents[i].spawned = false; for (Entity *e in ents)
e.spawned = false;
} }
void void
setspawn(uint i, bool on) setspawn(uint i, bool on)
{ {
if (i < (uint)ents.length()) if (i < (uint)ents.count)
ents[i].spawned = on; ents[i].spawned = on;
} }

View file

@ -6,6 +6,7 @@ executable('client',
'ConsoleLine.m', 'ConsoleLine.m',
'Cube.mm', 'Cube.mm',
'DynamicEntity.mm', 'DynamicEntity.mm',
'Entity.m',
'Identifier.m', 'Identifier.m',
'KeyMapping.m', 'KeyMapping.m',
'MD2.mm', 'MD2.mm',
@ -13,6 +14,7 @@ executable('client',
'Menu.m', 'Menu.m',
'MenuItem.m', 'MenuItem.m',
'OFString+Cube.mm', 'OFString+Cube.mm',
'PersistentEntity.m',
'Projectile.m', 'Projectile.m',
'ResolverResult.mm', 'ResolverResult.mm',
'ResolverThread.mm', 'ResolverThread.mm',

View file

@ -3,6 +3,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
static OFMutableArray<DynamicEntity *> *monsters; static OFMutableArray<DynamicEntity *> *monsters;
static int nextmonster, spawnremain, numkilled, monstertotal, mtimestart; static int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;
@ -121,11 +122,14 @@ monsterclear()
monstertotal = spawnremain = gamemode < 0 ? skill * 10 : 0; monstertotal = spawnremain = gamemode < 0 ? skill * 10 : 0;
} else if (m_classicsp) { } else if (m_classicsp) {
mtimestart = lastmillis; mtimestart = lastmillis;
loopv(ents) if (ents[i].type == MONSTER)
{ for (Entity *e in ents) {
DynamicEntity *m = basicmonster( if (e.type != MONSTER)
ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0); continue;
m.o = OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z);
DynamicEntity *m =
basicmonster(e.attr2, e.attr1, M_SLEEP, 100, 0);
m.o = OFMakeVector3D(e.x, e.y, e.z);
entinmap(m); entinmap(m);
monstertotal++; monstertotal++;
} }
@ -377,13 +381,13 @@ monsterthink()
endsp(true); endsp(true);
// equivalent of player entity touch, but only teleports are used // equivalent of player entity touch, but only teleports are used
loopv(ents) [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
{
entity &e = ents[i];
if (e.type != TELEPORT) if (e.type != TELEPORT)
continue; return;
if (OUTBORD(e.x, e.y)) if (OUTBORD(e.x, e.y))
continue; return;
OFVector3D v = OFVector3D v =
OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor); OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
for (DynamicEntity *monster in monsters) { for (DynamicEntity *monster in monsters) {
@ -396,11 +400,12 @@ monsterthink()
v.z += monster.eyeheight; v.z += monster.eyeheight;
vdist(dist, t, monster.o, v); vdist(dist, t, monster.o, v);
v.z -= monster.eyeheight; v.z -= monster.eyeheight;
if (dist < 4) if (dist < 4)
teleport((int)(&e - &ents[0]), monster); teleport(i, monster);
} }
} }
} }];
for (DynamicEntity *monster in monsters) for (DynamicEntity *monster in monsters)
if (monster.state == CS_ALIVE) if (monster.state == CS_ALIVE)

View file

@ -7,6 +7,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
#import "MapModelInfo.h" #import "MapModelInfo.h"
// collide with player or monster // 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 void
mmcollide(DynamicEntity *d, float &hi, float &lo) // collide with a mapmodel mmcollide(DynamicEntity *d, float &hi, float &lo) // collide with a mapmodel
{ {
loopv(ents) for (Entity *e in ents) {
{
entity &e = ents[i];
if (e.type != MAPMODEL) if (e.type != MAPMODEL)
continue; continue;
MapModelInfo *mmi = getmminfo(e.attr2); MapModelInfo *mmi = getmminfo(e.attr2);
if (mmi == nil || !mmi.h) if (mmi == nil || !mmi.h)
continue; continue;
const float r = mmi.rad + d.radius; const float r = mmi.rad + d.radius;
if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) { if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) {
float mmz = float mmz =
(float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3); (float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3);
if (d.o.z - d.eyeheight < mmz) { if (d.o.z - d.eyeheight < mmz) {
if (mmz < hi) if (mmz < hi)
hi = mmz; hi = mmz;

View file

@ -121,7 +121,7 @@ extern int findentity(int type, int index = 0);
extern void trigger(int tag, int type, bool savegame); extern void trigger(int tag, int type, bool savegame);
extern void resettagareas(); extern void resettagareas();
extern void settagareas(); 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); int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4);
// worldlight // worldlight
@ -227,7 +227,7 @@ extern void putint(uchar *&p, int n);
extern int getint(uchar *&p); extern int getint(uchar *&p);
extern void sendstring(OFString *t, uchar *&p); extern void sendstring(OFString *t, uchar *&p);
extern void startintermission(); extern void startintermission();
extern void restoreserverstate(vector<entity> &ents); extern void restoreserverstate(OFArray<Entity *> *ents);
extern uchar *retrieveservers(uchar *buf, int buflen); extern uchar *retrieveservers(uchar *buf, int buflen);
extern char msgsizelookup(int msg); extern char msgsizelookup(int msg);
extern void serverms(int mode, int numplayers, int minremain, 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); extern void endsp(bool allkilled);
// entities // entities
extern void initEntities();
extern void renderents(); extern void renderents();
extern void putitems(uchar *&p); extern void putitems(uchar *&p);
extern void checkquad(int time); extern void checkquad(int time);

View file

@ -3,6 +3,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
void void
line(int x1, int y1, float z1, int x2, int y2, float z2) 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 void
renderents() // show sparkly thingies for map entities in edit mode renderents()
{ {
closeent = @""; closeent = @"";
if (!editmode) if (!editmode)
return; return;
loopv(ents)
{ for (Entity *e in ents) {
entity &e = ents[i];
if (e.type == NOTUSED) if (e.type == NOTUSED)
continue; continue;
OFVector3D v = OFMakeVector3D(e.x, e.y, e.z); OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
particle_splash(2, 2, 40, v); particle_splash(2, 2, 40, v);
} }
int e = closestent(); int e = closestent();
if (e >= 0) { if (e >= 0) {
entity &c = ents[e]; Entity *c = ents[e];
closeent = closeent =
[OFString stringWithFormat:@"closest entity = %@ (%d, %d, " [OFString stringWithFormat:@"closest entity = %@ (%d, %d, "
@"%d, %d), selection = (%d, %d)", @"%d, %d), selection = (%d, %d)",

View file

@ -4,6 +4,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
#ifdef OF_BIG_ENDIAN #ifdef OF_BIG_ENDIAN
static const int islittleendian = 0; static const int islittleendian = 0;
@ -109,8 +110,9 @@ savestate(OFIRI *IRI)
min(getclientmap().UTF8StringLength, _MAXDEFSTR - 1)); min(getclientmap().UTF8StringLength, _MAXDEFSTR - 1));
gzwrite(f, map, _MAXDEFSTR); gzwrite(f, map, _MAXDEFSTR);
gzputi(gamemode); gzputi(gamemode);
gzputi(ents.length()); gzputi(ents.count);
loopv(ents) gzputc(f, ents[i].spawned); for (Entity *e in ents)
gzputc(f, e.spawned);
gzwrite(f, data.items, data.count); gzwrite(f, data.items, data.count);
OFArray<DynamicEntity *> *monsters = getmonsters(); OFArray<DynamicEntity *> *monsters = getmonsters();
gzputi(monsters.count); gzputi(monsters.count);
@ -205,14 +207,16 @@ loadgamerest()
if (demoplayback || !f) if (demoplayback || !f)
return; return;
if (gzgeti() != ents.length()) if (gzgeti() != ents.count)
return loadgameout(); return loadgameout();
loopv(ents)
{ for (Entity *e in ents) {
ents[i].spawned = gzgetc(f) != 0; e.spawned = (gzgetc(f) != 0);
if (ents[i].type == CARROT && !ents[i].spawned)
trigger(ents[i].attr1, ents[i].attr2, true); if (e.type == CARROT && !e.spawned)
trigger(e.attr1, e.attr2, true);
} }
restoreserverstate(ents); restoreserverstate(ents);
OFMutableData *data = OFMutableData *data =

View file

@ -4,6 +4,7 @@
#include "cube.h" #include "cube.h"
#import "Client.h" #import "Client.h"
#import "Entity.h"
enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };
@ -24,9 +25,9 @@ vector<server_entity> sents;
bool notgotitems = true; bool notgotitems = true;
int mode = 0; int mode = 0;
// hack: called from savegame code, only works in SP
void void
restoreserverstate( restoreserverstate(OFArray<Entity *> *ents)
vector<entity> &ents) // hack: called from savegame code, only works in SP
{ {
loopv(sents) loopv(sents)
{ {

View file

@ -3,6 +3,7 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
extern OFString *entnames[]; // lookup from map entities above to strings extern OFString *entnames[]; // lookup from map entities above to strings
@ -53,7 +54,11 @@ void
settagareas() settagareas()
{ {
settag(0, 1); 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 } // set for playing
void void
@ -256,21 +261,22 @@ closestent() // used for delent and edit mode ent display
{ {
if (noteditmode()) if (noteditmode())
return -1; return -1;
int best;
float bdist = 99999; __block int best;
loopv(ents) __block float bdist = 99999;
{ [ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
entity &e = ents[i];
if (e.type == NOTUSED) if (e.type == NOTUSED)
continue; return;
OFVector3D v = OFMakeVector3D(e.x, e.y, e.z); OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
vdist(dist, t, player1.o, v); vdist(dist, t, player1.o, v);
if (dist < bdist) { if (dist < bdist) {
best = i; best = i;
bdist = dist; bdist = dist;
} }
} }];
return bdist == 99999 ? -1 : best;
return (bdist == 99999 ? -1 : best);
} }
void void
@ -319,12 +325,21 @@ findtype(OFString *what)
return NOTUSED; return NOTUSED;
} }
entity * Entity *
newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4) newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4)
{ {
int type = findtype(what); 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) { switch (type) {
case LIGHT: case LIGHT:
if (v1 > 32) 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; e.attr1 = (int)player1.yaw;
break; 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); e.attr2, e.attr3, e.attr4);
ents.add(*((entity *)&e)); // unsafe!
[ents addObject:e]; // unsafe!
if (type == LIGHT) if (type == LIGHT)
calclight(); calclight();
return &ents.last();
return e;
} }
void void
clearents(OFString *name) clearents(OFString *name)
{ {
int type = findtype(name); int type = findtype(name);
if (noteditmode() || multiplayer()) if (noteditmode() || multiplayer())
return; return;
loopv(ents)
{ for (Entity *e in ents)
entity &e = ents[i];
if (e.type == type) if (e.type == type)
e.type = NOTUSED; e.type = NOTUSED;
}
if (type == LIGHT) if (type == LIGHT)
calclight(); calclight();
} }
COMMAND(clearents, ARG_1STR) COMMAND(clearents, ARG_1STR)
void static uchar
scalecomp(uchar &c, int intens) scalecomp(uchar c, int intens)
{ {
int n = c * intens / 100; int n = c * intens / 100;
if (n > 255) if (n > 255)
n = 255; n = 255;
c = n; return n;
} }
void void
scalelights(int f, int intens) scalelights(int f, int intens)
{ {
loopv(ents) for (Entity *e in ents) {
{
entity &e = ents[i];
if (e.type != LIGHT) if (e.type != LIGHT)
continue; continue;
e.attr1 = e.attr1 * f / 100; e.attr1 = e.attr1 * f / 100;
if (e.attr1 < 2) if (e.attr1 < 2)
e.attr1 = 2; e.attr1 = 2;
if (e.attr1 > 32) if (e.attr1 > 32)
e.attr1 = 32; e.attr1 = 32;
if (intens) { if (intens) {
scalecomp(e.attr2, intens); e.attr2 = scalecomp(e.attr2, intens);
scalecomp(e.attr3, intens); e.attr3 = scalecomp(e.attr3, intens);
scalecomp(e.attr4, intens); e.attr4 = scalecomp(e.attr4, intens);
} }
} }
calclight(); calclight();
} }
COMMAND(scalelights, ARG_2INT) COMMAND(scalelights, ARG_2INT)
@ -405,7 +424,7 @@ COMMAND(scalelights, ARG_2INT)
int int
findentity(int type, int index) 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) if (ents[i].type == type)
return i; return i;
loopj(index) if (ents[j].type == type) return j; 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) = *S(x + ssize / 4, y + ssize / 4) =
*SWS(oldworld, x, y, ssize / 2); *SWS(oldworld, x, y, ssize / 2);
} }
loopv(ents)
{ for (Entity *e in ents) {
ents[i].x += ssize / 4; e.x += ssize / 4;
ents[i].y += ssize / 4; e.y += ssize / 4;
} }
} else { } else {
char buffer[128] = "Untitled Map by Unknown"; char buffer[128] = "Untitled Map by Unknown";
@ -485,7 +504,7 @@ empty_world(int factor, bool force)
hdr.waterlevel = -100000; hdr.waterlevel = -100000;
loopi(15) hdr.reserved[i] = 0; loopi(15) hdr.reserved[i] = 0;
loopk(3) loopi(256) hdr.texlists[k][i] = i; loopk(3) loopi(256) hdr.texlists[k][i] = i;
ents.setsize(0); [ents removeAllObjects];
block b = { 8, 8, ssize - 16, ssize - 16 }; block b = { 8, 8, ssize - 16, ssize - 16 };
edittypexy(SPACE, b); edittypexy(SPACE, b);
} }

View file

@ -2,6 +2,15 @@
#include "cube.h" #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 void
backup(OFString *name, OFString *backupname) backup(OFString *name, OFString *backupname)
{ {
@ -173,15 +182,17 @@ save_world(OFString *mname)
} }
hdr.version = MAPVERSION; hdr.version = MAPVERSION;
hdr.numents = 0; 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; header tmp = hdr;
endianswap(&tmp.version, sizeof(int), 4); endianswap(&tmp.version, sizeof(int), 4);
endianswap(&tmp.waterlevel, sizeof(int), 16); endianswap(&tmp.waterlevel, sizeof(int), 16);
gzwrite(f, &tmp, sizeof(header)); gzwrite(f, &tmp, sizeof(header));
loopv(ents) for (Entity *e in ents) {
{ if (e.type != NOTUSED) {
if (ents[i].type != NOTUSED) { struct persistent_entity tmp = { e.x, e.y, e.z, e.attr1,
entity tmp = ents[i]; e.type, e.attr2, e.attr3, e.attr4 };
endianswap(&tmp, sizeof(short), 4); endianswap(&tmp, sizeof(short), 4);
gzwrite(f, &tmp, sizeof(persistent_entity)); gzwrite(f, &tmp, sizeof(persistent_entity));
} }
@ -273,13 +284,24 @@ load_world(OFString *mname) // still supports all map formats that have existed
} else { } else {
hdr.waterlevel = -100000; hdr.waterlevel = -100000;
} }
ents.setsize(0); [ents removeAllObjects];
loopi(hdr.numents) loopi(hdr.numents)
{ {
entity &e = ents.add(); struct persistent_entity tmp;
gzread(f, &e, sizeof(persistent_entity)); gzread(f, &tmp, sizeof(persistent_entity));
endianswap(&e, sizeof(short), 4); endianswap(&tmp, sizeof(short), 4);
e.spawned = false;
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.type == LIGHT) {
if (!e.attr2) if (!e.attr2)
e.attr2 = 255; // needed for MAPVERSION<=2 e.attr2 = 255; // needed for MAPVERSION<=2

View file

@ -3,14 +3,16 @@
#include "cube.h" #include "cube.h"
#import "DynamicEntity.h" #import "DynamicEntity.h"
#import "Entity.h"
#import "PersistentEntity.h"
extern bool hasoverbright; extern bool hasoverbright;
VAR(lightscale, 1, 4, 100); VAR(lightscale, 1, 4, 100);
// done in realtime, needs to be fast
void void
lightray(float bx, float by, lightray(float bx, float by, PersistentEntity *light)
persistent_entity &light) // done in realtime, needs to be fast
{ {
float lx = light.x + (rnd(21) - 10) * 0.1f; float lx = light.x + (rnd(21) - 10) * 0.1f;
float ly = light.y + (rnd(21) - 10) * 0.1f; float ly = light.y + (rnd(21) - 10) * 0.1f;
@ -121,7 +123,7 @@ lightray(float bx, float by,
} }
void void
calclightsource(persistent_entity &l) calclightsource(PersistentEntity *l)
{ {
int reach = l.attr1; int reach = l.attr1;
int sx = l.x - reach; int sx = l.x - reach;
@ -175,12 +177,9 @@ calclight()
s->r = s->g = s->b = 10; s->r = s->g = s->b = 10;
} }
loopv(ents) for (Entity *e in ents)
{
entity &e = ents[i];
if (e.type == LIGHT) if (e.type == LIGHT)
calclightsource(e); calclightsource(e);
}
block b = { 1, 1, ssize - 2, ssize - 2 }; block b = { 1, 1, ssize - 2, ssize - 2 };
postlightarea(b); postlightarea(b);
@ -236,8 +235,13 @@ dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach, int strength,
block *copy = blockcopy(b); block *copy = blockcopy(b);
[dlights addItem:&copy]; [dlights addItem:&copy];
persistent_entity l = { (short)v.x, (short)v.y, (short)v.z, PersistentEntity *l = [Entity entity];
(short)reach, LIGHT, (uchar)strength, 0, 0 }; l.x = v.x;
l.y = v.y;
l.z = v.z;
l.attr1 = reach;
l.type = LIGHT;
l.attr2 = strength;
calclightsource(l); calclightsource(l);
postlightarea(b); postlightarea(b);
} }