Cube  Check-in [753ff34122]

Overview
Comment:More style cleanup
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 753ff34122c906fdba43b8c382ac52cc9f0ac3ce585ec9f4db6b1dc1f21ef86a
User & Date: js on 2025-03-08 03:05:16
Other Links: manifest | tags
Context
2025-03-08
03:26
Use ObjFW functions for memory management check-in: 71ebb79f8f user: js tags: trunk
03:05
More style cleanup check-in: 753ff34122 user: js tags: trunk
02:38
Improve clang-format check-in: 6f5dd50626 user: js tags: trunk
Changes

Modified src/.clang-format from [2a796d79cc] to [0d630f9489].

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
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
AlwaysBreakAfterReturnType: AllDefinitions
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: Left
AlignOperands: DontAlign


ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCPropertyAttributeOrder: [
    class, direct,
    readonly, readwrite,
    nullable, nonnull, null_resettable, null_unspecified,
    assign, retain, strong, copy, weak, unsafe_unretained,
    atomic, nonatomic,
    getter, setter
]
SpaceBeforeInheritanceColon: false
QualifierAlignment: Left

#RemoveEmptyLinesInUnwrappedLines: true
RemoveSemicolon: true
CompactNamespaces: true
SortIncludes: CaseSensitive
IndentPPDirectives: AfterHash
PPIndentWidth: 1







>
>













>






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
  AfterExternBlock: false
  BeforeCatch: false
  BeforeElse: false
AlwaysBreakAfterReturnType: AllDefinitions
AlignAfterOpenBracket: DontAlign
AlignEscapedNewlines: Left
AlignOperands: DontAlign
Cpp11BracedListStyle: false
SpaceBeforeCpp11BracedList: true
ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
ObjCPropertyAttributeOrder: [
    class, direct,
    readonly, readwrite,
    nullable, nonnull, null_resettable, null_unspecified,
    assign, retain, strong, copy, weak, unsafe_unretained,
    atomic, nonatomic,
    getter, setter
]
SpaceBeforeInheritanceColon: false
QualifierAlignment: Left
MaxEmptyLinesToKeep: 1
#RemoveEmptyLinesInUnwrappedLines: true
RemoveSemicolon: true
CompactNamespaces: true
SortIncludes: CaseSensitive
IndentPPDirectives: AfterHash
PPIndentWidth: 1

Modified src/Cube.mm from [00b4da043c] to [b4e9c293d7].

25
26
27
28
29
30
31
32
33
34

35
36
37

38
39
40

41
42
43
44
45
46
47
	OFString *__autoreleasing master, *__autoreleasing passwd;

	processInitQueue();

#define log(s) conoutf(@"init: %@", s)
	log(@"sdl");

	const OFOptionsParserOption options[] = {
	    {'d', @"dedicated", 0, &dedicated, NULL},
	    {'t', @"window", 0, &windowed, NULL},

	    {'w', @"width", 1, NULL, NULL}, {'h', @"height", 1, NULL, NULL},
	    {'u', @"upload-rate", 1, NULL, NULL},
	    {'n', @"server-desc", 1, NULL, &sdesc}, {'i', @"ip", 1, NULL, &ip},

	    {'m', @"master", 1, NULL, &master},
	    {'p', @"password", 1, NULL, &passwd},
	    {'c', @"max-clients", 1, NULL, NULL}, {'\0', nil, 0, NULL, NULL}};

	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions:options];
	OFUnichar option;
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 'w':
			_width = (int)optionsParser.argument.longLongValue;







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







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
	OFString *__autoreleasing master, *__autoreleasing passwd;

	processInitQueue();

#define log(s) conoutf(@"init: %@", s)
	log(@"sdl");

	const OFOptionsParserOption options[] = { { 'd', @"dedicated", 0,
		                                      &dedicated, NULL },
		{ 't', @"window", 0, &windowed, NULL },
		{ 'w', @"width", 1, NULL, NULL },
		{ 'h', @"height", 1, NULL, NULL },
		{ 'u', @"upload-rate", 1, NULL, NULL },
		{ 'n', @"server-desc", 1, NULL, &sdesc },
		{ 'i', @"ip", 1, NULL, &ip },
		{ 'm', @"master", 1, NULL, &master },
		{ 'p', @"password", 1, NULL, &passwd },
		{ 'c', @"max-clients", 1, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL } };
	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions:options];
	OFUnichar option;
	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case 'w':
			_width = (int)optionsParser.argument.longLongValue;

Modified src/client.mm from [89ff04e5c2] to [cdd53f348e].

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
void
connects(OFString *servername)
{
	disconnect(1); // reset state
	addserver(servername);

	conoutf(@"attempting to connect to %@", servername);
	ENetAddress address = {ENET_HOST_ANY, CUBE_SERVER_PORT};
	@autoreleasepool {
		if (enet_address_set_host(&address, servername.UTF8String) <
		    0) {
			conoutf(@"could not resolve server %@", servername);
			return;
		}
	}







|







86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
void
connects(OFString *servername)
{
	disconnect(1); // reset state
	addserver(servername);

	conoutf(@"attempting to connect to %@", servername);
	ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT };
	@autoreleasepool {
		if (enet_address_set_host(&address, servername.UTF8String) <
		    0) {
			conoutf(@"could not resolve server %@", servername);
			return;
		}
	}
116
117
118
119
120
121
122
123

124
125
126
127
128

129
130

131
132
133
134
135
136
137
disconnect(int onlyclean, int async)
{
	if (clienthost) {
		if (!connecting && !disconnecting) {
			enet_peer_disconnect(clienthost->peers);
			enet_host_flush(clienthost);
			disconnecting = lastmillis;
		};

		if (clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) {
			if (async)
				return;
			enet_peer_reset(clienthost->peers);
		};

		enet_host_destroy(clienthost);
	};


	if (clienthost && !connecting)
		conoutf(@"disconnected");
	clienthost = NULL;
	connecting = 0;
	connattempts = 0;
	disconnecting = 0;







<
>




<
>

<
>







116
117
118
119
120
121
122

123
124
125
126
127

128
129

130
131
132
133
134
135
136
137
disconnect(int onlyclean, int async)
{
	if (clienthost) {
		if (!connecting && !disconnecting) {
			enet_peer_disconnect(clienthost->peers);
			enet_host_flush(clienthost);
			disconnecting = lastmillis;

		}
		if (clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) {
			if (async)
				return;
			enet_peer_reset(clienthost->peers);

		}
		enet_host_destroy(clienthost);

	}

	if (clienthost && !connecting)
		conoutf(@"disconnected");
	clienthost = NULL;
	connecting = 0;
	connattempts = 0;
	disconnecting = 0;
381
382
383
384
385
386
387
388
389


390
391
392
393
394
395
396
		conoutf(@"attempting to connect...");
		connecting = lastmillis;
		++connattempts;
		if (connattempts > 3) {
			conoutf(@"could not connect to server");
			disconnect();
			return;
		};
	};


	while (
	    clienthost != NULL && enet_host_service(clienthost, &event, 0) > 0)
		switch (event.type) {
		case ENET_EVENT_TYPE_CONNECT:
			conoutf(@"connected to server");
			connecting = 0;
			throttle();







<
<
>
>







381
382
383
384
385
386
387


388
389
390
391
392
393
394
395
396
		conoutf(@"attempting to connect...");
		connecting = lastmillis;
		++connattempts;
		if (connattempts > 3) {
			conoutf(@"could not connect to server");
			disconnect();
			return;


		}
	}
	while (
	    clienthost != NULL && enet_host_service(clienthost, &event, 0) > 0)
		switch (event.type) {
		case ENET_EVENT_TYPE_CONNECT:
			conoutf(@"connected to server");
			connecting = 0;
			throttle();

Modified src/clientextras.mm from [0e321e84f3] to [ccc9d61fe8].

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

14
15
16
17
18
19
20
// clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :)

#include "cube.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(dynent *d, bool team, OFString *mdlname, bool hellpig, float scale)
{
	int n = 3;
	float speed = 100.0f;
	float mz = d->o.z - d->eyeheight + 1.55f * scale;










|
|
|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :)

#include "cube.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(dynent *d, bool team, OFString *mdlname, bool hellpig, float scale)
{
	int n = 3;
	float speed = 100.0f;
	float mz = d->o.z - d->eyeheight + 1.55f * scale;
33
34
35
36
37
38
39
40
41


42
43
44
45
46
47
48
		if (t < 0 || t > 20000)
			return;
		if (t > (r - 1) * 100) {
			n += 4;
			if (t > (r + 10) * 100) {
				t -= (r + 10) * 100;
				mz -= t * t / 10000000000.0f * t;
			};
		};


		if (mz < -1000)
			return;
		// mdl = (((int)d>>6)&1)+1;
		// mz = d->o.z-d->eyeheight+0.2f;
		// scale = 1.2f;
	} else if (d->state == CS_EDITING) {
		n = 16;







<
<
>
>







34
35
36
37
38
39
40


41
42
43
44
45
46
47
48
49
		if (t < 0 || t > 20000)
			return;
		if (t > (r - 1) * 100) {
			n += 4;
			if (t > (r + 10) * 100) {
				t -= (r + 10) * 100;
				mz -= t * t / 10000000000.0f * t;


			}
		}
		if (mz < -1000)
			return;
		// mdl = (((int)d>>6)&1)+1;
		// mz = d->o.z-d->eyeheight+0.2f;
		// scale = 1.2f;
	} else if (d->state == CS_EDITING) {
		n = 16;

Modified src/clientgame.mm from [6689f47248] to [1adcd0a4ff].

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
		if (m_noitemsrail) {
			d->health = 1;
			d->ammo[GUN_RIFLE] = 100;
		} else {
			if (gamemode == 12) {
				d->gunselect = GUN_FIST;
				return;
			}; // eihrul's secret "instafist" mode
			d->health = 256;
			if (m_tarena) {
				int gun1 = rnd(4) + 1;
				baseammo(d->gunselect = gun1);
				for (;;) {
					int gun2 = rnd(4) + 1;
					if (gun1 != gun2) {
						baseammo(gun2);
						break;
					};
				};


			} else if (m_arena) // insta arena
			{
				d->ammo[GUN_RIFLE] = 100;
			} else // efficiency
			{
				loopi(4) baseammo(i + 1);
				d->gunselect = GUN_CG;
			};

			d->ammo[GUN_CG] /= 2;
		};

	} else {
		d->ammo[GUN_SG] = 5;
	};

}

dynent *
newdynent() // create a new blank player or monster
{
	dynent *d = (dynent *)malloc(sizeof(dynent));
	d->o.x = 0;







|









<
<
>
>







<
>

<
>


<
>







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
		if (m_noitemsrail) {
			d->health = 1;
			d->ammo[GUN_RIFLE] = 100;
		} else {
			if (gamemode == 12) {
				d->gunselect = GUN_FIST;
				return;
			} // eihrul's secret "instafist" mode
			d->health = 256;
			if (m_tarena) {
				int gun1 = rnd(4) + 1;
				baseammo(d->gunselect = gun1);
				for (;;) {
					int gun2 = rnd(4) + 1;
					if (gun1 != gun2) {
						baseammo(gun2);
						break;


					}
				}
			} else if (m_arena) // insta arena
			{
				d->ammo[GUN_RIFLE] = 100;
			} else // efficiency
			{
				loopi(4) baseammo(i + 1);
				d->gunselect = GUN_CG;

			}
			d->ammo[GUN_CG] /= 2;

		}
	} else {
		d->ammo[GUN_SG] = 5;

	}
}

dynent *
newdynent() // create a new blank player or monster
{
	dynent *d = (dynent *)malloc(sizeof(dynent));
	d->o.x = 0;
140
141
142
143
144
145
146
147

148
149
150
151
152
153
154
	if (d->state != CS_DEAD) {
		alive++;
		if (lastteam && strcmp(lastteam, d->team))
			oneteam = false;
		lastteam = d->team;
	} else {
		dead++;
	};

}

int arenarespawnwait = 0;
int arenadetectwait = 0;

void
arenarespawn()







<
>







140
141
142
143
144
145
146

147
148
149
150
151
152
153
154
	if (d->state != CS_DEAD) {
		alive++;
		if (lastteam && strcmp(lastteam, d->team))
			oneteam = false;
		lastteam = d->team;
	} else {
		dead++;

	}
}

int arenarespawnwait = 0;
int arenadetectwait = 0;

void
arenarespawn()
174
175
176
177
178
179
180
181
182


183
184
185
186
187
188
189
				conoutf(
				    @"team %s is last man standing", lastteam);
			else
				conoutf(@"everyone died!");
			arenarespawnwait = lastmillis + 5000;
			arenadetectwait = lastmillis + 10000;
			player1->roll = 0;
		};
	};


}

void
zapdynent(dynent *&d)
{
	if (d)
		free(d);







<
<
>
>







174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
189
				conoutf(
				    @"team %s is last man standing", lastteam);
			else
				conoutf(@"everyone died!");
			arenarespawnwait = lastmillis + 5000;
			arenadetectwait = lastmillis + 10000;
			player1->roll = 0;


		}
	}
}

void
zapdynent(dynent *&d)
{
	if (d)
		free(d);
197
198
199
200
201
202
203
204

205
206
207
208
209
210
211
{
	loopv(players) if (players[i])
	{
		const int lagtime = lastmillis - players[i]->lastupdate;
		if (lagtime > 1000 && players[i]->state == CS_ALIVE) {
			players[i]->state = CS_LAGGED;
			continue;
		};

		if (lagtime && players[i]->state != CS_DEAD &&
		    (!demoplayback || i != democlientnum))
			moveplayer(
			    players[i], 2, false); // use physics to extrapolate
			                           // player position
	}
}







<
>







197
198
199
200
201
202
203

204
205
206
207
208
209
210
211
{
	loopv(players) if (players[i])
	{
		const int lagtime = lastmillis - players[i]->lastupdate;
		if (lagtime > 1000 && players[i]->state == CS_ALIVE) {
			players[i]->state = CS_LAGGED;
			continue;

		}
		if (lagtime && players[i]->state != CS_DEAD &&
		    (!demoplayback || i != democlientnum))
			moveplayer(
			    players[i], 2, false); // use physics to extrapolate
			                           // player position
	}
}
269
270
271
272
273
274
275
276

277
278
279
280
281
282
283
					moveplayer(player1, 10, false);
				} else if (!m_arena && !m_sp &&
				    lastmillis - player1->lastaction > 10000)
					respawn();
			} else if (!intermission) {
				moveplayer(player1, 20, true);
				checkitems();
			};

			c2sinfo(player1); // do this last, to reduce the
			                  // effective frame lag
		}
	}
	lastmillis = millis;
}








<
>







269
270
271
272
273
274
275

276
277
278
279
280
281
282
283
					moveplayer(player1, 10, false);
				} else if (!m_arena && !m_sp &&
				    lastmillis - player1->lastaction > 10000)
					respawn();
			} else if (!intermission) {
				moveplayer(player1, 20, true);
				checkitems();

			}
			c2sinfo(player1); // do this last, to reduce the
			                  // effective frame lag
		}
	}
	lastmillis = millis;
}

315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
		d->o.z = ents[spawncycle].z;
		d->yaw = ents[spawncycle].attr1;
		d->pitch = 0;
		d->roll = 0;
	} else {
		d->o.x = d->o.y = (float)ssize / 2;
		d->o.z = 4;
	};

	entinmap(d);
	spawnstate(d);
	d->state = CS_ALIVE;
}

// movement input code








<
>







315
316
317
318
319
320
321

322
323
324
325
326
327
328
329
		d->o.z = ents[spawncycle].z;
		d->yaw = ents[spawncycle].attr1;
		d->pitch = 0;
		d->roll = 0;
	} else {
		d->o.x = d->o.y = (float)ssize / 2;
		d->o.z = 4;

	}
	entinmap(d);
	spawnstate(d);
	d->state = CS_ALIVE;
}

// movement input code

Modified src/clients2c.mm from [15295b6bce] to [e5e63c92dd].

42
43
44
45
46
47
48
49

50
51
52
53
54

55
56
57
58
59
60
61
	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)
			d->o.y += dy < 0 ? r - fy : -(r - fy); // push aside
		else
			d->o.x += dx < 0 ? r - fx : -(r - fx);
	};

	int lagtime = lastmillis - d->lastupdate;
	if (lagtime) {
		d->plag = (d->plag * 5 + lagtime) / 6;
		d->lastupdate = lastmillis;
	};

}

void
localservertoclient(
    uchar *buf, int len) // processes any updates from the server
{
	if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)







<
>




<
>







42
43
44
45
46
47
48

49
50
51
52
53

54
55
56
57
58
59
60
61
	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)
			d->o.y += dy < 0 ? r - fy : -(r - fy); // push aside
		else
			d->o.x += dx < 0 ? r - fx : -(r - fx);

	}
	int lagtime = lastmillis - d->lastupdate;
	if (lagtime) {
		d->plag = (d->plag * 5 + lagtime) / 6;
		d->lastupdate = lastmillis;

	}
}

void
localservertoclient(
    uchar *buf, int len) // processes any updates from the server
{
	if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
		case SV_EDITD:
		case SV_EDITE: {
			int x = getint(p);
			int y = getint(p);
			int xs = getint(p);
			int ys = getint(p);
			int v = getint(p);
			block b = {x, y, xs, ys};
			switch (type) {
			case SV_EDITH:
				editheightxy(v != 0, getint(p), b);
				break;
			case SV_EDITT:
				edittexxy(v, getint(p), b);
				break;







|







297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
		case SV_EDITD:
		case SV_EDITE: {
			int x = getint(p);
			int y = getint(p);
			int xs = getint(p);
			int ys = getint(p);
			int v = getint(p);
			block b = { x, y, xs, ys };
			switch (type) {
			case SV_EDITH:
				editheightxy(v != 0, getint(p), b);
				break;
			case SV_EDITT:
				edittexxy(v, getint(p), b);
				break;

Modified src/editing.mm from [f5920b6aeb] to [46292a408f].

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
// the edge of the map

block sel;

OF_CONSTRUCTOR()
{
	enqueueInit(^{
		sel = (block){
		    variable(@"selx", 0, 0, 4096, &sel.x, NULL, false),
		    variable(@"sely", 0, 0, 4096, &sel.y, NULL, false),
		    variable(@"selxs", 0, 0, 4096, &sel.xs, NULL, false),
		    variable(@"selys", 0, 0, 4096, &sel.ys, NULL, false),
		};
	});
}

int selh = 0;
bool selset = false;

#define loopselxy(b)                                      \
	{                                                 \
		makeundo();                               \
		loop(x, sel.xs) loop(y, sel.ys)           \
		{                                         \
			sqr *s = S(sel.x + x, sel.y + y); \
			b;                                \
		}                                         \
		remip(sel);                               \
	}

int cx, cy, ch;

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

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

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








|
|
|
|
|




















|







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
// the edge of the map

block sel;

OF_CONSTRUCTOR()
{
	enqueueInit(^{
		sel = (block) {
			variable(@"selx", 0, 0, 4096, &sel.x, NULL, false),
			variable(@"sely", 0, 0, 4096, &sel.y, NULL, false),
			variable(@"selxs", 0, 0, 4096, &sel.xs, NULL, false),
			variable(@"selys", 0, 0, 4096, &sel.ys, NULL, false),
		};
	});
}

int selh = 0;
bool selset = false;

#define loopselxy(b)                                      \
	{                                                 \
		makeundo();                               \
		loop(x, sel.xs) loop(y, sel.ys)           \
		{                                         \
			sqr *s = S(sel.x + x, sel.y + y); \
			b;                                \
		}                                         \
		remip(sel);                               \
	}

int cx, cy, ch;

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

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

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

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#define EDITMP                              \
	if (noteditmode() || multiplayer()) \
		return;

void
selectpos(int x, int y, int xs, int ys)
{
	block s = {x, y, xs, ys};
	sel = s;
	selh = 0;
	correctsel();
}

void
makesel()
{
	block s = {min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1,
	    abs(lasty - cy) + 1};
	sel = s;
	selh = max(lasth, ch);
	correctsel();
	if (selset)
		rtex = *S(sel.x, sel.y);
}








|








|
|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#define EDITMP                              \
	if (noteditmode() || multiplayer()) \
		return;

void
selectpos(int x, int y, int xs, int ys)
{
	block s = { x, y, xs, ys };
	sel = s;
	selh = 0;
	correctsel();
}

void
makesel()
{
	block s = { min(lastx, cx), min(lasty, cy), abs(lastx - cx) + 1,
		abs(lasty - cy) + 1 };
	sel = s;
	selh = max(lasth, ch);
	correctsel();
	if (selset)
		rtex = *S(sel.x, sel.y);
}

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
206
207
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
	cx = (int)x;
	cy = (int)y;

	if (OUTBORD(cx, cy))
		return;
	sqr *s = S(cx, cy);


	if (fabs(sheight(s, s, z) - z) > 1) // selected wall
	{
		x += x > player1->o.x ? 0.5f : -0.5f; // find right wall cube
		y += y > player1->o.y ? 0.5f : -0.5f;

		cx = (int)x;
		cy = (int)y;

		if (OUTBORD(cx, cy))
			return;
	};


	if (dragging)
		makesel();

	const int GRIDSIZE = 5;
	const float GRIDW = 0.5f;
	const float GRID8 = 2.0f;
	const float GRIDS = 2.0f;
	const int GRIDM = 0x7;

	// render editing grid

	for (int ix = cx - GRIDSIZE; ix <= cx + GRIDSIZE; ix++)
		for (int iy = cy - GRIDSIZE; iy <= cy + GRIDSIZE; iy++) {
			if (OUTBORD(ix, iy))
				continue;
			sqr *s = S(ix, iy);
			if (SOLID(s))
				continue;
			float h1 = sheight(s, s, z);
			float h2 = sheight(s, SWS(s, 1, 0, ssize), z);
			float h3 = sheight(s, SWS(s, 1, 1, ssize), z);
			float h4 = sheight(s, SWS(s, 0, 1, ssize), z);
			if (s->tag)
				linestyle(GRIDW, 0xFF, 0x40, 0x40);
			else if (s->type == FHF || s->type == CHF)
				linestyle(GRIDW, 0x80, 0xFF, 0x80);
			else
				linestyle(GRIDW, 0x80, 0x80, 0x80);
			block b = {ix, iy, 1, 1};
			box(b, h1, h2, h3, h4);
			linestyle(GRID8, 0x40, 0x40, 0xFF);
			if (!(ix & GRIDM))
				line(ix, iy, h1, ix, iy + 1, h4);
			if (!(ix + 1 & GRIDM))
				line(ix + 1, iy, h2, ix + 1, iy + 1, h3);
			if (!(iy & GRIDM))
				line(ix, iy, h1, ix + 1, iy, h2);
			if (!(iy + 1 & GRIDM))
				line(ix, iy + 1, h4, ix + 1, iy + 1, h3);
		};



	if (!SOLID(s)) {
		float ih = sheight(s, s, z);
		linestyle(GRIDS, 0xFF, 0xFF, 0xFF);
		block b = {cx, cy, 1, 1};
		box(b, ih, sheight(s, SWS(s, 1, 0, ssize), z),
		    sheight(s, SWS(s, 1, 1, ssize), z),
		    sheight(s, SWS(s, 0, 1, ssize), z));
		linestyle(GRIDS, 0xFF, 0x00, 0x00);
		dot(cx, cy, ih);
		ch = (int)ih;
	};


	if (selset) {
		linestyle(GRIDS, 0xFF, 0x40, 0x40);
		box(sel, (float)selh, (float)selh, (float)selh, (float)selh);
	};

}

vector<block *> undos;    // unlimited undo
VARP(undomegs, 0, 1, 10); // bounded by n megs

void
pruneundos(int maxremain) // bound memory







>
|
<








<
>












|
















|










<
>
>




|






<
>




<
>







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
206
207
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
	cx = (int)x;
	cy = (int)y;

	if (OUTBORD(cx, cy))
		return;
	sqr *s = S(cx, cy);

	// selected wall
	if (fabs(sheight(s, s, z) - z) > 1) {

		x += x > player1->o.x ? 0.5f : -0.5f; // find right wall cube
		y += y > player1->o.y ? 0.5f : -0.5f;

		cx = (int)x;
		cy = (int)y;

		if (OUTBORD(cx, cy))
			return;

	}

	if (dragging)
		makesel();

	const int GRIDSIZE = 5;
	const float GRIDW = 0.5f;
	const float GRID8 = 2.0f;
	const float GRIDS = 2.0f;
	const int GRIDM = 0x7;

	// render editing grid

	for (int ix = cx - GRIDSIZE; ix <= cx + GRIDSIZE; ix++) {
		for (int iy = cy - GRIDSIZE; iy <= cy + GRIDSIZE; iy++) {
			if (OUTBORD(ix, iy))
				continue;
			sqr *s = S(ix, iy);
			if (SOLID(s))
				continue;
			float h1 = sheight(s, s, z);
			float h2 = sheight(s, SWS(s, 1, 0, ssize), z);
			float h3 = sheight(s, SWS(s, 1, 1, ssize), z);
			float h4 = sheight(s, SWS(s, 0, 1, ssize), z);
			if (s->tag)
				linestyle(GRIDW, 0xFF, 0x40, 0x40);
			else if (s->type == FHF || s->type == CHF)
				linestyle(GRIDW, 0x80, 0xFF, 0x80);
			else
				linestyle(GRIDW, 0x80, 0x80, 0x80);
			block b = { ix, iy, 1, 1 };
			box(b, h1, h2, h3, h4);
			linestyle(GRID8, 0x40, 0x40, 0xFF);
			if (!(ix & GRIDM))
				line(ix, iy, h1, ix, iy + 1, h4);
			if (!(ix + 1 & GRIDM))
				line(ix + 1, iy, h2, ix + 1, iy + 1, h3);
			if (!(iy & GRIDM))
				line(ix, iy, h1, ix + 1, iy, h2);
			if (!(iy + 1 & GRIDM))
				line(ix, iy + 1, h4, ix + 1, iy + 1, h3);

		}
	}

	if (!SOLID(s)) {
		float ih = sheight(s, s, z);
		linestyle(GRIDS, 0xFF, 0xFF, 0xFF);
		block b = { cx, cy, 1, 1 };
		box(b, ih, sheight(s, SWS(s, 1, 0, ssize), z),
		    sheight(s, SWS(s, 1, 1, ssize), z),
		    sheight(s, SWS(s, 0, 1, ssize), z));
		linestyle(GRIDS, 0xFF, 0x00, 0x00);
		dot(cx, cy, ih);
		ch = (int)ih;

	}

	if (selset) {
		linestyle(GRIDS, 0xFF, 0x40, 0x40);
		box(sel, (float)selh, (float)selh, (float)selh, (float)selh);

	}
}

vector<block *> undos;    // unlimited undo
VARP(undomegs, 0, 1, 10); // bounded by n megs

void
pruneundos(int maxremain) // bound memory
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
		if (c >= 0) {
			uchar *p = hdr.texlists[i];
			int t = p[c];
			for (int a = c - 1; a >= 0; a--)
				p[a + 1] = p[a];
			p[0] = t;
			curedittex[i] = -1;
		};

	}
}

void
editdrag(bool isdown)
{
	if (dragging = isdown) {
		lastx = cx;
		lasty = cy;
		lasth = ch;
		selset = false;
		tofronttex();
	};

	makesel();
}

// the core editing function. all the *xy functions perform the core operations
// and are also called directly from the network, the function below it is
// strictly triggered locally. They all have very similar structure.








<
>












<
>







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
		if (c >= 0) {
			uchar *p = hdr.texlists[i];
			int t = p[c];
			for (int a = c - 1; a >= 0; a--)
				p[a + 1] = p[a];
			p[0] = t;
			curedittex[i] = -1;

		}
	}
}

void
editdrag(bool isdown)
{
	if (dragging = isdown) {
		lastx = cx;
		lasty = cy;
		lasth = ch;
		selset = false;
		tofronttex();

	}
	makesel();
}

// the core editing function. all the *xy functions perform the core operations
// and are also called directly from the network, the function below it is
// strictly triggered locally. They all have very similar structure.

386
387
388
389
390
391
392
393

394
395
396
397
398
399
400
{
	EDITSEL;
	if (type < 0 || type > 3)
		return;
	if (type != lasttype) {
		tofronttex();
		lasttype = type;
	};

	int atype = type == 3 ? 1 : type;
	int i = curedittex[atype];
	i = i < 0 ? 0 : i + dir;
	curedittex[atype] = i = min(max(i, 0), 255);
	int t = lasttex = hdr.texlists[atype][i];
	edittexxy(type, t, sel);
	addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t);







<
>







387
388
389
390
391
392
393

394
395
396
397
398
399
400
401
{
	EDITSEL;
	if (type < 0 || type > 3)
		return;
	if (type != lasttype) {
		tofronttex();
		lasttype = type;

	}
	int atype = type == 3 ? 1 : type;
	int i = curedittex[atype];
	i = i < 0 ? 0 : i + dir;
	curedittex[atype] = i = min(max(i, 0), 255);
	int t = lasttex = hdr.texlists[atype][i];
	edittexxy(type, t, sel);
	addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t);
420
421
422
423
424
425
426
427
428

429
430
431
432
433
434
435
436
			if (s->ctex == rtex.ctex)
				s->ctex = lasttex;
			break;
		case 3:
			if (s->utex == rtex.utex)
				s->utex = lasttex;
			break;
		};
	}

	block b = {0, 0, ssize, ssize};
	remip(b);
}

void
edittypexy(int type, block &sel)
{
	loopselxy(s->type = type);







<
|
>
|







421
422
423
424
425
426
427

428
429
430
431
432
433
434
435
436
437
			if (s->ctex == rtex.ctex)
				s->ctex = lasttex;
			break;
		case 3:
			if (s->utex == rtex.utex)
				s->utex = lasttex;
			break;

		}
	}
	block b = { 0, 0, ssize, ssize };
	remip(b);
}

void
edittypexy(int type, block &sel)
{
	loopselxy(s->type = type);
522
523
524
525
526
527
528
529

530
531
532
533
534
535
536

void
archvertex(int span, int vert, int delta)
{
	if (!archvinit) {
		archvinit = true;
		loop(s, MAXARCHVERT) loop(v, MAXARCHVERT) archverts[s][v] = 0;
	};

	if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0)
		return;
	archverts[span][vert] = delta;
}

void
arch(int sidedelta, int _a)







<
>







523
524
525
526
527
528
529

530
531
532
533
534
535
536
537

void
archvertex(int span, int vert, int delta)
{
	if (!archvinit) {
		archvinit = true;
		loop(s, MAXARCHVERT) loop(v, MAXARCHVERT) archverts[s][v] = 0;

	}
	if (span >= MAXARCHVERT || vert >= MAXARCHVERT || span < 0 || vert < 0)
		return;
	archverts[span][vert] = delta;
}

void
arch(int sidedelta, int _a)
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
}

VARF(
    fullbright, 0, 0, 1, if (fullbright) {
	    if (noteditmode())
		    return;
	    loopi(mipsize) world[i].r = world[i].g = world[i].b = 176;
    };);

void
edittag(int tag)
{
	EDITSELMP;
	loopselxy(s->tag = tag);
}







|







584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
}

VARF(
    fullbright, 0, 0, 1, if (fullbright) {
	    if (noteditmode())
		    return;
	    loopi(mipsize) world[i].r = world[i].g = world[i].b = 176;
    });

void
edittag(int tag)
{
	EDITSELMP;
	loopselxy(s->tag = tag);
}

Modified src/entities.mm from [6e39a8fac1] to [f745b5a428].

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
// entities.cpp: map entity related functions (pickup etc.)

#include "cube.h"

#import "MapModelInfo.h"

vector<entity> ents;

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

int triggertime = 0;

void
renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0,
    int numf = 1, int basetime = 0, float speed = 10.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
// entities.cpp: map entity related functions (pickup etc.)

#include "cube.h"

#import "MapModelInfo.h"

vector<entity> ents;

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

int triggertime = 0;

void
renderent(entity &e, OFString *mdlname, float z, float yaw, int frame = 0,
    int numf = 1, int basetime = 0, float speed = 10.0f)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
				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);
			} else
				switch (e.attr2) {
				case 1:
				case 3:
					continue;

				case 2:
				case 0:







|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
				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);
			} else {
				switch (e.attr2) {
				case 1:
				case 3:
					continue;

				case 2:
				case 0:
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
137
138
139
140
141
142
143
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ? 30
					                                 : 0,
					    (e.spawned || !triggertime) ? 1
					                                : 30,
					    triggertime, 35.0f);
					break;
				};
		};



	}
}

struct itemstat {
	int add, max, sound;
} itemstats[] = {
    10,
    50,
    S_ITEMAMMO,
    20,
    100,
    S_ITEMAMMO,
    5,
    25,
    S_ITEMAMMO,
    5,
    25,
    S_ITEMAMMO,
    25,
    100,
    S_ITEMHEALTH,
    50,
    200,
    S_ITEMHEALTH,
    100,
    100,
    S_ITEMARMOUR,
    150,
    150,
    S_ITEMARMOUR,
    20000,
    30000,
    S_ITEMPUP,
};

void
baseammo(int gun)
{
	player1->ammo[gun] = itemstats[gun - 1].add * 2;
}







<
<
>
>
>






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







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
					    (float)e.attr3 * 90,
					    (!e.spawned && !triggertime) ? 30
					                                 : 0,
					    (e.spawned || !triggertime) ? 1
					                                : 30,
					    triggertime, 35.0f);
					break;


				}
			}
		}
	}
}

struct itemstat {
	int add, max, sound;
} itemstats[] = {


	{ 10, 50, S_ITEMAMMO },


	{ 20, 100, S_ITEMAMMO },


	{ 5, 25, S_ITEMAMMO },


	{ 5, 25, S_ITEMAMMO },


	{ 25, 100, S_ITEMHEALTH },


	{ 50, 200, S_ITEMHEALTH },


	{ 100, 100, S_ITEMARMOUR },


	{ 150, 150, S_ITEMARMOUR },


	{ 20000, 30000, S_ITEMPUP },
};

void
baseammo(int gun)
{
	player1->ammo[gun] = itemstats[gun - 1].add * 2;
}
229
230
231
232
233
234
235
236
237


238
239
240
241
242
243
244
			d->o.z = ents[e].z;
			d->yaw = ents[e].attr1;
			d->pitch = 0;
			d->vel.x = d->vel.y = d->vel.z = 0;
			entinmap(d);
			playsoundc(S_TELEPORT);
			break;
		};
	};


}

void
pickup(int n, dynent *d)
{
	int np = 1;
	loopv(players) if (players[i]) np++;







<
<
>
>







212
213
214
215
216
217
218


219
220
221
222
223
224
225
226
227
			d->o.z = ents[e].z;
			d->yaw = ents[e].attr1;
			d->pitch = 0;
			d->vel.x = d->vel.y = d->vel.z = 0;
			entinmap(d);
			playsoundc(S_TELEPORT);
			break;


		}
	}
}

void
pickup(int n, dynent *d)
{
	int np = 1;
	loopv(players) if (players[i]) np++;
290
291
292
293
294
295
296
297

298
299
300
301
302
303
304
	case TELEPORT: {
		static int lastteleport = 0;
		if (lastmillis - lastteleport < 500)
			break;
		lastteleport = lastmillis;
		teleport(n, d);
		break;
	};


	case JUMPPAD: {
		static int lastjumppad = 0;
		if (lastmillis - lastjumppad < 300)
			break;
		lastjumppad = lastmillis;
		OFVector3D v = OFMakeVector3D((int)(char)ents[n].attr3 / 10.0f,







<
>







273
274
275
276
277
278
279

280
281
282
283
284
285
286
287
	case TELEPORT: {
		static int lastteleport = 0;
		if (lastmillis - lastteleport < 500)
			break;
		lastteleport = lastmillis;
		teleport(n, d);
		break;

	}

	case JUMPPAD: {
		static int lastjumppad = 0;
		if (lastmillis - lastjumppad < 300)
			break;
		lastjumppad = lastmillis;
		OFVector3D v = OFMakeVector3D((int)(char)ents[n].attr3 / 10.0f,

Modified src/monster.mm from [9d16bae907] to [3ac13a2932].

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
	short gun, speed, health, freq, lag, rate, pain, loyalty, mscale,
	    bscale;
	short painsound, diesound;
	OFConstantString *name, *mdlname;
}

monstertypes[NUMMONSTERTYPES] = {
    {GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1,
        @"an ogre", @"monster/ogro"},
    {GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR, @"a rhino",
        @"monster/rhino"},
    {GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE,
        @"ratamahatta", @"monster/rat"},
    {GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS,
        @"a slith", @"monster/slith"},
    {GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB, @"bauul",
        @"monster/bauul"},
    {GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2,
        @"a hellpig", @"monster/hellpig"},
    {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"},
};

dynent *
basicmonster(int type, int yaw, int state, int trigger, int move)
{
	if (type >= NUMMONSTERTYPES) {
		conoutf(@"warning: unknown monster in spawn: %d", type);







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







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
	short gun, speed, health, freq, lag, rate, pain, loyalty, mscale,
	    bscale;
	short painsound, diesound;
	OFConstantString *name, *mdlname;
}

monstertypes[NUMMONSTERTYPES] = {
	{ GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1,
	    @"an ogre", @"monster/ogro" },
	{ GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR,
	    @"a rhino", @"monster/rhino" },
	{ GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE,
	    @"ratamahatta", @"monster/rat" },
	{ GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS,
	    @"a slith", @"monster/slith" },
	{ GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB,
	    @"bauul", @"monster/bauul" },
	{ GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2,
	    @"a hellpig", @"monster/hellpig" },
	{ 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" },
};

dynent *
basicmonster(int type, int yaw, int state, int trigger, int move)
{
	if (type >= NUMMONSTERTYPES) {
		conoutf(@"warning: unknown monster in spawn: %d", type);
86
87
88
89
90
91
92
93
94
95
96
97


98
99
100
101
102
103
104
	return m;
}

void
spawnmonster() // spawn a random monster according to freq distribution in DMSP
{
	int n = rnd(TOTMFREQ), type;
	for (int i = 0;; i++)
		if ((n -= monstertypes[i].freq) < 0) {
			type = i;
			break;
		};


	basicmonster(type, rnd(360), M_SEARCH, 1000, 1);
}

void
monsterclear() // called after map start of when toggling edit mode to
               // reset/spawn all monsters to initial state
{







|



<
>
>







86
87
88
89
90
91
92
93
94
95
96

97
98
99
100
101
102
103
104
105
	return m;
}

void
spawnmonster() // spawn a random monster according to freq distribution in DMSP
{
	int n = rnd(TOTMFREQ), type;
	for (int i = 0;; i++) {
		if ((n -= monstertypes[i].freq) < 0) {
			type = i;
			break;

		}
	}
	basicmonster(type, rnd(360), M_SEARCH, 1000, 1);
}

void
monsterclear() // called after map start of when toggling edit mode to
               // reset/spawn all monsters to initial state
{
118
119
120
121
122
123
124
125

126
127
128
129
130
131
132
			    ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
			m->o.x = ents[i].x;
			m->o.y = ents[i].y;
			m->o.z = ents[i].z;
			entinmap(m);
			monstertotal++;
		}
	};

}

bool
los(float lx, float ly, float lz, float bx, float by, float bz,
    OFVector3D &v) // height-correct line of sight for monster shooting/seeing
{
	if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))







<
>







119
120
121
122
123
124
125

126
127
128
129
130
131
132
133
			    ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
			m->o.x = ents[i].x;
			m->o.y = ents[i].y;
			m->o.z = ents[i].z;
			entinmap(m);
			monstertotal++;
		}

	}
}

bool
los(float lx, float ly, float lz, float bx, float by, float bz,
    OFVector3D &v) // height-correct line of sight for monster shooting/seeing
{
	if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
154
155
156
157
158
159
160
161

162
163
164
165
166
167
168
			break;
		v.x = x;
		v.y = y;
		v.z = rz;
		x += dx / (float)steps;
		y += dy / (float)steps;
		i++;
	};

	return i >= steps;
}

bool
enemylos(dynent *m, OFVector3D &v)
{
	v = m->o;







<
>







155
156
157
158
159
160
161

162
163
164
165
166
167
168
169
			break;
		v.x = x;
		v.y = y;
		v.z = rz;
		x += dx / (float)steps;
		y += dy / (float)steps;
		i++;

	}
	return i >= steps;
}

bool
enemylos(dynent *m, OFVector3D &v)
{
	v = m->o;
199
200
201
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

230
231
232
233

234
235
236
237
238
239


240
241
242
243
244
245
246
void
monsteraction(
    dynent *m) // main AI thinking routine, called every frame for every monster
{
	if (m->enemy->state == CS_DEAD) {
		m->enemy = player1;
		m->anger = 0;
	};

	normalise(m, m->targetyaw);
	if (m->targetyaw > m->yaw) // slowly turn monster towards his target
	{
		m->yaw += curtime * 0.5f;
		if (m->targetyaw < m->yaw)
			m->yaw = m->targetyaw;
	} else {
		m->yaw -= curtime * 0.5f;
		if (m->targetyaw > m->yaw)
			m->yaw = m->targetyaw;
	};


	vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
	m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;

	if (m->blocked) // special case: if we run into scenery
	{

		m->blocked = false;
		if (!rnd(20000 /
		        monstertypes[m->mtype]
		            .speed)) // try to jump over obstackle (rare)
		{

			m->jumpnext = true;

		} else if (m->trigger < lastmillis &&
		    (m->monsterstate != M_HOME ||
		        !rnd(5))) // search for a way around (common)
		{

			m->targetyaw +=
			    180 + rnd(180); // patented "random walk" AI
			                    // pathfinding (tm) ;)
			transition(m, M_SEARCH, 1, 400, 1000);
		};
	};



	float enemyyaw =
	    -(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y) / PI *
	        180 +
	    180;

	switch (m->monsterstate) {







<
>










<
>




|
<
>

<
<
|
<
>

>
|
|
<
<
>
|
<
<

<
<
>
>







200
201
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
230
231


232
233


234


235
236
237
238
239
240
241
242
243
void
monsteraction(
    dynent *m) // main AI thinking routine, called every frame for every monster
{
	if (m->enemy->state == CS_DEAD) {
		m->enemy = player1;
		m->anger = 0;

	}
	normalise(m, m->targetyaw);
	if (m->targetyaw > m->yaw) // slowly turn monster towards his target
	{
		m->yaw += curtime * 0.5f;
		if (m->targetyaw < m->yaw)
			m->yaw = m->targetyaw;
	} else {
		m->yaw -= curtime * 0.5f;
		if (m->targetyaw > m->yaw)
			m->yaw = m->targetyaw;

	}

	vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
	m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;

	// special case: if we run into scenery

	if (m->blocked) {
		m->blocked = false;


		// try to jump over obstackle (rare)

		if (!rnd(20000 / monstertypes[m->mtype].speed))
			m->jumpnext = true;
		// search for a way around (common)
		else if (m->trigger < lastmillis &&
		    (m->monsterstate != M_HOME || !rnd(5))) {


			// patented "random walk" AI pathfinding (tm) ;)
			m->targetyaw += 180 + rnd(180);


			transition(m, M_SEARCH, 1, 400, 1000);


		}
	}

	float enemyyaw =
	    -(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y) / PI *
	        180 +
	    180;

	switch (m->monsterstate) {
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
		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) {
			transition(m, M_HOME, 1, 500, 200);
			playsound(S_GRUNT1 + rnd(2), &m->o);
		};

		break;
	};


	case M_AIMING: // this state is the delay between wanting to shoot and
	               // actually firing
		if (m->trigger < lastmillis) {
			m->lastaction = 0;
			m->attacking = true;
			shoot(m, m->attacktarget);
			transition(m, M_ATTACKING, 0, 600, 0);
		};

		break;

	case M_HOME: // monster has visual contact, heads straight for player
	             // and may want to shoot at any time
		m->targetyaw = enemyyaw;
		if (m->trigger < lastmillis) {
			OFVector3D target;







<
>

<
>








<
>







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
		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) {
			transition(m, M_HOME, 1, 500, 200);
			playsound(S_GRUNT1 + rnd(2), &m->o);

		}
		break;

	}

	case M_AIMING: // this state is the delay between wanting to shoot and
	               // actually firing
		if (m->trigger < lastmillis) {
			m->lastaction = 0;
			m->attacking = true;
			shoot(m, m->attacktarget);
			transition(m, M_ATTACKING, 0, 600, 0);

		}
		break;

	case M_HOME: // monster has visual contact, heads straight for player
	             // and may want to shoot at any time
		m->targetyaw = enemyyaw;
		if (m->trigger < lastmillis) {
			OFVector3D target;
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
					m->attacktarget = target;
					transition(m, M_AIMING, 0,
					    monstertypes[m->mtype].lag, 10);
				} else // track player some more
				{
					transition(m, M_HOME, 1,
					    monstertypes[m->mtype].rate, 0);
				};
			};
		};



		break;
	};


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

void
monsterpain(dynent *m, int damage, dynent *d)
{

	if (d->monsterstate) // a monster hit us
	{
		if (m != d) // guard for RL guys shooting themselves :)
		{

			m->anger++; // don't attack straight away, first get
			            // angry
			int anger =
			    m->mtype == d->mtype ? m->anger / 2 : m->anger;
			if (anger >= monstertypes[m->mtype].loyalty)
				m->enemy = d; // monster infight if very angry


		};
	} else // player hit us
	{
		m->anger = 0;
		m->enemy = d;
	};


	transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,
	    200); // in this state monster won't attack
	if ((m->health -= damage) <= 0) {
		m->state = CS_DEAD;
		m->lastaction = lastmillis;
		numkilled++;
		player1->frags = numkilled;
		playsound(monstertypes[m->mtype].diesound, &m->o);
		int remain = monstertotal - numkilled;
		if (remain > 0 && remain <= 5)
			conoutf(@"only %d monster(s) remaining", remain);
	} else {
		playsound(monstertypes[m->mtype].painsound, &m->o);
	}
}

void
endsp(bool allkilled)
{
	conoutf(allkilled ? @"you have cleared the map!"
	                  : @"you reached the exit!");







<
<
<
>
>
>

<
>







>
|
<
|
<
>
|
|



|
>
>
|
|
<


<
>
>
|
<









|

<







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
					m->attacktarget = target;
					transition(m, M_AIMING, 0,
					    monstertypes[m->mtype].lag, 10);
				} else // track player some more
				{
					transition(m, M_HOME, 1,
					    monstertypes[m->mtype].rate, 0);



				}
			}
		}
		break;

	}

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

void
monsterpain(dynent *m, int damage, dynent *d)
{
	// a monster hit us
	if (d->monsterstate) {

		// guard for RL guys shooting themselves :)

		if (m != d) {
			// don't attack straight away, first get angry
			m->anger++;
			int anger =
			    m->mtype == d->mtype ? m->anger / 2 : m->anger;
			if (anger >= monstertypes[m->mtype].loyalty)
				// monster infight if very angry
				m->enemy = d;
		}
	} else {
		// player hit us

		m->anger = 0;
		m->enemy = d;

	}
	// in this state monster won't attack
	transition(m, M_PAIN, 0, monstertypes[m->mtype].pain, 200);

	if ((m->health -= damage) <= 0) {
		m->state = CS_DEAD;
		m->lastaction = lastmillis;
		numkilled++;
		player1->frags = numkilled;
		playsound(monstertypes[m->mtype].diesound, &m->o);
		int remain = monstertotal - numkilled;
		if (remain > 0 && remain <= 5)
			conoutf(@"only %d monster(s) remaining", remain);
	} else
		playsound(monstertypes[m->mtype].painsound, &m->o);

}

void
endsp(bool allkilled)
{
	conoutf(allkilled ? @"you have cleared the map!"
	                  : @"you reached the exit!");
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
monsterthink()
{
	if (m_dmsp && spawnremain && lastmillis > nextmonster) {
		if (spawnremain-- == monstertotal)
			conoutf(@"The invasion has begun!");
		nextmonster = lastmillis + 1000;
		spawnmonster();
	};


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

	loopv(ents) // equivalent of player entity touch, but only teleports are
	            // used
	{
		entity &e = ents[i];
		if (e.type != TELEPORT)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;
		OFVector3D v =
		    OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
		loopv(monsters) if (monsters[i]->state == CS_DEAD)
		{

			if (lastmillis - monsters[i]->lastaction < 2000) {

				monsters[i]->move = 0;
				moveplayer(monsters[i], 1, false);
			};
		}
		else
		{
			v.z += monsters[i]->eyeheight;
			vdist(dist, t, monsters[i]->o, v);
			v.z -= monsters[i]->eyeheight;
			if (dist < 4)

				teleport((int)(&e - &ents[0]), monsters[i]);

		}
	}

	loopv(monsters) if (monsters[i]->state == CS_ALIVE)
	    monsteraction(monsters[i]);
}








<
>














|

>
|
>
|
|
<
|
|
<
|
|
|
|
>
|
>







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
monsterthink()
{
	if (m_dmsp && spawnremain && lastmillis > nextmonster) {
		if (spawnremain-- == monstertotal)
			conoutf(@"The invasion has begun!");
		nextmonster = lastmillis + 1000;
		spawnmonster();

	}

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

	loopv(ents) // equivalent of player entity touch, but only teleports are
	            // used
	{
		entity &e = ents[i];
		if (e.type != TELEPORT)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;
		OFVector3D v =
		    OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
		loopv(monsters)
		{
			if (monsters[i]->state == CS_DEAD) {
				if (lastmillis - monsters[i]->lastaction <
				    2000) {
					monsters[i]->move = 0;
					moveplayer(monsters[i], 1, false);

				}
			} else {

				v.z += monsters[i]->eyeheight;
				vdist(dist, t, monsters[i]->o, v);
				v.z -= monsters[i]->eyeheight;
				if (dist < 4)
					teleport(
					    (int)(&e - &ents[0]), monsters[i]);
			}
		}
	}

	loopv(monsters) if (monsters[i]->state == CS_ALIVE)
	    monsteraction(monsters[i]);
}

Modified src/physics.mm from [5d3f7bf679] to [cbd2b108e1].

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
		if (fabs(o->o.z - d->o.z) < o->aboveeye + d->eyeheight)
			return false;
		if (d->monsterstate)
			return false; // hack
		headspace = d->o.z - o->o.z - o->aboveeye - d->eyeheight;
		if (headspace < 0)
			headspace = 10;
	};

	return true;
}

bool
cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by,
    int &bs) // recursively collide with a mipmapped corner cube
{
	sqr *w = wmip[mip];
	int sz = ssize >> mip;
	bool stest =
	    SOLID(SWS(w, x + dx, y, sz)) && SOLID(SWS(w, x, y + dy, sz));
	mip++;
	x /= 2;
	y /= 2;
	if (SWS(wmip[mip], x, y, ssize >> mip)->type == CORNER) {
		bx = x << mip;
		by = y << mip;
		bs = 1 << mip;
		return cornertest(mip, x, y, dx, dy, bx, by, bs);
	};

	return stest;
}

void
mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
{
	loopv(ents)







<
>



















<
>







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
		if (fabs(o->o.z - d->o.z) < o->aboveeye + d->eyeheight)
			return false;
		if (d->monsterstate)
			return false; // hack
		headspace = d->o.z - o->o.z - o->aboveeye - d->eyeheight;
		if (headspace < 0)
			headspace = 10;

	}
	return true;
}

bool
cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by,
    int &bs) // recursively collide with a mipmapped corner cube
{
	sqr *w = wmip[mip];
	int sz = ssize >> mip;
	bool stest =
	    SOLID(SWS(w, x + dx, y, sz)) && SOLID(SWS(w, x, y + dy, sz));
	mip++;
	x /= 2;
	y /= 2;
	if (SWS(wmip[mip], x, y, ssize >> mip)->type == CORNER) {
		bx = x << mip;
		by = y << mip;
		bs = 1 << mip;
		return cornertest(mip, x, y, dx, dy, bx, by, bs);

	}
	return stest;
}

void
mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
{
	loopv(ents)
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
				            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;
			};


			case FHF: // FIXME: too simplistic collision with
			          // slopes, makes it feels like tiny stairs
				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)
				return false;
		};


	if (hi - lo < d->eyeheight + d->aboveeye)
		return false;

	float headspace = 10;
	loopv(players) // collide with other players
	{







<
>














<
>






<
>







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

			}

			case FHF: // FIXME: too simplistic collision with
			          // slopes, makes it feels like tiny stairs
				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)
				return false;

		}

	if (hi - lo < d->eyeheight + d->aboveeye)
		return false;

	float headspace = 10;
	loopv(players) // collide with other players
	{
190
191
192
193
194
195
196
197

198
199
200
201
202
203
204
205
206

207
208

209
210
211
212
213
214
215
				d->o.z = lo + d->eyeheight; // stick on step
			else if (space > -1.26f)
				d->o.z += rise; // rise thru stair
			else
				return false;
		} else {
			d->o.z -= min(min(drop, space), headspace); // gravity
		};


		const float space2 = hi - (d->o.z + d->aboveeye);
		if (space2 < 0) {
			if (space2 < -0.1)
				return false;      // hack alert!
			d->o.z = hi - d->aboveeye; // glue to ceiling
			d->vel.z = 0; // cancel out jumping velocity
		};


		d->onfloor = d->o.z - d->eyeheight - lo < 0.001f;
	};

	return true;
}

float
rad(float x)
{
	return x * 3.14159f / 180;







<
>







<
|
>

<
>







190
191
192
193
194
195
196

197
198
199
200
201
202
203
204

205
206
207

208
209
210
211
212
213
214
215
				d->o.z = lo + d->eyeheight; // stick on step
			else if (space > -1.26f)
				d->o.z += rise; // rise thru stair
			else
				return false;
		} else {
			d->o.z -= min(min(drop, space), headspace); // gravity

		}

		const float space2 = hi - (d->o.z + d->aboveeye);
		if (space2 < 0) {
			if (space2 < -0.1)
				return false;      // hack alert!
			d->o.z = hi - d->aboveeye; // glue to ceiling
			d->vel.z = 0; // cancel out jumping velocity

		}

		d->onfloor = d->o.z - d->eyeheight - lo < 0.001f;

	}
	return true;
}

float
rad(float x)
{
	return x * 3.14159f / 180;
225
226
227
228
229
230
231
232

233
234
235
236
237
238
239
{
	if (curtime >= MINFRAMETIME) {
		int faketime = curtime + physicsfraction;
		physicsrepeat = faketime / MINFRAMETIME;
		physicsfraction = faketime - physicsrepeat * MINFRAMETIME;
	} else {
		physicsrepeat = 1;
	};

}

// main physics routine, moves a player/monster for a curtime step
// moveres indicated the physics precision (which is lower for monsters and
// multiplayer prediction) local is false for multiplayer prediction

void







<
>







225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
{
	if (curtime >= MINFRAMETIME) {
		int faketime = curtime + physicsfraction;
		physicsrepeat = faketime / MINFRAMETIME;
		physicsfraction = faketime - physicsrepeat * MINFRAMETIME;
	} else {
		physicsrepeat = 1;

	}
}

// main physics routine, moves a player/monster for a curtime step
// moveres indicated the physics precision (which is lower for monsters and
// multiplayer prediction) local is false for multiplayer prediction

void
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
	d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
	d.z = 0;

	if (floating || water) {
		d.x *= (float)cos(rad(pl->pitch));
		d.y *= (float)cos(rad(pl->pitch));
		d.z = (float)(pl->move * sin(rad(pl->pitch)));
	};


	d.x += (float)(pl->strafe * cos(rad(pl->yaw - 180)));
	d.y += (float)(pl->strafe * sin(rad(pl->yaw - 180)));

	const float speed =
	    curtime / (water ? 2000.0f : 1000.0f) * pl->maxspeed;
	const float friction =







<
>







248
249
250
251
252
253
254

255
256
257
258
259
260
261
262
	d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
	d.z = 0;

	if (floating || water) {
		d.x *= (float)cos(rad(pl->pitch));
		d.y *= (float)cos(rad(pl->pitch));
		d.z = (float)(pl->move * sin(rad(pl->pitch)));

	}

	d.x += (float)(pl->strafe * cos(rad(pl->yaw - 180)));
	d.y += (float)(pl->strafe * sin(rad(pl->yaw - 180)));

	const float speed =
	    curtime / (water ? 2000.0f : 1000.0f) * pl->maxspeed;
	const float friction =
283
284
285
286
287
288
289


290
291
292
293
294

295
296
297
298
299
300
301
302
303
304
305
306
307

308
309
310
311

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
		}
	} else // apply velocity with collision
	{
		if (pl->onfloor || water) {
			if (pl->jumpnext) {
				pl->jumpnext = false;
				pl->vel.z = 1.7f; // physics impulse upwards


				if (water) {
					pl->vel.x /= 8;
					pl->vel.y /= 8;
				}; // dampen velocity change even harder, gives
				   // correct water feel

				if (local)
					playsoundc(S_JUMP);
				else if (pl->monsterstate)
					playsound(S_JUMP, &pl->o);
			} else if (pl->timeinair >
			    800) // if we land after long time must have
			         // been a high jump, make thud sound
			{
				if (local)
					playsoundc(S_LAND);
				else if (pl->monsterstate)
					playsound(S_LAND, &pl->o);
			};

			pl->timeinair = 0;
		} else {
			pl->timeinair += curtime;
		};


		const float gravity = 20;
		const float f = 1.0f / moveres;

		float dropf = ((gravity - 1) +
		    pl->timeinair / 15.0f); // incorrect, but works fine

		if (water) {
			dropf = 5;
			pl->timeinair = 0;
		}; // float slowly down in water

		const float drop = dropf * curtime / gravity / 100 /
		    moveres; // at high fps, gravity kicks in too fast
		const float rise = speed / moveres /
		    1.2f; // extra smoothness when lifting up stairs

		loopi(moveres) // discrete steps collision detection & sliding
		{
			// try move forward
			pl->o.x += f * d.x;
			pl->o.y += f * d.y;
			pl->o.z += f * d.z;
			if (collide(pl, false, drop, rise))
				continue;
			// player stuck, try slide along y axis
			pl->blocked = true;
			pl->o.x -= f * d.x;
			if (collide(pl, false, drop, rise)) {
				d.x = 0;
				continue;
			};

			pl->o.x += f * d.x;
			// still stuck, try x axis
			pl->o.y -= f * d.y;
			if (collide(pl, false, drop, rise)) {
				d.y = 0;
				continue;
			};

			pl->o.y += f * d.y;
			// try just dropping down
			pl->moving = false;
			pl->o.x -= f * d.x;
			pl->o.y -= f * d.y;
			if (collide(pl, false, drop, rise)) {
				d.y = d.x = 0;
				continue;
			};

			pl->o.z -= f * d.z;
			break;
		}
	};


	// detect wether player is outside map, used for skipping zbuffer clear
	// mostly

	if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
		pl->outsidemap = true;
	} else {
		sqr *s = S((int)pl->o.x, (int)pl->o.y);
		pl->outsidemap = SOLID(s) ||
		    pl->o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
		    pl->o.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 {
		pl->roll += pl->strafe * curtime / -30.0f;
		if (pl->roll > maxroll)
			pl->roll = (float)maxroll;
		if (pl->roll < -maxroll)
			pl->roll = (float)-maxroll;
	};


	// play sounds on water transitions

	if (!pl->inwater && water) {
		playsound(S_SPLASH2, &pl->o);
		pl->vel.z = 0;
	} else if (pl->inwater && !water)







>
>



<
<
>




|
|
|
<




<
>



<
>



>
|
<
>



<
>



















<
>






<
>








<
>



<
>











<
>











<
>







283
284
285
286
287
288
289
290
291
292
293
294


295
296
297
298
299
300
301
302

303
304
305
306

307
308
309
310

311
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
		}
	} else // apply velocity with collision
	{
		if (pl->onfloor || water) {
			if (pl->jumpnext) {
				pl->jumpnext = false;
				pl->vel.z = 1.7f; // physics impulse upwards
				// dampen velocity change even harder, gives
				// correct water feel
				if (water) {
					pl->vel.x /= 8;
					pl->vel.y /= 8;


				}
				if (local)
					playsoundc(S_JUMP);
				else if (pl->monsterstate)
					playsound(S_JUMP, &pl->o);
			} else if (pl->timeinair > 800) {
				// if we land after long time must have been a
				// high jump, make thud sound

				if (local)
					playsoundc(S_LAND);
				else if (pl->monsterstate)
					playsound(S_LAND, &pl->o);

			}
			pl->timeinair = 0;
		} else {
			pl->timeinair += curtime;

		}

		const float gravity = 20;
		const float f = 1.0f / moveres;
		// incorrect, but works fine
		float dropf = ((gravity - 1) + pl->timeinair / 15.0f);

		// float slowly down in water
		if (water) {
			dropf = 5;
			pl->timeinair = 0;

		}
		const float drop = dropf * curtime / gravity / 100 /
		    moveres; // at high fps, gravity kicks in too fast
		const float rise = speed / moveres /
		    1.2f; // extra smoothness when lifting up stairs

		loopi(moveres) // discrete steps collision detection & sliding
		{
			// try move forward
			pl->o.x += f * d.x;
			pl->o.y += f * d.y;
			pl->o.z += f * d.z;
			if (collide(pl, false, drop, rise))
				continue;
			// player stuck, try slide along y axis
			pl->blocked = true;
			pl->o.x -= f * d.x;
			if (collide(pl, false, drop, rise)) {
				d.x = 0;
				continue;

			}
			pl->o.x += f * d.x;
			// still stuck, try x axis
			pl->o.y -= f * d.y;
			if (collide(pl, false, drop, rise)) {
				d.y = 0;
				continue;

			}
			pl->o.y += f * d.y;
			// try just dropping down
			pl->moving = false;
			pl->o.x -= f * d.x;
			pl->o.y -= f * d.y;
			if (collide(pl, false, drop, rise)) {
				d.y = d.x = 0;
				continue;

			}
			pl->o.z -= f * d.z;
			break;
		}

	}

	// detect wether player is outside map, used for skipping zbuffer clear
	// mostly

	if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
		pl->outsidemap = true;
	} else {
		sqr *s = S((int)pl->o.x, (int)pl->o.y);
		pl->outsidemap = SOLID(s) ||
		    pl->o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
		    pl->o.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 {
		pl->roll += pl->strafe * curtime / -30.0f;
		if (pl->roll > maxroll)
			pl->roll = (float)maxroll;
		if (pl->roll < -maxroll)
			pl->roll = (float)-maxroll;

	}

	// play sounds on water transitions

	if (!pl->inwater && water) {
		playsound(S_SPLASH2, &pl->o);
		pl->vel.z = 0;
	} else if (pl->inwater && !water)

Modified src/rendercubes.mm from [e0804e2cb2] to [50158effb9].

43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
		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;







<
>







43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
		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;
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
}

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

void
finishstrips()
{
	stripend();
}

sqr sbright, sdark;
VAR(lighterror, 1, 8, 100);

void
render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3,
    sqr *l4, bool isceil) // floor/ceil quads
{
	vertcheck();
	if (showm) {
		l3 = l1 = &sbright;
		l4 = l2 = &sdark;
	};


	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float ys = size * yf;







|
<
>

















<
>







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
}

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

	}
void
finishstrips()
{
	stripend();
}

sqr sbright, sdark;
VAR(lighterror, 1, 8, 100);

void
render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3,
    sqr *l4, bool isceil) // floor/ceil quads
{
	vertcheck();
	if (showm) {
		l3 = l1 = &sbright;
		l4 = l2 = &sdark;

	}

	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float ys = size * yf;
124
125
126
127
128
129
130
131

132
133
134
135
136
137
138
		floorstrip = true;
		if (isceil) {
			vert(x + size, h, y, l2, xo + xs, yo);
			vert(x, h, y, l1, xo, yo);
		} else {
			vert(x, h, y, l1, xo, yo);
			vert(x + size, h, y, l2, xo + xs, yo);
		};

		ol3r = l1->r;
		ol3g = l1->g;
		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;
	} else // continue strip







<
>







124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
		floorstrip = true;
		if (isceil) {
			vert(x + size, h, y, l2, xo + xs, yo);
			vert(x, h, y, l1, xo, yo);
		} else {
			vert(x, h, y, l1, xo, yo);
			vert(x + size, h, y, l2, xo + xs, yo);

		}
		ol3r = l1->r;
		ol3g = l1->g;
		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;
	} else // continue strip
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
			ol3r = p3[0];
			ol3g = p3[1];
			ol3b = p3[2];
			uchar *p4 = (uchar *)(&verts[curvert - 2].r);
			ol4r = p4[0];
			ol4g = p4[1];
			ol4b = p4[2];
		};
	};



	if (isceil) {
		vert(x + size, h, y + size, l3, xo + xs, yo + ys);
		vert(x, h, y + size, l4, xo, yo + ys);
	} else {
		vert(x, h, y + size, l4, xo, yo + ys);
		vert(x + size, h, y + size, l3, xo + xs, yo + ys);
	};


	oy = y;
	nquads++;
}

void
render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, float h3,
    float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4,
    bool isceil) // floor/ceil quads on a slope
{
	vertcheck();
	if (showm) {
		l3 = l1 = &sbright;
		l4 = l2 = &sdark;
	};


	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float ys = size * yf;







<
<
>
>







<
>














<
>







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
			ol3r = p3[0];
			ol3g = p3[1];
			ol3b = p3[2];
			uchar *p4 = (uchar *)(&verts[curvert - 2].r);
			ol4r = p4[0];
			ol4g = p4[1];
			ol4b = p4[2];


		}
	}

	if (isceil) {
		vert(x + size, h, y + size, l3, xo + xs, yo + ys);
		vert(x, h, y + size, l4, xo, yo + ys);
	} else {
		vert(x, h, y + size, l4, xo, yo + ys);
		vert(x + size, h, y + size, l3, xo + xs, yo + ys);

	}

	oy = y;
	nquads++;
}

void
render_flatdelta(int wtex, int x, int y, int size, float h1, float h2, float h3,
    float h4, sqr *l1, sqr *l2, sqr *l3, sqr *l4,
    bool isceil) // floor/ceil quads on a slope
{
	vertcheck();
	if (showm) {
		l3 = l1 = &sbright;
		l4 = l2 = &sdark;

	}

	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float ys = size * yf;
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
230
231
232
233
234
		deltastrip = true;
		if (isceil) {
			vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);
			vertf((float)x, h1, (float)y, l1, xo, yo);
		} else {
			vertf((float)x, h1, (float)y, l1, xo, yo);
			vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);
		};

		ol3r = l1->r;
		ol3g = l1->g;
		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;
	};


	if (isceil) {
		vertf(
		    (float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
		vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
	} else {
		vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
		vertf(
		    (float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
	};


	oy = y;
	nquads++;
}

void
render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3,







<
>






<
>









<
>







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
230
231
232
233
234
		deltastrip = true;
		if (isceil) {
			vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);
			vertf((float)x, h1, (float)y, l1, xo, yo);
		} else {
			vertf((float)x, h1, (float)y, l1, xo, yo);
			vertf((float)x + size, h2, (float)y, l2, xo + xs, yo);

		}
		ol3r = l1->r;
		ol3g = l1->g;
		ol3b = l1->b;
		ol4r = l2->r;
		ol4g = l2->g;
		ol4b = l2->b;

	}

	if (isceil) {
		vertf(
		    (float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
		vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
	} else {
		vertf((float)x, h4, (float)y + size, l4, xo, yo + ys);
		vertf(
		    (float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);

	}

	oy = y;
	nquads++;
}

void
render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3,
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
310
311

312
313
314
315
316
317
318
	} else {
		if (h1)
			render_2tris(
			    h1, s, x, y, x + size, y, x, y + size, s, t, u);
		if (h2)
			render_2tris(h2, s, x + size, y, x + size, y + size, x,
			    y + size, t, u, v);
	};

}

void
render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2,
    int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2,
    bool flip) // wall quads
{
	stripend();
	vertcheck();
	if (showm) {
		l1 = &sbright;
		l2 = &sdark;
	};


	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float xo = xf * (x1 == x2 ? min(y1, y2) : min(x1, x2));

	if (!flip) {
		vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
		vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
		vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);
		vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
	} else {
		vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
		vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
		vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
		vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);
	};


	nquads++;
	addstrip(gltex, curvert - 4, 4);
}

int wx1, wy1, wx2, wy2;








<
>












<
>


















<
>







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
310

311
312
313
314
315
316
317
318
	} else {
		if (h1)
			render_2tris(
			    h1, s, x, y, x + size, y, x, y + size, s, t, u);
		if (h2)
			render_2tris(h2, s, x + size, y, x + size, y + size, x,
			    y + size, t, u, v);

	}
}

void
render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2,
    int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2,
    bool flip) // wall quads
{
	stripend();
	vertcheck();
	if (showm) {
		l1 = &sbright;
		l2 = &sdark;

	}

	int sx, sy;
	int gltex = lookuptexture(wtex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = size * xf;
	float xo = xf * (x1 == x2 ? min(y1, y2) : min(x1, x2));

	if (!flip) {
		vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
		vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
		vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);
		vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
	} else {
		vertf((float)x1, ceil1, (float)y1, l1, xo, -yf * ceil1);
		vertf((float)x2, ceil2, (float)y2, l2, xo + xs, -yf * ceil2);
		vertf((float)x1, floor1, (float)y1, l1, xo, -floor1 * yf);
		vertf((float)x2, floor2, (float)y2, l2, xo + xs, -floor2 * yf);

	}

	nquads++;
	addstrip(gltex, curvert - 4, 4);
}

int wx1, wy1, wx2, wy2;

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
		for (int yy = wy1; yy < wy2; yy += watersubdiv) {
			float xo = xf * (xx + t2);
			float yo = yf * (yy + t2);
			if (yy == wy1) {
				vertw(xx, hf, yy, &dl, dx(xo), dy(yo), t1);
				vertw(xx + watersubdiv, hf, yy, &dl,
				    dx(xo + xs), dy(yo), t1);
			};

			vertw(xx, hf, yy + watersubdiv, &dl, dx(xo),
			    dy(yo + ys), t1);
			vertw(xx + watersubdiv, hf, yy + watersubdiv, &dl,
			    dx(xo + xs), dy(yo + ys), t1);
		};

		int n = (wy2 - wy1 - 1) / watersubdiv;
		nquads += n;
		n = (n + 2) * 2;
		glDrawArrays(GL_TRIANGLE_STRIP, curvert -= n, n);
	};


	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);

	return nquads;
}








<
>




<
>




<
>







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
		for (int yy = wy1; yy < wy2; yy += watersubdiv) {
			float xo = xf * (xx + t2);
			float yo = yf * (yy + t2);
			if (yy == wy1) {
				vertw(xx, hf, yy, &dl, dx(xo), dy(yo), t1);
				vertw(xx + watersubdiv, hf, yy, &dl,
				    dx(xo + xs), dy(yo), t1);

			}
			vertw(xx, hf, yy + watersubdiv, &dl, dx(xo),
			    dy(yo + ys), t1);
			vertw(xx + watersubdiv, hf, yy + watersubdiv, &dl,
			    dx(xo + xs), dy(yo + ys), t1);

		}
		int n = (wy2 - wy1 - 1) / watersubdiv;
		nquads += n;
		n = (n + 2) * 2;
		glDrawArrays(GL_TRIANGLE_STRIP, curvert -= n, n);

	}

	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);

	return nquads;
}

408
409
410
411
412
413
414
415

416
417
418
419
420
421
422
423
424
425
426
427
428
			wx1 = x;
		if (y < wy1)
			wy1 = y;
		if (x2 > wx2)
			wx2 = x2;
		if (y2 > wy2)
			wy2 = y2;
	};

}

void
resetcubes()
{
	if (!verts)
		reallocv();
	floorstrip = deltastrip = false;
	wx1 = -1;
	nquads = 0;
	sbright.r = sbright.g = sbright.b = 255;
	sdark.r = sdark.g = sdark.b = 0;
}







<
>













408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
423
424
425
426
427
428
			wx1 = x;
		if (y < wy1)
			wy1 = y;
		if (x2 > wx2)
			wx2 = x2;
		if (y2 > wy2)
			wy2 = y2;

	}
}

void
resetcubes()
{
	if (!verts)
		reallocv();
	floorstrip = deltastrip = false;
	wx1 = -1;
	nquads = 0;
	sbright.r = sbright.g = sbright.b = 255;
	sdark.r = sdark.g = sdark.b = 0;
}

Modified src/renderextras.mm from [0c686b4d39] to [f6d88ed878].

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
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		}
		sinit = true;
	};

	if (sempty) {
		sphere *p = sempty;
		sempty = p->next;
		p->o = o;
		p->max = max;
		p->size = 1;
		p->type = type;
		p->next = slist;
		slist = p;
	};

}

void
renderspheres(int time)
{
	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);







<
>









<
>







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
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		}
		sinit = true;

	}
	if (sempty) {
		sphere *p = sempty;
		sempty = p->next;
		p->o = o;
		p->max = max;
		p->size = 1;
		p->type = type;
		p->next = slist;
		slist = p;

	}
}

void
renderspheres(int time)
{
	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
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
		if (p->size > p->max) {
			*pp = p->next;
			p->next = sempty;
			sempty = p;
		} else {
			p->size += time / 100.0f;
			pp = &p->next;
		};
	};



	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
}

string closeent;
OFString *entnames[] = {
    @"none?",
    @"light",
    @"playerstart",
    @"shells",
    @"bullets",
    @"rockets",
    @"riflerounds",
    @"health",
    @"healthboost",
    @"greenarmour",
    @"yellowarmour",
    @"quaddamage",
    @"teleport",
    @"teledest",
    @"mapmodel",
    @"monster",
    @"trigger",
    @"jumppad",
    @"?",
    @"?",
    @"?",
    @"?",
    @"?",
};

void
renderents() // show sparkly thingies for map entities in edit mode
{
	closeent[0] = 0;
	if (!editmode)







<
<
>
>







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







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
		if (p->size > p->max) {
			*pp = p->next;
			p->next = sempty;
			sempty = p;
		} else {
			p->size += time / 100.0f;
			pp = &p->next;


		}
	}

	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
}

string closeent;
OFString *entnames[] = {
	@"none?",
	@"light",
	@"playerstart",
	@"shells",
	@"bullets",
	@"rockets",
	@"riflerounds",
	@"health",
	@"healthboost",
	@"greenarmour",
	@"yellowarmour",
	@"quaddamage",
	@"teleport",
	@"teledest",
	@"mapmodel",
	@"monster",
	@"trigger",
	@"jumppad",
	@"?",
	@"?",
	@"?",
	@"?",
	@"?",
};

void
renderents() // show sparkly thingies for map entities in edit mode
{
	closeent[0] = 0;
	if (!editmode)
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
{
	@autoreleasepool {
		static OFString *lastsky = @"";

		if ([lastsky isEqual:basename])
			return;

		static const OFString *side[] = {
		    @"ft", @"bk", @"lf", @"rt", @"dn", @"up"};
		int texnum = 14;
		loopi(6)
		{
			OFString *path =
			    [OFString stringWithFormat:@"packages/%@_%@.jpg",
			              basename, side[i]];








|
|







206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
{
	@autoreleasepool {
		static OFString *lastsky = @"";

		if ([lastsky isEqual:basename])
			return;

		static const OFString *side[] = { @"ft", @"bk", @"lf", @"rt",
			@"dn", @"up" };
		int texnum = 14;
		loopi(6)
		{
			OFString *path =
			    [OFString stringWithFormat:@"packages/%@_%@.jpg",
			              basename, side[i]];

332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
	readmatrices();
	if (editmode) {
		if (cursordepth == 1.0f)
			worldpos = player1->o;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		cursorupdate();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	};


	glDisable(GL_DEPTH_TEST);
	invertperspective();
	glPushMatrix();
	glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
	glEnable(GL_BLEND);








<
>







332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
	readmatrices();
	if (editmode) {
		if (cursordepth == 1.0f)
			worldpos = player1->o;
		glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
		cursorupdate();
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	}

	glDisable(GL_DEPTH_TEST);
	invertperspective();
	glPushMatrix();
	glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
	glEnable(GL_BLEND);

438
439
440
441
442
443
444
445

446
447
448
449
450
451
452
453
454
			drawicon(
			    (float)(player1->armourtype * 64), 0, 620, 1650);
		int g = player1->gunselect;
		int r = 64;
		if (g > 2) {
			g -= 3;
			r = 128;
		};

		drawicon((float)(g * 64), (float)r, 1220, 1650);
		glPopMatrix();
	}

	glDepthMask(GL_TRUE);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
}







<
>









438
439
440
441
442
443
444

445
446
447
448
449
450
451
452
453
454
			drawicon(
			    (float)(player1->armourtype * 64), 0, 620, 1650);
		int g = player1->gunselect;
		int r = 64;
		if (g > 2) {
			g -= 3;
			r = 128;

		}
		drawicon((float)(g * 64), (float)r, 1220, 1650);
		glPopMatrix();
	}

	glDepthMask(GL_TRUE);
	glDisable(GL_BLEND);
	glDisable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
}

Modified src/rendergl.mm from [5b95ce281e] to [9007a349cc].

106
107
108
109
110
111
112



113
114


115
116
117
118
119
120
121
				SDL_FreeSurface(s);
				s = converted;
			} @finally {
				SDL_FreeFormat(format);
			}
		}




		// loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p =
		// 255-*p; };


		glBindTexture(GL_TEXTURE_2D, tnum);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
		    clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
		    clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
		glTexParameteri(







>
>
>
|
|
>
>







106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
				SDL_FreeSurface(s);
				s = converted;
			} @finally {
				SDL_FreeFormat(format);
			}
		}

#if 0
		loopi(s->w * s->h * 3)
		{
			uchar *p = (uchar *)s->pixels + i;
			*p = 255 - *p;
		}
#endif
		glBindTexture(GL_TEXTURE_2D, tnum);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
		    clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
		    clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT);
		glTexParameteri(
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274

	if (hasoverbright) {
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
		glTexEnvi(
		    GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
	};

}

int skyoglid;

struct strip {
	int tex, start, num;
};







<
>







265
266
267
268
269
270
271

272
273
274
275
276
277
278
279

	if (hasoverbright) {
		glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
		glTexEnvi(
		    GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);

	}
}

int skyoglid;

struct strip {
	int tex, start, num;
};
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
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)
{
	rendermodel(hudgunnames[player1->gunselect], start, end, 0, 1.0f,
	    player1->o.x, player1->o.z, player1->o.y, player1->yaw + 90,
	    player1->pitch, false, 1.0f, speed, 0, base);







|
|







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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)
{
	rendermodel(hudgunnames[player1->gunselect], start, end, 0, 1.0f,
	    player1->o.x, player1->o.z, player1->o.y, player1->yaw + 90,
	    player1->pitch, false, 1.0f, speed, 0, base);
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
	int rtime = reloadtime(player1->gunselect);
	if (player1->lastaction &&
	    player1->lastattackgun == player1->gunselect &&
	    lastmillis - player1->lastaction < rtime) {
		drawhudmodel(7, 18, rtime / 18.0f, player1->lastaction);
	} else {
		drawhudmodel(6, 1, 100, 0);
	};


	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(fovy, aspect, 0.15f, farplane);
	glMatrixMode(GL_MODELVIEW);

	glDisable(GL_CULL_FACE);
}

void
gl_drawframe(int w, int h, float curfps)
{
	float hf = hdr.waterlevel - 0.3f;
	float fovy = (float)fov * h / w;
	float aspect = w / (float)h;
	bool underwater = player1->o.z < hf;

	glFogi(GL_FOG_START, (fog + 64) / 8);
	glFogi(GL_FOG_END, fog);
	float fogc[4] = {(fogcolour >> 16) / 256.0f,
	    ((fogcolour >> 8) & 255) / 256.0f, (fogcolour & 255) / 256.0f,
	    1.0f};
	glFogfv(GL_FOG_COLOR, fogc);
	glClearColor(fogc[0], fogc[1], fogc[2], 1.0f);

	if (underwater) {
		fovy += (float)sin(lastmillis / 1000.0) * 2.0f;
		aspect += (float)sin(lastmillis / 1000.0 + PI) * 0.1f;
		glFogi(GL_FOG_START, 0);
		glFogi(GL_FOG_END, (fog + 96) / 8);
	};


	glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
	    GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	int farplane = fog * 5 / 2;







<
>



















|
|
|








<
>







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
	int rtime = reloadtime(player1->gunselect);
	if (player1->lastaction &&
	    player1->lastattackgun == player1->gunselect &&
	    lastmillis - player1->lastaction < rtime) {
		drawhudmodel(7, 18, rtime / 18.0f, player1->lastaction);
	} else {
		drawhudmodel(6, 1, 100, 0);

	}

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(fovy, aspect, 0.15f, farplane);
	glMatrixMode(GL_MODELVIEW);

	glDisable(GL_CULL_FACE);
}

void
gl_drawframe(int w, int h, float curfps)
{
	float hf = hdr.waterlevel - 0.3f;
	float fovy = (float)fov * h / w;
	float aspect = w / (float)h;
	bool underwater = player1->o.z < hf;

	glFogi(GL_FOG_START, (fog + 64) / 8);
	glFogi(GL_FOG_END, fog);
	float fogc[4] = { (fogcolour >> 16) / 256.0f,
		((fogcolour >> 8) & 255) / 256.0f, (fogcolour & 255) / 256.0f,
		1.0f };
	glFogfv(GL_FOG_COLOR, fogc);
	glClearColor(fogc[0], fogc[1], fogc[2], 1.0f);

	if (underwater) {
		fovy += (float)sin(lastmillis / 1000.0) * 2.0f;
		aspect += (float)sin(lastmillis / 1000.0 + PI) * 0.1f;
		glFogi(GL_FOG_START, 0);
		glFogi(GL_FOG_END, (fog + 96) / 8);

	}

	glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
	    GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	int farplane = fog * 5 / 2;

Modified src/renderparticles.mm from [9f00935137] to [b42cd85412].

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
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];
		}
		parinit = true;
	};

	if (parempty) {
		particle *p = parempty;
		parempty = p->next;
		p->o = o;
		p->d = d;
		p->fade = fade;
		p->type = type;
		p->millis = lastmillis;
		p->next = parlist;
		parlist = p;
	};

}

VAR(demotracking, 0, 0, 1);
VARP(particlesize, 20, 100, 500);

OFVector3D right, up;








<
>










<
>







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
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];
		}
		parinit = true;

	}
	if (parempty) {
		particle *p = parempty;
		parempty = p->next;
		p->o = o;
		p->d = d;
		p->fade = fade;
		p->type = type;
		p->millis = lastmillis;
		p->next = parlist;
		parlist = p;

	}
}

VAR(demotracking, 0, 0, 1);
VARP(particlesize, 20, 100, 500);

OFVector3D right, up;

65
66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
	glDisable(GL_FOG);

	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 (particle *p, **pp = &parlist; p = *pp;) {
		parttype *pt = &parttypes[p->type];








|
|
|
>
|
|
|
|
|
|







65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	glDisable(GL_FOG);

	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 (particle *p, **pp = &parlist; p = *pp;) {
		parttype *pt = &parttypes[p->type];

Modified src/rendertext.mm from [c20068be3f] to [d117eaf6b5].

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
// rendertext.cpp: based on Don's gl_text.cpp

#include "cube.h"

short char_coords[96][4] = {
    {0, 0, 25, 64},       //!
    {25, 0, 54, 64},      //"
    {54, 0, 107, 64},     // #
    {107, 0, 148, 64},    //$
    {148, 0, 217, 64},    //%
    {217, 0, 263, 64},    //&
    {263, 0, 280, 64},    //'
    {280, 0, 309, 64},    //(
    {309, 0, 338, 64},    //)
    {338, 0, 379, 64},    //*
    {379, 0, 432, 64},    //+
    {432, 0, 455, 64},    //,
    {455, 0, 484, 64},    //-
    {0, 64, 21, 128},     //.
    {23, 64, 52, 128},    ///
    {52, 64, 93, 128},    // 0
    {93, 64, 133, 128},   // 1
    {133, 64, 174, 128},  // 2
    {174, 64, 215, 128},  // 3
    {215, 64, 256, 128},  // 4
    {256, 64, 296, 128},  // 5
    {296, 64, 337, 128},  // 6
    {337, 64, 378, 128},  // 7
    {378, 64, 419, 128},  // 8
    {419, 64, 459, 128},  // 9
    {459, 64, 488, 128},  //:
    {0, 128, 29, 192},    //;
    {29, 128, 81, 192},   //<
    {81, 128, 134, 192},  //=
    {134, 128, 186, 192}, //>
    {186, 128, 221, 192}, //?
    {221, 128, 285, 192}, //@
    {285, 128, 329, 192}, // A
    {329, 128, 373, 192}, // B
    {373, 128, 418, 192}, // C
    {418, 128, 467, 192}, // D
    {0, 192, 40, 256},    // E
    {40, 192, 77, 256},   // F
    {77, 192, 127, 256},  // G
    {127, 192, 175, 256}, // H
    {175, 192, 202, 256}, // I
    {202, 192, 231, 256}, // J
    {231, 192, 275, 256}, // K
    {275, 192, 311, 256}, // L
    {311, 192, 365, 256}, // M
    {365, 192, 413, 256}, // N
    {413, 192, 463, 256}, // O
    {1, 256, 38, 320},    // P
    {38, 256, 89, 320},   // Q
    {89, 256, 133, 320},  // R
    {133, 256, 176, 320}, // S
    {177, 256, 216, 320}, // T
    {217, 256, 263, 320}, // U
    {263, 256, 307, 320}, // V
    {307, 256, 370, 320}, // W
    {370, 256, 414, 320}, // X
    {414, 256, 453, 320}, // Y
    {453, 256, 497, 320}, // Z
    {0, 320, 29, 384},    //[
    {29, 320, 58, 384},   //"\"
    {59, 320, 87, 384},   //]
    {87, 320, 139, 384},  //^
    {139, 320, 180, 384}, //_
    {180, 320, 221, 384}, //`
    {221, 320, 259, 384}, // a
    {259, 320, 299, 384}, // b
    {299, 320, 332, 384}, // c
    {332, 320, 372, 384}, // d
    {372, 320, 411, 384}, // e
    {411, 320, 433, 384}, // f
    {435, 320, 473, 384}, // g
    {0, 384, 40, 448},    // h
    {40, 384, 56, 448},   // i
    {58, 384, 80, 448},   // j
    {80, 384, 118, 448},  // k
    {118, 384, 135, 448}, // l
    {135, 384, 197, 448}, // m
    {197, 384, 238, 448}, // n
    {238, 384, 277, 448}, // o
    {277, 384, 317, 448}, // p
    {317, 384, 356, 448}, // q
    {357, 384, 384, 448}, // r
    {385, 384, 417, 448}, // s
    {417, 384, 442, 448}, // t
    {443, 384, 483, 448}, // u
    {0, 448, 38, 512},    // v
    {38, 448, 90, 512},   // w
    {90, 448, 128, 512},  // x
    {128, 448, 166, 512}, // y
    {166, 448, 200, 512}, // z
    {200, 448, 241, 512}, //{
    {241, 448, 270, 512}, //|
    {270, 448, 310, 512}, //}
    {310, 448, 363, 512}, //~
};

int
text_width(OFString *string)
{
	@autoreleasepool {
		const char *str = string.UTF8String;





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







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
// rendertext.cpp: based on Don's gl_text.cpp

#include "cube.h"

short char_coords[96][4] = {
	{ 0, 0, 25, 64 },       //!
	{ 25, 0, 54, 64 },      //"
	{ 54, 0, 107, 64 },     // #
	{ 107, 0, 148, 64 },    //$
	{ 148, 0, 217, 64 },    //%
	{ 217, 0, 263, 64 },    //&
	{ 263, 0, 280, 64 },    //'
	{ 280, 0, 309, 64 },    //(
	{ 309, 0, 338, 64 },    //)
	{ 338, 0, 379, 64 },    //*
	{ 379, 0, 432, 64 },    //+
	{ 432, 0, 455, 64 },    //,
	{ 455, 0, 484, 64 },    //-
	{ 0, 64, 21, 128 },     //.
	{ 23, 64, 52, 128 },    ///
	{ 52, 64, 93, 128 },    // 0
	{ 93, 64, 133, 128 },   // 1
	{ 133, 64, 174, 128 },  // 2
	{ 174, 64, 215, 128 },  // 3
	{ 215, 64, 256, 128 },  // 4
	{ 256, 64, 296, 128 },  // 5
	{ 296, 64, 337, 128 },  // 6
	{ 337, 64, 378, 128 },  // 7
	{ 378, 64, 419, 128 },  // 8
	{ 419, 64, 459, 128 },  // 9
	{ 459, 64, 488, 128 },  //:
	{ 0, 128, 29, 192 },    //;
	{ 29, 128, 81, 192 },   //<
	{ 81, 128, 134, 192 },  //=
	{ 134, 128, 186, 192 }, //>
	{ 186, 128, 221, 192 }, //?
	{ 221, 128, 285, 192 }, //@
	{ 285, 128, 329, 192 }, // A
	{ 329, 128, 373, 192 }, // B
	{ 373, 128, 418, 192 }, // C
	{ 418, 128, 467, 192 }, // D
	{ 0, 192, 40, 256 },    // E
	{ 40, 192, 77, 256 },   // F
	{ 77, 192, 127, 256 },  // G
	{ 127, 192, 175, 256 }, // H
	{ 175, 192, 202, 256 }, // I
	{ 202, 192, 231, 256 }, // J
	{ 231, 192, 275, 256 }, // K
	{ 275, 192, 311, 256 }, // L
	{ 311, 192, 365, 256 }, // M
	{ 365, 192, 413, 256 }, // N
	{ 413, 192, 463, 256 }, // O
	{ 1, 256, 38, 320 },    // P
	{ 38, 256, 89, 320 },   // Q
	{ 89, 256, 133, 320 },  // R
	{ 133, 256, 176, 320 }, // S
	{ 177, 256, 216, 320 }, // T
	{ 217, 256, 263, 320 }, // U
	{ 263, 256, 307, 320 }, // V
	{ 307, 256, 370, 320 }, // W
	{ 370, 256, 414, 320 }, // X
	{ 414, 256, 453, 320 }, // Y
	{ 453, 256, 497, 320 }, // Z
	{ 0, 320, 29, 384 },    //[
	{ 29, 320, 58, 384 },   //"\"
	{ 59, 320, 87, 384 },   //]
	{ 87, 320, 139, 384 },  //^
	{ 139, 320, 180, 384 }, //_
	{ 180, 320, 221, 384 }, //`
	{ 221, 320, 259, 384 }, // a
	{ 259, 320, 299, 384 }, // b
	{ 299, 320, 332, 384 }, // c
	{ 332, 320, 372, 384 }, // d
	{ 372, 320, 411, 384 }, // e
	{ 411, 320, 433, 384 }, // f
	{ 435, 320, 473, 384 }, // g
	{ 0, 384, 40, 448 },    // h
	{ 40, 384, 56, 448 },   // i
	{ 58, 384, 80, 448 },   // j
	{ 80, 384, 118, 448 },  // k
	{ 118, 384, 135, 448 }, // l
	{ 135, 384, 197, 448 }, // m
	{ 197, 384, 238, 448 }, // n
	{ 238, 384, 277, 448 }, // o
	{ 277, 384, 317, 448 }, // p
	{ 317, 384, 356, 448 }, // q
	{ 357, 384, 384, 448 }, // r
	{ 385, 384, 417, 448 }, // s
	{ 417, 384, 442, 448 }, // t
	{ 443, 384, 483, 448 }, // u
	{ 0, 448, 38, 512 },    // v
	{ 38, 448, 90, 512 },   // w
	{ 90, 448, 128, 512 },  // x
	{ 128, 448, 166, 512 }, // y
	{ 166, 448, 200, 512 }, // z
	{ 200, 448, 241, 512 }, //{
	{ 241, 448, 270, 512 }, //|
	{ 270, 448, 310, 512 }, //}
	{ 310, 448, 363, 512 }, //~
};

int
text_width(OFString *string)
{
	@autoreleasepool {
		const char *str = string.UTF8String;

Modified src/rndmap.mm from [db18b3b402] to [3e8d55fcae].

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


89
void
perlinarea(block &b, int scale, int seed, int psize)
{
	srand(seed);
	seed = rnd(10000);
	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++) {
			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;
		};


}







|











<
>
>

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

88
89
90
void
perlinarea(block &b, int scale, int seed, int psize)
{
	srand(seed);
	seed = rnd(10000);
	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++) {
			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.mm from [d7af13e4bd] to [518f13ae58].

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
void
stop()
{
	if (f) {
		if (demorecording)
			gzputi(-1);
		gzclose(f);
	};

	f = NULL;
	demorecording = false;
	demoplayback = false;
	demoloading = false;
	loopv(playerhistory) zapdynent(playerhistory[i]);
	playerhistory.setsize(0);
}







<
>







67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
void
stop()
{
	if (f) {
		if (demorecording)
			gzputi(-1);
		gzclose(f);

	}
	f = NULL;
	demorecording = false;
	demoplayback = false;
	demoloading = false;
	loopv(playerhistory) zapdynent(playerhistory[i]);
	playerhistory.setsize(0);
}

Modified src/server.mm from [2b7d5c9bfe] to [8f47418a23].

16
17
18
19
20
21
22
23
24

25
26
27
28
29
30
31
};

vector<client> clients;

int maxclients = 8;
static OFString *smapname;

struct server_entity // server side version of "entity" type
{

	bool spawned;
	int spawnsecs;
};

vector<server_entity> sents;

bool notgotitems =







|
<
>







16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
31
};

vector<client> clients;

int maxclients = 8;
static OFString *smapname;

// server side version of "entity" type

struct server_entity {
	bool spawned;
	int spawnsecs;
};

vector<server_entity> sents;

bool notgotitems =
64
65
66
67
68
69
70
71

72
73
74
75
76

77
78
79
80
81
82
83
	if (!packet)
		return;
	switch (clients[n].type) {
	case ST_TCPIP: {
		enet_peer_send(clients[n].peer, 0, packet);
		bsend += packet->dataLength;
		break;
	};


	case ST_LOCAL:
		localservertoclient(packet->data, packet->dataLength);
		break;
	};

}

void
send2(bool rel, int cn, int a, int b)
{
	ENetPacket *packet =
	    enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);







<
>




<
>







64
65
66
67
68
69
70

71
72
73
74
75

76
77
78
79
80
81
82
83
	if (!packet)
		return;
	switch (clients[n].type) {
	case ST_TCPIP: {
		enet_peer_send(clients[n].peer, 0, packet);
		bsend += packet->dataLength;
		break;

	}

	case ST_LOCAL:
		localservertoclient(packet->data, packet->dataLength);
		break;

	}
}

void
send2(bool rel, int cn, int a, int b)
{
	ENetPacket *packet =
	    enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0);
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147
{
	if (i >= (uint)sents.length())
		return;
	if (sents[i].spawned) {
		sents[i].spawned = false;
		sents[i].spawnsecs = sec;
		send2(true, sender, SV_ITEMACC, i);
	};

}

void
resetvotes()
{
	loopv(clients) clients[i].mapvote[0] = 0;
}







<
>







133
134
135
136
137
138
139

140
141
142
143
144
145
146
147
{
	if (i >= (uint)sents.length())
		return;
	if (sents[i].spawned) {
		sents[i].spawned = false;
		sents[i].spawnsecs = sec;
		send2(true, sender, SV_ITEMACC, i);

	}
}

void
resetvotes()
{
	loopv(clients) clients[i].mapvote[0] = 0;
}
186
187
188
189
190
191
192
193

194
195
196
197
198
199
200
void
process(ENetPacket *packet, int sender) // sender may be -1
{
	if (ENET_NET_TO_HOST_16(*(ushort *)packet->data) !=
	    packet->dataLength) {
		disconnect_client(sender, "packet length");
		return;
	};


	uchar *end = packet->data + packet->dataLength;
	uchar *p = packet->data + 2;
	char text[MAXTRANS];
	int cn = -1, type;

	while (p < end)







<
>







186
187
188
189
190
191
192

193
194
195
196
197
198
199
200
void
process(ENetPacket *packet, int sender) // sender may be -1
{
	if (ENET_NET_TO_HOST_16(*(ushort *)packet->data) !=
	    packet->dataLength) {
		disconnect_client(sender, "packet length");
		return;

	}

	uchar *end = packet->data + packet->dataLength;
	uchar *p = packet->data + 2;
	char text[MAXTRANS];
	int cn = -1, type;

	while (p < end)
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
257
258
259
260
261
262
263

264
265
266
267
268
269
270
			break;
		}

		case SV_ITEMLIST: {
			int n;
			while ((n = getint(p)) != -1)
				if (notgotitems) {
					server_entity se = {false, 0};
					while (sents.length() <= n)
						sents.add(se);
					sents[n].spawned = true;
				};

			notgotitems = false;
			break;
		}

		case SV_ITEMPICKUP: {
			int n = getint(p);
			pickup(n, getint(p), sender);
			break;
		}

		case SV_PING:
			send2(false, cn, SV_PONG, getint(p));
			break;

		case SV_POS: {
			cn = getint(p);
			if (cn < 0 || cn >= clients.length() ||
			    clients[cn].type == ST_EMPTY) {
				disconnect_client(sender, "client num");
				return;
			};

			int size = msgsizelookup(type);
			assert(size != -1);
			loopi(size - 2) getint(p);
			break;
		}

		case SV_SENDMAP: {







|



<
>




















<
>







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
257
258
259
260
261
262

263
264
265
266
267
268
269
270
			break;
		}

		case SV_ITEMLIST: {
			int n;
			while ((n = getint(p)) != -1)
				if (notgotitems) {
					server_entity se = { false, 0 };
					while (sents.length() <= n)
						sents.add(se);
					sents[n].spawned = true;

				}
			notgotitems = false;
			break;
		}

		case SV_ITEMPICKUP: {
			int n = getint(p);
			pickup(n, getint(p), sender);
			break;
		}

		case SV_PING:
			send2(false, cn, SV_PONG, getint(p));
			break;

		case SV_POS: {
			cn = getint(p);
			if (cn < 0 || cn >= clients.length() ||
			    clients[cn].type == ST_EMPTY) {
				disconnect_client(sender, "client num");
				return;

			}
			int size = msgsizelookup(type);
			assert(size != -1);
			loopi(size - 2) getint(p);
			break;
		}

		case SV_SENDMAP: {
282
283
284
285
286
287
288
289

290
291
292
293
294
295
296

297
298
299


300
301
302
303
304

305
306
307
308
309
310
311

		case SV_EXT: // allows for new features that require no server
		             // updates
		{
			for (int n = getint(p); n; n--)
				getint(p);
			break;
		};


		default: {
			int size = msgsizelookup(type);
			if (size == -1) {
				disconnect_client(sender, "tag type");
				return;
			};

			loopi(size - 1) getint(p);
		};
		};



	if (p > end) {
		disconnect_client(sender, "end of packet");
		return;
	};

	multicast(packet, sender);
}

void
send_welcome(int n)
{
	ENetPacket *packet =







<
>






<
>

<
<
>
>




<
>







282
283
284
285
286
287
288

289
290
291
292
293
294
295

296
297


298
299
300
301
302
303

304
305
306
307
308
309
310
311

		case SV_EXT: // allows for new features that require no server
		             // updates
		{
			for (int n = getint(p); n; n--)
				getint(p);
			break;

		}

		default: {
			int size = msgsizelookup(type);
			if (size == -1) {
				disconnect_client(sender, "tag type");
				return;

			}
			loopi(size - 1) getint(p);


		}
		}

	if (p > end) {
		disconnect_client(sender, "end of packet");
		return;

	}
	multicast(packet, sender);
}

void
send_welcome(int n)
{
	ENetPacket *packet =
361
362
363
364
365
366
367
368

369
370
371
372
373
374
375

void
checkintermission()
{
	if (!minremain) {
		interm = lastsec + 10;
		mapend = lastsec + 1000;
	};

	send2(true, -1, SV_TIMEUP, minremain--);
}

void
startintermission()
{
	minremain = 0;







<
>







361
362
363
364
365
366
367

368
369
370
371
372
373
374
375

void
checkintermission()
{
	if (!minremain) {
		interm = lastsec + 10;
		mapend = lastsec + 1000;

	}
	send2(true, -1, SV_TIMEUP, minremain--);
}

void
startintermission()
{
	minremain = 0;
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
	loopv(sents) // spawn entities when timer reached
	{
		if (sents[i].spawnsecs &&
		    (sents[i].spawnsecs -= seconds - lastsec) <= 0) {
			sents[i].spawnsecs = 0;
			sents[i].spawned = true;
			send2(true, -1, SV_ITEMSPAWN, i);
		};

	}

	lastsec = seconds;

	if ((mode > 1 || (mode == 0 && nonlocalclients)) &&
	    seconds > mapend - minremain * 60)
		checkintermission();
	if (interm && seconds > interm) {
		interm = 0;
		loopv(clients) if (clients[i].type != ST_EMPTY)
		{
			send2(true, i, SV_MAPRELOAD,
			    0); // ask a client to trigger map reload
			mapreload = true;
			break;
		}
	};


	resetserverifempty();

	if (!isdedicated)
		return; // below is network only

	int numplayers = 0;







<
>
















<
>







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
	loopv(sents) // spawn entities when timer reached
	{
		if (sents[i].spawnsecs &&
		    (sents[i].spawnsecs -= seconds - lastsec) <= 0) {
			sents[i].spawnsecs = 0;
			sents[i].spawned = true;
			send2(true, -1, SV_ITEMSPAWN, i);

		}
	}

	lastsec = seconds;

	if ((mode > 1 || (mode == 0 && nonlocalclients)) &&
	    seconds > mapend - minremain * 60)
		checkintermission();
	if (interm && seconds > interm) {
		interm = 0;
		loopv(clients) if (clients[i].type != ST_EMPTY)
		{
			send2(true, i, SV_MAPRELOAD,
			    0); // ask a client to trigger map reload
			mapreload = true;
			break;
		}

	}

	resetserverifempty();

	if (!isdedicated)
		return; // below is network only

	int numplayers = 0;
444
445
446
447
448
449
450
451

452
453
454
455
456
457
458
		laststatus = seconds;
		if (nonlocalclients || bsend || brec)
			printf("status: %d remote clients, %.1f send, %.1f rec "
			       "(K/sec)\n",
			    nonlocalclients, bsend / 60.0f / 1024,
			    brec / 60.0f / 1024);
		bsend = brec = 0;
	};


	ENetEvent event;
	if (enet_host_service(serverhost, &event, timeout) > 0) {
		switch (event.type) {
		case ENET_EVENT_TYPE_CONNECT: {
			client &c = addclient();
			c.type = ST_TCPIP;







<
>







444
445
446
447
448
449
450

451
452
453
454
455
456
457
458
		laststatus = seconds;
		if (nonlocalclients || bsend || brec)
			printf("status: %d remote clients, %.1f send, %.1f rec "
			       "(K/sec)\n",
			    nonlocalclients, bsend / 60.0f / 1024,
			    brec / 60.0f / 1024);
		bsend = brec = 0;

	}

	ENetEvent event;
	if (enet_host_service(serverhost, &event, timeout) > 0) {
		switch (event.type) {
		case ENET_EVENT_TYPE_CONNECT: {
			client &c = addclient();
			c.type = ST_TCPIP;
480
481
482
483
484
485
486
487
488

489
490
491
492

493
494
495
496
497
498
499
				break;
			printf("disconnected client (%s)\n",
			    clients[(intptr_t)event.peer->data].hostname);
			clients[(intptr_t)event.peer->data].type = ST_EMPTY;
			send2(true, -1, SV_CDIS, (intptr_t)event.peer->data);
			event.peer->data = (void *)-1;
			break;
		};


		if (numplayers > maxclients) {
			disconnect_client(lastconnect, "maxclients reached");
		};
	};

#ifndef _WIN32
	fflush(stdout);
#endif
}

void
cleanupserver()







<
|
>
|

<
<
>







480
481
482
483
484
485
486

487
488
489
490


491
492
493
494
495
496
497
498
				break;
			printf("disconnected client (%s)\n",
			    clients[(intptr_t)event.peer->data].hostname);
			clients[(intptr_t)event.peer->data].type = ST_EMPTY;
			send2(true, -1, SV_CDIS, (intptr_t)event.peer->data);
			event.peer->data = (void *)-1;
			break;

		}

		if (numplayers > maxclients)
			disconnect_client(lastconnect, "maxclients reached");


	}
#ifndef _WIN32
	fflush(stdout);
#endif
}

void
cleanupserver()
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546

547
548
549
550
551
552
553
554
555
556
557

{
	serverpassword = passwd;
	maxclients = maxcl;
	servermsinit(master ? master : @"wouter.fov120.com/cube/masterserver/",
	    sdesc, dedicated);

	if (isdedicated = dedicated) {
		ENetAddress address = {ENET_HOST_ANY, CUBE_SERVER_PORT};
		@autoreleasepool {
			if (ip.length > 0 &&
			    enet_address_set_host(&address, ip.UTF8String) < 0)
				printf("WARNING: server ip not resolved");
		}
		serverhost = enet_host_create(&address, MAXCLIENTS, 0, uprate);
		if (!serverhost)
			fatal(@"could not create server host\n");
		loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1;
	}

	resetserverifempty();

	if (isdedicated) // do not return, this becomes main loop
	{

#ifdef _WIN32
		SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#endif
		printf("dedicated server started, waiting for "
		       "clients...\nCtrl-C to exit\n\n");
		atexit(cleanupserver);
		atexit(enet_deinitialize);
		for (;;)
			serverslice(/*enet_time_get_sec()*/ time(NULL), 5);
	};
}








|













|
<
>









<
|
>
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544

545
546
547
548
549
550
551
552
553
554

555
556
{
	serverpassword = passwd;
	maxclients = maxcl;
	servermsinit(master ? master : @"wouter.fov120.com/cube/masterserver/",
	    sdesc, dedicated);

	if (isdedicated = dedicated) {
		ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT };
		@autoreleasepool {
			if (ip.length > 0 &&
			    enet_address_set_host(&address, ip.UTF8String) < 0)
				printf("WARNING: server ip not resolved");
		}
		serverhost = enet_host_create(&address, MAXCLIENTS, 0, uprate);
		if (!serverhost)
			fatal(@"could not create server host\n");
		loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1;
	}

	resetserverifempty();

	// do not return, this becomes main loop

	if (isdedicated) {
#ifdef _WIN32
		SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#endif
		printf("dedicated server started, waiting for "
		       "clients...\nCtrl-C to exit\n\n");
		atexit(cleanupserver);
		atexit(enet_deinitialize);
		for (;;)
			serverslice(/*enet_time_get_sec()*/ time(NULL), 5);

	}
}

Modified src/serverbrowser.mm from [4bf1a6646d] to [7dc044a533].

32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55
		if (resolverqueries.empty()) {
			SDL_UnlockMutex(resolvermutex);
			continue;
		}
		rt->query = resolverqueries.pop();
		rt->starttime = lastmillis;
		SDL_UnlockMutex(resolvermutex);
		ENetAddress address = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
		enet_address_set_host(&address, rt->query);
		SDL_LockMutex(resolvermutex);
		resolverresult &rr = resolverresults.add();
		rr.query = rt->query;
		rr.address = address;
		rt->query = NULL;
		rt->starttime = 0;
		SDL_UnlockMutex(resolvermutex);
	};

	return 0;
}

void
resolverinit(int threads, int limit)
{
	resolverlimit = limit;







|








<
>







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

48
49
50
51
52
53
54
55
		if (resolverqueries.empty()) {
			SDL_UnlockMutex(resolvermutex);
			continue;
		}
		rt->query = resolverqueries.pop();
		rt->starttime = lastmillis;
		SDL_UnlockMutex(resolvermutex);
		ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
		enet_address_set_host(&address, rt->query);
		SDL_LockMutex(resolvermutex);
		resolverresult &rr = resolverresults.add();
		rr.query = rt->query;
		rr.address = address;
		rt->query = NULL;
		rt->starttime = 0;
		SDL_UnlockMutex(resolvermutex);

	}
	return 0;
}

void
resolverinit(int threads, int limit)
{
	resolverlimit = limit;
121
122
123
124
125
126
127
128
129


130
131
132
133
134
135
136
		resolverthread &rt = resolverthreads[i];
		if (rt.query) {
			if (lastmillis - rt.starttime > resolverlimit) {
				resolverstop(rt, true);
				*name = rt.query;
				SDL_UnlockMutex(resolvermutex);
				return true;
			};
		};


	}
	SDL_UnlockMutex(resolvermutex);
	return false;
}

struct serverinfo {
	string name;







<
<
>
>







121
122
123
124
125
126
127


128
129
130
131
132
133
134
135
136
		resolverthread &rt = resolverthreads[i];
		if (rt.query) {
			if (lastmillis - rt.starttime > resolverlimit) {
				resolverstop(rt, true);
				*name = rt.query;
				SDL_UnlockMutex(resolvermutex);
				return true;


			}
		}
	}
	SDL_UnlockMutex(resolvermutex);
	return false;
}

struct serverinfo {
	string name;
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
	lastinfo = lastmillis;
}

void
checkresolver()
{
	char *name = NULL;
	ENetAddress addr = {ENET_HOST_ANY, CUBE_SERVINFO_PORT};
	while (resolvercheck(&name, &addr)) {
		if (addr.host == ENET_HOST_ANY)
			continue;
		loopv(servers)
		{
			serverinfo &si = servers[i];
			if (name == si.name) {







|







195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
	lastinfo = lastmillis;
}

void
checkresolver()
{
	char *name = NULL;
	ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT };
	while (resolvercheck(&name, &addr)) {
		if (addr.host == ENET_HOST_ANY)
			continue;
		loopv(servers)
		{
			serverinfo &si = servers[i];
			if (name == si.name) {
301
302
303
304
305
306
307
308

309
310
311
312
313
314
315

void
servermenu()
{
	if (pingsock == ENET_SOCKET_NULL) {
		pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
		resolverinit(1, 1000);
	};

	resolverclear();
	loopv(servers) resolverquery(servers[i].name);
	refreshservers();
	menuset(1);
}

void







<
>







301
302
303
304
305
306
307

308
309
310
311
312
313
314
315

void
servermenu()
{
	if (pingsock == ENET_SOCKET_NULL) {
		pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL);
		resolverinit(1, 1000);

	}
	resolverclear();
	loopv(servers) resolverquery(servers[i].name);
	refreshservers();
	menuset(1);
}

void

Modified src/serverms.mm from [21c02ed323] to [1d1c1a8ddd].

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
	enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
	if (enet_socket_wait(mssock, &events, 0) >= 0 && events) {
		int len = enet_socket_receive(mssock, NULL, &buf, 1);
		if (len <= 0) {
			enet_socket_destroy(mssock);
			mssock = ENET_SOCKET_NULL;
			return;
		};

		buf.data = ((char *)buf.data) + len;
		((char *)buf.data)[0] = 0;
		buf.dataLength -= len;
	};

}

uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");
	if (!s)
		s = strstr((char *)b, "\n\n");
	return s ? (uchar *)s : b;
}

ENetAddress masterserver = {ENET_HOST_ANY, 80};
int updmaster = 0;
string masterbase;
string masterpath;
uchar masterrep[MAXTRANS];
ENetBuffer masterb;

void
updatemasterserver(int seconds)
{
	if (seconds >
	    updmaster) // send alive signal to masterserver every hour of uptime
	{

		sprintf_sd(path)("%sregister.do?action=add", masterpath);
		httpgetsend(masterserver, masterbase, path, "cubeserver",
		    "Cube Server");
		masterrep[0] = 0;
		masterb.data = masterrep;
		masterb.dataLength = MAXTRANS - 1;
		updmaster = seconds + 60 * 60;
	};

}

void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(masterb);







<
>



<
>











|









<
|
<
>







<
>







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
	enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE;
	if (enet_socket_wait(mssock, &events, 0) >= 0 && events) {
		int len = enet_socket_receive(mssock, NULL, &buf, 1);
		if (len <= 0) {
			enet_socket_destroy(mssock);
			mssock = ENET_SOCKET_NULL;
			return;

		}
		buf.data = ((char *)buf.data) + len;
		((char *)buf.data)[0] = 0;
		buf.dataLength -= len;

	}
}

uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");
	if (!s)
		s = strstr((char *)b, "\n\n");
	return s ? (uchar *)s : b;
}

ENetAddress masterserver = { ENET_HOST_ANY, 80 };
int updmaster = 0;
string masterbase;
string masterpath;
uchar masterrep[MAXTRANS];
ENetBuffer masterb;

void
updatemasterserver(int seconds)
{

	// send alive signal to masterserver every hour of uptime

	if (seconds > updmaster) {
		sprintf_sd(path)("%sregister.do?action=add", masterpath);
		httpgetsend(masterserver, masterbase, path, "cubeserver",
		    "Cube Server");
		masterrep[0] = 0;
		masterb.data = masterrep;
		masterb.dataLength = MAXTRANS - 1;
		updmaster = seconds + 60 * 60;

	}
}

void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(masterb);
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
		if (!mid)
			mid = master;
		strcpy_s(masterpath, mid);
		strn0cpy(masterbase, master, mid - master + 1);
		serverdesc = sdesc;

		if (listen) {
			ENetAddress address = {
			    ENET_HOST_ANY, CUBE_SERVINFO_PORT};
			pongsock = enet_socket_create(
			    ENET_SOCKET_TYPE_DATAGRAM, &address);
			if (pongsock == ENET_SOCKET_NULL)
				fatal(@"could not create server info socket\n");
		}
	}
}







|
|







154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
		if (!mid)
			mid = master;
		strcpy_s(masterpath, mid);
		strn0cpy(masterbase, master, mid - master + 1);
		serverdesc = sdesc;

		if (listen) {
			ENetAddress address = { ENET_HOST_ANY,
				CUBE_SERVINFO_PORT };
			pongsock = enet_socket_create(
			    ENET_SOCKET_TYPE_DATAGRAM, &address);
			if (pongsock == ENET_SOCKET_NULL)
				fatal(@"could not create server info socket\n");
		}
	}
}

Modified src/serverutil.mm from [9a3fdd3f13] to [34361918f4].

16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
		*p++ = n >> 8;
	} else {
		*p++ = 0x81;
		*p++ = n;
		*p++ = n >> 8;
		*p++ = n >> 16;
		*p++ = n >> 24;
	};

}

int
getint(uchar *&p)
{
	int c = *((char *)p);
	p++;







<
>







16
17
18
19
20
21
22

23
24
25
26
27
28
29
30
		*p++ = n >> 8;
	} else {
		*p++ = 0x81;
		*p++ = n;
		*p++ = n >> 8;
		*p++ = n >> 16;
		*p++ = n >> 24;

	}
}

int
getint(uchar *&p)
{
	int c = *((char *)p);
	p++;
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
		while (*t)
			putint(p, *t++);
		putint(p, 0);
	}
}

static const OFString *modenames[] = {
    @"SP",
    @"DMSP",
    @"ffa/default",
    @"coopedit",
    @"ffa/duel",
    @"teamplay",
    @"instagib",
    @"instagib team",
    @"efficiency",
    @"efficiency team",
    @"insta arena",
    @"insta clan arena",
    @"tactics arena",
    @"tactics clan arena",
};

OFString *
modestr(int n)
{
	return (n >= -2 && n < 12) ? modenames[n + 2] : @"unknown";
}

char msgsizesl[] = // size inclusive message token, 0 for variable or
                   // not-checked sizes
    {SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS,
        2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6,
        SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8, SV_FRAGS, 2, SV_MAPCHANGE, 0,
        SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3, SV_DENIED, 2, SV_PING, 2, SV_PONG, 2,
        SV_CLIENTPING, 2, SV_GAMEMODE, 2, SV_TIMEUP, 2, SV_EDITENT, 10,
        SV_MAPRELOAD, 2, SV_ITEMACC, 2, SV_SENDMAP, 0, SV_RECVMAP, 1,
        SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_EXT, 0, -1};


char
msgsizelookup(int msg)
{
	for (char *p = msgsizesl; *p >= 0; p += 2)
		if (*p == msg)
			return p[1];







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







<
|
<
|
|
|
|
|
|
|
>







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
		while (*t)
			putint(p, *t++);
		putint(p, 0);
	}
}

static const OFString *modenames[] = {
	@"SP",
	@"DMSP",
	@"ffa/default",
	@"coopedit",
	@"ffa/duel",
	@"teamplay",
	@"instagib",
	@"instagib team",
	@"efficiency",
	@"efficiency team",
	@"insta arena",
	@"insta clan arena",
	@"tactics arena",
	@"tactics clan arena",
};

OFString *
modestr(int n)
{
	return (n >= -2 && n < 12) ? modenames[n + 2] : @"unknown";
}

// size inclusive message token, 0 for variable or not-checked sizes

char msgsizesl[] = { SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0,
	SV_SOUND, 2, SV_CDIS, 2, SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6,
	SV_EDITD, 6, SV_EDITE, 6, SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8,
	SV_FRAGS, 2, SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3,
	SV_DENIED, 2, SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2,
	SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_ITEMACC, 2,
	SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_EXT, 0,
	-1 };

char
msgsizelookup(int msg)
{
	for (char *p = msgsizesl; *p >= 0; p += 2)
		if (*p == msg)
			return p[1];
183
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
				passwd = a;
				break;
			case 'c':
				maxcl = atoi(a);
				break;
			default:
				printf("WARNING: unknown commandline option\n");
			};
	};



	if (enet_initialize() < 0)
		fatal(@"Unable to initialise network module");
	initserver(true, uprate, @(sdesc), @(ip), @(master), @(passwd), maxcl);
	return 0;
}
#endif







<
<
>
>







182
183
184
185
186
187
188


189
190
191
192
193
194
195
196
197
				passwd = a;
				break;
			case 'c':
				maxcl = atoi(a);
				break;
			default:
				printf("WARNING: unknown commandline option\n");


			}
	}

	if (enet_initialize() < 0)
		fatal(@"Unable to initialise network module");
	initserver(true, uprate, @(sdesc), @(ip), @(master), @(passwd), maxcl);
	return 0;
}
#endif

Modified src/sound.mm from [af1eb7e101] to [7dfddc04de].

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
#ifdef USE_MIXER
		Mix_HaltMusic();
		Mix_FreeMusic(mod);
#else
		FMUSIC_FreeSong(mod);
#endif
		mod = NULL;
	};

	if (stream) {
#ifndef USE_MIXER
		FSOUND_Stream_Close(stream);
#endif
		stream = NULL;
	};

}

VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);
#ifdef USE_MIXER
	if (Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen) <
	    0) {
		conoutf(@"sound init failed (SDL_mixer): %s",
		    (size_t)Mix_GetError());
		nosound = true;
	};

	Mix_AllocateChannels(MAXCHAN);
#else
	if (FSOUND_GetVersion() < FMOD_VERSION)
		fatal(@"old FMOD dll");
	if (!FSOUND_Init(SOUNDFREQ, MAXCHAN, FSOUND_INIT_GLOBALFOCUS)) {
		conoutf(@"sound init failed (FMOD): %d", FSOUND_GetError());
		nosound = true;







<
>





<
>














<
>







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
#ifdef USE_MIXER
		Mix_HaltMusic();
		Mix_FreeMusic(mod);
#else
		FMUSIC_FreeSong(mod);
#endif
		mod = NULL;

	}
	if (stream) {
#ifndef USE_MIXER
		FSOUND_Stream_Close(stream);
#endif
		stream = NULL;

	}
}

VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);
#ifdef USE_MIXER
	if (Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen) <
	    0) {
		conoutf(@"sound init failed (SDL_mixer): %s",
		    (size_t)Mix_GetError());
		nosound = true;

	}
	Mix_AllocateChannels(MAXCHAN);
#else
	if (FSOUND_GetVersion() < FMOD_VERSION)
		fatal(@"old FMOD dll");
	if (!FSOUND_Init(SOUNDFREQ, MAXCHAN, FSOUND_INIT_GLOBALFOCUS)) {
		conoutf(@"sound init failed (FMOD): %d", FSOUND_GetError());
		nosound = true;
178
179
180
181
182
183
184
185
186


187
188
189
190
191
192
193
			float yaw = -atan2(v.x, v.y) -
			    player1->yaw *
			        (PI / 180.0f); // relative angle of
			                       // sound along X-Y axis
			pan = int(255.9f *
			    (0.5 * sin(yaw) + 0.5f)); // range is from 0 (left)
			                              // to 255 (right)
		};
	};


	vol = (vol * MAXVOL) / 255;
#ifdef USE_MIXER
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);







<
<
>
>







178
179
180
181
182
183
184


185
186
187
188
189
190
191
192
193
			float yaw = -atan2(v.x, v.y) -
			    player1->yaw *
			        (PI / 180.0f); // relative angle of
			                       // sound along X-Y axis
			pan = int(255.9f *
			    (0.5 * sin(yaw) + 0.5f)); // range is from 0 (left)
			                              // to 255 (right)


		}
	}
	vol = (vol * MAXVOL) / 255;
#ifdef USE_MIXER
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);

Modified src/weapon.mm from [93de7b2cbf] to [a095df6286].

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

static const int MONSTERDAMAGEFACTOR = 4;
static const int SGRAYS = 20;
static const float SGSPREAD = 2;
static OFVector3D sg[SGRAYS];

static const guninfo guns[NUMGUNS] = {
    {S_PUNCH1, 250, 50, 0, 0, 1, @"fist"},
    {S_SG, 1400, 10, 0, 0, 20, @"shotgun"}, // *SGRAYS
    {S_CG, 100, 30, 0, 0, 7, @"chaingun"},
    {S_RLFIRE, 800, 120, 80, 0, 10, @"rocketlauncher"},
    {S_RIFLE, 1500, 100, 0, 0, 30, @"rifle"},
    {S_FLAUNCH, 200, 20, 50, 4, 1, @"fireball"},
    {S_ICEBALL, 200, 40, 30, 6, 1, @"iceball"},
    {S_SLIMEBALL, 200, 30, 160, 7, 1, @"slimeball"},
    {S_PIGR1, 250, 50, 0, 0, 1, @"bite"},
};

void
selectgun(int a, int b, int c)
{
	if (a < -1 || b < -1 || c < -1 || a >= NUMGUNS || b >= NUMGUNS ||
	    c >= NUMGUNS)







|
|
|
|
|
|
|
|
|







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

static const int MONSTERDAMAGEFACTOR = 4;
static const int SGRAYS = 20;
static const float SGSPREAD = 2;
static OFVector3D sg[SGRAYS];

static const guninfo guns[NUMGUNS] = {
	{ S_PUNCH1, 250, 50, 0, 0, 1, @"fist" },
	{ S_SG, 1400, 10, 0, 0, 20, @"shotgun" }, // *SGRAYS
	{ S_CG, 100, 30, 0, 0, 7, @"chaingun" },
	{ S_RLFIRE, 800, 120, 80, 0, 10, @"rocketlauncher" },
	{ S_RIFLE, 1500, 100, 0, 0, 30, @"rifle" },
	{ S_FLAUNCH, 200, 20, 50, 4, 1, @"fireball" },
	{ S_ICEBALL, 200, 40, 30, 6, 1, @"iceball" },
	{ S_SLIMEBALL, 200, 30, 160, 7, 1, @"slimeball" },
	{ S_PIGR1, 250, 50, 0, 0, 1, @"bite" },
};

void
selectgun(int a, int b, int c)
{
	if (a < -1 || b < -1 || c < -1 || a >= NUMGUNS || b >= NUMGUNS ||
	    c >= NUMGUNS)
169
170
171
172
173
174
175
176

177
178
179
180
181
182
183
	if (d == player1)
		selfdamage(damage, at == player1 ? -1 : -2, at);
	else if (d->monsterstate)
		monsterpain(d, damage, at);
	else {
		addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence);
		playsound(S_PAIN1 + rnd(5), &d->o);
	};

	particle_splash(3, damage, 1000, d->o);
	demodamage(damage, d->o);
}

const float RL_RADIUS = 5;
const float RL_DAMRAD = 7; // hack








<
>







169
170
171
172
173
174
175

176
177
178
179
180
181
182
183
	if (d == player1)
		selfdamage(damage, at == player1 ? -1 : -2, at);
	else if (d->monsterstate)
		monsterpain(d, damage, at);
	else {
		addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence);
		playsound(S_PAIN1 + rnd(5), &d->o);

	}
	particle_splash(3, damage, 1000, d->o);
	demodamage(damage, d->o);
}

const float RL_RADIUS = 5;
const float RL_DAMRAD = 7; // hack

Modified src/world.mm from [50f4e28f8c] to [e205b06d1f].

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
			if (tag) {
				if (tag == s->tag)
					s->type = SPACE;
				else
					continue;
			} else {
				s->type = type ? SOLID : SPACE;
			};

			if (x > maxx)
				maxx = x;
			if (y > maxy)
				maxy = y;
			if (x < minx)
				minx = x;
			if (y < miny)
				miny = y;
		};
	}

	block b = {minx, miny, maxx - minx + 1, maxy - miny + 1};
	if (maxx)
		remip(b); // remip minimal area of changed geometry
}

void
resettagareas()
{







<
>








<
|
>
|







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
			if (tag) {
				if (tag == s->tag)
					s->type = SPACE;
				else
					continue;
			} else {
				s->type = type ? SOLID : SPACE;

			}
			if (x > maxx)
				maxx = x;
			if (y > maxy)
				maxy = y;
			if (x < minx)
				minx = x;
			if (y < miny)
				miny = y;

		}
	}
	block b = { minx, miny, maxx - minx + 1, maxy - miny + 1 };
	if (maxx)
		remip(b); // remip minimal area of changed geometry
}

void
resettagareas()
{
93
94
95
96
97
98
99
100

101
102
103
104

105
106
107
108
109
110
111
	sqr *v = wmip[level + 1];
	int ws = ssize >> level;
	int vs = ssize >> (level + 1);
	block s = b;
	if (s.x & 1) {
		s.x--;
		s.xs++;
	};

	if (s.y & 1) {
		s.y--;
		s.ys++;
	};

	s.xs = (s.xs + 1) & ~1;
	s.ys = (s.ys + 1) & ~1;
	for (int x = s.x; x < s.x + s.xs; x += 2)
		for (int y = s.y; y < s.y + s.ys; y += 2) {
			sqr *o[4];
			o[0] = SWS(w, x, y, ws); // the 4 constituent cubes
			o[1] = SWS(w, x + 1, y, ws);







<
>



<
>







93
94
95
96
97
98
99

100
101
102
103

104
105
106
107
108
109
110
111
	sqr *v = wmip[level + 1];
	int ws = ssize >> level;
	int vs = ssize >> (level + 1);
	block s = b;
	if (s.x & 1) {
		s.x--;
		s.xs++;

	}
	if (s.y & 1) {
		s.y--;
		s.ys++;

	}
	s.xs = (s.xs + 1) & ~1;
	s.ys = (s.ys + 1) & ~1;
	for (int x = s.x; x < s.x + s.xs; x += 2)
		for (int y = s.y; y < s.y + s.ys; y += 2) {
			sqr *o[4];
			o[0] = SWS(w, x, y, ws); // the 4 constituent cubes
			o[1] = SWS(w, x + 1, y, ws);
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
							ch += o[i]->vdelta / 4 +
							    2; // FIXME: needs
							       // to somehow
							       // take into
							       // account middle
							       // vertices on
							       // higher mips
					};

					if (fh < floor)
						floor =
						    fh; // take lowest floor and
						        // highest ceil, so we
						        // never have to see
						        // missing lower/upper
						        // from the side
					if (ch > ceil)
						ceil = ch;
				}
				r->floor = floor;
				r->ceil = ceil;
			};

			if (r->type == CORNER)
				goto mip; // special case: don't ever split even
				          // if textures etc are different
			r->defer = 1;
			if (SOLID(r)) {
				loopi(3)
				{







<
>












<
>







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
							ch += o[i]->vdelta / 4 +
							    2; // FIXME: needs
							       // to somehow
							       // take into
							       // account middle
							       // vertices on
							       // higher mips

					}
					if (fh < floor)
						floor =
						    fh; // take lowest floor and
						        // highest ceil, so we
						        // never have to see
						        // missing lower/upper
						        // from the side
					if (ch > ceil)
						ceil = ch;
				}
				r->floor = floor;
				r->ceil = ceil;

			}
			if (r->type == CORNER)
				goto mip; // special case: don't ever split even
				          // if textures etc are different
			r->defer = 1;
			if (SOLID(r)) {
				loopi(3)
				{
214
215
216
217
218
219
220
221
222


223
224
225
226
227
228
229
230

231
232
233
234
235
236
237
					            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;
				};
			};


			{
				loopi(4) if (o[i]->defer) goto c;
			}; // if any of the constituents is not perfect, then
			   // this one isn't either
		mip:
			r->defer = 0;
		c:;
		};

	s.x /= 2;
	s.y /= 2;
	s.xs /= 2;
	s.ys /= 2;
	remip(s, level + 1);
}








<
<
>
>


|
|



<
>







214
215
216
217
218
219
220


221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
					            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;


				}
			}
			{
				loopi(4) if (o[i]->defer) goto c;
			} // if any of the constituents is not perfect, then
			  // this one isn't either
		mip:
			r->defer = 0;
		c:;

		}
	s.x /= 2;
	s.y /= 2;
	s.xs /= 2;
	s.ys /= 2;
	remip(s, level + 1);
}

263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
		if (e.type == NOTUSED)
			continue;
		OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
		vdist(dist, t, player1->o, v);
		if (dist < bdist) {
			best = i;
			bdist = dist;
		};

	}
	return bdist == 99999 ? -1 : best;
}

void
entproperty(int prop, int amount)
{







<
>







263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
		if (e.type == NOTUSED)
			continue;
		OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
		vdist(dist, t, player1->o, v);
		if (dist < bdist) {
			best = i;
			bdist = dist;

		}
	}
	return bdist == 99999 ? -1 : best;
}

void
entproperty(int prop, int amount)
{
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
		break;
	case 2:
		ents[e].attr3 += amount;
		break;
	case 3:
		ents[e].attr4 += amount;
		break;
	};

}

void
delent()
{
	int e = closestent();
	if (e < 0) {







<
>







287
288
289
290
291
292
293

294
295
296
297
298
299
300
301
		break;
	case 2:
		ents[e].attr3 += amount;
		break;
	case 3:
		ents[e].attr4 += amount;
		break;

	}
}

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

entity *
newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4)
{
	int type = findtype(what);
	persistent_entity e = {(short)x, (short)y, (short)z, (short)v1,
	    (uchar)type, (uchar)v2, (uchar)v3, (uchar)v4};
	switch (type) {
	case LIGHT:
		if (v1 > 32)
			v1 = 32;
		if (!v1)
			e.attr1 = 16;
		if (!v2 && !v3 && !v4)
			e.attr2 = 255;
		break;

	case MAPMODEL:
		e.attr4 = e.attr3;
		e.attr3 = e.attr2;
	case MONSTER:
	case TELEDEST:
		e.attr2 = (uchar)e.attr1;
	case PLAYERSTART:
		e.attr1 = (int)player1->yaw;
		break;
	};

	addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1,
	    e.attr2, e.attr3, e.attr4);
	ents.add(*((entity *)&e)); // unsafe!
	if (type == LIGHT)
		calclight();
	return &ents.last();
}







|
|



















<
>







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

entity *
newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4)
{
	int type = findtype(what);
	persistent_entity e = { (short)x, (short)y, (short)z, (short)v1,
		(uchar)type, (uchar)v2, (uchar)v3, (uchar)v4 };
	switch (type) {
	case LIGHT:
		if (v1 > 32)
			v1 = 32;
		if (!v1)
			e.attr1 = 16;
		if (!v2 && !v3 && !v4)
			e.attr2 = 255;
		break;

	case MAPMODEL:
		e.attr4 = e.attr3;
		e.attr3 = e.attr2;
	case MONSTER:
	case TELEDEST:
		e.attr2 = (uchar)e.attr1;
	case PLAYERSTART:
		e.attr1 = (int)player1->yaw;
		break;

	}
	addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1,
	    e.attr2, e.attr3, e.attr4);
	ents.add(*((entity *)&e)); // unsafe!
	if (type == LIGHT)
		calclight();
	return &ents.last();
}
395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
			e.attr1 = 2;
		if (e.attr1 > 32)
			e.attr1 = 32;
		if (intens) {
			scalecomp(e.attr2, intens);
			scalecomp(e.attr3, intens);
			scalecomp(e.attr4, intens);
		};

	}
	calclight();
}
COMMAND(scalelights, ARG_2INT)

int
findentity(int type, int index)







<
>







395
396
397
398
399
400
401

402
403
404
405
406
407
408
409
			e.attr1 = 2;
		if (e.attr1 > 32)
			e.attr1 = 32;
		if (intens) {
			scalecomp(e.attr2, intens);
			scalecomp(e.attr3, intens);
			scalecomp(e.attr4, intens);

		}
	}
	calclight();
}
COMMAND(scalelights, ARG_2INT)

int
findentity(int type, int index)
441
442
443
444
445
446
447
448

449
450
451
452
453
454
455
	cleardlights();
	pruneundos();
	sqr *oldworld = world;
	bool copy = false;
	if (oldworld && factor < 0) {
		factor = sfactor + 1;
		copy = true;
	};

	if (factor < SMALLEST_FACTOR)
		factor = SMALLEST_FACTOR;
	if (factor > LARGEST_FACTOR)
		factor = LARGEST_FACTOR;
	setupworld(factor);

	loop(x, ssize) loop(y, ssize)







<
>







441
442
443
444
445
446
447

448
449
450
451
452
453
454
455
	cleardlights();
	pruneundos();
	sqr *oldworld = world;
	bool copy = false;
	if (oldworld && factor < 0) {
		factor = sfactor + 1;
		copy = true;

	}
	if (factor < SMALLEST_FACTOR)
		factor = SMALLEST_FACTOR;
	if (factor > LARGEST_FACTOR)
		factor = LARGEST_FACTOR;
	setupworld(factor);

	loop(x, ssize) loop(y, ssize)
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
		}
	} else {
		strn0cpy(hdr.maptitle, "Untitled Map by Unknown", 128);
		hdr.waterlevel = -100000;
		loopi(15) hdr.reserved[i] = 0;
		loopk(3) loopi(256) hdr.texlists[k][i] = i;
		ents.setsize(0);
		block b = {8, 8, ssize - 16, ssize - 16};
		edittypexy(SPACE, b);
	}

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







|







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
		}
	} else {
		strn0cpy(hdr.maptitle, "Untitled Map by Unknown", 128);
		hdr.waterlevel = -100000;
		loopi(15) hdr.reserved[i] = 0;
		loopk(3) loopi(256) hdr.texlists[k][i] = i;
		ents.setsize(0);
		block b = { 8, 8, ssize - 16, ssize - 16 };
		edittypexy(SPACE, b);
	}

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

Modified src/worldlight.mm from [3bb65afc34] to [2af1475366].

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
137

138
139
140
141

142
143
144
145
146
147
148
				if (SOLID(s))
					return;
				x += stepx;
				y += stepy;
				l -= stepl;
				stepl -= 25;
			}
		};

	} else // the old (white) light code, here for the few people with old
	       // video cards that don't support overbright
	{
		loopi(steps)
		{
			sqr *s = S(x >> PRECBITS, y >> PRECBITS);
			int light = l >> PRECBITS;
			if (light > s->r)
				s->r = s->g = s->b = (uchar)light;
			if (SOLID(s))
				return;
			x += stepx;
			y += stepy;
			l -= stepl;
		}
	};

}

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

	rndreset();

	const float s = 0.8f;

	for (float sx2 = (float)sx; sx2 <= ex; sx2 += s * 2) {
		lightray(sx2, (float)sy, l);
		lightray(sx2, (float)ey, l);
	};

	for (float sy2 = sy + s; sy2 <= ey - s; sy2 += s * 2) {
		lightray((float)sx, sy2, l);
		lightray((float)ex, sy2, l);
	};


	rndtime();
}

void
postlightarea(block &a) // median filter, smooths out random noise in light and
                        // makes it more mipable







<
>















<
>


















<
>



<
>







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

137
138
139
140

141
142
143
144
145
146
147
148
				if (SOLID(s))
					return;
				x += stepx;
				y += stepy;
				l -= stepl;
				stepl -= 25;
			}

		}
	} else // the old (white) light code, here for the few people with old
	       // video cards that don't support overbright
	{
		loopi(steps)
		{
			sqr *s = S(x >> PRECBITS, y >> PRECBITS);
			int light = l >> PRECBITS;
			if (light > s->r)
				s->r = s->g = s->b = (uchar)light;
			if (SOLID(s))
				return;
			x += stepx;
			y += stepy;
			l -= stepl;
		}

	}
}

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

	rndreset();

	const float s = 0.8f;

	for (float sx2 = (float)sx; sx2 <= ex; sx2 += s * 2) {
		lightray(sx2, (float)sy, l);
		lightray(sx2, (float)ey, l);

	}
	for (float sy2 = sy + s; sy2 <= ey - s; sy2 += s * 2) {
		lightray((float)sx, sy2, l);
		lightray((float)ex, sy2, l);

	}

	rndtime();
}

void
postlightarea(block &a) // median filter, smooths out random noise in light and
                        // makes it more mipable
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == LIGHT)
			calclightsource(e);
	}

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

VARP(dynlight, 0, 16, 32);

vector<block *> dlights;







|







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == LIGHT)
			calclightsource(e);
	}

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

VARP(dynlight, 0, 16, 32);

vector<block *> dlights;
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
		reach = reach / 2;
	if (!reach)
		return;
	if (v.x < 0 || v.y < 0 || v.x > ssize || v.y > ssize)
		return;

	int creach = reach + 16; // dependant on lightray random offsets!
	block b = {(int)v.x - creach, (int)v.y - creach, creach * 2 + 1,
	    creach * 2 + 1};

	if (b.x < 1)
		b.x = 1;
	if (b.y < 1)
		b.y = 1;
	if (b.xs + b.x > ssize - 2)
		b.xs = ssize - 2 - b.x;
	if (b.ys + b.y > ssize - 2)
		b.ys = ssize - 2 - b.y;

	dlights.add(blockcopy(b)); // backup area before rendering in dynlight

	persistent_entity l = {(short)v.x, (short)v.y, (short)v.z, (short)reach,
	    LIGHT, (uchar)strength, 0, 0};
	calclightsource(l);
	postlightarea(b);
}

// utility functions also used by editing code

block *







|
|












|
|







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
		reach = reach / 2;
	if (!reach)
		return;
	if (v.x < 0 || v.y < 0 || v.x > ssize || v.y > ssize)
		return;

	int creach = reach + 16; // dependant on lightray random offsets!
	block b = { (int)v.x - creach, (int)v.y - creach, creach * 2 + 1,
		creach * 2 + 1 };

	if (b.x < 1)
		b.x = 1;
	if (b.y < 1)
		b.y = 1;
	if (b.xs + b.x > ssize - 2)
		b.xs = ssize - 2 - b.x;
	if (b.ys + b.y > ssize - 2)
		b.ys = ssize - 2 - b.y;

	dlights.add(blockcopy(b)); // backup area before rendering in dynlight

	persistent_entity l = { (short)v.x, (short)v.y, (short)v.z,
		(short)reach, LIGHT, (uchar)strength, 0, 0 };
	calclightsource(l);
	postlightarea(b);
}

// utility functions also used by editing code

block *

Modified src/worldocull.mm from [7b61604ca1] to [fabee6f557].

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
				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;
				if (SOLID(S(fast_f2nat(sx),
				        fast_f2nat(
				            sy)))) // 90% of time spend in this
				                   // function is on this line
				{

					rdist[i] = (float)(fabs(sx - vx) +
					    fabs(sy - vy));
					break;
				};
			};


		} else {
			rdist[i] = 2;
		};
	}
}

// test occlusion for a cube... one of the most computationally expensive
// functions in the engine as its done for every cube and entity, but its effect
// is more than worth it!








<
>





<
<
|
|
<
>



<
<
>
>
|

<







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

				if (SOLID(S(fast_f2nat(sx), fast_f2nat(sy)))) {
					rdist[i] = (float)(fabs(sx - vx) +
					    fabs(sy - vy));
					break;


				}
			}
		} else
			rdist[i] = 2;

	}
}

// test occlusion for a cube... one of the most computationally expensive
// functions in the engine as its done for every cube and entity, but its effect
// is more than worth it!

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

	int si = fast_f2nat(h * (NUMRAYS / 8)) +
	    NUMRAYS; // get indexes into occlusion map from angles
	int ei = fast_f2nat(l * (NUMRAYS / 8)) + NUMRAYS + 1;
	if (ei <= si)
		ei += NUMRAYS;

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


	return 1; // cube is entirely occluded
}







|
|
<












|
<
>











|



|
<
>








|
|
|
<
>



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

	}
	int si = fast_f2nat(h * (NUMRAYS / 8)) +
	    NUMRAYS; // get indexes into occlusion map from angles
	int ei = fast_f2nat(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.mm from [5690f47554] to [b1342f3226].

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
{
	if (SOLID(o) || o->type == SEMISOLID) {
		float c1 = s->floor;
		float c2 = s->floor;
		if (s->type == FHF) {
			c1 -= d1->vdelta / 4.0f;
			c2 -= d2->vdelta / 4.0f;
		};

		float f1 = s->ceil;
		float f2 = s->ceil;
		if (s->type == CHF) {
			f1 += d1->vdelta / 4.0f;
			f2 += d2->vdelta / 4.0f;
		};

		// if(f1-c1<=0 && f2-c2<=0) return;
		render_square(o->wtex, c1, c2, f1, f2, x1 << mip, y1 << mip,
		    x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);
		return;
	};

	{
		float f1 = s->floor;
		float f2 = s->floor;
		float c1 = o->floor;
		float c2 = o->floor;
		if (o->type == FHF && s->type != FHF) {
			c1 -= d1->vdelta / 4.0f;
			c2 -= d2->vdelta / 4.0f;
		}
		if (s->type == FHF && o->type != FHF) {
			f1 -= d1->vdelta / 4.0f;
			f2 -= d2->vdelta / 4.0f;
		}
		if (f1 >= c1 && f2 >= c2)
			goto skip;
		render_square(o->wtex, f1, f2, c1, c2, x1 << mip, y1 << mip,
		    x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);
	};

skip: {
	float f1 = o->ceil;
	float f2 = o->ceil;
	float c1 = s->ceil;
	float c2 = s->ceil;
	if (o->type == CHF && s->type != CHF) {
		f1 += d1->vdelta / 4.0f;
		f2 += d2->vdelta / 4.0f;
	} else if (s->type == CHF && o->type != CHF) {
		c1 += d1->vdelta / 4.0f;
		c2 += d2->vdelta / 4.0f;
	}
	if (c1 <= f1 && c2 <= f2)
		return;
	render_square(o->utex, f1, f2, c1, c2, x1 << mip, y1 << mip, x2 << mip,
	    y2 << mip, 1 << mip, d1, d2, topleft);
};

}

const int MAX_MIP = 5; // 32x32 unit blocks
const int MIN_LOD = 2;
const int LOW_LOD = 25;
const int MAX_LOD = 1000;








<
>





<
>




<
>

















<
>
















<
>







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
{
	if (SOLID(o) || o->type == SEMISOLID) {
		float c1 = s->floor;
		float c2 = s->floor;
		if (s->type == FHF) {
			c1 -= d1->vdelta / 4.0f;
			c2 -= d2->vdelta / 4.0f;

		}
		float f1 = s->ceil;
		float f2 = s->ceil;
		if (s->type == CHF) {
			f1 += d1->vdelta / 4.0f;
			f2 += d2->vdelta / 4.0f;

		}
		// if(f1-c1<=0 && f2-c2<=0) return;
		render_square(o->wtex, c1, c2, f1, f2, x1 << mip, y1 << mip,
		    x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);
		return;

	}
	{
		float f1 = s->floor;
		float f2 = s->floor;
		float c1 = o->floor;
		float c2 = o->floor;
		if (o->type == FHF && s->type != FHF) {
			c1 -= d1->vdelta / 4.0f;
			c2 -= d2->vdelta / 4.0f;
		}
		if (s->type == FHF && o->type != FHF) {
			f1 -= d1->vdelta / 4.0f;
			f2 -= d2->vdelta / 4.0f;
		}
		if (f1 >= c1 && f2 >= c2)
			goto skip;
		render_square(o->wtex, f1, f2, c1, c2, x1 << mip, y1 << mip,
		    x2 << mip, y2 << mip, 1 << mip, d1, d2, topleft);

	}
skip: {
	float f1 = o->ceil;
	float f2 = o->ceil;
	float c1 = s->ceil;
	float c2 = s->ceil;
	if (o->type == CHF && s->type != CHF) {
		f1 += d1->vdelta / 4.0f;
		f2 += d2->vdelta / 4.0f;
	} else if (s->type == CHF && o->type != CHF) {
		c1 += d1->vdelta / 4.0f;
		c2 += d2->vdelta / 4.0f;
	}
	if (c1 <= f1 && c2 <= f2)
		return;
	render_square(o->utex, f1, f2, c1, c2, x1 << mip, y1 << mip, x2 << mip,
	    y2 << mip, 1 << mip, d1, d2, topleft);

}
}

const int MAX_MIP = 5; // 32x32 unit blocks
const int MIN_LOD = 2;
const int LOW_LOD = 25;
const int MAX_LOD = 1000;

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
		if (issemi(mip, x + x1, y + y1, x1, y1, x2, y2))
			return true;
	case CORNER:
	case SOLID:
		break;
	default:
		return true;
	};

	switch (SWS(w, x + x2, y + y2, msize)->type) {
	case SEMISOLID:
		if (issemi(mip, x + x2, y + y2, x1, y1, x2, y2))
			return true;
	case CORNER:
	case SOLID:
		break;
	default:
		return true;
	};

	return false;
}

bool render_floor, render_ceil;

// the core recursive function, renders a rect of cubes at a certain mip level
// from a viewer perspective call itself for lower mip levels, on most modern







<
>









<
>







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
		if (issemi(mip, x + x1, y + y1, x1, y1, x2, y2))
			return true;
	case CORNER:
	case SOLID:
		break;
	default:
		return true;

	}
	switch (SWS(w, x + x2, y + y2, msize)->type) {
	case SEMISOLID:
		if (issemi(mip, x + x2, y + y2, x1, y1, x2, y2))
			return true;
	case CORNER:
	case SOLID:
		break;
	default:
		return true;

	}
	return false;
}

bool render_floor, render_ceil;

// the core recursive function, renders a rect of cubes at a certain mip level
// from a viewer perspective call itself for lower mip levels, on most modern
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
	    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);
	for (int ox = x; ox < xs; ox++)
		for (int oy = y; oy < ys;
		     oy++) // first collect occlusion information for this block
		{

			SWS(w, ox, oy, sz)->occluded =
			    isoccluded(player1->o.x, player1->o.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;
		SWS(w, pvx, pvy, sz)->occluded =
		    0; // player cell never occluded
	};


#define df(x) s->floor - (x->vdelta / 4.0f)
#define dc(x) s->ceil + (x->vdelta / 4.0f)

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







|
<
|
<
>



<
>
>







<
>







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
	    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);
	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->o.x, player1->o.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;
		SWS(w, pvx, pvy, sz)->occluded =
		    0; // player cell never occluded

	}

#define df(x) s->floor - (x->vdelta / 4.0f)
#define dc(x) s->ceil + (x->vdelta / 4.0f)

	// 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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

208
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
		render_flat(s->ctex, xx << mip, yy << mip, 1 << mip, s->ceil, s,
		    t, u, v, true);
	if (s->type == CHF) // if(s->ceil>=vh)
		render_flatdelta(s->ctex, xx << mip, yy << mip, 1 << mip, dc(s),
		    dc(t), dc(u), dc(v), s, t, u, v, true);
}
}
;

LOOPH continue; // floors
LOOPD
if ((s->type == SPACE || s->type == CHF) && s->floor <= vh && render_floor) {
	render_flat(s->ftex, xx << mip, yy << mip, 1 << mip, s->floor, s, t, u,
	    v, false);
	if (s->floor < hdr.waterlevel && !SOLID(s))
		addwaterquad(xx << mip, yy << mip, 1 << mip);
};

if (s->type == FHF) {
	render_flatdelta(s->ftex, xx << mip, yy << mip, 1 << mip, df(s), df(t),
	    df(u), df(v), s, t, u, v, false);
	if (s->floor - s->vdelta / 4.0f < hdr.waterlevel && !SOLID(s))
		addwaterquad(xx << mip, yy << mip, 1 << mip);
};
}
}
;


LOOPH continue; // walls
LOOPD
//  w
// zSt
//  vu








<








<
>





<


<
>







191
192
193
194
195
196
197

198
199
200
201
202
203
204
205

206
207
208
209
210
211

212
213

214
215
216
217
218
219
220
221
		render_flat(s->ctex, xx << mip, yy << mip, 1 << mip, s->ceil, s,
		    t, u, v, true);
	if (s->type == CHF) // if(s->ceil>=vh)
		render_flatdelta(s->ctex, xx << mip, yy << mip, 1 << mip, dc(s),
		    dc(t), dc(u), dc(v), s, t, u, v, true);
}
}


LOOPH continue; // floors
LOOPD
if ((s->type == SPACE || s->type == CHF) && s->floor <= vh && render_floor) {
	render_flat(s->ftex, xx << mip, yy << mip, 1 << mip, s->floor, s, t, u,
	    v, false);
	if (s->floor < hdr.waterlevel && !SOLID(s))
		addwaterquad(xx << mip, yy << mip, 1 << mip);

}
if (s->type == FHF) {
	render_flatdelta(s->ftex, xx << mip, yy << mip, 1 << mip, df(s), df(t),
	    df(u), df(v), s, t, u, v, false);
	if (s->floor - s->vdelta / 4.0f < hdr.waterlevel && !SOLID(s))
		addwaterquad(xx << mip, yy << mip, 1 << mip);

}
}

}

LOOPH continue; // walls
LOOPD
//  w
// zSt
//  vu

234
235
236
237
238
239
240
241

242
243
244
245
246
247
248
249
250

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
		if (SOLID(w)) {
			render_wall(w, h2 = s, xx + 1, yy, xx, yy + 1, mip, t,
			    v, false);
			topleft = false;
		} else if (SOLID(v)) {
			render_wall(v, h2 = s, xx, yy, xx + 1, yy + 1, mip, s,
			    u, false);
		};

	} else if (SOLID(t)) {
		if (SOLID(w)) {
			render_wall(w, h1 = s, xx + 1, yy + 1, xx, yy, mip, u,
			    s, false);
		} else if (SOLID(v)) {
			render_wall(v, h1 = s, xx, yy + 1, xx + 1, yy, mip, v,
			    t, false);
			topleft = false;
		};

	} else {
		normalwall = false;
		bool wv = w->ceil - w->floor < v->ceil - v->floor;
		if (z->ceil - z->floor < t->ceil - t->floor) {
			if (wv) {
				render_wall(h1 = s, h2 = v, xx + 1, yy, xx,
				    yy + 1, mip, t, v, false);
				topleft = false;
			} else {
				render_wall(h1 = s, h2 = w, xx, yy, xx + 1,
				    yy + 1, mip, s, u, false);
			};

		} else {
			if (wv) {
				render_wall(h2 = s, h1 = v, xx + 1, yy + 1, xx,
				    yy, mip, u, s, false);
			} else {
				render_wall(h2 = s, h1 = w, xx, yy + 1, xx + 1,
				    yy, mip, v, t, false);
				topleft = false;
			};
		};
	};



	render_tris(
	    xx << mip, yy << mip, 1 << mip, topleft, h1, h2, s, t, u, v);
}

if (normalwall) {
	bool inner = xx != sz - 1 && yy != sz - 1;








<
>








<
>











<
>








<
<
<
>
>
>







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
257
258
259

260
261
262
263
264
265
266
267
268



269
270
271
272
273
274
275
276
277
278
		if (SOLID(w)) {
			render_wall(w, h2 = s, xx + 1, yy, xx, yy + 1, mip, t,
			    v, false);
			topleft = false;
		} else if (SOLID(v)) {
			render_wall(v, h2 = s, xx, yy, xx + 1, yy + 1, mip, s,
			    u, false);

		}
	} else if (SOLID(t)) {
		if (SOLID(w)) {
			render_wall(w, h1 = s, xx + 1, yy + 1, xx, yy, mip, u,
			    s, false);
		} else if (SOLID(v)) {
			render_wall(v, h1 = s, xx, yy + 1, xx + 1, yy, mip, v,
			    t, false);
			topleft = false;

		}
	} else {
		normalwall = false;
		bool wv = w->ceil - w->floor < v->ceil - v->floor;
		if (z->ceil - z->floor < t->ceil - t->floor) {
			if (wv) {
				render_wall(h1 = s, h2 = v, xx + 1, yy, xx,
				    yy + 1, mip, t, v, false);
				topleft = false;
			} else {
				render_wall(h1 = s, h2 = w, xx, yy, xx + 1,
				    yy + 1, mip, s, u, false);

			}
		} else {
			if (wv) {
				render_wall(h2 = s, h1 = v, xx + 1, yy + 1, xx,
				    yy, mip, u, s, false);
			} else {
				render_wall(h2 = s, h1 = w, xx, yy + 1, xx + 1,
				    yy, mip, v, t, false);
				topleft = false;



			}
		}
	}
	render_tris(
	    xx << mip, yy << mip, 1 << mip, topleft, h1, h2, s, t, u, v);
}

if (normalwall) {
	bool inner = xx != sz - 1 && yy != sz - 1;

290
291
292
293
294
295
296
297
298
299
300
301
302

303
304
305
306
307
308
309
	    (!SOLID(s) || w->type != CORNER) &&
	    (w->type != SEMISOLID || issemi(mip, xx, yy - 1, 0, 1, 1, 1)))
		render_wall(s, w, xx, yy, xx + 1, yy, mip, s, t, false);
	if (yy <= vyy && inner && !SOLID(v) &&
	    (!SOLID(s) || v->type != CORNER) &&
	    (v->type != SEMISOLID || issemi(mip, xx, yy + 1, 0, 0, 1, 0)))
		render_wall(s, v, xx, yy + 1, xx + 1, yy + 1, mip, v, u, true);
};
}
}
;
}
;


void
distlod(int &low, int &high, int angle, float widef)
{
	float f = 90.0f / lod / widef;
	low = (int)((90 - angle) / f);
	high = (int)(angle / f);







<


<

<
>







288
289
290
291
292
293
294

295
296

297

298
299
300
301
302
303
304
305
	    (!SOLID(s) || w->type != CORNER) &&
	    (w->type != SEMISOLID || issemi(mip, xx, yy - 1, 0, 1, 1, 1)))
		render_wall(s, w, xx, yy, xx + 1, yy, mip, s, t, false);
	if (yy <= vyy && inner && !SOLID(v) &&
	    (!SOLID(s) || v->type != CORNER) &&
	    (v->type != SEMISOLID || issemi(mip, xx, yy + 1, 0, 0, 1, 0)))
		render_wall(s, v, xx, yy + 1, xx + 1, yy + 1, mip, v, u, true);

}
}

}

}

void
distlod(int &low, int &high, int angle, float widef)
{
	float f = 90.0f / lod / widef;
	low = (int)((90 - angle) / f);
	high = (int)(angle / f);
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
    float vx, float vy, float vh, int yaw, int pitch, float fov, int w, int h)
{
	loopi(LARGEST_FACTOR) stats[i] = 0;
	min_lod = MIN_LOD + abs(pitch) / 12;
	yaw = 360 - yaw;
	float widef = fov / 75.0f;
	int cdist = abs(yaw % 90 - 45);
	if (cdist < 7) // hack to avoid popup at high fovs at 45 yaw
	{


		min_lod = max(min_lod,
		    (int)(MIN_LOD +
		        (10 - cdist) / 1.0f *
		            widef)); // less if lod worked better
		widef = 1.0f;
	};

	lod = MAX_LOD;
	lodtop = lodbot = lodleft = lodright = min_lod;
	if (yaw > 45 && yaw <= 135) {
		lodleft = lod;
		distlod(lodtop, lodbot, yaw - 45, widef);
	} else if (yaw > 135 && yaw <= 225) {
		lodbot = lod;
		distlod(lodleft, lodright, yaw - 135, widef);
	} else if (yaw > 225 && yaw <= 315) {
		lodright = lod;
		distlod(lodbot, lodtop, yaw - 225, widef);
	} else {
		lodtop = lod;
		distlod(
		    lodright, lodleft, yaw <= 45 ? yaw + 45 : yaw - 315, widef);
	};

	float hyfov = fov * h / w / 2;
	render_floor = pitch < hyfov;
	render_ceil = -pitch < hyfov;

	render_seg_new(
	    vx, vy, vh, MAX_MIP, 0, 0, ssize >> MAX_MIP, ssize >> MAX_MIP);
	mipstats(stats[0], stats[1], stats[2]);
}







|
<
>
>
|
<
|
<

<
>















<
>








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
    float vx, float vy, float vh, int yaw, int pitch, float fov, int w, int h)
{
	loopi(LARGEST_FACTOR) stats[i] = 0;
	min_lod = MIN_LOD + abs(pitch) / 12;
	yaw = 360 - yaw;
	float widef = fov / 75.0f;
	int cdist = abs(yaw % 90 - 45);
	// hack to avoid popup at high fovs at 45 yaw

	if (cdist < 7) {
		// less if lod worked better
		min_lod =

		    max(min_lod, (int)(MIN_LOD + (10 - cdist) / 1.0f * widef));

		widef = 1.0f;

	}
	lod = MAX_LOD;
	lodtop = lodbot = lodleft = lodright = min_lod;
	if (yaw > 45 && yaw <= 135) {
		lodleft = lod;
		distlod(lodtop, lodbot, yaw - 45, widef);
	} else if (yaw > 135 && yaw <= 225) {
		lodbot = lod;
		distlod(lodleft, lodright, yaw - 135, widef);
	} else if (yaw > 225 && yaw <= 315) {
		lodright = lod;
		distlod(lodbot, lodtop, yaw - 225, widef);
	} else {
		lodtop = lod;
		distlod(
		    lodright, lodleft, yaw <= 45 ? yaw + 45 : yaw - 315, widef);

	}
	float hyfov = fov * h / w / 2;
	render_floor = pitch < hyfov;
	render_ceil = -pitch < hyfov;

	render_seg_new(
	    vx, vy, vh, MAX_MIP, 0, 0, ssize >> MAX_MIP, ssize >> MAX_MIP);
	mipstats(stats[0], stats[1], stats[2]);
}