00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include "../client.h"
00026 #include "../cl_game.h"
00027 #include "cl_particle.h"
00028 #include "cl_spawn.h"
00029 #include "../../shared/parse.h"
00030
00031 static r_light_t sun;
00032
00034 #define MISC_MODEL_GLOW 9
00035 #define SPAWNFLAG_NO_DAY 8
00036
00037 typedef struct {
00038 char classname[MAX_VAR];
00039 char target[MAX_VAR];
00040 char targetname[MAX_VAR];
00041 char tagname[MAX_VAR];
00042 char anim[MAX_VAR];
00043 char model[MAX_QPATH];
00044 char particle[MAX_VAR];
00045 char noise[MAX_QPATH];
00046 vec3_t origin;
00047 vec3_t angles;
00048 vec3_t scale;
00049 vec3_t color;
00050 vec3_t ambientNightColor;
00051 vec3_t ambientDayColor;
00052 vec2_t wait;
00053 int maxLevel;
00054 int maxMultiplayerTeams;
00055 int skin;
00056 int frame;
00057 int light;
00058 int spawnflags;
00059 float volume;
00060 float attenuation;
00061 float angle;
00062 int maxteams;
00063
00064
00065 const char *entStringPos;
00066 int entnum;
00067 } localEntityParse_t;
00068
00069 static const value_t localEntityValues[] = {
00070 {"skin", V_INT, offsetof(localEntityParse_t, skin), MEMBER_SIZEOF(localEntityParse_t, skin)},
00071 {"maxteams", V_INT, offsetof(localEntityParse_t, maxteams), MEMBER_SIZEOF(localEntityParse_t, maxteams)},
00072 {"spawnflags", V_INT, offsetof(localEntityParse_t, spawnflags), MEMBER_SIZEOF(localEntityParse_t, spawnflags)},
00073 {"maxlevel", V_INT, offsetof(localEntityParse_t, maxLevel), MEMBER_SIZEOF(localEntityParse_t, maxLevel)},
00074 {"attenuation", V_FLOAT, offsetof(localEntityParse_t, attenuation), MEMBER_SIZEOF(localEntityParse_t, attenuation)},
00075 {"volume", V_FLOAT, offsetof(localEntityParse_t, volume), MEMBER_SIZEOF(localEntityParse_t, volume)},
00076 {"frame", V_INT, offsetof(localEntityParse_t, frame), MEMBER_SIZEOF(localEntityParse_t, frame)},
00077 {"angle", V_FLOAT, offsetof(localEntityParse_t, angle), MEMBER_SIZEOF(localEntityParse_t, angle)},
00078 {"wait", V_POS, offsetof(localEntityParse_t, wait), MEMBER_SIZEOF(localEntityParse_t, wait)},
00079 {"angles", V_VECTOR, offsetof(localEntityParse_t, angles), MEMBER_SIZEOF(localEntityParse_t, angles)},
00080 {"origin", V_VECTOR, offsetof(localEntityParse_t, origin), MEMBER_SIZEOF(localEntityParse_t, origin)},
00081 {"color", V_VECTOR, offsetof(localEntityParse_t, color), MEMBER_SIZEOF(localEntityParse_t, color)},
00082 {"modelscale_vec", V_VECTOR, offsetof(localEntityParse_t, scale), MEMBER_SIZEOF(localEntityParse_t, scale)},
00083 {"wait", V_POS, offsetof(localEntityParse_t, wait), MEMBER_SIZEOF(localEntityParse_t, wait)},
00084 {"classname", V_STRING, offsetof(localEntityParse_t, classname), 0},
00085 {"model", V_STRING, offsetof(localEntityParse_t, model), 0},
00086 {"anim", V_STRING, offsetof(localEntityParse_t, anim), 0},
00087 {"particle", V_STRING, offsetof(localEntityParse_t, particle), 0},
00088 {"noise", V_STRING, offsetof(localEntityParse_t, noise), 0},
00089 {"tag", V_STRING, offsetof(localEntityParse_t, tagname), 0},
00090 {"target", V_STRING, offsetof(localEntityParse_t, target), 0},
00091 {"targetname", V_STRING, offsetof(localEntityParse_t, targetname), 0},
00092 {"light", V_INT, offsetof(localEntityParse_t, light), MEMBER_SIZEOF(localEntityParse_t, light)},
00093 {"ambient_day", V_VECTOR, offsetof(localEntityParse_t, ambientDayColor), MEMBER_SIZEOF(localEntityParse_t, ambientDayColor)},
00094 {"ambient_night", V_VECTOR, offsetof(localEntityParse_t, ambientNightColor), MEMBER_SIZEOF(localEntityParse_t, ambientNightColor)},
00095
00096 {NULL, 0, 0, 0}
00097 };
00098
00099 static void SP_worldspawn(const localEntityParse_t *entData);
00100 static void SP_misc_model(const localEntityParse_t *entData);
00101 static void SP_misc_particle(const localEntityParse_t *entData);
00102 static void SP_misc_sound(const localEntityParse_t *entData);
00103 static void SP_light(const localEntityParse_t *entData);
00104
00105 typedef struct {
00106 const char *name;
00107 void (*spawn) (const localEntityParse_t *entData);
00108 } spawn_t;
00109
00110 static const spawn_t spawns[] = {
00111 {"worldspawn", SP_worldspawn},
00112 {"misc_model", SP_misc_model},
00113 {"misc_particle", SP_misc_particle},
00114 {"misc_sound", SP_misc_sound},
00115 {"light", SP_light},
00116
00117 {NULL, NULL}
00118 };
00119
00123 static void CL_SpawnCall (const localEntityParse_t *entData)
00124 {
00125 const spawn_t *s;
00126
00127 if (entData->classname[0] == '\0')
00128 return;
00129
00130
00131 for (s = spawns; s->name; s++) {
00132
00133 if (!strcmp(s->name, entData->classname)) {
00134 s->spawn(entData);
00135 return;
00136 }
00137 }
00138 }
00139
00145 void CL_SpawnParseEntitystring (void)
00146 {
00147 const char *es = cl.mapData->mapEntityString;
00148 int entnum = 0, maxLevel;
00149
00150 if (cl.mapMaxLevel > 0 && cl.mapMaxLevel < PATHFINDING_HEIGHT)
00151 maxLevel = cl.mapMaxLevel;
00152 else
00153 maxLevel = PATHFINDING_HEIGHT;
00154
00155
00156 if (cl.numMapParticles || cl.numLMs)
00157 return;
00158
00159 memset(&sun, 0, sizeof(sun));
00160
00161
00162 while (1) {
00163 localEntityParse_t entData;
00164
00165 const char *entityToken = Com_Parse(&es);
00166
00167 if (!es)
00168 break;
00169
00170 if (entityToken[0] != '{')
00171 Com_Error(ERR_DROP, "V_ParseEntitystring: found %s when expecting {", entityToken);
00172
00173
00174 memset(&entData, 0, sizeof(entData));
00175 VectorSet(entData.scale, 1, 1, 1);
00176 entData.volume = SND_VOLUME_DEFAULT;
00177 entData.maxLevel = maxLevel;
00178 entData.entStringPos = es;
00179 entData.entnum = entnum;
00180 entData.maxMultiplayerTeams = TEAM_MAX_HUMAN;
00181 entData.attenuation = SOUND_ATTN_IDLE;
00182
00183
00184 while (1) {
00185 const value_t *v;
00186
00187 entityToken = Com_Parse(&es);
00188 if (entityToken[0] == '}')
00189 break;
00190 if (!es)
00191 Com_Error(ERR_DROP, "V_ParseEntitystring: EOF without closing brace");
00192
00193 for (v = localEntityValues; v->string; v++)
00194 if (!strcmp(entityToken, v->string)) {
00195
00196 entityToken = Com_Parse(&es);
00197 if (!es)
00198 Com_Error(ERR_DROP, "V_ParseEntitystring: EOF without closing brace");
00199 Com_EParseValue(&entData, entityToken, v->type, v->ofs, v->size);
00200 break;
00201 }
00202 }
00203 CL_SpawnCall(&entData);
00204
00205 entnum++;
00206 }
00207
00208
00209 R_AddLightsource(&sun);
00210
00211
00212
00213 LM_Think();
00214 }
00215
00216 #define MIN_AMBIENT_COMPONENT 0.1
00217 #define MIN_AMBIENT_SUM 1.25
00218
00219 static void SP_worldspawn (const localEntityParse_t *entData)
00220 {
00221 const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
00222 int i;
00223
00224
00225 cl.mapMaxLevel = entData->maxLevel;
00226
00227 if (GAME_IsMultiplayer()) {
00228 if (cl_teamnum->integer > entData->maxMultiplayerTeams || cl_teamnum->integer <= TEAM_CIVILIAN) {
00229 Com_Printf("The selected team is not usable. "
00230 "The map doesn't support %i teams but only %i teams\n",
00231 cl_teamnum->integer, entData->maxMultiplayerTeams);
00232 Cvar_SetValue("cl_teamnum", TEAM_DEFAULT);
00233 Com_Printf("Set teamnum to %i\n", cl_teamnum->integer);
00234 }
00235 }
00236
00239 if (dayLightmap)
00240 VectorCopy(entData->ambientDayColor, sun.ambientColor);
00241 else
00242 VectorCopy(entData->ambientNightColor, sun.ambientColor);
00243
00244
00245 for (i = 0; i < 3; i++)
00246 if (sun.ambientColor[i] < MIN_AMBIENT_COMPONENT)
00247 sun.ambientColor[i] = MIN_AMBIENT_COMPONENT;
00248
00249
00250 while (VectorSum(sun.ambientColor) < MIN_AMBIENT_SUM)
00251 VectorScale(sun.ambientColor, 1.25, sun.ambientColor);
00252
00253
00254 Vector4Set(sun.loc, 0, 0, -1, 0.0);
00255 sun.constantAttenuation = 1.0;
00256 sun.linearAttenuation = 0.0;
00257 sun.quadraticAttenuation = 0.0;
00258 sun.enabled = qtrue;
00259 if (dayLightmap) {
00260 Vector4Set(sun.diffuseColor, 0.8, 0.8, 0.8, 1);
00261 Vector4Set(sun.specularColor, 1.0, 1.0, 0.9, 1);
00262 } else {
00263 Vector4Set(sun.diffuseColor, 0.2, 0.2, 0.3, 1);
00264 Vector4Set(sun.specularColor, 0.5, 0.5, 0.7, 1);
00265 }
00266 }
00267
00268 static void SP_misc_model (const localEntityParse_t *entData)
00269 {
00270 localModel_t *lm;
00271 int renderFlags = 0;
00272
00273 if (entData->model[0] == '\0') {
00274 Com_Printf("misc_model without \"model\" specified\n");
00275 return;
00276 }
00277
00278 if (entData->spawnflags & (1 << MISC_MODEL_GLOW))
00279 renderFlags |= RF_PULSE;
00280
00281
00282 lm = LM_AddModel(entData->model, entData->origin, entData->angles, entData->entnum, (entData->spawnflags & 0xFF), renderFlags, entData->scale);
00283 if (lm) {
00284 if (LM_GetByID(entData->targetname) != NULL)
00285 Com_Error(ERR_DROP, "Ambiguous targetname '%s'", entData->targetname);
00286 Q_strncpyz(lm->id, entData->targetname, sizeof(lm->id));
00287 Q_strncpyz(lm->target, entData->target, sizeof(lm->target));
00288 Q_strncpyz(lm->tagname, entData->tagname, sizeof(lm->tagname));
00289
00290 if (lm->animname[0] != '\0' && lm->tagname[0] != '\0') {
00291 Com_Printf("Warning: Model has animation set, but also a tag - use the tag and skip the animation\n");
00292 lm->animname[0] = '\0';
00293 }
00294
00295 if (lm->tagname[0] != '\0' && lm->target[0] == '\0') {
00296 Com_Error(ERR_DROP, "Warning: Model has tag set, but no target given");
00297 }
00298
00299 lm->think = LMT_Init;
00300 lm->skin = entData->skin;
00301 lm->frame = entData->frame;
00302 if (!lm->frame)
00303 Q_strncpyz(lm->animname, entData->anim, sizeof(lm->animname));
00304 else
00305 Com_Printf("Warning: Model has frame and anim parameters - using frame (no animation)\n");
00306 }
00307 }
00308
00309 static void SP_misc_particle (const localEntityParse_t *entData)
00310 {
00311 const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
00312 if (!(dayLightmap && (entData->spawnflags & (1 << SPAWNFLAG_NO_DAY))))
00313 CL_AddMapParticle(entData->particle, entData->origin, entData->wait, entData->entStringPos, (entData->spawnflags & 0xFF));
00314 }
00315
00316 static void SP_misc_sound (const localEntityParse_t *entData)
00317 {
00318 const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
00319 if (!(dayLightmap && (entData->spawnflags & (1 << SPAWNFLAG_NO_DAY))))
00320 LE_AddAmbientSound(entData->noise, entData->origin, (entData->spawnflags & 0xFF), entData->volume, entData->attenuation);
00321 }
00322
00327 static void SP_light (const localEntityParse_t *entData)
00328 {
00329 #if 0
00330 r_light_t light;
00331 memset(&light, 0, sizeof(light));
00332 VectorCopy(entData->color, light.diffuseColor);
00333 light.diffuseColor[3] = 1.0;
00334 VectorCopy(entData->origin, light.loc);
00336 light.loc[3] = 0.0;
00337 light.enabled = qtrue;
00338 R_AddLightsource(&light);
00339 #endif
00340 }