Cube  Check-in [d3b4b2d476]

Overview
Comment:Migrate projectile to a class
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: d3b4b2d476d809f7ab00502eed47614c949264690b3eccc7a96c73687d0f05bf
User & Date: js on 2025-03-09 11:24:01
Other Links: manifest | tags
Context
2025-03-09
18:57
Convert dynent to a class check-in: d2b3ff790f user: js tags: trunk
11:24
Migrate projectile to a class check-in: d3b4b2d476 user: js tags: trunk
10:25
Use SDL_TextInputEvent for input check-in: 4c092023dc user: js tags: trunk
Changes

Added src/Projectile.h version [28cf35ad48].

Added src/Projectile.m version [902fed7b64].

Modified src/meson.build from [b573376159] to [2625064d57].

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
executable('client',
  [
    'Alias.m',
    'Command.mm',
    'Cube.mm',
    'Identifier.m',
    'KeyMapping.m',
    'MD2.mm',
    'MapModelInfo.m',
    'Menu.m',
    'MenuItem.m',

    'Variable.mm',
    'client.mm',
    'clientextras.mm',
    'clientgame.mm',
    'clients2c.mm',
    'commands.mm',
    'console.mm',











>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
executable('client',
  [
    'Alias.m',
    'Command.mm',
    'Cube.mm',
    'Identifier.m',
    'KeyMapping.m',
    'MD2.mm',
    'MapModelInfo.m',
    'Menu.m',
    'MenuItem.m',
    'Projectile.m',
    'Variable.mm',
    'client.mm',
    'clientextras.mm',
    'clientgame.mm',
    'clients2c.mm',
    'commands.mm',
    'console.mm',

Modified src/protos.h from [8800716a44] to [455fc77430].

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
extern void resettagareas();
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);







|
|







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
extern void resettagareas();
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(const OFVector3D &vold, const 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);
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
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(OFString *mname);







|









|







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
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(const 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, const 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(OFString *mname);
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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,







|







198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
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, const OFVector3D *loc = NULL);
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,

Modified src/renderextras.mm from [3268694917] to [4ee74611a3].

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	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];
		}







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
	int type;
	sphere *next;
};
sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
bool sinit = false;

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

Modified src/renderparticles.mm from [b42cd85412] to [f46610835d].

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	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];
		}







|
|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
	particle *next;
};
particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL;
bool parinit = false;

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

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

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







|










|
<







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

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

void
particle_splash(int type, int num, int fade, const 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);
		newparticle(p, OFMakeVector3D(x, y, z), rnd(fade * 3), type);

	}
}

void
particle_trail(int type, int fade, OFVector3D &s, OFVector3D &e)
{
	vdist(d, v, s, e);

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

88
89
90
91
92
93
94
95
96

97
98
99
100
101
102

103
104
105
106
107
108
109
		@autoreleasepool {
			OFString *path =
			    [OFString stringWithFormat:@"packages/%@", name];
			OFIRI *IRI = [Cube.sharedInstance.gameDataIRI
			    IRIByAppendingPathComponent:path];

#ifdef USE_MIXER
			if (mod = Mix_LoadMUS(
			        IRI.fileSystemRepresentation.UTF8String)) {

				Mix_PlayMusic(mod, -1);
				Mix_VolumeMusic((musicvol * MAXVOL) / 255);
			}
#else
			if (mod = FMUSIC_LoadSong(
			        IRI.fileSystemRepresentation.UTF8String)) {

				FMUSIC_PlaySong(mod);
				FMUSIC_SetMasterVolume(mod, musicvol);
			} else if (stream = FSOUND_Stream_Open(
			               IRI.fileSystemRepresentation.UTF8String,
			               FSOUND_LOOP_NORMAL, 0, 0)) {
				int chan =
				    FSOUND_Stream_Play(FSOUND_FREE, stream);







|
|
>




|
|
>







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
		@autoreleasepool {
			OFString *path =
			    [OFString stringWithFormat:@"packages/%@", name];
			OFIRI *IRI = [Cube.sharedInstance.gameDataIRI
			    IRIByAppendingPathComponent:path];

#ifdef USE_MIXER
			if ((mod = Mix_LoadMUS(
			         IRI.fileSystemRepresentation.UTF8String)) !=
			    NULL) {
				Mix_PlayMusic(mod, -1);
				Mix_VolumeMusic((musicvol * MAXVOL) / 255);
			}
#else
			if ((mod = FMUSIC_LoadSong(
			         IRI.fileSystemRepresentation.UTF8String)) !=
			    NULL) {
				FMUSIC_PlaySong(mod);
				FMUSIC_SetMasterVolume(mod, musicvol);
			} else if (stream = FSOUND_Stream_Open(
			               IRI.fileSystemRepresentation.UTF8String,
			               FSOUND_LOOP_NORMAL, 0, 0)) {
				int chan =
				    FSOUND_Stream_Play(FSOUND_FREE, stream);
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#else
	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)) {







|
|







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#else
	FSOUND_Close();
#endif
}

VAR(stereo, 0, 1, 1);

static void
updatechanvol(int chan, const 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)) {
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	Mix_SetPanning(chan, 255 - pan, pan);
#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







|
|







192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
	Mix_SetPanning(chan, 255 - pan, pan);
#else
	FSOUND_SetVolume(chan, vol);
	FSOUND_SetPan(chan, pan);
#endif
}

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

void
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
	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++;







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
	addmsg(0, 2, SV_SOUND, n);
	playsound(n);
}

int soundsatonce = 0, lastsoundmillis = 0;

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

Modified src/weapon.mm from [a095df6286] to [652afaa3f5].

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



16
17
18
19
20
21
22
// weapon.cpp: all shooting and effects code

#include "cube.h"

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

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

static const guninfo guns[NUMGUNS] = {



	{ S_PUNCH1, 250, 50, 0, 0, 1, @"fist" },
	{ S_SG, 1400, 10, 0, 0, 20, @"shotgun" }, // *SGRAYS
	{ S_CG, 100, 30, 0, 0, 7, @"chaingun" },
	{ S_RLFIRE, 800, 120, 80, 0, 10, @"rocketlauncher" },
	{ S_RIFLE, 1500, 100, 0, 0, 30, @"rifle" },
	{ S_FLAUNCH, 200, 20, 50, 4, 1, @"fireball" },
	{ S_ICEBALL, 200, 40, 30, 6, 1, @"iceball" },




|
<
<
<






|
>
>
>







1
2
3
4
5



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// weapon.cpp: all shooting and effects code

#include "cube.h"

#import "Projectile.h"




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

static const struct {
	short sound, attackdelay, damage, projspeed, part, kickamount;
	OFString *name;
} guns[NUMGUNS] = {
	{ S_PUNCH1, 250, 50, 0, 0, 1, @"fist" },
	{ S_SG, 1400, 10, 0, 0, 20, @"shotgun" }, // *SGRAYS
	{ S_CG, 100, 30, 0, 0, 7, @"chaingun" },
	{ S_RLFIRE, 800, 120, 80, 0, 10, @"rocketlauncher" },
	{ S_RIFLE, 1500, 100, 0, 0, 30, @"rifle" },
	{ S_FLAUNCH, 200, 20, 50, 4, 1, @"fireball" },
	{ S_ICEBALL, 200, 40, 30, 6, 1, @"iceball" },
79
80
81
82
83
84
85

86
87
88
89
90

91
92
93
94
95
96
97
#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 {







>

|
<

|
>







79
80
81
82
83
84
85
86
87
88

89
90
91
92
93
94
95
96
97
98
#define RNDD (rnd(101) - 50) * f
		OFVector3D r = OFMakeVector3D(RNDD, RNDD, RNDD);
		sg[i] = to;
		vadd(sg[i], r);
	}
}

// if lineseg hits entity bounding box
bool
intersect(dynent *d, const OFVector3D &from, const OFVector3D &to)

{
	OFVector3D v = to, w = d->o;
	const OFVector3D *p;
	vsub(v, from);
	vsub(w, from);
	float c1 = dotprod(w, v);

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

	return nil;
}

const int MAXPROJ = 100;
struct projectile {
	OFVector3D o, to;
	float speed;
	dynent *owner;
	int gun;
	bool inuse, local;
} 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;
		p->o = from;
		p->to = to;
		p->speed = speed;
		p->local = local;
		p->owner = owner;
		p->gun = gun;
		return;
	}
}

void
hit(int target, int damage, dynent *d, dynent *at)
{







|
<
<
<
<
<
<
|




>
|






|
>
|
<
|

>
|
|
|
|
|
|
|







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
		if (intersect(o, player1->o, worldpos))
			return @(o->name);
	}

	return nil;
}

static const size_t MAXPROJ = 100;






static Projectile *projs[MAXPROJ];

void
projreset()
{
	for (size_t i = 0; i < MAXPROJ; i++)
		projs[i].inuse = false;
}

void
newprojectile(OFVector3D &from, OFVector3D &to, float speed, bool local,
    dynent *owner, int gun)
{
	for (size_t i = 0; i < MAXPROJ; i++) {
		Projectile *p = projs[i];


		if (p.inuse)
			continue;

		p.inuse = true;
		p.o = from;
		p.to = to;
		p.speed = speed;
		p.local = local;
		p.owner = owner;
		p.gun = gun;
		return;
	}
}

void
hit(int target, int damage, dynent *d, dynent *at)
{
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
288
289
290
291
292
293
294
295
296
	particle_splash(3, damage, 1000, d->o);
	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 {
		playsound(S_RLHIT, &v);
		newsphere(v, RL_RADIUS, 0);
		dodynlight(vold, v, 0, 0, p->owner);
		if (!p->local)
			return;
		radialeffect(player1, v, -1, qdam, p->owner);
		loopv(players)
		{
			if (i == notthisplayer)
				continue;
			dynent *o = players[i];
			if (!o)
				continue;
			radialeffect(o, v, i, qdam, p->owner);
		}
		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);
	}
}

void
moveprojectiles(float time)
{
	loopi(MAXPROJ)

	{
		projectile *p = &projs[i];
		if (!p->inuse)
			continue;

		int qdam = guns[p->gun].damage * (p->owner->quadmillis ? 4 : 1);
		if (p->owner->monsterstate)
			qdam /= MONSTERDAMAGEFACTOR;
		vdist(dist, v, p->o, p->to);
		float dtime = dist * 1000 / p->speed;
		if (time > dtime)
			dtime = time;
		vmul(v, time / dtime);
		vadd(v, p->o) if (p->local)
		{
			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
{







|
|
















|
|


|
|





|
|

|







|



|




|



|
|
|






|
>
|
<
|

>
|
|

|
|



|








|



|


|

|

|
|




|



|







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
288
289
290
291
292
293
294
	particle_splash(3, damage, 1000, d->o);
	demodamage(damage, d->o);
}

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

static void
radialeffect(dynent *o, const 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, const OFVector3D &v, const 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 {
		playsound(S_RLHIT, &v);
		newsphere(v, RL_RADIUS, 0);
		dodynlight(vold, v, 0, 0, p.owner);
		if (!p.local)
			return;
		radialeffect(player1, v, -1, qdam, p.owner);
		loopv(players)
		{
			if (i == notthisplayer)
				continue;
			dynent *o = players[i];
			if (!o)
				continue;
			radialeffect(o, v, i, qdam, p.owner);
		}
		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);
	}
}

void
moveprojectiles(float time)
{
	for (size_t i = 0; i < MAXPROJ; i++) {
		Projectile *p = projs[i];


		if (!p.inuse)
			continue;

		int qdam = guns[p.gun].damage * (p.owner->quadmillis ? 4 : 1);
		if (p.owner->monsterstate)
			qdam /= MONSTERDAMAGEFACTOR;
		vdist(dist, v, p.o, p.to);
		float dtime = dist * 1000 / p.speed;
		if (time > dtime)
			dtime = time;
		vmul(v, time / dtime);
		vadd(v, p.o) if (p.local)
		{
			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
{

Modified src/worldlight.mm from [6a518301f9] to [ccc85287d7].

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;







|
|







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(const OFVector3D &vold, const OFVector3D &v, int reach, int strength,
    dynent *owner)
{
	if (!reach)
		reach = dynlight;
	if (owner->monsterstate)
		reach = reach / 2;
	if (!reach)
		return;