ADDED   src/Projectile.h
Index: src/Projectile.h
==================================================================
--- /dev/null
+++ src/Projectile.h
@@ -0,0 +1,11 @@
+#import <ObjFW/ObjFW.h>
+
+typedef struct dynent dynent;
+
+@interface Projectile: OFObject
+@property (nonatomic) OFVector3D o, to;
+@property (nonatomic) float speed;
+@property (nonatomic) dynent *owner;
+@property (nonatomic) int gun;
+@property (nonatomic) bool inuse, local;
+@end

ADDED   src/Projectile.m
Index: src/Projectile.m
==================================================================
--- /dev/null
+++ src/Projectile.m
@@ -0,0 +1,4 @@
+#import "Projectile.h"
+
+@implementation Projectile
+@end

Index: src/meson.build
==================================================================
--- src/meson.build
+++ src/meson.build
@@ -7,10 +7,11 @@
     'KeyMapping.m',
     'MD2.mm',
     'MapModelInfo.m',
     'Menu.m',
     'MenuItem.m',
+    'Projectile.m',
     'Variable.mm',
     'client.mm',
     'clientextras.mm',
     'clientgame.mm',
     'clients2c.mm',

Index: src/protos.h
==================================================================
--- src/protos.h
+++ src/protos.h
@@ -123,12 +123,12 @@
 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 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
@@ -164,21 +164,21 @@
 // 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 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, OFVector3D &p);
+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
@@ -200,11 +200,11 @@
 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 playsound(int n, const OFVector3D *loc = NULL);
 extern void playsoundc(int n);
 extern void initsound();
 extern void cleansound();
 
 // rendermd2

Index: src/renderextras.mm
==================================================================
--- src/renderextras.mm
+++ src/renderextras.mm
@@ -87,11 +87,11 @@
 };
 sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL;
 bool sinit = false;
 
 void
-newsphere(OFVector3D &o, float max, int type)
+newsphere(const OFVector3D &o, float max, int type)
 {
 	if (!sinit) {
 		loopi(MAXSPHERES)
 		{
 			spheres[i].next = sempty;

Index: src/renderparticles.mm
==================================================================
--- src/renderparticles.mm
+++ src/renderparticles.mm
@@ -13,12 +13,12 @@
 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)
+static void
+newparticle(const OFVector3D &o, const OFVector3D &d, int fade, int type)
 {
 	if (!parinit) {
 		loopi(MAXPARTICLES)
 		{
 			particles[i].next = parempty;
@@ -131,11 +131,11 @@
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
 }
 
 void
-particle_splash(int type, int num, int fade, OFVector3D &p)
+particle_splash(int type, int num, int fade, const OFVector3D &p)
 {
 	loopi(num)
 	{
 		const int radius = type == 5 ? 50 : 150;
 		int x, y, z;
@@ -142,12 +142,11 @@
 		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);
+		newparticle(p, OFMakeVector3D(x, y, z), rnd(fade * 3), type);
 	}
 }
 
 void
 particle_trail(int type, int fade, OFVector3D &s, OFVector3D &e)

Index: src/sound.mm
==================================================================
--- src/sound.mm
+++ src/sound.mm
@@ -90,18 +90,20 @@
 			    [OFString stringWithFormat:@"packages/%@", name];
 			OFIRI *IRI = [Cube.sharedInstance.gameDataIRI
 			    IRIByAppendingPathComponent:path];
 
 #ifdef USE_MIXER
-			if (mod = Mix_LoadMUS(
-			        IRI.fileSystemRepresentation.UTF8String)) {
+			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)) {
+			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)) {
@@ -164,12 +166,12 @@
 #endif
 }
 
 VAR(stereo, 0, 1, 1);
 
-void
-updatechanvol(int chan, OFVector3D *loc)
+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 /
@@ -192,12 +194,12 @@
 	FSOUND_SetVolume(chan, vol);
 	FSOUND_SetPan(chan, pan);
 #endif
 }
 
-void
-newsoundloc(int chan, OFVector3D *loc)
+static void
+newsoundloc(int chan, const OFVector3D *loc)
 {
 	assert(chan >= 0 && chan < MAXCHAN);
 	soundlocs[chan].loc = *loc;
 	soundlocs[chan].inuse = true;
 }
@@ -228,11 +230,11 @@
 }
 
 int soundsatonce = 0, lastsoundmillis = 0;
 
 void
-playsound(int n, OFVector3D *loc)
+playsound(int n, const OFVector3D *loc)
 {
 	if (nosound)
 		return;
 	if (!soundvol)
 		return;

Index: src/weapon.mm
==================================================================
--- src/weapon.mm
+++ src/weapon.mm
@@ -1,20 +1,20 @@
 // weapon.cpp: all shooting and effects code
 
 #include "cube.h"
 
-struct guninfo {
-	short sound, attackdelay, damage, projspeed, part, kickamount;
-	OFString *name;
-};
+#import "Projectile.h"
 
 static const int MONSTERDAMAGEFACTOR = 4;
 static const int SGRAYS = 20;
 static const float SGSPREAD = 2;
 static OFVector3D sg[SGRAYS];
 
-static const guninfo guns[NUMGUNS] = {
+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" },
@@ -81,15 +81,16 @@
 		sg[i] = to;
 		vadd(sg[i], r);
 	}
 }
 
+// if lineseg hits entity bounding box
 bool
-intersect(dynent *d, OFVector3D &from,
-    OFVector3D &to) // if lineseg hits entity bounding box
+intersect(dynent *d, const OFVector3D &from, const OFVector3D &to)
 {
-	OFVector3D v = to, w = d->o, *p;
+	OFVector3D v = to, w = d->o;
+	const OFVector3D *p;
 	vsub(v, from);
 	vsub(w, from);
 	float c1 = dotprod(w, v);
 
 	if (c1 <= 0)
@@ -126,41 +127,37 @@
 	}
 
 	return nil;
 }
 
-const int MAXPROJ = 100;
-struct projectile {
-	OFVector3D o, to;
-	float speed;
-	dynent *owner;
-	int gun;
-	bool inuse, local;
-} projs[MAXPROJ];
+static const size_t MAXPROJ = 100;
+static Projectile *projs[MAXPROJ];
 
 void
 projreset()
 {
-	loopi(MAXPROJ) projs[i].inuse = false;
+	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)
 {
-	loopi(MAXPROJ)
-	{
-		projectile *p = &projs[i];
-		if (p->inuse)
+	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;
+
+		p.inuse = true;
+		p.o = from;
+		p.to = to;
+		p.speed = speed;
+		p.local = local;
+		p.owner = owner;
+		p.gun = gun;
 		return;
 	}
 }
 
 void
@@ -179,12 +176,12 @@
 }
 
 const float RL_RADIUS = 5;
 const float RL_DAMRAD = 7; // hack
 
-void
-radialeffect(dynent *o, OFVector3D &v, int cn, int qdam, dynent *at)
+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
@@ -197,98 +194,99 @@
 		vadd(o->vel, temp);
 	}
 }
 
 void
-splash(projectile *p, OFVector3D &v, OFVector3D &vold, int notthisplayer,
-    int notthismonster, int qdam)
+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) {
+	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)
+		dodynlight(vold, v, 0, 0, p.owner);
+		if (!p.local)
 			return;
-		radialeffect(player1, v, -1, qdam, p->owner);
+		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);
+			radialeffect(o, v, i, qdam, p.owner);
 		}
 		dvector &mv = getmonsters();
 		loopv(mv) if (i != notthismonster)
-		    radialeffect(mv[i], v, i, qdam, p->owner);
+		    radialeffect(mv[i], v, i, qdam, p.owner);
 	}
 }
 
 inline void
-projdamage(dynent *o, projectile *p, OFVector3D &v, int i, int im, int qdam)
+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);
+	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)
+	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)
+
+		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;
+		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)
+		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)
+			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)
+			    mv[i] != p.owner)
 			    projdamage(mv[i], p, v, -1, i, qdam);
 		}
-		if (p->inuse) {
+		if (p.inuse) {
 			if (time == dtime)
-				splash(p, v, p->o, -1, -1, qdam);
+				splash(p, v, p.o, -1, -1, qdam);
 			else {
-				if (p->gun == GUN_RL) {
-					dodynlight(p->o, v, 0, 255, p->owner);
+				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);
+					    guns[p.gun].part, 1, 1, v);
 				}
 			}
 		}
-		p->o = v;
+		p.o = v;
 	}
 }
 
 void
 shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d,

Index: src/worldlight.mm
==================================================================
--- src/worldlight.mm
+++ src/worldlight.mm
@@ -198,12 +198,12 @@
 		free(backup);
 	}
 }
 
 void
-dodynlight(
-    OFVector3D &vold, OFVector3D &v, int reach, int strength, dynent *owner)
+dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach, int strength,
+    dynent *owner)
 {
 	if (!reach)
 		reach = dynlight;
 	if (owner->monsterstate)
 		reach = reach / 2;