Cube  Check-in [46c5779570]

Overview
Comment:Convert more files to pure Objective-C
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 46c577957085f072871cb93b46340fd8349c1e20503a448cc8e6d33ddaea1552
User & Date: js on 2025-03-20 21:52:26
Other Links: manifest | tags
Context
2025-03-20
22:22
Convert remaining files to pure Objective-C check-in: 12cac9666a user: js tags: trunk
21:52
Convert more files to pure Objective-C check-in: 46c5779570 user: js tags: trunk
21:18
Convert more files to pure Objective-C check-in: b250dfa8d4 user: js tags: trunk
Changes

Name change from src/Cube.mm to src/Cube.m.

Renamed and modified src/commands.mm [b377b2846b] to src/commands.m [5ae3539b0c].

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






16
17
18
19
20
21
22
// command.cpp: implements the parsing and execution of a tiny script language
// which is largely backwards compatible with the quake console language.

#include "cube.h"

#include <memory>

#import "Alias.h"
#import "Command.h"
#import "Identifier.h"
#import "OFString+Cube.h"
#import "Variable.h"

// contains ALL vars/commands/aliases
static OFMutableDictionary<OFString *, __kindof Identifier *> *identifiers;







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

	if (alias == nil) {





<
<








>
>
>
>
>
>







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
// command.cpp: implements the parsing and execution of a tiny script language
// which is largely backwards compatible with the quake console language.

#include "cube.h"



#import "Alias.h"
#import "Command.h"
#import "Identifier.h"
#import "OFString+Cube.h"
#import "Variable.h"

// contains ALL vars/commands/aliases
static OFMutableDictionary<OFString *, __kindof Identifier *> *identifiers;

static void
cleanup(char **string)
{
	free(*string);
}

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

	if (alias == nil) {
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

	identifiers[name] = command;

	return false;
}

// parse any nested set of () or []
char *
parseexp(char *&p, int right)
{
	int left = *p++;
	char *word = p;
	for (int brak = 1; brak;) {
		int c = *p++;
		if (c == '\r')
			*(p - 1) = ' '; // hack
		if (c == left)
			brak++;
		else if (c == right)
			brak--;
		else if (!c) {
			p--;
			conoutf(@"missing \"%c\"", right);
			return NULL;
		}
	}
	char *s = strndup(word, p - word - 1);
	if (left == '(') {
		OFString *t;
		@try {
			t = [OFString
			    stringWithFormat:@"%d", execute(@(s), true)];
		} @finally {
			free(s);
		}
		s = strdup(t.UTF8String);
	}
	return s;
}

// parse single argument, including expressions
char *
parseword(char *&p)
{
	p += strspn(p, " \t\r");
	if (p[0] == '/' && p[1] == '/')
		p += strcspn(p, "\n\0");
	if (*p == '\"') {
		p++;
		char *word = p;
		p += strcspn(p, "\"\r\n\0");
		char *s = strndup(word, p - word);
		if (*p == '\"')
			p++;
		return s;
	}
	if (*p == '(')
		return parseexp(p, ')');
	if (*p == '[')
		return parseexp(p, ']');
	char *word = p;
	p += strcspn(p, "; \t\r\n\0");
	if (p - word == 0)
		return NULL;
	return strndup(word, p - word);
}

// find value of ident referenced with $ in exp
OFString *
lookup(OFString *n)
{
	__kindof Identifier *identifier = identifiers[[n substringFromIndex:1]];







|
|

|
|

|

|





|




|














|
|

|
|
|
|
|
|
|
|
|
|


|

|

|
|
|

|







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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

	identifiers[name] = command;

	return false;
}

// parse any nested set of () or []
static char *
parseexp(char **p, int right)
{
	int left = *(*p)++;
	char *word = *p;
	for (int brak = 1; brak;) {
		int c = *(*p)++;
		if (c == '\r')
			*(*p - 1) = ' '; // hack
		if (c == left)
			brak++;
		else if (c == right)
			brak--;
		else if (!c) {
			(*p)--;
			conoutf(@"missing \"%c\"", right);
			return NULL;
		}
	}
	char *s = strndup(word, *p - word - 1);
	if (left == '(') {
		OFString *t;
		@try {
			t = [OFString
			    stringWithFormat:@"%d", execute(@(s), true)];
		} @finally {
			free(s);
		}
		s = strdup(t.UTF8String);
	}
	return s;
}

// parse single argument, including expressions
static char *
parseword(char **p)
{
	(*p) += strspn(*p, " \t\r");
	if ((*p)[0] == '/' && (*p)[1] == '/')
		*p += strcspn(*p, "\n\0");
	if (**p == '\"') {
		(*p)++;
		char *word = *p;
		*p += strcspn(*p, "\"\r\n\0");
		char *s = strndup(word, *p - word);
		if (**p == '\"')
			(*p)++;
		return s;
	}
	if (**p == '(')
		return parseexp(p, ')');
	if (**p == '[')
		return parseexp(p, ']');
	char *word = *p;
	*p += strcspn(*p, "; \t\r\n\0");
	if (*p - word == 0)
		return NULL;
	return strndup(word, *p - word);
}

// find value of ident referenced with $ in exp
OFString *
lookup(OFString *n)
{
	__kindof Identifier *identifier = identifiers[[n substringFromIndex:1]];
223
224
225
226
227
228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
	return 0;
}

// all evaluation happens here, recursively
int
execute(OFString *string, bool isDown)
{

	std::unique_ptr<char> copy(strdup(string.UTF8String));
	char *p = copy.get();
	const int MAXWORDS = 25; // limit, remove
	OFString *w[MAXWORDS];
	int val = 0;
	for (bool cont = true; cont;) {
		// for each ; seperated statement
		int numargs = MAXWORDS;
		loopi(MAXWORDS)
		{
			// collect all argument values
			w[i] = @"";
			if (i > numargs)
				continue;
			// parse and evaluate exps
			char *s = parseword(p);
			if (!s) {
				numargs = i;
				s = strdup("");
			}
			@try {
				if (*s == '$')
					// substitute variables







>
|
|













|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
	return 0;
}

// all evaluation happens here, recursively
int
execute(OFString *string, bool isDown)
{
	char *copy __attribute__((__cleanup__(cleanup))) =
	    strdup(string.UTF8String);
	char *p = copy;
	const int MAXWORDS = 25; // limit, remove
	OFString *w[MAXWORDS];
	int val = 0;
	for (bool cont = true; cont;) {
		// for each ; seperated statement
		int numargs = MAXWORDS;
		loopi(MAXWORDS)
		{
			// collect all argument values
			w[i] = @"";
			if (i > numargs)
				continue;
			// parse and evaluate exps
			char *s = parseword(&p);
			if (!s) {
				numargs = i;
				s = strdup("");
			}
			@try {
				if (*s == '$')
					// substitute variables
465
466
467
468
469
470
471

472
473


474


475
476
477
478
479
480
481
	return n + 1;
}

void
at(OFString *s_, OFString *pos)
{
	int n = pos.cube_intValue;

	std::unique_ptr<char> copy(strdup(s_.UTF8String));
	char *s = copy.get();


	loopi(n) s += strspn(s += strcspn(s, " \0"), " ");


	s[strcspn(s, " \0")] = 0;
	concat(@(s));
}

COMMANDN(loop, loopa, ARG_2STR)
COMMANDN(while, whilea, ARG_2STR)
COMMANDN(if, ifthen, ARG_3STR)







>
|
|
>
>
|
>
>







470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
	return n + 1;
}

void
at(OFString *s_, OFString *pos)
{
	int n = pos.cube_intValue;
	char *copy __attribute__((__cleanup__(cleanup))) =
	    strdup(s_.UTF8String);
	char *s = copy;
	loopi(n)
	{
		s += strcspn(s, " \0");
		s += strspn(s, " ");
	}
	s[strcspn(s, " \0")] = 0;
	concat(@(s));
}

COMMANDN(loop, loopa, ARG_2STR)
COMMANDN(while, whilea, ARG_2STR)
COMMANDN(if, ifthen, ARG_3STR)

Modified src/editing.m from [1b9f6190f4] to [27b5ed2226].

25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
		};
	});
}

int selh = 0;
bool selset = false;

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

int cx, cy, ch;

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

bool dragging = false;







|
|
|
|
|

|
|
|







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
		};
	});
}

int selh = 0;
bool selset = false;

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

int cx, cy, ch;

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

bool dragging = false;
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
	}
}

void
makeundo()
{
	if (undos == nil)
		undos =
		    [[OFMutableData alloc] initWithItemSize:sizeof(struct block *)];

	struct block *copy = blockcopy(&sel);
	[undos addItem:&copy];
	pruneundos(undomegs << 20);
}

void







|
|







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
	}
}

void
makeundo()
{
	if (undos == nil)
		undos = [[OFMutableData alloc]
		    initWithItemSize:sizeof(struct block *)];

	struct block *copy = blockcopy(&sel);
	[undos addItem:&copy];
	pruneundos(undomegs << 20);
}

void

Modified src/entities.m from [2808b0e950] to [df6381198b].

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void
initEntities()
{
	ents = [[OFMutableArray alloc] init];
}

static void
renderent(Entity *e, OFString *mdlname, float z, float yaw, int frame/* = 0*/,
    int numf/* = 1*/, int basetime/* = 0*/, float speed/* = 10.0f*/)
{
	rendermodel(mdlname, frame, numf, 0, 1.1f,
	    OFMakeVector3D(e.x, z + S(e.x, e.y)->floor, e.y), yaw, 0, false,
	    1.0f, speed, 0, basetime);
}

void







|
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
void
initEntities()
{
	ents = [[OFMutableArray alloc] init];
}

static void
renderent(Entity *e, OFString *mdlname, float z, float yaw, int frame /* = 0*/,
    int numf /* = 1*/, int basetime /* = 0*/, float speed /* = 10.0f*/)
{
	rendermodel(mdlname, frame, numf, 0, 1.1f,
	    OFMakeVector3D(e.x, z + S(e.x, e.y)->floor, e.y), yaw, 0, false,
	    1.0f, speed, 0, basetime);
}

void
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
					continue;
				if (e.type < I_SHELLS || e.type > TELEPORT)
					continue;
				renderent(e, entmdlnames[e.type - I_SHELLS],
				    (float)(1 +
				        sin(lastmillis / 100.0 + e.x + e.y) /
				            20),
				    lastmillis / 10.0f, 0,1,0,10.0f);
			} else {
				switch (e.attr2) {
				case 1:
				case 3:
					continue;

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

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







|

















|







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
					continue;
				if (e.type < I_SHELLS || e.type > TELEPORT)
					continue;
				renderent(e, entmdlnames[e.type - I_SHELLS],
				    (float)(1 +
				        sin(lastmillis / 100.0 + e.x + e.y) /
				            20),
				    lastmillis / 10.0f, 0, 1, 0, 10.0f);
			} else {
				switch (e.attr2) {
				case 1:
				case 3:
					continue;

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

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

Modified src/meson.build from [44296e0cb5] to [a429d4244b].

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
executable('client',
  [
    'Alias.m',
    'Client.m',
    'Command.m',
    'ConsoleLine.m',
    'Cube.mm',
    'DynamicEntity.m',
    'Entity.m',
    'Identifier.m',
    'KeyMapping.m',
    'MD2.m',
    'MapModelInfo.m',
    'Menu.m',
    'MenuItem.m',
    'OFString+Cube.m',
    'PersistentEntity.m',
    'Projectile.m',
    'ResolverResult.m',
    'ResolverThread.m',
    'ServerEntity.m',
    'ServerInfo.m',
    'Variable.m',
    'clients.m',
    'clientextras.m',
    'clientgame.m',
    'clients2c.m',
    'commands.mm',
    'console.m',
    'editing.m',
    'entities.m',
    'init.mm',
    'menus.m',
    'monster.m',
    'physics.mm',
    'rendercubes.mm',
    'renderextras.mm',
    'rendergl.mm',
    'rendermd2.mm',
    'renderparticles.mm',
    'rendertext.mm',
    'rndmap.mm',
    'savegamedemo.mm',
    'server.mm',
    'serverbrowser.mm',
    'serverms.mm',
    'serverutil.mm',
    'sound.mm',
    'tools.mm',
    'weapon.mm',
    'world.mm',
    'worldio.mm',
    'worldlight.mm',
    'worldocull.mm',
    'worldrender.mm',
  ],
  dependencies: [






|




















|






|
|
|






|

|
|
|
|
|







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
executable('client',
  [
    'Alias.m',
    'Client.m',
    'Command.m',
    'ConsoleLine.m',
    'Cube.m',
    'DynamicEntity.m',
    'Entity.m',
    'Identifier.m',
    'KeyMapping.m',
    'MD2.m',
    'MapModelInfo.m',
    'Menu.m',
    'MenuItem.m',
    'OFString+Cube.m',
    'PersistentEntity.m',
    'Projectile.m',
    'ResolverResult.m',
    'ResolverThread.m',
    'ServerEntity.m',
    'ServerInfo.m',
    'Variable.m',
    'clients.m',
    'clientextras.m',
    'clientgame.m',
    'clients2c.m',
    'commands.m',
    'console.m',
    'editing.m',
    'entities.m',
    'init.mm',
    'menus.m',
    'monster.m',
    'physics.m',
    'rendercubes.m',
    'renderextras.m',
    'rendergl.mm',
    'rendermd2.mm',
    'renderparticles.mm',
    'rendertext.mm',
    'rndmap.mm',
    'savegamedemo.mm',
    'server.m',
    'serverbrowser.mm',
    'serverms.m',
    'serverutil.m',
    'sound.m',
    'tools.m',
    'weapon.m',
    'world.mm',
    'worldio.mm',
    'worldlight.mm',
    'worldocull.mm',
    'worldrender.mm',
  ],
  dependencies: [
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  link_with: [enet],
  win_subsystem: 'windows')

executable('server',
  [
    'Client.m',
    'ServerEntity.m',
    'server.mm',
    'serverms.mm',
    'serverutil.mm',
    'tools.mm',
  ],
  objcpp_args: ['-DSTANDALONE'],
  dependencies: [
    objfw_dep,
    sdl_dep
  ],
  include_directories: [enet_includes],
  link_args: server_link_args,
  link_with: [enet],
  win_subsystem: 'console')







|
|
|
|

|








67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  link_with: [enet],
  win_subsystem: 'windows')

executable('server',
  [
    'Client.m',
    'ServerEntity.m',
    'server.m',
    'serverms.m',
    'serverutil.m',
    'tools.m',
  ],
  objc_args: ['-DSTANDALONE'],
  dependencies: [
    objfw_dep,
    sdl_dep
  ],
  include_directories: [enet_includes],
  link_args: server_link_args,
  link_with: [enet],
  win_subsystem: 'console')

Renamed and modified src/physics.mm [c9005101f7] to src/physics.m [1c146ff92c].

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

#include "cube.h"

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

// collide with player or monster
bool
plcollide(
    DynamicEntity *d, DynamicEntity *o, float &headspace, float &hi, float &lo)
{
	if (o.state != CS_ALIVE)
		return true;
	const float r = o.radius + d.radius;
	if (fabs(o.o.x - d.o.x) < r && fabs(o.o.y - d.o.y) < r) {
		if (d.o.z - d.eyeheight < o.o.z - o.eyeheight) {
			if (o.o.z - o.eyeheight < hi)
				hi = o.o.z - o.eyeheight - 1;
		} else if (o.o.z + o.aboveeye > lo)
			lo = o.o.z + o.aboveeye + 1;

		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(DynamicEntity *d, float &hi, float &lo) // collide with a mapmodel
{
	for (Entity *e in ents) {
		if (e.type != MAPMODEL)
			continue;

		MapModelInfo *mmi = getmminfo(e.attr2);
		if (mmi == nil || !mmi.h)
			continue;

		const float r = mmi.rad + d.radius;
		if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) {
			float mmz =
			    (float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3);

			if (d.o.z - d.eyeheight < mmz) {
				if (mmz < hi)
					hi = mmz;
			} else if (mmz + mmi.h > lo)
				lo = mmz + mmi.h;
		}
	}
}

// all collision happens here
// spawn is a dirty side effect used in spawning
// drop & rise are supplied by the physics below to indicate gravity/push for













|

|






|
|
|
|





|
|
|




>
|
|
<

|







|
|
|





>
|
|















|
|
|
|







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

#include "cube.h"

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

// collide with player or monster
static bool
plcollide(
    DynamicEntity *d, DynamicEntity *o, float *headspace, float *hi, float *lo)
{
	if (o.state != CS_ALIVE)
		return true;
	const float r = o.radius + d.radius;
	if (fabs(o.o.x - d.o.x) < r && fabs(o.o.y - d.o.y) < r) {
		if (d.o.z - d.eyeheight < o.o.z - o.eyeheight) {
			if (o.o.z - o.eyeheight < *hi)
				*hi = o.o.z - o.eyeheight - 1;
		} else if (o.o.z + o.aboveeye > *lo)
			*lo = o.o.z + o.aboveeye + 1;

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

// recursively collide with a mipmapped corner cube
static bool
cornertest(int mip, int x, int y, int dx, int dy, int *bx, int *by, int *bs)

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

// collide with a mapmodel
static void
mmcollide(DynamicEntity *d, float *hi, float *lo)
{
	for (Entity *e in ents) {
		if (e.type != MAPMODEL)
			continue;

		MapModelInfo *mmi = getmminfo(e.attr2);
		if (mmi == nil || !mmi.h)
			continue;

		const float r = mmi.rad + d.radius;
		if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) {
			float mmz =
			    (float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3);

			if (d.o.z - d.eyeheight < mmz) {
				if (mmz < *hi)
					*hi = mmz;
			} else if (mmz + mmi.h > *lo)
				*lo = mmz + mmi.h;
		}
	}
}

// all collision happens here
// spawn is a dirty side effect used in spawning
// drop & rise are supplied by the physics below to indicate gravity/push for
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
	    : -1000.0f;

	for (int x = x1; x <= x2; x++)
		for (int y = y1; y <= y2; y++) {
			// collide with map
			if (OUTBORD(x, y))
				return false;
			sqr *s = S(x, y);
			float ceil = s->ceil;
			float floor = s->floor;
			switch (s->type) {
			case SOLID:
				return false;

			case CORNER: {
				int bx = x, by = y, bs = 1;
				if (x == x1 && y == y1 &&
				        cornertest(
				            0, x, y, -1, -1, bx, by, bs) &&
				        fx1 - bx + fy1 - by <= bs ||
				    x == x2 && y == y1 &&
				        cornertest(
				            0, x, y, 1, -1, bx, by, bs) &&
				        fx2 - bx >= fy1 - by ||
				    x == x1 && y == y2 &&
				        cornertest(
				            0, x, y, -1, 1, bx, by, bs) &&
				        fx1 - bx <= fy2 - by ||
				    x == x2 && y == y2 &&
				        cornertest(0, x, y, 1, 1, bx, by, bs) &&

				        fx2 - bx + fy2 - by >= bs)
					return false;
				break;
			}

			case FHF: // FIXME: too simplistic collision with
			          // slopes, makes it feels like tiny stairs







|










|



|



|


|
>







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
	    : -1000.0f;

	for (int x = x1; x <= x2; x++)
		for (int y = y1; y <= y2; y++) {
			// collide with map
			if (OUTBORD(x, y))
				return false;
			struct sqr *s = S(x, y);
			float ceil = s->ceil;
			float floor = s->floor;
			switch (s->type) {
			case SOLID:
				return false;

			case CORNER: {
				int bx = x, by = y, bs = 1;
				if (x == x1 && y == y1 &&
				        cornertest(
				            0, x, y, -1, -1, &bx, &by, &bs) &&
				        fx1 - bx + fy1 - by <= bs ||
				    x == x2 && y == y1 &&
				        cornertest(
				            0, x, y, 1, -1, &bx, &by, &bs) &&
				        fx2 - bx >= fy1 - by ||
				    x == x1 && y == y2 &&
				        cornertest(
				            0, x, y, -1, 1, &bx, &by, &bs) &&
				        fx1 - bx <= fy2 - by ||
				    x == x2 && y == y2 &&
				        cornertest(
				            0, x, y, 1, 1, &bx, &by, &bs) &&
				        fx2 - bx + fy2 - by >= bs)
					return false;
				break;
			}

			case FHF: // FIXME: too simplistic collision with
			          // slopes, makes it feels like tiny stairs
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
	if (hi - lo < d.eyeheight + d.aboveeye)
		return false;

	float headspace = 10;
	for (id player in players) {
		if (player == [OFNull null] || player == d)
			continue;
		if (!plcollide(d, player, headspace, hi, lo))
			return false;
	}
	if (d != player1)
		if (!plcollide(d, player1, headspace, hi, lo))
			return false;
	// this loop can be a performance bottleneck with many monster on a slow
	// cpu, should replace with a blockmap but seems mostly fast enough
	for (DynamicEntity *monster in getmonsters())
		if (!vreject(d.o, monster.o, 7.0f) && d != monster &&
		    !plcollide(d, monster, headspace, hi, lo))
			return false;
	headspace -= 0.01f;

	mmcollide(d, hi, lo); // collide with map models

	if (spawn) {
		// just drop to floor (sideeffect)
		d.o = OFMakeVector3D(d.o.x, d.o.y, lo + d.eyeheight);
		d.onfloor = true;
	} else {
		const float space = d.o.z - d.eyeheight - lo;







|



|





|



|







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
	if (hi - lo < d.eyeheight + d.aboveeye)
		return false;

	float headspace = 10;
	for (id player in players) {
		if (player == [OFNull null] || player == d)
			continue;
		if (!plcollide(d, player, &headspace, &hi, &lo))
			return false;
	}
	if (d != player1)
		if (!plcollide(d, player1, &headspace, &hi, &lo))
			return false;
	// this loop can be a performance bottleneck with many monster on a slow
	// cpu, should replace with a blockmap but seems mostly fast enough
	for (DynamicEntity *monster in getmonsters())
		if (!vreject(d.o, monster.o, 7.0f) && d != monster &&
		    !plcollide(d, monster, &headspace, &hi, &lo))
			return false;
	headspace -= 0.01f;

	mmcollide(d, &hi, &lo); // collide with map models

	if (spawn) {
		// just drop to floor (sideeffect)
		d.o = OFMakeVector3D(d.o.x, d.o.y, lo + d.eyeheight);
		d.onfloor = true;
	} else {
		const float space = d.o.z - d.eyeheight - lo;
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
	}
}

// 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
moveplayer(DynamicEntity *pl, int moveres, bool local, int curtime)
{
	const bool water = hdr.waterlevel > pl.o.z - 0.5f;
	const bool floating = (editmode && local) || pl.state == CS_EDITING;

	OFVector3D d; // vector of direction we ideally want to move in

	d.x = (float)(pl.move * cos(rad(pl.yaw - 90)));







|
|







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
	}
}

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

static void
moveplayer4(DynamicEntity *pl, int moveres, bool local, int curtime)
{
	const bool water = hdr.waterlevel > pl.o.z - 0.5f;
	const bool floating = (editmode && local) || pl.state == CS_EDITING;

	OFVector3D d; // vector of direction we ideally want to move in

	d.x = (float)(pl.move * cos(rad(pl.yaw - 90)));
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

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








|







375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

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

407
408
409
410
411
412
413
414
415
416
417
	}
	pl.inwater = water;
}

void
moveplayer(DynamicEntity *pl, int moveres, bool local)
{
	loopi(physicsrepeat) moveplayer(pl, moveres, local,
	    i ? curtime / physicsrepeat
	      : curtime - curtime / physicsrepeat * (physicsrepeat - 1));
}







|



409
410
411
412
413
414
415
416
417
418
419
	}
	pl.inwater = water;
}

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

Renamed and modified src/rendercubes.mm [a098c5d67b] to src/rendercubes.m [9b7cf3b555].

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
// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills
// the vertex array for different cube surfaces.

#include "cube.h"

vertex *verts = NULL;
int curvert;
int curmaxverts = 10000;

void
setarraypointers()
{
	glVertexPointer(3, GL_FLOAT, sizeof(vertex), &verts[0].x);
	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &verts[0].r);
	glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &verts[0].u);
}

void
reallocv()
{
	verts =
	    (vertex *)OFResizeMemory(verts, (curmaxverts *= 2), sizeof(vertex));
	curmaxverts -= 10;
	setarraypointers();
}

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

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

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

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






|

|




|
|
|






|














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







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
// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills
// the vertex array for different cube surfaces.

#include "cube.h"

static struct vertex *verts = NULL;
int curvert;
static int curmaxverts = 10000;

void
setarraypointers()
{
	glVertexPointer(3, GL_FLOAT, sizeof(struct vertex), &verts[0].x);
	glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(struct vertex), &verts[0].r);
	glTexCoordPointer(2, GL_FLOAT, sizeof(struct vertex), &verts[0].u);
}

void
reallocv()
{
	verts =
	    OFResizeMemory(verts, (curmaxverts *= 2), sizeof(struct vertex));
	curmaxverts -= 10;
	setarraypointers();
}

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

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

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

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

84
85
86
87
88
89
90
91
92
93

94
95
96
97
98
99
100
101
102
103
	}
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;
	}








|


>

|
|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
	}
void
finishstrips()
{
	stripend();
}

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

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

167
168
169
170
171
172
173

174
175
176
177
178
179
180
181
182
183
184
		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;
	}








>


|
|







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
		vert(x + size, h, y + size, l3, xo + xs, yo + ys);
	}

	oy = y;
	nquads++;
}

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

225
226
227
228
229
230
231

232
233
234
235
236
237
238
239
240
241
		    (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,
    sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube
{
	stripend();
	vertcheck();

	int sx, sy;
	int gltex = lookuptexture(h->ftex, &sx, &sy);
	float xf = TEXTURESCALE / sx;







>

|
|







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
		    (float)x + size, h3, (float)y + size, l3, xo + xs, yo + ys);
	}

	oy = y;
	nquads++;
}

// floor/ceil tris on a corner cube
void
render_2tris(struct sqr *h, struct sqr *s, int x1, int y1, int x2, int y2,
    int x3, int y3, struct sqr *l1, struct sqr *l2, struct sqr *l3)
{
	stripend();
	vertcheck();

	int sx, sy;
	int gltex = lookuptexture(h->ftex, &sx, &sy);
	float xf = TEXTURESCALE / sx;
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
	vertf((float)x2, h->ceil, (float)y2, l2, xf * x2, yf * y2);
	vertf((float)x1, h->ceil, (float)y1, l1, xf * x1, yf * y1);
	addstrip(gltex, curvert - 3, 3);
	nquads++;
}

void
render_tris(int x, int y, int size, bool topleft, sqr *h1, sqr *h2, sqr *s,
    sqr *t, sqr *u, sqr *v)
{
	if (topleft) {
		if (h1)
			render_2tris(h1, s, x + size, y + size, x, y + size, x,
			    y, u, v, s);
		if (h2)
			render_2tris(h2, s, x, y, x + size, y, x + size,
			    y + size, s, t, v);
	} 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;







|
|




















|







257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
	vertf((float)x2, h->ceil, (float)y2, l2, xf * x2, yf * y2);
	vertf((float)x1, h->ceil, (float)y1, l1, xf * x1, yf * y1);
	addstrip(gltex, curvert - 3, 3);
	nquads++;
}

void
render_tris(int x, int y, int size, bool topleft, struct sqr *h1,
    struct sqr *h2, struct sqr *s, struct sqr *t, struct sqr *u, struct sqr *v)
{
	if (topleft) {
		if (h1)
			render_2tris(h1, s, x + size, y + size, x, y + size, x,
			    y, u, v, s);
		if (h2)
			render_2tris(h2, s, x, y, x + size, y, x + size,
			    y + size, s, t, v);
	} 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, struct sqr *l1, struct sqr *l2,
    bool flip) // wall quads
{
	stripend();
	vertcheck();
	if (showm) {
		l1 = &sbright;
		l2 = &sdark;
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

int wx1, wy1, wx2, wy2;

VAR(watersubdiv, 1, 4, 64);
VARF(waterlevel, -128, -128, 127,
    if (!noteditmode()) hdr.waterlevel = waterlevel);

inline void
vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t)
{
	vertcheck();
	vertf((float)v1, v2 - (float)sin(v1 * v3 * 0.1 + t) * 0.2f, (float)v3,
	    c, t1, t2);
}

inline float







|
|







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

int wx1, wy1, wx2, wy2;

VAR(watersubdiv, 1, 4, 64);
VARF(waterlevel, -128, -128, 127,
    if (!noteditmode()) hdr.waterlevel = waterlevel);

static inline void
vertw(int v1, float v2, int v3, struct sqr *c, float t1, float t2, float t)
{
	vertcheck();
	vertf((float)v1, v2 - (float)sin(v1 * v3 * 0.1 + t) * 0.2f, (float)v3,
	    c, t1, t2);
}

inline float
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = watersubdiv * xf;
	float ys = watersubdiv * yf;
	float t1 = lastmillis / 300.0f;
	float t2 = lastmillis / 4000.0f;

	sqr dl;
	dl.r = dl.g = dl.b = 255;

	for (int xx = wx1; xx < wx2; xx += watersubdiv) {
		for (int yy = wy1; yy < wy2; yy += watersubdiv) {
			float xo = xf * (xx + t2);
			float yo = yf * (yy + t2);
			if (yy == wy1) {







|







362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
	float xf = TEXTURESCALE / sx;
	float yf = TEXTURESCALE / sy;
	float xs = watersubdiv * xf;
	float ys = watersubdiv * yf;
	float t1 = lastmillis / 300.0f;
	float t2 = lastmillis / 4000.0f;

	struct sqr dl;
	dl.r = dl.g = dl.b = 255;

	for (int xx = wx1; xx < wx2; xx += watersubdiv) {
		for (int yy = wy1; yy < wy2; yy += watersubdiv) {
			float xo = xf * (xx + t2);
			float yo = yf * (yy + t2);
			if (yy == wy1) {

Renamed and modified src/renderextras.mm [5a2fb82c3f] to src/renderextras.m [866128604e].

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
linestyle(float width, int r, int g, int b)
{
	glLineWidth(width);
	glColor3ub(r, g, b);
}

void
box(const block *b, float z1, float z2, float z3, float z4)
{
	glBegin(GL_POLYGON);
	glVertex3f((float)b->x, z1, (float)b->y);
	glVertex3f((float)b->x + b->xs, z2, (float)b->y);
	glVertex3f((float)b->x + b->xs, z3, (float)b->y + b->ys);
	glVertex3f((float)b->x, z4, (float)b->y + b->ys);
	glEnd();







|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
linestyle(float width, int r, int g, int b)
{
	glLineWidth(width);
	glColor3ub(r, g, b);
}

void
box(const struct block *b, float z1, float z2, float z3, float z4)
{
	glBegin(GL_POLYGON);
	glVertex3f((float)b->x, z1, (float)b->y);
	glVertex3f((float)b->x + b->xs, z2, (float)b->y);
	glVertex3f((float)b->x + b->xs, z3, (float)b->y + b->ys);
	glVertex3f((float)b->x, z4, (float)b->y + b->ys);
	glEnd();
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
}

const int MAXSPHERES = 50;
struct sphere {
	OFVector3D o;
	float size, max;
	int type;
	sphere *next;
};
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;

void
newsphere(const OFVector3D *o, float max, int type)
{
	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);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glBindTexture(GL_TEXTURE_2D, 4);

	for (sphere *p, **pp = &slist; p = *pp;) {
		glPushMatrix();
		float size = p->size / p->max;
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f - size);
		glTranslatef(p->o.x, p->o.z, p->o.y);
		glRotatef(lastmillis / 5.0f, 1, 1, 1);
		glScalef(p->size, p->size, p->size);
		glCallList(1);







|

|














|


















|







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
}

const int MAXSPHERES = 50;
struct sphere {
	OFVector3D o;
	float size, max;
	int type;
	struct sphere *next;
};
static struct sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;

void
newsphere(const OFVector3D *o, float max, int type)
{
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		}
		sinit = true;
	}
	if (sempty) {
		struct 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);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glBindTexture(GL_TEXTURE_2D, 4);

	for (struct sphere *p, **pp = &slist; (p = *pp) != NULL;) {
		glPushMatrix();
		float size = p->size / p->max;
		glColor4f(1.0f, 1.0f, 1.0f, 1.0f - size);
		glTranslatef(p->o.x, p->o.z, p->o.y);
		glRotatef(lastmillis / 5.0f, 1, 1, 1);
		glScalef(p->size, p->size, p->size);
		glCallList(1);

Renamed and modified src/server.mm [e4950a7889] to src/server.m [761c67a79c].

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

#define MAXOBUF 100000

void process(ENetPacket *packet, int sender);
void multicast(ENetPacket *packet, int sender);
void disconnect_client(int n, OFString *reason);

void
send(int n, ENetPacket *packet)
{
	if (!packet)
		return;

	switch (clients[n].type) {
	case ST_TCPIP:
		enet_peer_send(clients[n].peer, 0, packet);







|
|







42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

#define MAXOBUF 100000

void process(ENetPacket *packet, int sender);
void multicast(ENetPacket *packet, int sender);
void disconnect_client(int n, OFString *reason);

static void
send_(int n, ENetPacket *packet)
{
	if (!packet)
		return;

	switch (clients[n].type) {
	case ST_TCPIP:
		enet_peer_send(clients[n].peer, 0, packet);
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
	putint(&p, a);
	putint(&p, b);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	if (cn < 0)
		process(packet, -1);
	else
		send(cn, packet);
	if (packet->referenceCount == 0)
		enet_packet_destroy(packet);
}

void
sendservmsg(OFString *msg)
{







|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
	putint(&p, a);
	putint(&p, b);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	if (cn < 0)
		process(packet, -1);
	else
		send_(cn, packet);
	if (packet->referenceCount == 0)
		enet_packet_destroy(packet);
}

void
sendservmsg(OFString *msg)
{
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
void
resetitems()
{
	[sents removeAllObjects];
	notgotitems = true;
}


void
pickup(uint i, int sec, int sender) // server side item pickup, acknowledge
                                    // first client that gets it
{
	if (i >= (uint)sents.count)
		return;
	if (sents[i].spawned) {
		sents[i].spawned = false;
		sents[i].spawnsecs = sec;
		send2(true, sender, SV_ITEMACC, i);







>
|
|
<







111
112
113
114
115
116
117
118
119
120

121
122
123
124
125
126
127
void
resetitems()
{
	[sents removeAllObjects];
	notgotitems = true;
}

// server side item pickup, acknowledge first client that gets it
static void
pickup(uint i, int sec, int sender)

{
	if (i >= (uint)sents.count)
		return;
	if (sents[i].spawned) {
		sents[i].spawned = false;
		sents[i].spawnsecs = sec;
		send2(true, sender, SV_ITEMACC, i);
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
			sgetstr();
			int mapsize = getint(&p);
			sendmaps(sender, @(text), mapsize, p);
			return;
		}

		case SV_RECVMAP:
			send(sender, recvmap(sender));
			return;

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







|







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
			sgetstr();
			int mapsize = getint(&p);
			sendmaps(sender, @(text), mapsize, p);
			return;
		}

		case SV_RECVMAP:
			send_(sender, recvmap(sender));
			return;

		// allows for new features that require no server updates
		case SV_EXT:
			for (int n = getint(&p); n; n--)
				getint(&p);
			break;
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 (e.spawned)
				putint(&p, i);
		}];
		putint(&p, -1);
	}
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	send(n, packet);
}

void
multicast(ENetPacket *packet, int sender)
{
	size_t count = clients.count;
	for (size_t i = 0; i < count; i++)
		if (i != sender)
			send(i, packet);
}

void
localclienttoserver(ENetPacket *packet)
{
	process(packet, 0);








|








|







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 (e.spawned)
				putint(&p, i);
		}];
		putint(&p, -1);
	}
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	send_(n, packet);
}

void
multicast(ENetPacket *packet, int sender)
{
	size_t count = clients.count;
	for (size_t i = 0; i < count; i++)
		if (i != sender)
			send_(i, packet);
}

void
localclienttoserver(ENetPacket *packet)
{
	process(packet, 0);

Renamed and modified src/serverms.mm [67d3e10cf2] to src/serverms.m [c145a0420f].

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
// all server side masterserver and pinging functionality

#include "cube.h"

static ENetSocket mssock = ENET_SOCKET_NULL;

static void
httpgetsend(ENetAddress &ad, OFString *hostname, OFString *req, OFString *ref,
    OFString *agent)
{
	if (ad.host == ENET_HOST_ANY) {
		[OFStdOut writeFormat:@"looking up %@...\n", hostname];
		enet_address_set_host(&ad, hostname.UTF8String);
		if (ad.host == ENET_HOST_ANY)
			return;
	}
	if (mssock != ENET_SOCKET_NULL)
		enet_socket_destroy(mssock);
	mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL);
	if (mssock == ENET_SOCKET_NULL) {
		printf("could not open socket\n");
		return;
	}
	if (enet_socket_connect(mssock, &ad) < 0) {
		printf("could not connect\n");
		return;
	}
	ENetBuffer buf;
	OFString *httpget = [OFString stringWithFormat:@"GET %@ HTTP/1.0\n"
	                                               @"Host: %@\n"
	                                               @"Referer: %@\n"
	                                               @"User-Agent: %@\n\n",
	                              req, hostname, ref, agent];
	buf.data = (void *)httpget.UTF8String;
	buf.dataLength = httpget.UTF8StringLength;
	[OFStdOut writeFormat:@"sending request to %@...\n", hostname];
	enet_socket_send(mssock, NULL, &buf, 1);
}

static void
httpgetrecieve(ENetBuffer &buf)
{
	if (mssock == ENET_SOCKET_NULL)
		return;
	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;
	}
}

static uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");







|


|

|
|









|
















|





|





|
|
|







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
// all server side masterserver and pinging functionality

#include "cube.h"

static ENetSocket mssock = ENET_SOCKET_NULL;

static void
httpgetsend(ENetAddress *ad, OFString *hostname, OFString *req, OFString *ref,
    OFString *agent)
{
	if (ad->host == ENET_HOST_ANY) {
		[OFStdOut writeFormat:@"looking up %@...\n", hostname];
		enet_address_set_host(ad, hostname.UTF8String);
		if (ad->host == ENET_HOST_ANY)
			return;
	}
	if (mssock != ENET_SOCKET_NULL)
		enet_socket_destroy(mssock);
	mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL);
	if (mssock == ENET_SOCKET_NULL) {
		printf("could not open socket\n");
		return;
	}
	if (enet_socket_connect(mssock, ad) < 0) {
		printf("could not connect\n");
		return;
	}
	ENetBuffer buf;
	OFString *httpget = [OFString stringWithFormat:@"GET %@ HTTP/1.0\n"
	                                               @"Host: %@\n"
	                                               @"Referer: %@\n"
	                                               @"User-Agent: %@\n\n",
	                              req, hostname, ref, agent];
	buf.data = (void *)httpget.UTF8String;
	buf.dataLength = httpget.UTF8StringLength;
	[OFStdOut writeFormat:@"sending request to %@...\n", hostname];
	enet_socket_send(mssock, NULL, &buf, 1);
}

static void
httpgetrecieve(ENetBuffer *buf)
{
	if (mssock == ENET_SOCKET_NULL)
		return;
	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;
	}
}

static uchar *
stripheader(uchar *b)
{
	char *s = strstr((char *)b, "\n\r\n");
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
static void
updatemasterserver(int seconds)
{
	// send alive signal to masterserver every hour of uptime
	if (seconds > updmaster) {
		OFString *path = [OFString
		    stringWithFormat:@"%@register.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;
	}
}

static void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(masterb);
	if (busy && mssock == ENET_SOCKET_NULL)
		printf("masterserver reply: %s\n", stripheader(masterrep));
}

uchar *
retrieveservers(uchar *buf, int buflen)
{
	OFString *path =
	    [OFString stringWithFormat:@"%@retrieve.do?item=list", masterpath];
	httpgetsend(
	    masterserver, masterbase, path, @"cubeserver", @"Cube Server");
	ENetBuffer eb;
	buf[0] = 0;
	eb.data = buf;
	eb.dataLength = buflen - 1;
	while (mssock != ENET_SOCKET_NULL)
		httpgetrecieve(eb);
	return stripheader(buf);
}

static ENetSocket pongsock = ENET_SOCKET_NULL;
static OFString *serverdesc;

void







|












|










|





|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
static void
updatemasterserver(int seconds)
{
	// send alive signal to masterserver every hour of uptime
	if (seconds > updmaster) {
		OFString *path = [OFString
		    stringWithFormat:@"%@register.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;
	}
}

static void
checkmasterreply()
{
	bool busy = mssock != ENET_SOCKET_NULL;
	httpgetrecieve(&masterb);
	if (busy && mssock == ENET_SOCKET_NULL)
		printf("masterserver reply: %s\n", stripheader(masterrep));
}

uchar *
retrieveservers(uchar *buf, int buflen)
{
	OFString *path =
	    [OFString stringWithFormat:@"%@retrieve.do?item=list", masterpath];
	httpgetsend(
	    &masterserver, masterbase, path, @"cubeserver", @"Cube Server");
	ENetBuffer eb;
	buf[0] = 0;
	eb.data = buf;
	eb.dataLength = buflen - 1;
	while (mssock != ENET_SOCKET_NULL)
		httpgetrecieve(&eb);
	return stripheader(buf);
}

static ENetSocket pongsock = ENET_SOCKET_NULL;
static OFString *serverdesc;

void

Name change from src/serverutil.mm to src/serverutil.m.

Renamed and modified src/sound.mm [eeaee8a784] to src/sound.m [fab11a9b01].

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

VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(soundloc) * MAXCHAN);
	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);







|







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

VAR(soundbufferlen, 128, 1024, 4096);

void
initsound()
{
	memset(soundlocs, 0, sizeof(struct soundloc) * MAXCHAN);
	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);
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
		vol -= (int)(dist * 3 * soundvol /
		    255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {
			// relative angle of sound along X-Y axis
			float yaw =
			    -atan2(v.x, v.y) - player1.yaw * (PI / 180.0f);
			// range is from 0 (left) to 255 (right)
			pan = int(255.9f * (0.5 * sin(yaw) + 0.5f));
		}
	}
	vol = (vol * MAXVOL) / 255;
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
}








|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
		vol -= (int)(dist * 3 * soundvol /
		    255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {
			// relative angle of sound along X-Y axis
			float yaw =
			    -atan2(v.x, v.y) - player1.yaw * (PI / 180.0f);
			// range is from 0 (left) to 255 (right)
			pan = (int)(255.9f * (0.5 * sin(yaw) + 0.5f));
		}
	}
	vol = (vol * MAXVOL) / 255;
	Mix_Volume(chan, vol);
	Mix_SetPanning(chan, 255 - pan, pan);
}

Renamed and modified src/tools.mm [22d3464477] to src/tools.m [f9cb2ee264].

1
2
3
4
5
6
7
8
9
10
11
// implementation of generic tools

#include "tools.h"
#include <new>

///////////////////////// misc tools ///////////////////////

void
endianswap(
    void *memory, int stride, int length) // little indians as storage format
{



<







1
2
3

4
5
6
7
8
9
10
// implementation of generic tools

#include "tools.h"


///////////////////////// misc tools ///////////////////////

void
endianswap(
    void *memory, int stride, int length) // little indians as storage format
{

Renamed and modified src/weapon.mm [b2ede2d0f5] to src/weapon.m [2e4de9f255].

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
		    DynamicEntity *monster, size_t i, bool *stop) {
			if (i != notthismonster)
				radialeffect(monster, v, i, qdam, p.owner);
		}];
	}
}

inline void
projdamage(DynamicEntity *o, Projectile *p, const OFVector3D *v, int i, int im,
    int qdam)
{
	if (o.state != CS_ALIVE)
		return;

	OFVector3D po = p.o;







|







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
		    DynamicEntity *monster, size_t i, bool *stop) {
			if (i != notthismonster)
				radialeffect(monster, v, i, qdam, p.owner);
		}];
	}
}

static inline void
projdamage(DynamicEntity *o, Projectile *p, const OFVector3D *v, int i, int im,
    int qdam)
{
	if (o.state != CS_ALIVE)
		return;

	OFVector3D po = p.o;