Cube  Check-in [4b002822f9]

Overview
Comment:Convert entity to a class
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4b002822f954056510cbb1f85d7687784e26782f63c5995f01ac6488d0632d80
User & Date: js on 2025-03-20 16:04:35
Other Links: manifest | tags
Context
2025-03-20
16:36
Convert server_entity to a class check-in: 21584a40b6 user: js tags: trunk
16:04
Convert entity to a class check-in: 4b002822f9 user: js tags: trunk
13:21
Make more use of convenience methods check-in: 89fbd7a152 user: js tags: trunk
Changes

Modified src/Cube.mm from [fe88d055bb] to [91778984c2].

90
91
92
93
94
95
96

97
98
99
100
101
102
103
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104







+







		    createDirectoryAtIRI:
		        [_userDataIRI IRIByAppendingPathComponent:@"savegames"]
		           createParents:true];

		if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0)
			fatal(@"Unable to initialize SDL");

		initEntities();
		initPlayers();

		log(@"net");
		if (enet_initialize() < 0)
			fatal(@"Unable to initialise network module");

		initclient();

Added src/Entity.h version [6a40859f40].

Added src/Entity.m version [54155c553d].

Added src/PersistentEntity.h version [1383bfd2c4].

Added src/PersistentEntity.m version [2ab320312c].

Modified src/clientgame.mm from [bec4825c49] to [a964a181aa].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







// clientgame.cpp: core game related stuff

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"
#import "OFString+Cube.h"

int nextmode = 0; // nextmode becomes gamemode after next map load
VAR(gamemode, 1, 0, 0);

void
mode(int n)

Modified src/clients2c.mm from [1561f54e4a] to [84894b97fe].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







// client processing of the incoming network stream

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"

extern int clientnum;
extern bool c2sinit, senditemstoserver;
extern OFString *toservermap;
extern OFString *clientpassword;

void
276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291







-
+







			setspawn(getint(p), false);
			getint(p);
			break;

		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);
			playsound(S_ITEMSPAWN, &v);
			break;
		}

324
325
326
327
328
329
330

331
332






333
334
335
336
337
338
339
325
326
327
328
329
330
331
332


333
334
335
336
337
338
339
340
341
342
343
344
345







+
-
-
+
+
+
+
+
+







			}
			break;
		}

		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);
			ents[i].y = getint(p);
			ents[i].z = getint(p);
			ents[i].attr1 = getint(p);
			ents[i].attr2 = getint(p);

Modified src/cube.h from [63027fcfcd] to [b293c88539].

1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20












+







// one big bad include file for the whole engine... nasty!

#import <ObjFW/ObjFW.h>

#define gamma gamma__
#include <SDL2/SDL.h>
#undef gamma

#include "tools.h"

#define _MAXDEFSTR 260

@class Entity;
@class DynamicEntity;

@interface Cube: OFObject <OFApplicationDelegate>
@property (class, readonly, nonatomic) Cube *sharedInstance;
@property (readonly, nonatomic) SDL_Window *window;
@property (readonly, nonatomic) OFIRI *gameDataIRI, *userDataIRI;
@property (nonatomic) bool repeatsKeys;
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
73
74
75
76
77
78
79












80
81
82
83
84
85
86







-
-
-
-
-
-
-
-
-
-
-
-







	MAPMODEL, // attr1 = angle, attr2 = idx
	MONSTER,  // attr1 = angle, attr2 = monstertype
	CARROT,   // attr1 = tag, attr2 = type
	JUMPPAD,  // attr1 = zpush, attr2 = ypush, attr3 = xpush
	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
struct header {
	char head[4];   // "CUBE"
	int version;    // any >8bit quantity is a little indian
	int headersize; // sizeof(header)
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263







-
+







extern int sfactor, ssize;     // ssize = 2^sfactor
extern int cubicsize, mipsize; // cubicsize = ssize^2
// special client ent that receives input and acts as camera
extern DynamicEntity *player1;
// all the other clients (in multiplayer)
extern OFMutableArray *players;
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 int lastmillis;      // last time
extern int curtime;         // current frame time
extern int gamemode, nextmode;
extern int xtraverts;
extern bool demoplayback;

Modified src/entities.mm from [c332d89e7c] to [e562f26211].

1
2
3
4
5

6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25






26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
1
2
3
4
5
6
7
8

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43
44
45



46
47
48
49
50
51
52
53
54





+


-
+

















+
+
+
+
+
+
-
+












-
-
-
+
+







// entities.cpp: map entity related functions (pickup etc.)

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"
#import "MapModelInfo.h"

vector<entity> ents;
OFMutableArray<Entity *> *ents;

static OFString *entmdlnames[] = {
	@"shells",
	@"bullets",
	@"rockets",
	@"rrounds",
	@"health",
	@"boost",
	@"g_armour",
	@"y_armour",
	@"quad",
	@"teleporter",
};

int triggertime = 0;

void
initEntities()
{
	ents = [[OFMutableArray alloc] init];
}

void
renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0,
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,
	    OFMakeVector3D(e.x, z + S(e.x, e.y)->floor, e.y), yaw, 0, false,
	    1.0f, speed, 0, basetime);
}

void
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)
				continue;
			rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad,
			    OFMakeVector3D(e.x,
			        (float)S(e.x, e.y)->floor + mmi.zoff + e.attr3,
295
296
297
298
299
300
301
302
303

304

305

306
307
308




309

310

311
312
313

314
315
316

317
318
319
320
321
322
323
324
325
326
327
328
329
330
331

332
333


334
335
336
337




338
339
340
341
342

343

344
345
346
347
348

349
350
301
302
303
304
305
306
307


308

309
310
311



312
313
314
315
316
317

318
319
320
321
322
323
324

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341


342
343




344
345
346
347
348
349
350
351
352
353

354
355
356
357
358

359
360
361







-
-
+
-
+

+
-
-
-
+
+
+
+

+
-
+



+


-
+















+
-
-
+
+
-
-
-
-
+
+
+
+





+
-
+




-
+


}

void
checkitems()
{
	if (editmode)
		return;
	loopv(ents)
	{

		entity &e = ents[i];
	[ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
		if (e.type == NOTUSED)
			return;
			continue;
		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))
			return;
			continue;

		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
checkquad(int time)
{
	if (player1.quadmillis && (player1.quadmillis -= time) < 0) {
		player1.quadmillis = 0;
		playsoundc(S_PUPOUT);
		conoutf(@"quad damage is over");
	}
}

void
putitems(uchar *&p) // puts items in network stream and also spawns them locally
{
	[ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
	loopv(ents) if ((ents[i].type >= I_SHELLS && ents[i].type <= I_QUAD) ||
	    ents[i].type == CARROT)
		if ((e.type >= I_SHELLS && e.type <= I_QUAD) ||
		    e.type == CARROT) {
	{
		putint(p, i);
		ents[i].spawned = true;
	}
			putint(p, i);
			e.spawned = true;
		}
	}];
}

void
resetspawns()
{
	for (Entity *e in ents)
	loopv(ents) ents[i].spawned = false;
		e.spawned = false;
}
void
setspawn(uint i, bool on)
{
	if (i < (uint)ents.length())
	if (i < (uint)ents.count)
		ents[i].spawned = on;
}

Modified src/meson.build from [7a5be72f65] to [ded2471dd3].

1
2
3
4
5
6
7
8

9
10
11
12
13
14
15

16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24








+







+







executable('client',
  [
    'Alias.m',
    'Client.mm',
    'Command.mm',
    'ConsoleLine.m',
    'Cube.mm',
    'DynamicEntity.mm',
    'Entity.m',
    'Identifier.m',
    'KeyMapping.m',
    'MD2.mm',
    'MapModelInfo.m',
    'Menu.m',
    'MenuItem.m',
    'OFString+Cube.mm',
    'PersistentEntity.m',
    'Projectile.m',
    'ResolverResult.mm',
    'ResolverThread.mm',
    'ServerInfo.mm',
    'Variable.mm',
    'clients.mm',
    'clientextras.mm',

Modified src/monster.mm from [d6e394fd76] to [7bb8f140a9].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







// monster.cpp: implements AI for single player monsters, currently client only

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"

static OFMutableArray<DynamicEntity *> *monsters;
static int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;

VARF(skill, 1, 3, 10, conoutf(@"skill is now %d", skill));

OFArray<DynamicEntity *> *
117
118
119
120
121
122
123
124
125
126
127
128








129
130
131
132
133
134
135
118
119
120
121
122
123
124





125
126
127
128
129
130
131
132
133
134
135
136
137
138
139







-
-
-
-
-
+
+
+
+
+
+
+
+







	monstertotal = 0;
	spawnremain = 0;
	if (m_dmsp) {
		nextmonster = mtimestart = lastmillis + 10000;
		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++;
		}
	}
}

// height-correct line of sight for monster shooting/seeing
373
374
375
376
377
378
379
380

381
382
383

384

385

386

387
388
389
390
391
392
393
394
395
396
397
398

399
400

401
402
403

404
405
406
407
408
409
410
377
378
379
380
381
382
383

384


385
386

387
388
389

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404

405
406
407

408
409
410
411
412
413
414
415







-
+
-
-

+
-
+

+
-
+












+

-
+


-
+







		spawnmonster();
	}

	if (monstertotal && !spawnremain && numkilled == monstertotal)
		endsp(true);

	// 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)
			return;
			continue;

		if (OUTBORD(e.x, e.y))
			return;
			continue;

		OFVector3D v =
		    OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
		for (DynamicEntity *monster in monsters) {
			if (monster.state == CS_DEAD) {
				if (lastmillis - monster.lastaction < 2000) {
					monster.move = 0;
					moveplayer(monster, 1, false);
				}
			} else {
				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)
			monsteraction(monster);
}

void

Modified src/physics.mm from [850bd490e8] to [c9005101f7].

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17









+







// physics.cpp: no physics books were hurt nor consulted in the construction of
// this code. All physics computations and constants were invented on the fly
// and simply tweaked until they "felt right", and have no basis in reality.
// Collision detection is simplistic but very robust (uses discrete steps at
// fixed fps).

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"
#import "MapModelInfo.h"

// collide with player or monster
bool
plcollide(
    DynamicEntity *d, DynamicEntity *o, float &headspace, float &hi, float &lo)
{
54
55
56
57
58
59
60
61

62
63
64
65

66
67
68

69
70
71
72

73
74
75
76
77
78
79
55
56
57
58
59
60
61

62


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81







-
+
-
-


+



+




+







	}
	return stest;
}

void
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)
			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;
			} else if (mmz + mmi.h > lo)
				lo = mmz + mmi.h;
		}
	}

Modified src/protos.h from [ca691ec5c9] to [79ed2ad413].

117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131







-
+







extern void remip(block &b, int level = 0);
extern void remipmore(block &b, int level = 0);
extern int closestent();
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
extern void calclight();
extern void dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach,
    int strength, DynamicEntity *owner);
extern void cleardlights();
223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237







-
+







extern void localdisconnect();
extern void localclienttoserver(struct _ENetPacket *);
extern void serverslice(int seconds, unsigned int timeout);
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<entity> &ents);
extern void restoreserverstate(OFArray<Entity *> *ents);
extern uchar *retrieveservers(uchar *buf, int buflen);
extern char msgsizelookup(int msg);
extern void serverms(int mode, int numplayers, int minremain,
    OFString *smapname, int seconds, bool isfull);
extern void servermsinit(OFString *master, OFString *sdesc, bool listen);
extern void sendmaps(int n, OFString *mapname, int mapsize, uchar *mapdata);
extern ENetPacket *recvmap(int n);
253
254
255
256
257
258
259

260
261
262
263
264
265
266
267
268
269
270
271
272
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273







+













extern void monsterthink();
extern void monsterrender();
extern OFArray<DynamicEntity *> *getmonsters();
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);
extern void checkitems();
extern void realpickup(int n, DynamicEntity *d);
extern void renderentities();
extern void resetspawns();
extern void setspawn(uint i, bool on);
extern void teleport(int n, DynamicEntity *d);
extern void baseammo(int gun);

// rndmap
extern void perlinarea(block &b, int scale, int seed, int psize);

Modified src/renderextras.mm from [0275a674d6] to [32dfc98d1d].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







// renderextras.cpp: misc gl render code and the HUD

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"

void
line(int x1, int y1, float z1, int x2, int y2, float z2)
{
	glBegin(GL_POLYGON);
	glVertex3f((float)x1, z1, (float)y1);
	glVertex3f((float)x1, z1, y1 + 0.01f);
171
172
173
174
175
176
177

178
179

180
181

182
183
184
185
186


187
188

189
190
191

192
193
194

195
196
197
198
199
200
201
172
173
174
175
176
177
178
179
180

181
182
183
184
185
186



187
188
189
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205







+

-
+


+


-
-
-
+
+


+



+


-
+







	@"?",
	@"?",
	@"?",
	@"?",
	@"?",
};

// 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)",
		              entnames[c.type], c.attr1, c.attr2, c.attr3,
		              c.attr4, getvar(@"selxs"), getvar(@"selys")];
	}
}

Modified src/savegamedemo.mm from [b5a43a0cf0] to [58839045b8].

1
2
3
4
5
6

7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
14






+







// loading and saving of savegames & demos, dumps the spawn state of all
// mapents, the full state of all dynents (monsters + player)

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"

#ifdef OF_BIG_ENDIAN
static const int islittleendian = 0;
#else
static const int islittleendian = 1;
#endif

105
106
107
108
109
110
111
112
113



114
115
116
117
118
119
120
106
107
108
109
110
111
112


113
114
115
116
117
118
119
120
121
122







-
-
+
+
+







	OFData *data = [player1 dataBySerializing];
	gzputi(data.count);
	char map[_MAXDEFSTR] = { 0 };
	memcpy(map, getclientmap().UTF8String,
	    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<DynamicEntity *> *monsters = getmonsters();
	gzputi(monsters.count);
	for (DynamicEntity *monster in monsters) {
		data = [monster dataBySerializing];
		gzwrite(f, data.items, data.count);
	}
201
202
203
204
205
206
207
208

209
210
211
212
213
214






215

216
217
218
219
220
221
222
203
204
205
206
207
208
209

210
211





212
213
214
215
216
217
218
219
220
221
222
223
224
225
226







-
+

-
-
-
-
-
+
+
+
+
+
+

+








void
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 =
	    [OFMutableData dataWithCapacity:DynamicEntity.serializedSize];
	[data increaseCountBy:DynamicEntity.serializedSize];
	gzread(f, data.mutableItems, data.count);
	[player1 setFromSerializedData:data];

Modified src/server.mm from [1804a617a4] to [69c5d759fc].

1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

27
28

29
30
31
32
33
34
35
36
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

30

31
32
33
34
35
36
37






+




















+

-
+
-







// server.cpp: little more than enhanced multicaster
// runs dedicated or as client coroutine

#include "cube.h"

#import "Client.h"
#import "Entity.h"

enum { ST_EMPTY, ST_LOCAL, ST_TCPIP };

static OFMutableArray<Client *> *clients;

int maxclients = 8;
static OFString *smapname;

// server side version of "entity" type
struct server_entity {
	bool spawned;
	int spawnsecs;
};

vector<server_entity> sents;

// true when map has changed and waiting for clients to send item
bool notgotitems = true;
int mode = 0;

// hack: called from savegame code, only works in SP
void
restoreserverstate(
restoreserverstate(OFArray<Entity *> *ents)
    vector<entity> &ents) // hack: called from savegame code, only works in SP
{
	loopv(sents)
	{
		sents[i].spawned = ents[i].spawned;
		sents[i].spawnsecs = 0;
	}
}

Modified src/world.mm from [2ac8b85a8f] to [ffa7840406].

1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
13





+







// world.cpp: core map management stuff

#include "cube.h"

#import "DynamicEntity.h"
#import "Entity.h"

extern OFString *entnames[]; // lookup from map entities above to strings

sqr *world = NULL;
int sfactor, ssize, cubicsize, mipsize;

header hdr;
49
50
51
52
53
54
55


56



57
58
59
60
61
62
63
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
66
67
68







+
+
-
+
+
+







{
	settag(0, 0);
} // reset for editing or map saving
void
settagareas()
{
	settag(0, 1);

	[ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
	loopv(ents) if (ents[i].type == CARROT) setspawn(i, true);
		if (ents[i].type == CARROT)
			setspawn(i, true);
	}];
} // set for playing

void
trigger(int tag, int type, bool savegame)
{
	if (!tag)
		return;
252
253
254
255
256
257
258

259
260
261



262
263
264

265

266
267
268
269
270
271

272
273


274
275
276
277
278
279
280
257
258
259
260
261
262
263
264



265
266
267


268
269

270
271
272
273
274
275
276
277


278
279
280
281
282
283
284
285
286







+
-
-
-
+
+
+
-
-

+
-
+






+
-
-
+
+







}

int
closestent() // used for delent and edit mode ent display
{
	if (noteditmode())
		return -1;

	int best;
	float bdist = 99999;
	loopv(ents)
	__block int best;
	__block float bdist = 99999;
	[ents enumerateObjectsUsingBlock:^(Entity *e, size_t i, bool *stop) {
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			return;
			continue;

		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
entproperty(int prop, int amount)
{
	int e = closestent();
	if (e < 0)
315
316
317
318
319
320
321
322

323
324
325
326
327











328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348

349

350


351
352

353

354
355
356
357
358
359

360
361
362
363
364


365
366
367

368
369
370
371
372
373
374


375
376
377
378
379

380
381
382
383
384
385

386
387
388
389

390
391
392
393
394

395
396
397
398



399
400

401
402
403
404
405
406
407
408

409
410
411
412
413
414
415
321
322
323
324
325
326
327

328
329
330
331


332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362

363
364
365

366
367
368
369
370

371
372
373
374
375
376
377
378
379
380



381
382
383
384

385
386
387
388
389
390


391
392
393
394
395
396

397
398
399
400
401
402

403


404
405
406
407
408
409
410
411
412
413



414
415
416
417
418
419
420
421
422
423
424
425
426

427
428
429
430
431
432
433
434







-
+



-
-
+
+
+
+
+
+
+
+
+
+
+




















-
+

+
-
+
+


+
-
+






+


-
-
-
+
+


-
+





-
-
+
+




-
+





-
+
-
-


+





+

-
-
-
+
+
+


+







-
+







findtype(OFString *what)
{
	loopi(MAXENTTYPES) if ([what isEqual:entnames[i]]) return i;
	conoutf(@"unknown entity type \"%@\"", 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)
			v1 = 32;
		if (!v1)
			e.attr1 = 16;
		if (!v2 && !v3 && !v4)
			e.attr2 = 255;
		break;

	case MAPMODEL:
		e.attr4 = e.attr3;
		e.attr3 = e.attr2;
	case MONSTER:
	case TELEDEST:
		e.attr2 = (uchar)e.attr1;
	case PLAYERSTART:
		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)
	for (Entity *e in ents) {
	{
		entity &e = ents[i];
		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)

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;
	return -1;
}

sqr *wmip[LARGEST_FACTOR * 2];
470
471
472
473
474
475
476
477
478
479
480




481
482
483
484
485
486
487
488

489
490
491
492
493
494
495
489
490
491
492
493
494
495




496
497
498
499
500
501
502
503
504
505
506

507
508
509
510
511
512
513
514







-
-
-
-
+
+
+
+







-
+








	if (copy) {
		loop(x, ssize / 2) loop(y, ssize / 2)
		{
			*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";
		memcpy(hdr.maptitle, buffer, 128);
		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);
	}

	calclight();
	startmap(@"base/unnamed");
	if (oldworld) {

Modified src/worldio.mm from [1dbc40dfe7] to [823b65f982].

1
2
3









4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19



+
+
+
+
+
+
+
+
+







// worldio.cpp: loading & saving of maps and savegames

#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)
{
	[OFFileManager.defaultManager removeItemAtPath:backupname];
	[OFFileManager.defaultManager moveItemAtPath:name toPath:backupname];
}
169
170
171
172
173
174
175

176


177
178
179
180
181

182
183
184



185
186
187
188
189
190
191
178
179
180
181
182
183
184
185

186
187
188
189
190
191

192



193
194
195
196
197
198
199
200
201
202







+
-
+
+




-
+
-
-
-
+
+
+







	    gzopen([cgzname cStringWithEncoding:OFLocale.encoding], "wb9");
	if (!f) {
		conoutf(@"could not write map to %@", cgzname);
		return;
	}
	hdr.version = MAPVERSION;
	hdr.numents = 0;
	for (Entity *e in ents)
	loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++;
		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)
	for (Entity *e in ents) {
	{
		if (ents[i].type != NOTUSED) {
			entity tmp = ents[i];
		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));
		}
	}
	sqr *t = NULL;
	int sc = 0;
#define spurge                          \
269
270
271
272
273
274
275
276

277
278
279
280
281
282















283
284
285
286
287
288
289
280
281
282
283
284
285
286

287
288
289




290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311







-
+


-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







		fatal(@"illegal map size");
	if (hdr.version >= 4) {
		gzread(f, &hdr.waterlevel, sizeof(int) * 16);
		endianswap(&hdr.waterlevel, sizeof(int), 16);
	} 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
			if (e.attr1 > 32)
				e.attr1 = 32; // 12_03 and below
		}
	}

Modified src/worldlight.mm from [05fab5d192] to [a6b73ab397].

1
2
3
4
5


6
7
8
9
10

11
12

13
14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12
13
14

15

16
17
18
19
20
21
22





+
+





+

-
+
-







// worldlight.cpp

#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,
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 ly = light.y + (rnd(21) - 10) * 0.1f;
	float dx = bx - lx;
	float dy = by - ly;
	float dist = (float)sqrt(dx * dx + dy * dy);
	if (dist < 1.0f)
117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
119
120
121
122
123
124
125

126
127
128
129
130
131
132
133







-
+







			y += stepy;
			l -= stepl;
		}
	}
}

void
calclightsource(persistent_entity &l)
calclightsource(PersistentEntity *l)
{
	int reach = l.attr1;
	int sx = l.x - reach;
	int ex = l.x + reach;
	int sy = l.y - reach;
	int ey = l.y + reach;

171
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190
173
174
175
176
177
178
179

180


181
182

183
184
185
186
187
188
189







-
+
-
-


-







{
	loop(x, ssize) loop(y, ssize)
	{
		sqr *s = S(x, y);
		s->r = s->g = s->b = 10;
	}

	loopv(ents)
	for (Entity *e in ents)
	{
		entity &e = ents[i];
		if (e.type == LIGHT)
			calclightsource(e);
	}

	block b = { 1, 1, ssize - 2, ssize - 2 };
	postlightarea(b);
	setvar(@"fullbright", 0);
}

VARP(dynlight, 0, 16, 32);
232
233
234
235
236
237
238
239
240







241
242
243
244
245
246
247
231
232
233
234
235
236
237


238
239
240
241
242
243
244
245
246
247
248
249
250
251







-
-
+
+
+
+
+
+
+







		dlights =
		    [[OFMutableData alloc] initWithItemSize:sizeof(block *)];

	// backup area before rendering in dynlight
	block *copy = blockcopy(b);
	[dlights addItem:&copy];

	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);
}

// utility functions also used by editing code

block *