cl_spawn.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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     /* not filled from entity string */
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     /* check normal spawn functions */
00131     for (s = spawns; s->name; s++) {
00132         /* found it */
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     /* vid restart? */
00156     if (cl.numMapParticles || cl.numLMs)
00157         return;
00158 
00159     memset(&sun, 0, sizeof(sun));
00160 
00161     /* parse ents */
00162     while (1) {
00163         localEntityParse_t entData;
00164         /* parse the opening brace */
00165         const char *entityToken = Com_Parse(&es);
00166         /* memorize the start */
00167         if (!es)
00168             break;
00169 
00170         if (entityToken[0] != '{')
00171             Com_Error(ERR_DROP, "V_ParseEntitystring: found %s when expecting {", entityToken);
00172 
00173         /* initialize */
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         /* go through all the dictionary pairs */
00184         while (1) {
00185             const value_t *v;
00186             /* parse key */
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                     /* found a definition */
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     /* add the appropriate directional source to the list of active light sources*/
00209     R_AddLightsource(&sun);
00210 
00211     /* after we have parsed all the entities we can resolve the target, targetname
00212      * connections for the misc_model entities */
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     /* maximum level */
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     /* clamp it */
00245     for (i = 0; i < 3; i++)
00246         if (sun.ambientColor[i] < MIN_AMBIENT_COMPONENT)
00247             sun.ambientColor[i] = MIN_AMBIENT_COMPONENT;
00248 
00249     /* scale it into a reasonable range, the clamp above ensures this will work */
00250     while (VectorSum(sun.ambientColor) < MIN_AMBIENT_SUM)
00251         VectorScale(sun.ambientColor, 1.25, sun.ambientColor);
00252 
00253     /* set up "global" (ie. directional) light sources */
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) { /* sunlight color */
00260         Vector4Set(sun.diffuseColor, 0.8, 0.8, 0.8, 1);
00261         Vector4Set(sun.specularColor, 1.0, 1.0, 0.9, 1);
00262     } else { /* moonlight color */
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     /* add it */
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 }

Generated by  doxygen 1.6.2