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
#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;
- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName: (OFString *)name
                      action: (OFString *)action
                   persisted: (bool)persisted OF_DESIGNATED_INITIALIZER
    OF_DIRECT;
@end

OF_ASSUME_NONNULL_END









|
|


|
|




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;
- (instancetype)initWithName: (OFString *)name OF_UNAVAILABLE;
- (instancetype)initWithName: (OFString *)name
		      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
#import "Alias.h"

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

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

	_action = [action copy];
	_persisted = persisted;

	return self;




|
|


|
|



|
|







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;
{
	return [[self alloc] initWithName: name
				   action: action
				persisted: persisted];
}

- (instancetype)initWithName: (OFString *)name
		      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
#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_];	\
		});						\
	}

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

+ (instancetype)commandWithName: (OFString *)name
                 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;
- (int)callWithArguments: (OFArray<OFString *> *)arguments isDown: (bool)isDown;
@end

OF_ASSUME_NONNULL_END




|
|
|
|
|
|
|
|
|







|
|


|
|




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_];			\
		});							\
	}

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

+ (instancetype)commandWithName: (OFString *)name
		 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;
- (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

@implementation Command
{
	id _block;
}

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

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

	_argumentsTypes = argumentsTypes;
	_block = block;

	return self;







|
|


|
|



|
|







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
{
	return [[self alloc] initWithName: name
			   argumentsTypes: argumentsTypes
				    block: block];
}

- (instancetype)initWithName: (OFString *)name
	      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
				_height = 1080;
			}
		}

		log(@"video: mode");
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		if ((_window = SDL_CreateWindow("cube engine",
		         SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		         _width, _height,
		         SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL |
		             (!windowed ? SDL_WINDOW_FULLSCREEN : 0))) ==
		        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);







|
<
|
|
<







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,

		    _width, _height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL |
		    (!windowed ? SDL_WINDOW_FULLSCREEN : 0))) == 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
					[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);
					break;
				case SDL_TEXTINPUT:
					input(@(event.text.text));
					break;
				case SDL_MOUSEMOTION:
					if (ignore) {
						ignore--;







|
|







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

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

	if ((image = SDL_CreateRGBSurface(SDL_SWSURFACE, _width, _height, 24,
	         0x0000FF, 0x00FF00, 0xFF0000, 0)) != NULL) {
		if ((temp = SDL_CreateRGBSurface(SDL_SWSURFACE, _width, _height,
		         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),
				    3 * _width);
				endianswap(dest, 3, _width);
			}

			OFString *path = [OFString stringWithFormat:
			    @"screenshots/screenshot_%d.bmp", lastmillis];
			SDL_SaveBMP(temp,
			    [_userDataIRI IRIByAppendingPathComponent: path]







|

|






<
|
|
<







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) {
		if ((temp = SDL_CreateRGBSurface(SDL_SWSURFACE, _width, _height,
		    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), 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
// failure exit
void
fatal(OFConstantString *s, ...)
{
	va_list args;
	va_start(args, s);
	OFMutableString *msg = [[OFMutableString alloc] initWithFormat: s
	                                                     arguments: args];
	va_end(args);

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

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







|







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];
	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
@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;
- (void)scaleWithFrame: (int)frame scale: (float)scale snap: (int)snap;
@end

OF_ASSUME_NONNULL_END







|
|
|
|
|
|
|
|
|




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

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

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








|







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"];
	} @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
		_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];

	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)];
	endianswap(_glCommands, sizeof(int), header.numGlCommands);

	_numFrames = header.numFrames;
	_numGlCommands = header.numGlCommands;
	_frameSize = header.frameSize;
	_numTriangles = header.numTriangles;
	_numVerts = header.numVertices;







|












|







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

	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)];
	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
		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
{
	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);







|
|
|
|
|
|
|
|
|







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
{
	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
#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;
- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithRad: (int)rad
                          h: (int)h
                       zoff: (int)zoff
                       snap: (int)snap
                       name: (OFString *)name;
@end

OF_ASSUME_NONNULL_END










|
|
|
|


|
|
|
|



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;
- (instancetype)init OF_UNAVAILABLE;
- (instancetype)initWithRad: (int)rad
			  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
#import "MapModelInfo.h"

@implementation MapModelInfo
+ (instancetype)infoWithRad: (int)rad
                          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
{
	self = [super init];

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




|
|
|
|









|
|
|
|







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
{
	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
{
	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
+ (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;
- (instancetype)initWithType: (int)type
                         yaw: (int)yaw
                       state: (int)state
                     trigger: (int)trigger
                        move: (int)move;
- (void)incurDamage: (int)damage fromEntity: (__kindof DynamicEntity *)d;
@end







|
|
|
|

|
|
|
|


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;
- (instancetype)initWithType: (int)type
			 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

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

+ (instancetype)monsterWithType: (int)type
                            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];
}

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

// for savegames
+ (void)restoreAll
{







|
|
|
|


|
|
|
|







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
{
	return [[self alloc] initWithType: type
				      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
	{ 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
{
	self = [super init];

	if (type >= NUMMONSTERTYPES) {
		conoutf(@"warning: unknown monster in spawn: %d", type);
		type = 0;
	}







|
|
|
|







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
{
	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
		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];
			m.origin = OFMakeVector3D(e.x, e.y, e.z);
			[monsters addObject: m];
			entinmap(m);
			monstertotal++;
		}
	}
}







|
|
|
|







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

	float enemyYaw = -(float)atan2(self.enemy.origin.x - self.origin.x,
	                     self.enemy.origin.y - self.origin.y) /
	        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
	{
		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) ||
		    (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];
		}
		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];
			} 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];
				} else {
					// track player some more
					int n =
					    monstertypes[self.monsterType].rate;
					[self transitionWithState: M_HOME
					                   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);
			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];

	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(@"score: %d kills in %d seconds", numkilled,
	    (lastmillis - mtimestart) / 1000);
	monstertotal = 0;
	startintermission();
}

+ (void)thinkAll







|
|
|




|
<
<












|
|
<





|
|
|




















|
|
|













|
|
|










|
|
|





|
|
|



















<
|
|












|
|
|



















|
|







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

	float enemyYaw = -(float)atan2(self.enemy.origin.x - self.origin.x,
	    self.enemy.origin.y - self.origin.y) / 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;

		// 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);
		// 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];
		}
		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];
			} 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];
				} else {
					// track player some more
					int n =
					    monstertypes[self.monsterType].rate;
					[self transitionWithState: M_HOME
							   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);
			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];

	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(@"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
#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];		\
			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];		\
			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];		\
			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];		\
			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;
- (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
    OF_DIRECT;
- (void)printValue OF_DIRECT;
- (void)setValue: (int)value OF_DIRECT;
@end

OF_ASSUME_NONNULL_END










|
|
|
|
|
|
|









|
|
|
|
|
|
|










|
|
|
|
|
|
|
















|
|
|
|
|
|
|

















|
|
|
|
|


|
|
|
|
|






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];			\
			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];			\
			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];			\
			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];			\
			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;
- (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
    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
#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
{
	return [[self alloc] initWithName: name
	                              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
{
	self = [super initWithName: name];

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






|
|
|
|
|


|
|
|
|
|



|
|
|
|
|







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
{
	return [[self alloc] initWithName: name
				      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
{
	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
// 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 };

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












|
|
>
|
|
>
>
|
|







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

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

	player1.roll += player1.roll > 0
	    ? droll
	    : (player1.roll < 0
	              ? -droll
	              : (rnd(2) ? 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 (%@)",
					    a.name);
				else
					conoutf(
					    @"you got fragged by %@", a.name);
			}
		}
		showscores(true);
		addmsg(1, 2, SV_DIED, actor);







>
|
<
<
<
|
<
<












|
<







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 ? droll : (player1.roll < 0 ? -droll :



	    (rnd(2) ? droll : -droll));


	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 (%@)", 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
	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));

void throttle();

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








|
|







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

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
		    msgsizelookup(type));
	if (messages.count == 100) {
		conoutf(@"command flood protection (type %d)", type);
		return;
	}

	OFMutableData *msg = [OFMutableData dataWithItemSize: sizeof(int)
	                                            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++) {







|







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];
	[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
	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);
	if (fx < r && fy < r && fz < rz && d.state != CS_DEAD) {
		if (fx < fy)
			// push aside
			d.origin = OFAddVectors3D(d.origin,
			    OFMakeVector3D(
			        0, (dy < 0 ? r - fy : -(r - fy)), 0));
		else
			d.origin = OFAddVectors3D(d.origin,
			    OFMakeVector3D(
			        (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;
	}
}







|



|
<
|

|
<
|







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);
	if (fx < r && fy < r && fz < rz && d.state != CS_DEAD) {
		if (fx < fy)
			// push aside
			d.origin = OFAddVectors3D(d.origin, OFMakeVector3D(

			    0, (dy < 0 ? r - fy : -(r - fy)), 0));
		else
			d.origin = OFAddVectors3D(d.origin, OFMakeVector3D(

			    (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
		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_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!");
				disconnect(false, false);
				return;
			}
			if (getint(&p) == 1)
				conoutf(@"server is FULL, disconnecting..");
			break;
		}







|














|







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_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!");
				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
				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);
					else
						conoutf(@"%@ fragged %@",
						    a.name, d.name);
				}
			}
			OFVector3D loc = d_.origin;
			playsound(S_DIE1 + rnd(2), &loc);







|
|







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

		case SV_EDITT:
		case SV_EDITS:
		case SV_EDITD:
		case SV_EDITE: {
			int x = getint(&p);
			int y = getint(&p);
			int xs = getint(&p);







|
|
>







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;

		// 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
		case SV_PING:
			getint(&p);
			break;

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

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








|
<







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

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

		case SV_EXT: // so we can messages without breaking previous
		             // clients/servers, if necessary
		{

			for (int n = getint(&p); n; n--)
				getint(&p);
			break;
		}

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







|
|
<
>










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;

		// 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
void
alias(OFString *name, OFString *action)
{
	Alias *alias = Identifier.identifiers[name];

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







|
|







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];
	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
void
conoutf(OFConstantString *format, ...)
{
	va_list arguments;
	va_start(arguments, format);

	OFString *string = [[OFString alloc] initWithFormat: format
	                                          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) {
			refs[nd++] = conline.text;
			if (nd == ndraw)
				break;
		}

		i++;
	}







|




















|
|







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

	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) {
			refs[nd++] = conline.text;
			if (nd == ndraw)
				break;
		}

		i++;
	}
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];
	mapping.action = action;
	[keyMappings addObject: mapping];
})

COMMAND(bind, ARG_2STR, ^ (OFString *key, OFString *action) {
	for (KeyMapping *mapping in keyMappings) {
		if ([mapping.name caseInsensitiveCompare: key] ==







|







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];
	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
			case SDLK_RETURN:
				break;

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

				resetcomplete();
				break;

			case SDLK_UP:
				if (histpos)
					commandbuf =







|
|







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

				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
			default:
				resetcomplete();
			}
		} else {
			if (code == SDLK_RETURN) {
				if (commandbuf.length > 0) {
					if (vhistory == nil)
						vhistory =
						    [[OFMutableArray alloc]
						        init];

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







|
<
|







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 = [[OFMutableArray

						    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
#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 || \
	    (y) >= ssize - MINBORD)

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

enum {







|
|







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 ||	\
	    (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
// 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
// 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 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) || \
	    (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 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)







|
|
|




|
|
|



















|
|

|
|
|
|
|


|
|
|
|
|
|







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
// 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 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) ||	\
	    (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 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
		}
	});
}

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++) {                \
				struct sqr *s = S(sel->x + x, sel->y + y); \
				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
	if (!editmode && !allowedittoggle())
		return; // not in most multiplayer modes

	if (!(editmode = !editmode)) {
		settagareas();     // reset triggers to allow quick playtesting

		entinmap(player1); // find spawn closest to current floating pos

	} else {
		resettagareas(); // clear trigger areas to allow them to be
		                 // edited

		player1.health = 100;
		if (m_classicsp)
			// all monsters back at their spawns for editing
			[Monster resetAll];
		projreset();
	}
	Cube.sharedInstance.repeatsKeys = editmode;







|
|
|
|
|

|
|
|
|



















|
|

|
>

|
>
|
>

|
<
>







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++) {		   \
				struct sqr *s = S(sel->x + x, sel->y + y); \
				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)
		// do not allow dead players to edit to avoid state
		return;
	if (!editmode && !allowedittoggle())
		// not in most multiplayer modes
		return;
	if (!(editmode = !editmode)) {
		// reset triggers to allow quick playtesting
		settagareas();
		// find spawn closest to current floating pos
		entinmap(player1);
	} else {
		// clear trigger areas to allow them to be 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
noselection()
{
	if (!selset)
		conoutf(@"no selection");
	return !selset;
}

#define EDITSEL                             \
	if (noteditmode() || noselection()) \
		return;
#define EDITSELMP                                            \
	if (noteditmode() || noselection() || multiplayer()) \
		return;
#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();







|
|

|
|

|
|







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())	\
		return;
#define EDITSELMP						\
	if (noteditmode() || noselection() || multiplayer())	\
		return;
#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
	}
	makeundo();
	copybuf->x = sel.x;
	copybuf->y = sel.y;
	blockpaste(copybuf);
})


void
tofronttex() // maintain most recently used of the texture lists when applying
             // 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--)







>

|
<







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

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

	});
}

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

	if (type < 0 || type > 3)
		return;







|
>
|
|
|
|
|
|
|
|
|
|
|
|
>
|







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

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
}

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







<
|
|







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)) {
		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
		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
	        ? (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)));
	remipmore(sel, 0);
})

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

	int off = 0;







|
<
|
|
|







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 ? (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)));
	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

	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,
			        e.y),
			    (float)((e.attr1 + 7) - (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) /
				            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",
					    (float)(1 +
					        sin(lastmillis / 100.0 + e.x +
					            e.y) /
					            20),
					    lastmillis /
					        (e.attr2 ? 1.0f : 10.0f),
					    0, 1, 0, 10.0f);
					break;

				case 4:
					renderent(e, @"switch2", 3,
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ? 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
					                                : 30,
					    triggertime, 35.0f);
					break;
				}
			}
		}
	}
}








|
|
<
|
|









<
|
<
|
>










|
<
|
<
<
|
<
|





|
<
|
|




|
|
|
<
|







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, e.y), (float)((e.attr1 + 7) -

			    (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) / 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", (float)(1 +

					    sin(lastmillis / 100.0 + e.x +


					    e.y) / 20), lastmillis / (e.attr2 ?

					    1.0f : 10.0f), 0, 1, 0, 10.0f);
					break;

				case 4:
					renderent(e, @"switch2", 3,
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ?

					    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 : 30, triggertime,

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

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







<
|
>







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)
{
	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
			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 &&
				        cornertest(
				            0, x, y, -1, -1, &bx, &by, &bs) &&
				        fx1 - bx + fy1 - by <= bs) ||
				    (x == x2 && y == y1 &&
				        cornertest(
				            0, x, y, 1, -1, &bx, &by, &bs) &&
				        fx2 - bx >= fy1 - by) ||
				    (x == x1 && y == y2 &&
				        cornertest(
				            0, x, y, -1, 1, &bx, &by, &bs) &&
				        fx1 - bx <= fy2 - by) ||
				    (x == x2 && 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) /
				    16.0f;
				break;
			case CHF:
				ceil += (s->vdelta + S(x + 1, y)->vdelta +
				            S(x, y + 1)->vdelta +
				            S(x + 1, y + 1)->vdelta) /
				    16.0f;
			}

			if (ceil < hi)
				hi = ceil;
			if (floor > lo)
				lo = floor;
			if (floor < minfloor)







|
<
|
<
|
<
|
|
<
<
|
|
<
<
|
|







|
|
<



|
|
<







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 && cornertest(0, x, y,

				    -1, -1, &bx, &by, &bs) && fx1 - bx + fy1 -

				    by <= bs) || (x == x2 && y == y1 &&

				    cornertest(0, x, y, 1, -1, &bx, &by, &bs) &&
				    fx2 - bx >= fy1 - by) || (x == x1 && y ==


				    y2 && cornertest(0, x, y, -1, 1, &bx, &by,
				    &bs) && fx1 - bx <= fy2 - by) || (x == x2 &&


				    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) / 16.0f;

				break;
			case CHF:
				ceil += (s->vdelta + S(x + 1, y)->vdelta +
				    S(x, y + 1)->vdelta +
				    S(x + 1, y + 1)->vdelta) / 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
				// rise thru stair
				d.origin = OFAddVectors3D(
				    d.origin, OFMakeVector3D(0, 0, rise));
			else
				return false;
		} else
			// gravity
			d.origin = OFSubtractVectors3D(d.origin,
			    OFMakeVector3D(
			        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(







|
<
|







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, OFMakeVector3D(

			    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
	// 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.origin.z <
		        s->floor - (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 {







|
<
|
|
|







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.origin.z < s->floor -

		    (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
	pl.inWater = water;
}

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







|
<
<
|

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, i ? curtime / physicsrepeat


		    : 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
	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 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)                                      \
	{                                                                 \
		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







|
|
|
|


|
|
|
|
|
|
|
|
|
|
|
|


|
|







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 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)					  \
	{								  \
		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
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) {                             \
			addstrip(ogltex, firstindex, curvert - firstindex); \
			floorstrip = deltastrip = false;                    \
		}                                                           \
	}
void
finishstrips()
{
	stripend();
}








|
|
|

|
|







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) {				    \
			addstrip(ogltex, firstindex, curvert - firstindex); \
			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
		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;
	} else // continue strip
	{
		int lighterr = lighterror * 2;

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







>

|
<
|
|
|
|
<







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

		    abs(ol3g - l3->g) < lighterr &&
		    abs(ol4g - l4->g) < lighterr &&
		    abs(ol3b - l3->b) < lighterr &&
		    abs(ol4b - l4->b) < lighterr) || !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

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

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

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

	if ([lastsky isEqual: basename])
		return;

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







<
|
|
|
|
|







|







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

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

	basename = [basename stringByReplacingOccurrencesOfString: @"\\"
						       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

	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!");

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize);

	purgetextures();

	if (!(qsphere = gluNewQuadric()))
		fatal(@"glu sphere");







|







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!");

	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
		}

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

			SDL_FreeSurface(s);
			s = converted;
		} @finally {
			SDL_FreeFormat(format);







|
<







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: %@", 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
		    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))
		fatal(@"could not build mipmaps");

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

	SDL_FreeSurface(s);








|







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))
		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
	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: @"/"];
}))

int
lookuptexture(int tex, int *xs, int *ys)
{
	int frame = 0; // other frames?
	int tid = mapping[tex][frame];







|







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: @"/"];
}))

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

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) {
		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.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(
	        player1.origin.x, player1.origin.z, 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;








|
|



















|
<




















|
<
|
>







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) {
		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.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(player1.origin.x, player1.origin.z,

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

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

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







|

















|
|
|






|
|
|



|
|
|
|
|
|
|
|
|

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))
		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.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.0f];
	}

	[m renderWithLight: light
		     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
	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.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
	};

	int numrender = 0;

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








|
<
|
|
|
|
|
|







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

	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

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];
	va_end(arguments);
	draw_text(str, left, top, gl_num);
}

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







|







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];
	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
	    ((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)) /
	    16;
	float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
	                  noise(x, y - 1, seed) + noise(x, y + 1, seed)) /
	    8;
	float center = noise(x, y, seed) / 4;
	return corners + sides + center;
}

float
interpolate(float a, float b, float x)
{







<
|
|
<

|
<







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)) / 16;

	float sides = (noise(x - 1, y, seed) + noise(x + 1, y, seed) +
	    noise(x, y - 1, seed) + noise(x, y + 1, seed)) / 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
	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) *
			            50 +
			        25);
			if (s->vdelta > 128)
				s->vdelta = 0;
		}
	}
}







|
|
|
<
<





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

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

void
loadgamerest()
{
	if (demoplayback || !f)
		return;







|
|
|











|
















|







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

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

void
fixwrap(DynamicEntity *a, DynamicEntity *b)
{
	while (b.yaw - a.yaw > 180)
		a.yaw += 360;







|
|
|
|
|
|
|
|
|
|
|


|
|
|
|
|







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

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
				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)) {
			Player *d = [target copy];
			d.lastUpdate = playbacktime;

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

			[playerhistory addObject: d];







<
|
|







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)) {
			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
	mapend = lastsec + minremain * 60;
	interm = 0;
}

int nonlocalclients = 0;
int lastconnect = 0;



void
serverslice(int seconds,
    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;







>
>

|
<
<







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, unsigned int timeout)


{
	// 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
		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
			        ? @(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;







|
<
|







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

			    ? @(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
	if (nosound)
		return;

	stopsound();

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

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

static OFMutableData *samples;







|






|







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: @"/"];
		OFString *path = [OFString stringWithFormat:
		    @"packages/%@", name];
		OFIRI *IRI = [Cube.sharedInstance.gameDataIRI
		    IRIByAppendingPathComponent: path];

		if ((mod = Mix_LoadMUS(
		    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

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

#ifndef OF_WINDOWS
# define __cdecl
#endif

#ifdef __cplusplus







|
|
|
|







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

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







|
<










|
<







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 + 2;

							if (o[i]->type == CHF)
								// FIXME: needs
								// to somehow
								// take into
								// account
								// middle
								// vertices on
								// higher mips
								ch +=
								    o[i]->vdelta /
								    4 + 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
					// 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 ||
					    abs(o[i + 1]->g - o[0]->g) >
					        lighterr ||
					    abs(o[i + 1]->b - o[0]->b) >
					        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)
					                ->vdelta ||
					    o[0]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 2, y + 2, ws)
					                ->vdelta ||
					    o[0]->vdelta - o[3]->vdelta !=
					        o[3]->vdelta -
					            SWS(w, x, y + 2, ws)
					                ->vdelta ||
					    o[3]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 2, y + 1, ws)
					                ->vdelta ||
					    o[1]->vdelta - o[2]->vdelta !=
					        o[2]->vdelta -
					            SWS(w, x + 1, y + 2, ws)
					                ->vdelta)
						goto c;
				}
			}
			{
				// if any of the constituents is not perfect,
				// then this one isn't either
				for (int i = 0; i < 4; i++)







|

|

|









|
|
<

|
|
<

|
|
<

|
|
<

|
|
<







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 ||
					    abs(o[i + 1]->g - o[0]->g) >
					    lighterr ||
					    abs(o[i + 1]->b - o[0]->b) >
					    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)->vdelta ||

					    o[0]->vdelta - o[2]->vdelta !=
					    o[2]->vdelta -
					    SWS(w, x + 2, y + 2, ws)->vdelta ||

					    o[0]->vdelta - o[3]->vdelta !=
					    o[3]->vdelta -
					    SWS(w, x, y + 2, ws)->vdelta ||

					    o[3]->vdelta - o[2]->vdelta !=
					    o[2]->vdelta -
					    SWS(w, x + 2, y + 1, ws)->vdelta ||

					    o[1]->vdelta - o[2]->vdelta !=
					    o[2]->vdelta -
					    SWS(w, x + 1, y + 2, ws)->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
// 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
	voptimize();
	toptimize();
	if (mname.length == 0)
		mname = getclientmap();
	setnames(mname);
	backup(cgzname, bakname);
	gzFile f = gzopen([cgzname cStringWithEncoding: OFLocale.encoding],







|
|







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)
{
	// 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
				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;         \
		}                       \
	}
	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







|
|
|
|
|
|
|
|
|
|







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;		\
		}			\
	}
	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
	settagareas();
}

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



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







>
>

|
<







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)

{
	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
	if (hasoverbright) {
		l /= lightscale;
		stepl /= lightscale;

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

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

			int g = light.attr3 << PRECBITS;







|
<
|
<
<







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((255 - (light.attr2 + light.attr3 +

			    light.attr4) / 3) / 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
}

// 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 y = 0; y < a->ys; y++) {
			struct sqr *s = S(x + a->x, y + a->y);


#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) /      \
	    14; // median is 4/2/1 instead
			median(r);
			median(g);
			median(b);
		}


	remip(a, 0);
}

void
calclight()
{







|


>
>
|
<
|
|
|
<




>







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

	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;

			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
	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)) &&
		    !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







|
|



>













>







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)) &&
		    !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

	// 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 + csize < vx) // ADF
		{

			if (cy <= vy) // AD
			{

				if (cy + csize < vy) {

					h = ca(-(cx - vx), -(cy + csize - vy)) +
					    4;
					l = ca(-(cx + csize - vx), -(cy - vy)) +
					    4;
				} // A
				else {

					h = ma(-(cx + csize - vx),
					        -(cy + csize - vy)) +
					    4;
					l = ma(-(cx + csize - vx), -(cy - vy)) +
					    4;
				} // D

			} else {

				h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
				l = ca(cy - vy, -(cx - vx)) + 2;

			} // F
		} else { // BG
			if (cy <= vy) {
				if (cy + csize < vy) {

					h = ma(-(cy + csize - vy), cx - vx) + 6;
					l = ma(-(cy + csize - vy),
					        cx + csize - vx) +
					    6;
				} // B
				else
					return 0;
			} else {

				h = ma(cy - vy, -(cx + csize - vx)) + 2;
				l = ma(cy - vy, -(cx - vx)) + 2;
			} // G
		}

	} else // CEH
	{

		if (cy <= vy) // CE
		{

			if (cy + csize < vy) {

				h = ca(-(cy - vy), cx - vx) + 6;
				l = ca(-(cy + csize - vy), cx + csize - vx) + 6;
			} // C
			else {

				h = ma(cx - vx, cy - vy);
				l = ma(cx - vx, cy + csize - vy);
			} // E

		} else {

			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++) {
		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
}







|
<
>
|
<
>
|
<
>

>




<
|
>

|
<
|
|
<
>

>


>
|
|


>


|
<
<
|


>


<
|
>
|
<
>
|
<
>

>


<
|
>


<
>

>


<
>







|




<



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 + csize < vx) {

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

				} else {
					// D
					h = ma(-(cx + csize - vx),
					    -(cy + csize - vy)) + 4;

					l = ma(-(cx + csize - vx),
					    -(cy - vy)) + 4;

				}
			} else {
				// F
				h = ca(cy + csize - vy, -(cx + csize - vx)) + 2;
				l = ca(cy - vy, -(cx - vx)) + 2;
			}
		} 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) + 6;


				} else
					return 0;
			} else {
				// G
				h = ma(cy - vy, -(cx + csize - vx)) + 2;
				l = ma(cy - vy, -(cx - vx)) + 2;

			}
		}
	} else {

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

			} else {
				// E
				h = ma(cx - vx, cy - vy);
				l = ma(cx - vx, cy + csize - vy);

			}
		} else {
			// H
			h = ca(cx + csize - vx, cy - vy);
			l = ca(cx - vx, cy + csize - vy);

		}
	}
	// 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++)
		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
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

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

	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;







<
|
|
>











|







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;

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

	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
	// 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 && \
				    yy < ry)
#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 &&







|
|
|
|
|
|
|
|
|

|
|
|







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 &&	\
				    yy < ry)
#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 &&