Cube  Check-in [f8f97851f3]

Overview
Comment:Make conoutf take an OFString
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f8f97851f3ec3f0920a95912e7bfe58995f5ca1bcd4c5b5ee3532cb1809eab46
User & Date: js on 2024-08-03 17:02:33
Other Links: manifest | tags
Context
2024-08-03
17:11
Improve clang-format file check-in: 2bc02cf470 user: js tags: trunk
17:02
Make conoutf take an OFString check-in: f8f97851f3 user: js tags: trunk
15:12
Start migrating commands / variables to ObjC++ check-in: f4c57c1df0 user: js tags: trunk
Changes

Modified src/client.mm from [217fc4ecf8] to [65bd73d8a9].

9
10
11
12
13
14
15
16

17
18
19
20
21
22
23

24
25

26
27
28
29
30

31
32

33
34

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59
60

61
62
63
64
65
66
67

68
69
70
71
72
73
74
75
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102


103
104
105
106
107
108
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
172
173
174
int clientnum = -1;   // our client id in the game
bool c2sinit = false; // whether we need to tell the other clients our stats

int
getclientnum()
{
	return clientnum;
};


bool
multiplayer()
{
	// check not correct on listen server?
	if (clienthost)
		conoutf("operation not available in multiplayer");

	return clienthost != NULL;
};


bool
allowedittoggle()
{
	bool allow = !clienthost || gamemode == 1;

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

	return allow;
};


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

void throttle();

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

void
throttle()
{
	if (!clienthost || connecting)
		return;
	assert(ENET_PEER_PACKET_THROTTLE_SCALE == 32);
	enet_peer_throttle_configure(clienthost->peers,
	    throttle_interval * 1000, throttle_accel, throttle_decel);
};


void
newname(char *name)
{
	c2sinit = false;
	strn0cpy(player1->name, name, 16);

};
void
newteam(char *name)
{
	c2sinit = false;
	strn0cpy(player1->team, name, 5);
};


COMMANDN(team, newteam, ARG_1STR);
COMMANDN(name, newname, ARG_1STR);

void
writeclientinfo(FILE *f)
{
	fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team);
};


void
connects(char *servername)
{
	disconnect(1); // reset state
	addserver(servername);

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

	clienthost = enet_host_create(NULL, 1, rate, rate);

	if (clienthost) {
		enet_host_connect(clienthost, &address, 1);
		enet_host_flush(clienthost);
		connecting = lastmillis;
		connattempts = 0;
	} else {
		conoutf("could not connect to server");
		disconnect();
	};
};



void
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;
	clientnum = -1;
	c2sinit = false;
	player1->lifesequence = 0;
	loopv(players) zapdynent(players[i]);

	localdisconnect();

	if (!onlyclean) {
		stop();
		localconnect();
	};
};



void
trydisconnect()
{
	if (!clienthost) {
		conoutf("not connected");
		return;
	};

	if (connecting) {
		conoutf("aborting connection attempt");
		disconnect();
		return;
	};

	conoutf("attempting to disconnect...");
	disconnect(0, !disconnecting);
};


string ctext;
void
toserver(char *text)
{
	conoutf("%s:\f %s", player1->name, text);
	strn0cpy(ctext, text, 80);

};
void
echo(char *text)
{
	conoutf("%s", text);
};


COMMAND(echo, ARG_VARI);
COMMANDN(say, toserver, ARG_VARI);
COMMANDN(connect, connects, ARG_1STR);
COMMANDN(disconnect, trydisconnect, ARG_NONE);

// collect c2s messages conveniently







<
>






|
>

<
>





>

|
>

<
>



















<
>






>
|





<
>








<
>







|


|











|

<
<
>
>



















|














<
<
>
>





|

<
>

|


<
>
|

<
>





|

>
|



|
<
>







9
10
11
12
13
14
15

16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36

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

57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104


105
106
107
108
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

172
173
174
175
176
177
178
179
int clientnum = -1;   // our client id in the game
bool c2sinit = false; // whether we need to tell the other clients our stats

int
getclientnum()
{
	return clientnum;

}

bool
multiplayer()
{
	// check not correct on listen server?
	if (clienthost)
		conoutf(@"operation not available in multiplayer");

	return clienthost != NULL;

}

bool
allowedittoggle()
{
	bool allow = !clienthost || gamemode == 1;

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

	return allow;

}

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

void throttle();

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

void
throttle()
{
	if (!clienthost || connecting)
		return;
	assert(ENET_PEER_PACKET_THROTTLE_SCALE == 32);
	enet_peer_throttle_configure(clienthost->peers,
	    throttle_interval * 1000, throttle_accel, throttle_decel);

}

void
newname(char *name)
{
	c2sinit = false;
	strn0cpy(player1->name, name, 16);
}

void
newteam(char *name)
{
	c2sinit = false;
	strn0cpy(player1->team, name, 5);

}

COMMANDN(team, newteam, ARG_1STR);
COMMANDN(name, newname, ARG_1STR);

void
writeclientinfo(FILE *f)
{
	fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team);

}

void
connects(char *servername)
{
	disconnect(1); // reset state
	addserver(servername);

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

	clienthost = enet_host_create(NULL, 1, rate, rate);

	if (clienthost) {
		enet_host_connect(clienthost, &address, 1);
		enet_host_flush(clienthost);
		connecting = lastmillis;
		connattempts = 0;
	} else {
		conoutf(@"could not connect to server");
		disconnect();


	}
}

void
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;
	clientnum = -1;
	c2sinit = false;
	player1->lifesequence = 0;
	loopv(players) zapdynent(players[i]);

	localdisconnect();

	if (!onlyclean) {
		stop();
		localconnect();


	}
}

void
trydisconnect()
{
	if (!clienthost) {
		conoutf(@"not connected");
		return;

	}
	if (connecting) {
		conoutf(@"aborting connection attempt");
		disconnect();
		return;

	}
	conoutf(@"attempting to disconnect...");
	disconnect(0, !disconnecting);

}

string ctext;
void
toserver(char *text)
{
	conoutf(@"%s:\f %s", player1->name, text);
	strn0cpy(ctext, text, 80);
}

void
echo(char *text)
{
	conoutf(@"%s", text);

}

COMMAND(echo, ARG_VARI);
COMMANDN(say, toserver, ARG_VARI);
COMMANDN(connect, connects, ARG_1STR);
COMMANDN(disconnect, trydisconnect, ARG_NONE);

// collect c2s messages conveniently
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
		return;
	if (num != msgsizelookup(type)) {
		sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type,
		    num, msgsizelookup(type));
		fatal(s);
	};
	if (messages.length() == 100) {
		conoutf("command flood protection (type %d)", type);
		return;
	};
	ivector &msg = messages.add();
	msg.add(num);
	msg.add(rel);
	msg.add(type);
	va_list marker;
	va_start(marker, type);
	loopi(num - 1) msg.add(va_arg(marker, int));
	va_end(marker);
};


void
server_err()
{
	conoutf("server network error, disconnecting...");
	disconnect();
};


int lastupdate = 0, lastping = 0;
string toservermap;
bool senditemstoserver =
    false; // after a map change, since server doesn't have map data

string clientpassword;







|










<
>




|

<
>







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
		return;
	if (num != msgsizelookup(type)) {
		sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type,
		    num, msgsizelookup(type));
		fatal(s);
	};
	if (messages.length() == 100) {
		conoutf(@"command flood protection (type %d)", type);
		return;
	};
	ivector &msg = messages.add();
	msg.add(num);
	msg.add(rel);
	msg.add(type);
	va_list marker;
	va_start(marker, type);
	loopi(num - 1) msg.add(va_arg(marker, int));
	va_end(marker);

}

void
server_err()
{
	conoutf(@"server network error, disconnecting...");
	disconnect();

}

int lastupdate = 0, lastping = 0;
string toservermap;
bool senditemstoserver =
    false; // after a map change, since server doesn't have map data

string clientpassword;
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
void
gets2c() // get updates from the server
{
	ENetEvent event;
	if (!clienthost)
		return;
	if (connecting && lastmillis / 3000 > connecting / 3000) {
		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();
			break;

		case ENET_EVENT_TYPE_RECEIVE:
			if (disconnecting)
				conoutf("attempting to disconnect...");
			else
				localservertoclient(event.packet->data,
				    event.packet->dataLength);
			enet_packet_destroy(event.packet);
			break;

		case ENET_EVENT_TYPE_DISCONNECT:







|



|








|






|







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
void
gets2c() // get updates from the server
{
	ENetEvent event;
	if (!clienthost)
		return;
	if (connecting && lastmillis / 3000 > connecting / 3000) {
		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();
			break;

		case ENET_EVENT_TYPE_RECEIVE:
			if (disconnecting)
				conoutf(@"attempting to disconnect...");
			else
				localservertoclient(event.packet->data,
				    event.packet->dataLength);
			enet_packet_destroy(event.packet);
			break;

		case ENET_EVENT_TYPE_DISCONNECT:

Modified src/clientextras.mm from [1f60f5711b] to [3d0c884313].

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
	    NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_SENDMAP);
	sendstring(mapname, p);
	putint(p, mapsize);
	if (65535 - (p - start) < mapsize) {
		conoutf("map %s is too large to send", mapname);
		free(mapdata);
		enet_packet_destroy(packet);
		return;
	};
	memcpy(p, mapdata, mapsize);
	p += mapsize;
	free(mapdata);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf("sending map %s to server...", mapname);
	sprintf_sd(msg)(
	    "[map %s uploaded to server, \"getmap\" to receive it]", mapname);
	toserver(msg);
}

void
getmap()
{
	ENetPacket *packet =
	    enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_RECVMAP);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf("requesting map from server...");
}

COMMAND(sendmap, ARG_1STR);
COMMAND(getmap, ARG_NONE);







|










|
















|




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
	    NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_SENDMAP);
	sendstring(mapname, p);
	putint(p, mapsize);
	if (65535 - (p - start) < mapsize) {
		conoutf(@"map %s is too large to send", mapname);
		free(mapdata);
		enet_packet_destroy(packet);
		return;
	};
	memcpy(p, mapdata, mapsize);
	p += mapsize;
	free(mapdata);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf(@"sending map %s to server...", mapname);
	sprintf_sd(msg)(
	    "[map %s uploaded to server, \"getmap\" to receive it]", mapname);
	toserver(msg);
}

void
getmap()
{
	ENetPacket *packet =
	    enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE);
	uchar *start = packet->data;
	uchar *p = start + 2;
	putint(p, SV_RECVMAP);
	*(ushort *)start = ENET_HOST_TO_NET_16(p - start);
	enet_packet_resize(packet, p - start);
	sendpackettoserv(packet);
	conoutf(@"requesting map from server...");
}

COMMAND(sendmap, ARG_1STR);
COMMAND(getmap, ARG_NONE);

Modified src/clientgame.mm from [f88f2d9175] to [8334b2a352].

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

void
arenarespawn()
{
	if (arenarespawnwait) {
		if (arenarespawnwait < lastmillis) {
			arenarespawnwait = 0;
			conoutf("new round starting... fight!");
			respawnself();
		};
	} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
		arenadetectwait = 0;
		int alive = 0, dead = 0;
		char *lastteam = NULL;
		bool oneteam = true;
		loopv(players) if (players[i])
		    arenacount(players[i], alive, dead, lastteam, oneteam);
		arenacount(player1, alive, dead, lastteam, oneteam);
		if (dead > 0 && (alive <= 1 || (m_teammode && oneteam))) {
			conoutf(
			    "arena round is over! next round in 5 seconds...");
			if (alive)
				conoutf(
				    "team %s is last man standing", lastteam);
			else
				conoutf("everyone died!");
			arenarespawnwait = lastmillis + 5000;
			arenadetectwait = lastmillis + 10000;
			player1->roll = 0;
		};
	};
};








|












|


|

|







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

void
arenarespawn()
{
	if (arenarespawnwait) {
		if (arenarespawnwait < lastmillis) {
			arenarespawnwait = 0;
			conoutf(@"new round starting... fight!");
			respawnself();
		};
	} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
		arenadetectwait = 0;
		int alive = 0, dead = 0;
		char *lastteam = NULL;
		bool oneteam = true;
		loopv(players) if (players[i])
		    arenacount(players[i], alive, dead, lastteam, oneteam);
		arenacount(player1, alive, dead, lastteam, oneteam);
		if (dead > 0 && (alive <= 1 || (m_teammode && oneteam))) {
			conoutf(
			    @"arena round is over! next round in 5 seconds...");
			if (alive)
				conoutf(
				    @"team %s is last man standing", lastteam);
			else
				conoutf(@"everyone died!");
			arenarespawnwait = lastmillis + 5000;
			arenadetectwait = lastmillis + 10000;
			player1->roll = 0;
		};
	};
};

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

void
respawn()
{
	if (player1->state == CS_DEAD) {
		player1->attacking = false;
		if (m_arena) {
			conoutf("waiting for new round to start...");
			return;
		};
		if (m_sp) {
			nextmode = gamemode;
			changemap(clientmap);
			return;
		}; // if we die in SP we try the same map again







|







214
215
216
217
218
219
220
221
222
223
224
225
226
227
228

void
respawn()
{
	if (player1->state == CS_DEAD) {
		player1->attacking = false;
		if (m_arena) {
			conoutf(@"waiting for new round to start...");
			return;
		};
		if (m_sp) {
			nextmode = gamemode;
			changemap(clientmap);
			return;
		}; // if we die in SP we try the same map again
295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
		d->o.x += dx;
		d->o.y += dy;
		if (collide(d, true, 0, 0))
			return;
		d->o.x -= dx;
		d->o.y -= dy;
	};
	conoutf(
	    "can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y);

	// leave ent at original pos, possibly stuck
};

int spawncycle = -1;
int fixspawn = 2;

void







<
|
>







295
296
297
298
299
300
301

302
303
304
305
306
307
308
309
310
		d->o.x += dx;
		d->o.y += dy;
		if (collide(d, true, 0, 0))
			return;
		d->o.x -= dx;
		d->o.y -= dy;
	};

	conoutf(@"can't find entity spawn spot! (%d, %d)", (int)d->o.x,
	    (int)d->o.y);
	// leave ent at original pos, possibly stuck
};

int spawncycle = -1;
int fixspawn = 2;

void
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442



443
444
445
446
447
448
449
450
451
452
453
454
455
456


457
458
459
460
461
462
463
464
465
466
467
468
469
470


471
472
473
474
475
476
477
478
479
480
481
482

483
484
485
486
487
488
489
490
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
	        : (player1->roll < 0
	                  ? -droll
	                  : (rnd(2) ? droll
	                            : -droll)); // give player a kick depending
	                                        // on amount of damage
	if ((player1->health -= damage) <= 0) {
		if (actor == -2) {
			conoutf("you got killed by %s!", act->name);
		} else if (actor == -1) {
			actor = getclientnum();
			conoutf("you suicided!");
			addmsg(1, 2, SV_FRAGS, --player1->frags);
		} else {
			dynent *a = getclient(actor);
			if (a) {
				if (isteam(a->team, player1->team)) {
					conoutf("you got fragged by a teammate "
					        "(%s)",
					    a->name);
				} else {
					conoutf(
					    "you got fragged by %s", a->name);
				};
			};
		};



		showscores(true);
		addmsg(1, 2, SV_DIED, actor);
		player1->lifesequence++;
		player1->attacking = false;
		player1->state = CS_DEAD;
		player1->pitch = 0;
		player1->roll = 60;
		playsound(S_DIE1 + rnd(2));
		spawnstate(player1);
		player1->lastaction = lastmillis;
	} else {
		playsound(S_PAIN6);
	};
};



void
timeupdate(int timeremain)
{
	if (!timeremain) {
		intermission = true;
		player1->attacking = false;
		conoutf("intermission:");
		conoutf("game has ended!");
		showscores(true);
	} else {
		conoutf("time remaining: %d minutes", timeremain);
	};
};



dynent *
getclient(int cn) // ensure valid entity
{
	if (cn < 0 || cn >= MAXCLIENTS) {
		neterr("clientnum");
		return NULL;
	};
	while (cn >= players.length())
		players.add(NULL);
	return players[cn] ? players[cn] : (players[cn] = newdynent());
};


void
initclient()
{
	clientmap[0] = 0;
	initclientnet();
};

void
startmap(char *name) // called just after a map load
{
	if (netmapstart() && m_sp) {
		gamemode = 0;
		conoutf("coop sp not supported yet");
	};

	sleepwait = 0;
	monsterclear();
	projreset();
	spawncycle = -1;
	spawnplayer(player1);
	player1->frags = 0;
	loopv(players) if (players[i]) players[i]->frags = 0;
	resetspawns();
	strcpy_s(clientmap, name);
	if (editmode)
		toggleedit();
	setvar("gamespeed", 100);
	setvar("fog", 180);
	setvar("fogcolour", 0x8099B3);
	showscores(false);
	intermission = false;
	framesinmap = 0;
	conoutf("game mode is %s", modestr(gamemode));
};

COMMANDN(map, changemap, ARG_1STR);







|


|





|
|



|
<
<
<
>
>
>












<
<
>
>







|
|


|
<
<
>
>











<
>













|
<
>

















|



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439



440
441
442
443
444
445
446
447
448
449
450
451
452
453
454


455
456
457
458
459
460
461
462
463
464
465
466
467
468


469
470
471
472
473
474
475
476
477
478
479
480
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
	        : (player1->roll < 0
	                  ? -droll
	                  : (rnd(2) ? droll
	                            : -droll)); // give player a kick depending
	                                        // on amount of damage
	if ((player1->health -= damage) <= 0) {
		if (actor == -2) {
			conoutf(@"you got killed by %s!", act->name);
		} else if (actor == -1) {
			actor = getclientnum();
			conoutf(@"you suicided!");
			addmsg(1, 2, SV_FRAGS, --player1->frags);
		} else {
			dynent *a = getclient(actor);
			if (a) {
				if (isteam(a->team, player1->team)) {
					conoutf(@"you got fragged by a "
					        @"teammate (%s)",
					    a->name);
				} else {
					conoutf(
					    @"you got fragged by %s", a->name);



				}
			}
		}
		showscores(true);
		addmsg(1, 2, SV_DIED, actor);
		player1->lifesequence++;
		player1->attacking = false;
		player1->state = CS_DEAD;
		player1->pitch = 0;
		player1->roll = 60;
		playsound(S_DIE1 + rnd(2));
		spawnstate(player1);
		player1->lastaction = lastmillis;
	} else {
		playsound(S_PAIN6);


	}
}

void
timeupdate(int timeremain)
{
	if (!timeremain) {
		intermission = true;
		player1->attacking = false;
		conoutf(@"intermission:");
		conoutf(@"game has ended!");
		showscores(true);
	} else {
		conoutf(@"time remaining: %d minutes", timeremain);


	}
}

dynent *
getclient(int cn) // ensure valid entity
{
	if (cn < 0 || cn >= MAXCLIENTS) {
		neterr("clientnum");
		return NULL;
	};
	while (cn >= players.length())
		players.add(NULL);
	return players[cn] ? players[cn] : (players[cn] = newdynent());

}

void
initclient()
{
	clientmap[0] = 0;
	initclientnet();
};

void
startmap(char *name) // called just after a map load
{
	if (netmapstart() && m_sp) {
		gamemode = 0;
		conoutf(@"coop sp not supported yet");

	}
	sleepwait = 0;
	monsterclear();
	projreset();
	spawncycle = -1;
	spawnplayer(player1);
	player1->frags = 0;
	loopv(players) if (players[i]) players[i]->frags = 0;
	resetspawns();
	strcpy_s(clientmap, name);
	if (editmode)
		toggleedit();
	setvar("gamespeed", 100);
	setvar("fog", 180);
	setvar("fogcolour", 0x8099B3);
	showscores(false);
	intermission = false;
	framesinmap = 0;
	conoutf(@"game mode is %s", modestr(gamemode));
};

COMMANDN(map, changemap, ARG_1STR);

Modified src/clients2c.mm from [eb9667cf69] to [e92e9ee6ab].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// client processing of the incoming network stream

#include "cube.h"

extern int clientnum;
extern bool c2sinit, senditemstoserver;
extern string toservermap;
extern string clientpassword;

void
neterr(char *s)
{
	conoutf("illegal network message (%s)", s);
	disconnect();
};

void
changemapserv(char *name, int mode) // forced map change from the server
{
	gamemode = mode;












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// client processing of the incoming network stream

#include "cube.h"

extern int clientnum;
extern bool c2sinit, senditemstoserver;
extern string toservermap;
extern string clientpassword;

void
neterr(char *s)
{
	conoutf(@"illegal network message (%s)", s);
	disconnect();
};

void
changemapserv(char *name, int mode) // forced map change from the server
{
	gamemode = mode;
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
	while (p < end)
		switch (type = getint(p)) {
		case SV_INITS2C: // welcome messsage from the server
		{
			cn = getint(p);
			int prot = getint(p);
			if (prot != PROTOCOL_VERSION) {
				conoutf("you are using a different game "
				        "protocol (you: %d, server: %d)",
				    PROTOCOL_VERSION, prot);
				disconnect();
				return;
			};
			toservermap[0] = 0;
			clientnum = cn; // we are now fully connected
			if (!getint(p))
				strcpy_s(toservermap,
				    getclientmap()); // we are the first client
				                     // on this server, set map
			sgetstr();
			if (text[0] && strcmp(text, clientpassword)) {
				conoutf("you need to set the correct password "
				        "to join this server!");
				disconnect();
				return;
			};
			if (getint(p) == 1) {
				conoutf("server is FULL, disconnecting..");
			};
			break;
		};

		case SV_POS: // position of another client
		{
			cn = getint(p);







|
|












|
|




|







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
	while (p < end)
		switch (type = getint(p)) {
		case SV_INITS2C: // welcome messsage from the server
		{
			cn = getint(p);
			int prot = getint(p);
			if (prot != PROTOCOL_VERSION) {
				conoutf(@"you are using a different game "
				        @"protocol (you: %d, server: %d)",
				    PROTOCOL_VERSION, prot);
				disconnect();
				return;
			};
			toservermap[0] = 0;
			clientnum = cn; // we are now fully connected
			if (!getint(p))
				strcpy_s(toservermap,
				    getclientmap()); // we are the first client
				                     // on this server, set map
			sgetstr();
			if (text[0] && strcmp(text, clientpassword)) {
				conoutf(@"you need to set the correct password "
				        @"to join this server!");
				disconnect();
				return;
			};
			if (getint(p) == 1) {
				conoutf(@"server is FULL, disconnecting..");
			};
			break;
		};

		case SV_POS: // position of another client
		{
			cn = getint(p);
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

159
160
161
162
163
164
165
166
167
168

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

191
192
193
194
195
196
197
198
199
200
201
202
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
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

281
282
283
284
285
286
287

		case SV_SOUND:
			playsound(getint(p), &d->o);
			break;

		case SV_TEXT:
			sgetstr();
			conoutf("%s:\f %s", d->name, text);
			break;

		case SV_MAPCHANGE:
			sgetstr();
			changemapserv(text, getint(p));
			mapchanged = true;
			break;

		case SV_ITEMLIST: {
			int n;
			if (mapchanged) {
				senditemstoserver = false;
				resetspawns();
			};
			while ((n = getint(p)) != -1)
				if (mapchanged)
					setspawn(n, true);
			break;
		};


		case SV_MAPRELOAD: // server requests next map
		{
			getint(p);
			sprintf_sd(nextmapalias)("nextmap_%s", getclientmap());
			char *map =
			    getalias(nextmapalias); // look up map in the cycle
			changemap(map ? map : getclientmap());
			break;
		};


		case SV_INITC2S: // another client either connected or changed
		                 // name/team
		{
			sgetstr();
			if (d->name[0]) // already connected
			{
				if (strcmp(d->name, text))
					conoutf("%s is now known as %s",
					    d->name, text);
			} else // new client
			{
				c2sinit =
				    false; // send new players my info again
				conoutf("connected: %s", text);
			};
			strcpy_s(d->name, text);
			sgetstr();
			strcpy_s(d->team, text);
			d->lifesequence = getint(p);
			break;
		};


		case SV_CDIS:
			cn = getint(p);
			if (!(d = getclient(cn)))
				break;
			conoutf("player %s disconnected",
			    d->name[0] ? d->name : "[incompatible client]");
			zapdynent(players[cn]);
			break;

		case SV_SHOT: {
			int gun = getint(p);
			vec s, e;
			s.x = getint(p) / DMF;
			s.y = getint(p) / DMF;
			s.z = getint(p) / DMF;
			e.x = getint(p) / DMF;
			e.y = getint(p) / DMF;
			e.z = getint(p) / DMF;
			if (gun == GUN_SG)
				createrays(s, e);
			shootv(gun, s, e, d);
			break;
		};


		case SV_DAMAGE: {
			int target = getint(p);
			int damage = getint(p);
			int ls = getint(p);
			if (target == clientnum) {
				if (ls == player1->lifesequence)
					selfdamage(damage, cn, d);
			} else
				playsound(
				    S_PAIN1 + rnd(5), &getclient(target)->o);
			break;
		};


		case SV_DIED: {
			int actor = getint(p);
			if (actor == cn) {
				conoutf("%s suicided", d->name);
			} else if (actor == clientnum) {
				int frags;
				if (isteam(player1->team, d->team)) {
					frags = -1;
					conoutf("you fragged a teammate (%s)",
					    d->name);
				} else {
					frags = 1;
					conoutf("you fragged %s", d->name);
				};

				addmsg(1, 2, SV_FRAGS, player1->frags += frags);
			} else {
				dynent *a = getclient(actor);
				if (a) {
					if (isteam(a->team, d->name)) {
						conoutf("%s fragged his "
						        "teammate (%s)",
						    a->name, d->name);
					} else {
						conoutf("%s fragged %s",
						    a->name, d->name);
					};
				};
			};



			playsound(S_DIE1 + rnd(2), &d->o);
			d->lifesequence++;
			break;
		};


		case SV_FRAGS:
			players[cn]->frags = getint(p);
			break;

		case SV_ITEMPICKUP:
			setspawn(getint(p), false);
			getint(p);
			break;

		case SV_ITEMSPAWN: {
			uint i = getint(p);
			setspawn(i, true);
			if (i >= (uint)ents.length())
				break;
			vec v = {(float)ents[i].x, (float)ents[i].y,
			    (float)ents[i].z};
			playsound(S_ITEMSPAWN, &v);
			break;
		};


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

		case SV_EDITH: // coop editing messages, should be extended to







|


















<
>









<
>








|





|






<
>





|

















<
>












<
>




|




|



|
<
>





|
|


|

<
<
<
>
>
>



<
>



















<
>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167

168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199
200
201
202
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
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
281
282
283
284
285
286
287

		case SV_SOUND:
			playsound(getint(p), &d->o);
			break;

		case SV_TEXT:
			sgetstr();
			conoutf(@"%s:\f %s", d->name, text);
			break;

		case SV_MAPCHANGE:
			sgetstr();
			changemapserv(text, getint(p));
			mapchanged = true;
			break;

		case SV_ITEMLIST: {
			int n;
			if (mapchanged) {
				senditemstoserver = false;
				resetspawns();
			};
			while ((n = getint(p)) != -1)
				if (mapchanged)
					setspawn(n, true);
			break;

		}

		case SV_MAPRELOAD: // server requests next map
		{
			getint(p);
			sprintf_sd(nextmapalias)("nextmap_%s", getclientmap());
			char *map =
			    getalias(nextmapalias); // look up map in the cycle
			changemap(map ? map : getclientmap());
			break;

		}

		case SV_INITC2S: // another client either connected or changed
		                 // name/team
		{
			sgetstr();
			if (d->name[0]) // already connected
			{
				if (strcmp(d->name, text))
					conoutf(@"%s is now known as %s",
					    d->name, text);
			} else // new client
			{
				c2sinit =
				    false; // send new players my info again
				conoutf(@"connected: %s", text);
			};
			strcpy_s(d->name, text);
			sgetstr();
			strcpy_s(d->team, text);
			d->lifesequence = getint(p);
			break;

		}

		case SV_CDIS:
			cn = getint(p);
			if (!(d = getclient(cn)))
				break;
			conoutf(@"player %s disconnected",
			    d->name[0] ? d->name : "[incompatible client]");
			zapdynent(players[cn]);
			break;

		case SV_SHOT: {
			int gun = getint(p);
			vec s, e;
			s.x = getint(p) / DMF;
			s.y = getint(p) / DMF;
			s.z = getint(p) / DMF;
			e.x = getint(p) / DMF;
			e.y = getint(p) / DMF;
			e.z = getint(p) / DMF;
			if (gun == GUN_SG)
				createrays(s, e);
			shootv(gun, s, e, d);
			break;

		}

		case SV_DAMAGE: {
			int target = getint(p);
			int damage = getint(p);
			int ls = getint(p);
			if (target == clientnum) {
				if (ls == player1->lifesequence)
					selfdamage(damage, cn, d);
			} else
				playsound(
				    S_PAIN1 + rnd(5), &getclient(target)->o);
			break;

		}

		case SV_DIED: {
			int actor = getint(p);
			if (actor == cn) {
				conoutf(@"%s suicided", d->name);
			} else if (actor == clientnum) {
				int frags;
				if (isteam(player1->team, d->team)) {
					frags = -1;
					conoutf(@"you fragged a teammate (%s)",
					    d->name);
				} else {
					frags = 1;
					conoutf(@"you fragged %s", d->name);

				}
				addmsg(1, 2, SV_FRAGS, player1->frags += frags);
			} else {
				dynent *a = getclient(actor);
				if (a) {
					if (isteam(a->team, d->name)) {
						conoutf(@"%s fragged his "
						        @"teammate (%s)",
						    a->name, d->name);
					} else {
						conoutf(@"%s fragged %s",
						    a->name, d->name);



					}
				}
			}
			playsound(S_DIE1 + rnd(2), &d->o);
			d->lifesequence++;
			break;

		}

		case SV_FRAGS:
			players[cn]->frags = getint(p);
			break;

		case SV_ITEMPICKUP:
			setspawn(getint(p), false);
			getint(p);
			break;

		case SV_ITEMSPAWN: {
			uint i = getint(p);
			setspawn(i, true);
			if (i >= (uint)ents.length())
				break;
			vec v = {(float)ents[i].x, (float)ents[i].y,
			    (float)ents[i].z};
			playsound(S_ITEMSPAWN, &v);
			break;

		}

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

		case SV_EDITH: // coop editing messages, should be extended to
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
				break;
			case SV_EDITD:
				setvdeltaxy(v, b);
				break;
			case SV_EDITE:
				editequalisexy(v != 0, b);
				break;
			};

			break;
		};


		case SV_EDITENT: // coop edit of ent
		{
			uint i = getint(p);
			while ((uint)ents.length() <= i)
				ents.add().type = NOTUSED;
			int to = ents[i].type;
			ents[i].type = getint(p);
			ents[i].x = getint(p);
			ents[i].y = getint(p);
			ents[i].z = getint(p);
			ents[i].attr1 = getint(p);
			ents[i].attr2 = getint(p);
			ents[i].attr3 = getint(p);
			ents[i].attr4 = getint(p);
			ents[i].spawned = false;
			if (ents[i].type == LIGHT || to == LIGHT)
				calclight();
			break;
		};


		case SV_PING:
			getint(p);
			break;

		case SV_PONG:
			addmsg(0, 2, SV_CLIENTPING,







<
>

<
>



















<
>







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
				break;
			case SV_EDITD:
				setvdeltaxy(v, b);
				break;
			case SV_EDITE:
				editequalisexy(v != 0, b);
				break;

			}
			break;

		}

		case SV_EDITENT: // coop edit of ent
		{
			uint i = getint(p);
			while ((uint)ents.length() <= i)
				ents.add().type = NOTUSED;
			int to = ents[i].type;
			ents[i].type = getint(p);
			ents[i].x = getint(p);
			ents[i].y = getint(p);
			ents[i].z = getint(p);
			ents[i].attr1 = getint(p);
			ents[i].attr2 = getint(p);
			ents[i].attr3 = getint(p);
			ents[i].attr4 = getint(p);
			ents[i].spawned = false;
			if (ents[i].type == LIGHT || to == LIGHT)
				calclight();
			break;

		}

		case SV_PING:
			getint(p);
			break;

		case SV_PONG:
			addmsg(0, 2, SV_CLIENTPING,
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



		case SV_TIMEUP:
			timeupdate(getint(p));
			break;

		case SV_RECVMAP: {
			sgetstr();
			conoutf("received map \"%s\" from server, reloading..",
			    text);
			int mapsize = getint(p);
			writemap(text, mapsize, p);
			p += mapsize;
			changemapserv(text, gamemode);
			break;
		};


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

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


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









|






<
>



|








<
>




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

		case SV_TIMEUP:
			timeupdate(getint(p));
			break;

		case SV_RECVMAP: {
			sgetstr();
			conoutf(@"received map \"%s\" from server, reloading..",
			    text);
			int mapsize = getint(p);
			writemap(text, mapsize, p);
			p += mapsize;
			changemapserv(text, gamemode);
			break;

		}

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

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

		}

		default:
			neterr("type");
			return;


		}
}

Modified src/command.mm from [ccec6a17e5] to [61c1fa172a].

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

			idents[@(name)] = b;
		} else {
			if (b.type == ID_ALIAS)
				b.action = exchangestr(b.action, action);
			else
				conoutf(
				    "cannot redefine builtin %s with an alias",
				    name);
		}
	}
}

COMMAND(alias, ARG_2STR)








|







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

			idents[@(name)] = b;
		} else {
			if (b.type == ID_ALIAS)
				b.action = exchangestr(b.action, action);
			else
				conoutf(
				    @"cannot redefine builtin %s with an alias",
				    name);
		}
	}
}

COMMAND(alias, ARG_2STR)

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
			*(p - 1) = ' '; // hack
		if (c == left)
			brak++;
		else if (c == right)
			brak--;
		else if (!c) {
			p--;
			conoutf("missing \"%c\"", right);
			return NULL;
		}
	}
	char *s = newstring(word, p - word - 1);
	if (left == '(') {
		string t;
		itoa(t,







|







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
			*(p - 1) = ' '; // hack
		if (c == left)
			brak++;
		else if (c == right)
			brak--;
		else if (!c) {
			p--;
			conoutf(@"missing \"%c\"", right);
			return NULL;
		}
	}
	char *s = newstring(word, p - word - 1);
	if (left == '(') {
		string t;
		itoa(t,
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
				return exchangestr(n, t);
			case ID_ALIAS:
				return exchangestr(n, ID.action);
			}
		}
	}

	conoutf("unknown alias lookup: %s", n + 1);
	return n;
}

int
execute(char *p, bool isdown) // all evaluation happens here, recursively
{
	const int MAXWORDS = 25; // limit, remove







|







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
				return exchangestr(n, t);
			case ID_ALIAS:
				return exchangestr(n, ID.action);
			}
		}
	}

	conoutf(@"unknown alias lookup: %s", n + 1);
	return n;
}

int
execute(char *p, bool isdown) // all evaluation happens here, recursively
{
	const int MAXWORDS = 25; // limit, remove
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

		@autoreleasepool {
			Ident *ID = idents[@(c)];

			if (ID == nil) {
				val = ATOI(c);
				if (!val && *c != '0')
					conoutf("unknown command: %s", c);
			} else {
				switch (ID.type) {
				// game defined commands
				case ID_COMMAND:
					// use very ad-hoc function signature,
					// and just call it
					switch (ID.narg) {







|







250
251
252
253
254
255
256
257
258
259
260
261
262
263
264

		@autoreleasepool {
			Ident *ID = idents[@(c)];

			if (ID == nil) {
				val = ATOI(c);
				if (!val && *c != '0')
					conoutf(@"unknown command: %s", c);
			} else {
				switch (ID.type) {
				// game defined commands
				case ID_COMMAND:
					// use very ad-hoc function signature,
					// and just call it
					switch (ID.narg) {
391
392
393
394
395
396
397
398
399
400
401
402

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422

423

424

425

426

427

428

429

430

431

432

433

434

435
436
437
438
439
440
441
442
443
				// game defined variables
				case ID_VAR:
					if (isdown) {
						if (!w[1][0])
							// var with no value
							// just prints its
							// current value
							conoutf("%s = %d", c,
							    *ID.storage);
						else {
							if (ID.min > ID.max) {
								conoutf("variab"

								        "le is "
								        "read-"
								        "only");
							} else {
								int i1 =
								    ATOI(w[1]);
								if (i1 <
								        ID.min ||
								    i1 >
								        ID.max) {
									// clamp
									// to
									// valid
									// range
									i1 =
									    i1 < ID.min
									        ? ID.min
									        : ID.max;
									conoutf(
									    "va"

									    "li"

									    "d "

									    "ra"

									    "ng"

									    "e "

									    "fo"

									    "r "

									    "%s"

									    " i"

									    "s "

									    "%d"

									    ".."

									    "%"
									    "d",
									    c,
									    ID.min,
									    ID.max);
								}
								*ID.storage =
								    i1;
							}







|



|
>
|
|
|
















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







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
				// game defined variables
				case ID_VAR:
					if (isdown) {
						if (!w[1][0])
							// var with no value
							// just prints its
							// current value
							conoutf(@"%s = %d", c,
							    *ID.storage);
						else {
							if (ID.min > ID.max) {
								conoutf(
								    @"variable "
								    @"is "
								    @"read-"
								    @"only");
							} else {
								int i1 =
								    ATOI(w[1]);
								if (i1 <
								        ID.min ||
								    i1 >
								        ID.max) {
									// clamp
									// to
									// valid
									// range
									i1 =
									    i1 < ID.min
									        ? ID.min
									        : ID.max;
									conoutf(
									    @"v"
									    @"a"
									    @"l"
									    @"i"
									    @"d"
									    @" "
									    @"r"
									    @"a"
									    @"n"
									    @"g"
									    @"e"
									    @" "
									    @"f"
									    @"o"
									    @"r"
									    @" "
									    @"%"
									    @"s"
									    @" "
									    @"i"
									    @"s"
									    @" "
									    @"%"
									    @"d"
									    @"."
									    @"."
									    @"%"
									    @"d",
									    c,
									    ID.min,
									    ID.max);
								}
								*ID.storage =
								    i1;
							}
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
	return true;
}

void
exec(char *cfgfile)
{
	if (!execfile(cfgfile))
		conoutf("could not read \"%s\"", cfgfile);
}

void
writecfg()
{
	FILE *f = fopen("config.cfg", "w");
	if (!f)







|







543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
	return true;
}

void
exec(char *cfgfile)
{
	if (!execfile(cfgfile))
		conoutf(@"could not read \"%s\"", cfgfile);
}

void
writecfg()
{
	FILE *f = fopen("config.cfg", "w");
	if (!f)

Modified src/console.mm from [ee8378b098] to [1df29be187].

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
	puts(cl.cref);
#ifndef _WIN32
	fflush(stdout);
#endif
};

void
conoutf(const char *s, ...)
{
	sprintf_sdv(sf, s);
	s = sf;
	int n = 0;
	while (strlen(s) > WORDWRAP) // cut strings to fit on screen
	{
		string t;
		strn0cpy(t, s, WORDWRAP + 1);
		conline(t, n++ != 0);
		s += WORDWRAP;
	};

	conline(s, n != 0);
};

void
renderconsole() // render buffer taking into account time & scrolling
{
	int nd = 0;







|

|
|







<
>







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
	puts(cl.cref);
#ifndef _WIN32
	fflush(stdout);
#endif
};

void
conoutf(OFString *str, ...)
{
	sprintf_sdv(sf, str.UTF8String);
	const char *s = sf;
	int n = 0;
	while (strlen(s) > WORDWRAP) // cut strings to fit on screen
	{
		string t;
		strn0cpy(t, s, WORDWRAP + 1);
		conline(t, n++ != 0);
		s += WORDWRAP;

	}
	conline(s, n != 0);
};

void
renderconsole() // render buffer taking into account time & scrolling
{
	int nd = 0;
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
	for (char *x = key; *x; x++)
		*x = toupper(*x);
	loopi(numkm) if (strcmp(keyms[i].name, key) == 0)
	{
		strcpy_s(keyms[i].action, action);
		return;
	};
	conoutf("unknown key \"%s\"", key);
};

COMMANDN(bind, bindkey, ARG_2STR);

void
saycommand(char *init) // turns input to the command line on or off
{







|







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
	for (char *x = key; *x; x++)
		*x = toupper(*x);
	loopi(numkm) if (strcmp(keyms[i].name, key) == 0)
	{
		strcpy_s(keyms[i].action, action);
		return;
	};
	conoutf(@"unknown key \"%s\"", key);
};

COMMANDN(bind, bindkey, ARG_2STR);

void
saycommand(char *init) // turns input to the command line on or off
{

Modified src/cube.h from [ac53062292] to [9271127084].

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
};

// nasty macros for registering script functions, abuses globals to avoid
// excessive infrastructure
#define COMMANDN(name, fun, nargs)                                             \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		  addcommand(#name, (void (*)())fun, nargs);                   \
		});                                                            \
	}
#define COMMAND(name, nargs) COMMANDN(name, name, nargs)
#define VARP(name, min, cur, max)                                              \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		  name = variable(#name, min, cur, max, &name, NULL, true);    \
		});                                                            \
	}
#define VAR(name, min, cur, max)                                               \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		  name = variable(#name, min, cur, max, &name, NULL, false);   \
		});                                                            \
	}
#define VARF(name, min, cur, max, body)                                        \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		  name = variable(                                             \
		      #name, min, cur, max, &name, var_##name, false);         \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }
#define VARFP(name, min, cur, max, body)                                       \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                                 \
		  name =                                                       \
		      variable(#name, min, cur, max, &name, var_##name, true); \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }

#define ATOI(s) strtol(s, NULL, 0) // supports hexadecimal numbers







|








|







|








|










|







390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
};

// nasty macros for registering script functions, abuses globals to avoid
// excessive infrastructure
#define COMMANDN(name, fun, nargs)                                             \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                          \
		  addcommand(#name, (void (*)())fun, nargs);                   \
		});                                                            \
	}
#define COMMAND(name, nargs) COMMANDN(name, name, nargs)
#define VARP(name, min, cur, max)                                              \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                          \
		  name = variable(#name, min, cur, max, &name, NULL, true);    \
		});                                                            \
	}
#define VAR(name, min, cur, max)                                               \
	int name;                                                              \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                          \
		  name = variable(#name, min, cur, max, &name, NULL, false);   \
		});                                                            \
	}
#define VARF(name, min, cur, max, body)                                        \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                          \
		  name = variable(                                             \
		      #name, min, cur, max, &name, var_##name, false);         \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }
#define VARFP(name, min, cur, max, body)                                       \
	void var_##name();                                                     \
	static int name;                                                       \
	OF_CONSTRUCTOR()                                                       \
	{                                                                      \
		enqueueInit(#name, ^{                                          \
		  name =                                                       \
		      variable(#name, min, cur, max, &name, var_##name, true); \
		});                                                            \
	}                                                                      \
	void var_##name() { body; }

#define ATOI(s) strtol(s, NULL, 0) // supports hexadecimal numbers

Modified src/editing.mm from [df7a7d4fb4] to [35e47e0fb8].

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
};

bool
noteditmode()
{
	correctsel();
	if (!editmode)
		conoutf("this function is only allowed in edit mode");
	return !editmode;
};

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

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







|







|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
};

bool
noteditmode()
{
	correctsel();
	if (!editmode)
		conoutf(@"this function is only allowed in edit mode");
	return !editmode;
};

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

#define EDITSEL                                                                \
	if (noteditmode() || noselection())                                    \
		return;
#define EDITSELMP                                                              \
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
};

void
editundo()
{
	EDITMP;
	if (undos.empty()) {
		conoutf("nothing more to undo");
		return;
	};
	block *p = undos.pop();
	blockpaste(*p);
	free(p);
};








|







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
};

void
editundo()
{
	EDITMP;
	if (undos.empty()) {
		conoutf(@"nothing more to undo");
		return;
	};
	block *p = undos.pop();
	blockpaste(*p);
	free(p);
};

278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
};

void
paste()
{
	EDITMP;
	if (!copybuf) {
		conoutf("nothing to paste");
		return;
	};
	sel.xs = copybuf->xs;
	sel.ys = copybuf->ys;
	correctsel();
	if (!selset || sel.xs != copybuf->xs || sel.ys != copybuf->ys) {
		conoutf("incorrect selection");
		return;
	};
	makeundo();
	copybuf->x = sel.x;
	copybuf->y = sel.y;
	blockpaste(*copybuf);
};







|






|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
};

void
paste()
{
	EDITMP;
	if (!copybuf) {
		conoutf(@"nothing to paste");
		return;
	};
	sel.xs = copybuf->xs;
	sel.ys = copybuf->ys;
	correctsel();
	if (!selset || sel.xs != copybuf->xs || sel.ys != copybuf->ys) {
		conoutf(@"incorrect selection");
		return;
	};
	makeundo();
	copybuf->x = sel.x;
	copybuf->y = sel.y;
	blockpaste(*copybuf);
};
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
void
edittype(int type)
{
	EDITSEL;
	if (type == CORNER &&
	    (sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
	        sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
		conoutf("corner selection must be power of 2 aligned");
		return;
	};
	edittypexy(type, sel);
	addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type);
};

void







|







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
void
edittype(int type)
{
	EDITSEL;
	if (type == CORNER &&
	    (sel.xs != sel.ys || sel.xs == 3 || sel.xs > 4 && sel.xs != 8 ||
	        sel.x & ~-sel.xs || sel.y & ~-sel.ys)) {
		conoutf(@"corner selection must be power of 2 aligned");
		return;
	};
	edittypexy(type, sel);
	addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type);
};

void

Modified src/entities.mm from [a95a1806cd] to [2a578e1163].

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
	case I_YELLOWARMOUR:
		radditem(n, d->armour);
		d->armourtype = A_YELLOW;
		break;

	case I_QUAD:
		radditem(n, d->quadmillis);
		conoutf("you got the quad!");
		break;
	};
};



// these functions are called when the client touches the item

void
additem(int i, int &v, int spawnsec)
{
	if (v < itemstats[ents[i].type - I_SHELLS]
	            .max) // don't pick up if not needed
	{
		addmsg(1, 3, SV_ITEMPICKUP, i,
		    m_classicsp ? 100000
		                : spawnsec); // first ask the server for an ack
		ents[i].spawned = false; // even if someone else gets it first
	};
};



void
teleport(int n, dynent *d) // also used by monsters
{
	int e = -1, tag = ents[n].attr1, beenhere = -1;
	for (;;) {
		e = findentity(TELEDEST, e + 1);
		if (e == beenhere || e < 0) {
			conoutf("no teleport destination for tag %d", tag);
			return;
		};
		if (beenhere < 0)
			beenhere = e;
		if (ents[e].attr2 == tag) {
			d->o.x = ents[e].x;
			d->o.y = ents[e].y;







|

<
<
>
>













<
<
>
>








|







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
	case I_YELLOWARMOUR:
		radditem(n, d->armour);
		d->armourtype = A_YELLOW;
		break;

	case I_QUAD:
		radditem(n, d->quadmillis);
		conoutf(@"you got the quad!");
		break;


	}
}

// these functions are called when the client touches the item

void
additem(int i, int &v, int spawnsec)
{
	if (v < itemstats[ents[i].type - I_SHELLS]
	            .max) // don't pick up if not needed
	{
		addmsg(1, 3, SV_ITEMPICKUP, i,
		    m_classicsp ? 100000
		                : spawnsec); // first ask the server for an ack
		ents[i].spawned = false; // even if someone else gets it first


	}
}

void
teleport(int n, dynent *d) // also used by monsters
{
	int e = -1, tag = ents[n].attr1, beenhere = -1;
	for (;;) {
		e = findentity(TELEDEST, e + 1);
		if (e == beenhere || e < 0) {
			conoutf(@"no teleport destination for tag %d", tag);
			return;
		};
		if (beenhere < 0)
			beenhere = e;
		if (ents[e].attr2 == tag) {
			d->o.x = ents[e].x;
			d->o.y = ents[e].y;
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

void
checkquad(int time)
{
	if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
		player1->quadmillis = 0;
		playsoundc(S_PUPOUT);
		conoutf("quad damage is over");
	};
};



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



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







|
<
<
>
>









<
<
>
>












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

void
checkquad(int time)
{
	if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
		player1->quadmillis = 0;
		playsoundc(S_PUPOUT);
		conoutf(@"quad damage is over");


	}
}

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


	}
}

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

Modified src/main.mm from [65508559f3] to [6994beff69].

102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	bool dedicated = false;
	int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4;
	char *sdesc = "", *ip = "", *master = NULL, *passwd = "";
	islittleendian = *((char *)&islittleendian);

	processInitQueue();

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

	for (int i = 1; i < argc; i++) {
		char *a = &argv[i][2];
		if (argv[i][0] == '-')
			switch (argv[i][1]) {
			case 'd':







|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	bool dedicated = false;
	int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4;
	char *sdesc = "", *ip = "", *master = NULL, *passwd = "";
	islittleendian = *((char *)&islittleendian);

	processInitQueue();

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

	for (int i = 1; i < argc; i++) {
		char *a = &argv[i][2];
		if (argv[i][0] == '-')
			switch (argv[i][1]) {
			case 'd':
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
			case 'p':
				passwd = a;
				break;
			case 'c':
				maxcl = atoi(a);
				break;
			default:
				conoutf("unknown commandline option");
			}
		else
			conoutf("unknown commandline argument");
	};

#ifdef _DEBUG
	par = SDL_INIT_NOPARACHUTE;
	fs = 0;
#endif








|


|







140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
			case 'p':
				passwd = a;
				break;
			case 'c':
				maxcl = atoi(a);
				break;
			default:
				conoutf(@"unknown commandline option");
			}
		else
			conoutf(@"unknown commandline argument");
	};

#ifdef _DEBUG
	par = SDL_INIT_NOPARACHUTE;
	fs = 0;
#endif

Modified src/monster.mm from [c901f05b50] to [1a6c1928af].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// monster.cpp: implements AI for single player monsters, currently client only

#include "cube.h"

dvector monsters;
int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;

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

dvector &
getmonsters()
{
	return monsters;
};
void







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// monster.cpp: implements AI for single player monsters, currently client only

#include "cube.h"

dvector monsters;
int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;

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

dvector &
getmonsters()
{
	return monsters;
};
void
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
        "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);
		type = 0;
	};
	dynent *m = newdynent();
	monstertype *t = &monstertypes[m->mtype = type];
	m->eyeheight = 2.0f;
	m->aboveeye = 1.9f;
	m->radius *= t->bscale / 10.0f;







|







48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
        "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);
		type = 0;
	};
	dynent *m = newdynent();
	monstertype *t = &monstertypes[m->mtype = type];
	m->eyeheight = 2.0f;
	m->aboveeye = 1.9f;
	m->radius *= t->bscale / 10.0f;
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
		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!");
	conoutf("score: %d kills in %d seconds", numkilled,
	    (lastmillis - mtimestart) / 1000);
	monstertotal = 0;
	startintermission();
};

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








|








|
|
|










|







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

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

Modified src/protos.h from [135758e2d7] to [93475710ee].

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extern void alias(char *name, char *action);
extern char *getalias(char *name);
extern void writecfg();

// console
extern void keypress(int code, bool isdown, int cooked);
extern void renderconsole();
extern void conoutf(const char *s, ...);
extern char *getcurcommand();
extern void writebinds(FILE *f);

// init
extern void enqueueInit(const char *name, void (^init)(void));
extern void processInitQueue(void);








|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extern void alias(char *name, char *action);
extern char *getalias(char *name);
extern void writecfg();

// console
extern void keypress(int code, bool isdown, int cooked);
extern void renderconsole();
extern void conoutf(OFString *s, ...);
extern char *getcurcommand();
extern void writebinds(FILE *f);

// init
extern void enqueueInit(const char *name, void (^init)(void));
extern void processInitQueue(void);

Modified src/rendercubes.mm from [53768c91a1] to [93318b77b9].

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{
	showm = !showm;
};
void
mipstats(int a, int b, int c)
{
	if (showm)
		conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c);
};

COMMAND(showmip, ARG_NONE);

#define stripend()                                                             \
	{                                                                      \
		if (floorstrip || deltastrip) {                                \







|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
{
	showm = !showm;
};
void
mipstats(int a, int b, int c)
{
	if (showm)
		conoutf(@"1x1/2x2/4x4: %d / %d / %d", a, b, c);
};

COMMAND(showmip, ARG_NONE);

#define stripend()                                                             \
	{                                                                      \
		if (floorstrip || deltastrip) {                                \

Modified src/renderextras.mm from [6c33874cb2] to [93b4bffbbf].

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
	char *side[] = {"ft", "bk", "lf", "rt", "dn", "up"};
	int texnum = 14;
	loopi(6)
	{
		sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]);
		int xs, ys;
		if (!installtex(texnum + i, path(name), xs, ys, true))
			conoutf("could not load sky textures");
	};
	strcpy_s(lastsky, basename);
};

COMMAND(loadsky, ARG_1STR);

float cursordepth = 0.9f;







|







206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
	char *side[] = {"ft", "bk", "lf", "rt", "dn", "up"};
	int texnum = 14;
	loopi(6)
	{
		sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]);
		int xs, ys;
		if (!installtex(texnum + i, path(name), xs, ys, true))
			conoutf(@"could not load sky textures");
	};
	strcpy_s(lastsky, basename);
};

COMMAND(loadsky, ARG_1STR);

float cursordepth = 0.9f;

Modified src/rendergl.mm from [b149b97fcf] to [155f47c457].

44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	glEnable(GL_CULL_FACE);

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

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

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize);

	purgetextures();

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







|
|







44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
	glEnable(GL_CULL_FACE);

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

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

	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize);

	purgetextures();

	if (!(qsphere = gluNewQuadric()))
		fatal("glu sphere");
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
};

bool
installtex(int tnum, char *texname, int &xs, int &ys, bool clamp)
{
	SDL_Surface *s = IMG_Load(texname);
	if (!s) {
		conoutf("couldn't load texture %s", texname);
		return false;
	};
	if (s->format->BitsPerPixel != 24) {
		conoutf("texture must be 24bpp: %s", texname);
		return false;
	};
	// 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);







|



|







73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
};

bool
installtex(int tnum, char *texname, int &xs, int &ys, bool clamp)
{
	SDL_Surface *s = IMG_Load(texname);
	if (!s) {
		conoutf(@"couldn't load texture %s", texname);
		return false;
	};
	if (s->format->BitsPerPixel != 24) {
		conoutf(@"texture must be 24bpp: %s", texname);
		return false;
	};
	// 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);
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	ys = s->h;
	while (xs > glmaxtexsize || ys > glmaxtexsize) {
		xs /= 2;
		ys /= 2;
	};
	void *scaledimg = s->pixels;
	if (xs != s->w) {
		conoutf("warning: quality loss: scaling %s",
		    texname); // for voodoo cards under linux
		scaledimg = alloc(xs * ys * 3);
		gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels,
		    xs, ys, GL_UNSIGNED_BYTE, scaledimg);
	};
	if (gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB,
	        GL_UNSIGNED_BYTE, scaledimg))







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
	ys = s->h;
	while (xs > glmaxtexsize || ys > glmaxtexsize) {
		xs /= 2;
		ys /= 2;
	};
	void *scaledimg = s->pixels;
	if (xs != s->w) {
		conoutf(@"warning: quality loss: scaling %s",
		    texname); // for voodoo cards under linux
		scaledimg = alloc(xs * ys * 3);
		gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels,
		    xs, ys, GL_UNSIGNED_BYTE, scaledimg);
	};
	if (gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB,
	        GL_UNSIGNED_BYTE, scaledimg))
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	s.num = n;
};

VARFP(gamma, 30, 100, 300, {
	float f = gamma / 100.0f;
	if (SDL_SetGamma(f, f, f) == -1) {
		conoutf(
		    "Could not set gamma (card/driver doesn't support it?)");
		conoutf("sdl: %s", SDL_GetError());
	};
});

void
transplayer()
{
	glLoadIdentity();







|
|







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	s.num = n;
};

VARFP(gamma, 30, 100, 300, {
	float f = gamma / 100.0f;
	if (SDL_SetGamma(f, f, f) == -1) {
		conoutf(
		    @"Could not set gamma (card/driver doesn't support it?)");
		conoutf(@"sdl: %s", SDL_GetError());
	};
});

void
transplayer()
{
	glLoadIdentity();

Modified src/savegamedemo.mm from [c33c84c43c] to [5f329655d6].

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
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
172
173
174
175

void
savestate(char *fn)
{
	stop();
	f = gzopen(fn, "wb9");
	if (!f) {
		conoutf("could not write %s", fn);
		return;
	};
	gzwrite(f, (void *)"CUBESAVE", 8);
	gzputc(f, islittleendian);
	gzputi(SAVEGAMEVERSION);
	gzputi(sizeof(dynent));
	gzwrite(f, getclientmap(), _MAXDEFSTR);
	gzputi(gamemode);
	gzputi(ents.length());
	loopv(ents) gzputc(f, ents[i].spawned);
	gzwrite(f, player1, sizeof(dynent));
	dvector &monsters = getmonsters();
	gzputi(monsters.length());
	loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent));
	gzputi(players.length());
	loopv(players)
	{
		gzput(players[i] == NULL);
		gzwrite(f, players[i], sizeof(dynent));
	};
};



void
savegame(char *name)
{
	if (!m_classicsp) {
		conoutf("can only save classic sp games");
		return;
	};

	sprintf_sd(fn)("savegames/%s.csgz", name);
	savestate(fn);
	stop();
	conoutf("wrote %s", fn);
};


void
loadstate(char *fn)
{
	stop();
	if (multiplayer())
		return;
	f = gzopen(fn, "rb9");
	if (!f) {
		conoutf("could not open %s", fn);
		return;
	};


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


void
loadgame(char *name)
{
	sprintf_sd(fn)("savegames/%s.csgz", name);
	loadstate(fn);
};


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

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







|



















<
<
>
>





|

<
>



|
<
>









|

<
>

















|
|

<
>






<
>





|
|







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
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
172
173
174
175

void
savestate(char *fn)
{
	stop();
	f = gzopen(fn, "wb9");
	if (!f) {
		conoutf(@"could not write %s", fn);
		return;
	};
	gzwrite(f, (void *)"CUBESAVE", 8);
	gzputc(f, islittleendian);
	gzputi(SAVEGAMEVERSION);
	gzputi(sizeof(dynent));
	gzwrite(f, getclientmap(), _MAXDEFSTR);
	gzputi(gamemode);
	gzputi(ents.length());
	loopv(ents) gzputc(f, ents[i].spawned);
	gzwrite(f, player1, sizeof(dynent));
	dvector &monsters = getmonsters();
	gzputi(monsters.length());
	loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent));
	gzputi(players.length());
	loopv(players)
	{
		gzput(players[i] == NULL);
		gzwrite(f, players[i], sizeof(dynent));


	}
}

void
savegame(char *name)
{
	if (!m_classicsp) {
		conoutf(@"can only save classic sp games");
		return;

	}
	sprintf_sd(fn)("savegames/%s.csgz", name);
	savestate(fn);
	stop();
	conoutf(@"wrote %s", fn);

}

void
loadstate(char *fn)
{
	stop();
	if (multiplayer())
		return;
	f = gzopen(fn, "rb9");
	if (!f) {
		conoutf(@"could not open %s", fn);
		return;

	}

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

}

void
loadgame(char *name)
{
	sprintf_sd(fn)("savegames/%s.csgz", name);
	loadstate(fn);

}

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

void
loadgamerest()
{
	if (demoplayback || !f)
		return;
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
	loopi(nplayers) if (!gzget())
	{
		dynent *d = getclient(i);
		assert(d);
		gzread(f, d, sizeof(dynent));
	};

	conoutf("savegame restored");
	if (demoloading)
		startdemo();
	else
		stop();
};

// demo functions

int starttime = 0;
int playbacktime = 0;
int ddamage, bdamage;
vec dorig;

void
record(char *name)
{
	if (m_sp) {
		conoutf("cannot record singleplayer games");
		return;
	};
	int cn = getclientnum();
	if (cn < 0)
		return;
	sprintf_sd(fn)("demos/%s.cdgz", name);
	savestate(fn);
	gzputi(cn);
	conoutf("started recording demo to %s", fn);
	demorecording = true;
	starttime = lastmillis;
	ddamage = bdamage = 0;
};

void
demodamage(int damage, vec &o)







|

















|








|







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
	loopi(nplayers) if (!gzget())
	{
		dynent *d = getclient(i);
		assert(d);
		gzread(f, d, sizeof(dynent));
	};

	conoutf(@"savegame restored");
	if (demoloading)
		startdemo();
	else
		stop();
};

// demo functions

int starttime = 0;
int playbacktime = 0;
int ddamage, bdamage;
vec dorig;

void
record(char *name)
{
	if (m_sp) {
		conoutf(@"cannot record singleplayer games");
		return;
	};
	int cn = getclientnum();
	if (cn < 0)
		return;
	sprintf_sd(fn)("demos/%s.cdgz", name);
	savestate(fn);
	gzputi(cn);
	conoutf(@"started recording demo to %s", fn);
	demorecording = true;
	starttime = lastmillis;
	ddamage = bdamage = 0;
};

void
demodamage(int damage, vec &o)
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
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
		gzput(player1->state);
		gzputi(bdamage);
		bdamage = 0;
		gzputi(ddamage);
		if (ddamage) {
			gzputv(dorig);
			ddamage = 0;
		};

		// FIXME: add all other client state which is not send through
		// the network
	};
};



void
demo(char *name)
{
	sprintf_sd(fn)("demos/%s.cdgz", name);
	loadstate(fn);
	demoloading = true;
};


void
stopreset()
{
	conoutf("demo stopped (%d msec elapsed)", lastmillis - starttime);
	stop();
	loopv(players) zapdynent(players[i]);
	disconnect(0, 0);
};


VAR(demoplaybackspeed, 10, 100, 1000);
int
scaletime(int t)
{
	return (int)(t * (100.0f / demoplaybackspeed)) + starttime;
};


void
readdemotime()
{
	if (gzeof(f) || (playbacktime = gzgeti()) == -1) {
		stopreset();
		return;
	};

	playbacktime = scaletime(playbacktime);
};


void
startdemo()
{
	democlientnum = gzgeti();
	demoplayback = true;
	starttime = lastmillis;
	conoutf("now playing demo");
	dynent *d = getclient(democlientnum);
	assert(d);
	*d = *player1;
	readdemotime();
};


VAR(demodelaymsec, 0, 120, 500);

void
catmulrom(
    vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation
{







<
>


<
<
>
>







<
>




|



<
>






<
>







<
>

<
>







|




<
>







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
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
		gzput(player1->state);
		gzputi(bdamage);
		bdamage = 0;
		gzputi(ddamage);
		if (ddamage) {
			gzputv(dorig);
			ddamage = 0;

		}
		// FIXME: add all other client state which is not send through
		// the network


	}
}

void
demo(char *name)
{
	sprintf_sd(fn)("demos/%s.cdgz", name);
	loadstate(fn);
	demoloading = true;

}

void
stopreset()
{
	conoutf(@"demo stopped (%d msec elapsed)", lastmillis - starttime);
	stop();
	loopv(players) zapdynent(players[i]);
	disconnect(0, 0);

}

VAR(demoplaybackspeed, 10, 100, 1000);
int
scaletime(int t)
{
	return (int)(t * (100.0f / demoplaybackspeed)) + starttime;

}

void
readdemotime()
{
	if (gzeof(f) || (playbacktime = gzgeti()) == -1) {
		stopreset();
		return;

	}
	playbacktime = scaletime(playbacktime);

}

void
startdemo()
{
	democlientnum = gzgeti();
	demoplayback = true;
	starttime = lastmillis;
	conoutf(@"now playing demo");
	dynent *d = getclient(democlientnum);
	assert(d);
	*d = *player1;
	readdemotime();

}

VAR(demodelaymsec, 0, 120, 500);

void
catmulrom(
    vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation
{
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
void
demoplaybackstep()
{
	while (demoplayback && lastmillis >= playbacktime) {
		int len = gzgeti();
		if (len < 1 || len > MAXTRANS) {
			conoutf(
			    "error: huge packet during demo play (%d)", len);
			stopreset();
			return;
		};
		uchar buf[MAXTRANS];
		gzread(f, buf, len);
		localservertoclient(buf, len); // update game state








|







374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
void
demoplaybackstep()
{
	while (demoplayback && lastmillis >= playbacktime) {
		int len = gzgeti();
		if (len < 1 || len > MAXTRANS) {
			conoutf(
			    @"error: huge packet during demo play (%d)", len);
			stopreset();
			return;
		};
		uchar buf[MAXTRANS];
		gzread(f, buf, len);
		localservertoclient(buf, len); // update game state

479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
void
stopn()
{
	if (demoplayback)
		stopreset();
	else
		stop();
	conoutf("demo stopped");
};

COMMAND(record, ARG_1STR);
COMMAND(demo, ARG_1STR);
COMMANDN(stop, stopn, ARG_NONE);

COMMAND(savegame, ARG_1STR);
COMMAND(loadgame, ARG_1STR);







|








479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
void
stopn()
{
	if (demoplayback)
		stopreset();
	else
		stop();
	conoutf(@"demo stopped");
};

COMMAND(record, ARG_1STR);
COMMAND(demo, ARG_1STR);
COMMANDN(stop, stopn, ARG_NONE);

COMMAND(savegame, ARG_1STR);
COMMAND(loadgame, ARG_1STR);

Modified src/serverbrowser.mm from [01d1ff60c2] to [164c8571dd].

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
updatefrommaster()
{
	const int MAXUPD = 32000;
	uchar buf[MAXUPD];
	uchar *reply = retrieveservers(buf, MAXUPD);
	if (!*reply || strstr((char *)reply, "<html>") ||
	    strstr((char *)reply, "<HTML>"))
		conoutf("master server not replying");
	else {
		servers.setsize(0);
		execute((char *)reply);
	};
	servermenu();
};








|







302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
updatefrommaster()
{
	const int MAXUPD = 32000;
	uchar buf[MAXUPD];
	uchar *reply = retrieveservers(buf, MAXUPD);
	if (!*reply || strstr((char *)reply, "<html>") ||
	    strstr((char *)reply, "<HTML>"))
		conoutf(@"master server not replying");
	else {
		servers.setsize(0);
		execute((char *)reply);
	};
	servermenu();
};

Modified src/sound.mm from [ebc1ddf348] to [da17c5965d].

59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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;
	};
#endif
};

void
music(char *name)







|








|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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;
	};
#endif
};

void
music(char *name)
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
			int chan = FSOUND_Stream_Play(FSOUND_FREE, stream);
			if (chan >= 0) {
				FSOUND_SetVolume(
				    chan, (musicvol * MAXVOL) / 255);
				FSOUND_SetPaused(chan, false);
			};
		} else {
			conoutf("could not play music: %s", sn);
		};
#endif
	};
};

COMMAND(music, ARG_1STR);








|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
			int chan = FSOUND_Stream_Play(FSOUND_FREE, stream);
			if (chan >= 0) {
				FSOUND_SetVolume(
				    chan, (musicvol * MAXVOL) / 255);
				FSOUND_SetPaused(chan, false);
			};
		} else {
			conoutf(@"could not play music: %s", sn);
		};
#endif
	};
};

COMMAND(music, ARG_1STR);

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
	else
		soundsatonce = 1;
	lastsoundmillis = lastmillis;
	if (soundsatonce > 5)
		return; // avoid bursts of sounds with heavy packetloss and in
		        // sp
	if (n < 0 || n >= samples.length()) {
		conoutf("unregistered sound: %d", n);
		return;
	};

	if (!samples[n]) {
		sprintf_sd(buf)("packages/sounds/%s.wav", snames[n]);

#ifdef USE_MIXER
		samples[n] = Mix_LoadWAV(path(buf));
#else
		samples[n] =
		    FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0);
#endif

		if (!samples[n]) {
			conoutf("failed to load sample: %s", buf);
			return;
		};
	};

#ifdef USE_MIXER
	int chan = Mix_PlayChannel(-1, samples[n], 0);
#else







|














|







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
	else
		soundsatonce = 1;
	lastsoundmillis = lastmillis;
	if (soundsatonce > 5)
		return; // avoid bursts of sounds with heavy packetloss and in
		        // sp
	if (n < 0 || n >= samples.length()) {
		conoutf(@"unregistered sound: %d", n);
		return;
	};

	if (!samples[n]) {
		sprintf_sd(buf)("packages/sounds/%s.wav", snames[n]);

#ifdef USE_MIXER
		samples[n] = Mix_LoadWAV(path(buf));
#else
		samples[n] =
		    FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0);
#endif

		if (!samples[n]) {
			conoutf(@"failed to load sample: %s", buf);
			return;
		};
	};

#ifdef USE_MIXER
	int chan = Mix_PlayChannel(-1, samples[n], 0);
#else

Modified src/weapon.mm from [ce8dd423e7] to [2770740083].

46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
	else if (s != GUN_RIFLE && player1->ammo[GUN_RIFLE])
		s = GUN_RIFLE;
	else
		s = GUN_FIST;
	if (s != player1->gunselect)
		playsoundc(S_WEAPLOAD);
	player1->gunselect = s;
	// conoutf("%s selected", (int)guns[s].name);
};

int
reloadtime(int gun)
{
	return guns[gun].attackdelay;
};







|







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
	else if (s != GUN_RIFLE && player1->ammo[GUN_RIFLE])
		s = GUN_RIFLE;
	else
		s = GUN_FIST;
	if (s != player1->gunselect)
		playsoundc(S_WEAPLOAD);
	player1->gunselect = s;
	// conoutf(@"%s selected", (int)guns[s].name);
};

int
reloadtime(int gun)
{
	return guns[gun].attackdelay;
};

Modified src/world.mm from [cb52ab13f9] to [0157413baa].

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

void
delent()
{
	int e = closestent();
	if (e < 0) {
		conoutf("no more entities");
		return;
	};
	int t = ents[e].type;
	conoutf("%s entity deleted", entnames[t]);
	ents[e].type = NOTUSED;
	addmsg(1, 10, SV_EDITENT, e, NOTUSED, 0, 0, 0, 0, 0, 0, 0);
	if (t == LIGHT)
		calclight();
};

int
findtype(char *what)
{
	loopi(MAXENTTYPES) if (strcmp(what, entnames[i]) == 0) return i;
	conoutf("unknown entity type \"%s\"", what);
	return NOTUSED;
}

entity *
newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
{
	int type = findtype(what);







|



|










|







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

void
delent()
{
	int e = closestent();
	if (e < 0) {
		conoutf(@"no more entities");
		return;
	};
	int t = ents[e].type;
	conoutf(@"%s entity deleted", entnames[t]);
	ents[e].type = NOTUSED;
	addmsg(1, 10, SV_EDITENT, e, NOTUSED, 0, 0, 0, 0, 0, 0, 0);
	if (t == LIGHT)
		calclight();
};

int
findtype(char *what)
{
	loopi(MAXENTTYPES) if (strcmp(what, entnames[i]) == 0) return i;
	conoutf(@"unknown entity type \"%s\"", what);
	return NOTUSED;
}

entity *
newentity(int x, int y, int z, char *what, int v1, int v2, int v3, int v4)
{
	int type = findtype(what);

Modified src/worldio.mm from [4ce3f10da1] to [e1e98ece3c].

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
void
writemap(char *mname, int msize, uchar *mdata)
{
	setnames(mname);
	backup(cgzname, bakname);
	FILE *f = fopen(cgzname, "wb");
	if (!f) {
		conoutf("could not write map to %s", cgzname);
		return;
	};
	fwrite(mdata, 1, msize, f);
	fclose(f);
	conoutf("wrote map %s as file %s", mname, cgzname);
}

uchar *
readmap(char *mname, int *msize)
{
	setnames(mname);
	uchar *mdata = (uchar *)loadfile(cgzname, msize);
	if (!mdata) {
		conoutf("could not read map %s", cgzname);
		return NULL;
	};
	return mdata;
}

// save map as .cgz file. uses 2 layers of compression: first does simple
// run-length encoding and leaves out data for certain kinds of cubes, then zlib







|




|








|







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
void
writemap(char *mname, int msize, uchar *mdata)
{
	setnames(mname);
	backup(cgzname, bakname);
	FILE *f = fopen(cgzname, "wb");
	if (!f) {
		conoutf(@"could not write map to %s", cgzname);
		return;
	};
	fwrite(mdata, 1, msize, f);
	fclose(f);
	conoutf(@"wrote map %s as file %s", mname, cgzname);
}

uchar *
readmap(char *mname, int *msize)
{
	setnames(mname);
	uchar *mdata = (uchar *)loadfile(cgzname, msize);
	if (!mdata) {
		conoutf(@"could not read map %s", cgzname);
		return NULL;
	};
	return mdata;
}

// save map as .cgz file. uses 2 layers of compression: first does simple
// run-length encoding and leaves out data for certain kinds of cubes, then zlib
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
	toptimize();
	if (!*mname)
		mname = getclientmap();
	setnames(mname);
	backup(cgzname, bakname);
	gzFile f = gzopen(cgzname, "wb9");
	if (!f) {
		conoutf("could not write map to %s", cgzname);
		return;
	};
	hdr.version = MAPVERSION;
	hdr.numents = 0;
	loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++;
	header tmp = hdr;
	endianswap(&tmp.version, sizeof(int), 4);







|







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
	toptimize();
	if (!*mname)
		mname = getclientmap();
	setnames(mname);
	backup(cgzname, bakname);
	gzFile f = gzopen(cgzname, "wb9");
	if (!f) {
		conoutf(@"could not write map to %s", cgzname);
		return;
	};
	hdr.version = MAPVERSION;
	hdr.numents = 0;
	loopv(ents) if (ents[i].type != NOTUSED) hdr.numents++;
	header tmp = hdr;
	endianswap(&tmp.version, sizeof(int), 4);
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
				gzputc(f, s->tag);
			};
		};
		t = s;
	};
	spurge;
	gzclose(f);
	conoutf("wrote map file %s", cgzname);
	settagareas();
};

void
load_world(char *mname) // still supports all map formats that have existed
                        // since the earliest cube betas!
{
	stopifrecording();
	cleardlights();
	pruneundos();
	setnames(mname);
	gzFile f = gzopen(cgzname, "rb9");
	if (!f) {
		conoutf("could not read map %s", cgzname);
		return;
	};
	gzread(f, &hdr, sizeof(header) - sizeof(int) * 16);
	endianswap(&hdr.version, sizeof(int), 4);
	if (strncmp(hdr.head, "CUBE", 4) != 0)
		fatal("while reading map: header malformatted");
	if (hdr.version > MAPVERSION)







|













|







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
				gzputc(f, s->tag);
			};
		};
		t = s;
	};
	spurge;
	gzclose(f);
	conoutf(@"wrote map file %s", cgzname);
	settagareas();
};

void
load_world(char *mname) // still supports all map formats that have existed
                        // since the earliest cube betas!
{
	stopifrecording();
	cleardlights();
	pruneundos();
	setnames(mname);
	gzFile f = gzopen(cgzname, "rb9");
	if (!f) {
		conoutf(@"could not read map %s", cgzname);
		return;
	};
	gzread(f, &hdr, sizeof(header) - sizeof(int) * 16);
	endianswap(&hdr.version, sizeof(int), 4);
	if (strncmp(hdr.head, "CUBE", 4) != 0)
		fatal("while reading map: header malformatted");
	if (hdr.version > MAPVERSION)
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
			texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1;
	};
	gzclose(f);
	calclight();
	settagareas();
	int xs, ys;
	loopi(256) if (texuse) lookuptexture(i, xs, ys);
	conoutf("read map %s (%d milliseconds)", cgzname,
	    SDL_GetTicks() - lastmillis);
	conoutf("%s", hdr.maptitle);
	startmap(mname);
	loopl(256)
	{
		sprintf_sd(aliasname)(
		    "level_trigger_%d", l); // can this be done smarter?
		if (identexists(aliasname))
			alias(aliasname, "");







|

|







353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
			texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1;
	};
	gzclose(f);
	calclight();
	settagareas();
	int xs, ys;
	loopi(256) if (texuse) lookuptexture(i, xs, ys);
	conoutf(@"read map %s (%d milliseconds)", cgzname,
	    SDL_GetTicks() - lastmillis);
	conoutf(@"%s", hdr.maptitle);
	startmap(mname);
	loopl(256)
	{
		sprintf_sd(aliasname)(
		    "level_trigger_%d", l); // can this be done smarter?
		if (identexists(aliasname))
			alias(aliasname, "");