Beispiel #1
0
/**
 * @brief Allows switching boolean cvars between zero and not-zero from console
 */
static void Cvar_Switch_f (void)
{
	const int c = Cmd_Argc();
	if (c != 2 && c != 3) {
		Com_Printf("Usage: %s <variable> [u / s / a]\n", Cmd_Argv(0));
		return;
	}

	if (c == 3) {
		const char* arg = Cmd_Argv(2);
		int flags = 0;

		while (arg[0] != '\0') {
			switch (arg[0]) {
			case 'u':
				flags |= CVAR_USERINFO;
				break;
			case 's':
				flags |= CVAR_SERVERINFO;
				break;
			case 'a':
				flags |= CVAR_ARCHIVE;
				break;
			default:
				Com_Printf("invalid flags %c given\n", arg[0]);
				break;
			}
			arg++;
		}
		Cvar_FullSet(Cmd_Argv(1), va("%i", !Cvar_GetInteger(Cmd_Argv(1))), flags);
	} else {
		Com_Printf("val: %i\n", Cvar_GetInteger(Cmd_Argv(1)));
		Cvar_Set(Cmd_Argv(1), "%i", !Cvar_GetInteger(Cmd_Argv(1)));
	}
}
Beispiel #2
0
/**
 * @brief Reset cheat cvar values to default
 * @sa CL_SendCommand
 */
void Cvar_FixCheatVars (void)
{
	if (!(Com_ServerState() && !Cvar_GetInteger("sv_cheats")))
		return;

	for (cvar_t* var = cvarVars; var; var = var->next) {
		if (!(var->flags & CVAR_CHEAT))
			continue;

		if (!var->defaultString) {
			Com_Printf("Cheat cvars: Cvar %s has no default value\n", var->name);
			continue;
		}

		if (Q_streq(var->string, var->defaultString))
			continue;

		/* also remove the oldString value here */
		Mem_Free(var->oldString);
		var->oldString = nullptr;
		Mem_Free(var->string);
		var->string = Mem_PoolStrDup(var->defaultString, com_cvarSysPool, 0);
		var->value = atof(var->string);
		var->integer = atoi(var->string);

		Com_Printf("'%s' is a cheat cvar - activate sv_cheats to use it.\n", var->name);
	}
}
Beispiel #3
0
/**
 * @brief Starts a new skirmish game
 */
static void GAME_SK_Start_f (void)
{
	char map[MAX_VAR];
	mapDef_t *md;

	if (!chrDisplayList.num) {
		unsigned int i;
		/** @todo make the teamdef configurable */
		const char *ugvTeamDefID = "phalanx_ugv_phoenix";
		const char *name = Cvar_GetString("cl_equip");
		const equipDef_t *ed = INV_GetEquipmentDefinitionByID(name);
		const size_t size = GAME_GetCharacterArraySize();
		uint32_t maxSoldiers = Cvar_GetInteger("sv_maxsoldiersperplayer");
		uint32_t ugvs = Cvar_GetInteger("cl_ugvs");

		if (maxSoldiers <= 0)
			maxSoldiers = size;

		ugvs = min(ugvs, size - maxSoldiers);
		Com_Printf("Starting skirmish with %i soldiers and %i ugvs\n", maxSoldiers, ugvs);
		GAME_AutoTeam(name, maxSoldiers);
		for (i = 0; i < ugvs; i++)
			GAME_AppendTeamMember(i + maxSoldiers, ugvTeamDefID, ed);
	} else {
		Com_Printf("Using already loaded team with %i members\n", chrDisplayList.num);
	}

	assert(cls.currentSelectedMap >= 0);
	assert(cls.currentSelectedMap < MAX_MAPDEFS);

	md = Com_GetMapDefByIDX(cls.currentSelectedMap);
	if (!md)
		return;

	GAME_SK_SetMissionParameters(md);

	assert(md->map);
	Com_sprintf(map, sizeof(map), "map %s %s %s;", Cvar_GetInteger("mn_serverday") ? "day" : "night", md->map, md->param ? md->param : "");

	/* prepare */
	UI_InitStack(NULL, "singleplayermission", qtrue, qfalse);

	Cbuf_AddText(map);
}
Beispiel #4
0
/**
 * @brief Use current skin for all team members onboard.
 */
static void CL_ChangeSkinForWholeTeam_f (void)
{
	/* Get selected skin and fall back to default skin if it is not valid. */
	const int newSkin = CL_FixActorSkinIDX(Cvar_GetInteger("mn_body_skin"));
	/* Apply new skin to all (shown/displayed) team-members. */
	/** @todo What happens if a model of a team member does not have the selected skin? */
	LIST_Foreach(chrDisplayList, character_t, chr) {
		/** @todo Get the skin id from the model by using the actorskin id */
		/** @todo Or remove skins from models and convert character_t->skin to string */
		chr->bodySkin = newSkin;
	}
}
Beispiel #5
0
/**
 * @brief Change the skin of the selected actor.
 */
static void CL_ChangeSkin_f (void)
{
	const int sel = cl_selected->integer;
	character_t* chr = (character_t*)LIST_GetByIdx(chrDisplayList, sel);
	if (chr == nullptr) {
		return;
	}
	const int newSkin = CL_FixActorSkinIDX(Cvar_GetInteger("mn_body_skin"));
	Cvar_SetValue("mn_body_skin", newSkin);
	/** @todo Get the skin id from the model by using the actorskin id */
	/** @todo Or remove skins from models and convert character_t->skin to string */
	chr->bodySkin = newSkin;
}
Beispiel #6
0
/**
 * @sa AIR_IsAircraftOnGeoscape
 */
aircraft_t *UFO_GetNextOnGeoscape (aircraft_t *lastUFO)
{
	aircraft_t* ufo = lastUFO;
	while ((ufo = UFO_GetNext(ufo)) != NULL) {
		if (UFO_IsUFOSeenOnGeoscape(ufo)
#ifdef DEBUG
		|| Cvar_GetInteger("debug_showufos")
#endif
		)
			return ufo;
	}

	return NULL;
}
Beispiel #7
0
/**
 * @brief Take a screen shot of the map with the position of the radar
 *
 * We add 1 pixel into the border to easy check the result:
 * the screen shot must have a border of 1 black pixel
 */
static void CL_BattlescapeRadarGenerate_f (void)
{
	const int border = 0;
	const char* mapName = Cvar_GetString("sv_mapname");

	const int level = Cvar_GetInteger("cl_worldlevel");
	const char* filename = nullptr;
	int x, y, width, height;

	CL_BattlescapeRadarMapInFrameBuffer(&x, &y, &width, &height);
	if (mapName)
		filename = va("%s_%i", mapName, level + 1);
	R_ScreenShot(x - border, y - border, width + border * 2, height + border * 2, filename, nullptr);
}
Beispiel #8
0
/**
 * @brief Use current skin for all team members onboard.
 */
static void CL_ChangeSkinForWholeTeam_f (void)
{
	int newSkin, i;

	/* Get selected skin and fall back to default skin if it is not valid. */
	newSkin = Cvar_GetInteger("mn_body_skin");
	newSkin = CL_FixActorSkinIDX(newSkin);

	/* Apply new skin to all (shown/displayed) team-members. */
	/** @todo What happens if a model of a team member does not have the selected skin? */
	for (i = 0; i < chrDisplayList.num; i++) {
		character_t *chr = chrDisplayList.chr[i];
		assert(chr);
		/** @todo Get the skin id from the model by using the actorskin id */
		/** @todo Or remove skins from models and convert character_t->skin to string */
		chr->bodySkin = newSkin;
	}
}
Beispiel #9
0
/**
 * @brief Change the skin of the selected actor.
 */
static void CL_ChangeSkin_f (void)
{
	const int sel = cl_selected->integer;

	if (sel >= 0 && sel < chrDisplayList.num) {
		int newSkin = Cvar_GetInteger("mn_body_skin");
		character_t *chr = chrDisplayList.chr[sel];
		newSkin = CL_FixActorSkinIDX(newSkin);

		if (chr) {
			/** @todo Get the skin id from the model by using the actorskin id */
			/** @todo Or remove skins from models and convert character_t->skin to string */
			chr->bodySkin = newSkin;

			Cvar_SetValue("mn_body_skin", newSkin);
			Cvar_Set("mn_skinname", CL_GetTeamSkinName(newSkin));
		}
	}
}
Beispiel #10
0
static void Com_DeveloperSet_f (void)
{
	int oldValue = Cvar_GetInteger("developer");
	int newValue = oldValue;
	int i = 0;

	if (Cmd_Argc() == 2) {
		const char* debugLevel = Cmd_Argv(1);
		while (debugLevels[i].str) {
			if (Q_streq(debugLevel, debugLevels[i].str)) {
				if (oldValue & debugLevels[i].debugLevel)	/* if it's already set... */
					newValue &= ~debugLevels[i].debugLevel;	/* ...reset it. */
				else
					newValue |= debugLevels[i].debugLevel;
				break;
			}
			i++;
		}
		if (!debugLevels[i].str) {
			Com_Printf("No valid debug mode parameter\n");
			return;
		}
		Cvar_SetValue("developer", newValue);
		Com_Printf("Currently selected debug print levels\n");
		i = 0;
		while (debugLevels[i].str) {
			if (newValue & debugLevels[i].debugLevel)
				Com_Printf("* %s\n", debugLevels[i].str);
			i++;
		}
	} else {
		Com_Printf("Usage: %s <debug_level>\n", Cmd_Argv(0));
		Com_Printf("  valid debug_levels are:\n");
		while (debugLevels[i].str) {
			Com_Printf("  * %s\n", debugLevels[i].str);
			i++;
		}
	}
}
void	GL_RenderScene(camera_t *camera, vec3_t userpos, unsigned int sceneRenderFlags)
{
	if (!world.cl_loaded || !gfx_render.integer)
	{
		//GL_2dMode();
		return;
	}
	
	static float lasttime=0;
	camera_t sunCam;
	Pass basePass;
	worldLight_t rimLight;
	float lerp;
	float ambBoost = (vid_realbright.integer && gfx_GLSLQuality.integer<=1) ? vid_realbrightMult.value : 1.0f;
	
	basePass.setViewer(camera);
	scene_cam = camera;
	
	sunLight.origin[0] = -wr_sun_x.value;
	sunLight.origin[1] = -wr_sun_y.value;
	sunLight.origin[2] = -wr_sun_z.value;
	
	memset(&rimLight, 0, sizeof(worldLight_t));
	
	M_MultVec3(sunLight.origin, -1, rimLight.origin);
	rimLight.type = LIGHT_DIRECTIONAL;
	
	//// SHADOW MAP RENDER ////
	//shadow pass, render to texture
	if(gfx_shadow.integer && gfx_shadowQuality.integer >= 1 && gfx_GLSLQuality.integer>1)
	{
		vec3_t wmin, wmax, sunLook;

		
			
			World_GetBounds(wmin, wmax);
			//SET_VEC3(sunLook, (wmax[0]-wmin[0])*.5f, (wmax[1]-wmin[1])*.5f, 0 * .5f);
			M_MultVec3(camera->viewaxis[AXIS_FORWARD], camera->farclip*0.05, sunLook);
			M_AddVec2(sunLook, camera->origin, sunLook);
			
			Cam_DefaultCamera(&sunCam, gfx_shadowSize.integer, gfx_shadowSize.integer, 2, 8192);
		if(wr_sun_z.value < 0) {
			SET_VEC3(sunCam.origin, -wr_sun_x.value, -wr_sun_y.value, -wr_sun_z.value);
		} else {
			SET_VEC3(sunCam.origin, wr_sun_x.value, wr_sun_y.value, wr_sun_z.value);
		}
			M_Normalize(sunCam.origin);
			M_MultVec3(sunCam.origin, wmax[0], sunCam.origin);
			sunCam.origin[0]+=sunLook[0];
			sunCam.origin[1]+=sunLook[1];
			M_SubVec3(sunLook, sunCam.origin, sunCam.viewaxis[AXIS_FORWARD]);
		
			M_GetAxisFromForwardVec(sunCam.viewaxis[AXIS_FORWARD], sunCam.viewaxis);
			sunCam.fovy = 50;
			sunCam.time = camera->time;
			sunCam.fog_far=100000;
			sunCam.fog_near=99999;
			sunCam.flags |= CAM_NO_SKY;
			
			//CSM setup
			if(gfx_shadowQuality.integer >= 2) {
				shadowSplit=0;
				for(int i=0;i<gfx_shadowQuality.integer;i++) {
					cascadedPass[i].setViewer(&sunCam);
					renderer.addPass(&cascadedPass[i]);
				}
			} else {
				shadowPass.setViewer(&sunCam);
				renderer.addPass(&shadowPass);
			}
		
		
	} else if( gfx_GLSLQuality.integer > 1 ) {
		renderer.addPass(&shadowPass);
	}
	
	if(gfx_postProcEnabled.integer && gfx_GLSLQuality.integer>1 && (gfx_depthNormalPass.integer || gfx_postSSAO.integer)) {
		zPass->setViewer(camera);
		renderer.addPass(zPass);
	}
	
	basePass.setDepthFunc(GL_LEQUAL);
	basePass.setDepthMask(true);
	basePass.clearDepth(true);
	
	if(gfx_GLSLQuality.integer>1 && gfx_postProcessing.integer)
		basePass.setTarget(screenPostTarget);
	else
		basePass.setTarget(glScreen);
	
	renderer.addPass(&basePass);
	
	//// BUILD RENDER LIST ////
	
	// sun/moon
	
	if(wr_sun_z.value < 0) {
		lerp = CLAMP(fabs(-1 - cos(wr_sun_phi.value)), 0, 1);
		
		//Console_Printf("phi %f lerp %f\n", wr_sun_phi.value, lerp);
		
		//setup sun light color and ambient
		sunLight.ambient[0] = obj_ambient_r.value*ambBoost;
		sunLight.ambient[1] = obj_ambient_g.value*ambBoost;
		sunLight.ambient[2] = obj_ambient_b.value*ambBoost;
		sunLight.color[0] = obj_light0_r.value*.9;
		sunLight.color[1] = obj_light0_g.value*.9;
		sunLight.color[2] = obj_light0_b.value*.98;
		
		rimLight.ambient[0] = 0;
		rimLight.ambient[1] = 0;
		rimLight.ambient[2] = 0;
		rimLight.color[0] = lerp*obj_light1_r.value*.9;
		rimLight.color[1] = lerp*obj_light1_g.value*.9;
		rimLight.color[2] = lerp*obj_light1_b.value*.98;
	} else {
		if(wr_sun_phi.value < 5.23f && wr_sun_phi.value > 1.0472f)
			lerp = CLAMP(fabs(1-cos(3*(M_PI-wr_sun_phi.value))), 0, 1);
		else
			lerp = 0;
		
		//Console_Printf("phi %f lerp %f cos %f\n", wr_sun_phi.value, lerp, cos(3*(M_PI-wr_sun_phi.value)));
		
		//the lights need to flip
		M_MultVec3(sunLight.origin, -1, sunLight.origin);
		M_MultVec3(rimLight.origin, -1, rimLight.origin);
		
		//setup moon light color and ambient
		sunLight.ambient[0] = obj_ambient_r.value*ambBoost;
		sunLight.ambient[1] = obj_ambient_g.value*ambBoost;
		sunLight.ambient[2] = obj_ambient_b.value*ambBoost;
		sunLight.color[0] = obj_light1_r.value*.9;
		sunLight.color[1] = obj_light1_g.value*.9;
		sunLight.color[2] = obj_light1_b.value*.98;
		
		rimLight.ambient[0] = 0;
		rimLight.ambient[1] = 0;
		rimLight.ambient[2] = 0;
		rimLight.color[0] = lerp*obj_light0_r.value*.9;
		rimLight.color[1] = lerp*obj_light0_g.value*.9;
		rimLight.color[2] = lerp*obj_light0_b.value*.98;
	}
	
	renderer.addLight(&sunLight, NULL, 0);
	renderer.addLight(&rimLight, NULL, 0);
	
	// The deal here is rather than changing the code in scene.cpp to support the
	// new renderer, we just "import" all the scene data
	
	//// SCENE DATA ////
	
	lightCount = 0;
	//objects
	scenelist_t *list;
	for (list = scenelist; list; list = list->next)
	{
		//if(list->cull)
		//	continue;
		
		switch (list->obj.objtype)
		{
			case OBJTYPE_MODEL:
				GL_AddModelToLists(&list->obj);
				break;
			case OBJTYPE_LIGHT:
				GL_AddLight(0, &list->obj);
				break;
			default:
				break;
		}
	}
	
	//polys
	scenefacelist_t *flist;
	for (flist = scenefacelist; flist; flist = flist->next)
	{
		renderer.addPoly(flist, NULL, flist->shader);
	}
	
	//decals
	for (flist = scenefacelist_decals; flist; flist = flist->next)
	{
		renderer.addPoly(flist,  NULL, flist->shader);
	}
	
	//lights
	scenelightlist_t *llist;
	for (llist = scenelightlist; llist; llist = llist->next)
	{
		worldLight_t *l = &worldlights[lightCount++];
		M_CopyVec3(llist->light.color, l->color);
		M_CopyVec3(llist->light.pos, l->origin);
		renderer.addLight(l, NULL, 0);
	}
	
	//// END SCENE DATA ////
	
	//// ENVIRO ////
	
	if(!(camera->flags & CAM_NO_WORLD)) {
		
		//terrain
		if (!(scene_cam->flags & CAM_NO_TERRAIN))
			renderer.addCustomListItem(&terrainItem, false);
		
		//sprites
		for (std::list<scenelist_t*>::iterator itr = spritelist.begin(); itr != spritelist.end(); itr++)
		{
			scenelist_t *sprite = *itr;
			renderer.addSprite(sprite, &sprite->obj, sprite->obj.shader);
		}
		
		//clouds (at the back)
		if(gfx_sky.integer) {
			renderer.addCustomListItem(&cloudsItem, false);
		}
	
		if(gfx_water.integer) {
			waterItem.set(NULL,NULL,0);
			renderer.addCustomListItem(&waterItem, false);
		}
		
		//sky
		if (gfx_sky.integer) {
			if(Cvar_GetInteger("tl_suntod") <= 1440) 
				sky.setTimeofDay(Cvar_GetValue("tl_suntod")/1440.0f);
			else
				sky.setTimeofDay((Cvar_GetValue("tod_sunminute"))/1440.0f);
			
			//renderer.addListItem(&clouds, false);
			renderer.addCustomListItem(&sky, false);
		}
		
		//// FX LAYER ////
		
		//polys
		for (flist = scenefxfacelist; flist; flist = flist->next)
		{
			renderer.addPolyFX(flist, NULL, flist->shader);
		}
		
		//decals
		for (flist = scenefxfacelist_decals; flist; flist = flist->next)
		{
			renderer.addPolyFX(flist,  NULL, flist->shader);
		}
		
		//sprites
		for (std::list<scenelist_t*>::iterator itr = spritefxlist.begin(); itr != spritefxlist.end(); itr++)
		{
			scenelist_t *sprite = *itr;
			renderer.addSpriteFX(sprite, &sprite->obj, sprite->obj.shader);
		}
	}
	
	//// RENDER ////
	
	renderer.render(camera->time-lasttime);
	
	lasttime = camera->time;
}
Beispiel #12
0
/**
 * @brief Draws weather effects
 */
void Weather::render (void)
{
	/* Don't play the weather particles if the user doesn't want them */
	if (!Cvar_GetInteger("cl_particleweather")) {
		return;
	}

	GLfloat prtPos[3 * 4 * Weather::MAX_PARTICLES];
	GLfloat prtTexcoord[2 * 4 * Weather::MAX_PARTICLES];
	GLushort prtIndex[3 * 2 * Weather::MAX_PARTICLES];
	size_t prtCount = 0;
	//const float splashTimeScale = 0.001f / splashTime; /* from msec to 1/sec units, so division is done only once per frame */
	/** @todo shadowcasting at least for the sunlight */
#if 0 // disabled because of bizarre colors
	vec4_t prtColor = {color[0] * (refdef.ambientColor[0] + refdef.sunDiffuseColor[0]),
		color[1] * (refdef.ambientColor[1] + refdef.sunDiffuseColor[1]),
		color[2] * (refdef.ambientColor[2] + refdef.sunDiffuseColor[2]),
		color[3]};
#else
	vec_t prtBrightness = VectorLength(refdef.ambientColor) + VectorLength(refdef.sunDiffuseColor); /** @todo proper color to brightness mapping */
	vec4_t prtColor = {color[0] * prtBrightness, color[1] * prtBrightness, color[2] * prtBrightness, color[3]}; /* alpha is not to be scaled */
#endif

	for (size_t i = 0; i < Weather::MAX_PARTICLES; i++) {
		Weather::particle &prt = particles[i];

		if (prt.ttl < 0)
			continue;

		/* if particle is alive, construct a camera-facing quad */
		GLfloat x, y, z;
		GLfloat dx, dy, dz;
		GLfloat thisParticleSize = particleSize;
		if (prt.vz == 0 && splashTime > 0) {
			/* splash particle, do zoom and other things */
			/** @todo alpha decay */
			const float splashFactor = prt.ttl / 500.0f;//1.0f - prt.ttl * splashTimeScale;
			thisParticleSize *= (splashSize - 1.0f) * splashFactor  + 1.0f;
			dx = dy = thisParticleSize;
		} else {
			dx = i & 1 ? thisParticleSize* 2 : thisParticleSize; dy = i & 1 ? thisParticleSize : thisParticleSize * 2 ; /** @todo make a proper projection instead of this hack */
		}

		x = prt.x; y = prt.y; z = prt.z;
		dz = smearLength * prt.vz * 0.5; /** @todo should use proper velocity vector ... or maybe not */
		/* construct a particle geometry; */
		GLfloat *pos = &prtPos[3 * 4 * prtCount];
		GLfloat *texcoord = &prtTexcoord[2 * 4 * prtCount];
		GLushort *idx = &prtIndex[3 * 2 * prtCount];

		/** @todo actually rotate billboard in the correct position */
		pos[0] = x - dx; pos[1] = y - dy; pos[2] = z + dz * 2;
		pos[3] = x + dx; pos[4] = y - dy; pos[5] = z + dz *2;
		pos[6] = x + dx; pos[7] = y + dy; pos[8] = z;
		pos[9] = x - dx; pos[10] = y + dy; pos[11] = z;

		texcoord[0] = 0; texcoord[1] = 0; /* upper left vertex */
		texcoord[2] = 1.0f; texcoord[3] = 0; /* upper right vertex */
		texcoord[4] = 1.0f; texcoord[5] = 1.0f; /* bottom right vertex */
		texcoord[6] = 0; texcoord[7] = 1.0f; /* bottom left vertex */

		idx[0] = 4 * prtCount; idx[1] = 4 * prtCount + 1; idx[2] = 4 * prtCount + 2;
		idx[3] = 4 * prtCount + 2; idx[4] = 4 * prtCount + 3; idx[5] = 4 * prtCount;

		prtCount++;
	}

	if (prtCount < 1)
		return;

	/* draw the generated array */
	R_Color(prtColor);
	glBindTexture(GL_TEXTURE_2D, wpTexture());
	R_BindArray(GL_VERTEX_ARRAY, GL_FLOAT, prtPos);
	R_BindArray(GL_TEXTURE_COORD_ARRAY, GL_FLOAT, prtTexcoord);
	R_EnableBlend(true);
	glDrawElements(GL_TRIANGLES, prtCount * 3 * 2, GL_UNSIGNED_SHORT, prtIndex);
	R_EnableBlend(false);
	R_ResetArrayState();
	R_Color(nullptr);

	refdef.batchCount++;
}
Beispiel #13
0
/**
 * @brief Updates weather for the time passed; handles particle creation/removal automatically
 */
void Weather::update (int milliseconds)
{
	/* Don't play the weather particles if the user doesn't want them */
	if (!Cvar_GetInteger("cl_particleweather")) {
		/* This makes weather look very weird if it is enabled mid-battle */
		/* clearParticles(); */
		return;
	}

	size_t dead = 0;
	/* physics: check for ttl and move live particles */
	for (size_t i = 0; i < Weather::MAX_PARTICLES; i++) {
		Weather::particle &prt = particles[i];
		if (prt.ttl < 0) {
			dead++;
			continue;
		} else { /** @todo creates vanishing-before-impact particles at low framerates -- should improve that somehow */
			int restOfLife = prt.ttl -= milliseconds;
			if (restOfLife < 0) {
				if (fabs(prt.vz) < 0.001f || splashTime < 1) {
					/* either no splash or is a splash particle dying */
					dead++;
					continue;
				}
				/* convert it into splash particle */
				/* technically, we should complete the last frame of movement, but with current particle types it looks good even without it */
				prt.vz = 0; prt.vx = 0; prt.vy = 0;
				prt.ttl += splashTime;
			}
		}
		/* if we got so far, particle is alive and probably needs a physics update */
		const int timeAfterUpdate = prt.timeout -= milliseconds;
		const float moveDuration = milliseconds * 0.001f;

		prt.x += prt.vx * moveDuration; prt.y += prt.vy * moveDuration; prt.z += prt.vz * moveDuration;

		if (timeAfterUpdate > 0) {
			/* just move linearly */
			continue;
		}
		/* alter motion vector */
		/** @todo */
	}
	/* create new ones in place of dead */
	if (dead) {
		const float windX = cos(windDirection) * (windStrength + crand() * windTurbulence);
		const float windY = sin(windDirection) * (windStrength + crand() * windTurbulence);

		AABB weatherZone; /** < extents of zone in which weather particles will be rendered */
		weatherZone = cl.mapData->mapBox;
		weatherZone.expandXY(256);
		weatherZone.maxs[2] += 512;

		Line prtPath;

		if (dead > 200) dead = 200; /* avoid creating too much particles in the single frame, it could kill the low-end hardware */

		int debugCreated = 0;
		for (size_t i = 0; dead &&  i < Weather::MAX_PARTICLES && i < weatherStrength * Weather::MAX_PARTICLES; i++) {
			Weather::particle &prt = particles[i];
			if (prt.ttl >= 0)
				continue;
			prt.x = (frand() * (weatherZone.getMaxX() - weatherZone.getMinX())) + weatherZone.getMinX();
			prt.y = (frand() * (weatherZone.getMaxY() - weatherZone.getMinY())) + weatherZone.getMinY();
			prt.z = weatherZone.getMaxZ();

			prt.vx = windX;
			prt.vy = windY;
			prt.vz = -fallingSpeed * (frand() * 0.2f + 0.9f);

			float lifeTime = (weatherZone.getMaxZ() - weatherZone.getMinZ()) / -prt.vz; /* default */

			VectorSet(prtPath.start, prt.x, prt.y, prt.z);
			VectorSet(prtPath.stop, prt.x + prt.vx * lifeTime, prt.y + prt.vy * lifeTime, prt.z + prt.vz * lifeTime);

			trace_t trace = CL_Trace(prtPath, AABB::EMPTY, nullptr, nullptr, MASK_SOLID, cl.mapMaxLevel - 1); /* find the collision point */
			lifeTime *= trace.fraction;

			prt.ttl = 1000 * lifeTime; /* convert to milliseconds */

			prt.timeout = prt.ttl + 1000000; /** @todo proper code for physics */
			debugCreated++;
			dead--;
		}
	}
#if 0
	if (debugCreated) Com_Printf("created %i weather particles\n", debugCreated);
#endif
}