Cube  Check-in [2e931ae3e1]

Overview
Comment:Migrate vec to OFVector3D
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 2e931ae3e1bb8a3ac23d368cfacc7394d07f295bd0bc56a40e0c70190d019d8b
User & Date: js on 2025-03-04 00:14:25
Other Links: manifest | tags
Context
2025-03-04
00:51
Convert md2 and mapmodelinfo to ObjC check-in: cfbd2ec1e6 user: js tags: trunk
00:14
Migrate vec to OFVector3D check-in: 2e931ae3e1 user: js tags: trunk
2025-03-03
23:54
Merge accidental fork check-in: a6544d60c4 user: js tags: trunk
Changes

Modified src/clients2c.mm from [0783f692c0] to [4923c509fd].

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
			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)







|







197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
			conoutf(@"player %s disconnected",
			    d->name[0] ? d->name : "[incompatible client]");
			zapdynent(players[cn]);
			break;

		case SV_SHOT: {
			int gun = getint(p);
			OFVector3D 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)
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
			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);







|
|







270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
			break;

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

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

Modified src/command.mm from [ddbc7f66e7] to [926958b6fe].

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
		b.persist = true;

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

int
variable(OFString *name, int min, int cur, int max, int *storage, void (*fun)(),
    bool persist)







|
|







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
		b.persist = true;

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

int
variable(OFString *name, int min, int cur, int max, int *storage, void (*fun)(),
    bool persist)

Modified src/cube.h from [ea9ed1c660] to [b2a4ca42d9].

98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#define LARGEST_FACTOR 11 // 10 is already insane
#define SOLID(x) ((x)->type == SOLID)
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
#define OUTBORD(x, y)                                                          \
	((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD ||           \
	    (y) >= ssize - MINBORD)

struct vec {
	float x, y, z;
};
struct block {
	int x, y, xs, ys;
};
struct mapmodelinfo {
	int rad, h, zoff, snap;
	const char *name;
};







<
<
<







98
99
100
101
102
103
104



105
106
107
108
109
110
111
#define LARGEST_FACTOR 11 // 10 is already insane
#define SOLID(x) ((x)->type == SOLID)
#define MINBORD 2 // 2 cubes from the edge of the world are always solid
#define OUTBORD(x, y)                                                          \
	((x) < MINBORD || (y) < MINBORD || (x) >= ssize - MINBORD ||           \
	    (y) >= ssize - MINBORD)




struct block {
	int x, y, xs, ys;
};
struct mapmodelinfo {
	int rad, h, zoff, snap;
	const char *name;
};
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
	GUN_SLIMEBALL,
	GUN_BITE,
	NUMGUNS
};

struct dynent // players & monsters
{
	vec o, vel;             // origin, velocity
	float yaw, pitch, roll; // used as vec in one place
	float maxspeed;         // cubes per second, 24 for player
	bool outsidemap;        // from his eyes
	bool inwater;
	bool onfloor, jumpnext;
	int move, strafe;
	bool k_left, k_right, k_up, k_down; // see input code
	int timeinair;                      // used for fake gravity







|
|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
	GUN_SLIMEBALL,
	GUN_BITE,
	NUMGUNS
};

struct dynent // players & monsters
{
	OFVector3D o, vel;      // origin, velocity
	float yaw, pitch, roll; // used as OFVector3D in one place
	float maxspeed;         // cubes per second, 24 for player
	bool outsidemap;        // from his eyes
	bool inwater;
	bool onfloor, jumpnext;
	int move, strafe;
	bool k_left, k_right, k_up, k_down; // see input code
	int timeinair;                      // used for fake gravity
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
	int monsterstate;     // one of M_* below, M_NONE means human
	int mtype;            // see monster.cpp
	dynent *enemy;        // monster wants to kill this entity
	float targetyaw;      // monster wants to look in this direction
	bool blocked, moving; // used by physics to signal ai
	int trigger; // millis at which transition to another monsterstate takes
	             // place
	vec attacktarget; // delayed attacks
	int anger;        // how many times already hit by fellow monster
	string name, team;
};

#define SAVEGAMEVERSION                                                        \
	4 // bump if dynent/netprotocol changes or any other savegame/demo data

enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off







|
|







147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
	int monsterstate;     // one of M_* below, M_NONE means human
	int mtype;            // see monster.cpp
	dynent *enemy;        // monster wants to kill this entity
	float targetyaw;      // monster wants to look in this direction
	bool blocked, moving; // used by physics to signal ai
	int trigger; // millis at which transition to another monsterstate takes
	             // place
	OFVector3D attacktarget; // delayed attacks
	int anger;               // how many times already hit by fellow monster
	string name, team;
};

#define SAVEGAMEVERSION                                                        \
	4 // bump if dynent/netprotocol changes or any other savegame/demo data

enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
extern int sfactor, ssize;     // ssize = 2^sfactor
extern int cubicsize, mipsize; // cubicsize = ssize^2
extern dynent
    *player1; // special client ent that receives input and acts as camera
extern dvector players; // all the other clients (in multiplayer)
extern bool editmode;
extern vector<entity> ents; // map entities
extern vec worldpos;        // current target of the crosshair in the world
extern int lastmillis;      // last time
extern int curtime;         // current frame time
extern int gamemode, nextmode;
extern int xtraverts;
extern bool demoplayback;

#define DMF 16.0f







|







288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
extern int sfactor, ssize;     // ssize = 2^sfactor
extern int cubicsize, mipsize; // cubicsize = ssize^2
extern dynent
    *player1; // special client ent that receives input and acts as camera
extern dvector players; // all the other clients (in multiplayer)
extern bool editmode;
extern vector<entity> ents; // map entities
extern OFVector3D worldpos; // current target of the crosshair in the world
extern int lastmillis;      // last time
extern int curtime;         // current frame time
extern int gamemode, nextmode;
extern int xtraverts;
extern bool demoplayback;

#define DMF 16.0f
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
#define vsub(u, v)                                                             \
	{                                                                      \
		(u).x -= (v).x;                                                \
		(u).y -= (v).y;                                                \
		(u).z -= (v).z;                                                \
	};
#define vdist(d, v, e, s)                                                      \
	vec v = s;                                                             \
	vsub(v, e);                                                            \
	float d = (float)sqrt(dotprod(v, v));
#define vreject(v, u, max)                                                     \
	((v).x > (u).x + (max) || (v).x < (u).x - (max) ||                     \
	    (v).y > (u).y + (max) || (v).y < (u).y - (max))
#define vlinterp(v, f, u, g)                                                   \
	{                                                                      \







|







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#define vsub(u, v)                                                             \
	{                                                                      \
		(u).x -= (v).x;                                                \
		(u).y -= (v).y;                                                \
		(u).z -= (v).z;                                                \
	};
#define vdist(d, v, e, s)                                                      \
	OFVector3D v = s;                                                      \
	vsub(v, e);                                                            \
	float d = (float)sqrt(dotprod(v, v));
#define vreject(v, u, max)                                                     \
	((v).x > (u).x + (max) || (v).x < (u).x - (max) ||                     \
	    (v).y > (u).y + (max) || (v).y < (u).y - (max))
#define vlinterp(v, f, u, g)                                                   \
	{                                                                      \

Modified src/entities.mm from [22648f5295] to [feb802b67f].

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

	case JUMPPAD: {
		static int lastjumppad = 0;
		if (lastmillis - lastjumppad < 300)
			break;
		lastjumppad = lastmillis;
		vec v = {(int)(char)ents[n].attr3 / 10.0f,
		    (int)(char)ents[n].attr2 / 10.0f, ents[n].attr1 / 10.0f};
		player1->vel.z = 0;
		vadd(player1->vel, v);
		playsoundc(S_JUMPPAD);
		break;
	};
	};
};




void
checkitems()
{
	if (editmode)
		return;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		if (!ents[i].spawned && e.type != TELEPORT && e.type != JUMPPAD)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;
		vec v = {(float)e.x, (float)e.y,
		    (float)S(e.x, e.y)->floor + player1->eyeheight};
		vdist(dist, t, player1->o, v);
		if (dist < (e.type == TELEPORT ? 4 : 2.5))
			pickup(i, player1);
	};
};



void
checkquad(int time)
{
	if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
		player1->quadmillis = 0;
		playsoundc(S_PUPOUT);







|
|




<
<
<
>
>
>















|
|



<
<
>
>







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

	case JUMPPAD: {
		static int lastjumppad = 0;
		if (lastmillis - lastjumppad < 300)
			break;
		lastjumppad = lastmillis;
		OFVector3D v = OFMakeVector3D((int)(char)ents[n].attr3 / 10.0f,
		    (int)(char)ents[n].attr2 / 10.0f, ents[n].attr1 / 10.0f);
		player1->vel.z = 0;
		vadd(player1->vel, v);
		playsoundc(S_JUMPPAD);
		break;



	}
	}
}

void
checkitems()
{
	if (editmode)
		return;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		if (!ents[i].spawned && e.type != TELEPORT && e.type != JUMPPAD)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;
		OFVector3D v = OFMakeVector3D(
		    e.x, e.y, (float)S(e.x, e.y)->floor + player1->eyeheight);
		vdist(dist, t, player1->o, v);
		if (dist < (e.type == TELEPORT ? 4 : 2.5))
			pickup(i, player1);


	}
}

void
checkquad(int time)
{
	if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
		player1->quadmillis = 0;
		playsoundc(S_PUPOUT);

Modified src/monster.mm from [89afc85b2b] to [a11e61394d].

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
			monstertotal++;
		};
	};
};

bool
los(float lx, float ly, float lz, float bx, float by, float bz,
    vec &v) // height-correct line of sight for monster shooting/seeing
{
	if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
		return false;
	float dx = bx - lx;
	float dy = by - ly;
	int steps = (int)(sqrt(dx * dx + dy * dy) / 0.9);
	if (!steps)







|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
			monstertotal++;
		};
	};
};

bool
los(float lx, float ly, float lz, float bx, float by, float bz,
    OFVector3D &v) // height-correct line of sight for monster shooting/seeing
{
	if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
		return false;
	float dx = bx - lx;
	float dy = by - ly;
	int steps = (int)(sqrt(dx * dx + dy * dy) / 0.9);
	if (!steps)
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
		y += dy / (float)steps;
		i++;
	};
	return i >= steps;
};

bool
enemylos(dynent *m, vec &v)
{
	v = m->o;
	return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y,
	    m->enemy->o.z, v);
};

// monster AI is sequenced using transitions: they are in a particular state







|







158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
		y += dy / (float)steps;
		i++;
	};
	return i >= steps;
};

bool
enemylos(dynent *m, OFVector3D &v)
{
	v = m->o;
	return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y,
	    m->enemy->o.z, v);
};

// monster AI is sequenced using transitions: they are in a particular state
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
		if (m->trigger < lastmillis)
			transition(m, M_HOME, 1, 100, 200);
		break;

	case M_SLEEP: // state classic sp monster start in, wait for visual
	              // contact
	{
		vec target;
		if (editmode || !enemylos(m, target))
			return; // skip running physics
		normalise(m, enemyyaw);
		float angle = (float)fabs(enemyyaw - m->yaw);
		if (disttoenemy < 8 // the better the angle to the player, the
		                    // further the monster can see/hear
		    || (disttoenemy < 16 && angle < 135) ||







|







249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
		if (m->trigger < lastmillis)
			transition(m, M_HOME, 1, 100, 200);
		break;

	case M_SLEEP: // state classic sp monster start in, wait for visual
	              // contact
	{
		OFVector3D target;
		if (editmode || !enemylos(m, target))
			return; // skip running physics
		normalise(m, enemyyaw);
		float angle = (float)fabs(enemyyaw - m->yaw);
		if (disttoenemy < 8 // the better the angle to the player, the
		                    // further the monster can see/hear
		    || (disttoenemy < 16 && angle < 135) ||
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
		};
		break;

	case M_HOME: // monster has visual contact, heads straight for player
	             // and may want to shoot at any time
		m->targetyaw = enemyyaw;
		if (m->trigger < lastmillis) {
			vec target;
			if (!enemylos(
			        m, target)) // no visual contact anymore, let
			                    // monster get as close as possible
			                    // then search for player
			{
				transition(m, M_HOME, 1, 800, 500);
			} else // the closer the monster is the more likely he







|







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
		};
		break;

	case M_HOME: // monster has visual contact, heads straight for player
	             // and may want to shoot at any time
		m->targetyaw = enemyyaw;
		if (m->trigger < lastmillis) {
			OFVector3D target;
			if (!enemylos(
			        m, target)) // no visual contact anymore, let
			                    // monster get as close as possible
			                    // then search for player
			{
				transition(m, M_HOME, 1, 800, 500);
			} else // the closer the monster is the more likely he
376
377
378
379
380
381
382

383
384
385
386
387
388
389
390
	            // used
	{
		entity &e = ents[i];
		if (e.type != TELEPORT)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;

		vec v = {(float)e.x, (float)e.y, (float)S(e.x, e.y)->floor};
		loopv(monsters) if (monsters[i]->state == CS_DEAD)
		{
			if (lastmillis - monsters[i]->lastaction < 2000) {
				monsters[i]->move = 0;
				moveplayer(monsters[i], 1, false);
			};
		}







>
|







376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
	            // used
	{
		entity &e = ents[i];
		if (e.type != TELEPORT)
			continue;
		if (OUTBORD(e.x, e.y))
			continue;
		OFVector3D v =
		    OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
		loopv(monsters) if (monsters[i]->state == CS_DEAD)
		{
			if (lastmillis - monsters[i]->lastaction < 2000) {
				monsters[i]->move = 0;
				moveplayer(monsters[i], 1, false);
			};
		}

Modified src/physics.mm from [864d2d9b42] to [023335c214].

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

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

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

	d.x = (float)(pl->move * cos(rad(pl->yaw - 90)));
	d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
	d.z = 0;

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







|







236
237
238
239
240
241
242
243
244
245
246
247
248
249
250

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

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

	d.x = (float)(pl->move * cos(rad(pl->yaw - 90)));
	d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
	d.z = 0;

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

Modified src/protos.h from [e663cdaad7] to [4c05d76f8a].

122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
extern void settagareas();
extern entity *newentity(
    int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4);

// worldlight
extern void calclight();
extern void dodynlight(
    vec &vold, vec &v, int reach, int strength, dynent *owner);
extern void cleardlights();
extern block *blockcopy(block &b);
extern void blockpaste(block &b);

// worldrender
extern void render_world(float vx, float vy, float vh, int yaw, int pitch,
    float widef, int w, int h);







|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
extern void settagareas();
extern entity *newentity(
    int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4);

// worldlight
extern void calclight();
extern void dodynlight(
    OFVector3D &vold, OFVector3D &v, int reach, int strength, dynent *owner);
extern void cleardlights();
extern block *blockcopy(block &b);
extern void blockpaste(block &b);

// worldrender
extern void render_world(float vx, float vy, float vh, int yaw, int pitch,
    float widef, int w, int h);
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
extern void pruneundos(int maxremain = 0);

// renderextras
extern void line(int x1, int y1, float z1, int x2, int y2, float z2);
extern void box(block &b, float z1, float z2, float z3, float z4);
extern void dot(int x, int y, float z);
extern void linestyle(float width, int r, int g, int b);
extern void newsphere(vec &o, float max, int type);
extern void renderspheres(int time);
extern void gl_drawhud(
    int w, int h, int curfps, int nquads, int curvert, bool underwater);
extern void readdepth(int w, int h);
extern void blendbox(int x1, int y1, int x2, int y2, bool border);
extern void damageblend(int n);

// renderparticles
extern void setorient(vec &r, vec &u);
extern void particle_splash(int type, int num, int fade, vec &p);
extern void particle_trail(int type, int fade, vec &from, vec &to);

extern void render_particles(int time);

// worldio
extern void save_world(OFString *fname);
extern void load_world(char *mname);
extern void writemap(char *mname, int msize, uchar *mdata);
extern uchar *readmap(const char *mname, int *msize);
extern void loadgamerest();
extern void incomingdemodata(uchar *buf, int len, bool extras = false);
extern void demoplaybackstep();
extern void stop();
extern void stopifrecording();
extern void demodamage(int damage, vec &o);
extern void demoblend(int damage);

// physics
extern void moveplayer(dynent *pl, int moveres, bool local);
extern bool collide(dynent *d, bool spawn, float drop, float rise);
extern void entinmap(dynent *d);
extern void setentphysics(int mml, int mmr);
extern void physicsframe();

// sound
extern void playsound(int n, vec *loc = 0);
extern void playsoundc(int n);
extern void initsound();
extern void cleansound();

// rendermd2
extern void rendermodel(OFString *mdl, int frame, int range, int tex, float rad,
    float x, float y, float z, float yaw, float pitch, bool teammate,







|








|
|
|
>












|










|







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
extern void pruneundos(int maxremain = 0);

// renderextras
extern void line(int x1, int y1, float z1, int x2, int y2, float z2);
extern void box(block &b, float z1, float z2, float z3, float z4);
extern void dot(int x, int y, float z);
extern void linestyle(float width, int r, int g, int b);
extern void newsphere(OFVector3D &o, float max, int type);
extern void renderspheres(int time);
extern void gl_drawhud(
    int w, int h, int curfps, int nquads, int curvert, bool underwater);
extern void readdepth(int w, int h);
extern void blendbox(int x1, int y1, int x2, int y2, bool border);
extern void damageblend(int n);

// renderparticles
extern void setorient(OFVector3D &r, OFVector3D &u);
extern void particle_splash(int type, int num, int fade, OFVector3D &p);
extern void particle_trail(
    int type, int fade, OFVector3D &from, OFVector3D &to);
extern void render_particles(int time);

// worldio
extern void save_world(OFString *fname);
extern void load_world(char *mname);
extern void writemap(char *mname, int msize, uchar *mdata);
extern uchar *readmap(const char *mname, int *msize);
extern void loadgamerest();
extern void incomingdemodata(uchar *buf, int len, bool extras = false);
extern void demoplaybackstep();
extern void stop();
extern void stopifrecording();
extern void demodamage(int damage, OFVector3D &o);
extern void demoblend(int damage);

// physics
extern void moveplayer(dynent *pl, int moveres, bool local);
extern bool collide(dynent *d, bool spawn, float drop, float rise);
extern void entinmap(dynent *d);
extern void setentphysics(int mml, int mmr);
extern void physicsframe();

// sound
extern void playsound(int n, OFVector3D *loc = 0);
extern void playsoundc(int n);
extern void initsound();
extern void cleansound();

// rendermd2
extern void rendermodel(OFString *mdl, int frame, int range, int tex, float rad,
    float x, float y, float z, float yaw, float pitch, bool teammate,
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    int seconds, bool isfull);
extern void servermsinit(const char *master, const char *sdesc, bool listen);
extern void sendmaps(int n, string mapname, int mapsize, uchar *mapdata);
extern ENetPacket *recvmap(int n);

// weapon
extern void selectgun(int a = -1, int b = -1, int c = -1);
extern void shoot(dynent *d, vec &to);
extern void shootv(
    int gun, vec &from, vec &to, dynent *d = 0, bool local = false);
extern void createrays(vec &from, vec &to);
extern void moveprojectiles(float time);
extern void projreset();
extern char *playerincrosshair();
extern int reloadtime(int gun);

// monster
extern void monsterclear();







|
|
|
|







233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
    int seconds, bool isfull);
extern void servermsinit(const char *master, const char *sdesc, bool listen);
extern void sendmaps(int n, string mapname, int mapsize, uchar *mapdata);
extern ENetPacket *recvmap(int n);

// weapon
extern void selectgun(int a = -1, int b = -1, int c = -1);
extern void shoot(dynent *d, OFVector3D &to);
extern void shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d = 0,
    bool local = false);
extern void createrays(OFVector3D &from, OFVector3D &to);
extern void moveprojectiles(float time);
extern void projreset();
extern char *playerincrosshair();
extern int reloadtime(int gun);

// monster
extern void monsterclear();

Modified src/renderextras.mm from [a7072e43d2] to [4e49ca5579].

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	glEnable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glDepthMask(GL_TRUE);
};

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

void
newsphere(vec &o, float max, int type)
{
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		};







|








|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	glEnable(GL_BLEND);
	glEnable(GL_TEXTURE_2D);
	glDepthMask(GL_TRUE);
};

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

void
newsphere(OFVector3D &o, float max, int type)
{
	if (!sinit) {
		loopi(MAXSPHERES)
		{
			spheres[i].next = sempty;
			sempty = &spheres[i];
		};
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
	if (!editmode)
		return;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		vec v = {(float)e.x, (float)e.y, (float)e.z};
		particle_splash(2, 2, 40, v);
	};

	int e = closestent();
	if (e >= 0) {
		entity &c = ents[e];
		@autoreleasepool {
			sprintf_s(closeent)(
			    "closest entity = %s (%d, %d, %d, %d), "
			    "selection = (%d, %d)",







|

<
>







180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
	if (!editmode)
		return;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
		particle_splash(2, 2, 40, v);

	}
	int e = closestent();
	if (e >= 0) {
		entity &c = ents[e];
		@autoreleasepool {
			sprintf_s(closeent)(
			    "closest entity = %s (%d, %d, %d, %d), "
			    "selection = (%d, %d)",
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	}
}
COMMAND(loadsky, ARG_1STR)

float cursordepth = 0.9f;
GLint viewport[4];
GLdouble mm[16], pm[16];
vec worldpos;

void
readmatrices()
{
	glGetIntegerv(GL_VIEWPORT, viewport);
	glGetDoublev(GL_MODELVIEW_MATRIX, mm);
	glGetDoublev(GL_PROJECTION_MATRIX, pm);







|







222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
	}
}
COMMAND(loadsky, ARG_1STR)

float cursordepth = 0.9f;
GLint viewport[4];
GLdouble mm[16], pm[16];
OFVector3D worldpos;

void
readmatrices()
{
	glGetIntegerv(GL_VIEWPORT, viewport);
	glGetDoublev(GL_MODELVIEW_MATRIX, mm);
	glGetDoublev(GL_PROJECTION_MATRIX, pm);
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
	    w / 2, h / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
	double worldx = 0, worldy = 0, worldz = 0;
	gluUnProject(w / 2, h / 2, depthcorrect(cursordepth), mm, pm, viewport,
	    &worldx, &worldz, &worldy);
	worldpos.x = (float)worldx;
	worldpos.y = (float)worldy;
	worldpos.z = (float)worldz;
	vec r = {(float)mm[0], (float)mm[4], (float)mm[8]};
	vec u = {(float)mm[1], (float)mm[5], (float)mm[9]};
	setorient(r, u);
};

void
drawicon(float tx, float ty, int x, int y)
{
	glBindTexture(GL_TEXTURE_2D, 5);







|
|







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
	    w / 2, h / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth);
	double worldx = 0, worldy = 0, worldz = 0;
	gluUnProject(w / 2, h / 2, depthcorrect(cursordepth), mm, pm, viewport,
	    &worldx, &worldz, &worldy);
	worldpos.x = (float)worldx;
	worldpos.y = (float)worldy;
	worldpos.z = (float)worldz;
	OFVector3D r = OFMakeVector3D(mm[0], mm[4], mm[8]);
	OFVector3D u = OFMakeVector3D(mm[1], mm[5], mm[9]);
	setorient(r, u);
};

void
drawicon(float tx, float ty, int x, int y)
{
	glBindTexture(GL_TEXTURE_2D, 5);

Modified src/rendermd2.mm from [567a64e197] to [a893c08a25].

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
	int numGlCommands;
	int *glCommands;
	int numTriangles;
	int frameSize;
	int numFrames;
	int numVerts;
	char *frames;
	vec **mverts;
	int displaylist;
	int displaylistverts;

	mapmodelinfo mmi;
	char *loadname;
	int mdlnum;
	bool loaded;

	bool load(char *filename);
	void render(vec &light, int numFrame, int range, float x, float y,
	    float z, float yaw, float pitch, float scale, float speed, int snap,
	    int basetime);
	void scale(int frame, float scale, int sn);

	md2()
	    : numGlCommands(0), frameSize(0), numFrames(0), displaylist(0),
	      loaded(false) {};

	~md2()







|









|
|
|







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
	int numGlCommands;
	int *glCommands;
	int numTriangles;
	int frameSize;
	int numFrames;
	int numVerts;
	char *frames;
	OFVector3D **mverts;
	int displaylist;
	int displaylistverts;

	mapmodelinfo mmi;
	char *loadname;
	int mdlnum;
	bool loaded;

	bool load(char *filename);
	void render(OFVector3D &light, int numFrame, int range, float x,
	    float y, float z, float yaw, float pitch, float scale, float speed,
	    int snap, int basetime);
	void scale(int frame, float scale, int sn);

	md2()
	    : numGlCommands(0), frameSize(0), numFrames(0), displaylist(0),
	      loaded(false) {};

	~md2()
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
	numGlCommands = header.numGlCommands;
	frameSize = header.frameSize;
	numTriangles = header.numTriangles;
	numVerts = header.numVertices;

	fclose(file);

	mverts = new vec *[numFrames];
	loopj(numFrames) mverts[j] = NULL;

	return true;
};

float
snap(int sn, float f)
{
	return sn ? (float)(((int)(f + sn * 0.5f)) & (~(sn - 1))) : f;
};

void
md2::scale(int frame, float scale, int sn)
{
	mverts[frame] = new vec[numVerts];
	md2_frame *cf = (md2_frame *)((char *)frames + frameSize * frame);
	float sc = 16.0f / scale;
	loop(vi, numVerts)
	{
		uchar *cv = (uchar *)&cf->vertices[vi].vertex;
		vec *v = &(mverts[frame])[vi];
		v->x = (snap(sn, cv[0] * cf->scale[0]) + cf->translate[0]) / sc;
		v->y =
		    -(snap(sn, cv[1] * cf->scale[1]) + cf->translate[1]) / sc;
		v->z = (snap(sn, cv[2] * cf->scale[2]) + cf->translate[2]) / sc;
	};
};

void
md2::render(vec &light, int frame, int range, float x, float y, float z,
    float yaw, float pitch, float sc, float speed, int snap, int basetime)
{
	loopi(range) if (!mverts[frame + i]) scale(frame + i, sc, snap);

	glPushMatrix();
	glTranslatef(x, y, z);
	glRotatef(yaw + 180, 0, -1, 0);







|














|





|








|







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
	numGlCommands = header.numGlCommands;
	frameSize = header.frameSize;
	numTriangles = header.numTriangles;
	numVerts = header.numVertices;

	fclose(file);

	mverts = new OFVector3D *[numFrames];
	loopj(numFrames) mverts[j] = NULL;

	return true;
};

float
snap(int sn, float f)
{
	return sn ? (float)(((int)(f + sn * 0.5f)) & (~(sn - 1))) : f;
};

void
md2::scale(int frame, float scale, int sn)
{
	mverts[frame] = new OFVector3D[numVerts];
	md2_frame *cf = (md2_frame *)((char *)frames + frameSize * frame);
	float sc = 16.0f / scale;
	loop(vi, numVerts)
	{
		uchar *cv = (uchar *)&cf->vertices[vi].vertex;
		OFVector3D *v = &(mverts[frame])[vi];
		v->x = (snap(sn, cv[0] * cf->scale[0]) + cf->translate[0]) / sc;
		v->y =
		    -(snap(sn, cv[1] * cf->scale[1]) + cf->translate[1]) / sc;
		v->z = (snap(sn, cv[2] * cf->scale[2]) + cf->translate[2]) / sc;
	};
};

void
md2::render(OFVector3D &light, int frame, int range, float x, float y, float z,
    float yaw, float pitch, float sc, float speed, int snap, int basetime)
{
	loopi(range) if (!mverts[frame + i]) scale(frame + i, sc, snap);

	glPushMatrix();
	glTranslatef(x, y, z);
	glRotatef(yaw + 180, 0, -1, 0);
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
		int fr1 = (int)(time / speed);
		float frac1 = (time - fr1 * speed) / speed;
		float frac2 = 1 - frac1;
		fr1 = fr1 % range + frame;
		int fr2 = fr1 + 1;
		if (fr2 >= frame + range)
			fr2 = frame;
		vec *verts1 = mverts[fr1];
		vec *verts2 = mverts[fr2];

		for (int *command = glCommands; (*command) != 0;) {
			int numVertex = *command++;
			if (numVertex > 0) {
				glBegin(GL_TRIANGLE_STRIP);
			} else {
				glBegin(GL_TRIANGLE_FAN);
				numVertex = -numVertex;
			};

			loopi(numVertex)
			{
				float tu = *((float *)command++);
				float tv = *((float *)command++);
				glTexCoord2f(tu, tv);
				int vn = *command++;
				vec &v1 = verts1[vn];
				vec &v2 = verts2[vn];
#define ip(c) v1.c *frac2 + v2.c *frac1
				glVertex3f(ip(x), ip(z), ip(y));
			};

			xtraverts += numVertex;

			glEnd();







|
|
















|
|







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
		int fr1 = (int)(time / speed);
		float frac1 = (time - fr1 * speed) / speed;
		float frac2 = 1 - frac1;
		fr1 = fr1 % range + frame;
		int fr2 = fr1 + 1;
		if (fr2 >= frame + range)
			fr2 = frame;
		OFVector3D *verts1 = mverts[fr1];
		OFVector3D *verts2 = mverts[fr2];

		for (int *command = glCommands; (*command) != 0;) {
			int numVertex = *command++;
			if (numVertex > 0) {
				glBegin(GL_TRIANGLE_STRIP);
			} else {
				glBegin(GL_TRIANGLE_FAN);
				numVertex = -numVertex;
			};

			loopi(numVertex)
			{
				float tu = *((float *)command++);
				float tv = *((float *)command++);
				glTexCoord2f(tu, tv);
				int vn = *command++;
				OFVector3D &v1 = verts1[vn];
				OFVector3D &v2 = verts2[vn];
#define ip(c) v1.c *frac2 + v2.c *frac1
				glVertex3f(ip(x), ip(z), ip(y));
			};

			xtraverts += numVertex;

			glEnd();
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

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

	int ix = (int)x;
	int iy = (int)z;
	vec light = {1.0f, 1.0f, 1.0f};

	if (!OUTBORD(ix, iy)) {
		sqr *s = S(ix, iy);
		float ll = 256.0f; // 0.96f;
		float of = 0.0f;   // 0.1f;
		light.x = s->r / ll + of;
		light.y = s->g / ll + of;







|







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

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

	int ix = (int)x;
	int iy = (int)z;
	OFVector3D light = OFMakeVector3D(1, 1, 1);

	if (!OUTBORD(ix, iy)) {
		sqr *s = S(ix, iy);
		float ll = 256.0f; // 0.96f;
		float of = 0.0f;   // 0.1f;
		light.x = s->r / ll + of;
		light.y = s->g / ll + of;

Modified src/renderparticles.mm from [4855ce96c9] to [d554171715].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// renderparticles.cpp

#include "cube.h"

const int MAXPARTICLES = 10500;
const int NUMPARTCUTOFF = 20;
struct particle {
	vec o, d;
	int fade, type;
	int millis;
	particle *next;
};
particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL;
bool parinit = false;

VARP(maxparticles, 100, 2000, MAXPARTICLES - 500);

void
newparticle(vec &o, vec &d, int fade, int type)
{
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];
		};







|










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// renderparticles.cpp

#include "cube.h"

const int MAXPARTICLES = 10500;
const int NUMPARTCUTOFF = 20;
struct particle {
	OFVector3D o, d;
	int fade, type;
	int millis;
	particle *next;
};
particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL;
bool parinit = false;

VARP(maxparticles, 100, 2000, MAXPARTICLES - 500);

void
newparticle(OFVector3D &o, OFVector3D &d, int fade, int type)
{
	if (!parinit) {
		loopi(MAXPARTICLES)
		{
			particles[i].next = parempty;
			parempty = &particles[i];
		};
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
		parlist = p;
	};
};

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

vec right, up;

void
setorient(vec &r, vec &u)
{
	right = r;
	up = u;
};


void
render_particles(int time)
{
	if (demoplayback && demotracking) {
		vec nom = {0, 0, 0};
		newparticle(player1->o, nom, 100000000, 8);
	};


	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
	glDisable(GL_FOG);

	struct parttype {







|


|



<
>





|

<
>







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
		parlist = p;
	};
};

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

OFVector3D right, up;

void
setorient(OFVector3D &r, OFVector3D &u)
{
	right = r;
	up = u;

}

void
render_particles(int time)
{
	if (demoplayback && demotracking) {
		OFVector3D nom = OFMakeVector3D(0, 0, 0);
		newparticle(player1->o, nom, 100000000, 8);

	}

	glDepthMask(GL_FALSE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
	glDisable(GL_FOG);

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


			*pp = p->next;
			p->next = parempty;
			parempty = p;
		} else {
			if (pt->gr)
				p->o.z -= ((lastmillis - p->millis) / 3.0f) *
				          curtime / (pt->gr * 10000);
			vec a = p->d;
			vmul(a, time);
			vdiv(a, 20000.0f);
			vadd(p->o, a);
			pp = &p->next;
		};
	};



	glEnable(GL_FOG);
	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
};

void
particle_splash(int type, int num, int fade, vec &p)
{
	loopi(num)
	{
		const int radius = type == 5 ? 50 : 150;
		int x, y, z;
		do {
			x = rnd(radius * 2) - radius;
			y = rnd(radius * 2) - radius;
			z = rnd(radius * 2) - radius;
		} while (x * x + y * y + z * z > radius * radius);
		vec d = {(float)x, (float)y, (float)z};
		newparticle(p, d, rnd(fade * 3), type);
	};
};



void
particle_trail(int type, int fade, vec &s, vec &e)
{
	vdist(d, v, s, e);
	vdiv(v, d * 2 + 0.1f);
	vec p = s;
	loopi((int)d * 2)
	{
		vadd(p, v);
		vec d = {
		    float(rnd(11) - 5), float(rnd(11) - 5), float(rnd(11) - 5)};
		newparticle(p, d, rnd(fade) + fade, type);
	};
};









|




<
<
>
>







|










|

<
<
|
>
>

|



|



|
|

<
<
>
>
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
			*pp = p->next;
			p->next = parempty;
			parempty = p;
		} else {
			if (pt->gr)
				p->o.z -= ((lastmillis - p->millis) / 3.0f) *
				          curtime / (pt->gr * 10000);
			OFVector3D a = p->d;
			vmul(a, time);
			vdiv(a, 20000.0f);
			vadd(p->o, a);
			pp = &p->next;


		}
	}

	glEnable(GL_FOG);
	glDisable(GL_BLEND);
	glDepthMask(GL_TRUE);
};

void
particle_splash(int type, int num, int fade, OFVector3D &p)
{
	loopi(num)
	{
		const int radius = type == 5 ? 50 : 150;
		int x, y, z;
		do {
			x = rnd(radius * 2) - radius;
			y = rnd(radius * 2) - radius;
			z = rnd(radius * 2) - radius;
		} while (x * x + y * y + z * z > radius * radius);
		OFVector3D d = OFMakeVector3D(x, y, z);
		newparticle(p, d, rnd(fade * 3), type);


	}
}

void
particle_trail(int type, int fade, OFVector3D &s, OFVector3D &e)
{
	vdist(d, v, s, e);
	vdiv(v, d * 2 + 0.1f);
	OFVector3D p = s;
	loopi((int)d * 2)
	{
		vadd(p, v);
		OFVector3D d =
		    OFMakeVector3D(rnd(11) - 5, rnd(11) - 5, rnd(11) - 5);
		newparticle(p, d, rnd(fade) + fade, type);


	}
}

Modified src/savegamedemo.mm from [8854cf5de9] to [f5fe70f5b3].

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void
gzputi(int i)
{
	gzwrite(f, &i, sizeof(int));
}

void
gzputv(vec &v)
{
	gzwrite(f, &v, sizeof(vec));
}

void
gzcheck(int a, int b)
{
	if (a != b)
		fatal("savegame file corrupt (short)");







|

|







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void
gzputi(int i)
{
	gzwrite(f, &i, sizeof(int));
}

void
gzputv(OFVector3D &v)
{
	gzwrite(f, &v, sizeof(OFVector3D));
}

void
gzcheck(int a, int b)
{
	if (a != b)
		fatal("savegame file corrupt (short)");
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
{
	int i;
	gzcheck(gzread(f, &i, sizeof(int)), sizeof(int));
	return i;
}

void
gzgetv(vec &v)
{
	gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec));
}

void
stop()
{
	if (f) {
		if (demorecording)







|

|







55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
{
	int i;
	gzcheck(gzread(f, &i, sizeof(int)), sizeof(int));
	return i;
}

void
gzgetv(OFVector3D &v)
{
	gzcheck(gzread(f, &v, sizeof(OFVector3D)), sizeof(OFVector3D));
}

void
stop()
{
	if (f) {
		if (demorecording)
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
};

// demo functions

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

void
record(OFString *name)
{
	@autoreleasepool {
		if (m_sp) {
			conoutf(@"cannot record singleplayer games");







|







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
};

// demo functions

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

void
record(OFString *name)
{
	@autoreleasepool {
		if (m_sp) {
			conoutf(@"cannot record singleplayer games");
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
		starttime = lastmillis;
		ddamage = bdamage = 0;
	}
}
COMMAND(record, ARG_1STR)

void
demodamage(int damage, vec &o)
{
	ddamage = damage;
	dorig = o;
};
void
demoblend(int damage)
{







|







265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
		starttime = lastmillis;
		ddamage = bdamage = 0;
	}
}
COMMAND(record, ARG_1STR)

void
demodamage(int damage, OFVector3D &o)
{
	ddamage = damage;
	dorig = o;
};
void
demoblend(int damage)
{
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
	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
{
	vec t1 = b, t2 = c;

	vsub(t1, z);
	vmul(t1, 0.5f) vsub(t2, a);
	vmul(t2, 0.5f);

	float s2 = s * s;
	float s3 = s * s2;

	dest = a;
	vec t = b;

	vmul(dest, 2 * s3 - 3 * s2 + 1);
	vmul(t, -2 * s3 + 3 * s2);
	vadd(dest, t);
	vmul(t1, s3 - 2 * s2 + s);
	vadd(dest, t1);
	vmul(t2, s3 - s2);







>

|
|

|









|







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
	assert(d);
	*d = *player1;
	readdemotime();
}

VAR(demodelaymsec, 0, 120, 500);

// spline interpolation
void
catmulrom(OFVector3D &z, OFVector3D &a, OFVector3D &b, OFVector3D &c, float s,
    OFVector3D &dest)
{
	OFVector3D t1 = b, t2 = c;

	vsub(t1, z);
	vmul(t1, 0.5f) vsub(t2, a);
	vmul(t2, 0.5f);

	float s2 = s * s;
	float s3 = s * s2;

	dest = a;
	OFVector3D t = b;

	vmul(dest, 2 * s3 - 3 * s2 + 1);
	vmul(t, -2 * s3 + 3 * s2);
	vadd(dest, t);
	vmul(t1, s3 - 2 * s2 + s);
	vadd(dest, t1);
	vmul(t2, s3 - s2);
403
404
405
406
407
408
409
410

411
412
413
414
415
416
417
	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

		dynent *target = players[democlientnum];
		assert(target);








<
>







404
405
406
407
408
409
410

411
412
413
414
415
416
417
418
	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

		dynent *target = players[democlientnum];
		assert(target);

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
			target->state = gzget();
			target->lastmove = playbacktime;
			if (bdamage = gzgeti())
				damageblend(bdamage);
			if (ddamage = gzgeti()) {
				gzgetv(dorig);
				particle_splash(3, ddamage, 1000, dorig);
			};

			// FIXME: set more client state here
		};


		// insert latest copy of player into history
		if (extras &&
		    (playerhistory.empty() ||
		        playerhistory.last()->lastupdate != playbacktime)) {
			dynent *d = newdynent();
			*d = *target;
			d->lastupdate = playbacktime;
			playerhistory.add(d);
			if (playerhistory.length() > 20) {
				zapdynent(playerhistory[0]);
				playerhistory.remove(0);
			};
		};



		readdemotime();
	};


	if (demoplayback) {
		int itime = lastmillis - demodelaymsec;
		loopvrev(playerhistory) if (playerhistory[i]->lastupdate <
		                            itime) // find 2 positions in
		                                   // history that surround
		                                   // interpolation time point







<
>

<
>












<
<
|
>
>

<
>







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
			target->state = gzget();
			target->lastmove = playbacktime;
			if (bdamage = gzgeti())
				damageblend(bdamage);
			if (ddamage = gzgeti()) {
				gzgetv(dorig);
				particle_splash(3, ddamage, 1000, dorig);

			}
			// FIXME: set more client state here

		}

		// insert latest copy of player into history
		if (extras &&
		    (playerhistory.empty() ||
		        playerhistory.last()->lastupdate != playbacktime)) {
			dynent *d = newdynent();
			*d = *target;
			d->lastupdate = playbacktime;
			playerhistory.add(d);
			if (playerhistory.length() > 20) {
				zapdynent(playerhistory[0]);
				playerhistory.remove(0);


			}
		}

		readdemotime();

	}

	if (demoplayback) {
		int itime = lastmillis - demodelaymsec;
		loopvrev(playerhistory) if (playerhistory[i]->lastupdate <
		                            itime) // find 2 positions in
		                                   // history that surround
		                                   // interpolation time point
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
				fixwrap(z, player1);
				vdist(dist, v, z->o, c->o);
				if (dist < 16) // if teleport or spawn, dont't
				               // interpolate
				{
					catmulrom(z->o, a->o, b->o, c->o, bf,
					    player1->o);
					catmulrom(*(vec *)&z->yaw,
					    *(vec *)&a->yaw, *(vec *)&b->yaw,

					    *(vec *)&c->yaw, bf,
					    *(vec *)&player1->yaw);
				};

				fixplayer1range();
			};

			break;
		};

		// if(player1->state!=CS_DEAD) showscores(false);
	};
};



void
stopn()
{
	if (demoplayback)
		stopreset();
	else
		stop();
	conoutf(@"demo stopped");
}
COMMANDN(stop, stopn, ARG_NONE)







|
|
>
|
|
<
>

<
>

<
>

<
<
>
>











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
				fixwrap(z, player1);
				vdist(dist, v, z->o, c->o);
				if (dist < 16) // if teleport or spawn, dont't
				               // interpolate
				{
					catmulrom(z->o, a->o, b->o, c->o, bf,
					    player1->o);
					catmulrom(*(OFVector3D *)&z->yaw,
					    *(OFVector3D *)&a->yaw,
					    *(OFVector3D *)&b->yaw,
					    *(OFVector3D *)&c->yaw, bf,
					    *(OFVector3D *)&player1->yaw);

				}
				fixplayer1range();

			}
			break;

		}
		// if(player1->state!=CS_DEAD) showscores(false);


	}
}

void
stopn()
{
	if (demoplayback)
		stopreset();
	else
		stop();
	conoutf(@"demo stopped");
}
COMMANDN(stop, stopn, ARG_NONE)

Modified src/sound.mm from [143b0d78b9] to [b86e910aec].

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
VARP(musicvol, 0, 128, 255);
bool nosound = false;

#define MAXCHAN 32
#define SOUNDFREQ 22050

struct soundloc {
	vec loc;
	bool inuse;
} soundlocs[MAXCHAN];

#ifdef USE_MIXER
# include "SDL_mixer.h"
# define MAXVOL MIX_MAX_VOLUME
Mix_Music *mod = NULL;







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
VARP(musicvol, 0, 128, 255);
bool nosound = false;

#define MAXCHAN 32
#define SOUNDFREQ 22050

struct soundloc {
	OFVector3D loc;
	bool inuse;
} soundlocs[MAXCHAN];

#ifdef USE_MIXER
# include "SDL_mixer.h"
# define MAXVOL MIX_MAX_VOLUME
Mix_Music *mod = NULL;
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	FSOUND_Close();
#endif
};

VAR(stereo, 0, 1, 1);

void
updatechanvol(int chan, vec *loc)
{
	int vol = soundvol, pan = 255 / 2;
	if (loc) {
		vdist(dist, v, *loc, player1->o);
		vol -= (int)(dist * 3 * soundvol /
		             255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {







|







146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
	FSOUND_Close();
#endif
};

VAR(stereo, 0, 1, 1);

void
updatechanvol(int chan, OFVector3D *loc)
{
	int vol = soundvol, pan = 255 / 2;
	if (loc) {
		vdist(dist, v, *loc, player1->o);
		vol -= (int)(dist * 3 * soundvol /
		             255); // simple mono distance attenuation
		if (stereo && (v.x != 0 || v.y != 0)) {
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);
#endif
};

void
newsoundloc(int chan, vec *loc)
{
	assert(chan >= 0 && chan < MAXCHAN);
	soundlocs[chan].loc = *loc;
	soundlocs[chan].inuse = true;
};

void







|







174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);
#endif
};

void
newsoundloc(int chan, OFVector3D *loc)
{
	assert(chan >= 0 && chan < MAXCHAN);
	soundlocs[chan].loc = *loc;
	soundlocs[chan].inuse = true;
};

void
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	addmsg(0, 2, SV_SOUND, n);
	playsound(n);
};

int soundsatonce = 0, lastsoundmillis = 0;

void
playsound(int n, vec *loc)
{
	if (nosound)
		return;
	if (!soundvol)
		return;
	if (lastmillis == lastsoundmillis)
		soundsatonce++;







|







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	addmsg(0, 2, SV_SOUND, n);
	playsound(n);
};

int soundsatonce = 0, lastsoundmillis = 0;

void
playsound(int n, OFVector3D *loc)
{
	if (nosound)
		return;
	if (!soundvol)
		return;
	if (lastmillis == lastsoundmillis)
		soundsatonce++;

Modified src/weapon.mm from [d7fe676e8d] to [88f240c5f9].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// weapon.cpp: all shooting and effects code

#include "cube.h"

struct guninfo {
	short sound, attackdelay, damage, projspeed, part, kickamount;
	char *name;
};

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

guninfo guns[NUMGUNS] = {
    {S_PUNCH1, 250, 50, 0, 0, 1, "fist"},
    {S_SG, 1400, 10, 0, 0, 20, "shotgun"}, // *SGRAYS
    {S_CG, 100, 30, 0, 0, 7, "chaingun"},
    {S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher"},
    {S_RIFLE, 1500, 100, 0, 0, 30, "rifle"},












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// weapon.cpp: all shooting and effects code

#include "cube.h"

struct guninfo {
	short sound, attackdelay, damage, projspeed, part, kickamount;
	char *name;
};

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

guninfo guns[NUMGUNS] = {
    {S_PUNCH1, 250, 50, 0, 0, 1, "fist"},
    {S_SG, 1400, 10, 0, 0, 20, "shotgun"}, // *SGRAYS
    {S_CG, 100, 30, 0, 0, 7, "chaingun"},
    {S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher"},
    {S_RIFLE, 1500, 100, 0, 0, 30, "rifle"},
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
		    a2.UTF8String[0] ? atoi(a2.UTF8String) : -1,
		    a3.UTF8String[0] ? atoi(a3.UTF8String) : -1);
	}
}
COMMAND(weapon, ARG_3STR)

void

createrays(vec &from, vec &to) // create random spread of rays for the shotgun
{
	vdist(dist, dvec, from, to);
	float f = dist * SGSPREAD / 1000;
	loopi(SGRAYS)
	{
#define RNDD (rnd(101) - 50) * f
		vec r = {RNDD, RNDD, RNDD};
		sg[i] = to;
		vadd(sg[i], r);
	};
};

bool

intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box
{
	vec v = to, w = d->o, *p;
	vsub(v, from);
	vsub(w, from);
	float c1 = dotprod(w, v);

	if (c1 <= 0)
		p = &from;
	else {







>
|






|






>
|

|







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
		    a2.UTF8String[0] ? atoi(a2.UTF8String) : -1,
		    a3.UTF8String[0] ? atoi(a3.UTF8String) : -1);
	}
}
COMMAND(weapon, ARG_3STR)

void
createrays(OFVector3D &from,
    OFVector3D &to) // create random spread of rays for the shotgun
{
	vdist(dist, dvec, from, to);
	float f = dist * SGSPREAD / 1000;
	loopi(SGRAYS)
	{
#define RNDD (rnd(101) - 50) * f
		OFVector3D r = OFMakeVector3D(RNDD, RNDD, RNDD);
		sg[i] = to;
		vadd(sg[i], r);
	};
};

bool
intersect(dynent *d, OFVector3D &from,
    OFVector3D &to) // if lineseg hits entity bounding box
{
	OFVector3D v = to, w = d->o, *p;
	vsub(v, from);
	vsub(w, from);
	float c1 = dotprod(w, v);

	if (c1 <= 0)
		p = &from;
	else {
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
			return o->name;
	};
	return NULL;
};

const int MAXPROJ = 100;
struct projectile {
	vec o, to;
	float speed;
	dynent *owner;
	int gun;
	bool inuse, local;
};
projectile projs[MAXPROJ];

void
projreset()
{
	loopi(MAXPROJ) projs[i].inuse = false;
};

void
newprojectile(
    vec &from, vec &to, float speed, bool local, dynent *owner, int gun)
{
	loopi(MAXPROJ)
	{
		projectile *p = &projs[i];
		if (p->inuse)
			continue;
		p->inuse = true;







|














|
|







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
			return o->name;
	};
	return NULL;
};

const int MAXPROJ = 100;
struct projectile {
	OFVector3D o, to;
	float speed;
	dynent *owner;
	int gun;
	bool inuse, local;
};
projectile projs[MAXPROJ];

void
projreset()
{
	loopi(MAXPROJ) projs[i].inuse = false;
};

void
newprojectile(OFVector3D &from, OFVector3D &to, float speed, bool local,
    dynent *owner, int gun)
{
	loopi(MAXPROJ)
	{
		projectile *p = &projs[i];
		if (p->inuse)
			continue;
		p->inuse = true;
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
	demodamage(damage, d->o);
};

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

void
radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at)
{
	if (o->state != CS_ALIVE)
		return;
	vdist(dist, temp, v, o->o);
	dist -= 2; // account for eye distance imprecision
	if (dist < RL_DAMRAD) {
		if (dist < 0)
			dist = 0;
		int damage = (int)(qdam * (1 - (dist / RL_DAMRAD)));
		hit(cn, damage, o, at);
		vmul(temp, (RL_DAMRAD - dist) * damage / 800);
		vadd(o->vel, temp);
	};
};

void
splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster,
    int qdam)
{
	particle_splash(0, 50, 300, v);
	p->inuse = false;
	if (p->gun != GUN_RL) {
		playsound(S_FEXPLODE, &v);
		// no push?
	} else {







|
















|
|







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
	demodamage(damage, d->o);
};

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

void
radialeffect(dynent *o, OFVector3D &v, int cn, int qdam, dynent *at)
{
	if (o->state != CS_ALIVE)
		return;
	vdist(dist, temp, v, o->o);
	dist -= 2; // account for eye distance imprecision
	if (dist < RL_DAMRAD) {
		if (dist < 0)
			dist = 0;
		int damage = (int)(qdam * (1 - (dist / RL_DAMRAD)));
		hit(cn, damage, o, at);
		vmul(temp, (RL_DAMRAD - dist) * damage / 800);
		vadd(o->vel, temp);
	};
};

void
splash(projectile *p, OFVector3D &v, OFVector3D &vold, int notthisplayer,
    int notthismonster, int qdam)
{
	particle_splash(0, 50, 300, v);
	p->inuse = false;
	if (p->gun != GUN_RL) {
		playsound(S_FEXPLODE, &v);
		// no push?
	} else {
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
		dvector &mv = getmonsters();
		loopv(mv) if (i != notthismonster)
		    radialeffect(mv[i], v, i, qdam, p->owner);
	};
};

inline void
projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam)
{
	if (o->state != CS_ALIVE)
		return;
	if (intersect(o, p->o, v)) {
		splash(p, v, p->o, i, im, qdam);
		hit(i, qdam, o, p->owner);
	};







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
		dvector &mv = getmonsters();
		loopv(mv) if (i != notthismonster)
		    radialeffect(mv[i], v, i, qdam, p->owner);
	};
};

inline void
projdamage(dynent *o, projectile *p, OFVector3D &v, int i, int im, int qdam)
{
	if (o->state != CS_ALIVE)
		return;
	if (intersect(o, p->o, v)) {
		splash(p, v, p->o, i, im, qdam);
		hit(i, qdam, o, p->owner);
	};
260
261
262
263
264
265
266
267

268
269
270
271
272
273
274

275
276
277
278
279
280
281
282
283
284
285
286
287
288



289
290
291
292
293
294
295
296
297
298
299
300
301
		{
			loopv(players)
			{
				dynent *o = players[i];
				if (!o)
					continue;
				projdamage(o, p, v, i, -1, qdam);
			};

			if (p->owner != player1)
				projdamage(player1, p, v, -1, -1, qdam);
			dvector &mv = getmonsters();
			loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
			              mv[i] != p->owner)
			    projdamage(mv[i], p, v, -1, i, qdam);
		};

		if (p->inuse) {
			if (time == dtime)
				splash(p, v, p->o, -1, -1, qdam);
			else {
				if (p->gun == GUN_RL) {
					dodynlight(p->o, v, 0, 255, p->owner);
					particle_splash(5, 2, 200, v);
				} else {
					particle_splash(1, 1, 200, v);
					particle_splash(
					    guns[p->gun].part, 1, 1, v);
				};
			};
		};



		p->o = v;
	};
};

void
shootv(int gun, vec &from, vec &to, dynent *d,
    bool local) // create visual effect from a shot
{
	playsound(guns[gun].sound, d == player1 ? NULL : &d->o);
	int pspeed = 25;
	switch (gun) {
	case GUN_FIST:
		break;







<
>






<
>











<
<
<
>
>
>





|







262
263
264
265
266
267
268

269
270
271
272
273
274
275

276
277
278
279
280
281
282
283
284
285
286
287



288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
		{
			loopv(players)
			{
				dynent *o = players[i];
				if (!o)
					continue;
				projdamage(o, p, v, i, -1, qdam);

			}
			if (p->owner != player1)
				projdamage(player1, p, v, -1, -1, qdam);
			dvector &mv = getmonsters();
			loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
			              mv[i] != p->owner)
			    projdamage(mv[i], p, v, -1, i, qdam);

		}
		if (p->inuse) {
			if (time == dtime)
				splash(p, v, p->o, -1, -1, qdam);
			else {
				if (p->gun == GUN_RL) {
					dodynlight(p->o, v, 0, 255, p->owner);
					particle_splash(5, 2, 200, v);
				} else {
					particle_splash(1, 1, 200, v);
					particle_splash(
					    guns[p->gun].part, 1, 1, v);



				}
			}
		}
		p->o = v;
	};
};

void
shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d,
    bool local) // create visual effect from a shot
{
	playsound(guns[gun].sound, d == player1 ? NULL : &d->o);
	int pspeed = 25;
	switch (gun) {
	case GUN_FIST:
		break;
324
325
326
327
328
329
330
331

332
333
334
335
336
337
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
		particle_splash(0, 50, 200, to);
		particle_trail(1, 500, from, to);
		break;
	};
};

void
hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to)

{
	hit(target, damage, d, at);
	vdist(dist, v, from, to);
	vmul(v, damage / dist / 50);
	vadd(d->vel, v);
};


void
raydamage(dynent *o, vec &from, vec &to, dynent *d, int i)
{
	if (o->state != CS_ALIVE)
		return;
	int qdam = guns[d->gunselect].damage;
	if (d->quadmillis)
		qdam *= 4;
	if (d->monsterstate)
		qdam /= MONSTERDAMAGEFACTOR;
	if (d->gunselect == GUN_SG) {
		int damage = 0;
		loop(r, SGRAYS) if (intersect(o, from, sg[r])) damage += qdam;
		if (damage)
			hitpush(i, damage, o, d, from, to);
	} else if (intersect(o, from, to))
		hitpush(i, qdam, o, d, from, to);
};

void
shoot(dynent *d, vec &targ)
{
	int attacktime = lastmillis - d->lastaction;
	if (attacktime < d->gunwait)
		return;
	d->gunwait = 0;
	if (!d->attacking)
		return;
	d->lastaction = lastmillis;
	d->lastattackgun = d->gunselect;
	if (!d->ammo[d->gunselect]) {
		playsoundc(S_NOAMMO);
		d->gunwait = 250;
		d->lastattackgun = -1;
		return;
	};
	if (d->gunselect)
		d->ammo[d->gunselect]--;
	vec from = d->o;
	vec to = targ;
	from.z -= 0.2f; // below eye

	vdist(dist, unitv, from, to);
	vdiv(unitv, dist);
	vec kickback = unitv;
	vmul(kickback, guns[d->gunselect].kickamount * -0.01f);
	vadd(d->vel, kickback);
	if (d->pitch < 80.0f)
		d->pitch += guns[d->gunselect].kickamount * 0.05f;

	if (d->gunselect == GUN_FIST || d->gunselect == GUN_BITE) {
		vmul(unitv, 3); // punch range







|
>





<
|
>

|


















|

















|
|




|







326
327
328
329
330
331
332
333
334
335
336
337
338
339

340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
		particle_splash(0, 50, 200, to);
		particle_trail(1, 500, from, to);
		break;
	};
};

void
hitpush(int target, int damage, dynent *d, dynent *at, OFVector3D &from,
    OFVector3D &to)
{
	hit(target, damage, d, at);
	vdist(dist, v, from, to);
	vmul(v, damage / dist / 50);
	vadd(d->vel, v);

}

void
raydamage(dynent *o, OFVector3D &from, OFVector3D &to, dynent *d, int i)
{
	if (o->state != CS_ALIVE)
		return;
	int qdam = guns[d->gunselect].damage;
	if (d->quadmillis)
		qdam *= 4;
	if (d->monsterstate)
		qdam /= MONSTERDAMAGEFACTOR;
	if (d->gunselect == GUN_SG) {
		int damage = 0;
		loop(r, SGRAYS) if (intersect(o, from, sg[r])) damage += qdam;
		if (damage)
			hitpush(i, damage, o, d, from, to);
	} else if (intersect(o, from, to))
		hitpush(i, qdam, o, d, from, to);
};

void
shoot(dynent *d, OFVector3D &targ)
{
	int attacktime = lastmillis - d->lastaction;
	if (attacktime < d->gunwait)
		return;
	d->gunwait = 0;
	if (!d->attacking)
		return;
	d->lastaction = lastmillis;
	d->lastattackgun = d->gunselect;
	if (!d->ammo[d->gunselect]) {
		playsoundc(S_NOAMMO);
		d->gunwait = 250;
		d->lastattackgun = -1;
		return;
	};
	if (d->gunselect)
		d->ammo[d->gunselect]--;
	OFVector3D from = d->o;
	OFVector3D to = targ;
	from.z -= 0.2f; // below eye

	vdist(dist, unitv, from, to);
	vdiv(unitv, dist);
	OFVector3D kickback = unitv;
	vmul(kickback, guns[d->gunselect].kickamount * -0.01f);
	vadd(d->vel, kickback);
	if (d->pitch < 80.0f)
		d->pitch += guns[d->gunselect].kickamount * 0.05f;

	if (d->gunselect == GUN_FIST || d->gunselect == GUN_BITE) {
		vmul(unitv, 3); // punch range

Modified src/world.mm from [da94b33242] to [061f7f026c].

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
	int best;
	float bdist = 99999;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		vec v = {(float)e.x, (float)e.y, (float)e.z};
		vdist(dist, t, player1->o, v);
		if (dist < bdist) {
			best = i;
			bdist = dist;
		};
	};
	return bdist == 99999 ? -1 : best;







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
	int best;
	float bdist = 99999;
	loopv(ents)
	{
		entity &e = ents[i];
		if (e.type == NOTUSED)
			continue;
		OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
		vdist(dist, t, player1->o, v);
		if (dist < bdist) {
			best = i;
			bdist = dist;
		};
	};
	return bdist == 99999 ? -1 : best;

Modified src/worldlight.mm from [ae7e6c7975] to [93b7010f75].

196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
		block *backup = dlights.pop();
		blockpaste(*backup);
		free(backup);
	}
}

void

dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner)
{
	if (!reach)
		reach = dynlight;
	if (owner->monsterstate)
		reach = reach / 2;
	if (!reach)
		return;







>
|







196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
		block *backup = dlights.pop();
		blockpaste(*backup);
		free(backup);
	}
}

void
dodynlight(
    OFVector3D &vold, OFVector3D &v, int reach, int strength, dynent *owner)
{
	if (!reach)
		reach = dynlight;
	if (owner->monsterstate)
		reach = reach / 2;
	if (!reach)
		return;