Cube  Check-in [c634a689e7]

Overview
Comment:More style fixes
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c634a689e76e7c60fb8b685ea40cb9993a86f23474d1ead7159eb331e98f1110
User & Date: js on 2025-03-29 17:13:40
Other Links: manifest | tags
Context
2025-03-29
22:29
Add variables with a getter / setter block check-in: cd2ac12a14 user: js tags: trunk
17:13
More style fixes check-in: c634a689e7 user: js tags: trunk
14:25
Switch from clang-format to manual formatting check-in: 75e920ae30 user: js tags: trunk
Changes

Modified src/Alias.h from [0fb6418ce5] to [c03f900e5c].

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









-
-
+
+


-
-
+
+




#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

@interface Alias: Identifier
@property (direct, copy, nonatomic) OFString *action;
@property (readonly, nonatomic) bool persisted;

+ (instancetype)aliasWithName: (OFString *)name
                       action: (OFString *)action
                    persisted: (bool)persisted OF_DIRECT;
		       action: (OFString *)action
		    persisted: (bool)persisted OF_DIRECT;
- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName: (OFString *)name
                      action: (OFString *)action
                   persisted: (bool)persisted OF_DESIGNATED_INITIALIZER
		      action: (OFString *)action
		   persisted: (bool)persisted OF_DESIGNATED_INITIALIZER
    OF_DIRECT;
@end

OF_ASSUME_NONNULL_END

Modified src/Alias.m from [fe2a009013] to [99fa37b5c7].

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




-
-
+
+


-
-
+
+



-
-
+
+







#import "Alias.h"

@implementation Alias
+ (instancetype)aliasWithName: (OFString *)name
                       action: (OFString *)action
                    persisted: (bool)persisted;
		       action: (OFString *)action
		    persisted: (bool)persisted;
{
	return [[self alloc] initWithName: name
	                           action: action
	                        persisted: persisted];
				   action: action
				persisted: persisted];
}

- (instancetype)initWithName: (OFString *)name
                      action: (OFString *)action
                   persisted: (bool)persisted
		      action: (OFString *)action
		   persisted: (bool)persisted
{
	self = [super initWithName: name];

	_action = [action copy];
	_persisted = persisted;

	return self;

Modified src/Command.h from [f6eb8c644d] to [4155619926].

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
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




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







-
-
+
+


-
-
+
+




#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

#define COMMAND(name, nargs, block_)				\
	OF_CONSTRUCTOR()					\
	{							\
		enqueueInit(^ {					\
			Identifier.identifiers[@#name] =	\
			    [Command commandWithName: @#name	\
			              argumentsTypes: nargs	\
			                       block: block_];	\
		});						\
#define COMMAND(name, nargs, block_)					\
	OF_CONSTRUCTOR()						\
	{								\
		enqueueInit(^ {						\
			Identifier.identifiers[@#name] = [Command	\
			    commandWithName: @#name			\
			     argumentsTypes: nargs			\
				      block: block_];			\
		});							\
	}

OF_DIRECT_MEMBERS
@interface Command: Identifier
@property (readonly, nonatomic) int argumentsTypes;

+ (instancetype)commandWithName: (OFString *)name
                 argumentsTypes: (int)argumentsTypes
                          block: (id)block;
		 argumentsTypes: (int)argumentsTypes
			  block: (id)block;
- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName: (OFString *)name
              argumentsTypes: (int)argumentsTypes
                       block: (id)block OF_DESIGNATED_INITIALIZER;
	      argumentsTypes: (int)argumentsTypes
		       block: (id)block OF_DESIGNATED_INITIALIZER;
- (int)callWithArguments: (OFArray<OFString *> *)arguments isDown: (bool)isDown;
@end

OF_ASSUME_NONNULL_END

Modified src/Command.m from [254356cb16] to [5f400a60e2].

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
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







-
-
+
+


-
-
+
+



-
-
+
+








@implementation Command
{
	id _block;
}

+ (instancetype)commandWithName: (OFString *)name
                 argumentsTypes: (int)argumentsTypes
                          block: (id)block
		 argumentsTypes: (int)argumentsTypes
			  block: (id)block
{
	return [[self alloc] initWithName: name
	                   argumentsTypes: argumentsTypes
	                            block: block];
			   argumentsTypes: argumentsTypes
				    block: block];
}

- (instancetype)initWithName: (OFString *)name
              argumentsTypes: (int)argumentsTypes
                       block: (id)block
	      argumentsTypes: (int)argumentsTypes
		       block: (id)block
{
	self = [super initWithName: name];

	_argumentsTypes = argumentsTypes;
	_block = block;

	return self;

Modified src/Cube.m from [67dd855e73] to [4173147859].

123
124
125
126
127
128
129
130

131
132
133


134
135
136
137
138
139
140
141
123
124
125
126
127
128
129

130



131
132

133
134
135
136
137
138
139







-
+
-
-
-
+
+
-







				_height = 1080;
			}
		}

		log(@"video: mode");
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		if ((_window = SDL_CreateWindow("cube engine",
		         SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		    SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		         _width, _height,
		         SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL |
		             (!windowed ? SDL_WINDOW_FULLSCREEN : 0))) ==
		    _width, _height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL |
		    (!windowed ? SDL_WINDOW_FULLSCREEN : 0))) == NULL ||
		        NULL ||
		    SDL_GL_CreateContext(_window) == NULL)
			fatal(@"Unable to create OpenGL screen");

		log(@"video: misc");
		SDL_SetWindowGrab(_window, SDL_TRUE);
		SDL_SetRelativeMouseMode(SDL_TRUE);
		SDL_ShowCursor(0);
240
241
242
243
244
245
246
247
248


249
250
251
252
253
254
255
238
239
240
241
242
243
244


245
246
247
248
249
250
251
252
253







-
-
+
+







					[self quit];
					break;
				case SDL_KEYDOWN:
				case SDL_KEYUP:
					if (_repeatsKeys ||
					    event.key.repeat == 0)
						keypress(event.key.keysym.sym,
						    event.key.state ==
						        SDL_PRESSED);
						    (event.key.state ==
						    SDL_PRESSED));
					break;
				case SDL_TEXTINPUT:
					input(@(event.text.text));
					break;
				case SDL_MOUSEMOTION:
					if (ignore) {
						ignore--;
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
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







-
+

-
+






-
-
-
+
+
-








- (void)screenshot
{
	SDL_Surface *image;
	SDL_Surface *temp;

	if ((image = SDL_CreateRGBSurface(SDL_SWSURFACE, _width, _height, 24,
	         0x0000FF, 0x00FF00, 0xFF0000, 0)) != NULL) {
	    0x0000FF, 0x00FF00, 0xFF0000, 0)) != NULL) {
		if ((temp = SDL_CreateRGBSurface(SDL_SWSURFACE, _width, _height,
		         24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) != NULL) {
		    24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) != NULL) {
			glReadPixels(0, 0, _width, _height, GL_RGB,
			    GL_UNSIGNED_BYTE, image->pixels);

			for (int idx = 0; idx < _height; idx++) {
				char *dest =
				    (char *)temp->pixels + 3 * _width * idx;
				memcpy(dest,
				    (char *)image->pixels +
				        3 * _width * (_height - 1 - idx),
				memcpy(dest, (char *)image->pixels + 3 *
				    _width * (_height - 1 - idx), 3 * _width);
				    3 * _width);
				endianswap(dest, 3, _width);
			}

			OFString *path = [OFString stringWithFormat:
			    @"screenshots/screenshot_%d.bmp", lastmillis];
			SDL_SaveBMP(temp,
			    [_userDataIRI IRIByAppendingPathComponent: path]
343
344
345
346
347
348
349
350

351
352
353
354
355
356
357
339
340
341
342
343
344
345

346
347
348
349
350
351
352
353







-
+







// failure exit
void
fatal(OFConstantString *s, ...)
{
	va_list args;
	va_start(args, s);
	OFMutableString *msg = [[OFMutableString alloc] initWithFormat: s
	                                                     arguments: args];
							     arguments: args];
	va_end(args);

	[msg appendFormat: @" (%s)\n", SDL_GetError()];

	[Cube.sharedInstance showMessage: msg];
	[OFApplication terminateWithStatus: 1];
}

Modified src/MD2.h from [25323c6c56] to [af445ff14a].

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









26
27
28
29
10
11
12
13
14
15
16









17
18
19
20
21
22
23
24
25
26
27
28
29







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




@property (copy, nonatomic) OFString *loadname;
@property (nonatomic) int mdlnum;
@property (nonatomic) bool loaded;

+ (instancetype)md2;
- (bool)loadWithIRI: (OFIRI *)IRI;
- (void)renderWithLight: (OFColor *)light
                  frame: (int)frame
                  range: (int)range
               position: (OFVector3D)position
                    yaw: (float)yaw
                  pitch: (float)pitch
                  scale: (float)scale
                  speed: (float)speed
                   snap: (int)snap
               basetime: (int)basetime;
		  frame: (int)frame
		  range: (int)range
	       position: (OFVector3D)position
		    yaw: (float)yaw
		  pitch: (float)pitch
		  scale: (float)scale
		  speed: (float)speed
		   snap: (int)snap
	       basetime: (int)basetime;
- (void)scaleWithFrame: (int)frame scale: (float)scale snap: (int)snap;
@end

OF_ASSUME_NONNULL_END

Modified src/MD2.m from [64d68c27a0] to [c2a1e1b6fe].

65
66
67
68
69
70
71
72

73
74
75
76
77
78
79
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79







-
+








- (bool)loadWithIRI: (OFIRI *)IRI
{
	OFSeekableStream *stream;
	@try {
		stream = (OFSeekableStream *)[[OFIRIHandler handlerForIRI: IRI]
		    openItemAtIRI: IRI
		             mode: @"r"];
			     mode: @"r"];
	} @catch (id e) {
		return false;
	}

	if (![stream isKindOfClass: OFSeekableStream.class])
		return false;

88
89
90
91
92
93
94
95

96
97
98
99
100
101
102
103
104
105
106
107
108

109
110
111
112
113
114
115
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115







-
+












-
+







		_frames = OFAllocMemory(header.numFrames, header.frameSize);
	} @catch (OFOutOfMemoryException *e) {
		return false;
	}

	[stream seekToOffset: header.offsetFrames whence: OFSeekSet];
	[stream readIntoBuffer: _frames
	           exactLength: header.frameSize * header.numFrames];
		   exactLength: header.frameSize * header.numFrames];

	for (int i = 0; i < header.numFrames; ++i)
		endianswap(_frames + i * header.frameSize, sizeof(float), 6);

	@try {
		_glCommands = OFAllocMemory(header.numGlCommands, sizeof(int));
	} @catch (OFOutOfMemoryException *e) {
		return false;
	}

	[stream seekToOffset: header.offsetGlCommands whence: OFSeekSet];
	[stream readIntoBuffer: _glCommands
	           exactLength: header.numGlCommands * sizeof(int)];
		   exactLength: header.numGlCommands * sizeof(int)];
	endianswap(_glCommands, sizeof(int), header.numGlCommands);

	_numFrames = header.numFrames;
	_numGlCommands = header.numGlCommands;
	_frameSize = header.frameSize;
	_numTriangles = header.numTriangles;
	_numVerts = header.numVertices;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151









152
153
154
155
156
157
158
136
137
138
139
140
141
142









143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158







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







		v->y =
		    -(snap(sn, cv[1] * cf->scale[1]) + cf->translate[1]) / sc;
		v->z = (snap(sn, cv[2] * cf->scale[2]) + cf->translate[2]) / sc;
	}
}

- (void)renderWithLight: (OFColor *)light
                  frame: (int)frame
                  range: (int)range
               position: (OFVector3D)position
                    yaw: (float)yaw
                  pitch: (float)pitch
                  scale: (float)sc
                  speed: (float)speed
                   snap: (int)sn
               basetime: (int)basetime
		  frame: (int)frame
		  range: (int)range
	       position: (OFVector3D)position
		    yaw: (float)yaw
		  pitch: (float)pitch
		  scale: (float)sc
		  speed: (float)speed
		   snap: (int)sn
	       basetime: (int)basetime
{
	for (int i = 0; i < range; i++)
		if (!_mverts[frame + i])
			[self scaleWithFrame: frame + i scale: sc snap: sn];

	glPushMatrix();
	glTranslatef(position.x, position.y, position.z);

Modified src/MapModelInfo.h from [9966a1ab68] to [1a4824d0f9].

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




15
16
17
18
19
20




21
22
23
1
2
3
4
5
6
7
8
9
10




11
12
13
14
15
16




17
18
19
20
21
22
23










-
-
-
-
+
+
+
+


-
-
-
-
+
+
+
+



#import <ObjFW/ObjFW.h>

OF_ASSUME_NONNULL_BEGIN

OF_DIRECT_MEMBERS
@interface MapModelInfo: OFObject
@property (nonatomic) int rad, h, zoff, snap;
@property (copy, nonatomic) OFString *name;

+ (instancetype)infoWithRad: (int)rad
                          h: (int)h
                       zoff: (int)zoff
                       snap: (int)snap
                       name: (OFString *)name;
			  h: (int)h
		       zoff: (int)zoff
		       snap: (int)snap
		       name: (OFString *)name;
- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithRad: (int)rad
                          h: (int)h
                       zoff: (int)zoff
                       snap: (int)snap
                       name: (OFString *)name;
			  h: (int)h
		       zoff: (int)zoff
		       snap: (int)snap
		       name: (OFString *)name;
@end

OF_ASSUME_NONNULL_END

Modified src/MapModelInfo.m from [19135ee4c2] to [bd54b60804].

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
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




-
-
-
-
+
+
+
+









-
-
-
-
+
+
+
+







#import "MapModelInfo.h"

@implementation MapModelInfo
+ (instancetype)infoWithRad: (int)rad
                          h: (int)h
                       zoff: (int)zoff
                       snap: (int)snap
                       name: (OFString *)name
			  h: (int)h
		       zoff: (int)zoff
		       snap: (int)snap
		       name: (OFString *)name
{
	return [[self alloc] initWithRad: rad
				       h: h
				    zoff: zoff
				    snap: snap
				    name: name];
}

- (instancetype)initWithRad: (int)rad
                          h: (int)h
                       zoff: (int)zoff
                       snap: (int)snap
                       name: (OFString *)name
			  h: (int)h
		       zoff: (int)zoff
		       snap: (int)snap
		       name: (OFString *)name
{
	self = [super init];

	_rad = rad;
	_h = h;
	_zoff = zoff;
	_snap = snap;

Modified src/Monster.h from [ecb02516a6] to [09f03dc0a4].

23
24
25
26
27
28
29
30
31
32
33




34
35
36
37
38




39
40
23
24
25
26
27
28
29




30
31
32
33
34




35
36
37
38
39
40







-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+


+ (void)restoreAll;
+ (void)resetAll;
+ (void)thinkAll;
+ (void)renderAll;
// TODO: Move this somewhere else
+ (void)endSinglePlayerWithAllKilled: (bool)allKilled;
+ (instancetype)monsterWithType: (int)type
                            yaw: (int)yaw
                          state: (int)state
                        trigger: (int)trigger
                           move: (int)move;
			    yaw: (int)yaw
			  state: (int)state
			trigger: (int)trigger
			   move: (int)move;
- (instancetype)initWithType: (int)type
                         yaw: (int)yaw
                       state: (int)state
                     trigger: (int)trigger
                        move: (int)move;
			 yaw: (int)yaw
		       state: (int)state
		     trigger: (int)trigger
			move: (int)move;
- (void)incurDamage: (int)damage fromEntity: (__kindof DynamicEntity *)d;
@end

Modified src/Monster.m from [911d4fb855] to [aa7be47ff2].

19
20
21
22
23
24
25
26
27
28
29




30
31
32
33
34
35




36
37
38
39
40
41
42
19
20
21
22
23
24
25




26
27
28
29
30
31




32
33
34
35
36
37
38
39
40
41
42







-
-
-
-
+
+
+
+


-
-
-
-
+
+
+
+








+ (OFMutableArray<Monster *> *)monsters
{
	return monsters;
}

+ (instancetype)monsterWithType: (int)type
                            yaw: (int)yaw
                          state: (int)state
                        trigger: (int)trigger
                           move: (int)move
			    yaw: (int)yaw
			  state: (int)state
			trigger: (int)trigger
			   move: (int)move
{
	return [[self alloc] initWithType: type
	                              yaw: yaw
	                            state: state
	                          trigger: trigger
	                             move: move];
				      yaw: yaw
				    state: state
				  trigger: trigger
				     move: move];
}

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

// for savegames
+ (void)restoreAll
{
72
73
74
75
76
77
78
79
80
81
82




83
84
85
86
87
88
89
72
73
74
75
76
77
78




79
80
81
82
83
84
85
86
87
88
89







-
-
-
-
+
+
+
+







	{ GUN_ICEBALL, 12, 250, 1, 0, 10, 400, 6, 18, 18, S_PAINH, S_DEATHH,
	    @"a knight", @"monster/knight" },
	{ GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD,
	    @"a goblin", @"monster/goblin" },
};

- (instancetype)initWithType: (int)type
                         yaw: (int)yaw
                       state: (int)state
                     trigger: (int)trigger
                        move: (int)move
			 yaw: (int)yaw
		       state: (int)state
		     trigger: (int)trigger
			move: (int)move
{
	self = [super init];

	if (type >= NUMMONSTERTYPES) {
		conoutf(@"warning: unknown monster in spawn: %d", type);
		type = 0;
	}
168
169
170
171
172
173
174
175
176
177
178




179
180
181
182
183
184
185
168
169
170
171
172
173
174




175
176
177
178
179
180
181
182
183
184
185







-
-
-
-
+
+
+
+







		mtimestart = lastmillis;

		for (Entity *e in ents) {
			if (e.type != MONSTER)
				continue;

			Monster *m = [Monster monsterWithType: e.attr2
			                                  yaw: e.attr1
			                                state: M_SLEEP
			                              trigger: 100
			                                 move: 0];
							  yaw: e.attr1
							state: M_SLEEP
						      trigger: 100
							 move: 0];
			m.origin = OFMakeVector3D(e.x, e.y, e.z);
			[monsters addObject: m];
			entinmap(m);
			monstertotal++;
		}
	}
}
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
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
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
435
436
437
438
439
440
441
442


443
444
445
446
447
448
449
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


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
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
435
436


437
438
439
440
441
442
443
444
445







-
-
-
+
+
+




-
+
-
-












-
-
+
+
-





-
-
-
+
+
+




















-
-
-
+
+
+













-
-
-
+
+
+










-
-
-
+
+
+





-
-
-
+
+
+



















-
-
-
+
+












-
-
-
+
+
+



















-
-
+
+







			self.jumpNext = true;
		// search for a way around (common)
		else if (self.trigger < lastmillis &&
		    (self.monsterState != M_HOME || !rnd(5))) {
			// patented "random walk" AI pathfinding (tm) ;)
			self.targetYaw += 180 + rnd(180);
			[self transitionWithState: M_SEARCH
			                   moving: 1
			                        n: 400
			                        r: 1000];
					   moving: 1
						n: 400
						r: 1000];
		}
	}

	float enemyYaw = -(float)atan2(self.enemy.origin.x - self.origin.x,
	                     self.enemy.origin.y - self.origin.y) /
	    self.enemy.origin.y - self.origin.y) / PI * 180 + 180;
	        PI * 180 +
	    180;

	switch (self.monsterState) {
	case M_PAIN:
	case M_ATTACKING:
	case M_SEARCH:
		if (self.trigger < lastmillis)
			[self transitionWithState: M_HOME
					   moving: 1
						n: 100
						r: 200];
		break;

	case M_SLEEP: // state classic sp monster start in, wait for visual
	              // contact
		// state classic sp monster start in, wait for visual contact
	case M_SLEEP: {
	{
		OFVector3D target;
		if (editmode || !enemylos(self, &target))
			return; // skip running physics
		[self normalizeWithAngle: enemyYaw];
		float angle = (float)fabs(enemyYaw - self.yaw);
		if (disttoenemy < 8 // the better the angle to the player, the
		                    // further the monster can see/hear
		    || (disttoenemy < 16 && angle < 135) ||
		// the better the angle to the player, the further the monster
		// can see/hear
		if (disttoenemy < 8 || (disttoenemy < 16 && angle < 135) ||
		    (disttoenemy < 32 && angle < 90) ||
		    (disttoenemy < 64 && angle < 45) || angle < 10) {
			[self transitionWithState: M_HOME
					   moving: 1
						n: 500
						r: 200];
			OFVector3D loc = self.origin;
			playsound(S_GRUNT1 + rnd(2), &loc);
		}
		break;
	}

	case M_AIMING:
		// this state is the delay between wanting to shoot and actually
		// firing
		if (self.trigger < lastmillis) {
			self.lastAction = 0;
			self.attacking = true;
			shoot(self, self.attackTarget);
			[self transitionWithState: M_ATTACKING
			                   moving: 0
			                        n: 600
			                        r: 0];
					   moving: 0
						n: 600
						r: 0];
		}
		break;

	case M_HOME:
		// monster has visual contact, heads straight for player and
		// may want to shoot at any time
		self.targetYaw = enemyYaw;
		if (self.trigger < lastmillis) {
			OFVector3D target;
			if (!enemylos(self, &target)) {
				// no visual contact anymore, let monster get
				// as close as possible then search for player
				[self transitionWithState: M_HOME
				                   moving: 1
				                        n: 800
				                        r: 500];
						   moving: 1
							n: 800
							r: 500];
			} else {
				// the closer the monster is the more likely he
				// wants to shoot
				if (!rnd((int)disttoenemy / 3 + 1) &&
				    self.enemy.state == CS_ALIVE) {
					// get ready to fire
					self.attackTarget = target;
					int n =
					    monstertypes[self.monsterType].lag;
					[self transitionWithState: M_AIMING
					                   moving: 0
					                        n: n
					                        r: 10];
							   moving: 0
								n: n
								r: 10];
				} else {
					// track player some more
					int n =
					    monstertypes[self.monsterType].rate;
					[self transitionWithState: M_HOME
					                   moving: 1
					                        n: n
					                        r: 0];
							   moving: 1
								n: n
								r: 0];
				}
			}
		}
		break;
	}

	moveplayer(self, 1, false); // use physics to move monster
}

- (void)incurDamage: (int)damage fromEntity: (__kindof DynamicEntity *)d
{
	// a monster hit us
	if ([d isKindOfClass: Monster.class]) {
		Monster *m = (Monster *)d;

		// guard for RL guys shooting themselves :)
		if (self != m) {
			// don't attack straight away, first get angry
			self.anger++;
			int anger =
			    (self.monsterType == m.monsterType ? self.anger / 2
			                                       : self.anger);
			int anger = (self.monsterType == m.monsterType
			    ? self.anger / 2 : self.anger);
			if (anger >= monstertypes[self.monsterType].loyalty)
				// monster infight if very angry
				self.enemy = m;
		}
	} else {
		// player hit us
		self.anger = 0;
		self.enemy = d;
	}

	// in this state monster won't attack
	[self transitionWithState: M_PAIN
	                   moving: 0
	                        n: monstertypes[self.monsterType].pain
	                        r: 200];
			   moving: 0
				n: monstertypes[self.monsterType].pain
				r: 200];

	if ((self.health -= damage) <= 0) {
		self.state = CS_DEAD;
		self.lastAction = lastmillis;
		numkilled++;
		Player.player1.frags = numkilled;
		OFVector3D loc = self.origin;
		playsound(monstertypes[self.monsterType].diesound, &loc);
		int remain = monstertotal - numkilled;
		if (remain > 0 && remain <= 5)
			conoutf(@"only %d monster(s) remaining", remain);
	} else {
		OFVector3D loc = self.origin;
		playsound(monstertypes[self.monsterType].painsound, &loc);
	}
}

+ (void)endSinglePlayerWithAllKilled: (bool)allKilled
{
	conoutf(allKilled ? @"you have cleared the map!"
	                  : @"you reached the exit!");
	conoutf(allKilled
	    ? @"you have cleared the map!" : @"you reached the exit!");
	conoutf(@"score: %d kills in %d seconds", numkilled,
	    (lastmillis - mtimestart) / 1000);
	monstertotal = 0;
	startintermission();
}

+ (void)thinkAll

Modified src/Variable.h from [780b5706d6] to [237ce5cd40].

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
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95





96
97
98
99
100
101
102





103
104
105
106
107
108
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
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
82
83
84
85
86
87
88
89
90





91
92
93
94
95
96
97





98
99
100
101
102
103
104
105
106
107
108










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









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










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
















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

















-
-
-
-
-
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+






#import "Identifier.h"

OF_ASSUME_NONNULL_BEGIN

#define VARP(name, min_, cur, max_)					\
	int name = cur;							\
									\
	OF_CONSTRUCTOR()						\
	{								\
		enqueueInit(^ {						\
			Variable *variable =				\
			    [Variable variableWithName: @#name		\
			                           min: min_		\
			                           max: max_		\
			                       storage: &name		\
			                      function: NULL		\
			                     persisted: true];		\
			Variable *variable = [Variable			\
			    variableWithName: @#name			\
					 min: min_			\
					 max: max_			\
				     storage: &name			\
				    function: NULL			\
				   persisted: true];			\
			Identifier.identifiers[@#name] = variable;	\
		});							\
	}
#define VAR(name, min_, cur, max_)					\
	int name = cur;							\
									\
	OF_CONSTRUCTOR()						\
	{								\
		enqueueInit(^ {						\
			Variable *variable =				\
			    [Variable variableWithName: @#name		\
			                           min: min_		\
			                           max: max_		\
			                       storage: &name		\
			                      function: NULL		\
			                     persisted: false];		\
			Variable *variable = [Variable			\
			    variableWithName: @#name			\
					 min: min_			\
					 max: max_			\
				     storage: &name			\
				    function: NULL			\
				   persisted: false];			\
			Identifier.identifiers[@#name] = variable;	\
		});							\
	}
#define VARF(name, min_, cur, max_, body)				\
	static void var_##name(void);					\
	static int name = cur;						\
									\
	OF_CONSTRUCTOR()						\
	{								\
		enqueueInit(^ {						\
			Variable *variable =				\
			    [Variable variableWithName: @#name		\
			                           min: min_		\
			                           max: max_		\
			                       storage: &name		\
			                      function: var_##name	\
			                     persisted: false];		\
			Variable *variable = [Variable			\
			    variableWithName: @#name			\
					 min: min_			\
					 max: max_			\
				     storage: &name			\
				    function: var_##name		\
				   persisted: false];			\
			Identifier.identifiers[@#name] = variable;	\
		});							\
	}								\
									\
	static void							\
	var_##name(void)						\
	{								\
		body;							\
	}
#define VARFP(name, min_, cur, max_, body)				\
	static void var_##name(void);					\
	static int name = cur;						\
									\
	OF_CONSTRUCTOR()						\
	{								\
		enqueueInit(^ {						\
			Variable *variable =				\
			    [Variable variableWithName: @#name		\
			                           min: min_		\
			                           max: max_		\
			                       storage: &name		\
			                      function: var_##name	\
			                     persisted: true];		\
			Variable *variable = [Variable			\
			variableWithName: @#name			\
				     min: min_				\
				     max: max_				\
				 storage: &name				\
				function: var_##name			\
			       persisted: true];			\
			Identifier.identifiers[@#name] = variable;	\
		});							\
	}								\
									\
	static void							\
	var_##name(void)						\
	{								\
		body;							\
	}

@interface Variable: Identifier
@property (direct, readonly, nonatomic) int min, max;
@property (direct, readonly, nonatomic) int *storage;
@property (direct, readonly, nullable, nonatomic) void (*function)();
@property (readonly, nonatomic) bool persisted;

+ (instancetype)variableWithName: (OFString *)name
                             min: (int)min
                             max: (int)max
                         storage: (int *)storage
                        function: (void (*_Nullable)())function
                       persisted: (bool)persisted OF_DIRECT;
			     min: (int)min
			     max: (int)max
			 storage: (int *)storage
			function: (void (*_Nullable)())function
		       persisted: (bool)persisted OF_DIRECT;
- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName: (OFString *)name
                         min: (int)min
                         max: (int)max
                     storage: (int *)storage
                    function: (void (*_Nullable)())function
                   persisted: (bool)persisted OF_DESIGNATED_INITIALIZER
			 min: (int)min
			 max: (int)max
		     storage: (int *)storage
		    function: (void (*_Nullable)())function
		   persisted: (bool)persisted OF_DESIGNATED_INITIALIZER
    OF_DIRECT;
- (void)printValue OF_DIRECT;
- (void)setValue: (int)value OF_DIRECT;
@end

OF_ASSUME_NONNULL_END

Modified src/Variable.m from [60a145c003] to [892ebc7d62].

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
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






-
-
-
-
-
+
+
+
+
+


-
-
-
-
-
+
+
+
+
+



-
-
-
-
-
+
+
+
+
+







#import "Variable.h"

#include "cube.h"

@implementation Variable
+ (instancetype)variableWithName: (OFString *)name
                             min: (int)min
                             max: (int)max
                         storage: (int *)storage
                        function: (void (*__cdecl)())function
                       persisted: (bool)persisted
			     min: (int)min
			     max: (int)max
			 storage: (int *)storage
			function: (void (*__cdecl)())function
		       persisted: (bool)persisted
{
	return [[self alloc] initWithName: name
	                              min: min
	                              max: max
	                          storage: storage
	                         function: function
	                        persisted: persisted];
				      min: min
				      max: max
				  storage: storage
				 function: function
				persisted: persisted];
}

- (instancetype)initWithName: (OFString *)name
                         min: (int)min
                         max: (int)max
                     storage: (int *)storage
                    function: (void (*__cdecl)())function
                   persisted: (bool)persisted
			 min: (int)min
			 max: (int)max
		     storage: (int *)storage
		    function: (void (*__cdecl)())function
		   persisted: (bool)persisted
{
	self = [super initWithName: name];

	_min = min;
	_max = max;
	_storage = storage;
	_function = function;

Modified src/clientextras.m from [d459541c88] to [84515fc725].

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
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












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







// clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :)

#include "cube.h"

#import "Command.h"
#import "Monster.h"
#import "Player.h"

// render players & monsters
// very messy ad-hoc handling of animation frames, should be made more
// configurable

//              D    D    D    D'   D    D    D    D'   A   A'  P   P'  I   I'
//              R,  R'  E    L    J   J'
int frame[] = { 178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0,
	40, 1, 162, 162, 67, 168 };
int range[] = { 6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1,
	1 };
// D    D    D    D'   D    D    D    D'   A   A'  P   P'  I   I'
// R,  R'  E    L    J   J'
int frame[] = {
	178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0, 40, 1,
	162, 162, 67, 168
};
int range[] = {
	6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1, 1
};

void
renderclient(
    DynamicEntity *d, bool team, OFString *mdlname, bool hellpig, float scale)
{
	int n = 3;
	float speed = 100.0f;

Modified src/clientgame.m from [0b8b0d202d] to [418707bf92].

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
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







+
-
+
-
-
-
-
+
-
-












-
+
-







	// let armour absorb when possible
	int ad = damage * (player1.armourType + 1) * 20 / 100;
	if (ad > Player.player1.armour)
		ad = Player.player1.armour;
	player1.armour -= ad;
	damage -= ad;
	float droll = damage / 0.5f;
	// give player a kick depending on amount of damage
	player1.roll += player1.roll > 0
	player1.roll += player1.roll > 0 ? droll : (player1.roll < 0 ? -droll :
	    ? droll
	    : (player1.roll < 0
	              ? -droll
	              : (rnd(2) ? droll
	    (rnd(2) ? droll : -droll));
	                        : -droll)); // give player a kick depending
	                                    // on amount of damage
	if ((player1.health -= damage) <= 0) {
		if (actor == -2) {
			conoutf(@"you got killed by %@!", act.name);
		} else if (actor == -1) {
			actor = getclientnum();
			conoutf(@"you suicided!");
			addmsg(1, 2, SV_FRAGS, --player1.frags);
		} else {
			Player *a = getclient(actor);
			if (a != nil) {
				if (isteam(a.team, player1.team))
					conoutf(@"you got fragged by a "
					        @"teammate (%@)",
					    @"teammate (%@)", a.name);
					    a.name);
				else
					conoutf(
					    @"you got fragged by %@", a.name);
			}
		}
		showscores(true);
		addmsg(1, 2, SV_DIED, actor);

Modified src/clients.m from [0b6e5b2153] to [895b86000b].

39
40
41
42
43
44
45
46
47


48
49
50
51
52
53
54
39
40
41
42
43
44
45


46
47
48
49
50
51
52
53
54







-
-
+
+







	if (!allow)
		conoutf(@"editing in multiplayer requires coopedit mode (1)");

	return allow;
}

VARF(rate, 0, 0, 25000,
    if (clienthost && (!rate || rate > 1000))
        enet_host_bandwidth_limit(clienthost, rate, rate));
	if (clienthost && (!rate || rate > 1000))
		enet_host_bandwidth_limit(clienthost, rate, rate));

void throttle();

VARF(throttle_interval, 0, 5, 30, throttle());
VARF(throttle_accel, 0, 2, 32, throttle());
VARF(throttle_decel, 0, 2, 32, throttle());

212
213
214
215
216
217
218
219

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

219
220
221
222
223
224
225
226







-
+







		    msgsizelookup(type));
	if (messages.count == 100) {
		conoutf(@"command flood protection (type %d)", type);
		return;
	}

	OFMutableData *msg = [OFMutableData dataWithItemSize: sizeof(int)
	                                            capacity: num + 2];
						    capacity: num + 2];
	[msg addItem: &num];
	[msg addItem: &rel];
	[msg addItem: &type];

	va_list marker;
	va_start(marker, type);
	for (int i = 0; i < num - 1; i++) {

Modified src/clients2c.m from [11f87c82e6] to [9597798812].

41
42
43
44
45
46
47
48

49
50
51
52

53
54

55
56

57
58

59
60
61
62
63
64
65
41
42
43
44
45
46
47

48
49
50
51

52


53
54

55


56
57
58
59
60
61
62
63







-
+



-
+
-
-
+

-
+
-
-
+







	Player *player1 = Player.player1;
	const float r = player1.radius + d.radius;
	const float dx = player1.origin.x - d.origin.x;
	const float dy = player1.origin.y - d.origin.y;
	const float dz = player1.origin.z - d.origin.z;
	const float rz = player1.aboveEye + d.eyeHeight;
	const float fx = (float)fabs(dx), fy = (float)fabs(dy),
	            fz = (float)fabs(dz);
	    fz = (float)fabs(dz);
	if (fx < r && fy < r && fz < rz && d.state != CS_DEAD) {
		if (fx < fy)
			// push aside
			d.origin = OFAddVectors3D(d.origin,
			d.origin = OFAddVectors3D(d.origin, OFMakeVector3D(
			    OFMakeVector3D(
			        0, (dy < 0 ? r - fy : -(r - fy)), 0));
			    0, (dy < 0 ? r - fy : -(r - fy)), 0));
		else
			d.origin = OFAddVectors3D(d.origin,
			d.origin = OFAddVectors3D(d.origin, OFMakeVector3D(
			    OFMakeVector3D(
			        (dx < 0 ? r - fx : -(r - fx)), 0, 0));
			    (dx < 0 ? r - fx : -(r - fx)), 0, 0));
	}
	int lagtime = lastmillis - d.lastUpdate;
	if (lagtime) {
		d.lag = (d.lag * 5 + lagtime) / 6;
		d.lastUpdate = lastmillis;
	}
}
83
84
85
86
87
88
89
90

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

106
107
108
109
110
111
112
81
82
83
84
85
86
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110







-
+














-
+







		switch (type = getint(&p)) {
		case SV_INITS2C: // welcome messsage from the server
		{
			cn = getint(&p);
			int prot = getint(&p);
			if (prot != PROTOCOL_VERSION) {
				conoutf(@"you are using a different game "
				        @"protocol (you: %d, server: %d)",
				    @"protocol (you: %d, server: %d)",
				    PROTOCOL_VERSION, prot);
				disconnect(false, false);
				return;
			}
			toservermap = @"";
			clientnum = cn; // we are now fully connected
			if (!getint(&p))
				// we are the first client on this server, set
				// map
				toservermap = getclientmap();
			sgetstr();
			if (text[0] &&
			    strcmp(text, clientpassword.UTF8String)) {
				conoutf(@"you need to set the correct password "
				        @"to join this server!");
				    @"to join this server!");
				disconnect(false, false);
				return;
			}
			if (getint(&p) == 1)
				conoutf(@"server is FULL, disconnecting..");
			break;
		}
261
262
263
264
265
266
267
268
269


270
271
272
273
274
275
276
259
260
261
262
263
264
265


266
267
268
269
270
271
272
273
274







-
-
+
+







				addmsg(1, 2, SV_FRAGS,
				    (Player.player1.frags += frags));
			} else {
				Player *a = getclient(actor);
				if (a != nil) {
					if (isteam(a.team, d.name))
						conoutf(@"%@ fragged his "
						        @"teammate (%@)",
						    a.name, d.name);
						    @"teammate (%@)", a.name,
						    d.name);
					else
						conoutf(@"%@ fragged %@",
						    a.name, d.name);
				}
			}
			OFVector3D loc = d_.origin;
			playsound(S_DIE1 + rnd(2), &loc);
299
300
301
302
303
304
305
306
307



308
309
310
311
312
313
314
297
298
299
300
301
302
303


304
305
306
307
308
309
310
311
312
313







-
-
+
+
+







			break;
		}
		// server acknowledges that I picked up this item
		case SV_ITEMACC:
			realpickup(getint(&p), Player.player1);
			break;

		case SV_EDITH: // coop editing messages, should be extended to
		               // include all possible editing ops
		// coop editing messages, should be extended to include all
		// possible editing ops
		case SV_EDITH:
		case SV_EDITT:
		case SV_EDITS:
		case SV_EDITD:
		case SV_EDITE: {
			int x = getint(&p);
			int y = getint(&p);
			int xs = getint(&p);
363
364
365
366
367
368
369
370

371
372
373
374
375
376
377
378
362
363
364
365
366
367
368

369

370
371
372
373
374
375
376







-
+
-







		case SV_PING:
			getint(&p);
			break;

		case SV_PONG:
			addmsg(0, 2, SV_CLIENTPING,
			    Player.player1.ping = (Player.player1.ping * 5 +
			                              lastmillis - getint(&p)) /
				lastmillis - getint(&p)) / 6);
			        6);
			break;

		case SV_CLIENTPING:
			OFAssert([players[cn] isKindOfClass: Player.class]);
			((Player *)players[cn]).ping = getint(&p);
			break;

397
398
399
400
401
402
403
404
405


406

407
408
409
410
411
412
413
414
415
416
395
396
397
398
399
400
401


402
403

404
405
406
407
408
409
410
411
412
413
414







-
-
+
+
-
+










		}

		case SV_SERVMSG:
			sgetstr();
			conoutf(@"%s", text);
			break;

		case SV_EXT: // so we can messages without breaking previous
		             // clients/servers, if necessary
		// so we can messages without breaking previous
		// clients/servers, if necessary
		{
		case SV_EXT: {
			for (int n = getint(&p); n; n--)
				getint(&p);
			break;
		}

		default:
			neterr(@"type");
			return;
		}
}

Modified src/commands.m from [e072f98aae] to [e486b084f6].

18
19
20
21
22
23
24
25
26


27
28
29
30
31
32
33
18
19
20
21
22
23
24


25
26
27
28
29
30
31
32
33







-
-
+
+







void
alias(OFString *name, OFString *action)
{
	Alias *alias = Identifier.identifiers[name];

	if (alias == nil)
		Identifier.identifiers[name] = [Alias aliasWithName: name
		                                             action: action
		                                          persisted: true];
							     action: action
							  persisted: true];
	else {
		if ([alias isKindOfClass: Alias.class])
			alias.action = action;
		else
			conoutf(
			    @"cannot redefine builtin %@ with an alias", name);
	}

Modified src/console.m from [1d463d8f9f] to [a781a15ab9].

58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


88
89
90
91
92
93
94
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85


86
87
88
89
90
91
92
93
94







-
+




















-
-
+
+







void
conoutf(OFConstantString *format, ...)
{
	va_list arguments;
	va_start(arguments, format);

	OFString *string = [[OFString alloc] initWithFormat: format
	                                          arguments: arguments];
						  arguments: arguments];

	va_end(arguments);

	int n = 0;
	while (string.length > WORDWRAP) {
		conline([string substringToIndex: WORDWRAP], n++ != 0);
		string = [string substringFromIndex: WORDWRAP];
	}
	conline(string, n != 0);
}

// render buffer taking into account time & scrolling
void
renderconsole()
{
	int nd = 0;
	OFString *refs[ndraw];

	size_t i = 0;
	for (ConsoleLine *conline in conlines) {
		if (conskip ? i >= conskip - 1 || i >= conlines.count - ndraw
		            : lastmillis - conline.outtime < 20000) {
		if (conskip ? i >= conskip - 1 || i >= conlines.count - ndraw :
		    lastmillis - conline.outtime < 20000) {
			refs[nd++] = conline.text;
			if (nd == ndraw)
				break;
		}

		i++;
	}
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117
103
104
105
106
107
108
109

110
111
112
113
114
115
116
117







-
+







static OFMutableArray<KeyMapping *> *keyMappings = nil;

COMMAND(keymap, ARG_3STR, ^ (OFString *code, OFString *key, OFString *action) {
	if (keyMappings == nil)
		keyMappings = [[OFMutableArray alloc] init];

	KeyMapping *mapping = [KeyMapping mappingWithCode: code.cube_intValue
	                                             name: key];
						     name: key];
	mapping.action = action;
	[keyMappings addObject: mapping];
})

COMMAND(bind, ARG_2STR, ^ (OFString *key, OFString *action) {
	for (KeyMapping *mapping in keyMappings) {
		if ([mapping.name caseInsensitiveCompare: key] ==
181
182
183
184
185
186
187
188
189


190
191
192
193
194
195
196
181
182
183
184
185
186
187


188
189
190
191
192
193
194
195
196







-
-
+
+







			case SDLK_RETURN:
				break;

			case SDLK_BACKSPACE:
			case SDLK_LEFT:
				if (commandbuf.length > 0)
					[commandbuf deleteCharactersInRange:
					        OFMakeRange(
					            commandbuf.length - 1, 1)];
					    OFMakeRange(
					    commandbuf.length - 1, 1)];

				resetcomplete();
				break;

			case SDLK_UP:
				if (histpos)
					commandbuf =
217
218
219
220
221
222
223
224

225
226

227
228
229
230
231
232
233
217
218
219
220
221
222
223

224


225
226
227
228
229
230
231
232







-
+
-
-
+







			default:
				resetcomplete();
			}
		} else {
			if (code == SDLK_RETURN) {
				if (commandbuf.length > 0) {
					if (vhistory == nil)
						vhistory =
						vhistory = [[OFMutableArray
						    [[OFMutableArray alloc]
						        init];
						    alloc] init];

					if (vhistory.count == 0 ||
					    ![vhistory.lastObject isEqual:
					    commandbuf]) {
						// cap this?
						[vhistory addObject:
						    [commandbuf copy]];

Modified src/cube.h from [e6d07ee71e] to [966c5f23a3].

104
105
106
107
108
109
110
111
112


113
114
115
116
117
118
119
104
105
106
107
108
109
110


111
112
113
114
115
116
117
118
119







-
-
+
+







#define SW(w, x, y) SWS(w, x, y, ssize)
#define S(x, y) SW(world, x, y) // convenient lookup of a lowest mip cube
#define SMALLEST_FACTOR 6       // determines number of mips there can be
#define DEFAULT_FACTOR 8
#define LARGEST_FACTOR 11 // 10 is already insane
#define SOLID(x) ((x)->type == SOLID)
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
#define OUTBORD(x, y)                                                \
	((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD || \
#define OUTBORD(x, y)							\
	((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD ||	\
	    (y) >= ssize - MINBORD)

struct block {
	int x, y, xs, ys;
};

enum {
251
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
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
251
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
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







-
-
-
+
+
+




-
-
-
+
+
+



















-
-
+
+

-
-
-
-
-
+
+
+
+
+


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







// globals ooh naughty

#ifdef __cplusplus
extern "C" {
#endif
// map data, the mips are sequential 2D arrays in memory
extern struct sqr *world, *wmip[];
extern struct header hdr;      // current map header
extern int sfactor, ssize;     // ssize = 2^sfactor
extern int cubicsize, mipsize; // cubicsize = ssize^2
extern struct header hdr;	// current map header
extern int sfactor, ssize;	// ssize = 2^sfactor
extern int cubicsize, mipsize;	// cubicsize = ssize^2
// all the other clients (in multiplayer)
extern OFMutableArray *players;
extern bool editmode;
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 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;
#ifdef __cplusplus
}
#endif

#define DMF 16.0f
#define DAF 1.0f
#define DVF 100.0f

#define VIRTW 2400 // virtual screen size for text & HUD
#define VIRTH 1800
#define FONTH 64
#define PIXELTAB (VIRTW / 12)

#define PI (3.1415927f)
#define PI2 (2 * PI)

#define vreject(v, u, max)                                 \
	((v).x > (u).x + (max) || (v).x < (u).x - (max) || \
#define vreject(v, u, max)					\
	((v).x > (u).x + (max) || (v).x < (u).x - (max) ||	\
	    (v).y > (u).y + (max) || (v).y < (u).y - (max))
#define vlinterp(v, f, u, g)                   \
	{                                      \
		(v).x = (v).x * f + (u).x * g; \
		(v).y = (v).y * f + (u).y * g; \
		(v).z = (v).z * f + (u).z * g; \
#define vlinterp(v, f, u, g)			\
	{					\
		(v).x = (v).x * f + (u).x * g;	\
		(v).y = (v).y * f + (u).y * g;	\
		(v).z = (v).z * f + (u).z * g;	\
	}

#define sgetstr()                        \
	{                                \
		char *t = text;          \
		do {                     \
			*t = getint(&p); \
		} while (*t++);          \
#define sgetstr()				\
	{					\
		char *t = text;			\
		do {				\
			*t = getint(&p);	\
		} while (*t++);			\
	} // used by networking

#define m_noitems (gamemode >= 4)
#define m_noitemsrail (gamemode <= 5)
#define m_arena (gamemode >= 8)
#define m_tarena (gamemode >= 10)
#define m_teammode (gamemode & 1 && gamemode > 2)

Modified src/editing.m from [1510ae8332] to [974268cfe4].

39
40
41
42
43
44
45
46
47
48
49
50





51
52
53
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
80
81




82
83

84

85
86
87
88
89
90
91
39
40
41
42
43
44
45





46
47
48
49
50
51




52
53
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
80


81
82
83
84
85

86

87
88
89
90
91
92
93
94







-
-
-
-
-
+
+
+
+
+

-
-
-
-
+
+
+
+



















-
-
+
+

-
+
+

-
-
+
+
+
+

-
+
-
+







		}
	});
}

int selh = 0;
bool selset = false;

#define loopselxy(b)                                                       \
	{                                                                  \
		makeundo();                                                \
		for (int x = 0; x < sel->xs; x++) {                        \
			for (int y = 0; y < sel->ys; y++) {                \
#define loopselxy(b)							   \
	{								   \
		makeundo();						   \
		for (int x = 0; x < sel->xs; x++) {			   \
			for (int y = 0; y < sel->ys; y++) {		   \
				struct sqr *s = S(sel->x + x, sel->y + y); \
				b;                                         \
			}                                                  \
		}                                                          \
		remip(sel, 0);                                             \
				b;					   \
			}						   \
		}							   \
		remip(sel, 0);						   \
	}

int cx, cy, ch;

int curedittex[] = { -1, -1, -1 };

bool dragging = false;
int lastx, lasty, lasth;

int lasttype = 0, lasttex = 0;
static struct sqr rtex;

VAR(editing, 0, 0, 1);

void
toggleedit()
{
	Player *player1 = Player.player1;
	if (player1.state == CS_DEAD)
		return; // do not allow dead players to edit to avoid state
		        // confusion
		// do not allow dead players to edit to avoid state
		return;
	if (!editmode && !allowedittoggle())
		return; // not in most multiplayer modes
		// not in most multiplayer modes
		return;
	if (!(editmode = !editmode)) {
		settagareas();     // reset triggers to allow quick playtesting
		entinmap(player1); // find spawn closest to current floating pos
		// reset triggers to allow quick playtesting
		settagareas();
		// find spawn closest to current floating pos
		entinmap(player1);
	} else {
		resettagareas(); // clear trigger areas to allow them to be
		// clear trigger areas to allow them to be edited
		                 // edited
		resettagareas();
		player1.health = 100;
		if (m_classicsp)
			// all monsters back at their spawns for editing
			[Monster resetAll];
		projreset();
	}
	Cube.sharedInstance.repeatsKeys = editmode;
123
124
125
126
127
128
129
130
131


132
133
134


135
136
137


138
139
140
141
142
143
144
126
127
128
129
130
131
132


133
134
135


136
137
138


139
140
141
142
143
144
145
146
147







-
-
+
+

-
-
+
+

-
-
+
+







noselection()
{
	if (!selset)
		conoutf(@"no selection");
	return !selset;
}

#define EDITSEL                             \
	if (noteditmode() || noselection()) \
#define EDITSEL					\
	if (noteditmode() || noselection())	\
		return;
#define EDITSELMP                                            \
	if (noteditmode() || noselection() || multiplayer()) \
#define EDITSELMP						\
	if (noteditmode() || noselection() || multiplayer())	\
		return;
#define EDITMP                              \
	if (noteditmode() || multiplayer()) \
#define EDITMP					\
	if (noteditmode() || multiplayer())	\
		return;

COMMAND(select, ARG_4INT, (^ (int x, int y, int xs, int ys) {
	struct block s = { x, y, xs, ys };
	sel = s;
	selh = 0;
	correctsel();
338
339
340
341
342
343
344

345
346

347
348
349
350
351
352
353
354
341
342
343
344
345
346
347
348
349

350

351
352
353
354
355
356
357







+

-
+
-







	}
	makeundo();
	copybuf->x = sel.x;
	copybuf->y = sel.y;
	blockpaste(copybuf);
})

// maintain most recently used of the texture lists when applying texture
void
tofronttex() // maintain most recently used of the texture lists when applying
tofronttex()
             // texture
{
	for (int i = 0; i < 3; i++) {
		int c = curedittex[i];
		if (c >= 0) {
			unsigned char *p = hdr.texlists[i];
			int t = p[c];
			for (int a = c - 1; a >= 0; a--)
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
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
435
436







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







COMMAND(editheight, ARG_2INT, ^ (int flr, int amount) {
	editheight(flr, amount);
})

void
edittexxy(int type, int t, const struct block *sel)
{
	loopselxy(switch (type) {
	        case 0:
		        s->ftex = t;
		        break;
	        case 1:
		        s->wtex = t;
		        break;
	        case 2:
		        s->ctex = t;
		        break;
	        case 3:
		        s->utex = t;
		        break;
	});
	loopselxy(
		switch (type) {
		case 0:
			s->ftex = t;
			break;
		case 1:
			s->wtex = t;
			break;
		case 2:
			s->ctex = t;
			break;
		case 3:
			s->utex = t;
			break;
		}
	);
}

COMMAND(edittex, ARG_2INT, ^ (int type, int dir) {
	EDITSEL;

	if (type < 0 || type > 3)
		return;
482
483
484
485
486
487
488
489
490
491


492
493
494
495
496
497
498
487
488
489
490
491
492
493



494
495
496
497
498
499
500
501
502







-
-
-
+
+







}

static void
edittype(int type)
{
	EDITSEL;

	if (type == CORNER &&
	    (sel.xs != sel.ys || sel.xs == 3 || (sel.xs > 4 && sel.xs != 8) ||
	        sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
	if (type == CORNER && (sel.xs != sel.ys || sel.xs == 3 || (sel.xs > 4 &&
	    sel.xs != 8) || sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
		conoutf(@"corner selection must be power of 2 aligned");
		return;
	}

	edittypexy(type, &sel);
	addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type);
}
578
579
580
581
582
583
584
585

586
587
588
589



590
591
592
593
594
595
596
582
583
584
585
586
587
588

589




590
591
592
593
594
595
596
597
598
599







-
+
-
-
-
-
+
+
+







		sel.xs = MAXARCHVERT;
	if (sel.ys > MAXARCHVERT)
		sel.ys = MAXARCHVERT;

	struct block *sel_ = &sel;
	// Ugly hack to make the macro work.
	struct block *sel = sel_;
	loopselxy(s->vdelta = sel->xs > sel->ys
	loopselxy(s->vdelta = sel->xs > sel->ys ? (archverts[sel->xs - 1][x] +
	        ? (archverts[sel->xs - 1][x] +
	              (y == 0 || y == sel->ys - 1 ? sidedelta : 0))
	        : (archverts[sel->ys - 1][y] +
	              (x == 0 || x == sel->xs - 1 ? sidedelta : 0)));
	    (y == 0 || y == sel->ys - 1 ? sidedelta : 0)) :
	    (archverts[sel->ys - 1][y] + (x == 0 || x == sel->xs - 1 ?
	    sidedelta : 0)));
	remipmore(sel, 0);
})

COMMAND(slope, ARG_2INT, ^ (int xd, int yd) {
	EDITSELMP;

	int off = 0;

Modified src/entities.m from [945da2b6da] to [384373114a].

47
48
49
50
51
52
53
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
80
81
82

83
84

85
86
87

88
89

90
91
92
93
94
95

96
97
98


99
100
101
102
103
104
105



106
107

108
109
110
111
112
113
114
47
48
49
50
51
52
53


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

80


81



82


83
84
85
86
87
88

89



90
91
92
93
94
95



96
97
98


99
100
101
102
103
104
105
106







-
-
+
+
-
-
-
+
+









-
-
+
-
-
+
+










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





-
+
-
-
-
+
+




-
-
-
+
+
+
-
-
+








	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,
			    OFMakeVector3D(e.x, (float)S(e.x, e.y)->floor +
			    mmi.zoff + e.attr3, e.y), (float)((e.attr1 + 7) -
			        e.y),
			    (float)((e.attr1 + 7) - (e.attr1 + 7) % 15), 0,
			    false, 1.0f, 10.0f, mmi.snap, 0);
			    (e.attr1 + 7) % 15), 0, false, 1.0f, 10.0f,
			    mmi.snap, 0);
		} else {
			if (OUTBORD(e.x, e.y))
				continue;
			if (e.type != CARROT) {
				if (!e.spawned && e.type != TELEPORT)
					continue;
				if (e.type < I_SHELLS || e.type > TELEPORT)
					continue;
				renderent(e, entmdlnames[e.type - I_SHELLS],
				    (float)(1 +
				        sin(lastmillis / 100.0 + e.x + e.y) /
				    (float)(1 + sin(lastmillis / 100.0 + e.x +
				            20),
				    lastmillis / 10.0f, 0, 1, 0, 10.0f);
				    e.y) / 20), lastmillis / 10.0f, 0, 1, 0,
				    10.0f);
			} else {
				switch (e.attr2) {
				case 1:
				case 3:
					continue;

				case 2:
				case 0:
					if (!e.spawned)
						continue;
					renderent(e, @"carrot",
					renderent(e, @"carrot", (float)(1 +
					    (float)(1 +
					        sin(lastmillis / 100.0 + e.x +
					    sin(lastmillis / 100.0 + e.x +
					            e.y) /
					            20),
					    lastmillis /
					    e.y) / 20), lastmillis / (e.attr2 ?
					        (e.attr2 ? 1.0f : 10.0f),
					    0, 1, 0, 10.0f);
					    1.0f : 10.0f), 0, 1, 0, 10.0f);
					break;

				case 4:
					renderent(e, @"switch2", 3,
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ? 1
					    (!e.spawned && !triggertime) ?
					                                 : 0,
					    (e.spawned || !triggertime) ? 1 : 2,
					    triggertime, 1050.0f);
					    1 : 0, (e.spawned || !triggertime) ?
					    1 : 2, triggertime, 1050.0f);
					break;
				case 5:
					renderent(e, @"switch1", -0.15f,
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ? 30
					                                 : 0,
					    (e.spawned || !triggertime) ? 1
					    (!e.spawned && !triggertime) ?
					    30 : 0, (e.spawned ||
					    !triggertime) ? 1 : 30, triggertime,
					                                : 30,
					    triggertime, 35.0f);
					    35.0f);
					break;
				}
			}
		}
	}
}

Modified src/physics.m from [95d4c13f64] to [775d6c79ad].

11
12
13
14
15
16
17
18
19


20
21
22
23
24
25
26
11
12
13
14
15
16
17


18
19
20
21
22
23
24
25
26







-
-
+
+







#import "MapModelInfo.h"
#import "Monster.h"
#import "Player.h"
#import "Variable.h"

// collide with player or monster
static bool
plcollide(
    DynamicEntity *d, DynamicEntity *o, float *headspace, float *hi, float *lo)
plcollide(DynamicEntity *d, DynamicEntity *o, float *headspace, float *hi,
    float *lo)
{
	if (o.state != CS_ALIVE)
		return true;

	const float r = o.radius + d.radius;
	if (fabs(o.origin.x - d.origin.x) < r &&
	    fabs(o.origin.y - d.origin.y) < r) {
121
122
123
124
125
126
127
128

129
130

131
132

133
134
135


136
137
138
139


140
141
142
143


144
145
146
147
148
149
150
151
152


153
154
155
156
157
158


159
160
161
162
163
164
165
166
121
122
123
124
125
126
127

128


129


130



131
132




133
134




135
136
137
138
139
140
141
142
143


144
145

146
147
148


149
150

151
152
153
154
155
156
157







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







-
-
+
+
-



-
-
+
+
-







			float floor = s->floor;

			switch (s->type) {
			case SOLID:
				return false;
			case CORNER: {
				int bx = x, by = y, bs = 1;
				if ((x == x1 && y == y1 &&
				if ((x == x1 && y == y1 && cornertest(0, x, y,
				        cornertest(
				            0, x, y, -1, -1, &bx, &by, &bs) &&
				    -1, -1, &bx, &by, &bs) && fx1 - bx + fy1 -
				        fx1 - bx + fy1 - by <= bs) ||
				    (x == x2 && y == y1 &&
				    by <= bs) || (x == x2 && y == y1 &&
				        cornertest(
				            0, x, y, 1, -1, &bx, &by, &bs) &&
				        fx2 - bx >= fy1 - by) ||
				    cornertest(0, x, y, 1, -1, &bx, &by, &bs) &&
				    fx2 - bx >= fy1 - by) || (x == x1 && y ==
				    (x == x1 && y == y2 &&
				        cornertest(
				            0, x, y, -1, 1, &bx, &by, &bs) &&
				        fx1 - bx <= fy2 - by) ||
				    y2 && cornertest(0, x, y, -1, 1, &bx, &by,
				    &bs) && fx1 - bx <= fy2 - by) || (x == x2 &&
				    (x == x2 && y == y2 &&
				        cornertest(
				            0, x, y, 1, 1, &bx, &by, &bs) &&
				        fx2 - bx + fy2 - by >= bs))
				    y == y2 && cornertest(0, x, y, 1, 1, &bx,
				    &by, &bs) && fx2 - bx + fy2 - by >= bs))
					return false;
				break;
			}
			// FIXME: too simplistic collision with slopes, makes
			// it feels like tiny stairs
			case FHF:
				floor -= (s->vdelta + S(x + 1, y)->vdelta +
				             S(x, y + 1)->vdelta +
				             S(x + 1, y + 1)->vdelta) /
				    S(x, y + 1)->vdelta +
				    S(x + 1, y + 1)->vdelta) / 16.0f;
				    16.0f;
				break;
			case CHF:
				ceil += (s->vdelta + S(x + 1, y)->vdelta +
				            S(x, y + 1)->vdelta +
				            S(x + 1, y + 1)->vdelta) /
				    S(x, y + 1)->vdelta +
				    S(x + 1, y + 1)->vdelta) / 16.0f;
				    16.0f;
			}

			if (ceil < hi)
				hi = ceil;
			if (floor > lo)
				lo = floor;
			if (floor < minfloor)
210
211
212
213
214
215
216
217

218
219

220
221
222
223
224
225
226
201
202
203
204
205
206
207

208


209
210
211
212
213
214
215
216







-
+
-
-
+







				// rise thru stair
				d.origin = OFAddVectors3D(
				    d.origin, OFMakeVector3D(0, 0, rise));
			else
				return false;
		} else
			// gravity
			d.origin = OFSubtractVectors3D(d.origin,
			d.origin = OFSubtractVectors3D(d.origin, OFMakeVector3D(
			    OFMakeVector3D(
			        0, 0, min(min(drop, space), headspace)));
			    0, 0, min(min(drop, space), headspace)));

		const float space2 = hi - (d.origin.z + d.aboveEye);
		if (space2 < 0) {
			if (space2 < -0.1)
				return false; // hack alert!
			// glue to ceiling
			d.origin = OFMakeVector3D(
403
404
405
406
407
408
409
410

411
412
413
414



415
416
417
418
419
420
421
393
394
395
396
397
398
399

400




401
402
403
404
405
406
407
408
409
410







-
+
-
-
-
-
+
+
+







	// mostly

	if (pl.origin.x < 0 || pl.origin.x >= ssize || pl.origin.y < 0 ||
	    pl.origin.y > ssize)
		pl.outsideMap = true;
	else {
		struct sqr *s = S((int)pl.origin.x, (int)pl.origin.y);
		pl.outsideMap = SOLID(s) ||
		pl.outsideMap = (SOLID(s) || pl.origin.z < s->floor -
		    pl.origin.z <
		        s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
		    pl.origin.z >
		        s->ceil + (s->type == CHF ? s->vdelta / 4 : 0);
		    (s->type == FHF ? s->vdelta / 4 : 0) ||
		    pl.origin.z > s->ceil + (s->type == CHF
		    ? s->vdelta / 4 : 0));
	}

	// automatically apply smooth roll when strafing

	if (pl.strafe == 0)
		pl.roll = pl.roll / (1 + (float)sqrt((float)curtime) / 25);
	else {
439
440
441
442
443
444
445
446

447
448
449

450
428
429
430
431
432
433
434

435



436
437







-
+
-
-
-
+

	pl.inWater = water;
}

void
moveplayer(DynamicEntity *pl, int moveres, bool local)
{
	for (int i = 0; i < physicsrepeat; i++)
		moveplayer4(pl, moveres, local,
		moveplayer4(pl, moveres, local, i ? curtime / physicsrepeat
		    i ? curtime / physicsrepeat
		      : curtime -
		            curtime / physicsrepeat * (physicsrepeat - 1));
		    : curtime - curtime / physicsrepeat * (physicsrepeat - 1));
}

Modified src/rendercubes.m from [d20e5d1984] to [ee90f3d4f5].

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
55


56
57
58
59
60
61
62
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
55
56
57
58
59
60
61
62







-
-
-
-
+
+
+
+


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


-
-
+
+







	setarraypointers();
}

// generating the actual vertices is done dynamically every frame and sits at
// the leaves of all these functions, and are part of the cpu bottleneck on
// really slow machines, hence the macros.

#define vertcheck()                         \
	{                                   \
		if (curvert >= curmaxverts) \
			reallocv();         \
#define vertcheck()				\
	{					\
		if (curvert >= curmaxverts)	\
			reallocv();		\
	}

#define vertf(v1, v2, v3, ls, t1, t2)                 \
	{                                             \
		struct vertex *v = &verts[curvert++]; \
		v->u = t1;                            \
		v->v = t2;                            \
		v->x = v1;                            \
		v->y = v2;                            \
		v->z = v3;                            \
		v->r = ls->r;                         \
		v->g = ls->g;                         \
		v->b = ls->b;                         \
		v->a = 255;                           \
#define vertf(v1, v2, v3, ls, t1, t2)			\
	{						\
		struct vertex *v = &verts[curvert++];	\
		v->u = t1;				\
		v->v = t2;				\
		v->x = v1;				\
		v->y = v2;				\
		v->z = v3;				\
		v->r = ls->r;				\
		v->g = ls->g;				\
		v->b = ls->b;				\
		v->a = 255;				\
	}

#define vert(v1, v2, v3, ls, t1, t2)                                      \
	{                                                                 \
#define vert(v1, v2, v3, ls, t1, t2)					  \
	{								  \
		vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); \
	}

int nquads;
const float TEXTURESCALE = 32.0f;
bool floorstrip = false, deltastrip = false;
int oh, oy, ox, ogltex; // the o* vars are used by the stripification
71
72
73
74
75
76
77
78
79
80



81
82
83


84
85
86
87
88
89
90
71
72
73
74
75
76
77



78
79
80
81


82
83
84
85
86
87
88
89
90







-
-
-
+
+
+

-
-
+
+







void
mipstats(int a, int b, int c)
{
	if (showm)
		conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c);
}

#define stripend()                                                          \
	{                                                                   \
		if (floorstrip || deltastrip) {                             \
#define stripend()							    \
	{								    \
		if (floorstrip || deltastrip) {				    \
			addstrip(ogltex, firstindex, curvert - firstindex); \
			floorstrip = deltastrip = false;                    \
		}                                                           \
			floorstrip = deltastrip = false;		    \
		}							    \
	}
void
finishstrips()
{
	stripend();
}

134
135
136
137
138
139
140

141
142

143
144
145
146
147




148
149
150
151
152
153
154
155
134
135
136
137
138
139
140
141
142

143





144
145
146
147

148
149
150
151
152
153
154







+

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







		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;
	} else // continue strip
	{
		int lighterr = lighterror * 2;
		// skip vertices if light values are close enough
		if ((abs(ol3r - l3->r) < lighterr &&
		        abs(ol4r - l4->r) < lighterr // skip vertices if light
		    abs(ol4r - l4->r) < lighterr &&
		                                     // values are close enough
		        && abs(ol3g - l3->g) < lighterr &&
		        abs(ol4g - l4->g) < lighterr &&
		        abs(ol3b - l3->b) < lighterr &&
		        abs(ol4b - l4->b) < lighterr) ||
		    abs(ol3g - l3->g) < lighterr &&
		    abs(ol4g - l4->g) < lighterr &&
		    abs(ol3b - l3->b) < lighterr &&
		    abs(ol4b - l4->b) < lighterr) || !wtex) {
		    !wtex) {
			curvert -= 2;
			nquads--;
		} else {
			unsigned char *p3 =
			    (unsigned char *)(&verts[curvert - 1].r);
			ol3r = p3[0];
			ol3g = p3[1];

Modified src/renderextras.m from [f1478ac26e] to [8a14c68979].

202
203
204
205
206
207
208
209
210
211
212
213
214





215
216
217
218
219
220
221
222

223
224
225
226
227
228
229
202
203
204
205
206
207
208






209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227
228







-
-
-
-
-
-
+
+
+
+
+







-
+








		particle_splash(2, 2, 40, OFMakeVector3D(e.x, e.y, e.z));
	}

	int e = closestent();
	if (e >= 0) {
		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")];
		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")];
	}
}

COMMAND(loadsky, ARG_1STR, (^ (OFString *basename) {
	static OFString *lastsky = @"";

	basename = [basename stringByReplacingOccurrencesOfString: @"\\"
	                                               withString: @"/"];
						       withString: @"/"];

	if ([lastsky isEqual: basename])
		return;

	static const OFString *side[] = { @"ft", @"bk", @"lf", @"rt", @"dn",
		@"up" };
	int texnum = 14;

Modified src/rendergl.m from [8875dffa42] to [e10c3367c9].

54
55
56
57
58
59
60
61

62
63
64
65
66
67
68
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68







-
+








	char *exts = (char *)glGetString(GL_EXTENSIONS);

	if (strstr(exts, "GL_EXT_texture_env_combine"))
		hasoverbright = true;
	else
		conoutf(@"WARNING: cannot use overbright lighting, using old "
		        @"lighting model!");
		    @"lighting model!");

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize);

	purgetextures();

	if (!(qsphere = gluNewQuadric()))
		fatal(@"glu sphere");
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115
100
101
102
103
104
105
106

107

108
109
110
111
112
113
114







-
+
-







		}

		@try {
			SDL_Surface *converted =
			    SDL_ConvertSurface(s, format, 0);
			if (converted == NULL) {
				conoutf(@"texture cannot be converted "
				        @"to 24bpp: %@",
				    @"to 24bpp: %@", IRI.string);
				    IRI.string);
				return false;
			}

			SDL_FreeSurface(s);
			s = converted;
		} @finally {
			SDL_FreeFormat(format);
147
148
149
150
151
152
153
154

155
156
157
158
159
160
161
146
147
148
149
150
151
152

153
154
155
156
157
158
159
160







-
+







		    IRI.string); // for voodoo cards under linux
		scaledimg = OFAllocMemory(1, *xs * *ys * 3);
		gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels,
		    *xs, *ys, GL_UNSIGNED_BYTE, scaledimg);
	}

	if (gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, *xs, *ys, GL_RGB,
	        GL_UNSIGNED_BYTE, scaledimg))
	    GL_UNSIGNED_BYTE, scaledimg))
		fatal(@"could not build mipmaps");

	if (*xs != s->w)
		free(scaledimg);

	SDL_FreeSurface(s);

198
199
200
201
202
203
204
205

206
207
208
209
210
211
212
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211







-
+







	int num = curtexnum++, frame = aframe.cube_intValue;

	if (num < 0 || num >= 256 || frame < 0 || frame >= MAXFRAMES)
		return;

	mapping[num][frame] = 1;
	mapname[num][frame] = [name stringByReplacingOccurrencesOfString: @"\\"
	                                                      withString: @"/"];
							      withString: @"/"];
}))

int
lookuptexture(int tex, int *xs, int *ys)
{
	int frame = 0; // other frames?
	int tid = mapping[tex][frame];
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
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







-
-
+
+



















-
+
-




















-
+
-
-
+
+








VARFP(gamma, 30, 100, 300, {
	float f = gamma / 100.0f;
	Uint16 ramp[256];

	SDL_CalculateGammaRamp(f, ramp);

	if (SDL_SetWindowGammaRamp(
	        Cube.sharedInstance.window, ramp, ramp, ramp) == -1) {
	if (SDL_SetWindowGammaRamp(Cube.sharedInstance.window,
	    ramp, ramp, ramp) == -1) {
		conoutf(
		    @"Could not set gamma (card/driver doesn't support it?)");
		conoutf(@"sdl: %s", SDL_GetError());
	}
})

void
transplayer()
{
	Player *player1 = Player.player1;

	glLoadIdentity();

	glRotated(player1.roll, 0.0, 0.0, 1.0);
	glRotated(player1.pitch, -1.0, 0.0, 0.0);
	glRotated(player1.yaw, 0.0, 1.0, 0.0);

	glTranslated(-player1.origin.x,
	    (player1.state == CS_DEAD ? player1.eyeHeight - 0.2f : 0) -
	        player1.origin.z,
	    player1.origin.z, -player1.origin.y);
	    -player1.origin.y);
}

VARP(fov, 10, 105, 120);

int xtraverts;

VAR(fog, 64, 180, 1024);
VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF);

VARP(hudgun, 0, 1, 1);

OFString *hudgunnames[] = { @"hudguns/fist", @"hudguns/shotg",
	@"hudguns/chaing", @"hudguns/rocket", @"hudguns/rifle" };

void
drawhudmodel(int start, int end, float speed, int base)
{
	Player *player1 = Player.player1;

	rendermodel(hudgunnames[player1.gunSelect], start, end, 0, 1.0f,
	    OFMakeVector3D(
	    OFMakeVector3D(player1.origin.x, player1.origin.z,
	        player1.origin.x, player1.origin.z, player1.origin.y),
	    player1.yaw + 90, player1.pitch, false, 1.0f, speed, 0, base);
	    player1.origin.y), player1.yaw + 90, player1.pitch, false, 1.0f,
	    speed, 0, base);
}

void
drawhudgun(float fovy, float aspect, int farplane)
{
	Player *player1 = Player.player1;

Modified src/rendermd2.m from [923fa1d88f] to [11936c1b8d].

87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114



115
116
117
118
119
120
121
122
123



124
125
126
127
128
129
130
131
132
133
134
135









136
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111



112
113
114
115
116
117
118
119
120



121
122
123
124
125
126









127
128
129
130
131
132
133
134
135
136







-
+

















-
-
-
+
+
+






-
-
-
+
+
+



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

rendermodel(OFString *mdl, int frame, int range, int tex, float rad,
    OFVector3D position, float yaw, float pitch, bool teammate, float scale,
    float speed, int snap, int basetime)
{
	MD2 *m = loadmodel(mdl);

	if (isoccluded(Player.player1.origin.x, Player.player1.origin.y,
	        position.x - rad, position.z - rad, rad * 2))
	    position.x - rad, position.z - rad, rad * 2))
		return;

	delayedload(m);

	int xs, ys;
	glBindTexture(GL_TEXTURE_2D,
	    tex ? lookuptexture(tex, &xs, &ys) : FIRSTMDL + m.mdlnum);

	int ix = (int)position.x;
	int iy = (int)position.z;
	OFColor *light = OFColor.white;

	if (!OUTBORD(ix, iy)) {
		struct sqr *s = S(ix, iy);
		float ll = 256.0f; // 0.96f;
		float of = 0.0f;   // 0.1f;
		light = [OFColor colorWithRed: s->r / ll + of
		                        green: s->g / ll + of
		                         blue: s->b / ll + of
		                        alpha: 1];
					green: s->g / ll + of
					 blue: s->b / ll + of
					alpha: 1.0f];
	}

	if (teammate) {
		float red, green, blue;
		[light getRed: &red green: &green blue: &blue alpha: NULL];
		light = [OFColor colorWithRed: red * 0.6f
		                        green: green * 0.7f
		                         blue: blue * 1.2f
		                        alpha: 1];
					green: green * 0.7f
					 blue: blue * 1.2f
					alpha: 1.0f];
	}

	[m renderWithLight: light
	             frame: frame
	             range: range
	          position: position
	               yaw: yaw
	             pitch: pitch
	             scale: scale
	             speed: speed
	              snap: snap
	          basetime: basetime];
		     frame: frame
		     range: range
		  position: position
		       yaw: yaw
		     pitch: pitch
		     scale: scale
		     speed: speed
		      snap: snap
		  basetime: basetime];
}

Modified src/renderparticles.m from [f1b52c549a] to [187397123f].

68
69
70
71
72
73
74
75

76
77
78
79
80
81
82






83
84
85
86
87
88
89
68
69
70
71
72
73
74

75







76
77
78
79
80
81
82
83
84
85
86
87
88







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







	struct parttype {
		float r, g, b;
		int gr, tex;
		float sz;
	} parttypes[] = {
		{ 0.7f, 0.6f, 0.3f, 2, 3, 0.06f },  // yellow: sparks
		{ 0.5f, 0.5f, 0.5f, 20, 7, 0.15f }, // grey:   small smoke
		{ 0.2f, 0.2f, 1.0f, 20, 3,
		{ 0.2f, 0.2f, 1.0f, 20, 3, 0.08f }, // blue:   edit mode entities
		    0.08f },                       // blue:   edit mode entities
		{ 1.0f, 0.1f, 0.1f, 1, 7, 0.06f }, // red:    blood spats
		{ 1.0f, 0.8f, 0.8f, 20, 6, 1.2f }, // yellow: fireball1
		{ 0.5f, 0.5f, 0.5f, 20, 7, 0.6f }, // grey:   big smoke
		{ 1.0f, 1.0f, 1.0f, 20, 8, 1.2f }, // blue:   fireball2
		{ 1.0f, 1.0f, 1.0f, 20, 9, 1.2f }, // green:  fireball3
		{ 1.0f, 0.1f, 0.1f, 0, 7, 0.2f },  // red:    demotrack
		{ 1.0f, 0.1f, 0.1f, 1, 7, 0.06f },  // red:    blood spats
		{ 1.0f, 0.8f, 0.8f, 20, 6, 1.2f },  // yellow: fireball1
		{ 0.5f, 0.5f, 0.5f, 20, 7, 0.6f },  // grey:   big smoke
		{ 1.0f, 1.0f, 1.0f, 20, 8, 1.2f },  // blue:   fireball2
		{ 1.0f, 1.0f, 1.0f, 20, 9, 1.2f },  // green:  fireball3
		{ 1.0f, 0.1f, 0.1f, 0, 7, 0.2f },   // red:    demotrack
	};

	int numrender = 0;

	for (struct particle *p, **pp = &parlist; (p = *pp) != NULL;) {
		struct parttype *pt = &parttypes[p->type];

Modified src/rendertext.m from [656fe9791c] to [af0dd28de8].

136
137
138
139
140
141
142
143

144
145
146
147
148
149
150
136
137
138
139
140
141
142

143
144
145
146
147
148
149
150







-
+








void
draw_textf(OFConstantString *format, int left, int top, int gl_num, ...)
{
	va_list arguments;
	va_start(arguments, gl_num);
	OFString *str = [[OFString alloc] initWithFormat: format
	                                       arguments: arguments];
					       arguments: arguments];
	va_end(arguments);
	draw_text(str, left, top, gl_num);
}

void
draw_text(OFString *string, int left, int top, int gl_num)
{

Modified src/rndmap.m from [050a1e4f8d] to [b1dc1f80ec].

12
13
14
15
16
17
18
19
20
21


22
23
24

25
26
27
28
29
30
31
32
12
13
14
15
16
17
18



19
20

21

22

23
24
25
26
27
28
29







-
-
-
+
+
-

-
+
-







	    ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) /
	    1073741824.0f;
}

float
smoothednoise(int x, int y, int seed)
{
	float corners =
	    (noise(x - 1, y - 1, seed) + noise(x + 1, y - 1, seed) +
	        noise(x - 1, y + 1, seed) + noise(x + 1, y + 1, seed)) /
	float corners = (noise(x - 1, y - 1, seed) + noise(x + 1, y - 1, seed) +
	    noise(x - 1, y + 1, seed) + noise(x + 1, y + 1, seed)) / 16;
	    16;
	float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
	                  noise(x, y - 1, seed) + noise(x, y + 1, seed)) /
	    noise(x, y - 1, seed) + noise(x, y + 1, seed)) / 8;
	    8;
	float center = noise(x, y, seed) / 4;
	return corners + sides + center;
}

float
interpolate(float a, float b, float x)
{
74
75
76
77
78
79
80
81
82
83



84
85
86
87
88
89
90
71
72
73
74
75
76
77



78
79
80


81
82
83
84
85







-
-
-
+
+
+
-
-





	if (!scale)
		scale = 10;
	for (int x = b->x; x <= b->x + b->xs; x++) {
		for (int y = b->y; y <= b->y + b->ys; y++) {
			struct sqr *s = S(x, y);
			if (!SOLID(s) && x != b->x + b->xs && y != b->y + b->ys)
				s->type = FHF;
			s->vdelta =
			    (int)(perlinnoise_2D(x / ((float)scale) + seed,
			              y / ((float)scale) + seed, 1000, 0.01f) *
			s->vdelta = (int)(perlinnoise_2D(
			    x / ((float)scale) + seed,
			    y / ((float)scale) + seed, 1000, 0.01f) * 50 + 25);
			            50 +
			        25);
			if (s->vdelta > 128)
				s->vdelta = 0;
		}
	}
}

Modified src/savegamedemo.m from [94bda1495e] to [09c2cc3e3c].

160
161
162
163
164
165
166
167
168
169



170
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
202
203
204
205
160
161
162
163
164
165
166



167
168
169
170
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
202
203
204
205







-
-
-
+
+
+











-
+
















-
+








	char mapname[_MAXDEFSTR] = { 0 };
	char buf[8];
	gzread(f, buf, 8);
	if (strncmp(buf, "CUBESAVE", 8))
		goto out;
	if (gzgetc(f) != islittleendian)
		goto out; // not supporting save->load accross
		          // incompatible architectures simpifies things
		          // a LOT
		// not supporting save->load accross incompatible architectures
		// simpifies things a LOT
		goto out;
	if (gzgeti() != SAVEGAMEVERSION ||
	    gzgeti() != DynamicEntity.serializedSize)
		goto out;
	gzread(f, mapname, _MAXDEFSTR);
	nextmode = gzgeti();
	// continue below once map has been loaded and client & server
	// have updated
	changemap(@(mapname));
	return;
out:
	conoutf(@"aborting: savegame/demo from a different version of "
	        @"cube or cpu architecture");
	    @"cube or cpu architecture");
	stop();
}

COMMAND(loadgame, ARG_1STR, (^ (OFString *name) {
	OFString *path = [OFString stringWithFormat:
	    @"savegames/%@.csgz", name];
	OFIRI *IRI = [Cube.sharedInstance.userDataIRI
	    IRIByAppendingPathComponent: path];
	loadstate(IRI);
}))

void
loadgameout()
{
	stop();
	conoutf(@"loadgame incomplete: savegame from a different version of "
	        @"this map");
	    @"this map");
}

void
loadgamerest()
{
	if (demoplayback || !f)
		return;
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
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







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


-
-
-
-
-
+
+
+
+
+







	setclient(democlientnum, [Player.player1 copy]);
	readdemotime();
}

VAR(demodelaymsec, 0, 120, 500);

// spline interpolation
#define catmulrom(z, a, b, c, s, dest)                                  \
	{                                                               \
		OFVector3D t1 = OFSubtractVectors3D(b, z);              \
		t1 = OFMultiplyVector3D(t1, 0.5f);                      \
                                                                        \
		OFVector3D t2 = OFSubtractVectors3D(c, a);              \
		t2 = OFMultiplyVector3D(t2, 0.5f);                      \
                                                                        \
		float s2 = s * s;                                       \
		float s3 = s * s2;                                      \
                                                                        \
#define catmulrom(z, a, b, c, s, dest)					\
	{								\
		OFVector3D t1 = OFSubtractVectors3D(b, z);		\
		t1 = OFMultiplyVector3D(t1, 0.5f);			\
									\
		OFVector3D t2 = OFSubtractVectors3D(c, a);		\
		t2 = OFMultiplyVector3D(t2, 0.5f);			\
									\
		float s2 = s * s;					\
		float s3 = s * s2;					\
									\
		dest = OFMultiplyVector3D(a, 2 * s3 - 3 * s2 + 1);      \
		OFVector3D t = OFMultiplyVector3D(b, -2 * s3 + 3 * s2); \
		dest = OFAddVectors3D(dest, t);                         \
		t1 = OFMultiplyVector3D(t1, s3 - 2 * s2 + s);           \
		dest = OFAddVectors3D(dest, t1);                        \
		t2 = OFMultiplyVector3D(t2, s3 - s2);                   \
		dest = OFAddVectors3D(dest, t2);                        \
		dest = OFAddVectors3D(dest, t);				\
		t1 = OFMultiplyVector3D(t1, s3 - 2 * s2 + s);		\
		dest = OFAddVectors3D(dest, t1);			\
		t2 = OFMultiplyVector3D(t2, s3 - s2);			\
		dest = OFAddVectors3D(dest, t2);			\
	}

void
fixwrap(DynamicEntity *a, DynamicEntity *b)
{
	while (b.yaw - a.yaw > 180)
		a.yaw += 360;
449
450
451
452
453
454
455
456
457
458


459
460
461
462
463
464
465
449
450
451
452
453
454
455



456
457
458
459
460
461
462
463
464







-
-
-
+
+







				gzgetv(&dorig);
				particle_splash(3, ddamage, 1000, dorig);
			}
			// FIXME: set more client state here
		}

		// insert latest copy of player into history
		if (extras &&
		    (playerhistory.count == 0 ||
		        playerhistory.lastObject.lastUpdate != playbacktime)) {
		if (extras && (playerhistory.count == 0 ||
		    playerhistory.lastObject.lastUpdate != playbacktime)) {
			Player *d = [target copy];
			d.lastUpdate = playbacktime;

			if (playerhistory == nil)
				playerhistory = [[OFMutableArray alloc] init];

			[playerhistory addObject: d];

Modified src/server.m from [95faadaf28] to [043bbacb23].

395
396
397
398
399
400
401


402
403

404
405
406
407
408
409
410
411
412
395
396
397
398
399
400
401
402
403
404

405


406
407
408
409
410
411
412







+
+

-
+
-
-







	mapend = lastsec + minremain * 60;
	interm = 0;
}

int nonlocalclients = 0;
int lastconnect = 0;

// main server update, called from cube main loop in sp, or dedicated server
// loop
void
serverslice(int seconds,
serverslice(int seconds, unsigned int timeout)
    unsigned int timeout) // main server update, called from cube main loop in
                          // sp, or dedicated server loop
{
	// spawn entities when timer reached
	[sents enumerateObjectsUsingBlock:
	    ^ (ServerEntity *e, size_t i, bool *stop) {
		if (e.spawnsecs && (e.spawnsecs -= seconds - lastsec) <= 0) {
			e.spawnsecs = 0;
			e.spawned = true;
468
469
470
471
472
473
474
475

476
477

478
479
480
481
482
483
484
468
469
470
471
472
473
474

475


476
477
478
479
480
481
482
483







-
+
-
-
+







		case ENET_EVENT_TYPE_CONNECT: {
			Client *c = addclient();
			c.type = ST_TCPIP;
			c.peer = event.peer;
			c.peer->data = (void *)(clients.count - 1);
			char hn[1024];
			c.hostname = (enet_address_get_host(
			                  &c.peer->address, hn, sizeof(hn)) == 0
			    &c.peer->address, hn, sizeof(hn)) == 0
			        ? @(hn)
			        : @"localhost");
			    ? @(hn) : @"localhost");
			[OFStdOut writeFormat: @"client connected (%@)\n",
					       c.hostname];
			send_welcome(lastconnect = clients.count - 1);
			break;
		}
		case ENET_EVENT_TYPE_RECEIVE:
			brec += event.packet->dataLength;

Modified src/sound.m from [78a898d2ad] to [16682ac683].

53
54
55
56
57
58
59
60

61
62
63
64
65
66
67

68
69
70
71
72
73
74
53
54
55
56
57
58
59

60
61
62
63
64
65
66

67
68
69
70
71
72
73
74







-
+






-
+







	if (nosound)
		return;

	stopsound();

	if (soundvol && musicvol) {
		name = [name stringByReplacingOccurrencesOfString: @"\\"
		                                       withString: @"/"];
						       withString: @"/"];
		OFString *path = [OFString stringWithFormat:
		    @"packages/%@", name];
		OFIRI *IRI = [Cube.sharedInstance.gameDataIRI
		    IRIByAppendingPathComponent: path];

		if ((mod = Mix_LoadMUS(
		         IRI.fileSystemRepresentation.UTF8String)) != NULL) {
		    IRI.fileSystemRepresentation.UTF8String)) != NULL) {
			Mix_PlayMusic(mod, -1);
			Mix_VolumeMusic((musicvol * MAXVOL) / 255);
		}
	}
}))

static OFMutableData *samples;

Modified src/tools.h from [754911f423] to [d97579a315].

14
15
16
17
18
19
20
21
22
23
24




25
26
27
28
29
30
31
14
15
16
17
18
19
20




21
22
23
24
25
26
27
28
29
30
31







-
-
-
-
+
+
+
+








#import <ObjFW/ObjFW.h>

#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define rnd(max) (rand() % (max))
#define rndreset() (srand(1))
#define rndtime()                                            \
	{                                                    \
		for (int i = 0; i < (lastmillis & 0xF); i++) \
			rnd(i + 1);                          \
#define rndtime()						\
	{							\
		for (int i = 0; i < (lastmillis & 0xF); i++)	\
			rnd(i + 1);				\
	}

#ifndef OF_WINDOWS
# define __cdecl
#endif

#ifdef __cplusplus

Modified src/world.m from [a8feb3f63c] to [555501440d].

157
158
159
160
161
162
163
164

165
166
167
168
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
157
158
159
160
161
162
163

164

165
166
167
168
169
170
171
172
173
174

175

176
177
178
179
180
181
182







-
+
-










-
+
-







								// crap hack,
								// needed for
								// rendering
								// large mips
								// next to hfs
								fh -=
								    o[i]->vdelta /
								        4 +
								    4 + 2;
								    2;
							if (o[i]->type == CHF)
								// FIXME: needs
								// to somehow
								// take into
								// account
								// middle
								// vertices on
								// higher mips
								ch +=
								    o[i]->vdelta /
								        4 +
								    4 + 2;
								    2;
						}
						if (fh < floor)
							// take lowest floor and
							// highest ceil, so we
							// never have to see
							// missing lower/upper
							// from the side
210
211
212
213
214
215
216
217

218
219

220
221

222
223
224
225
226
227
228
229
230
231
232


233
234
235
236


237
238
239
240


241
242
243
244


245
246
247
248


249
250
251
252
253
254
255
256
208
209
210
211
212
213
214

215
216

217
218

219
220
221
222
223
224
225
226
227
228


229
230

231


232
233

234


235
236

237


238
239

240


241
242

243
244
245
246
247
248
249







-
+

-
+

-
+









-
-
+
+
-

-
-
+
+
-

-
-
+
+
-

-
-
+
+
-

-
-
+
+
-







					// exactly equal
					if (o[i]->type != o[3]->type ||
					    o[i]->floor != o[3]->floor ||
					    o[i]->ceil != o[3]->ceil ||
					    o[i]->ftex != o[3]->ftex ||
					    o[i]->ctex != o[3]->ctex ||
					    abs(o[i + 1]->r - o[0]->r) >
					        lighterr ||
					    lighterr ||
					    abs(o[i + 1]->g - o[0]->g) >
					        lighterr ||
					    lighterr ||
					    abs(o[i + 1]->b - o[0]->b) >
					        lighterr ||
					    lighterr ||
					    o[i]->utex != o[3]->utex ||
					    o[i]->wtex != o[3]->wtex)
						goto c;
				}

				// can make a perfect mip out of a hf if slopes
				// lie on one line
				if (r->type == CHF || r->type == FHF) {
					if (o[0]->vdelta - o[1]->vdelta !=
					        o[1]->vdelta -
					            SWS(w, x + 2, y, ws)
					    o[1]->vdelta -
					    SWS(w, x + 2, y, ws)->vdelta ||
					                ->vdelta ||
					    o[0]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 2, y + 2, ws)
					    o[2]->vdelta -
					    SWS(w, x + 2, y + 2, ws)->vdelta ||
					                ->vdelta ||
					    o[0]->vdelta - o[3]->vdelta !=
					        o[3]->vdelta -
					            SWS(w, x, y + 2, ws)
					    o[3]->vdelta -
					    SWS(w, x, y + 2, ws)->vdelta ||
					                ->vdelta ||
					    o[3]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 2, y + 1, ws)
					    o[2]->vdelta -
					    SWS(w, x + 2, y + 1, ws)->vdelta ||
					                ->vdelta ||
					    o[1]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 1, y + 2, ws)
					    o[2]->vdelta -
					    SWS(w, x + 1, y + 2, ws)->vdelta)
					                ->vdelta)
						goto c;
				}
			}
			{
				// if any of the constituents is not perfect,
				// then this one isn't either
				for (int i = 0; i < 4; i++)

Modified src/worldio.m from [f35a650122] to [3c6f02e0b1].

166
167
168
169
170
171
172
173
174


175
176
177
178
179
180
181
166
167
168
169
170
171
172


173
174
175
176
177
178
179
180
181







-
-
+
+







// run-length encoding and leaves out data for certain kinds of cubes, then zlib
// removes the last bits of redundancy. Both passes contribute greatly to the
// miniscule map sizes.

void
save_world(OFString *mname)
{
	resettagareas(); // wouldn't be able to reproduce tagged areas
	                 // otherwise
	// wouldn't be able to reproduce tagged areas otherwise
	resettagareas();
	voptimize();
	toptimize();
	if (mname.length == 0)
		mname = getclientmap();
	setnames(mname);
	backup(cgzname, bakname);
	gzFile f = gzopen([cgzname cStringWithEncoding: OFLocale.encoding],
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215










216
217
218
219
220
221
222
199
200
201
202
203
204
205










206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222







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







				e.type, e.attr2, e.attr3, e.attr4 };
			endianswap(&tmp, sizeof(short), 4);
			gzwrite(f, &tmp, sizeof(struct persistent_entity));
		}
	}
	struct sqr *t = NULL;
	int sc = 0;
#define spurge                          \
	while (sc) {                    \
		gzputc(f, 255);         \
		if (sc > 255) {         \
			gzputc(f, 255); \
			sc -= 255;      \
		} else {                \
			gzputc(f, sc);  \
			sc = 0;         \
		}                       \
#define spurge				\
	while (sc) {			\
		gzputc(f, 255);		\
		if (sc > 255) {		\
			gzputc(f, 255);	\
			sc -= 255;	\
		} else {		\
			gzputc(f, sc);	\
			sc = 0;		\
		}			\
	}
	for (int k = 0; k < cubicsize; k++) {
		struct sqr *s = &world[k];
#define c(f) (s->f == t->f)
		// 4 types of blocks, to compress a bit:
		// 255 (2): same as previous block + count
		// 254 (3): same as previous, except light // deprecated
258
259
260
261
262
263
264


265
266

267
268
269
270
271
272
273
274
258
259
260
261
262
263
264
265
266
267

268

269
270
271
272
273
274
275







+
+

-
+
-







	settagareas();
}

COMMAND(savemap, ARG_1STR, ^ (OFString *mname) {
	save_world(mname);
})

// still supports all map formats that have existed since the earliest cube
// betas!
void
load_world(OFString *mname) // still supports all map formats that have existed
load_world(OFString *mname)
                            // since the earliest cube betas!
{
	stopifrecording();
	cleardlights();
	pruneundos(0);
	setnames(mname);
	gzFile f = gzopen([cgzname cStringWithEncoding: OFLocale.encoding],
	    "rb9");

Modified src/worldlight.m from [38a360a277] to [05ada5ded7].

38
39
40
41
42
43
44
45

46
47

48
49
50
51
52
53
54
55
56
38
39
40
41
42
43
44

45


46


47
48
49
50
51
52
53







-
+
-
-
+
-
-







	if (hasoverbright) {
		l /= lightscale;
		stepl /= lightscale;

		// coloured light version, special case because most lights are
		// white
		if (light.attr3 || light.attr4) {
			int dimness = rnd(
			int dimness = rnd((255 - (light.attr2 + light.attr3 +
			    (255 -
			        (light.attr2 + light.attr3 + light.attr4) / 3) /
			    light.attr4) / 3) / 16 + 1);
			        16 +
			    1);
			x += stepx * dimness;
			y += stepy * dimness;

			if (OUTBORD(x >> PRECBITS, y >> PRECBITS))
				return;

			int g = light.attr3 << PRECBITS;
144
145
146
147
148
149
150
151

152
153


154

155
156
157
158



159
160
161
162
163

164
165
166
167
168
169
170
141
142
143
144
145
146
147

148
149
150
151
152

153




154
155
156

157
158
159
160
161
162
163
164
165
166
167
168







-
+


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




+







}

// median filter, smooths out random noise in light and makes it more mipable
void
postlightarea(const struct block *a)
{
	// assumes area not on edge of world
	for (int x = 0; x < a->xs; x++)
	for (int x = 0; x < a->xs; x++) {
		for (int y = 0; y < a->ys; y++) {
			struct sqr *s = S(x + a->x, y + a->y);

			// median is 4/2/1 instead
#define median(m)                                                            \
#define median(m)							 \
	s->m =                                                               \
	    (s->m * 2 + SW(s, 1, 0)->m * 2 + SW(s, 0, 1)->m * 2 +            \
	        SW(s, -1, 0)->m * 2 + SW(s, 0, -1)->m * 2 + SW(s, 1, 1)->m + \
	        SW(s, 1, -1)->m + SW(s, -1, 1)->m + SW(s, -1, -1)->m) /      \
	s->m = (s->m * 2 + SW(s, 1, 0)->m * 2 + SW(s, 0, 1)->m * 2 +	 \
	    SW(s, -1, 0)->m * 2 + SW(s, 0, -1)->m * 2 + SW(s, 1, 1)->m + \
	    SW(s, 1, -1)->m + SW(s, -1, 1)->m + SW(s, -1, -1)->m) / 14;
	    14; // median is 4/2/1 instead
			median(r);
			median(g);
			median(b);
		}
	}

	remip(a, 0);
}

void
calclight()
{

Modified src/worldocull.m from [a7de71ad12] to [2e489013a3].

34
35
36
37
38
39
40
41
42


43
44
45

46
47
48
49
50
51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
34
35
36
37
38
39
40


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67







-
-
+
+



+













+







	float syaw = (player1.yaw - 90 - af) / 360 * PI2;

	for (int i = 0; i < NUMRAYS; i++) {
		float angle = i * PI2 / NUMRAYS;
		// try to avoid tracing ray if outside of frustrum
		// apitch must be bigger if fov > 120
		if ((apitch > 45 || (angle < byaw && angle > syaw) ||
		        (angle < byaw - PI2 && angle > syaw - PI2) ||
		        (angle < byaw + PI2 && angle > syaw + PI2)) &&
		    (angle < byaw - PI2 && angle > syaw - PI2) ||
		    (angle < byaw + PI2 && angle > syaw + PI2)) &&
		    !OUTBORD(vx, vy) && !SOLID(S((int)vx, (int)vy))) {
			float ray = i * 8 / (float)NUMRAYS;
			float dx, dy;

			if (ray > 1 && ray < 3) {
				dx = -(ray - 2);
				dy = 1;
			} else if (ray >= 3 && ray < 5) {
				dx = -1;
				dy = -(ray - 4);
			} else if (ray >= 5 && ray < 7) {
				dx = ray - 6;
				dy = -1;
			} else {
				dx = 1;
				dy = ray > 4 ? ray - 8 : ray;
			}

			float sx = vx;
			float sy = vy;
			for (;;) {
				sx += dx;
				sy += dy;
				// 90% of time spend in this function is on this
				// line
121
122
123
124
125
126
127
128

129
130


131
132


133

134

135
136
137
138
139
140


141
142

143
144
145


146

147

148
149

150
151


152
153

154
155
156

157
158
159

160
161

162
163
164
165
166



167
168


169

170

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
123
124
125
126
127
128
129

130


131
132


133
134

135
136
137
138
139
140
141


142
143
144

145



146
147

148
149
150
151
152
153


154
155
156
157
158
159
160

161



162
163
164
165
166
167



168
169
170


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
202







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

+




-
-
+
+

-
+
-
-
-
+
+
-
+

+


+
-
-
+
+


+


-
+
-
-
-
+


+


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

+


-
-
+
+


-
+

+


-
+







-
+




-




	// find highest and lowest angle in the occlusion map that this cube
	// spans, based on its most left and right points on the border from the
	// viewer pov... I see no easier way to do this than this silly code
	// below

	float h, l;
	if (cx <= vx) // ABDFG
	if (cx <= vx) {
	{
		if (cx + csize < vx) // ADF
		// ABDFG
		if (cx + csize < vx) {
		{
			if (cy <= vy) // AD
			// ADF
			if (cy <= vy) {
			{
				// AD
				if (cy + csize < vy) {
					// A
					h = ca(-(cx - vx), -(cy + csize - vy)) +
					    4;
					l = ca(-(cx + csize - vx), -(cy - vy)) +
					    4;
				} // A
				else {
				} else {
					// D
					h = ma(-(cx + csize - vx),
					        -(cy + csize - vy)) +
					    -(cy + csize - vy)) + 4;
					    4;
					l = ma(-(cx + csize - vx), -(cy - vy)) +
					    4;
					l = ma(-(cx + csize - vx),
					    -(cy - vy)) + 4;
				} // D
				}
			} else {
				// F
				h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
				l = ca(cy - vy, -(cx - vx)) + 2;
			}
			} // F
		} else { // BG
		} else {
			// BG
			if (cy <= vy) {
				if (cy + csize < vy) {
					// B
					h = ma(-(cy + csize - vy), cx - vx) + 6;
					l = ma(-(cy + csize - vy),
					        cx + csize - vx) +
					    cx + csize - vx) + 6;
					    6;
				} // B
				else
				} else
					return 0;
			} else {
				// G
				h = ma(cy - vy, -(cx + csize - vx)) + 2;
				l = ma(cy - vy, -(cx - vx)) + 2;
			} // G
		}
	} else // CEH
			}
		}
	} else {
	{
		if (cy <= vy) // CE
		// CEH
		if (cy <= vy) {
		{
			// CE
			if (cy + csize < vy) {
				// C
				h = ca(-(cy - vy), cx - vx) + 6;
				l = ca(-(cy + csize - vy), cx + csize - vx) + 6;
			} // C
			else {
			} else {
				// E
				h = ma(cx - vx, cy - vy);
				l = ma(cx - vx, cy + csize - vy);
			} // E
			}
		} else {
			// H
			h = ca(cx + csize - vx, cy - vy);
			l = ca(cx - vx, cy + csize - vy);
		} // H
		}
	}
	// get indexes into occlusion map from angles
	int si = h * (NUMRAYS / 8) + NUMRAYS;
	int ei = l * (NUMRAYS / 8) + NUMRAYS + 1;
	if (ei <= si)
		ei += NUMRAYS;

	for (int i = si; i <= ei; i++) {
	for (int i = si; i <= ei; i++)
		if (dist < rdist[i & (NUMRAYS - 1)])
			// if any value in this segment of the occlusion map is
			// further away then cube is not occluded
			return 0;
	}

	return 1; // cube is entirely occluded
}

Modified src/worldrender.m from [60ed27f341] to [e1db597b95].

121
122
123
124
125
126
127
128
129
130



131
132
133
134
135
136
137
138
139
140
141
142

143
144
145
146
147
148
149
121
122
123
124
125
126
127



128
129
130
131
132
133
134
135
136
137
138
139
140
141

142
143
144
145
146
147
148
149







-
-
-
+
+
+











-
+







render_seg_new(
    float vx, float vy, float vh, int mip, int x, int y, int xs, int ys)
{
	struct sqr *w = wmip[mip];
	int sz = ssize >> mip;
	int vxx = ((int)vx + (1 << mip) / 2) >> mip;
	int vyy = ((int)vy + (1 << mip) / 2) >> mip;
	int lx =
	    vxx - lodleft; // these mark the rect inside the current rest that
	                   // we want to render using a lower mip level
	// these mark the rect inside the current rest that we want to render
	// using a lower mip level
	int lx = vxx - lodleft;
	int ly = vyy - lodtop;
	int rx = vxx + lodright;
	int ry = vyy + lodbot;

	float fsize = (float)(1 << mip);
	Player *player1 = Player.player1;
	for (int ox = x; ox < xs; ox++) {
		// first collect occlusion information for this block
		for (int oy = y; oy < ys; oy++) {
			SWS(w, ox, oy, sz)->occluded =
			    isoccluded(player1.origin.x, player1.origin.y,
			        (float)(ox << mip), (float)(oy << mip), fsize);
			    (float)(ox << mip), (float)(oy << mip), fsize);
		}
	}

	int pvx = (int)vx >> mip;
	int pvy = (int)vy >> mip;
	if (pvx >= 0 && pvy >= 0 && pvx < sz && pvy < sz) {
		// SWS(w,vxx,vyy,sz)->occluded = 0;
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172









173
174
175
176



177
178
179
180
181
182
183
157
158
159
160
161
162
163









164
165
166
167
168
169
170
171
172
173



174
175
176
177
178
179
180
181
182
183







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

-
-
-
+
+
+







	// loop through the rect 3 times (for floor/ceil/walls seperately, to
	// facilitate dynamic stripify) for each we skip occluded cubes
	// (occlusion at higher mip levels is a big time saver!). during the
	// first loop (ceil) we collect cubes that lie within the lower mip rect
	// and are also deferred, and render them recursively. Anything left
	// (perfect mips and higher lods) we render here.

#define LOOPH                                                          \
	{                                                              \
		for (int xx = x; xx < xs; xx++)                        \
			for (int yy = y; yy < ys; yy++) {              \
				struct sqr *s = SWS(w, xx, yy, sz);    \
				if (s->occluded == 1)                  \
					continue;                      \
				if (s->defer && !s->occluded && mip && \
				    xx >= lx && xx < rx && yy >= ly && \
#define LOOPH								\
	{								\
		for (int xx = x; xx < xs; xx++)				\
			for (int yy = y; yy < ys; yy++) {		\
				struct sqr *s = SWS(w, xx, yy, sz);	\
				if (s->occluded == 1)			\
					continue;			\
				if (s->defer && !s->occluded && mip &&	\
				    xx >= lx && xx < rx && yy >= ly &&	\
				    yy < ry)
#define LOOPD                             \
	struct sqr *t = SWS(s, 1, 0, sz); \
	struct sqr *u = SWS(s, 1, 1, sz); \
#define LOOPD					\
	struct sqr *t = SWS(s, 1, 0, sz);	\
	struct sqr *u = SWS(s, 1, 1, sz);	\
	struct sqr *v = SWS(s, 0, 1, sz);

	LOOPH // ceils
	{
		int start = yy;
		struct sqr *next;
		while (yy < ys - 1 && (next = SWS(w, xx, yy + 1, sz))->defer &&