/* ================= fire_lockblob ================= */ gentity_t *fire_lockblob( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; bolt = G_NewEntity(); bolt->classname = "lockblob"; bolt->pointAgainstWorld = qtrue; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_LOCKBLOB_LAUNCHER; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 0; bolt->flightSplashDamage = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_UNKNOWN; //doesn't do damage so will never kill bolt->clipmask = MASK_SHOT; bolt->target = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, LOCKBLOB_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
/* ================= fire_bounceBall ================= */ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; bolt = G_NewEntity(); bolt->classname = "bounceball"; bolt->pointAgainstWorld = qtrue; bolt->nextthink = level.time + 3000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_ALEVEL3_UPG; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = LEVEL3_BOUNCEBALL_DMG; bolt->flightSplashDamage = 0; bolt->splashDamage = LEVEL3_BOUNCEBALL_DMG; bolt->splashRadius = LEVEL3_BOUNCEBALL_RADIUS; bolt->methodOfDeath = MOD_LEVEL3_BOUNCEBALL; bolt->splashMethodOfDeath = MOD_LEVEL3_BOUNCEBALL; bolt->clipmask = MASK_SHOT; bolt->target = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, LEVEL3_BOUNCEBALL_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
/* ================= fire_pulseRifle ================= */ gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; bolt = G_NewEntity(); bolt->classname = "pulse"; bolt->pointAgainstWorld = qtrue; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_PULSE_RIFLE; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = PRIFLE_DMG; bolt->flightSplashDamage = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_PRIFLE; bolt->splashMethodOfDeath = MOD_PRIFLE; bolt->clipmask = MASK_SHOT; bolt->target = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -PRIFLE_SIZE; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = PRIFLE_SIZE; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, PRIFLE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
/* ================= fire_hive ================= */ gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; bolt = G_NewEntity(); bolt->classname = "hive"; bolt->pointAgainstWorld = qfalse; bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD; bolt->think = AHive_SearchAndDestroy; bolt->s.eType = ET_MISSILE; bolt->s.eFlags |= EF_BOUNCE | EF_NO_BOUNCE_SOUND; bolt->s.weapon = WP_HIVE; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = HIVE_DMG; bolt->flightSplashDamage = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_SWARM; bolt->clipmask = MASK_SHOT; bolt->target = self->target; bolt->timestamp = level.time + HIVE_LIFETIME; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, HIVE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
/* ================= fire_luciferCannon ================= */ gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius, int speed ) { gentity_t *bolt; float charge; bolt = G_NewEntity(); bolt->classname = "lcannon"; bolt->pointAgainstWorld = qtrue; if ( damage == LCANNON_DAMAGE ) { bolt->nextthink = level.time; } else { bolt->nextthink = level.time + 10000; } bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_LUCIFER_CANNON; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = damage; bolt->flightSplashDamage = 0; bolt->splashDamage = damage / 2; bolt->splashRadius = radius; bolt->methodOfDeath = MOD_LCANNON; bolt->splashMethodOfDeath = MOD_LCANNON_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target = NULL; // Give the missile a small bounding box bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -LCANNON_SIZE; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = -bolt->r.mins[ 0 ]; // Pass the missile charge through charge = ( float )( damage - LCANNON_SECONDARY_DAMAGE ) / LCANNON_DAMAGE; bolt->s.torsoAnim = charge * 255; if ( bolt->s.torsoAnim < 0 ) { bolt->s.torsoAnim = 0; } bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, speed, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); // Com_Printf("Luciball power = %d, speed = %d/s.\n", damage, speed); return bolt; }
static void CreateNewZap( gentity_t *creator, gentity_t *target ) { int i; zap_t *zap; for ( i = 0; i < MAX_ZAPS; i++ ) { zap = &zaps[ i ]; if ( zap->used ) { continue; } zap->used = qtrue; zap->timeToLive = LEVEL2_AREAZAP_TIME; zap->creator = creator; zap->targets[ 0 ] = target; zap->numTargets = 1; // the zap chains only through living entities if ( target->health > 0 ) { G_Damage( target, creator, creator, forward, target->s.origin, LEVEL2_AREAZAP_DMG, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP ); FindZapChainTargets( zap ); for ( i = 1; i < zap->numTargets; i++ ) { G_Damage( zap->targets[ i ], target, zap->creator, forward, target->s.origin, LEVEL2_AREAZAP_DMG * ( 1 - powf( ( zap->distances[ i ] / LEVEL2_AREAZAP_CHAIN_RANGE ), LEVEL2_AREAZAP_CHAIN_FALLOFF ) ) + 1, DAMAGE_NO_KNOCKBACK | DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP ); } } zap->effectChannel = G_NewEntity(); zap->effectChannel->s.eType = ET_LEV2_ZAP_CHAIN; zap->effectChannel->classname = "lev2zapchain"; UpdateZapEffect( zap ); return; } }
void G_SpawnFakeEntities() { level.fakeLocation = G_NewEntity(); level.fakeLocation->s.origin[ 0 ] = level.fakeLocation->s.origin[ 1 ] = level.fakeLocation->s.origin[ 2 ] = 1.7e19f; // well out of range level.fakeLocation->message = nullptr; level.fakeLocation->s.eType = entityType_t::ET_LOCATION; level.fakeLocation->r.svFlags = SVF_BROADCAST; level.fakeLocation->nextPathSegment = level.locationHead; level.fakeLocation->s.generic1 = G_LocationIndex( "" ); level.locationHead = level.fakeLocation; G_SetOrigin( level.fakeLocation, level.fakeLocation->s.origin ); }
static void CreateNewZap( gentity_t *creator, gentity_t *target ) { int i; zap_t *zap; for ( i = 0; i < MAX_ZAPS; i++ ) { zap = &zaps[ i ]; if ( zap->used ) { continue; } zap->used = true; zap->timeToLive = LEVEL2_AREAZAP_TIME; zap->creator = creator; zap->targets[ 0 ] = target; zap->numTargets = 1; // Zap chains only originate from alive entities. if (target->entity->Damage((float)LEVEL2_AREAZAP_DMG, creator, Vec3::Load(target->s.origin), Vec3::Load(forward), DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP)) { FindZapChainTargets( zap ); for ( i = 1; i < zap->numTargets; i++ ) { float damage = LEVEL2_AREAZAP_DMG * ( 1 - powf( ( zap->distances[ i ] / LEVEL2_AREAZAP_CHAIN_RANGE ), LEVEL2_AREAZAP_CHAIN_FALLOFF ) ) + 1; target->entity->Damage(damage, zap->creator, Vec3::Load(target->s.origin), Vec3::Load(forward), DAMAGE_NO_LOCDAMAGE, MOD_LEVEL2_ZAP); } } zap->effectChannel = G_NewEntity(); zap->effectChannel->s.eType = ET_LEV2_ZAP_CHAIN; zap->effectChannel->classname = "lev2zapchain"; UpdateZapEffect( zap ); return; } }
/* ================= G_NewTempEntity Spawns an event entity that will be auto-removed The origin will be snapped to save net bandwidth, so care must be taken if the origin is right on a surface (snap towards start vector first) ================= */ gentity_t *G_NewTempEntity( const vec3_t origin, int event ) { gentity_t *newEntity; vec3_t snapped; newEntity = G_NewEntity(); newEntity->s.eType = (entityType_t) ( ET_EVENTS + event ); newEntity->classname = "tempEntity"; newEntity->eventTime = level.time; newEntity->freeAfterEvent = true; VectorCopy( origin, snapped ); SnapVector( snapped ); // save network bandwidth G_SetOrigin( newEntity, snapped ); // find cluster for PVS trap_LinkEntity( newEntity ); return newEntity; }
/** * @brief Create a new ET_BEACON entity. * @param conflictHandler How to handle existing similiar beacons. * @return A pointer to the new entity. */ gentity_t *New( const vec3_t origin, beaconType_t type, int data, team_t team, int owner, beaconConflictHandler_t conflictHandler ) { gentity_t *ent; int decayTime; switch( conflictHandler ) { case BCH_MOVE: return MoveSimilar( origin, origin, type, data, team, owner ); case BCH_REMOVE: RemoveSimilar( origin, type, data, team, owner ); break; default: break; } ent = G_NewEntity( ); ent->s.eType = ET_BEACON; ent->classname = "beacon"; ent->s.bc_type = type; ent->s.bc_data = data; ent->s.bc_team = team; ent->s.bc_owner = owner; ent->think = Think; ent->nextthink = level.time; ent->s.bc_ctime = level.time; ent->s.bc_mtime = level.time; decayTime = BG_Beacon( type )->decayTime; ent->s.bc_etime = ( decayTime ? level.time + decayTime : 0 ); ent->s.pos.trType = TR_INTERPOLATE; Move( ent, origin ); return ent; }
/* ================= launch_grenade ================= */ gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; bolt = G_NewEntity(); bolt->classname = "grenade"; bolt->pointAgainstWorld = qfalse; bolt->nextthink = level.time + 5000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_GRENADE; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = GRENADE_DAMAGE; bolt->flightSplashDamage = 0; bolt->splashDamage = GRENADE_DAMAGE; bolt->splashRadius = GRENADE_RANGE; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE; bolt->clipmask = MASK_SHOT; bolt->target = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f; bolt->s.time = level.time; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, GRENADE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); trap_SendServerCommand( self - g_entities, "vcommand grenade" ); return bolt; }
/* ================= fire_flamer ================= */ gentity_t *fire_flamer( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; vec3_t pvel; bolt = G_NewEntity(); bolt->classname = "flame"; bolt->pointAgainstWorld = qfalse; bolt->nextthink = level.time + FLAMER_LIFETIME; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_FLAMER; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = FLAMER_DMG; bolt->flightSplashDamage = FLAMER_FLIGHTSPLASHDAMAGE; bolt->splashDamage = FLAMER_SPLASHDAMAGE; bolt->splashRadius = FLAMER_RADIUS; bolt->methodOfDeath = MOD_FLAMER; bolt->splashMethodOfDeath = MOD_FLAMER_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -FLAMER_SIZE; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = FLAMER_SIZE; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( self->client->ps.velocity, FLAMER_LAG, pvel ); VectorMA( pvel, FLAMER_SPEED, dir, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
/* =================== G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from level.spawnVars[], then call the class specfic spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { int i, j; gentity_t *spawningEntity; // get the next free entity spawningEntity = G_NewEntity(); for ( i = 0; i < level.numSpawnVars; i++ ) { G_ParseField( level.spawnVars[ i ][ 0 ], level.spawnVars[ i ][ 1 ], spawningEntity ); } if(G_SpawnBoolean( "nop", qfalse ) || G_SpawnBoolean( "notunv", qfalse )) { G_FreeEntity( spawningEntity ); return; } /* * will have only the classname or missing it… * both aren't helping us and might even create a error later * in the server, where we dont know as much anymore about it, * so we fail rather here, so mappers have a chance to remove it */ if( level.numSpawnVars <= 1 ) { G_Error( S_ERROR "encountered ghost-entity #%i with only one field: %s = %s\n", spawningEntity->s.number, level.spawnVars[ 0 ][ 0 ], level.spawnVars[ 0 ][ 1 ] ); } // move editor origin to pos VectorCopy( spawningEntity->s.origin, spawningEntity->s.pos.trBase ); VectorCopy( spawningEntity->s.origin, spawningEntity->r.currentOrigin ); // don't leave any "gaps" between multiple names j = 0; for (i = 0; i < MAX_ENTITY_ALIASES; ++i) { if (spawningEntity->names[i]) spawningEntity->names[j++] = spawningEntity->names[i]; } spawningEntity->names[ j ] = NULL; /* * for backward compatbility, since before targets were used for calling, * we'll have to copy them over to the called-targets as well for now */ if(!spawningEntity->callTargetCount) { for (i = 0; i < MAX_ENTITY_TARGETS && i < MAX_ENTITY_CALLTARGETS; i++) { if (!spawningEntity->targets[i]) continue; spawningEntity->calltargets[i].event = "target"; spawningEntity->calltargets[i].eventType = ON_DEFAULT; spawningEntity->calltargets[i].actionType = ECA_DEFAULT; spawningEntity->calltargets[i].name = spawningEntity->targets[i]; spawningEntity->callTargetCount++; } } // don't leave any "gaps" between multiple targets j = 0; for (i = 0; i < MAX_ENTITY_TARGETS; ++i) { if (spawningEntity->targets[i]) spawningEntity->targets[j++] = spawningEntity->targets[i]; } spawningEntity->targets[ j ] = NULL; // if we didn't get necessary fields (like the classname), don't bother spawning anything if ( !G_CallSpawnFunction( spawningEntity ) ) { G_FreeEntity( spawningEntity ); } }
/* ============= SpawnCorpse A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ static void SpawnCorpse( gentity_t *ent ) { gentity_t *body; int contents; vec3_t origin, mins; VectorCopy( ent->r.currentOrigin, origin ); trap_UnlinkEntity( ent ); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( origin, -1 ); if ( contents & CONTENTS_NODROP ) { return; } body = G_NewEntity(); VectorCopy( ent->s.apos.trBase, body->s.angles ); body->s.eFlags = EF_DEAD; body->s.eType = entityType_t::ET_CORPSE; body->timestamp = level.time; body->s.event = 0; body->r.contents = CONTENTS_CORPSE; body->clipmask = MASK_DEADSOLID; body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ]; body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL; if ( ent->client->pers.team == TEAM_HUMANS ) { body->classname = "humanCorpse"; } else { body->classname = "alienCorpse"; } body->s.misc = MAX_CLIENTS; body->think = BodySink; body->nextthink = level.time + 20000; body->s.legsAnim = ent->s.legsAnim; if ( !body->nonSegModel ) { switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } } else { switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case NSPA_DEATH1: case NSPA_DEAD1: body->s.legsAnim = NSPA_DEAD1; break; case NSPA_DEATH2: case NSPA_DEAD2: body->s.legsAnim = NSPA_DEAD2; break; case NSPA_DEATH3: case NSPA_DEAD3: default: body->s.legsAnim = NSPA_DEAD3; break; } } //change body dimensions BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], mins, nullptr, nullptr, body->r.mins, body->r.maxs ); //drop down to match the *model* origins of ent and body origin[2] += mins[ 2 ] - body->r.mins[ 2 ]; G_SetOrigin( body, origin ); body->s.pos.trType = trType_t::TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); trap_LinkEntity( body ); }
gentity_t *G_SpawnMissile( missile_t missile, gentity_t *parent, vec3_t start, vec3_t dir, gentity_t *target, void ( *think )( gentity_t *self ), int nextthink ) { gentity_t *m; const missileAttributes_t *ma; vec3_t velocity; if ( !parent ) { return nullptr; } ma = BG_Missile( missile ); m = G_NewEntity(); // generic m->s.eType = ET_MISSILE; m->s.modelindex = missile; m->r.ownerNum = parent->s.number; m->parent = parent; m->target = target; m->think = think; m->nextthink = nextthink; // from attribute config file m->s.weapon = ma->number; m->classname = ma->name; m->pointAgainstWorld = ma->pointAgainstWorld; m->damage = ma->damage; m->methodOfDeath = ma->meansOfDeath; m->splashDamage = ma->splashDamage; m->splashRadius = ma->splashRadius; m->splashMethodOfDeath = ma->splashMeansOfDeath; m->clipmask = ma->clipmask; m->r.mins[ 0 ] = m->r.mins[ 1 ] = m->r.mins[ 2 ] = -ma->size; m->r.maxs[ 0 ] = m->r.maxs[ 1 ] = m->r.maxs[ 2 ] = ma->size; m->s.eFlags = ma->flags; // not yet implemented / deprecated m->flightSplashDamage = 0; m->flightSplashRadius = 0; // trajectory { // set trajectory type m->s.pos.trType = ma->trajectoryType; // move a bit on the first frame m->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // set starting point VectorCopy( start, m->s.pos.trBase ); VectorCopy( start, m->r.currentOrigin ); // set speed VectorScale( dir, ma->speed, velocity ); // add lag if ( ma->lag && parent->client ) { VectorMA( velocity, ma->lag, parent->client->ps.velocity, velocity ); } // copy velocity VectorCopy( velocity, m->s.pos.trDelta ); // save net bandwidth SnapVector( m->s.pos.trDelta ); } return m; }