UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_spawn.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 */
24 
25 #include "cl_spawn.h"
26 #include "../client.h"
27 #include "../cgame/cl_game.h"
28 #include "cl_particle.h"
29 #include "../../shared/parse.h"
30 
31 /* position in the spawnflags */
32 #define MISC_MODEL_GLOW 9
33 #define SPAWNFLAG_NO_DAY 8
34 
35 typedef struct localEntityParse_s {
37  char target[MAX_VAR];
40  char anim[MAX_VAR];
57  int maxLevel;
59  int skin;
60  int frame;
61  int light;
63  float volume;
64  float attenuation;
65  float angle;
66  int maxteams;
67 
68  /* not filled from entity string */
69  const char* entStringPos;
70  int entnum;
72 
73 static const value_t localEntityValues[] = {
74  {"skin", V_INT, offsetof(localEntityParse_t, skin), MEMBER_SIZEOF(localEntityParse_t, skin)},
75  {"maxteams", V_INT, offsetof(localEntityParse_t, maxteams), MEMBER_SIZEOF(localEntityParse_t, maxteams)},
76  {"spawnflags", V_INT, offsetof(localEntityParse_t, spawnflags), MEMBER_SIZEOF(localEntityParse_t, spawnflags)},
77  {"maxlevel", V_INT, offsetof(localEntityParse_t, maxLevel), MEMBER_SIZEOF(localEntityParse_t, maxLevel)},
78  {"attenuation", V_FLOAT, offsetof(localEntityParse_t, attenuation), MEMBER_SIZEOF(localEntityParse_t, attenuation)},
79  {"volume", V_FLOAT, offsetof(localEntityParse_t, volume), MEMBER_SIZEOF(localEntityParse_t, volume)},
80  {"frame", V_INT, offsetof(localEntityParse_t, frame), MEMBER_SIZEOF(localEntityParse_t, frame)},
81  {"angle", V_FLOAT, offsetof(localEntityParse_t, angle), MEMBER_SIZEOF(localEntityParse_t, angle)},
82  {"wait", V_POS, offsetof(localEntityParse_t, wait), MEMBER_SIZEOF(localEntityParse_t, wait)},
83  {"angles", V_VECTOR, offsetof(localEntityParse_t, angles), MEMBER_SIZEOF(localEntityParse_t, angles)},
85  {"color", V_VECTOR, offsetof(localEntityParse_t, color), MEMBER_SIZEOF(localEntityParse_t, color)},
86  {"_color", V_VECTOR, offsetof(localEntityParse_t, color), MEMBER_SIZEOF(localEntityParse_t, color)},
87  {"modelscale_vec", V_VECTOR, offsetof(localEntityParse_t, scale), MEMBER_SIZEOF(localEntityParse_t, scale)},
88  {"wait", V_POS, offsetof(localEntityParse_t, wait), MEMBER_SIZEOF(localEntityParse_t, wait)},
89  {"classname", V_STRING, offsetof(localEntityParse_t, classname), 0},
90  {"model", V_STRING, offsetof(localEntityParse_t, model), 0},
91  {"anim", V_STRING, offsetof(localEntityParse_t, anim), 0},
92  {"particle", V_STRING, offsetof(localEntityParse_t, particle), 0},
93  {"noise", V_STRING, offsetof(localEntityParse_t, noise), 0},
94  {"tag", V_STRING, offsetof(localEntityParse_t, tagname), 0},
95  {"target", V_STRING, offsetof(localEntityParse_t, target), 0},
96  {"targetname", V_STRING, offsetof(localEntityParse_t, targetname), 0},
97  {"light", V_INT, offsetof(localEntityParse_t, light), MEMBER_SIZEOF(localEntityParse_t, light)},
98  {"ambient_day", V_VECTOR, offsetof(localEntityParse_t, ambientDayColor), MEMBER_SIZEOF(localEntityParse_t, ambientDayColor)},
99  {"light_day", V_FLOAT, offsetof(localEntityParse_t, dayLight), MEMBER_SIZEOF(localEntityParse_t, dayLight)},
100  {"angles_day", V_POS, offsetof(localEntityParse_t, daySunAngles), MEMBER_SIZEOF(localEntityParse_t, daySunAngles)},
101  {"color_day", V_VECTOR, offsetof(localEntityParse_t, daySunColor), MEMBER_SIZEOF(localEntityParse_t, daySunColor)},
102  {"ambient_night", V_VECTOR, offsetof(localEntityParse_t, ambientNightColor), MEMBER_SIZEOF(localEntityParse_t, ambientNightColor)},
103  {"light_night", V_FLOAT, offsetof(localEntityParse_t, nightLight), MEMBER_SIZEOF(localEntityParse_t, nightLight)},
104  {"angles_night", V_POS, offsetof(localEntityParse_t, nightSunAngles), MEMBER_SIZEOF(localEntityParse_t, nightSunAngles)},
105  {"color_night", V_VECTOR, offsetof(localEntityParse_t, nightSunColor), MEMBER_SIZEOF(localEntityParse_t, nightSunColor)},
106 
107  {nullptr, V_NULL, 0, 0}
108 };
109 
110 #define MIN_AMBIENT_COMPONENT 0.1
111 #define MIN_AMBIENT_SUM 0.50
112 
114 static void SP_worldspawn (const localEntityParse_t* entData)
115 {
116  /* maximum level */
117  cl.mapMaxLevel = entData->maxLevel;
118 
119  if (GAME_IsMultiplayer()) {
121  Com_Printf("The selected team is not usable. "
122  "The map doesn't support %i teams but only %i teams\n",
124  Cvar_SetValue("cl_teamnum", TEAM_DEFAULT);
125  Com_Printf("Set teamnum to %i\n", cl_teamnum->integer);
126  }
127  }
128 
130  const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
131 
133  vec3_t sunAngles;
134  vec4_t sunColor;
135  vec_t sunIntensity;
136  if (dayLightmap) {
137  /* set defaults for daylight */
138  Vector4Set(refdef.ambientColor, 0.26, 0.26, 0.26, 1.0);
139  sunIntensity = 280;
140  VectorSet(sunAngles, -75, 100, 0);
141  Vector4Set(sunColor, 0.90, 0.75, 0.65, 1.0);
142 
143  /* override defaults with data from worldspawn entity, if any */
144  if (VectorNotEmpty(entData->ambientDayColor))
146 
147  if (entData->dayLight)
148  sunIntensity = entData->dayLight;
149 
150  if (Vector2NotEmpty(entData->daySunAngles))
151  Vector2Copy(entData->daySunAngles, sunAngles);
152 
153  if (VectorNotEmpty(entData->daySunColor))
154  VectorCopy(entData->daySunColor, sunColor);
155 
156  Vector4Set(refdef.sunSpecularColor, 1.0, 1.0, 0.9, 1);
157  } else {
158  /* set defaults for night light */
159  Vector4Set(refdef.ambientColor, 0.16, 0.16, 0.17, 1.0);
160  sunIntensity = 15;
161  VectorSet(sunAngles, -80, 220, 0);
162  Vector4Set(sunColor, 0.25, 0.25, 0.35, 1.0);
163 
164  /* override defaults with data from worldspawn entity, if any */
165  if (VectorNotEmpty(entData->ambientNightColor))
167 
168  if (entData->nightLight)
169  sunIntensity = entData->nightLight;
170 
171  if (Vector2NotEmpty(entData->nightSunAngles))
172  Vector2Copy(entData->nightSunAngles, sunAngles);
173 
174  if (VectorNotEmpty(entData->nightSunColor))
175  VectorCopy(entData->nightSunColor, sunColor);
176 
177  Vector4Set(refdef.sunSpecularColor, 0.5, 0.5, 0.7, 1);
178  }
179 
180  ColorNormalize(sunColor, sunColor);
181  VectorScale(sunColor, sunIntensity/255.0, sunColor);
183 
184  /* clamp ambient for models */
186  for (int i = 0; i < 3; i++)
189 
190  /* scale it into a reasonable range, the clamp above ensures this will work */
193 
194  AngleVectors(sunAngles, refdef.sunVector, nullptr, nullptr);
195  refdef.sunVector[3] = 0.0; /* to use as directional light source in OpenGL */
196 
199  refdef.fogColor[3] = 1.0;
200  VectorSet(refdef.fogColor, 0.75, 0.75, 0.75);
201 }
202 
203 static void SP_misc_model (const localEntityParse_t* entData)
204 {
205  if (entData->model[0] == '\0') {
206  Com_Printf("misc_model without \"model\" specified\n");
207  return;
208  }
209 
210  int renderFlags = 0;
211  if (entData->spawnflags & (1 << MISC_MODEL_GLOW))
212  renderFlags |= RF_PULSE;
213 
214  /* add it */
215  localModel_t* lm = LM_AddModel(entData->model, entData->origin, entData->angles, entData->entnum, (entData->spawnflags & 0xFF), renderFlags, entData->scale);
216  if (lm) {
217  if (LM_GetByID(entData->targetname) != nullptr)
218  Com_Error(ERR_DROP, "Ambiguous targetname '%s'", entData->targetname);
219  Q_strncpyz(lm->id, entData->targetname, sizeof(lm->id));
220  Q_strncpyz(lm->target, entData->target, sizeof(lm->target));
221  Q_strncpyz(lm->tagname, entData->tagname, sizeof(lm->tagname));
222 
223  if (lm->animname[0] != '\0' && lm->tagname[0] != '\0') {
224  Com_Printf("Warning: Model has animation set, but also a tag - use the tag and skip the animation\n");
225  lm->animname[0] = '\0';
226  }
227 
228  if (lm->tagname[0] != '\0' && lm->target[0] == '\0') {
229  Com_Error(ERR_DROP, "Warning: Model has tag set, but no target given");
230  }
231 
232  lm->think = LMT_Init;
233  lm->skin = entData->skin;
234  lm->frame = entData->frame;
235  if (!lm->frame)
236  Q_strncpyz(lm->animname, entData->anim, sizeof(lm->animname));
237  else
238  Com_Printf("Warning: Model has frame and anim parameters - using frame (no animation)\n");
239  }
240 }
241 
242 static void SP_misc_particle (const localEntityParse_t* entData)
243 {
244  const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
245  if (!(dayLightmap && (entData->spawnflags & (1 << SPAWNFLAG_NO_DAY))))
246  CL_AddMapParticle(entData->particle, entData->origin, entData->wait, entData->entStringPos, (entData->spawnflags & 0xFF));
247 }
248 
249 static void SP_misc_sound (const localEntityParse_t* entData)
250 {
251  const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
252  if (!(dayLightmap && (entData->spawnflags & (1 << SPAWNFLAG_NO_DAY))))
253  LE_AddAmbientSound(entData->noise, entData->origin, (entData->spawnflags & 0xFF), entData->volume, entData->attenuation);
254 }
255 
259 static void SP_light (const localEntityParse_t* entData)
260 {
261  if (entData->light < 1.0)
262  return;
263 
264  const int dayLightmap = CL_GetConfigStringInteger(CS_LIGHTMAP);
265  if (!(dayLightmap && (entData->spawnflags & (1 << SPAWNFLAG_NO_DAY)))) {
266  R_AddStaticLight(entData->origin, entData->light, entData->color);
267  }
268 }
269 
270 typedef struct {
271  const char* name;
272  void (*spawn) (const localEntityParse_t* entData);
273 } spawn_t;
274 
275 static const spawn_t spawns[] = {
276  {"worldspawn", SP_worldspawn},
277  {"misc_model", SP_misc_model},
278  {"misc_particle", SP_misc_particle},
279  {"misc_sound", SP_misc_sound},
280  {"light", SP_light},
281 
282  {nullptr, nullptr}
283 };
284 
288 static void CL_SpawnCall (const localEntityParse_t* entData)
289 {
290  if (entData->classname[0] == '\0')
291  return;
292 
293  /* check normal spawn functions */
294  for (const spawn_t* s = spawns; s->name; s++) {
295  if (Q_streq(s->name, entData->classname)) {
296  /* found it */
297  s->spawn(entData);
298  return;
299  }
300  }
301 }
302 
309 {
310  int maxLevel;
311 
313  maxLevel = cl.mapMaxLevel;
314  else
315  maxLevel = PATHFINDING_HEIGHT;
316 
317  /* vid restart? */
318  if (cl.numMapParticles || cl.numLMs)
319  return;
320 
321  /* parse ents */
322  const char* es = cl.mapData->mapEntityString;
323  int entnum = 0;
324  while (1) {
325  localEntityParse_t entData;
326  /* parse the opening brace */
327  const char* entityToken = Com_Parse(&es);
328  /* memorize the start */
329  if (!es)
330  break;
331 
332  if (entityToken[0] != '{')
333  Com_Error(ERR_DROP, "V_ParseEntitystring: found %s when expecting {", entityToken);
334 
335  /* initialize */
336  OBJZERO(entData);
337  VectorSet(entData.scale, 1, 1, 1);
338  entData.volume = SND_VOLUME_DEFAULT;
339  entData.maxLevel = maxLevel;
340  entData.entStringPos = es;
341  entData.entnum = entnum;
343  entData.attenuation = SOUND_ATTN_IDLE;
344 
345  /* go through all the dictionary pairs */
346  while (1) {
347  /* parse key */
348  entityToken = Com_Parse(&es);
349  if (entityToken[0] == '}')
350  break;
351  if (!es)
352  Com_Error(ERR_DROP, "V_ParseEntitystring: EOF without closing brace");
353 
354  for (const value_t* v = localEntityValues; v->string; v++)
355  if (Q_streq(entityToken, v->string)) {
356  /* found a definition */
357  entityToken = Com_Parse(&es);
358  if (!es)
359  Com_Error(ERR_DROP, "V_ParseEntitystring: EOF without closing brace");
360  Com_EParseValue(&entData, entityToken, v->type, v->ofs, v->size);
361  break;
362  }
363  }
364  CL_SpawnCall(&entData);
365 
366  entnum++;
367  }
368 
369  /* after we have parsed all the entities we can resolve the target, targetname
370  * connections for the misc_model entities */
371  LM_Think();
372 }
#define MIN_AMBIENT_SUM
Definition: cl_spawn.cpp:111
#define Vector2NotEmpty(a)
Definition: vector.h:75
vec4_t sunDiffuseColor
Definition: cl_renderer.h:184
#define VectorCopy(src, dest)
Definition: vector.h:51
char model[MAX_QPATH]
Definition: cl_spawn.cpp:41
cvar_t * cl_teamnum
Definition: cl_main.cpp:81
char classname[MAX_VAR]
Definition: cl_spawn.cpp:36
#define VectorSet(v, x, y, z)
Definition: vector.h:59
#define RF_PULSE
Definition: r_entity.h:47
vec_t ColorNormalize(const vec3_t in, vec3_t out)
Definition: mathlib.cpp:190
vec4_t sunSpecularColor
Definition: cl_renderer.h:185
vec3_t ambientNightColor
Definition: cl_spawn.cpp:48
static const vec3_t scale
void CL_AddMapParticle(const char *ptl, const vec3_t origin, const vec2_t wait, const char *info, int levelflags)
Spawns the map particle.
void CL_SpawnParseEntitystring(void)
Parse the map entity string and spawns those entities that are client-side only.
Definition: cl_spawn.cpp:308
voidpf uLong int origin
Definition: ioapi.h:45
char targetname[MAX_VAR]
Definition: cl_spawn.cpp:38
char tagname[MAX_VAR]
Definition: cl_spawn.cpp:39
char anim[MAX_VAR]
Definition: cl_spawn.cpp:40
float vec_t
Definition: ufotypes.h:37
static void SP_worldspawn(const localEntityParse_t *entData)
Definition: cl_spawn.cpp:114
vec4_t sunVector
Definition: cl_renderer.h:186
void LM_Think(void)
#define TEAM_CIVILIAN
Definition: q_shared.h:61
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
int CL_GetConfigStringInteger(int index)
char noise[MAX_QPATH]
Definition: cl_spawn.cpp:43
void LMT_Init(localModel_t *localModel)
vec3_t nightSunColor
Definition: cl_spawn.cpp:51
int integer
Definition: cvar.h:81
#define VectorScale(in, scale, out)
Definition: vector.h:79
#define VectorSum(a)
Definition: vector.h:60
#define TEAM_MAX_HUMAN
Definition: q_shared.h:64
char particle[MAX_VAR]
Definition: cl_spawn.cpp:42
#define SOUND_ATTN_IDLE
Definition: common.h:187
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
static const value_t localEntityValues[]
Definition: cl_spawn.cpp:73
vec2_t nightSunAngles
Definition: cl_spawn.cpp:50
#define MISC_MODEL_GLOW
Definition: cl_spawn.cpp:32
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Create the rotation matrix in order to rotate something.
Definition: mathlib.cpp:631
#define WEATHER_NONE
Definition: cl_renderer.h:37
#define ERR_DROP
Definition: common.h:211
rendererData_t refdef
Definition: r_main.cpp:45
#define OBJZERO(obj)
Definition: shared.h:178
struct localEntityParse_s localEntityParse_t
#define MAX_VAR
Definition: shared.h:36
#define Vector4Set(v, r, g, b, a)
Definition: vector.h:62
static void SP_misc_particle(const localEntityParse_t *entData)
Definition: cl_spawn.cpp:242
localModel_t * LM_GetByID(const char *id)
char target[MAX_VAR]
clientBattleScape_t cl
bool GAME_IsMultiplayer(void)
Definition: cl_game.cpp:299
#define TEAM_DEFAULT
Definition: defines.h:51
#define VectorNotEmpty(a)
Definition: vector.h:72
vec3_t ambientDayColor
Definition: cl_spawn.cpp:52
const char * entStringPos
Definition: cl_spawn.cpp:69
const GLuint *typedef void(APIENTRY *GenRenderbuffersEXT_t)(GLsizei
Definition: r_gl.h:189
#define SPAWNFLAG_NO_DAY
Definition: cl_spawn.cpp:33
static void SP_misc_model(const localEntityParse_t *entData)
Definition: cl_spawn.cpp:203
static void SP_light(const localEntityParse_t *entData)
Definition: cl_spawn.cpp:259
char id[MAX_VAR]
int Com_EParseValue(void *base, const char *token, valueTypes_t type, int ofs, size_t size)
Definition: scripts.cpp:978
#define PATHFINDING_HEIGHT
15 max, adjusting above 8 will require a rewrite to the DV code
Definition: defines.h:294
localModel_t * LM_AddModel(const char *model, const vec3_t origin, const vec3_t angles, int entnum, int levelflags, int renderFlags, const vec3_t scale)
Prepares local (not known or handled by the server) models to the map, which will be added later in L...
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
Definition: scripts.h:49
static void SP_misc_sound(const localEntityParse_t *entData)
Definition: cl_spawn.cpp:249
char target[MAX_VAR]
Definition: cl_spawn.cpp:37
#define MAX_QPATH
Definition: filesys.h:40
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define CS_LIGHTMAP
Definition: q_shared.h:321
char mapEntityString[MAX_MAP_ENTSTRING]
Definition: typedefs.h:331
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
struct spawn_s spawn_t
vec_t vec3_t[3]
Definition: ufotypes.h:39
#define Vector2Copy(src, dest)
Definition: vector.h:52
vec_t vec2_t[2]
Definition: ufotypes.h:38
void(* think)(struct localModel_s *localModel)
Definition: scripts.h:52
void LE_AddAmbientSound(const char *sound, const vec3_t origin, int levelflags, float volume, float attenuation)
Adds ambient sounds from misc_sound entities.
vec4_t ambientColor
Definition: cl_renderer.h:182
vec4_t modelAmbientColor
Definition: cl_renderer.h:183
#define Q_streq(a, b)
Definition: shared.h:136
#define MIN_AMBIENT_COMPONENT
Definition: cl_spawn.cpp:110
const char * name
Definition: cl_spawn.cpp:271
void Cvar_SetValue(const char *varName, float value)
Expands value to a string and calls Cvar_Set.
Definition: cvar.cpp:671
char tagname[MAX_VAR]
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
local models
#define Vector4Copy(src, dest)
Definition: vector.h:53
Definition: scripts.h:55
char animname[MAX_QPATH]
static const spawn_t spawns[]
Definition: cl_spawn.cpp:275
void R_AddStaticLight(const vec3_t origin, float radius, const vec3_t color)
Add static light for model lighting (world already got them baked into lightmap)
Definition: r_light.cpp:261
static void CL_SpawnCall(const localEntityParse_t *entData)
Finds the spawn function for the entity and calls it.
Definition: cl_spawn.cpp:288
#define SND_VOLUME_DEFAULT
Definition: s_main.h:42
vec_t vec4_t[4]
Definition: ufotypes.h:40