Exemplo n.º 1
0
/**
 * @brief Replace injection identifiers (e.g. <eventParam>) by a value
 * @note The injection identifier can be every node value - e.g. <image> or <width>.
 * It's also possible to do something like
 * @code
 * cmd "set someCvar <min>/<max>"
 * @endcode
 */
const char* UI_GenInjectedString (const char* input, qboolean addNewLine, const uiCallContext_t *context)
{
	static char cmd[256];
	int length = sizeof(cmd) - (addNewLine ? 2 : 1);
	static char propertyName[MAX_VAR];
	const char *cin = input;
	char *cout = cmd;

	while (length && cin[0] != '\0') {
		if (cin[0] == '<') {
			/* read propertyName between '<' and '>' */
			const char *next = UI_GenCommandReadProperty(cin, propertyName, sizeof(propertyName));
			if (next) {
				/* cvar injection */
				if (Q_strstart(propertyName, "cvar:")) {
					const cvar_t *cvar = Cvar_Get(propertyName + 5, "", 0, NULL);
					const int l = snprintf(cout, length, "%s", cvar->string);
					cout += l;
					cin = next;
					length -= l;
					continue;

				} else if (Q_strstart(propertyName, "node:")) {
					const char *path = propertyName + 5;
					uiNode_t *node;
					const value_t *property;
					const char* string;
					int l;
					UI_ReadNodePath(path, context->source, &node, &property);
					if (!node) {
						Com_Printf("UI_GenInjectedString: Node '%s' wasn't found; '' returned\n", path);
#ifdef DEBUG
						Com_Printf("UI_GenInjectedString: Path relative to '%s'\n", UI_GetPath(context->source));
#endif
						string = "";
					} else if (!property) {
						Com_Printf("UI_GenInjectedString: Property '%s' wasn't found; '' returned\n", path);
						string = "";
					} else {
						string = UI_GetStringFromNodeProperty(node, property);
						if (string == NULL) {
							Com_Printf("UI_GenInjectedString: String getter for '%s' property do not exists; '' injected\n", path);
							string = "";
						}
					}

					l = snprintf(cout, length, "%s", string);
					cout += l;
					cin = next;
					length -= l;
					continue;

				/* source path injection */
				} else if (Q_strstart(propertyName, "path:")) {
					if (context->source) {
						const char *command = propertyName + 5;
						const uiNode_t *node = NULL;
						if (Q_streq(command, "root"))
							node = context->source->root;
						else if (Q_streq(command, "this"))
							node = context->source;
						else if (Q_streq(command, "parent"))
							node = context->source->parent;
						else
							Com_Printf("UI_GenCommand: Command '%s' for path injection unknown\n", command);

						if (node) {
							const int l = snprintf(cout, length, "%s", UI_GetPath(node));
							cout += l;
							cin = next;
							length -= l;
							continue;
						}
					}

				/* no prefix */
				} else {
					/* source property injection */
					if (context->source) {
						/* find property definition */
						const value_t *property = UI_GetPropertyFromBehaviour(context->source->behaviour, propertyName);
						if (property) {
							const char* value;
							int l;
							/* inject the property value */
							value = UI_GetStringFromNodeProperty(context->source, property);
							if (value == NULL)
								value = "";
							l = snprintf(cout, length, "%s", value);
							cout += l;
							cin = next;
							length -= l;
							continue;
						}
					}

					/* param injection */
					if (UI_GetParamNumber(context) != 0) {
						int arg;
						const int checked = sscanf(propertyName, "%d", &arg);
						if (checked == 1 && arg >= 1 && arg <= UI_GetParamNumber(context)) {
							const int l = snprintf(cout, length, "%s", UI_GetParam(context, arg));
							cout += l;
							cin = next;
							length -= l;
							continue;
						}
					}
				}
			}
		}
		*cout++ = *cin++;
		length--;
	}

	/* is buffer too small? */
	assert(cin[0] == '\0');

	if (addNewLine)
		*cout++ = '\n';

	*cout++ = '\0';

	/* copy the result into a free va slot */
	return va("%s", cmd);
}
Exemplo n.º 2
0
/**
 * @brief Copies an entry from the installation description file into the list of installation templates.
 * @note Parses one "installation" entry in the installation.ufo file and writes
 * it into the next free entry in installationTemplates.
 * @param[in] name Unique test-id of a installationTemplate_t.
 * @param[in] text the rest of the script file that is tokenized here
 */
void INS_ParseInstallations (const char *name, const char **text)
{
	installationTemplate_t *installation;
	const char *errhead = "INS_ParseInstallations: unexpected end of file (names ";
	const char *token;
	int i;

	/* get id list body */
	token = Com_Parse(text);
	if (!*text || *token != '{') {
		Com_Printf("INS_ParseInstallations: installation \"%s\" without body ignored\n", name);
		return;
	}

	if (!name) {
		Com_Printf("INS_ParseInstallations: installation name not specified.\n");
		return;
	}

	if (ccs.numInstallationTemplates >= MAX_INSTALLATION_TEMPLATES) {
		Com_Printf("INS_ParseInstallations: too many installation templates\n");
		ccs.numInstallationTemplates = MAX_INSTALLATION_TEMPLATES;	/* just in case it's bigger. */
		return;
	}

	for (i = 0; i < ccs.numInstallationTemplates; i++) {
		if (Q_streq(ccs.installationTemplates[i].name, name)) {
			Com_Printf("INS_ParseInstallations: Second installation with same name found (%s) - second ignored\n", name);
			return;
		}
	}

	/* new entry */
	installation = &ccs.installationTemplates[ccs.numInstallationTemplates];
	OBJZERO(*installation);
	installation->id = Mem_PoolStrDup(name, cp_campaignPool, 0);

	Com_DPrintf(DEBUG_CLIENT, "...found installation %s\n", installation->id);

	ccs.numInstallationTemplates++;
	do {
		/* get the name type */
		token = Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		/* check for some standard values */
		if (!Com_ParseBlockToken(name, text, installation, installation_vals, cp_campaignPool, token)) {
			/* other values */
			if (Q_streq(token, "cost")) {
				char cvarname[MAX_VAR] = "mn_installation_";

				Q_strcat(cvarname, installation->id, sizeof(cvarname));
				Q_strcat(cvarname, "_cost", sizeof(cvarname));

				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;
				installation->cost = atoi(token);

				Cvar_Set(cvarname, va(_("%d c"), atoi(token)));
			} else if (Q_streq(token, "buildtime")) {
				char cvarname[MAX_VAR];

				token = Com_EParse(text, errhead, name);
				if (!*text)
					return;
				installation->buildTime = atoi(token);

				Com_sprintf(cvarname, sizeof(cvarname), "mn_installation_%s_buildtime", installation->id);
				Cvar_Set(cvarname, va(ngettext("%d day", "%d days", atoi(token)), atoi(token)));
			}
		}
	} while (*text);
}
Exemplo n.º 3
0
/**
 * @brief This test cycles through the list of map definitions found in the maps.ufo script
 * and builds each map with each ufo and every assembly for all valid seeds.
 * In other words: this a FULL test to check if some seed causes problems in any of the
 * possible combinations, so it should not be run on the buildserver on a daily basis.
 */
TEST_F(MapDefMassRMATest, DISABLED_MapDefsMassRMA)
{
	/** You can test a certain assembly by passing "-Dmapdef-id=assembly" to testall. */
	const char* filterId = TEST_GetStringProperty("mapdef-id");
	const mapDef_t* md;
	int mapCount = 0;

	ASSERT_TRUE(csi.numMDs > 0);

	MapDef_Foreach(md) {
		if (md->mapTheme[0] == '.')
			continue;
		if (filterId && !Q_streq(filterId, md->id))
			continue;
		if (++mapCount <= 0)		/* change 0 to n to skip the first n assemblies */
			continue;

		{
			char* p = md->mapTheme;

			if (*p == '+')
				p++;
			else
				continue;

			const char* asmName = (const char*)LIST_GetByIdx(md->params, 0);
			Com_Printf("\nMap: %s Assembly: %s AssNr: %i\n", p, asmName, mapCount);

			sv_threads->integer = 0;

			/* This is tricky. Some maps don't have any ufo on them and thus in the mapdef.
			 * That would cause a LIST_Foreach macro to never run it's body. That's why these
			 * for-loops seem to have two termination conditions. In fact, we have to manually
			 * exit the for-loops if we ran it just once (without ufos nor dropships).
			 */
			bool didItOnce = false;
			linkedList_t* iterDrop;
			for (iterDrop = md->aircraft; iterDrop || !didItOnce; iterDrop = iterDrop->next) {
				const char* craft = nullptr;
				if (iterDrop)
					craft = (const char*) (iterDrop->data);

				if (craft)
					Cvar_Set("rm_drop", "%s", Com_GetRandomMapAssemblyNameForCraft(craft));
				else
					Cvar_Set("rm_drop", "+craft_drop_firebird");

				linkedList_t* iterUfo;
				for (iterUfo = md->ufos; iterUfo || !didItOnce; iterUfo = iterUfo->next) {
					const char* ufo = nullptr;
					if (iterUfo)
						ufo = (const char*) (iterUfo->data);
					if (ufo)
						Cvar_Set("rm_ufo", "%s", Com_GetRandomMapAssemblyNameForCraft(ufo));
					else
						Cvar_Set("rm_ufo", "+craft_ufo_scout");

					Com_Printf("\nDrop: %s Ufo: %s", craft, ufo);
					Com_Printf("\nSeed:");
					for (int i = 0; i < RMA_HIGHEST_SUPPORTED_SEED; i++) {
						asmName = nullptr;
						srand(i);
						long time = Sys_Milliseconds();
						Com_Printf(" %i", i);
#if 0

						typedef struct skip_info {
							int         seed;
							char const* map;
							char const* params;
							char const* craft;
							char const* ufo;
						} skip_info;
						/* if we have known problems with some combinations, we can skip them */
						skip_info const skip_list[] = {
							/* examples: */
						//	{ 20, "forest",		"large",		"craft_drop_raptor",   0                     },
						//	{ 12, "forest",		"large"			"craft_drop_herakles", "craft_ufo_harvester" },
							{ -1, "frozen",		"nature_medium",0,                     0                     },
							{ 11, "village",	"medium",		0,                     0					 },
							{ 19, "village",	"medium",		0,                     0					 },
							{ -1, "village",	"medium_noufo",	0,                     0					 },
							{ -1, "village",	"small",		0,                     0					 },
						};

						bool skip = false;
						for (skip_info const* e = skip_list; e != endof(skip_list); ++e) {
							if (e->seed >= 0 && i != e->seed)
								continue;
							if (e->map && !Q_streq(p, e->map))
								continue;
							if (e->params && !Q_streq(md->params, e->params))
								continue;
							if (e->craft && !Q_streq(craft, e->craft))
								continue;
							if (e->ufo && !Q_streq(ufo, e->ufo))
								continue;
							skip = true;
							break;
						}
						if (skip)
							continue;
#endif
						/* for ufocrash map, the ufoname is the assemblyame */
						if (Q_streq(p, "ufocrash"))
							asmName = Com_GetRandomMapAssemblyNameForCraft(ufo) + 1;	/* +1 = get rid of the '+' */
						else
							asmName = (const char*)LIST_GetByIdx(md->params, 0);

						char* entityString = SV_GetConfigString(CS_ENTITYSTRING);
						const int numPlaced = SV_AssembleMap(p, asmName, mapStr, posStr, entityString, i, false);
						ASSERT_TRUE(numPlaced != 0);
						time = (Sys_Milliseconds() - time);
						ASSERT_TRUE(time < 30000);
						if (time > 10000)
							Com_Printf("\nMap: %s Assembly: %s Seed: %i tiles: %i ms: %li\n", p, asmName, i, numPlaced, time);
					}
					didItOnce = true;
					if (!iterUfo)
						break;
				}
				if (!iterDrop)
					break;
			}
		}
	}
}
Exemplo n.º 4
0
/**
 * @todo need to merge UI model case, and the common case (look to be a copy-pasted code)
 */
void UI_DrawModelNode (uiNode_t *node, const char *source)
{
	modelInfo_t mi;
	uiModel_t *model;
	vec3_t nodeorigin;
	vec3_t autoScale;
	vec3_t autoCenter;

	assert(UI_NodeInstanceOf(node, "model"));			/**< We use model extradata */

	if (source[0] == '\0')
		return;

	model = UI_GetUIModel(source);
	/* direct model name - no UI model definition */
	if (!model) {
		/* prevent the searching for a model def in the next frame */
		mi.model = R_FindModel(source);
		mi.name = source;
		if (!mi.model) {
			Com_Printf("Could not find model '%s'\n", source);
			return;
		}
	}

	/* compute the absolute origin ('origin' property is relative to the node center) */
	UI_GetNodeAbsPos(node, nodeorigin);
	R_CleanupDepthBuffer(nodeorigin[0], nodeorigin[1], node->size[0], node->size[1]);
	if (EXTRADATA(node).clipOverflow)
		R_PushClipRect(nodeorigin[0], nodeorigin[1], node->size[0], node->size[1]);
	nodeorigin[0] += node->size[0] / 2 + EXTRADATA(node).origin[0];
	nodeorigin[1] += node->size[1] / 2 + EXTRADATA(node).origin[1];
	nodeorigin[2] = EXTRADATA(node).origin[2];

	VectorMA(EXTRADATA(node).angles, cls.frametime, EXTRADATA(node).omega, EXTRADATA(node).angles);
	mi.origin = nodeorigin;
	mi.angles = EXTRADATA(node).angles;
	mi.scale = EXTRADATA(node).scale;
	mi.center = nullVector;
	mi.color = node->color;
	mi.mesh = 0;

	/* special case to draw models with UI model */
	if (model) {
		UI_DrawModelNodeWithUIModel(node, source, &mi, model);
		if (EXTRADATA(node).clipOverflow)
			R_PopClipRect();
		return;
	}

	/* if the node is linked to a parent, the parent will display it */
	if (EXTRADATA(node).tag) {
		if (EXTRADATA(node).clipOverflow)
			R_PopClipRect();
		return;
	}

	/* autoscale? */
	if (EXTRADATA(node).autoscale) {
		const vec2_t size = {node->size[0] - node->padding, node->size[1] - node->padding};
		R_ModelAutoScale(size, &mi, autoScale, autoCenter);
	}

	/* no animation */
	mi.frame = 0;
	mi.oldframe = 0;
	mi.backlerp = 0;

	/* get skin */
	if (EXTRADATA(node).skin && *EXTRADATA(node).skin)
		mi.skin = atoi(UI_GetReferenceString(node, EXTRADATA(node).skin));
	else
		mi.skin = 0;

	/* do animations */
	if (EXTRADATA(node).animation && *EXTRADATA(node).animation) {
		animState_t *as;
		const char *ref;
		ref = UI_GetReferenceString(node, EXTRADATA(node).animation);

		/* check whether the cvar value changed */
		if (strncmp(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE)) {
			Q_strncpyz(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE);
			/* model has changed but mem is already reserved in pool */
			if (EXTRADATA(node).animationState) {
				Mem_Free(EXTRADATA(node).animationState);
				EXTRADATA(node).animationState = NULL;
			}
		}
		if (!EXTRADATA(node).animationState) {
			as = (animState_t *) Mem_PoolAlloc(sizeof(*as), cl_genericPool, 0);
			if (!as)
				Com_Error(ERR_DROP, "Model %s should have animState_t for animation %s - but doesn't\n", mi.name, ref);
			R_AnimChange(as, mi.model, ref);
			EXTRADATA(node).animationState = as;
		} else {
			const char *anim;
			/* change anim if needed */
			as = EXTRADATA(node).animationState;
			if (!as)
				Com_Error(ERR_DROP, "Model %s should have animState_t for animation %s - but doesn't\n", mi.name, ref);
			anim = R_AnimGetName(as, mi.model);
			if (anim && !Q_streq(anim, ref))
				R_AnimChange(as, mi.model, ref);
			R_AnimRun(as, mi.model, cls.frametime * 1000);
		}

		mi.frame = as->frame;
		mi.oldframe = as->oldframe;
		mi.backlerp = as->backlerp;
	}

	/* draw the main model on the node */
	R_DrawModelDirect(&mi, NULL, NULL);

	/* draw all children */
	if (node->firstChild) {
		uiNode_t *child;
		modelInfo_t pmi = mi;
		for (child = node->firstChild; child; child = child->next) {
			const char *tag;
			char childSource[MAX_VAR];
			const char* childRef;

			/* skip non "model" nodes */
			if (child->behaviour != node->behaviour)
				continue;

			/* skip invisible child */
			if (child->invis || !UI_CheckVisibility(child))
				continue;

			OBJZERO(mi);
			mi.angles = EXTRADATA(child).angles;
			mi.scale = EXTRADATA(child).scale;
			mi.center = nullVector;
			mi.origin = EXTRADATA(child).origin;
			mi.color = pmi.color;

			/* get the anchor name to link the model into the parent */
			tag = EXTRADATA(child).tag;

			/* init model name */
			childRef = UI_GetReferenceString(child, EXTRADATA(child).model);
			if (Q_strnull(childRef))
				childSource[0] = '\0';
			else
				Q_strncpyz(childSource, childRef, sizeof(childSource));
			mi.model = R_FindModel(childSource);
			mi.name = childSource;

			/* init skin */
			if (EXTRADATA(child).skin && *EXTRADATA(child).skin)
				mi.skin = atoi(UI_GetReferenceString(child, EXTRADATA(child).skin));
			else
				mi.skin = 0;

			R_DrawModelDirect(&mi, &pmi, tag);
		}
	}

	if (EXTRADATA(node).clipOverflow)
		R_PopClipRect();
}
Exemplo n.º 5
0
/**
 * @brief Parse medals and ranks defined in the medals.ufo file.
 * @sa CL_ParseScriptFirst
 */
void CL_ParseRanks (const char* name, const char** text)
{
	rank_t* rank;
	const char* errhead = "CL_ParseRanks: unexpected end of file (medal/rank ";
	const char* token;

	/* get name list body body */
	token = Com_Parse(text);

	if (!*text || *token != '{') {
		Com_Printf("CL_ParseRanks: rank/medal \"%s\" without body ignored\n", name);
		return;
	}

	for (int i = 0; i < ccs.numRanks; i++) {
		if (Q_streq(name, ccs.ranks[i].name)) {
			Com_Printf("CL_ParseRanks: Rank with same name '%s' already loaded.\n", name);
			return;
		}
	}
	/* parse ranks */
	if (ccs.numRanks >= MAX_RANKS) {
		Com_Printf("CL_ParseRanks: Too many rank descriptions, '%s' ignored.\n", name);
		ccs.numRanks = MAX_RANKS;
		return;
	}

	rank = &ccs.ranks[ccs.numRanks++];
	OBJZERO(*rank);
	rank->id = cgi->PoolStrDup(name, cp_campaignPool, 0);
	rank->level = -1;

	do {
		/* get the name type */
		token = cgi->Com_EParse(text, errhead, name);
		if (!*text)
			break;
		if (*token == '}')
			break;

		if (cgi->Com_ParseBlockToken(name, text, rank, rankValues, cp_campaignPool, token)) {
			continue;
		} else if (Q_streq(token, "type")) {
			/* employeeType_t */
			token = cgi->Com_EParse(text, errhead, name);
			if (!*text)
				return;
			/* error check is performed in E_GetEmployeeType function */
			rank->type = E_GetEmployeeType(token);
		} else
			Com_Printf("CL_ParseRanks: unknown token \"%s\" ignored (medal/rank %s)\n", token, name);
	} while (*text);

	if (rank->image == nullptr || !strlen(rank->image))
		cgi->Com_Error(ERR_DROP, "CL_ParseRanks: image is missing for rank %s", rank->id);

	if (rank->name == nullptr || !strlen(rank->name))
		cgi->Com_Error(ERR_DROP, "CL_ParseRanks: name is missing for rank %s", rank->id);

	if (rank->shortname == nullptr || !strlen(rank->shortname))
		rank->shortname = rank->name;

	if (rank->level == -1)
		cgi->Com_Error(ERR_DROP, "CL_ParseRanks: level is missing for rank %s", rank->id);
}
Exemplo n.º 6
0
/**
 * @brief Create lights out of patches and entity lights
 * @sa LightWorld
 * @sa BuildPatch
 */
void BuildLights (void)
{
	int i;
	light_t* l;

	/* surfaces */
	for (i = 0; i < MAX_MAP_FACES; i++) {
		/* iterate subdivided patches */
		for(const patch_t* p = face_patches[i]; p; p = p->next) {
			if (VectorEmpty(p->light))
				continue;

			numlights[config.compile_for_day]++;
			l = Mem_AllocType(light_t);

			VectorCopy(p->origin, l->origin);

			l->next = lights[config.compile_for_day];
			lights[config.compile_for_day] = l;

			l->type = emit_surface;

			l->intensity = ColorNormalize(p->light, l->color);
			l->intensity *= p->area * config.surface_scale;
		}
	}

	/* entities (skip the world) */
	for (i = 1; i < num_entities; i++) {
		float intensity;
		const char* color;
		const char* target;
		const entity_t* e = &entities[i];
		const char* name = ValueForKey(e, "classname");
		if (!Q_strstart(name, "light"))
			continue;

		/* remove those lights that are only for the night version */
		if (config.compile_for_day) {
			const int spawnflags = atoi(ValueForKey(e, "spawnflags"));
			if (!(spawnflags & 1))	/* day */
				continue;
		}

		numlights[config.compile_for_day]++;
		l = Mem_AllocType(light_t);

		GetVectorForKey(e, "origin", l->origin);

		/* link in */
		l->next = lights[config.compile_for_day];
		lights[config.compile_for_day] = l;

		intensity = FloatForKey(e, "light");
		if (!intensity)
			intensity = 300.0;
		color = ValueForKey(e, "_color");
		if (color && color[0] != '\0'){
			if (sscanf(color, "%f %f %f", &l->color[0], &l->color[1], &l->color[2]) != 3)
				Sys_Error("Invalid _color entity property given: %s", color);
			ColorNormalize(l->color, l->color);
		} else
			VectorSet(l->color, 1.0, 1.0, 1.0);
		l->intensity = intensity * config.entity_scale;
		l->type = emit_point;

		target = ValueForKey(e, "target");
		if (target[0] != '\0' || Q_streq(name, "light_spot")) {
			l->type = emit_spotlight;
			l->stopdot = FloatForKey(e, "_cone");
			if (!l->stopdot)
				l->stopdot = 10;
			l->stopdot = cos(l->stopdot * torad);
			if (target[0] != '\0') {	/* point towards target */
				entity_t* e2 = FindTargetEntity(target);
				if (!e2)
					Com_Printf("WARNING: light at (%i %i %i) has missing target '%s' - e.g. create an info_null that has a 'targetname' set to '%s'\n",
						(int)l->origin[0], (int)l->origin[1], (int)l->origin[2], target, target);
				else {
					vec3_t dest;
					GetVectorForKey(e2, "origin", dest);
					VectorSubtract(dest, l->origin, l->normal);
					VectorNormalize(l->normal);
				}
			} else {	/* point down angle */
				const float angle = FloatForKey(e, "angle");
				if (angle == ANGLE_UP) {
					l->normal[0] = l->normal[1] = 0.0;
					l->normal[2] = 1.0;
				} else if (angle == ANGLE_DOWN) {
					l->normal[0] = l->normal[1] = 0.0;
					l->normal[2] = -1.0;
				} else {
					l->normal[2] = 0;
					l->normal[0] = cos(angle * torad);
					l->normal[1] = sin(angle * torad);
				}
			}
		}
	}

	/* handle worldspawn light settings */
	{
		const entity_t* e = &entities[0];
		const char* ambient, *light, *angles, *color;
		float f;
		int i;

		if (config.compile_for_day) {
			ambient = ValueForKey(e, "ambient_day");
			light = ValueForKey(e, "light_day");
			angles = ValueForKey(e, "angles_day");
			color = ValueForKey(e, "color_day");
		} else {
			ambient = ValueForKey(e, "ambient_night");
			light = ValueForKey(e, "light_night");
			angles = ValueForKey(e, "angles_night");
			color = ValueForKey(e, "color_night");
		}

		if (light[0] != '\0')
			sun_intensity = atoi(light);

		if (angles[0] != '\0') {
			VectorClear(sun_angles);
			if (sscanf(angles, "%f %f", &sun_angles[0], &sun_angles[1]) != 2)
				Sys_Error("wrong angles values given: '%s'", angles);
			AngleVectors(sun_angles, sun_normal, nullptr, nullptr);
		}

		if (color[0] != '\0') {
			GetVectorFromString(color, sun_color);
			ColorNormalize(sun_color, sun_color);
		}

		if (ambient[0] != '\0')
			GetVectorFromString(ambient, sun_ambient_color);

		/* optionally pull brightness from worldspawn */
		f = FloatForKey(e, "brightness");
		if (f > 0.0)
			config.brightness = f;

		/* saturation as well */
		f = FloatForKey(e, "saturation");
		if (f > 0.0)
			config.saturation = f;
		else
			Verb_Printf(VERB_EXTRA, "Invalid saturation setting (%f) in worldspawn found\n", f);

		f = FloatForKey(e, "contrast");
		if (f > 0.0)
			config.contrast = f;
		else
			Verb_Printf(VERB_EXTRA, "Invalid contrast setting (%f) in worldspawn found\n", f);

		/* lightmap resolution downscale (e.g. 4 = 1 << 4) */
		i = atoi(ValueForKey(e, "quant"));
		if (i >= 1 && i <= 6)
			config.lightquant = i;
		else
			Verb_Printf(VERB_EXTRA, "Invalid quant setting (%i) in worldspawn found\n", i);
	}

	Verb_Printf(VERB_EXTRA, "light settings:\n * intensity: %i\n * sun_angles: pitch %f yaw %f\n * sun_color: %f:%f:%f\n * sun_ambient_color: %f:%f:%f\n",
		sun_intensity, sun_angles[0], sun_angles[1], sun_color[0], sun_color[1], sun_color[2], sun_ambient_color[0], sun_ambient_color[1], sun_ambient_color[2]);
	Verb_Printf(VERB_NORMAL, "%i direct lights for %s lightmap\n", numlights[config.compile_for_day], (config.compile_for_day ? "day" : "night"));
}
Exemplo n.º 7
0
/**
 * @brief Initialize a node behaviour memory, after registration, and before unsing it.
 * @param behaviour Behaviour to initialize
 */
void UI_InitializeNodeBehaviour (uiBehaviour_t* behaviour)
{
	if (behaviour->isInitialized)
		return;

	/* everything inherits 'abstractnode' */
	if (behaviour->extends == NULL && !Q_streq(behaviour->name, "abstractnode")) {
		behaviour->extends = "abstractnode";
	}

	if (behaviour->extends) {
		int i = 0;

		/** TODO Find a way to remove that, if possible */
		behaviour->super = UI_GetNodeBehaviour(behaviour->extends);
		UI_InitializeNodeBehaviour(behaviour->super);

		while (qtrue) {
			const size_t pos = virtualFunctions[i];
			uintptr_t superFunc;
			uintptr_t func;
			if (pos == -1)
				break;

			/* cache super function if we don't overwrite it */
			superFunc = *(uintptr_t*)((byte*)behaviour->super + pos);
			func = *(uintptr_t*)((byte*)behaviour + pos);
			if (func == 0 && superFunc != 0)
				*(uintptr_t*)((byte*)behaviour + pos) = superFunc;

			i++;
		}
	}

	/* sort properties by alphabet */
	if (behaviour->localProperties) {
		int i = 0;
		const value_t* previous;
		const value_t** oldmemory = behaviour->localProperties;
		behaviour->localProperties = UI_AllocHunkMemory(sizeof(value_t*) * (behaviour->propertyCount+1), STRUCT_MEMORY_ALIGN, qfalse);
		if (behaviour->localProperties == NULL) {
			Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: UI memory hunk exceeded - increase the size");
		}

		previous = NULL;
		for (i = 0; i < behaviour->propertyCount; i++) {
			const value_t* better = NULL;
			const value_t** current;
			/* search the next element after previous */
			for (current = oldmemory; *current != NULL; current++) {
				if (previous != NULL && Q_strcasecmp(previous->string, (*current)->string) >= 0) {
					continue;
				}
				if (better == NULL || Q_strcasecmp(better->string, (*current)->string) >= 0) {
					better = *current;
				}
			}
			previous = better;
			behaviour->localProperties[i] = better;
		}
		behaviour->localProperties[behaviour->propertyCount] = NULL;
		Mem_Free(oldmemory);
	}

	/* property must not overwrite another property */
	if (behaviour->super && behaviour->localProperties) {
		const value_t** property = behaviour->localProperties;
		while (*property) {
			const value_t *p = UI_GetPropertyFromBehaviour(behaviour->super, (*property)->string);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			const uiBehaviour_t *b = UI_GetNodeBehaviour(current->string);
#endif
			if (p != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' overwrite another property", (*property)->string, behaviour->name);
#if 0	/**< @todo not possible at the moment, not sure its the right way */
			if (b != NULL)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' use the name of an existing node behaviour", (*property)->string, behaviour->name);
#endif
			property++;
		}
	}

	/* Sanity: A property must not be outside the node memory */
	if (behaviour->localProperties) {
		const int size = sizeof(uiNode_t) + behaviour->extraDataSize;
		const value_t** property = behaviour->localProperties;
		while (*property) {
			if ((*property)->type != V_UI_NODEMETHOD && (*property)->ofs + (*property)->size > size)
				Com_Error(ERR_FATAL, "UI_InitializeNodeBehaviour: property '%s' from node behaviour '%s' is outside the node memory. The C code need a fix.", (*property)->string, behaviour->name);
			property++;
		}
	}

	behaviour->isInitialized = qtrue;
}
Exemplo n.º 8
0
/**
 * @brief Copies an entry from the building description file into the list of building types.
 * @note Parses one "building" entry in the basemanagement.ufo file and writes
 * it into the next free entry in bmBuildings[0], which is the list of buildings
 * in the first base (building_t).
 * @param[in] name Unique script id of a building. This is parsed from "building xxx" -> id=xxx.
 * @param[in] text the whole following text that is part of the "building" item definition in .ufo.
 * @param[in] link Bool value that decides whether to link the tech pointer in or not
 * @sa CL_ParseScriptFirst (link is false here)
 * @sa CL_ParseScriptSecond (link it true here)
 */
void B_ParseBuildings (const char* name, const char** text, bool link)
{
	building_t* building;
	technology_t* techLink;
	const char* errhead = "B_ParseBuildings: unexpected end of file (names ";
	const char* token;

	/* get id list body */
	token = Com_Parse(text);
	if (!*text || *token != '{') {
		cgi->Com_Printf("B_ParseBuildings: building \"%s\" without body ignored\n", name);
		return;
	}

	if (ccs.numBuildingTemplates >= MAX_BUILDINGS)
		cgi->Com_Error(ERR_DROP, "B_ParseBuildings: too many buildings");

	if (!link) {
		for (int i = 0; i < ccs.numBuildingTemplates; i++) {
			if (Q_streq(ccs.buildingTemplates[i].id, name)) {
				cgi->Com_Printf("B_ParseBuildings: Second building with same name found (%s) - second ignored\n", name);
				return;
			}
		}

		/* new entry */
		building = &ccs.buildingTemplates[ccs.numBuildingTemplates];
		OBJZERO(*building);
		building->id = cgi->PoolStrDup(name, cp_campaignPool, 0);

		cgi->Com_DPrintf(DEBUG_CLIENT, "...found building %s\n", building->id);

		/* set standard values */
		building->tpl = building;	/* Self-link just in case ... this way we can check if it is a template or not. */
		building->idx = -1;			/* No entry in buildings list (yet). */
		building->base = nullptr;
		building->buildingType = MAX_BUILDING_TYPE;
		building->dependsBuilding = nullptr;
		building->maxCount = -1;	/* Default: no limit */
		building->size[0] = 1;
		building->size[1] = 1;

		ccs.numBuildingTemplates++;
		do {
			/* get the name type */
			token = cgi->Com_EParse(text, errhead, name);
			if (!*text)
				break;
			if (*token == '}')
				break;

			/* get values */
			if (Q_streq(token, "type")) {
				token = cgi->Com_EParse(text, errhead, name);
				if (!*text)
					return;

				building->buildingType = B_GetBuildingTypeByBuildingID(token);
				if (building->buildingType >= MAX_BUILDING_TYPE)
					cgi->Com_Printf("didn't find buildingType '%s'\n", token);
			} else {
				/* no linking yet */
				if (Q_streq(token, "depends")) {
					cgi->Com_EParse(text, errhead, name);
					if (!*text)
						return;
				} else {
					if (!cgi->Com_ParseBlockToken(name, text, building, valid_building_vars, cp_campaignPool, token))
						cgi->Com_Printf("B_ParseBuildings: unknown token \"%s\" ignored (building %s)\n", token, name);
				}
			}
		} while (*text);
		if (building->size[0] < 1 || building->size[1] < 1 || building->size[0] >= BASE_SIZE || building->size[1] >= BASE_SIZE) {
			cgi->Com_Printf("B_ParseBuildings: Invalid size for building %s (%i, %i)\n", building->id, (int)building->size[0], (int)building->size[1]);
			ccs.numBuildingTemplates--;
		}
	} else {
		building = B_GetBuildingTemplate(name);
		if (!building)
			cgi->Com_Error(ERR_DROP, "B_ParseBuildings: Could not find building with id %s\n", name);

		techLink = RS_GetTechByProvided(name);
		if (techLink)
			building->tech = techLink;

		do {
			/* get the name type */
			token = cgi->Com_EParse(text, errhead, name);
			if (!*text)
				break;
			if (*token == '}')
				break;
			/* get values */
			if (Q_streq(token, "depends")) {
				const building_t* dependsBuilding = B_GetBuildingTemplate(cgi->Com_EParse(text, errhead, name));
				if (!dependsBuilding)
					cgi->Com_Error(ERR_DROP, "Could not find building depend of %s\n", building->id);
				building->dependsBuilding = dependsBuilding;
				if (!*text)
					return;
			}
		} while (*text);
	}
}
Exemplo n.º 9
0
/*
======================
SV_Map

  the full syntax is:

  map [*]<map>$<startspot>+<nextserver>

command from the console or progs.
Map can also be a.cin, .pcx, or .dm2 file
Nextserver is used to allow a cinematic to play, then proceed to
another level:

	map tram.cin+jail_e3
======================
*/
void SV_Map (qboolean attractloop, const char *levelstring, qboolean loadgame)
{
	char	level[MAX_QPATH];
	char	*ch;
	int		l;
	char	spawnpoint[MAX_QPATH];

	strcpy(level, levelstring); // jit - copy level string before it gets modified by other commands (since it's a command argument)
	sv.loadgame = loadgame;
	sv.attractloop = attractloop;

	if (sv.state == ss_dead && !sv.loadgame)
		SV_InitGame();	// the game is just starting

	// if there is a + in the map, set nextserver to the remainder
	ch = strstr(level, "+");

	if (ch)
	{
		*ch = 0;
		Cvar_Set("nextserver", va("gamemap \"%s\"", ch + 1));
	}
	else
	{
		Cvar_Set("nextserver", "");
	}

	//ZOID special hack for end game screen in coop mode
	if (Cvar_VariableValue("coop") && Q_strcaseeq(level, "victory.pcx"))
		Cvar_Set("nextserver", "gamemap \"*base1\"");

	// if there is a $, use the remainder as a spawnpoint
	ch = strstr(level, "$");

	if (ch)
	{
		*ch = 0;
		strcpy(spawnpoint, ch + 1);
	}
	else
	{
		spawnpoint[0] = 0;
	}

	// skip the end-of-unit flag if necessary
	if (level[0] == '*')
		strcpy (level, level+1);

	l = strlen(level);

	if (l > 4 && Q_streq(level + l - 4, ".cin"))
	{
		SCR_BeginLoadingPlaque(NULL);			// for local system
		SV_BroadcastCommand("changing\n");
		SV_SpawnServer(level, spawnpoint, ss_cinematic, attractloop, loadgame);
	}
	else if (l > 4 && Q_streq(level + l - 4, ".dm2"))
	{
		SCR_BeginLoadingPlaque(NULL);			// for local system
		SV_BroadcastCommand("changing\n");
		SV_SpawnServer(level, spawnpoint, ss_demo, attractloop, loadgame);
	}
	else if (l > 4 && Q_streq(level + l - 4, ".pcx"))
	{
		SCR_BeginLoadingPlaque(NULL);			// for local system
		SV_BroadcastCommand("changing\n");
		SV_SpawnServer(level, spawnpoint, ss_pic, attractloop, loadgame);
	}
	else
	{
		char changing_cmd[1024];

		if (!dedicated->value)
			SCR_BeginLoadingPlaque(level);			// for local system

		Com_sprintf(changing_cmd, sizeof(changing_cmd), "changing \"%s\"\n", level);
		SV_BroadcastCommand(changing_cmd);
		SV_SendClientMessages();
		SV_SpawnServer(level, spawnpoint, ss_game, attractloop, loadgame);
		Cbuf_CopyToDefer();
	}

	SV_BroadcastCommand("reconnect\n");
}
Exemplo n.º 10
0
/**
 * @brief Sets a cvar values
 * Handles write protection and latched cvars as expected
 * @param[in] varName Which cvar
 * @param[in] value Set the cvar to the value specified by 'value'
 * @param[in] force Force the update of the cvar
 */
static cvar_t *Cvar_Set2 (const char *varName, const char *value, bool force)
{
	cvar_t *var;

	if (!value)
		return NULL;

	var = Cvar_FindVar(varName);
	/* create it */
	if (!var)
		return Cvar_Get(varName, value);

	if (var->flags & (CVAR_USERINFO | CVAR_SERVERINFO)) {
		if (!Cvar_InfoValidate(value)) {
			Com_Printf("invalid info cvar value '%s' of cvar '%s'\n", value, varName);
			return var;
		}
	}

	if (!force) {
		if (var->flags & CVAR_NOSET) {
			Com_Printf("%s is write protected.\n", varName);
			return var;
		}
#ifndef DEBUG
		if (var->flags & CVAR_DEVELOPER) {
			Com_Printf("%s is a developer cvar.\n", varName);
			return var;
		}
#endif

		if (var->flags & CVAR_LATCH) {
			if (var->latchedString) {
				if (Q_streq(value, var->latchedString))
					return var;
				Mem_Free(var->latchedString);
				var->latchedString = NULL;
			} else {
				if (Q_streq(value, var->string))
					return var;
			}

			/* if we are running a server */
			if (Com_ServerState()) {
				Com_Printf("%s will be changed for next game.\n", varName);
				var->latchedString = Mem_PoolStrDup(value, com_cvarSysPool, 0);
			} else {
				Mem_Free(var->oldString);
				var->oldString = var->string;
				var->string = Mem_PoolStrDup(value, com_cvarSysPool, 0);
				var->value = atof(var->string);
				var->integer = atoi(var->string);
			}

			if (var->check && var->check(var))
				Com_Printf("Invalid value for cvar %s\n", varName);

			return var;
		}
	} else {
		Mem_Free(var->latchedString);
		var->latchedString = NULL;
	}

	if (Q_streq(value, var->string))
		return var;				/* not changed */

	if (var->flags & CVAR_R_MASK)
		Com_SetRenderModified(true);

	Mem_Free(var->oldString);		/* free the old value string */
	var->oldString = var->string;
	var->modified = true;

	if (var->flags & CVAR_USERINFO)
		Com_SetUserinfoModified(true);	/* transmit at next opportunity */

	var->string = Mem_PoolStrDup(value, com_cvarSysPool, 0);
	var->value = atof(var->string);
	var->integer = atoi(var->string);

	if (var->check && var->check(var)) {
		Com_Printf("Invalid value for cvar %s\n", varName);
		return var;
	}

	Cvar_ExecuteChangeListener(var);

	return var;
}
Exemplo n.º 11
0
static inline bool IsInvalidEntityToken (const char* token)
{
	return Q_streq(token, "}") || Q_streq(token, "{");
}
Exemplo n.º 12
0
/**
 * @brief Called at client startup
 * @note not called for dedicated servers
 * parses all *.ufos that are needed for single- and multiplayer
 * @sa Com_ParseScripts
 * @sa CL_ParseScriptSecond
 * @sa CL_ParseScriptFirst
 * @note Nothing here should depends on items, equipments, actors and all other
 * entities that are parsed in Com_ParseScripts (because maybe items are not parsed
 * but e.g. techs would need those parsed items - thus we have to parse e.g. techs
 * at a later stage)
 * @note This data is persistent until you shutdown the game
 * @return True if the parsing function succeeded.
 */
bool CL_ParseClientData (const char* type, const char* name, const char** text)
{
#ifndef COMPILE_UNITTESTS
	static int progressCurrent = 0;

	progressCurrent++;
	if (progressCurrent % 10 == 0)
		SCR_DrawLoadingScreen(false, std::min(progressCurrent * 30 / 1500, 30));
#endif

	if (Q_streq(type, "window"))
		return UI_ParseWindow(type, name, text);
	else if (Q_streq(type, "component"))
		return UI_ParseComponent(type, name, text);
	else if (Q_streq(type, "particle"))
		CL_ParseParticle(name, text);
	else if (Q_streq(type, "language"))
		CL_ParseLanguages(name, text);
	else if (Q_streq(type, "font"))
		return UI_ParseFont(name, text);
	else if (Q_streq(type, "tutorial"))
		TUT_ParseTutorials(name, text);
	else if (Q_streq(type, "menu_model"))
		return UI_ParseUIModel(name, text);
	else if (Q_streq(type, "sprite"))
		return UI_ParseSprite(name, text);
	else if (Q_streq(type, "sequence"))
		CL_ParseSequence(name, text);
	else if (Q_streq(type, "music"))
		M_ParseMusic(name, text);
	else if (Q_streq(type, "actorskin"))
		CL_ParseActorSkin(name, text);
	else if (Q_streq(type, "cgame"))
		GAME_ParseModes(name, text);
	else if (Q_streq(type, "tip"))
		CL_ParseTipOfTheDay(name, text);
	return true;
}
Exemplo n.º 13
0
/**
 * @brief Responses to broadcasts, etc
 * @sa CL_ReadPackets
 * @sa CL_Frame
 * @sa SVC_DirectConnect
 * @param[in,out] msg The client stream message buffer to read from
 */
static void CL_ConnectionlessPacket (dbuffer* msg)
{
	char s[512];
	NET_ReadStringLine(msg, s, sizeof(s));

	Cmd_TokenizeString(s, false);

	const char* c = Cmd_Argv(0);
	Com_DPrintf(DEBUG_CLIENT, "server OOB: %s (%s)\n", c, Cmd_Args());

	/* server connection */
	if (Q_streq(c, CL_CMD_CLIENT_CONNECT)) {
		int i;
		for (i = 1; i < Cmd_Argc(); i++) {
			if (char const* const p = Q_strstart(Cmd_Argv(i), "dlserver=")) {
				Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "ufo://%s", cls.servername);
				CL_SetHTTPServer(p);
				if (cls.downloadServer[0])
					Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer);
			}
		}
		if (cls.state == ca_connected) {
			Com_Printf("Dup connect received. Ignored.\n");
			return;
		}
		dbuffer buf(5);
		NET_WriteByte(&buf, clc_stringcmd);
		NET_WriteString(&buf, NET_STATE_NEW "\n");
		NET_WriteMsg(cls.netStream, buf);
		GAME_InitMissionBriefing(_("Loading"));
		return;
	}

	/* remote command from gui front end */
	if (Q_streq(c, CL_CMD_COMMAND)) {
		if (!NET_StreamIsLoopback(cls.netStream)) {
			Com_Printf("Command packet from remote host. Ignored.\n");
			return;
		} else {
			char str[512];
			NET_ReadString(msg, str, sizeof(str));
			Cbuf_AddText("%s\n", str);
		}
		return;
	}

	/* ping from server */
	if (Q_streq(c, CL_CMD_PING)) {
		NET_OOB_Printf(cls.netStream, SV_CMD_ACK);
		return;
	}

	/* echo request from server */
	if (Q_streq(c, CL_CMD_ECHO)) {
		NET_OOB_Printf(cls.netStream, "%s", Cmd_Argv(1));
		return;
	}

	/* print */
	if (Q_streq(c, SV_CMD_PRINT)) {
		NET_ReadString(msg, popupText, sizeof(popupText));
		/* special reject messages needs proper handling */
		if (strstr(popupText, REJ_PASSWORD_REQUIRED_OR_INCORRECT)) {
			UI_PushWindow("serverpassword");
			if (Q_strvalid(Cvar_GetString("password"))) {
				Cvar_Set("password", "");
				CL_Drop();
				UI_Popup(_("Connection failure"), _("The password you specified was wrong."));
			} else {
				CL_Drop();
				UI_Popup(_("Connection failure"), _("This server requires a password."));
			}
		} else if (strstr(popupText, REJ_SERVER_FULL)) {
			CL_Drop();
			UI_Popup(_("Connection failure"), _("This server is full."));
		} else if (strstr(popupText, REJ_BANNED)) {
			CL_Drop();
			UI_Popup(_("Connection failure"), _("You are banned on this server."));
		} else if (strstr(popupText, REJ_GAME_ALREADY_STARTED)) {
			CL_Drop();
			UI_Popup(_("Connection failure"), _("The game has already started."));
		} else if (strstr(popupText, REJ_SERVER_VERSION_MISMATCH)) {
			CL_Drop();
			UI_Popup(_("Connection failure"), _("The server is running a different version of the game."));
		} else if (strstr(popupText, BAD_RCON_PASSWORD)) {
			Cvar_Set("rcon_password", "");
			UI_Popup(_("Bad rcon password"), _("The rcon password you specified was wrong."));
		} else if (strstr(popupText, REJ_CONNECTION_REFUSED)) {
			CL_Drop();
			UI_Popup(_("Connection failure"), _("The server refused the connection."));
		} else if (Q_strvalid(popupText)) {
			UI_Popup(_("Notice"), _(popupText));
		}
		return;
	}

	if (!GAME_HandleServerCommand(c, msg))
		Com_Printf("Unknown command received \"%s\"\n", c);
}
Exemplo n.º 14
0
void uiOptionTreeNode::draw (uiNode_t* node)
{
	uiNode_t* option;
	const char* ref;
	const char* font;
	vec2_t pos;
	int fontHeight;
	int currentY;
	int currentDecY = 0;
	const float* textColor;
	int count = 0;
	uiOptionIterator_t iterator;

	if (!systemExpand)
		systemExpand = UI_GetSpriteByName("icons/system_expand");
	if (!systemCollapse)
		systemCollapse = UI_GetSpriteByName("icons/system_collapse");

	ref = UI_AbstractOptionGetCurrentValue(node);
	if (ref == nullptr)
		return;

	UI_GetNodeAbsPos(node, pos);

	if (EXTRADATA(node).background) {
		UI_DrawSpriteInBox(false, EXTRADATA(node).background, SPRITE_STATUS_NORMAL, pos[0], pos[1], node->box.size[0], node->box.size[1]);
	}

	font = UI_GetFontFromNode(node);
	fontHeight = EXTRADATA(node).lineHeight;
	currentY = pos[1] + node->padding;
	if (fontHeight == 0)
		fontHeight = UI_FontGetHeight(font);
	else {
		const int height = UI_FontGetHeight(font);
		currentDecY = (fontHeight - height) / 2;
	}

	/* skip option over current position */
	option = UI_OptionTreeNodeGetFirstOption(node);
	UI_OptionTreeNodeUpdateScroll(node);
	option = UI_InitOptionIteratorAtIndex(EXTRADATA(node).scrollY.viewPos, option, &iterator);

	/* draw all available options for this selectbox */
	for (; option; option = UI_OptionIteratorNextOption(&iterator)) {
		int decX;

		/* outside the node */
		if (currentY + fontHeight > pos[1] + node->box.size[1] - node->padding) {
			count++;
			break;
		}

		/* draw the hover effect */
		if (OPTIONEXTRADATA(option).hovered)
			UI_DrawFill(pos[0] + node->padding, currentY, node->box.size[0] - node->padding - node->padding, fontHeight, node->color);

		/* text color */
		if (Q_streq(OPTIONEXTRADATA(option).value, ref)) {
			textColor = node->selectedColor;
		} else if (node->disabled || option->disabled) {
			textColor = node->disabledColor;
		} else if (option->color[3] == 0.0f) {
			textColor = node->color;
		} else {
			textColor = option->color;
		}

		/* print the option label */
		decX = pos[0] + node->padding + iterator.depthPos * DEPTH_WIDTH;

		R_Color(nullptr);
		if (option->firstChild) {
			uiSprite_t* icon = OPTIONEXTRADATA(option).collapsed ? systemExpand : systemCollapse;
			UI_DrawSpriteInBox(OPTIONEXTRADATA(option).flipIcon, icon, SPRITE_STATUS_NORMAL, decX, currentY, icon->size[0], fontHeight);
		}

		decX += COLLAPSEBUTTON_WIDTH;

		if (OPTIONEXTRADATA(option).icon) {
			uiSpriteStatus_t iconStatus = SPRITE_STATUS_NORMAL;
			if (option->disabled)
				iconStatus = SPRITE_STATUS_DISABLED;
			UI_DrawSpriteInBox(OPTIONEXTRADATA(option).flipIcon, OPTIONEXTRADATA(option).icon, iconStatus, decX, currentY,
					OPTIONEXTRADATA(option).icon->size[0], fontHeight);
			decX += OPTIONEXTRADATA(option).icon->size[0] + fontHeight / 4;
		}

		const char* label = CL_Translate(OPTIONEXTRADATA(option).label);

		R_Color(textColor);
		UI_DrawString(font, ALIGN_UL, decX, currentY + currentDecY,
			pos[0], node->box.size[0] - node->padding - node->padding,
			0, label, 0, 0, nullptr, false, LONGLINES_PRETTYCHOP);

		/* next entries' position */
		currentY += fontHeight;
		count++;
	}
	R_Color(nullptr);
}
Exemplo n.º 15
0
bool SCP_Load (xmlNode_t *parent)
{
	xmlNode_t *node;
	xmlNode_t *snode;

	node = cgi->XML_GetNode(parent, SAVE_STATICCAMPAIGN);
	if (!node) {
		return false;
	}

	SCP_Parse();

	/* read static campaign data */
	for (snode = cgi->XML_GetNode(node, SAVE_STATICCAMPAIGN_STAGE); snode;
			snode = cgi->XML_GetNextNode(snode, node, SAVE_STATICCAMPAIGN_STAGE)) {
		xmlNode_t *stateNode;
		const char *id = cgi->XML_GetString(snode, SAVE_STATICCAMPAIGN_STAGENAME);
		stageState_t *state = SCP_CampaignActivateStage(id);
		if (!state) {
			Com_Printf("......error: unable to load campaign, unknown stage '%s'\n", id);
			return false;
		}

		cgi->XML_GetDate(snode, SAVE_STATICCAMPAIGN_STAGEDATE, &state->start.day, &state->start.sec);
		int num = 0;
		for (stateNode = cgi->XML_GetNode(snode, SAVE_STATICCAMPAIGN_SETSTATE); stateNode;
				stateNode = cgi->XML_GetNextNode(stateNode, snode, SAVE_STATICCAMPAIGN_SETSTATE)) {
			num++;
			setState_t *set;
			int j;
			const char *name = cgi->XML_GetString(stateNode, SAVE_STATICCAMPAIGN_SETSTATENAME);
			for (j = 0, set = &scd->set[state->def->first]; j < state->def->num; j++, set++)
				if (Q_streq(name, set->def->name))
					break;
			if (j >= state->def->num) {
				Com_Printf("......error: Set '%s' not found (%i/%i)\n", name, num, state->def->num);
				return false;
			}

			set->active = cgi->XML_GetBool(stateNode, SAVE_STATICCAMPAIGN_SETSTATEACTIVE, false);
			set->num = cgi->XML_GetInt(stateNode, SAVE_STATICCAMPAIGN_SETSTATENUM, 0);
			set->done = cgi->XML_GetInt(stateNode, SAVE_STATICCAMPAIGN_SETSTATEDONE, 0);

			cgi->XML_GetDate(stateNode, SAVE_STATICCAMPAIGN_SETSTATESTARTDATE, &set->start.day, &set->start.sec);
			cgi->XML_GetDate(stateNode, SAVE_STATICCAMPAIGN_SETSTATEEVENTDATE, &set->event.day, &set->event.sec);
		}

		if (num != state->def->num)
			Com_Printf("......warning: Different number of sets: savegame: %i, scripts: %i\n", num, state->def->num);
	}

	for (snode = cgi->XML_GetNode(node, SAVE_STATICCAMPAIGN_ACTIVEMISSION); snode;
			snode = cgi->XML_GetNextNode(snode, node, SAVE_STATICCAMPAIGN_ACTIVEMISSION)) {
		int j;
		const char *name = cgi->XML_GetString(snode, SAVE_STATICCAMPAIGN_STAGESETNAME);
		actMis_t *mis = &scd->activeMissions[scd->numActiveMissions++];
		mis->def = NULL;
		mis->cause = NULL;

		for (j = 0; j < scd->numStageSets; j++)
			if (Q_streq(name, scd->stageSets[j].name)) {
				mis->cause = &scd->set[j];
				break;
			}
		if (j >= scd->numStageSets) {
			Com_Printf("......error: Stage set '%s' not found\n", name);
			return false;
		}

		/* get mission definition */
		name = cgi->XML_GetString(snode, SAVE_STATICCAMPAIGN_MISSIONNAME);
		for (j = 0; j < scd->numMissions; j++)
			if (Q_streq(name, scd->missions[j].id)) {
				mis->def = &scd->missions[j];
				break;
			}

		/* ignore incomplete info */
		if (!mis->cause || !mis->def) {
			Com_Printf("......error: Incomplete mission info for mission %s\n", name);
			return false;
		}

		mis->def->count = cgi->XML_GetInt(snode, SAVE_STATICCAMPAIGN_MISSIONCOUNT, 0);

		name = cgi->XML_GetString(snode, SAVE_STATICCAMPAIGN_MISSIONID);
		mis->mission = CP_GetMissionByIDSilent(name);
		if (!mis->mission) {
			Com_Printf("......error: Could not find mission for %s\n", name);
			return false;
		}

		/* read time */
		cgi->XML_GetDate(snode, SAVE_STATICCAMPAIGN_MISSIONEXPIREDATE, &mis->expire.day, &mis->expire.sec);
	}

	return true;
}
Exemplo n.º 16
0
/*
===========
Joy_AdvancedUpdate_f
===========
*/
void Joy_AdvancedUpdate_f (void)
{

	// called once by IN_ReadJoystick and by user whenever an update is needed
	// cvars are now available
	int	i;
	DWORD dwTemp;

	// initialize all the maps
	for (i = 0; i < JOY_MAX_AXES; i++)
	{
		dwAxisMap[i] = AxisNada;
		dwControlMap[i] = JOY_ABSOLUTE_AXIS;
		pdwRawValue[i] = RawValuePointer(i);
	}

	if ( joy_advanced->value == 0.0)
	{
		// default joystick initialization
		// 2 axes only with joystick control
		dwAxisMap[JOY_AXIS_X] = AxisTurn;
		// dwControlMap[JOY_AXIS_X] = JOY_ABSOLUTE_AXIS;
		dwAxisMap[JOY_AXIS_Y] = AxisForward;
		// dwControlMap[JOY_AXIS_Y] = JOY_ABSOLUTE_AXIS;
	}
	else
	{
		if (!Q_streq(joy_name->string, "joystick"))
		{
			// notify user of advanced controller
			Com_Printf ("\n%s configured.\n\n", joy_name->string);
		}

		// advanced initialization here
		// data supplied by user via joy_axisn cvars
		dwTemp = (DWORD) joy_advaxisx->value;
		dwAxisMap[JOY_AXIS_X] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_X] = dwTemp & JOY_RELATIVE_AXIS;
		dwTemp = (DWORD) joy_advaxisy->value;
		dwAxisMap[JOY_AXIS_Y] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_Y] = dwTemp & JOY_RELATIVE_AXIS;
		dwTemp = (DWORD) joy_advaxisz->value;
		dwAxisMap[JOY_AXIS_Z] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_Z] = dwTemp & JOY_RELATIVE_AXIS;
		dwTemp = (DWORD) joy_advaxisr->value;
		dwAxisMap[JOY_AXIS_R] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_R] = dwTemp & JOY_RELATIVE_AXIS;
		dwTemp = (DWORD) joy_advaxisu->value;
		dwAxisMap[JOY_AXIS_U] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_U] = dwTemp & JOY_RELATIVE_AXIS;
		dwTemp = (DWORD) joy_advaxisv->value;
		dwAxisMap[JOY_AXIS_V] = dwTemp & 0x0000000f;
		dwControlMap[JOY_AXIS_V] = dwTemp & JOY_RELATIVE_AXIS;
	}

	// compute the axes to collect from DirectInput
	joy_flags = JOY_RETURNCENTERED | JOY_RETURNBUTTONS | JOY_RETURNPOV;
	for (i = 0; i < JOY_MAX_AXES; i++)
	{
		if (dwAxisMap[i] != AxisNada)
		{
			joy_flags |= dwAxisFlags[i];
		}
	}
}
Exemplo n.º 17
0
static void UI_SelectBoxNodeDraw (uiNode_t *node)
{
	uiNode_t* option;
	int selBoxX, selBoxY;
	const char *ref;
	const char *font;
	vec2_t nodepos;
	const char* imageName;
	const image_t *image;
	static vec4_t invisColor = {1.0, 1.0, 1.0, 0.7};

	ref = UI_AbstractOptionGetCurrentValue(node);
	if (ref == NULL)
		return;

	UI_GetNodeAbsPos(node, nodepos);
	imageName = UI_GetReferenceString(node, node->image);
	if (!imageName)
		imageName = "ui/selectbox";

	image = UI_LoadImage(imageName);

	font = UI_GetFontFromNode(node);
	selBoxX = nodepos[0] + SELECTBOX_SIDE_WIDTH;
	selBoxY = nodepos[1] + SELECTBOX_SPACER;

	/* left border */
	UI_DrawNormImage(qfalse, nodepos[0], nodepos[1], SELECTBOX_SIDE_WIDTH, node->size[1],
		SELECTBOX_SIDE_WIDTH, SELECTBOX_DEFAULT_HEIGHT, 0.0f, 0.0f, image);
	/* stretched middle bar */
	UI_DrawNormImage(qfalse, nodepos[0] + SELECTBOX_SIDE_WIDTH, nodepos[1], node->size[0]-SELECTBOX_SIDE_WIDTH-SELECTBOX_RIGHT_WIDTH, node->size[1],
		12.0f, SELECTBOX_DEFAULT_HEIGHT, 7.0f, 0.0f, image);
	/* right border (arrow) */
	UI_DrawNormImage(qfalse, nodepos[0] + node->size[0] - SELECTBOX_RIGHT_WIDTH, nodepos[1], SELECTBOX_DEFAULT_HEIGHT, node->size[1],
		12.0f + SELECTBOX_RIGHT_WIDTH, SELECTBOX_DEFAULT_HEIGHT, 12.0f, 0.0f, image);

	/* draw the label for the current selected option */
	for (option = UI_AbstractOptionGetFirstOption(node); option; option = option->next) {
		const char *label;

		if (!Q_streq(OPTIONEXTRADATA(option).value, ref))
			continue;

		if (option->invis)
			R_Color(invisColor);

		label = OPTIONEXTRADATA(option).label;
		if (label[0] == '_')
			label = _(label + 1);

		UI_DrawString(font, ALIGN_UL, selBoxX, selBoxY,
			selBoxX, node->size[0] - 4,
			0, label, 0, 0, NULL, qfalse, LONGLINES_PRETTYCHOP);

		R_Color(NULL);
		break;
	}

	/* must we draw the drop-down list */
	if (UI_GetMouseCapture() == node) {
		UI_CaptureDrawOver(node);
	}
}
Exemplo n.º 18
0
/**
 * @note Think functions are only executed when the match is running
 * or in other word, the game has started
 */
void G_MissionThink (edict_t *self)
{
	edict_t *chain = self->groupMaster;
	edict_t *ent;
	int team;

	if (!G_MatchIsRunning())
		return;

	/* when every player has joined the match - spawn the mission target
	 * particle (if given) to mark the trigger */
	if (self->particle) {
		G_SpawnParticle(self->origin, self->spawnflags, self->particle);

		/* This is automatically freed on map shutdown */
		self->particle = NULL;
	}

	if (!chain)
		chain = self;
	while (chain) {
		if (chain->type == ET_MISSION) {
			if (chain->item) {
				const invList_t *ic;
				G_GetFloorItems(chain);
				ic = FLOOR(chain);
				if (!ic) {
					/* reset the counter if there is no item */
					chain->count = 0;
					return;
				}
				for (; ic; ic = ic->next) {
					const objDef_t *od = ic->item.t;
					assert(od);
					/* not the item we are looking for */
					if (Q_streq(od->id, chain->item))
						break;
				}
				if (!ic) {
					/* reset the counter if it's not the searched item */
					chain->count = 0;
					return;
				}
			}
			if (chain->time) {
				/* not every edict in the group chain has
				 * been occupied long enough */
				if (!chain->count || level.actualRound - chain->count < chain->time)
					return;
			}
			/* not destroyed yet */
			if ((chain->flags & FL_DESTROYABLE) && chain->HP)
				return;
		}
		chain = chain->groupChain;
	}

	if (self->use)
		self->use(self, NULL);

	/* store team before the edict is released */
	team = self->team;

	chain = self->groupMaster;
	if (!chain)
		chain = self;
	while (chain) {
		if (chain->item != NULL) {
			edict_t *item = G_GetEdictFromPos(chain->pos, ET_ITEM);
			if (item != NULL) {
				if (!G_InventoryRemoveItemByID(chain->item, item, gi.csi->idFloor)) {
					Com_Printf("Could not remove item '%s' from floor edict %i\n",
							chain->item, item->number);
				} else {
					G_AppearPerishEvent(G_VisToPM(item->visflags), false, item, NULL);
				}
			}
		}
		if (chain->particle != NULL) {
			/** @todo not yet working - particle stays active */
			edict_t *particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE);
			if (particle != NULL) {
				G_AppearPerishEvent(PM_ALL, false, particle, NULL);
				G_FreeEdict(particle);
			}
		}

		ent = chain->groupChain;
		/* free the trigger */
		if (chain->child)
			G_FreeEdict(chain->child);
		/* free the group chain */
		G_FreeEdict(chain);
		chain = ent;
	}
	self = NULL;

	/* still active mission edicts left */
	ent = NULL;
	while ((ent = G_EdictsGetNextInUse(ent)))
		if (ent->type == ET_MISSION && ent->team == team)
			return;

	G_MatchEndTrigger(team, 10);
}
Exemplo n.º 19
0
/**
 * @brief Returns what the actor can see.
 */
static int AIL_see (lua_State *L)
{
	int vision, team;
	int i, j, k, n, cur;
	Edict *check = nullptr;
	aiActor_t target;
	Edict *sorted[MAX_EDICTS], *unsorted[MAX_EDICTS];
	float distLookup[MAX_EDICTS];

	/* Defaults. */
	team = TEAM_ALL;
	vision = 0;

	/* Handle parameters. */
	if ((lua_gettop(L) > 0)) {
		/* Get what to "see" with. */
		if (lua_isstring(L, 1)) {
			const char *s = lua_tostring(L, 1);
			/** @todo Properly implement at edict level, get rid of magic numbers.
			 * These are only "placeholders". */
			if (Q_streq(s, "all"))
				vision = 0;
			else if (Q_streq(s, "sight"))
				vision = 1;
			else if (Q_streq(s, "psionic"))
				vision = 2;
			else if (Q_streq(s, "infrared"))
				vision = 3;
			else
				AIL_invalidparameter(1);
		} else
			AIL_invalidparameter(1);

		/* We now check for different teams. */
		if ((lua_gettop(L) > 1)) {
			if (lua_isstring(L, 2)) {
				const char *s = lua_tostring(L, 2);
				team = AIL_toTeamInt(s);
			} else
				AIL_invalidparameter(2);
		}
	}

	n = 0;
	/* Get visible things. */
	while ((check = G_EdictsGetNextLivingActor(check))) {
		if (AIL_ent == check)
			continue;
		if (vision == 0 && (team == TEAM_ALL || check->team == team) /* Check for team match if needed. */
		 && G_Vis(AIL_ent->team, AIL_ent, check, VT_NOFRUSTUM)) {
			distLookup[n] = VectorDistSqr(AIL_ent->pos, check->pos);
			unsorted[n++] = check;
		}
	}

	/* Sort by distance - nearest first. */
	for (i = 0; i < n; i++) { /* Until we fill sorted */
		cur = -1;
		for (j = 0; j < n; j++) { /* Check for closest */
			/* Is shorter then current minimum? */
			if (cur < 0 || distLookup[j] < distLookup[cur]) {
				/* Check if not already in sorted. */
				for (k = 0; k < i; k++)
					if (sorted[k] == unsorted[j])
						break;

				/* Not already sorted and is new minimum. */
				if (k == i)
					cur = j;
			}
		}

		sorted[i] = unsorted[cur];
	}

	/* Now save it in a Lua table. */
	lua_newtable(L);
	for (i = 0; i < n; i++) {
		lua_pushnumber(L, i + 1); /* index, starts with 1 */
		target.ent = sorted[i];
		lua_pushactor(L, &target); /* value */
		lua_rawset(L, -3); /* store the value in the table */
	}
	return 1; /* Returns the table of actors. */
}
Exemplo n.º 20
0
/**
 * @brief Checks if the given object/item matched the given filter type.
 * @param[in] obj A pointer to an objDef_t item.
 * @param[in] filterType Filter type to check against.
 * @return @c true if obj is in filterType
 */
bool INV_ItemMatchesFilter (const objDef_t *obj, const itemFilterTypes_t filterType)
{
	int i;

	if (!obj)
		return false;

	switch (filterType) {
	case FILTER_S_PRIMARY:
		if (obj->isPrimary && !obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_SECONDARY:
		if (obj->isSecondary && !obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_HEAVY:
		if (obj->isHeavy)
			return true;

		/* Check if one of the items that uses this ammo matches this filter type. */
		for (i = 0; i < obj->numWeapons; i++) {
			const objDef_t *weapon = obj->weapons[i];
			if (weapon && weapon != obj && INV_ItemMatchesFilter(weapon, filterType))
				return true;
		}
		break;

	case FILTER_S_ARMOUR:
		return obj->isArmour();

	case FILTER_S_MISC:
		return obj->isMisc;

	case FILTER_CRAFTITEM:
		/** @todo Should we handle FILTER_AIRCRAFT here as well? */
		return obj->isCraftItem();

	case FILTER_UGVITEM:
		return obj->isUGVitem;

	case FILTER_DUMMY:
		return obj->isDummy;

	case FILTER_AIRCRAFT:
		return Q_streq(obj->type, "aircraft");

	case FILTER_DISASSEMBLY:
		/** @todo I guess we should search for components matching this item here. */
		break;

	case MAX_SOLDIER_FILTERTYPES:
	case MAX_FILTERTYPES:
	case FILTER_ENSURE_32BIT:
		Com_Printf("INV_ItemMatchesFilter: Unknown filter type for items: %i\n", filterType);
		break;
	}

	/* The given filter type is unknown. */
	return false;
}
Exemplo n.º 21
0
/**
 * @return Alien mission category
 * @sa interestCategory_t
 */
static interestCategory_t CP_GetAlienMissionTypeByID (const char *type)
{
	if (Q_streq(type, "recon"))
		return INTERESTCATEGORY_RECON;
	else if (Q_streq(type, "terror"))
		return INTERESTCATEGORY_TERROR_ATTACK;
	else if (Q_streq(type, "baseattack"))
		return INTERESTCATEGORY_BASE_ATTACK;
	else if (Q_streq(type, "building"))
		return INTERESTCATEGORY_BUILDING;
	else if (Q_streq(type, "supply"))
		return INTERESTCATEGORY_SUPPLY;
	else if (Q_streq(type, "xvi"))
		return INTERESTCATEGORY_XVI;
	else if (Q_streq(type, "intercept"))
		return INTERESTCATEGORY_INTERCEPT;
	else if (Q_streq(type, "harvest"))
		return INTERESTCATEGORY_HARVEST;
	else if (Q_streq(type, "alienbase"))
		return INTERESTCATEGORY_ALIENBASE;
	else if (Q_streq(type, "ufocarrier"))
		return INTERESTCATEGORY_UFOCARRIER;
	else if (Q_streq(type, "rescue"))
		return INTERESTCATEGORY_RESCUE;
	else {
		Com_Printf("CP_GetAlienMissionTypeByID: unknown alien mission category '%s'\n", type);
		return INTERESTCATEGORY_NONE;
	}
}
Exemplo n.º 22
0
/**
 * @brief Draw a model using UI model definition
 */
static void UI_DrawModelNodeWithUIModel (uiNode_t *node, const char *source, modelInfo_t *mi, uiModel_t *model)
{
	qboolean autoScaleComputed = qfalse;
	vec3_t autoScale;
	vec3_t autoCenter;

	while (model) {
		/* no animation */
		mi->frame = 0;
		mi->oldframe = 0;
		mi->backlerp = 0;

		assert(model->model);
		mi->model = R_FindModel(model->model);
		if (!mi->model) {
			model = model->next;
			continue;
		}

		mi->skin = model->skin;
		mi->name = model->model;

		/* set mi pointers to model */
		mi->origin = model->origin;
		mi->angles = model->angles;
		mi->center = model->center;
		mi->color = model->color;
		mi->scale = model->scale;

		if (model->tag && model->parent) {
			/* tag and parent defined */
			uiModel_t *parentModel;
			modelInfo_t pmi;
			vec3_t pmiorigin;
			animState_t *as;
			/* place this model part on an already existing model tag */
			parentModel = UI_GetUIModel(model->parent);
			if (!parentModel) {
				Com_Printf("UI Model: Could not get the model '%s'\n", model->parent);
				break;
			}
			pmi.model = R_FindModel(parentModel->model);
			if (!pmi.model) {
				Com_Printf("UI Model: Could not get the model '%s'\n", parentModel->model);
				break;
			}

			pmi.name = parentModel->model;

			pmi.origin = pmiorigin;
			pmi.angles = parentModel->angles;
			pmi.scale = parentModel->scale;
			pmi.center = parentModel->center;
			pmi.color = parentModel->color;

			pmi.origin[0] = parentModel->origin[0] + mi->origin[0];
			pmi.origin[1] = parentModel->origin[1] + mi->origin[1];
			pmi.origin[2] = parentModel->origin[2];
			/* don't count window offset twice for tagged models */
			mi->origin[0] -= node->root->pos[0];
			mi->origin[1] -= node->root->pos[1];

			/* autoscale? */
			if (EXTRADATA(node).autoscale) {
				if (!autoScaleComputed)
					Sys_Error("Wrong order of model nodes - the tag and parent model node must be after the base model node");
				pmi.scale = autoScale;
				pmi.center = autoCenter;
			}

			as = &parentModel->animState;
			if (!as)
				Com_Error(ERR_DROP, "Model %s should have animState_t for animation %s - but doesn't\n", pmi.name, parentModel->anim);
			pmi.frame = as->frame;
			pmi.oldframe = as->oldframe;
			pmi.backlerp = as->backlerp;

			R_DrawModelDirect(mi, &pmi, model->tag);
		} else {
			/* no tag and no parent means - base model or single model */
			const char *ref;
			UI_InitModelInfoView(node, mi, model);
			Vector4Copy(node->color, mi->color);

			/* compute the scale and center for the first model.
			 * it think its the bigger of composite models.
			 * All next elements use the same result
			 */
			if (EXTRADATA(node).autoscale) {
				if (!autoScaleComputed) {
					vec2_t size;
					size[0] = node->size[0] - node->padding;
					size[1] = node->size[1] - node->padding;
					R_ModelAutoScale(size, mi, autoScale, autoCenter);
					autoScaleComputed = qtrue;
				} else {
					mi->scale = autoScale;
					mi->center = autoCenter;
				}
			}

			/* get the animation given by node properties */
			if (EXTRADATA(node).animation && *EXTRADATA(node).animation) {
				ref = UI_GetReferenceString(node, EXTRADATA(node).animation);
			/* otherwise use the standard animation from UI model definition */
			} else
				ref = model->anim;

			/* only base models have animations */
			if (ref && *ref) {
				animState_t *as = &model->animState;
				const char *anim = R_AnimGetName(as, mi->model);
				/* initial animation or animation change */
				if (anim == NULL || !Q_streq(anim, ref))
					R_AnimChange(as, mi->model, ref);
				else
					R_AnimRun(as, mi->model, cls.frametime * 1000);

				mi->frame = as->frame;
				mi->oldframe = as->oldframe;
				mi->backlerp = as->backlerp;
			}
			R_DrawModelDirect(mi, NULL, NULL);
		}

		/* next */
		model = model->next;
	}
}