// g_misc.c #include "g_local.h" /*QUAKED func_group (0 0 0) ? Used to group brushes together just for editor convenience. */ //===================================================== void Use_Areaportal (edict_t *ent, edict_t *other, edict_t *activator) { ent->count ^= 1; // toggle state // gi.dprintf ("portalstate: %i = %i\n", ent->style, ent->count); gi.SetAreaPortalState (ent->style, ent->count); } /*QUAKED func_areaportal (0 0 0) ? This is a non-visible object that divides the world into areas that are seperated when this portal is not activated. Usually enclosed in the middle of a door. */ void SP_func_areaportal (edict_t *ent) { ent->use = Use_Areaportal; ent->count = 0; // always start closed; } //===================================================== /* ================= Misc functions ================= */ void VelocityForDamage (int damage, vec3_t v) { v[0] = 300.0 * crandom(); v[1] = 300.0 * crandom(); v[2] = 200.0 + 400.0 * random(); if (damage < 50) VectorScale (v, 0.7, v); else VectorScale (v, 1.2, v); } void ClipGibVelocity (edict_t *ent) { if (ent->velocity[0] < -400) ent->velocity[0] = -400; else if (ent->velocity[0] > 400) ent->velocity[0] = 400; if (ent->velocity[1] < -400) ent->velocity[1] = -400; else if (ent->velocity[1] > 400) ent->velocity[1] = 400; if (ent->velocity[2] < 200) ent->velocity[2] = 200; // always some upwards else if (ent->velocity[2] > 500) ent->velocity[2] = 500; } /* ================= gibs ================= */ void gib_think (edict_t *self) { self->s.frame++; self->nextthink = level.time + FRAMETIME; if (self->s.frame == 10) { self->think = G_FreeEdict; self->nextthink = level.time + 8 + random()*10; } } void gib_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t normal_angles, right; if (!self->groundentity) return; self->touch = NULL; if (plane) { // JOSEPH 29-MAR-99 //gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0); // END JOSEPH vectoangles (plane->normal, normal_angles); AngleVectors (normal_angles, NULL, right, NULL); vectoangles (right, self->s.angles); if (self->s.modelindex == sm_meat_index) { self->s.frame++; self->think = gib_think; self->nextthink = level.time + FRAMETIME; } } } // RAFAEL /* Ridah, this has been diasabled in the latest code void gib_touchacid (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t normal_angles, right; if (other->takedamage) { // T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0); T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); G_FreeEdict (self); } if (!self->groundentity) return; if (plane) { // JOSEPH 29-MAR-99 gi.sound (self, CHAN_VOICE, gi.soundindex ("misc/fhit3.wav"), 1, ATTN_NORM, 0); // END JOSEPH vectoangles (plane->normal, normal_angles); AngleVectors (normal_angles, NULL, right, NULL); vectoangles (right, self->s.angles); if (self->s.modelindex == sm_meat_index) { self->s.frame++; self->think = gib_think; self->nextthink = level.time + FRAMETIME; } } } */ void gib_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { G_FreeEdict (self); } edict_t *ThrowGib (edict_t *self, char *gibname, int damage, int type) { edict_t *gib; vec3_t vd; vec3_t origin; vec3_t size; float vscale; gib = G_Spawn(); VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); gib->s.origin[0] = origin[0] + crandom() * size[0]; gib->s.origin[1] = origin[1] + crandom() * size[1]; gib->s.origin[2] = origin[2] + crandom() * size[2]; gi.setmodel (gib, gibname); gib->solid = SOLID_NOT; gib->s.effects |= EF_GIB; gib->flags |= FL_NO_KNOCKBACK; gib->takedamage = DAMAGE_YES; gib->s.renderfx2 |= RF2_NOSHADOW; gib->die = gib_die; if (type == GIB_ORGANIC) { gib->movetype = MOVETYPE_TOSS; gib->touch = gib_touch; vscale = 0.5; } else { gib->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage (damage, vd); VectorMA (self->velocity, vscale, vd, gib->velocity); ClipGibVelocity (gib); gib->avelocity[0] = random()*600; gib->avelocity[1] = random()*600; gib->avelocity[2] = random()*600; gib->think = G_FreeEdict; gib->nextthink = level.time + 10 + random()*10; gi.linkentity (gib); return gib; } void ThrowHead (edict_t *self, char *gibname, int damage, int type) { vec3_t vd; float vscale; self->s.skinnum = 0; self->s.frame = 0; VectorClear (self->mins); VectorClear (self->maxs); // self->s.modelindex2 = 0; gi.setmodel (self, gibname); self->solid = SOLID_NOT; self->s.effects |= EF_GIB; self->s.effects &= ~EF_FLIES; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->svflags &= ~SVF_MONSTER; self->takedamage = DAMAGE_YES; self->die = gib_die; if (type == GIB_ORGANIC) { self->movetype = MOVETYPE_TOSS; self->touch = gib_touch; vscale = 0.5; } else { self->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage (damage, vd); VectorMA (self->velocity, vscale, vd, self->velocity); ClipGibVelocity (self); self->avelocity[YAW] = crandom()*600; self->think = G_FreeEdict; self->nextthink = level.time + 10 + random()*10; gi.linkentity (self); } void ThrowClientHead (edict_t *self, int damage) { vec3_t vd; char *gibname; /* if (rand()&1) { gibname = "models/objects/gibs/head2/tris.md2"; self->s.skinnum = 1; // second skin is player } else { gibname = "models/objects/gibs/skull/tris.md2"; self->s.skinnum = 0; } */ gibname = "models/props/gibs/gib3.mdx"; self->s.skinnum = 0; self->s.origin[2] += 32; self->s.frame = 0; // gi.setmodel (self, gibname); self->s.num_parts = 0; self->s.modelindex = gi.modelindex( gibname ); VectorSet (self->mins, -16, -16, 0); VectorSet (self->maxs, 16, 16, 16); self->takedamage = DAMAGE_NO; self->solid = SOLID_NOT; // self->s.effects |= EF_GIB; self->s.sound = 0; self->flags |= FL_NO_KNOCKBACK; self->movetype = MOVETYPE_BOUNCE; VelocityForDamage (damage, vd); VectorAdd (self->velocity, vd, self->velocity); if (self->client) // bodies in the queue don't have a client anymore { self->client->anim_priority = ANIM_DEATH; self->client->anim_end = self->s.frame; } else { self->think = NULL; self->nextthink = 0; } gi.linkentity (self); } /* ============= GibEntity Gibs an entity.. can be used for client's or AI ============= */ void GibEntity( edict_t *self, edict_t *inflictor, float damage ) { vec3_t dir, vel; int m, n; // turn off flames if (self->onfiretime) self->onfiretime = 0; if (inflictor->client || VectorCompare( inflictor->velocity, vec3_origin) ) VectorSubtract( self->s.origin, inflictor->s.origin, dir ); else VectorCopy( inflictor->velocity, dir ); VectorNormalize( dir ); // JOSEPH 12-MAY-99-B // send the client-side gib message gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_GIBS); gi.WritePosition (self->s.origin); gi.WriteDir (dir); gi.WriteByte ( 16 ); // number of gibs gi.WriteByte ( (damage*2 > 128 ? 128 : (int)(damage*2)) ); // scale of direction to add to velocity gi.WriteByte ( 16 ); // random origin offset scale gi.WriteByte ( 100 ); // random velocity scale gi.multicast (self->s.origin, MULTICAST_PVS); // END JOSEPH for (n= 0; n < 6; n++) { VelocityForDamage (damage, vel); // spawn a decal in this direction for (m=0; m<4; m++) // try 4 times with a different Z value { vec3_t bdir; vec3_t dorg; trace_t tr; VectorCopy( vel, bdir ); bdir[2] -= random()*bdir[2]*(1+4*random()); VectorAdd( self->s.origin, bdir, dorg ); tr = gi.trace( self->s.origin, NULL, NULL, dorg, self, MASK_SOLID ); if (tr.fraction < 1 && tr.ent == &g_edicts[0] && !(tr.surface->flags & SURF_SKY)) { float rnd; rnd = (1.5 + 5.0*random()); if (SurfaceSpriteEffect(SFX_SPRITE_SURF_BLOOD1, (unsigned char)(SFX_BLOOD_WIDTH*rnd), (unsigned char)(SFX_BLOOD_HEIGHT*rnd), tr.ent, tr.endpos, tr.plane.normal)) { break; } } } } if (!self->client) // Ridah, added this check, since in deathmatch, the body is left there, just only clients with parental lock ENABLEd will see them, since they don't see the gibs ThrowClientHead (self, damage); self->takedamage = DAMAGE_NO; } /* ================= debris ================= */ void debris_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { G_FreeEdict (self); } // JOSEPH 22-FEB-99 void Think_Debris (edict_t *ent) { if (!ent->misstime--) { ent->nextthink = 0; G_FreeEdict(ent); } else { if (ent->misstime <= 15) { if (ent->misstime == 15) { ent->s.renderfx2 |= RF2_PASSALPHA; ent->s.effects = 1; // this is full alpha now } ent->s.effects += (255/15); if (ent->s.effects > 255) ent->s.effects = 255; } //ent->s.angles[1] += ent->avelocity[1]; ent->nextthink = level.time + 0.1; } } void ThrowDebris (edict_t *self, char *modelname, float speed, vec3_t origin) { edict_t *chunk; vec3_t v; chunk = G_Spawn(); VectorCopy (origin, chunk->s.origin); gi.setmodel (chunk, modelname); v[0] = 50 * crandom(); v[1] = 50 * crandom(); v[2] = 50 + 50 * crandom(); VectorMA (self->velocity, speed, v, chunk->velocity); chunk->movetype = MOVETYPE_BOUNCE; chunk->solid = SOLID_NOT; chunk->avelocity[0] = random()*600; chunk->avelocity[1] = random()*600; chunk->avelocity[2] = random()*600; chunk->think = G_FreeEdict; chunk->nextthink = level.time + 5 + random()*5; chunk->s.frame = 0; chunk->flags = 0; chunk->classname = "debris"; chunk->takedamage = DAMAGE_YES; chunk->die = debris_die; chunk->think = Think_Debris; chunk->misstime = 20; chunk->nextthink = level.time + 0.1; chunk->s.renderfx2 |= RF2_NOSHADOW; chunk->avelocity[1] = ((rand()&15)-8); //chunk->s.angles[0] = 90; gi.linkentity (chunk); } // END JOSEPH // JOSEPH 5-JUN-99-B void ThrowDebris_stuff (edict_t *self, char *modelname, float speed, vec3_t origin) { edict_t *chunk; vec3_t v; chunk = G_Spawn(); VectorCopy (origin, chunk->s.origin); gi.setmodel (chunk, modelname); v[0] = 150 * crandom(); v[1] = 150 * crandom(); v[2] = 150 + 50 * crandom(); VectorMA (self->velocity, speed, v, chunk->velocity); chunk->movetype = MOVETYPE_BOUNCE; chunk->solid = SOLID_NOT; chunk->avelocity[0] = random()*600; chunk->avelocity[1] = random()*600; chunk->avelocity[2] = random()*600; chunk->think = G_FreeEdict; chunk->nextthink = level.time + 5 + random()*5; chunk->s.frame = 0; chunk->flags = 0; chunk->classname = "debris"; chunk->takedamage = DAMAGE_YES; chunk->die = debris_die; chunk->think = Think_Debris; chunk->misstime = 40; chunk->nextthink = level.time + 0.1; chunk->s.renderfx2 |= RF2_NOSHADOW; chunk->avelocity[1] = ((rand()&15)-8); chunk->lightit = 30; gi.linkentity (chunk); } // END JOSEPH // JOSEPH 1-JUN-99 extern void rat_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject); void BecomeExplosion1 (edict_t *self) { if ((self->classname) && (!strcmp(self->classname, "props_rat"))) { rat_die (self, NULL, NULL, 0, NULL, 0, 0); } else { vec3_t vec; VectorClear(vec); vec[2] = 1; gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_EXPLOSION1); gi.WritePosition (self->s.origin); gi.WriteDir( vec ); gi.WriteByte( (int)(self->dmg / 2) ); gi.WriteByte (self->fxdensity); gi.multicast (self->s.origin, MULTICAST_PVS); { edict_t *breakit; breakit = G_Spawn(); if (breakit) { VectorCopy (self->s.origin, breakit->s.origin); gi.linkentity(breakit); gi.sound (breakit, CHAN_VOICE, gi.soundindex("world/explosion1.wav"), 1, ATTN_NORM, 0); breakit->think = G_FreeEdict; breakit->nextthink = level.time + 5.0; } } G_FreeEdict (self); } } // END JOSEPH void BecomeExplosion2 (edict_t *self) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_EXPLOSION2); gi.WritePosition (self->s.origin); gi.multicast (self->s.origin, MULTICAST_PVS); G_FreeEdict (self); } /*QUAKED path_corner_cast (.5 .3 0) (-16 -16 -24) (16 16 42) TELEPORT Target: next path corner Scriptname: hard-coded script to call when reaching this marker Pathtarget: gets used when an entity that has this path_corner targeted touches it Could be used to trigger a button, or a character Combattarget: when the cast reaches this marker, they'll take on this combattarget */ void path_corner_cast_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t v; edict_t *next; if ((other->goal_ent != self) && (other->combat_goalent != self)) return; if (other->health < 0) return; if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets (self, other); self->target = savetarget; } // play a sound if there is one if (self->name) gi.sound( other, CHAN_VOICE, gi.soundindex( self->name ), 1.0, 1, 0 ); if (self->combattarget) other->next_combattarget = self->combattarget; if (self->target) next = G_PickTarget(self->target); else next = NULL; if ((next) && (next->spawnflags & 1)) { VectorCopy (next->s.origin, v); v[2] += next->mins[2]; v[2] -= other->mins[2]; VectorCopy (v, other->s.origin); next = G_PickTarget(next->target); } if (other->goal_ent == self) other->goal_ent = next; else other->combat_goalent = next; if (self->wait) { other->cast_info.goal_ent_pausetime = level.time + self->wait; other->cast_info.currentmove = other->cast_info.move_stand; goto done; } if (!other->goal_ent && !other->combat_goalent) { // end of the line other->target = NULL; other->cast_info.currentmove = other->cast_info.move_stand; } else // remember this marker { if (other->goal_ent == self) { other->target = other->goal_ent->target; VectorSubtract (other->goal_ent->s.origin, other->s.origin, v); } else // combattarget { if (!(other->combat_goalent)) { other->target = other->goal_ent->target; VectorSubtract (other->goal_ent->s.origin, other->s.origin, v); } else { VectorSubtract (other->combat_goalent->s.origin, other->s.origin, v); other->combat_goalent->cast_info.aiflags |= AI_GOAL_RUN; } } other->ideal_yaw = vectoyaw (v); } done: // Ridah, hard-coded scripting if (self->scriptname) EP_EventScript( other, self->scriptname ); } void SP_path_corner_cast (edict_t *self) { if (!self->targetname) { gi.dprintf ("path_corner_cast with no targetname at %s\n", vtos(self->s.origin)); G_FreeEdict (self); return; } strcpy(self->classname, "path_corner"); // so the existing code still works VectorSet (self->mins, -16, -16, -24); VectorSet (self->maxs, 16, 16, 42); self->solid = SOLID_TRIGGER; self->touch = path_corner_cast_touch; self->svflags |= SVF_NOCLIENT; M_droptofloor(self); self->s.origin[2] += 4; gi.linkentity (self); } /*QUAKED path_corner (.5 .3 0) (-8 -8 -8) (8 8 8) TELEPORT NOTE: Use path_corner_cast for character path's Target: next path corner Pathtarget: gets used when an entity that has this path_corner targeted touches it */ void path_corner_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { vec3_t v; edict_t *next; if (other->movetarget != self) return; if (other->enemy) return; if (self->pathtarget) { char *savetarget; savetarget = self->target; self->target = self->pathtarget; G_UseTargets (self, other); self->target = savetarget; } if (self->target) next = G_PickTarget(self->target); else next = NULL; if ((next) && (next->spawnflags & 1)) { VectorCopy (next->s.origin, v); v[2] += next->mins[2]; v[2] -= other->mins[2]; VectorCopy (v, other->s.origin); next = G_PickTarget(next->target); } other->goalentity = other->movetarget = next; if (self->wait) { other->cast_info.pausetime = level.time + self->wait; // other->cast_info.stand (other); return; } if (!other->movetarget) { other->cast_info.pausetime = level.time + 100000000; // other->cast_info.stand (other); } else { VectorSubtract (other->goalentity->s.origin, other->s.origin, v); other->ideal_yaw = vectoyaw (v); } } void SP_path_corner (edict_t *self) { if (!self->targetname) { gi.dprintf ("path_corner with no targetname at %s\n", vtos(self->s.origin)); G_FreeEdict (self); return; } self->solid = SOLID_TRIGGER; self->touch = path_corner_touch; VectorSet (self->mins, -8, -8, -8); VectorSet (self->maxs, 8, 8, 8); self->svflags |= SVF_NOCLIENT; gi.linkentity (self); } // JOSEPH 8-FEB-99 /*UAKED viewthing (0 .5 .8) (-8 -8 -8) (8 8 8) Just for the debugging level. Don't use */ // END JOSEPH void TH_viewthing(edict_t *ent) { ent->s.frame = (ent->s.frame + 1) % 7; ent->nextthink = level.time + FRAMETIME; } void SP_viewthing(edict_t *ent) { gi.dprintf ("viewthing spawned\n"); ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.renderfx = RF_FRAMELERP; VectorSet (ent->mins, -16, -16, -24); VectorSet (ent->maxs, 16, 16, 32); ent->s.modelindex = gi.modelindex ("models/objects/banner/tris.md2"); gi.linkentity (ent); ent->nextthink = level.time + 0.5; ent->think = TH_viewthing; return; } /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for spotlights, etc. */ void SP_info_null (edict_t *self) { if (!self->targetname) // Ridah, 5-7-99, had to add this or the info_null behind the radio gets deleted G_FreeEdict (self); }; /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for lightning. */ void SP_info_notnull (edict_t *self) { VectorCopy (self->s.origin, self->absmin); VectorCopy (self->s.origin, self->absmax); }; #define MIN_LIGHT_VALUE 128 #define MIN_LIGHT_INTENSITY 0.2 #define START_OFF 1 #define FLARE 2 #define DYNAMIC 8 void AddLightSource(edict_t *self) { if (!(self->spawnflags & DYNAMIC)) return; if (level.num_light_sources >= MAX_LIGHT_SOURCES) { gi.dprintf("Warning: MAX_LIGHT_SOURCES exceeded\n"); return; } if (self->dmg_radius <= 0.0) self->dmg_radius = 1.0; if (self->light_level < MIN_LIGHT_VALUE) self->light_level = MIN_LIGHT_VALUE; VectorCopy( self->s.origin, level.light_orgs[level.num_light_sources] ); VectorCopy( self->rotate, level.light_colors[level.num_light_sources] ); // light_values[num_light_sources] = ((float) self->light_level) * 3.0; level.light_values[level.num_light_sources] = ((float) self->light_level * 1.5); // level.light_radius[level.num_light_sources] = self->dmg_radius; level.light_styles[level.num_light_sources] = self->style; level.num_light_sources++; self->svflags |= SVF_NOCLIENT; } void LightConfigstrings () { char str[256]; char fullstr[256]; int i, inc, j; fullstr[0] = str[0] = '\0'; inc = 0; j = 0; for (i=0; is.origin, vec ); // trivia reject // lights less that 100 are rejected by default done in sp_light { float length; length = VectorLength (vec); if (((level.light_values[i]) - length) < 0) continue; } if (!gi.inPVS(self->s.origin, level.light_orgs[i])) continue; { vec3_t spot1; vec3_t spot2; trace_t trace; VectorCopy (self->s.origin, spot1); spot1[2] += self->viewheight; VectorCopy (level.light_orgs[i], spot2); trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE); if (trace.fraction != 1.0) continue; } // passed normal tests. if we've used all lights, look for one to replace if (ent_lights >= MAX_MODEL_DIR_LIGHTS) { for (j=0; js.model_lighting.light_intensities[j] - VectorDistance( self->s.origin, self->s.model_lighting.light_orgs[j] )) < (level.light_values[i] - VectorDistance( self->s.origin, level.light_orgs[i] ))) { break; // replace this light } } if (j == MAX_MODEL_DIR_LIGHTS) continue; // no light to replace } else { j = ent_lights; } // end reject test self->s.model_lighting.light_indexes[j] = i; self->s.model_lighting.light_intensities[j] = level.light_values[i]; VectorCopy( level.light_colors[i], self->s.model_lighting.light_colors[j] ); VectorCopy( level.light_orgs[i], self->s.model_lighting.light_orgs[j] ); VectorNormalize( vec ); VectorCopy( vec, self->s.model_lighting.light_vecs[j] ); self->s.model_lighting.light_styles[j] = level.light_styles[i]; if (showlights->value) NAV_DrawLine( self->s.origin, level.light_orgs[i] ); if (ent_lights < MAX_MODEL_DIR_LIGHTS) ent_lights++; } if (showlights->value) gi.dprintf ("num lights %d\n", ent_lights); self->s.model_lighting.num_dir_lights = ent_lights; } /*QUAKED junior (0 1 0) (-8 -8 -8) (8 8 8) START_OFF */ static void junior_use (edict_t *self, edict_t *other, edict_t *activator) { if (self->spawnflags & START_OFF) { gi.configstring (CS_LIGHTS+self->style, "m"); self->spawnflags &= ~START_OFF; } else { gi.configstring (CS_LIGHTS+self->style, "a"); self->spawnflags |= START_OFF; } } void SP_junior (edict_t *self ) { if (self->style >= 32) { self->use = junior_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } self->spawnflags |= DYNAMIC; if (!(self->light_level)) self->light_level = 300; AddLightSource(self); } // ----------------------------------------------------------------------------- /*QUAKED lightflare (0 1 0) (-2 -2 -2) (2 2 2) START_OFF FLARE NORESIZE DYNAMIC Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 */ /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) START_OFF FLARE NORESIZE DYNAMIC Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 */ void SP_Flare (edict_t *self); static void light_use (edict_t *self, edict_t *other, edict_t *activator) { if (self->spawnflags & START_OFF) { gi.configstring (CS_LIGHTS+self->style, "m"); self->spawnflags &= ~START_OFF; } else { gi.configstring (CS_LIGHTS+self->style, "a"); self->spawnflags |= START_OFF; } } void SP_light (edict_t *self) { if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } // RAFAEL if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } } #define RESIZE 2 void SP_Flare (edict_t *self) { lightflare_t lf; // char lf_str[256], temp_str[256], zero_str[256];//, numstr[2]; // int i,j; if (num_flares == MAX_LIGHTFLARES-1) { gi.dprintf("Too many light flares, num_flares == MAX_LIGHTFLARES\n"); return; } if (!self->health) // size self->health = 24; if (!self->dmg) // type self->dmg = FLARE_NORMAL; if (self->dmg > 5) { gi.dprintf("Not a valid flare color 0,1,2,3,4,5 only\n"); return; } lf.lightstyle = (char)self->style; if (self->spawnflags & 4) lf.lightstyle |= RESIZE; lf.type = (char) self->dmg; lf.size = (float) self->health; VectorCopy( self->s.origin, lf.pos ); // NOTE to Rafael this just shows what sort of values are required to define a Sun-Flare /* { static int done_sun=0; if (!done_sun) { lf.type = FLARE_SUN; // use self->dmg in editor lf.size = 24; VectorSet( lf.pos, 1000, 1000, 3000 ); // if the mappers place the sunflare out near the extremes of the map, in the position of the sun, these values will be set accordingly (no need to specify manually) done_sun = true; } } */ if (lf.type == FLARE_SUN) VectorNormalize( lf.pos ); // just send it as a formatted string { char str[256]; sprintf( str, "%3i %1i %4.1f %4.1f %4.1f %3.0f", // "999 9 9999.9 9999.9 9999.9 999" length = 30 (int)lf.lightstyle, (int)lf.type, lf.pos[0], lf.pos[1], lf.pos[2], lf.size ); gi.configstring (CS_LIGHTFLARES + num_flares, str ); num_flares++; //gi.dprintf( "game flares: %i\n", num_flares ); } } // JOSEPH 12-MAY-99-B #define LIGHTB 16 #define LIGHTG 32 #define LIGHTR 64 #define LIGHTY 128 /*QUAKED light_sconce (1 1 1) (-4 -4 -8) (4 4 8) START_OFF FLARE NORESIZE DYNAMIC LIGHTB LIGHTG LIGHTR LIGHTY A sconce?? Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 model="models\props\sconce\light.md2" */ void SP_light_sconce (edict_t *self) { self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_PROP; if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } if (self->spawnflags & LIGHTB) { self->model = "models/props/sconce/lightb.md2"; } else if (self->spawnflags & LIGHTG) { self->model = "models/props/sconce/lightg.md2"; } else if (self->spawnflags & LIGHTR) { self->model = "models/props/sconce/lightr.md2"; } else if (self->spawnflags & LIGHTY) { self->model = "models/props/sconce/lighty.md2"; } else { self->model = "models/props/sconce/light.md2"; } self->s.modelindex = gi.modelindex (self->model); self->s.renderfx2 |= RF2_NOSHADOW; if ((self->light_level) && (!(self->spawnflags & LIGHTB))) { self->s.renderfx |= RF_FULLBRIGHT; self->flags |= RF_FULLBRIGHT; } gi.linkentity (self); } // END JOSEPH // JOSEPH 1-MAR-99 /*QUAKED light_bulb (1 1 1) (-2 -2 -5) (2 2 5) START_OFF FLARE NORESIZE DYNAMIC A bulb that outputs light Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 */ // END JOSEPH void SP_light_bulb (edict_t *self) { self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_PROP; if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } self->model = "models/props/light/tris.md2"; self->s.modelindex = gi.modelindex (self->model); self->s.renderfx2 |= RF2_NOSHADOW; if (self->light_level) { self->s.renderfx |= RF_FULLBRIGHT; self->flags |= RF_FULLBRIGHT; } gi.linkentity (self); } // END JOSEPH // JOSEPH 16-APR-99 /*QUAKED light_deco_sconce (1 1 1) (-8 -8 -12) (8 8 12) START_OFF FLARE NORESIZE DYNAMIC A deco sconce Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 model="models\props\decosconce\tris.md2" */ void SP_light_deco_sconce (edict_t *self) { self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_PROP; if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } self->model = "models/props/decosconce/tris.md2"; self->s.modelindex = gi.modelindex (self->model); self->s.renderfx2 |= RF2_NOSHADOW; if (self->light_level) { self->s.renderfx |= RF_FULLBRIGHT; self->flags |= RF_FULLBRIGHT; } gi.linkentity (self); } // JOSEPH 3-JUN-99 /*QUAKED light_chandelier (1 1 1) (-36 -34 -32) (36 34 32) START_OFF FLARE NORESIZE DYNAMIC A deco sconce Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 model="models\props\chandelier\tris.md2" */ void SP_light_chandelier (edict_t *self) { self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_PROP; if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } self->model = "models/props/chandelier/tris.md2"; self->s.modelindex = gi.modelindex (self->model); self->s.renderfx2 |= RF2_NOSHADOW; if (self->light_level) { self->s.renderfx |= RF_FULLBRIGHT; self->flags |= RF_FULLBRIGHT; } gi.linkentity (self); } // END JOSEPH /*QUAKED light_pendant (1 1 1) (-16 -16 -4) (16 16 4) START_OFF FLARE NORESIZE DYNAMIC A pendant light Non-displayed light. Default light value is 300. Default style is 0. If targeted, will toggle between on and off. Default _cone value is 10 (used to set size of light for spotlights) movedir will be the color of the flare health is size for lightflare dmg will specify the flare type FLARE_NORMAL 0 FLARE_SUN 1 FLARE_AMBER 2 FLARE_RED 3 FLARE_BLUE 4 FLARE_GREEN 5 model="models\props\pendant\tris.md2" */ void SP_light_pendant (edict_t *self) { self->solid = SOLID_NOT; self->movetype = MOVETYPE_NONE; self->svflags |= SVF_PROP; if (self->style >= 32) { self->use = light_use; if (self->spawnflags & START_OFF) gi.configstring (CS_LIGHTS+self->style, "a"); else gi.configstring (CS_LIGHTS+self->style, "m"); } if (self->spawnflags & FLARE) { SP_Flare (self); } // flares will never be a level.light_source else { if (!(self->light_level)) self->light_level = 300; if (self->light_level > 100) AddLightSource(self); } self->model = "models/props/pendant/tris.md2"; self->s.modelindex = gi.modelindex (self->model); self->s.renderfx2 |= RF2_NOSHADOW; if (self->light_level) { self->s.renderfx |= RF_FULLBRIGHT; self->flags |= RF_FULLBRIGHT; } gi.linkentity (self); } // END JOSEPH /*QUAKED func_wall (0 .5 .8) ? TRIGGER_SPAWN TOGGLE START_ON ANIMATED ANIMATED_FAST SURF2_ALPHA This is just a solid wall if not inhibited TRIGGER_SPAWN the wall will not be present until triggered it will then blink in to existance; it will kill anything that was in it's way TOGGLE only valid for TRIGGER_SPAWN walls this allows the wall to be turned on and off START_ON only valid for TRIGGER_SPAWN walls the wall will initially be present */ void func_wall_use (edict_t *self, edict_t *other, edict_t *activator) { if (self->solid == SOLID_NOT) { self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; KillBox (self); } else { self->solid = SOLID_NOT; self->svflags |= SVF_NOCLIENT; } gi.linkentity (self); if (!(self->spawnflags & 2)) self->use = NULL; } void SP_func_wall (edict_t *self) { self->movetype = MOVETYPE_PUSH; gi.setmodel (self, self->model); if (self->spawnflags & 8) self->s.effects |= EF_ANIM_ALL; if (self->spawnflags & 16) self->s.effects |= EF_ANIM_ALLFAST; // RAFAEL if (self->spawnflags & 32) self->s.renderfx2 |= RF2_SURF_ALPHA; // just a wall if ((self->spawnflags & 7) == 0) { self->solid = SOLID_BSP; gi.linkentity (self); return; } // it must be TRIGGER_SPAWN if (!(self->spawnflags & 1)) { // gi.dprintf("func_wall missing TRIGGER_SPAWN\n"); self->spawnflags |= 1; } // yell if the spawnflags are odd if (self->spawnflags & 4) { if (!(self->spawnflags & 2)) { gi.dprintf("func_wall START_ON without TOGGLE\n"); self->spawnflags |= 2; } } self->use = func_wall_use; if (self->spawnflags & 4) { self->solid = SOLID_BSP; } else { self->solid = SOLID_NOT; self->svflags |= SVF_NOCLIENT; } gi.linkentity (self); } /*QUAKED func_object (0 .5 .8) ? TRIGGER_SPAWN ANIMATED ANIMATED_FAST This is solid bmodel that will fall if it's support it removed. */ void func_object_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { // only squash thing we fall on top of if (!plane) return; if (plane->normal[2] < 1.0) return; if (other->takedamage == DAMAGE_NO) return; T_Damage (other, self, self, vec3_origin, self->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH); } void func_object_release (edict_t *self) { self->movetype = MOVETYPE_TOSS; self->touch = func_object_touch; } void func_object_use (edict_t *self, edict_t *other, edict_t *activator) { self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; self->use = NULL; KillBox (self); func_object_release (self); } void SP_func_object (edict_t *self) { gi.setmodel (self, self->model); self->mins[0] += 1; self->mins[1] += 1; self->mins[2] += 1; self->maxs[0] -= 1; self->maxs[1] -= 1; self->maxs[2] -= 1; if (!self->dmg) self->dmg = 100; if (self->spawnflags == 0) { self->solid = SOLID_BSP; self->movetype = MOVETYPE_PUSH; self->think = func_object_release; self->nextthink = level.time + 2 * FRAMETIME; } else { self->solid = SOLID_NOT; self->movetype = MOVETYPE_PUSH; self->use = func_object_use; self->svflags |= SVF_NOCLIENT; } if (self->spawnflags & 2) self->s.effects |= EF_ANIM_ALL; if (self->spawnflags & 4) self->s.effects |= EF_ANIM_ALLFAST; self->clipmask = MASK_MONSTERSOLID; gi.linkentity (self); } // JOSEPH 18-FEB-99 /*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST SURF2_ALPHA Any brush that you want to explode or break apart. If you want an explosion, set dmg and it will do a radius explosion of that amount at the center of the bursh. If targeted it will not be shootable. health defaults to 100. type - type of debris ("glass", "wood" or "metal") type default is "glass" mass defaults to 75. This determines how much debris is emitted when it explodes. You get one large chunk per 100 of mass (up to 8) and one small chunk per 25 of mass (up to 16). So 800 gives the most. "dmg" how much radius damage should be done, defaults to 0 "fxdensity" size of explosion 1 - 100 (default is 10) */ // END JOSEPH void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; PlayerNoise( attacker, attacker->s.origin, PNOISE_WEAPON ); // bmodel origins are (0 0 0), we need to adjust that here VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); VectorCopy (origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize (self->velocity); VectorScale (self->velocity, 250, self->velocity); // start chunks towards the center VectorScale (size, 0.5, size); mass = self->mass; if (!mass) mass = 75; // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) count = 8; if ((!self->type) || ((self->type) && (!strcmp(self->type, "glass")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/glass/glass1.md2", 1, chunkorigin); } } else if (((self->type) && (!strcmp(self->type, "wood")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/wood/wood1.md2", 1, chunkorigin); } } else if (((self->type) && (!strcmp(self->type, "metal")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/metal/metal1.md2", 1, chunkorigin); } } } // small chunks count = mass / 25; if (count > 16) count = 16; if ((!self->type) || ((self->type) && (!strcmp(self->type, "glass")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", 2, chunkorigin); } } else if (((self->type) && (!strcmp(self->type, "wood")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/wood/wood2.md2", 2, chunkorigin); } } else if (((self->type) && (!strcmp(self->type, "metal")))) { while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/props/metal/metal2.md2", 2, chunkorigin); } } G_UseTargets (self, attacker); if (self->dmg) BecomeExplosion1 (self); else G_FreeEdict (self); } // END JOSEPH void func_explosive_use(edict_t *self, edict_t *other, edict_t *activator) { func_explosive_explode (self, self, other, self->health, vec3_origin, 0, 0); } // JOSEPH 7-MAR-99 void func_explosive_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { if (!(other->svflags & SVF_MONSTER)) return; if (!(other->onfiretime > 0)) return; func_explosive_explode (self, self, other, self->health, vec3_origin, 0, 0); } // END JOSEPH void func_explosive_spawn (edict_t *self, edict_t *other, edict_t *activator) { self->solid = SOLID_BSP; self->svflags &= ~SVF_NOCLIENT; self->use = NULL; KillBox (self); gi.linkentity (self); } void SP_func_explosive (edict_t *self) { if (deathmatch->value) { // auto-remove for deathmatch G_FreeEdict (self); return; } self->movetype = MOVETYPE_PUSH; // JOSEPH 10-FEB-99 if (self->spawnflags & 8) self->s.renderfx2 |= RF2_SURF_ALPHA; // END JOSEPH gi.modelindex ("models/props/glass/glass1.md2"); gi.modelindex ("models/props/glass/glass2.md2"); gi.setmodel (self, self->model); if (self->spawnflags & 1) { self->svflags |= SVF_NOCLIENT; self->solid = SOLID_NOT; self->use = func_explosive_spawn; } else { self->solid = SOLID_BSP; if (self->targetname) self->use = func_explosive_use; } if (self->spawnflags & 2) self->s.effects |= EF_ANIM_ALL; if (self->spawnflags & 4) self->s.effects |= EF_ANIM_ALLFAST; if (self->use != func_explosive_use) { if (!self->health) self->health = 100; self->die = func_explosive_explode; self->takedamage = DAMAGE_YES; } // JOSEPH 7-MARCH-99 self->touch = func_explosive_touch; // END JOSEPH gi.linkentity (self); } // JOSEPH 8-FEB-99 /*UAKED misc_explobox (0 .5 .8) (-16 -16 0) (16 16 40) Large exploding box. You can override its mass (100), health (80), and dmg (150). */ // END JOSEPH void barrel_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { float ratio; vec3_t v; if ((!other->groundentity) || (other->groundentity == self)) return; ratio = (float)other->mass / (float)self->mass; VectorSubtract (self->s.origin, other->s.origin, v); M_walkmove (self, vectoyaw(v), 20 * ratio * FRAMETIME); } void barrel_explode (edict_t *self) { vec3_t org; float spd; vec3_t save; T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL); VectorCopy (self->s.origin, save); VectorMA (self->absmin, 0.5, self->size, self->s.origin); // a few big chunks spd = 1.5 * (float)self->dmg / 200.0; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass1.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass1.md2", spd, org); // bottom corners spd = 1.75 * (float)self->dmg / 200.0; VectorCopy (self->absmin, org); ThrowDebris (self, "models/props/glass/glass3.md2", spd, org); VectorCopy (self->absmin, org); org[0] += self->size[0]; ThrowDebris (self, "models/props/glass/glass3.md2", spd, org); VectorCopy (self->absmin, org); org[1] += self->size[1]; ThrowDebris (self, "models/props/glass/glass3.md2", spd, org); VectorCopy (self->absmin, org); org[0] += self->size[0]; org[1] += self->size[1]; ThrowDebris (self, "models/props/glass/glass3.md2", spd, org); // a bunch of little chunks spd = 2 * self->dmg / 200; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/props/glass/glass2.md2", spd, org); VectorCopy (save, self->s.origin); if (self->groundentity) BecomeExplosion2 (self); else BecomeExplosion1 (self); } void barrel_delay (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { self->takedamage = DAMAGE_NO; self->nextthink = level.time + 2 * FRAMETIME; self->think = barrel_explode; self->activator = attacker; } // JOSEPH 26-AUG-98 // JOSEPH TEMP void SP_props_trashcanA (edict_t *self); void SP_misc_explobox (edict_t *self) { // JOSEPH TEMP SP_props_trashcanA (self); return; // END JOSEPH if (deathmatch->value) { // auto-remove for deathmatch G_FreeEdict (self); return; } gi.modelindex ("models/props/glass/glass1.md2"); gi.modelindex ("models/props/glass/glass2.md2"); gi.modelindex ("models/props/glass/glass3.md2"); self->solid = SOLID_BBOX; self->movetype = MOVETYPE_STEP; self->model = "models/objects/barrels/tris.md2"; self->s.modelindex = gi.modelindex (self->model); VectorSet (self->mins, -16, -16, 0); VectorSet (self->maxs, 16, 16, 40); if (!self->mass) self->mass = 400; if (!self->health) self->health = 10; if (!self->dmg) self->dmg = 150; self->die = barrel_delay; self->takedamage = DAMAGE_YES; self->cast_info.aiflags = AI_NOSTEP; self->touch = barrel_touch; self->think = M_droptofloor; self->nextthink = level.time + 2 * FRAMETIME; gi.linkentity (self); } // JOSEPH 8-FEB-99 /*UAKED light_mine1 (0 1 0) (-2 -2 -12) (2 2 12) */ // END JOSEPH void SP_light_mine1 (edict_t *ent) { ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.modelindex = gi.modelindex ("models/objects/minelite/light1/tris.md2"); gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED light_mine2 (0 1 0) (-2 -2 -12) (2 2 12) */ // END JOSEPH void SP_light_mine2 (edict_t *ent) { ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_BBOX; ent->s.modelindex = gi.modelindex ("models/objects/minelite/light2/tris.md2"); gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED misc_gib_arm (1 0 0) (-8 -8 -8) (8 8 8) Intended for use with the target_spawner */ // END JOSEPH void SP_misc_gib_arm (edict_t *ent) { gi.setmodel (ent, "models/objects/gibs/arm/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random()*200; ent->avelocity[1] = random()*200; ent->avelocity[2] = random()*200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED misc_gib_leg (1 0 0) (-8 -8 -8) (8 8 8) Intended for use with the target_spawner */ // END JOSEPH void SP_misc_gib_leg (edict_t *ent) { gi.setmodel (ent, "models/objects/gibs/leg/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random()*200; ent->avelocity[1] = random()*200; ent->avelocity[2] = random()*200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED misc_gib_head (1 0 0) (-8 -8 -8) (8 8 8) Intended for use with the target_spawner */ // END JOSEPH void SP_misc_gib_head (edict_t *ent) { gi.setmodel (ent, "models/objects/gibs/head/tris.md2"); ent->solid = SOLID_NOT; ent->s.effects |= EF_GIB; ent->takedamage = DAMAGE_YES; ent->die = gib_die; ent->movetype = MOVETYPE_TOSS; ent->svflags |= SVF_MONSTER; ent->deadflag = DEAD_DEAD; ent->avelocity[0] = random()*200; ent->avelocity[1] = random()*200; ent->avelocity[2] = random()*200; ent->think = G_FreeEdict; ent->nextthink = level.time + 30; gi.linkentity (ent); } //===================================================== /*QUAKED target_character (0 0 1) ? used with target_string (must be on same "team") "count" is position in the string (starts at 1) */ void SP_target_character (edict_t *self) { self->movetype = MOVETYPE_PUSH; gi.setmodel (self, self->model); self->solid = SOLID_BSP; self->s.frame = 12; gi.linkentity (self); return; } /*QUAKED target_string (0 0 1) (-8 -8 -8) (8 8 8) */ void target_string_use (edict_t *self, edict_t *other, edict_t *activator) { edict_t *e; int n, l; char c; l = strlen(self->message); for (e = self->teammaster; e; e = e->teamchain) { if (!e->count) continue; n = e->count - 1; if (n > l) { e->s.frame = 12; continue; } c = self->message[n]; if (c >= '0' && c <= '9') e->s.frame = c - '0'; else if (c == '-') e->s.frame = 10; else if (c == ':') e->s.frame = 11; else e->s.frame = 12; } } void SP_target_string (edict_t *self) { if (!self->message) self->message = ""; self->use = target_string_use; } /*QUAKED func_clock (0 0 1) (-8 -8 -8) (8 8 8) TIMER_UP TIMER_DOWN START_OFF MULTI_USE target a target_string with this The default is to be a time of day clock TIMER_UP and TIMER_DOWN run for "count" seconds and the fire "pathtarget" If START_OFF, this entity must be used before it starts "style" 0 "xx" 1 "xx:xx" 2 "xx:xx:xx" */ #define CLOCK_MESSAGE_SIZE 16 // don't let field width of any clock messages change, or it // could cause an overwrite after a game load static void func_clock_reset (edict_t *self) { self->activator = NULL; if (self->spawnflags & 1) { self->health = 0; self->wait = self->count; } else if (self->spawnflags & 2) { self->health = self->count; self->wait = 0; } } static void func_clock_format_countdown (edict_t *self) { if (self->style == 0) { Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i", self->health); return; } if (self->style == 1) { Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i", self->health / 60, self->health % 60); if (self->message[3] == ' ') self->message[3] = '0'; return; } if (self->style == 2) { Com_sprintf(self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", self->health / 3600, (self->health - (self->health / 3600) * 3600) / 60, self->health % 60); if (self->message[3] == ' ') self->message[3] = '0'; if (self->message[6] == ' ') self->message[6] = '0'; return; } } void func_clock_think (edict_t *self) { if (!self->enemy) { self->enemy = G_Find (NULL, FOFS(targetname), self->target); if (!self->enemy) return; } if (self->spawnflags & 1) { func_clock_format_countdown (self); self->health++; } else if (self->spawnflags & 2) { func_clock_format_countdown (self); self->health--; } else { struct tm *ltime; time_t gmtime; time(&gmtime); ltime = localtime(&gmtime); Com_sprintf (self->message, CLOCK_MESSAGE_SIZE, "%2i:%2i:%2i", ltime->tm_hour, ltime->tm_min, ltime->tm_sec); if (self->message[3] == ' ') self->message[3] = '0'; if (self->message[6] == ' ') self->message[6] = '0'; } self->enemy->message = self->message; self->enemy->use (self->enemy, self, self); if (((self->spawnflags & 1) && (self->health > self->wait)) || ((self->spawnflags & 2) && (self->health < self->wait))) { if (self->pathtarget) { char *savetarget; char *savemessage; savetarget = self->target; savemessage = self->message; self->target = self->pathtarget; self->message = NULL; G_UseTargets (self, self->activator); self->target = savetarget; self->message = savemessage; } if (!(self->spawnflags & 8)) return; func_clock_reset (self); if (self->spawnflags & 4) return; } self->nextthink = level.time + 1; } void func_clock_use (edict_t *self, edict_t *other, edict_t *activator) { if (!(self->spawnflags & 8)) self->use = NULL; if (self->activator) return; self->activator = activator; self->think (self); } void SP_func_clock (edict_t *self) { if (!self->target) { gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin)); G_FreeEdict (self); return; } if ((self->spawnflags & 2) && (!self->count)) { gi.dprintf("%s with no count at %s\n", self->classname, vtos(self->s.origin)); G_FreeEdict (self); return; } if ((self->spawnflags & 1) && (!self->count)) self->count = 60*60;; func_clock_reset (self); self->message = gi.TagMalloc (CLOCK_MESSAGE_SIZE, TAG_LEVEL); self->think = func_clock_think; if (self->spawnflags & 4) self->use = func_clock_use; else self->nextthink = level.time + 1; } //================================================================================= void teleporter_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) { edict_t *dest; int i; if (!(other->client)) return; dest = G_Find (NULL, FOFS(targetname), self->target); if (!dest) { gi.dprintf ("Couldn't find destination\n"); return; } // unlink to make sure it can't possibly interfere with KillBox gi.unlinkentity (other); VectorCopy (dest->s.origin, other->s.origin); VectorCopy (dest->s.origin, other->s.old_origin); other->s.origin[2] += 10; // clear the velocity and hold them in place briefly VectorClear (other->velocity); other->client->ps.pmove.pm_time = 160>>3; // hold time other->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT; // draw the teleport splash at source and on the player self->owner->s.event = EV_PLAYER_TELEPORT; other->s.event = EV_PLAYER_TELEPORT; // set angles for (i=0 ; i<3 ; i++) other->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.angles[i] - other->client->resp.cmd_angles[i]); VectorClear (other->s.angles); VectorClear (other->client->ps.viewangles); VectorClear (other->client->v_angle); // kill anything at the destination KillBox (other); gi.linkentity (other); } /*QUAKED misc_teleporter (1 0 0) (-32 -32 -24) (32 32 -16) Stepping onto this disc will teleport players to the targeted misc_teleporter_dest object. */ void SP_misc_teleporter (edict_t *ent) { edict_t *trig; if (!ent->target) { gi.dprintf ("teleporter without a target.\n"); G_FreeEdict (ent); return; } gi.setmodel (ent, "models/objects/dmspot/tris.md2"); ent->s.skinnum = 1; ent->s.effects = EF_TELEPORTER; ent->s.sound = gi.soundindex ("world/amb10.wav"); ent->solid = SOLID_BBOX; VectorSet (ent->mins, -32, -32, -24); VectorSet (ent->maxs, 32, 32, -16); gi.linkentity (ent); trig = G_Spawn (); trig->touch = teleporter_touch; trig->solid = SOLID_TRIGGER; trig->target = ent->target; trig->owner = ent; VectorCopy (ent->s.origin, trig->s.origin); VectorSet (trig->mins, -8, -8, 8); VectorSet (trig->maxs, 8, 8, 24); gi.linkentity (trig); } /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16) Point teleporters at these. */ void SP_misc_teleporter_dest (edict_t *ent) { gi.setmodel (ent, "models/objects/dmspot/tris.md2"); ent->s.skinnum = 0; ent->solid = SOLID_BBOX; // ent->s.effects |= EF_FLIES; VectorSet (ent->mins, -32, -32, -24); VectorSet (ent->maxs, 32, 32, -16); gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED misc_amb4 (1 0 0) (-16 -16 -16) (16 16 16) Mal's amb4 loop entity */ // END JOSEPH static int amb4sound; void amb4_think (edict_t *ent) { ent->nextthink = level.time + 2.7; gi.sound(ent, CHAN_VOICE, amb4sound, 1, ATTN_NONE, 0); } void SP_misc_amb4 (edict_t *ent) { ent->think = amb4_think; ent->nextthink = level.time + 1; amb4sound = gi.soundindex ("world/amb4.wav"); gi.linkentity (ent); } // JOSEPH 8-FEB-99 /*UAKED misc_smoke (1 0 0) (-16 -16 -64) (16 16 64) ALPHA1 ALPHA2 ALPHA4 ALPHA6 ALPHA8 SCALE50 ALPHA1 = 0.1 ALPHA8 = 0.8 SCALE 50 will scale it down 50 percent */ // END JOSEPH void SP_misc_smoke (edict_t *ent) { ent->s.modelindex = gi.modelindex ("sprites/s_smoke4.sp2"); ent->s.effects |= EF_ANIM_ALLFAST; ent->s.renderfx |= RF_FACINGUP|RF_TRANSLUCENT; ent->s.renderfx2 |= ent->spawnflags; ent->movetype = MOVETYPE_NONE; //ent->solid = SOLID_BBOX; gi.linkentity (ent); } // Test vehicle models void dodgeviper_think( edict_t *self) { if ((self->s.angles[1] += 8) > 360) self->s.angles[1] -= 360; self->nextthink = level.time + 0.1; } void SP_misc_car (edict_t *self) { self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; VectorSet (self->mins, -16, -16, -24); VectorSet (self->maxs, 16, 16, 32); // temp for E3 demo, KP2 has characters in the ground while (!ValidBoxAtLoc(self->s.origin, self->mins, self->maxs, self, MASK_PLAYERSOLID)) { self->s.origin[2] += 16; gi.linkentity (self); } self->s.modelindex = gi.modelindex("models/vehicles/cars/viper/tris.md2"); self->s.renderfx |= RF_REFL_MAP; self->think = dodgeviper_think; self->nextthink = level.time + 0.1; } // JOSEPH 8-FEB-99 /*UAKED cast_punk_window (1 0 0) (-16 -16 -16) (16 16 16) */ // END JOSEPH static int windowsound; void usewindow (edict_t *ent, edict_t *other, edict_t *activator) { // ent->use = NULL; gi.sound(ent, CHAN_VOICE, windowsound , 1, ATTN_NONE, 0); } void thinkdeadwindow (edict_t *ent) { ent->s.frame++; if (ent->s.frame < 336) ent->nextthink = level.time + 0.1; } void window_die (edict_t *ent, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point, int mdx_part, int mdx_subobject) { ent->takedamage = DAMAGE_NO; ent->nextthink = level.time + FRAMETIME; ent->think = thinkdeadwindow; ent->activator = attacker; ent->s.frame = 322; } void thinkwindow (edict_t *ent) { ent->nextthink = level.time + 0.1; ent->s.frame++; if (ent->s.frame > 211) ent->s.frame = 0; } void SP_cast_punk_window (edict_t *ent) { ent->s.modelindex = gi.modelindex ("models/actors/fidelC/tris.md2"); ent->movetype = MOVETYPE_STEP; ent->solid = SOLID_BBOX; gi.linkentity (ent); ent->takedamage = DAMAGE_YES; // ent->cast_info.aiflags = AI_NOSTEP; ent->health = 1; ent->use = usewindow; ent->think = thinkwindow; ent->nextthink = level.time + 0.1; windowsound = gi.soundindex ("actors/specific/spec3.wav"); VectorSet (ent->mins, -16, -16, 0); VectorSet (ent->maxs, 16, 16, 40); ent->die = window_die; } /*QUAKED refl (1 0 0) (-16 -16 -16) (16 16 16) */ void SP_refl (edict_t *ent) { ent->s.modelindex = gi.modelindex ("models/vehicles/cars/viper/tris.md2"); ent->s.renderfx |= RF_REFL_MAP; ent->movetype = MOVETYPE_NONE; gi.linkentity (ent); } /*QUAKED elps (1 0 0) (-32 -32 -32) (32 32 32) test entity for mdx bbox hit test */ void elpsthink (edict_t *self) { self->nextthink = level.time + 0.1; self->s.angles[YAW] += 3.0; self->health = 1000; } void SP_elps (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/elps_seg/up.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/elps_seg/up.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/elps_seg/rt.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/elps_seg/rt.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/elps_seg/fwd.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/elps_seg/fwd.mdx", &self->s.model_parts[PART_BODY] ); self->s.num_parts++; self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/elps_seg/diag.mdx"); for (i=0; is.model_parts[PART_GUN].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/elps_seg/diag.mdx", &self->s.model_parts[PART_GUN] ); self->movetype = MOVETYPE_STEP; self->solid = SOLID_BBOX; gi.linkentity (self); self->takedamage = DAMAGE_YES; self->health = 1000; self->think = elpsthink; self->nextthink = level.time + 0.1; VectorSet (self->mins, -64, -64, -64); VectorSet (self->maxs, 64, 64, 64); } /*QUAKED misc_caustic (1 0 0) (-16 -16 -64) (16 16 64) ALPHA1 ALPHA2 ALPHA4 ALPHA6 ALPHA8 ALPHA1 = 0.1 ALPHA8 = 0.8 */ // Ridah, not used, removed RF2_CAUSTIC flag /* void SP_misc_caustic (edict_t *ent) { ent->s.modelindex = gi.modelindex ("sprites/s_smoke4.sp2"); ent->s.renderfx |= RF_TRANSLUCENT; ent->s.renderfx2 |= ent->spawnflags; ent->s.renderfx2 |= RF2_CAUSTIC; ent->movetype = MOVETYPE_NONE; gi.linkentity (ent); } */ /*QUAKED misc_rosie (1 .5 0) (-16 -16 -24) (16 16 32) */ void rosie_think (edict_t *self) { self->nextthink = level.time + 0.1; self->s.frame ++; if (self->s.frame > 390) self->s.frame = 0; } void SP_misc_rosie (edict_t *self) { int i; //self->s.origin[2] += 1; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/rosie_seg/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/rosie_seg/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/rosie_seg/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/rosie_seg/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/rosie_seg/body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/rosie_seg/body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); self->think = rosie_think; self->nextthink = level.time + 0.1; } /*QUAKED misc_fidelA (1 .5 0) (-16 -16 -24) (16 16 32) */ void fidelA_think (edict_t *self) { //return; self->nextthink = level.time + 0.1; self->s.frame ++; if (self->s.frame > 54) self->s.frame = 0; gi.dprintf ("frame: %d\n", self->s.frame); } void SP_misc_fidelA (edict_t *self) { int i; //return; //self->s.origin[2] += 1; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_mdx/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_mdx/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_mdx/lower_body.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_mdx/lower_body.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_mdx/upper_body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_mdx/upper_body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); self->think = fidelA_think; self->nextthink = level.time + 0.1; } #define ALARM_ON 1 #define ALARM_OFF 2 void Think_Alarm (edict_t *ent) { if (ent->wait < 0) { // just ring once /* if (ent->delay) { ent->delay --; ent->wait = ent->count; } else */ return; } gi.sound (ent, CHAN_VOICE, gi.soundindex ("world/alarm.wav"), 1, ATTN_NORM, 0); ent->nextthink = level.time + 4.8; ent->wait -= 4.8; } void Use_Alarm (edict_t *ent, edict_t *other, edict_t *activator) { ent->nextthink = level.time + 3; ent->think = Think_Alarm; if (ent->moveinfo.state == ALARM_OFF) { // gi.dprintf ("turning alarm on\n"); ent->moveinfo.state = ALARM_ON; ent->wait = ent->count; ent->delay = ent->speed; // Ridah, do some special handling here EP_EventScript( activator, "alarm" ); } else { // gi.dprintf ("turning alarm off\n"); ent->moveinfo.state = ALARM_OFF; ent->wait = -1; ent->delay = ent->speed; } } /*QUAKED misc_alarm (1 .5 0) (-8 -8 -8) (8 8 8) must be triggered to work count is the duration of the alarm ringing default is 60 sec */ void SP_misc_alarm (edict_t *ent) { ent->use = Use_Alarm; if (!(ent->count)) ent->count = 60; if (!ent->speed || ent->speed < 0) ent->delay = ent->speed = 3; ent->moveinfo.state = ALARM_OFF; // Ridah, NAV code only understands func_button's ent->classname = "func_button"; ent->name = "misc_alarm"; } /*QUAKED misc_corky_rosie_mdx (0 0 1) (-16 -16 -24) (16 16 34) */ void SP_misc_corky_rosie_mdx (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); } /*QUAKED misc_corky_fatguy_mdx (0 0 1) (-16 -16 -24) (16 16 34) */ void SP_misc_corky_fatguy_mdx (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fatguy_mdx/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fatguy_mdx/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fatguy_mdx/lower_body.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fatguy_mdx/lower_body.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fatguy_mdx/upper_body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fatguy_mdx/upper_body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); } /*QUAKED misc_corky_fidel_mdx_pcx (0 0 1) (-16 -16 -24) (16 16 34) */ /*QUAKED misc_corky_fidel_mdx_tga (0 0 1) (-16 -16 -24) (16 16 34) */ void SP_misc_corky_fidel_mdx_tga (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_tga/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_tga/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_tga/lower_body.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_tga/lower_body.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_tga/upper_body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_tga/upper_body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); } void SP_misc_corky_fidel_mdx_pcx (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/thug/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/thug/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/thug/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/thug/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/thug/body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/thug/body.mdx", &self->s.model_parts[PART_BODY] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; gi.linkentity (self); } /*QUAKED misc_barry_fidelc_maya (0 0 1) (-16 -16 -24) (16 16 34) */ /*QUAKED misc_barry_bitch (0 0 1) (-16 -16 -24) (16 16 34) */ void misc_barry_fidelc_maya_think (edict_t *self) { self->s.frame++; self->nextthink = level.time + 0.1; if (self->s.frame >= 761) self->s.frame = 0; gi.dprintf ("frame: %d\n", self->s.frame); } void SP_misc_barry_fidelc_maya (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx"); for (i=0; is.model_parts[PART_HEAD].baseskin = self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].baseskin = self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx"); for (i=0; is.model_parts[PART_BODY].baseskin = self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] ); /* int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/bitch/head.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/bitch/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/bitch/body.mdx"); for (i=0; is.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/body.mdx", &self->s.model_parts[PART_BODY] ); self->s.num_parts++; self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/bitch/gun.mdx"); for (i=0; is.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/bitch/gun.mdx", &self->s.model_parts[PART_GUN] ); */ /* memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/fidel_seg/head.mdx"); for (i=0; is.model_parts[PART_HEAD].baseskin = self->s.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_seg/head.mdx", &self->s.model_parts[PART_HEAD] ); self->s.num_parts++; self->s.model_parts[PART_LEGS].modelindex = gi.modelindex("models/actors/fidel_seg/legs.mdx"); for (i=0; is.model_parts[PART_LEGS].baseskin = self->s.model_parts[PART_LEGS].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_seg/legs.mdx", &self->s.model_parts[PART_LEGS] ); self->s.num_parts++; self->s.model_parts[PART_BODY].modelindex = gi.modelindex("models/actors/fidel_seg/body.mdx"); for (i=0; is.model_parts[PART_BODY].baseskin = self->s.model_parts[PART_BODY].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_seg/body.mdx", &self->s.model_parts[PART_BODY] ); self->s.num_parts++; self->s.model_parts[PART_GUN].modelindex = gi.modelindex("models/actors/fidel_seg/gun.mdx"); for (i=0; is.model_parts[PART_GUN].baseskin = self->s.model_parts[PART_GUN].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/fidel_seg/gun.mdx", &self->s.model_parts[PART_GUN] ); */ self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; self->think = misc_barry_fidelc_maya_think; self->nextthink = level.time + 0.1; gi.linkentity (self); } /*QUAKED misc_barry_cube_mdx (0 0 1) (-16 -16 -24) (16 16 34) */ void misc_barry_cube_mdx_think (edict_t *self) { self->s.frame++; self->nextthink = level.time + 0.1; if (self->s.frame >= 20) self->s.frame = 0; } void SP_misc_barry_cube_mdx (edict_t *self) { int i; memset(&(self->s.model_parts[0]), 0, sizeof(model_part_t) * MAX_MODEL_PARTS); self->s.num_parts++; self->s.model_parts[PART_HEAD].modelindex = gi.modelindex("models/actors/cube_mdx/cube.mdx"); for (i=0; is.model_parts[PART_HEAD].skinnum[i] = self->s.skinnum; gi.GetObjectBounds( "models/actors/cube_mdx/cube.mdx", &self->s.model_parts[PART_HEAD] ); self->movetype = MOVETYPE_NONE; self->s.renderfx2 |= RF2_DIR_LIGHTS; self->think = misc_barry_cube_mdx_think; self->nextthink = level.time + 0.1; gi.linkentity (self); } /*QUAKED path_attractor (.4 .3 .8) (-16 -16 -24) (16 16 48) When this is placed on the map it will attract actors by name from the episodic ai routines default delay is 10 sec. */ void SP_path_attractor (edict_t *self) { self->movetype = MOVETYPE_NONE; self->solid = SOLID_NOT; VectorSet (self->mins, -16, -16, -24); VectorSet (self->maxs, 16, 16, 48); if (!self->delay) self->delay = 10; AI_Ent_droptofloor( self ); } // JOSEPH 16-DEC-98 /*QUAKED misc_cut_scene (0 0 1) (-16 -16 -16) (16 16 16) */ /*void CutSceneThink (edict_t *ent) { ent->nextthink = level.time + 0.1; //if (level.time > (ent->nextthink + ent->wait)) // JOSEPH 7-DEC-98 if (!ent->wait--) EndCutScene (ent); } void CutSceneThinkEnd (edict_t *ent) { EndCutScene (ent); } void CutSceneThinkStart (edict_t *ent) { if (!ent->count) return; ent->count--; BeginCutScene (ent); // JOSEPH 7-DEC-98 ent->think = CutSceneThink; ent->nextthink = level.time + 0.1; } void Touch_CutScene (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { ent->think = CutSceneThinkStart; ent->nextthink = level.time + 0.1; } void SP_misc_cut_scene (edict_t *self) { VectorSet (self->mins, -16, -16, -16); VectorSet (self->maxs, 16, 16, 16); self->solid = SOLID_TRIGGER; self->touch = Touch_CutScene; self->svflags |= SVF_NOCLIENT; gi.linkentity (self); if (!self->wait) self->wait = 60; self->wait=10; // just for now self->count = 1; }*/ // JOSEPH 25-FEB-99 /*QUAKED misc_cutscene_trigger (0 0 1) ? Targets a misc_cutscene_camera target - camera to target debugprint - set to 1 to print out camera end position and angles duration - fade in time. */ // END JOSEPH // JOSEPH 19-MAR-99-B /*QUAKED misc_cutscene_camera (0 0 1) (-16 -16 -16) (16 16 16) Camera to be targeted from a misc_cutscene_trigger targetname - camera target ID cameraorigin - X Y Z camera start position cameraangle - X Y Z start angles rotate - X Y Z rotational velocity during cut scene cameravel - [forward] [right] [up] speed to move from initial angle. cameravelrel - [forward] [right] [up] speed to move relative to current frame angle. wait - cut scene length in seconds (default 5) target - next camera to target. target2 - [NOT WORKING!!] camera angle points to this entity (overides other angle commands) deadticks - fov for this camera; duration - fade out time. reactdelay - time into camera to start fading out */ // END JOSEPH void CutSceneThink (edict_t *ent) { if (!ent->wait--) { { edict_t *e; int i; int found = 0; // Print end position if required if (ent->debugprint) { // JOSEPH 24-FEB-99 vec3_t neworigin; VectorCopy(ent->target_ent->s.origin, neworigin); neworigin[2] += 40; gi.dprintf("Camera \"%s\" end position %s\n", ent->target_ent->targetname, vtos(neworigin)); gi.dprintf("Camera \"%s\" end angles %s\n", ent->target_ent->targetname, vtos(ent->target_ent->s.angles)); // END JOSEPH } // Restore camera start position and angle VectorCopy(ent->target_ent->save_avel, ent->target_ent->s.angles); VectorCopy(ent->target_ent->savecameraorigin, ent->target_ent->s.origin); // No more cameras if (!ent->target_ent->target) EndCutScene (ent->target_ent); // Find next target entity camera if (ent->target_ent->target) { for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) { if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target))) { ent->target_ent = e; found = 1; break; } } } // Next camera if (found) { // See if a target2 can be found if (ent->target_ent->target2) { for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) { if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target2))) { ent->target_ent->target2_ent = e; break; } } } ent->wait = ent->target_ent->wait*10.0; if (!ent->wait) ent->wait = 5.0*10.0; NewCutSceneCamera (ent->target_ent); ent->nextthink = level.time + 0.1; // Ridah, Camera accel/decel ent->target_ent->timestamp = level.time; ent->target_ent->speed = 0; // Ridah, play a sound if there is one if (ent->target_ent->name) { edict_t *talkent; if (!ent->target_ent->sight_target) { //gi.dprintf( "Warning: cutsecene camera has voice sound without a \"sight_target\" (speaking charecter's name)\n" ); gi.positioned_sound( ent->target_ent->s.origin, ent->target_ent, CHAN_AUTO, gi.soundindex( ent->target_ent->name ), 1.0, ATTN_NONE, 0); } else { talkent = EP_GetCharacterByName( ent->target_ent->sight_target ); gi.sound( talkent, CHAN_VOICE, gi.soundindex( ent->target_ent->name ), 1.0, 1, 0); } } // Ridah, scripting if (ent->target_ent->scriptname) { EP_EventScript( &g_edicts[1], ent->target_ent->scriptname ); } return; } } // Ridah, scripting if (ent->scriptname) { EP_EventScript( &g_edicts[1], ent->scriptname ); } // No more cameras or target camera not found ent->think = 0; EndCutScene (ent->target_ent); } else { // Process camera frame AdjustCutSceneCamera(ent->target_ent); ent->nextthink = level.time + 0.1; } } void CutSceneThinkStart (edict_t *ent) { edict_t *e; int i; if (!ent->count) return; // JOSEPH 19-MAR-99-B if (ent->duration) { level.inversefade = 0; level.totalfade = ent->duration; level.fadeendtime = level.time + level.totalfade; } // END JOSEPH ent->count--; // Find target entity ent->target_ent = 0; if (ent->target) { for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) { if ((e->targetname) && (!strcmp(e->targetname, ent->target))) { ent->target_ent = e; break; } } } if (!ent->target_ent) return; // See if a target2 can be found if (ent->target_ent->target2) { for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) { if ((e->targetname) && (!strcmp(e->targetname, ent->target_ent->target2))) { ent->target_ent->target2_ent = e; break; } } } // Ridah, Camera accel/decel ent->target_ent->timestamp = level.time; ent->target_ent->speed = 0; // Ridah, play a sound if there is one if (ent->target_ent->name) { edict_t *talkent; if (!ent->target_ent->sight_target) { //gi.dprintf( "Warning: cutsecene camera has voice sound without a \"sight_target\" (speaking charecter's name)\n" ); gi.positioned_sound( ent->target_ent->s.origin, ent->target_ent, CHAN_AUTO, gi.soundindex( ent->target_ent->name ), 1.0, ATTN_NONE, 0); } else { talkent = EP_GetCharacterByName( ent->target_ent->sight_target ); gi.sound( talkent, CHAN_VOICE, gi.soundindex( ent->target_ent->name ), 1.0, 1, 0); } } // Ridah, scripting if (ent->target_ent->scriptname) { EP_EventScript( &g_edicts[1], ent->target_ent->scriptname ); } ent->wait = ent->target_ent->wait*10.0; if (!ent->wait) ent->wait = 5.0*5.0; BeginCutScene (ent->target_ent); ent->think = CutSceneThink; ent->nextthink = level.time + 0.1; } void Touch_CutScene (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) { CutSceneThinkStart(ent); } // JOSEPH 17-MAR-99-B void Use_CutScene (edict_t *self, edict_t *other, edict_t *activator) { CutSceneThinkStart(self); } // END JOSEPH // OLD void SP_misc_cut_scene (edict_t *self) { } // JOSEPH 2-MAR-99 void PrecacheCutStuff (char* s) { char *start; char data[MAX_QPATH]; int len; if (!s || !s[0]) return; // parse the space seperated precache string while (*s) { start = s; while (*s && *s != ' ') s++; len = s-start; if (len >= MAX_QPATH || len < 5) gi.error ("Bad precache string"); memcpy (data, start, len); data[len] = 0; if (*s) s++; // determine type based on extension if (!strcmp(data+len-3, "md2")) gi.modelindex (data); else if (!strcmp (data+len-3, "mdx")) gi.modelindex (data); else if (!strcmp(data+len-3, "wav")) gi.soundindex (data); if (!strcmp(data+len-3, "pcx")) gi.imageindex (data); } } // JOSEPH 19-MAR-99-B void SP_misc_cutscene_trigger (edict_t *self) { self->solid = SOLID_TRIGGER; gi.setmodel (self, self->model); self->svflags |= SVF_NOCLIENT; if (self->targetname) { self->use = Use_CutScene; } else { self->touch = Touch_CutScene; } if (self->duration) { level.inversefade = 0; level.totalfade = 60.0; level.fadeendtime = level.time + level.totalfade; } gi.linkentity (self); self->count = 1; PrecacheCutStuff (self->name); } // END JOSEPH // JOSEPH 19-MAR-99-B /*QUAKED misc_use_cutscene (.5 .5 .5) (-8 -8 -8) (8 8 8) This fixed size trigger targets a misc_cutscene_camera target - camera to target debugprint - set to 1 to print out camera end position and angles duration - fade in time. */ void SP_misc_use_cutscene (edict_t *self) { self->use = Use_CutScene; if (self->duration) { level.inversefade = 0; level.totalfade = 60.0; level.fadeendtime = level.time + level.totalfade; } self->count = 1; PrecacheCutStuff (self->name); } // END JOSEPH void SP_misc_cutscene_camera (edict_t *self) { VectorSet (self->mins, -16, -16, -16); VectorSet (self->maxs, 16, 16, 16); VectorClear(self->s.angles); if ((self->cameraorigin[0] != 0) || (self->cameraorigin[1] != 0) || (self->cameraorigin[2] != 0)) { VectorCopy(self->cameraorigin, self->s.origin); self->s.origin[2] -= 40; } else { self->s.origin[0] += 8; self->s.origin[1] += 8; self->s.origin[2] += 8; } self->solid = SOLID_NOT; self->svflags |= SVF_NOCLIENT; gi.linkentity (self); if (!self->deadticks) self->deadticks = 90; PrecacheCutStuff (self->name); } // END JOSEPH /*QUAKED sfx_beacon (0 0 1) (-16 -16 -16) (16 16 16) */ void beacon_think (edict_t *ent) { edict_t *ignore; vec3_t start; vec3_t end; trace_t tr; vec3_t forward; ignore = ent; ent->s.angles[YAW] += anglemod(level.time * 0.1); AngleVectors (ent->s.angles, forward, NULL, NULL ); VectorCopy (ent->s.origin, start); VectorMA (start, 2048, forward, end); VectorCopy (forward, ent->movedir); tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER); VectorCopy (tr.endpos, ent->s.old_origin); ent->nextthink = level.time + FRAMETIME; } void SP_sfx_beacon (edict_t *ent) { ent->movetype = MOVETYPE_NONE; ent->solid = SOLID_NOT; ent->s.modelindex = 1; ent->s.renderfx |= RF_BEAM; ent->s.renderfx2 |= RF2_BEAM2; ent->s.frame = 64; ent->s.effects |= EF_ROTATE; ent->nextthink = level.time + 0.1; ent->think = beacon_think; ent->s.skinnum = 0xf2f2f0f0; gi.linkentity (ent); }