//---------------------------------------------------------- void fx_explosion_trail_link( gentity_t *ent ) { vec3_t dir; gentity_t *target = NULL; // we ony activate when used ent->e_UseFunc = useF_fx_explosion_trail_use; if ( ent->target ) { // try to use the target to override the orientation target = G_Find( target, FOFS(targetname), ent->target ); if ( !target ) { gi.Printf( S_COLOR_RED"ERROR: fx_explosion_trail %s could not find target %s\n", ent->targetname, ent->target ); G_FreeEntity( ent ); return; } // Our target is valid so lets use that VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); } else { // we are assuming that we have angles, but there are no checks to verify this AngleVectors( ent->s.angles, dir, NULL, NULL ); } // NOTE: this really isn't an angle, but rather an orientation vector G_SetAngles( ent, dir ); }
//----------------------------------------------------- void SP_misc_turret( gentity_t *base ) //----------------------------------------------------- { base->s.modelindex2 = G_ModelIndex( "models/map_objects/hoth/turret_bottom.md3" ); base->s.modelindex = G_ModelIndex( "models/map_objects/hoth/turret_base.md3" ); //base->playerModel = gi.G2API_InitGhoul2Model( base->ghoul2, "models/map_objects/imp_mine/turret_canon.glm", base->s.modelindex ); //base->s.radius = 80.0f; //gi.G2API_SetBoneAngles( &base->ghoul2[base->playerModel], "Bone_body", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL ); //base->torsoBolt = gi.G2API_AddBolt( &base->ghoul2[base->playerModel], "*flash03" ); G_SetAngles( base, base->s.angles ); G_SetOrigin( base, base->s.origin ); base->r.contents = CONTENTS_BODY; VectorSet( base->r.maxs, 32.0f, 32.0f, 128.0f ); VectorSet( base->r.mins, -32.0f, -32.0f, 0.0f ); base->use = turret_base_use; base->think = turret_base_think; // don't start working right away base->nextthink = level.time + FRAMETIME * 5; trap_LinkEntity( base ); if ( !turret_base_spawn_top( base ) ) { G_FreeEntity( base ); } }
//[/CoOp] //----------------------------------------------------- void SP_misc_turret( gentity_t *base ) //----------------------------------------------------- { char* s; //[CoOp] //wahoo turret - actually a spawn flag of 5 spawns a huge space turbo turret //and others spawn the normal g2 turret so fixed this if statement. In sp, spawnflags //are checked for the value of 5 to spawn the turbo turrets, but in mp it checks for //a spawn flag of 8 in sp_misc_turretg2, so setting to 8 before spawning. No hoth style //turrets can be found in sp so far. if( g_gametype.integer == GT_SINGLE_PLAYER ) {//SP uses this same map entity name for the ceiling turrets. Override to use them //in CoOp. if( base->spawnflags & 5 ) base->spawnflags |= 8; SP_misc_turretG2( base ); return; } //[/CoOp] base->s.modelindex2 = G_ModelIndex( "models/map_objects/hoth/turret_bottom.md3" ); base->s.modelindex = G_ModelIndex( "models/map_objects/hoth/turret_base.md3" ); //base->playerModel = gi.G2API_InitGhoul2Model( base->ghoul2, "models/map_objects/imp_mine/turret_canon.glm", base->s.modelindex ); //base->s.radius = 80.0f; //gi.G2API_SetBoneAngles( &base->ghoul2[base->playerModel], "Bone_body", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL ); //base->torsoBolt = gi.G2API_AddBolt( &base->ghoul2[base->playerModel], "*flash03" ); G_SpawnString( "icon", "", &s ); if (s && s[0]) { // We have an icon, so index it now. We are reusing the genericenemyindex // variable rather than adding a new one to the entity state. base->s.genericenemyindex = G_IconIndex(s); } G_SetAngles( base, base->s.angles ); G_SetOrigin( base, base->s.origin ); base->r.contents = CONTENTS_BODY; VectorSet( base->r.maxs, 32.0f, 32.0f, 128.0f ); VectorSet( base->r.mins, -32.0f, -32.0f, 0.0f ); base->use = turret_base_use; base->think = turret_base_think; // don't start working right away base->nextthink = level.time + FRAMETIME * 5; trap_LinkEntity( base ); if ( !turret_base_spawn_top( base ) ) { G_FreeEntity( base ); } }
//---------------------------------------------------------- void fx_explosion_trail_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { gentity_t *missile = G_Spawn(); // We aren't a missile in the truest sense, rather we just move through the world and spawn effects if ( missile ) { missile->classname = "fx_exp_trail"; missile->nextthink = level.time + 50; missile->e_ThinkFunc = thinkF_fx_explosion_trail_think; missile->s.eType = ET_MOVER; missile->owner = self; missile->s.modelindex = self->s.modelindex2; missile->s.pos.trTime = level.time; G_SetOrigin( missile, self->currentOrigin ); if ( self->spawnflags & 1 ) // gravity { missile->s.pos.trType = TR_GRAVITY; } else { missile->s.pos.trType = TR_LINEAR; } missile->spawnflags = self->spawnflags; G_SetAngles( missile, self->currentAngles ); VectorScale( self->currentAngles, self->speed, missile->s.pos.trDelta ); missile->s.pos.trTime = level.time; missile->radius = self->radius; missile->damage = self->damage; missile->splashDamage = self->splashDamage; missile->splashRadius = self->splashRadius; missile->fxID = self->fxID; missile->fullName = self->fullName; missile->clipmask = MASK_SHOT; gi.linkentity( missile ); if ( VALIDSTRING( self->soundSet ) == true ) { G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START )); missile->s.loopSound = CAS_GetBModelSound( self->soundSet, BMS_MID ); missile->soundSet = self->soundSet; if ( missile->s.loopSound < 0 ) { missile->s.loopSound = 0; } } } }
void Rancor_DropVictim( gentity_t *self ) { //FIXME: if Rancor dies, it should drop its victim. //FIXME: if Rancor is removed, it must remove its victim. if ( self->activator ) { if ( self->activator->client ) { self->activator->client->ps.eFlags2 &= ~EF2_HELD_BY_MONSTER; self->activator->client->ps.hasLookTarget = qfalse; self->activator->client->ps.lookTarget = ENTITYNUM_NONE; self->activator->client->ps.viewangles[ROLL] = 0; SetClientViewAngle( self->activator, self->activator->client->ps.viewangles ); self->activator->r.currentAngles[PITCH] = self->activator->r.currentAngles[ROLL] = 0; G_SetAngles( self->activator, self->activator->r.currentAngles ); } if ( self->activator->health <= 0 ) { //if ( self->activator->s.number ) {//never free player if ( self->count == 1 ) {//in my hand, just drop them if ( self->activator->client ) { self->activator->client->ps.legsTimer = self->activator->client->ps.torsoTimer = 0; //FIXME: ragdoll? } } else { if ( self->activator->client ) { self->activator->client->ps.eFlags |= EF_NODRAW;//so his corpse doesn't drop out of me... } //G_FreeEntity( self->activator ); } } } else { if ( self->activator->NPC ) {//start thinking again self->activator->NPC->nextBStateThink = level.time; } //clear their anim and let them fall self->activator->client->ps.legsTimer = self->activator->client->ps.torsoTimer = 0; } if ( self->enemy == self->activator ) { self->enemy = NULL; } self->activator = NULL; } self->count = 0;//drop him }
//---------------------------------------------------------- void fx_runner_link( gentity_t *ent ) { vec3_t dir; if ( ent->roffname && ent->roffname[0] ) { // try to use the target to override the orientation gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->roffname ); if ( !target ) { // Bah, no good, dump a warning, but continue on and use the UP vector Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->roffname ); Com_Printf( " -assuming UP orientation.\n" ); } else { // Our target is valid so let's override the default UP vector VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles ); } } // don't really do anything with this right now other than do a check to warn the designers if the target is bogus if ( ent->target ) { gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->target ); if ( !target ) { // Target is bogus, but we can still continue Com_Printf( "fx_runner_link: target was specified but is not valid: %s\n", ent->target ); } } G_SetAngles( ent, ent->s.angles ); if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT { // We won't even consider thinking until we are used ent->nextthink = -1; } else { // Let's get to work right now! ent->think = fx_runner_think; ent->nextthink = level.time + 100; // wait a small bit, then start working } }
//--------------------------------------------- void SP_misc_model_ammo_rack( gentity_t *ent ) { // If BLASTER is checked...or nothing is checked then we'll do blasters if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL ))) { if ( ent->spawnflags & RACK_WEAPONS ) { RegisterItem( FindItemForWeapon( WP_BLASTER )); } RegisterItem( FindItemForAmmo( AMMO_BLASTER )); } if (( ent->spawnflags & RACK_METAL_BOLTS )) { if ( ent->spawnflags & RACK_WEAPONS ) { RegisterItem( FindItemForWeapon( WP_REPEATER )); } RegisterItem( FindItemForAmmo( AMMO_METAL_BOLTS )); } if (( ent->spawnflags & RACK_ROCKETS )) { if ( ent->spawnflags & RACK_WEAPONS ) { RegisterItem( FindItemForWeapon( WP_ROCKET_LAUNCHER )); } RegisterItem( FindItemForAmmo( AMMO_ROCKETS )); } if (( ent->spawnflags & RACK_PWR_CELL )) { RegisterItem( FindItemForAmmo( AMMO_POWERCELL )); } if (( ent->spawnflags & RACK_HEALTH )) { RegisterItem( FindItem( "item_medpak_instant" )); } ent->e_ThinkFunc = thinkF_spawn_rack_goods; ent->nextthink = level.time + 100; G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); ent->contents = CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP;//CONTENTS_SOLID;//so use traces can go through them gi.linkentity( ent ); }
//=========================================================================== // Routine : AOTCTC_Create_Holocron // Description : Put a single holocron on the map... void AOTCTC_Create_Holocron( int type, vec3_t point ) {// Put a single holocron on the map... gentity_t *holocron = G_Spawn(); vec3_t angles; VectorSet(angles, 0, 0, 0); G_SetOrigin( holocron, point ); G_SetAngles( holocron, angles ); holocron->count = type; holocron->classname = "misc_holocron"; VectorCopy(point, holocron->s.pos.trBase); VectorCopy(point, holocron->s.origin); trap_LinkEntity(holocron); SP_misc_holocron( holocron ); }
//----------------------------------------------------- void SP_misc_turret( gentity_t *base ) //----------------------------------------------------- { char* s; base->s.modelindex2 = G_ModelIndex( "models/map_objects/hoth/turret_bottom.md3" ); base->s.modelindex = G_ModelIndex( "models/map_objects/hoth/turret_base.md3" ); //base->playerModel = trap->G2API_InitGhoul2Model( base->ghoul2, "models/map_objects/imp_mine/turret_canon.glm", base->s.modelindex ); //base->s.radius = 80.0f; //trap->G2API_SetBoneAngles( &base->ghoul2[base->playerModel], "Bone_body", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL ); //base->torsoBolt = trap->G2API_AddBolt( &base->ghoul2[base->playerModel], "*flash03" ); G_SpawnString( "icon", "", &s ); if (s && s[0]) { // We have an icon, so index it now. We are reusing the genericenemyindex // variable rather than adding a new one to the entity state. base->s.genericenemyindex = G_IconIndex(s); } G_SetAngles( base, base->s.angles ); G_SetOrigin( base, base->s.origin ); base->r.contents = CONTENTS_BODY; VectorSet( base->r.maxs, 32.0f, 32.0f, 128.0f ); VectorSet( base->r.mins, -32.0f, -32.0f, 0.0f ); base->use = turret_base_use; base->think = turret_base_think; // don't start working right away base->nextthink = level.time + FRAMETIME * 5; trap->LinkEntity( (sharedEntity_t *)base ); if ( !turret_base_spawn_top( base ) ) { G_FreeEntity( base ); } }
//----------------------------------------------------------------------------- void WP_Stick( gentity_t *missile, trace_t *trace, float fudge_distance ) //----------------------------------------------------------------------------- { vec3_t org, ang; // not moving or rotating missile->s.pos.trType = TR_STATIONARY; VectorClear( missile->s.pos.trDelta ); VectorClear( missile->s.apos.trDelta ); // so we don't stick into the wall VectorMA( trace->endpos, fudge_distance, trace->plane.normal, org ); G_SetOrigin( missile, org ); vectoangles( trace->plane.normal, ang ); G_SetAngles( missile, ang ); // I guess explode death wants me as the normal? // VectorCopy( trace->plane.normal, missile->pos1 ); gi.linkentity( missile ); }
/* ================ Cmd_UseSeeker_f ================ */ void Cmd_UseSeeker_f( gentity_t *ent ) { if ( ent->health < 1 || in_camera ) { return; } // don't use them if we don't have any...also don't use them if one is already going if ( ent->client && ent->client->ps.inventory[INV_SEEKER] > 0 && level.time > ent->client->ps.powerups[PW_SEEKER] ) { gentity_t *tent = G_Spawn(); if ( tent ) { vec3_t fwd, right, spot; AngleVectors( ent->client->ps.viewangles, fwd, right, NULL ); VectorCopy( ent->currentOrigin, spot ); // does nothing really, just initialize the goods... if ( PickSeekerSpawnPoint( ent->currentOrigin, fwd, right, ent->s.number, spot )) { VectorCopy( spot, tent->s.origin ); G_SetOrigin( tent, spot ); G_SetAngles( tent, ent->currentAngles ); extern void SP_NPC_Droid_Seeker( gentity_t *ent ); SP_NPC_Droid_Seeker( tent ); G_Sound( tent, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); // make sure that we even have some ent->client->ps.inventory[INV_SEEKER]--; ent->client->ps.powerups[PW_SEEKER] = level.time + 1000;// can only drop one every second..maybe this is annoying? } } } }
gentity_t *G_DropSaberItem( const char *saberType, saber_colors_t saberColor, vec3_t saberPos, vec3_t saberVel, vec3_t saberAngles, gentity_t *copySaber ) {//turn it into a pick-uppable item! gentity_t *newItem = NULL; if ( saberType && saberType[0] ) {//have a valid string to use for saberType newItem = G_Spawn(); if ( newItem ) { newItem->classname = G_NewString( "weapon_saber" ); VectorCopy( saberPos, newItem->s.origin ); G_SetOrigin( newItem, newItem->s.origin ); VectorCopy( saberAngles, newItem->s.angles ); G_SetAngles( newItem, newItem->s.angles ); newItem->spawnflags = 128;/*ITMSF_USEPICKUP*/ newItem->spawnflags |= 64;/*ITMSF_NOGLOW*/ newItem->NPC_type = G_NewString( saberType );//saberType //FIXME: transfer per-blade color somehow? newItem->NPC_targetname = (char *)saberColorStringForColor[saberColor]; newItem->count = 1; newItem->flags = FL_DROPPED_ITEM; G_SpawnItem( newItem, FindItemForWeapon( WP_SABER ) ); newItem->s.pos.trType = TR_GRAVITY; newItem->s.pos.trTime = level.time; VectorCopy( saberVel, newItem->s.pos.trDelta ); //newItem->s.eFlags |= EF_BOUNCE_HALF; //copy some values from another saber, if provided: G_CopySaberItemValues( copySaber, newItem ); //don't *think* about calling FinishSpawningItem, just do it! newItem->e_ThinkFunc = thinkF_NULL; newItem->nextthink = -1; FinishSpawningItem( newItem ); newItem->delay = level.time + 500;//so you can't pick it back up right away } } return newItem; }
//----------------------------------------------------- void SP_misc_turret( gentity_t *base ) //----------------------------------------------------- { char* s; base->s.modelindex2 = G_ModelIndex( "models/map_objects/hoth/turret_bottom.md3" ); base->s.modelindex = G_ModelIndex( "models/map_objects/hoth/turret_base.md3" ); G_SpawnString( "icon", "", &s ); if (s && s[0]) { // We have an icon, so index it now. We are reusing the genericenemyindex // variable rather than adding a new one to the entity state. base->s.genericenemyindex = G_IconIndex(s); } G_SetAngles( base, base->s.angles ); G_SetOrigin( base, base->s.origin ); base->r.contents = CONTENTS_BODY; VectorSet( base->r.maxs, 32.0f, 32.0f, 128.0f ); VectorSet( base->r.mins, -32.0f, -32.0f, 0.0f ); base->use = turret_base_use; base->think = turret_base_think; // don't start working right away base->nextthink = level.time + FRAMETIME * 5; trap_LinkEntity( base ); if ( !turret_base_spawn_top( base ) ) { G_FreeEntity( base ); } }
//--------------------------------------------- void SP_misc_model_gun_rack( gentity_t *ent ) { gitem_t *blaster = NULL, *repeater = NULL, *rocket = NULL; int ct = 0; float ofz[3]; gitem_t *itemList[3]; // If BLASTER is checked...or nothing is checked then we'll do blasters if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_REPEATER | RACK_ROCKET ))) { blaster = FindItemForWeapon( WP_BLASTER ); } if (( ent->spawnflags & RACK_REPEATER )) { repeater = FindItemForWeapon( WP_REPEATER ); } if (( ent->spawnflags & RACK_ROCKET )) { rocket = FindItemForWeapon( WP_ROCKET_LAUNCHER ); } //---------weapon types if ( blaster ) { ofz[ct] = 23.0f; itemList[ct++] = blaster; } if ( repeater ) { ofz[ct] = 24.5f; itemList[ct++] = repeater; } if ( rocket ) { ofz[ct] = 25.5f; itemList[ct++] = rocket; } if ( ct ) //..should always have at least one item on their, but just being safe { for ( ; ct < 3 ; ct++ ) { ofz[ct] = ofz[0]; itemList[ct] = itemList[0]; // first weapon ALWAYS propagates to fill up the shelf } } // now actually add the items to the shelf...validate that we have a list to add if ( ct ) { for ( int i = 0; i < ct; i++ ) { GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 2, ( i - 1 ) * 9 + crandom() * 2, ofz[i] ); } } ent->s.modelindex = G_ModelIndex( "models/map_objects/kejim/weaponsrack.md3" ); G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); ent->contents = CONTENTS_SOLID; gi.linkentity( ent ); }
void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fright, float fup ) { vec3_t fwd, right; gentity_t *it_ent = G_Spawn(); qboolean rotate = qtrue; AngleVectors( angs, fwd, right, NULL ); if ( it_ent && gun ) { // FIXME: scaling the ammo will probably need to be tweaked to a reasonable amount...adjust as needed // Set base ammo per type if ( gun->giType == IT_WEAPON ) { it_ent->spawnflags |= 16;// VERTICAL switch( gun->giTag ) { case WP_BLASTER: it_ent->count = 15; break; case WP_REPEATER: it_ent->count = 100; break; case WP_ROCKET_LAUNCHER: it_ent->count = 4; break; } } else { rotate = qfalse; // must deliberately make it small, or else the objects will spawn inside of each other. VectorSet( it_ent->maxs, 6.75f, 6.75f, 6.75f ); VectorScale( it_ent->maxs, -1, it_ent->mins ); } it_ent->spawnflags |= 1;// ITMSF_SUSPEND it_ent->classname = G_NewString(gun->classname); //copy it so it can be freed safely G_SpawnItem( it_ent, gun ); // FinishSpawningItem handles everything, so clear the thinkFunc that was set in G_SpawnItem FinishSpawningItem( it_ent ); if ( gun->giType == IT_AMMO ) { if ( gun->giTag == AMMO_BLASTER ) // I guess this just has to use different logic?? { if ( g_spskill->integer >= 2 ) { it_ent->count += 10; // give more on higher difficulty because there will be more/harder enemies? } } else { // scale ammo based on skill switch ( g_spskill->integer ) { case 0: // do default break; case 1: it_ent->count *= 0.75f; break; case 2: it_ent->count *= 0.5f; break; } } } it_ent->nextthink = 0; VectorCopy( org, it_ent->s.origin ); VectorMA( it_ent->s.origin, fright, right, it_ent->s.origin ); VectorMA( it_ent->s.origin, ffwd, fwd, it_ent->s.origin ); it_ent->s.origin[2] += fup; VectorCopy( angs, it_ent->s.angles ); // by doing this, we can force the amount of ammo we desire onto the weapon for when it gets picked-up it_ent->flags |= ( FL_DROPPED_ITEM | FL_FORCE_PULLABLE_ONLY ); it_ent->physicsBounce = 0.1f; for ( int t = 0; t < 3; t++ ) { if ( rotate ) { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + crandom() * 14 ); } else { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + crandom() * 4 ); } } else { if ( t == YAW ) { it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + crandom() * 4 ); } } } G_SetAngles( it_ent, it_ent->s.angles ); G_SetOrigin( it_ent, it_ent->s.origin ); gi.linkentity( it_ent ); } }
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION START_OFF SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health AUTOANIMATE - Will cycle it's anim DEADSOLID - Stay solid even when destroyed (in case damage model is rather large). NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists USE_MODEL - When used, will toggle to it's usemodel (model + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage PLAYER_USE - Player can use it with the use button NO_EXPLOSION - By default, will explode when it dies...this is your override. START_OFF - Will start off and will not appear until used. "model" arbitrary .md3 file to display "modelscale" "x" uniform scale "modelscale_vec" "x y z" scale model in each axis - height, width and length - bbox will scale with it "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving "targetname" when used, dies and displays damagemodel (model + "_d1.md3"), if any (if not, removes itself) "target" What to use when it dies "target2" What to use when it's repaired "target3" What to use when it's used while it's broken "paintarget" target to fire when hit (but not destroyed) "count" the amount of armor/health/ammo given (default 50) "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks "NPC_targetname" - Only the NPC with this name can damage this "forcevisible" - When you turn on force sight (any level), you can see these draw through the entire level... "redCrosshair" - crosshair turns red when you look at this "gravity" if set to 1, this will be affected by gravity "throwtarget" if set (along with gravity), this thing, when used, will throw itself at the entity whose targetname matches this string "mass" if gravity is on, this determines how much damage this thing does when it hits someone. Default is the size of the object from one corner to the other, that works very well. Only override if this is an object whose mass should be very high or low for it's size (density) Damage: default is none "splashDamage" - damage to do (will make it explode on death) "splashRadius" - radius for above damage "team" - This cannot take damage from members of this team: "player" "neutral" "enemy" "material" - default is "8 - MAT_NONE" - choose from this list: 0 = MAT_METAL (grey metal) 1 = MAT_GLASS 2 = MAT_ELECTRICAL (sparks only) 3 = MAT_ELEC_METAL (METAL chunks and sparks) 4 = MAT_DRK_STONE (brown stone chunks) 5 = MAT_LT_STONE (tan stone chunks) 6 = MAT_GLASS_METAL (glass and METAL chunks) 7 = MAT_METAL2 (blue/grey metal) 8 = MAT_NONE (no chunks-DEFAULT) 9 = MAT_GREY_STONE (grey colored stone) 10 = MAT_METAL3 (METAL and METAL2 chunk combo) 11 = MAT_CRATE1 (yellow multi-colored crate chunks) 12 = MAT_GRATE1 (grate chunks--looks horrible right now) 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits ) 14 = MAT_CRATE2 (red multi-colored crate chunks) 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout ) */ void SP_misc_model_breakable( gentity_t *ent ) { char damageModel[MAX_QPATH]; char chunkModel[MAX_QPATH]; char useModel[MAX_QPATH]; int len; // Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this. G_SpawnInt( "material", "8", (int*)&ent->material ); G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer qboolean bHasScale = G_SpawnVector("modelscale_vec", "0 0 0", ent->s.modelScale); if (!bHasScale) { float temp; G_SpawnFloat( "modelscale", "0", &temp); if (temp != 0.0f) { ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] = temp; bHasScale = qtrue; } } CacheChunkEffects( ent->material ); misc_model_breakable_init( ent ); len = strlen( ent->model ) - 4; assert(ent->model[len]=='.');//we're expecting ".md3" strncpy( damageModel, ent->model, sizeof(damageModel) ); damageModel[len] = 0; //chop extension strncpy( chunkModel, damageModel, sizeof(chunkModel)); strncpy( useModel, damageModel, sizeof(useModel)); if (ent->takedamage) { //Dead/damaged model if( !(ent->spawnflags & 8) ) { //no dmodel strcat( damageModel, "_d1.md3" ); ent->s.modelindex2 = G_ModelIndex( damageModel ); } //Chunk model strcat( chunkModel, "_c1.md3" ); ent->s.modelindex3 = G_ModelIndex( chunkModel ); } //Use model if( ent->spawnflags & 32 ) { //has umodel strcat( useModel, "_u1.md3" ); ent->sound1to2 = G_ModelIndex( useModel ); } if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] ) { VectorSet (ent->mins, -16, -16, -16); } if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] ) { VectorSet (ent->maxs, 16, 16, 16); } // Scale up the tie-bomber bbox a little. if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 ) { VectorSet (ent->mins, -80, -80, -80); VectorSet (ent->maxs, 80, 80, 80); //ent->s.modelScale[ 0 ] = ent->s.modelScale[ 1 ] = ent->s.modelScale[ 2 ] *= 2.0f; //bHasScale = qtrue; } if (bHasScale) { //scale the x axis of the bbox up. ent->maxs[0] *= ent->s.modelScale[0];//*scaleFactor; ent->mins[0] *= ent->s.modelScale[0];//*scaleFactor; //scale the y axis of the bbox up. ent->maxs[1] *= ent->s.modelScale[1];//*scaleFactor; ent->mins[1] *= ent->s.modelScale[1];//*scaleFactor; //scale the z axis of the bbox up and adjust origin accordingly ent->maxs[2] *= ent->s.modelScale[2]; float oldMins2 = ent->mins[2]; ent->mins[2] *= ent->s.modelScale[2]; ent->s.origin[2] += (oldMins2-ent->mins[2]); } if ( ent->spawnflags & 2 ) { ent->s.eFlags |= EF_ANIM_ALLFAST; } G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); gi.linkentity (ent); if ( ent->spawnflags & 128 ) {//Can be used by the player's BUTTON_USE ent->svFlags |= SVF_PLAYER_USABLE; } if ( ent->team && ent->team[0] ) { ent->noDamageTeam = (team_t)GetIDForString( TeamTable, ent->team ); if ( ent->noDamageTeam == TEAM_FREE ) { G_Error("team name %s not recognized\n", ent->team); } } ent->team = NULL; //HACK if ( ent->model && Q_stricmp( "models/map_objects/ships/x_wing_nogear.md3", ent->model ) == 0 ) { if( ent->splashDamage > 0 && ent->splashRadius > 0 ) { ent->s.loopSound = G_SoundIndex( "sound/vehicles/x-wing/loop.wav" ); ent->s.eFlags |= EF_LESS_ATTEN; } } else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 ) {//run a think G_EffectIndex( "explosions/fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" ); /* G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" );*/ G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" ); /* G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" );*/ G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); RegisterItem( FindItemForWeapon( WP_TIE_FIGHTER )); ent->s.eFlags |= EF_LESS_ATTEN; if( ent->splashDamage > 0 && ent->splashRadius > 0 ) { ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" ); //ent->e_ThinkFunc = thinkF_TieFighterThink; //ent->e_UseFunc = thinkF_TieFighterThink; //ent->nextthink = level.time + FRAMETIME; ent->e_UseFunc = useF_TieFighterUse; // Yeah, I could have just made this value changable from the editor, but I // need it immediately! float light; vec3_t color; qboolean lightSet, colorSet; // if the "color" or "light" keys are set, setup constantLight lightSet = qtrue;//G_SpawnFloat( "light", "100", &light ); light = 255; //colorSet = "1 1 1"//G_SpawnVector( "color", "1 1 1", color ); colorSet = qtrue; color[0] = 1; color[1] = 1; color[2] = 1; if ( lightSet || colorSet ) { int r, g, b, i; r = color[0] * 255; if ( r > 255 ) { r = 255; } g = color[1] * 255; if ( g > 255 ) { g = 255; } b = color[2] * 255; if ( b > 255 ) { b = 255; } i = light / 4; if ( i > 255 ) { i = 255; } ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 ); } } } else if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_bomber.md3", ent->model ) == 0 ) { G_EffectIndex( "ships/tiebomber_bomb_falling" ); G_EffectIndex( "ships/tiebomber_explosion2" ); G_EffectIndex( "explosions/fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); ent->e_ThinkFunc = thinkF_TieBomberThink; ent->nextthink = level.time + FRAMETIME; ent->attackDebounceTime = level.time + 1000; // We only take damage from a heavy weapon class missiles. ent->flags |= FL_DMG_BY_HEAVY_WEAP_ONLY; ent->s.loopSound = G_SoundIndex( "sound/vehicles/tie-bomber/loop.wav" ); ent->s.eFlags |= EF_LESS_ATTEN; } float grav = 0; G_SpawnFloat( "gravity", "0", &grav ); if ( grav ) {//affected by gravity G_SetAngles( ent, ent->s.angles ); G_SetOrigin( ent, ent->currentOrigin ); G_SpawnString( "throwtarget", NULL, &ent->target4 ); // used to throw itself at something misc_model_breakable_gravity_init( ent, qtrue ); } // Start off. if ( ent->spawnflags & 4096 ) { ent->spawnContents = ent->contents; // It Navs can temporarly turn it "on" ent->s.solid = 0; ent->contents = 0; ent->clipmask = 0; ent->svFlags |= SVF_NOCLIENT; ent->s.eFlags |= EF_NODRAW; ent->count = 0; } int forceVisible = 0; G_SpawnInt( "forcevisible", "0", &forceVisible ); if ( forceVisible ) {//can see these through walls with force sight, so must be broadcast //ent->svFlags |= SVF_BROADCAST; ent->s.eFlags |= EF_FORCE_VISIBLE; } int redCrosshair = 0; G_SpawnInt( "redCrosshair", "0", &redCrosshair ); if ( redCrosshair ) {//can see these through walls with force sight, so must be broadcast ent->flags |= FL_RED_CROSSHAIR; } }
//----------------------------------------------------- qboolean turret_base_spawn_top( gentity_t *base ) { vec3_t org; int t; gentity_t *top = G_Spawn(); if ( !top ) { return qfalse; } top->s.modelindex = G_ModelIndex( "models/map_objects/hoth/turret_top_new.md3" ); top->s.modelindex2 = G_ModelIndex( "models/map_objects/hoth/turret_top.md3" ); G_SetAngles( top, base->s.angles ); VectorCopy( base->s.origin, org ); org[2] += 128; G_SetOrigin( top, org ); base->r.ownerNum = top->s.number; top->r.ownerNum = base->s.number; if ( base->team && base->team[0] && //g_gametype.integer == GT_SIEGE && !base->teamnodmg) { base->teamnodmg = atoi(base->team); } base->team = NULL; top->teamnodmg = base->teamnodmg; top->alliedTeam = base->alliedTeam; base->s.eType = ET_GENERAL; // Set up our explosion effect for the ExplodeDeath code.... G_EffectIndex( "turret/explode" ); G_EffectIndex( "sparks/spark_exp_nosnd" ); G_EffectIndex( "turret/hoth_muzzle_flash" ); // this is really the pitch angle..... top->speed = 0; // this is a random time offset for the no-enemy-search-around-mode top->count = random() * 9000; if ( !base->health ) { base->health = 3000; } top->health = base->health; G_SpawnInt( "showhealth", "0", &t ); if (t) { //a non-0 maxhealth value will mean we want to show the health on the hud top->maxHealth = base->health; //acts as "maxhealth" G_ScaleNetHealth(top); base->maxHealth = base->health; G_ScaleNetHealth(base); } base->takedamage = qtrue; base->pain = TurretBasePain; base->die = bottom_die; //design specified shot speed G_SpawnFloat( "shotspeed", "1100", &base->mass ); top->mass = base->mass; //even if we don't want to show health, let's at least light the crosshair up properly over ourself if ( !top->s.teamowner ) { top->s.teamowner = top->alliedTeam; } base->alliedTeam = top->alliedTeam; base->s.teamowner = top->s.teamowner; base->s.shouldtarget = qtrue; top->s.shouldtarget = qtrue; //link them to each other base->target_ent = top; top->target_ent = base; //top->s.owner = MAX_CLIENTS; //not owned by any client // search radius if ( !base->radius ) { base->radius = 1024; } top->radius = base->radius; // How quickly to fire if ( !base->wait ) { base->wait = 300 + random() * 55; } top->wait = base->wait; if ( !base->splashDamage ) { base->splashDamage = 300; } top->splashDamage = base->splashDamage; if ( !base->splashRadius ) { base->splashRadius = 128; } top->splashRadius = base->splashRadius; // how much damage each shot does if ( !base->damage ) { base->damage = 100; } top->damage = base->damage; // how fast it turns if ( !base->speed ) { base->speed = 20; } top->speed = base->speed; VectorSet( top->r.maxs, 48.0f, 48.0f, 16.0f ); VectorSet( top->r.mins, -48.0f, -48.0f, 0.0f ); // Precache moving sounds //G_SoundIndex( "sound/chars/turret/startup.wav" ); //G_SoundIndex( "sound/chars/turret/shutdown.wav" ); //G_SoundIndex( "sound/chars/turret/ping.wav" ); G_SoundIndex( "sound/vehicles/weapons/hoth_turret/turn.wav" ); top->genericValue13 = G_EffectIndex( "turret/hoth_muzzle_flash" ); top->genericValue14 = G_EffectIndex( "turret/hoth_shot" ); top->genericValue15 = G_EffectIndex( "turret/hoth_impact" ); top->r.contents = CONTENTS_BODY; //base->max_health = base->health; top->takedamage = qtrue; top->pain = TurretPain; top->die = auto_turret_die; top->material = MAT_METAL; //base->r.svFlags |= SVF_NO_TELEPORT|SVF_NONNPC_ENEMY|SVF_SELF_ANIMATING; // Register this so that we can use it for the missile effect RegisterItem( BG_FindItemForWeapon( WP_EMPLACED_GUN )); // But set us as a turret so that we can be identified as a turret top->s.weapon = WP_EMPLACED_GUN; trap_LinkEntity( top ); return qtrue; }
/*QUAKED misc_model_breakable (1 0 0) (-16 -16 -16) (16 16 16) SOLID AUTOANIMATE DEADSOLID NO_DMODEL NO_SMOKE USE_MODEL USE_NOT_BREAK PLAYER_USE NO_EXPLOSION SOLID - Movement is blocked by it, if not set, can still be broken by explosions and shots if it has health AUTOANIMATE - Will cycle it's anim DEADSOLID - Stay solid even when destroyed (in case damage model is rather large). NO_DMODEL - Makes it NOT display a damage model when destroyed, even if one exists USE_MODEL - When used, will toggle to it's usemodel (model name + "_u1.md3")... this obviously does nothing if USE_NOT_BREAK is not checked USE_NOT_BREAK - Using it, doesn't make it break, still can be destroyed by damage PLAYER_USE - Player can use it with the use button NO_EXPLOSION - By default, will explode when it dies...this is your override. "model" arbitrary .md3 file to display "health" how much health to have - default is zero (not breakable) If you don't set the SOLID flag, but give it health, it can be shot but will not block NPCs or players from moving "targetname" when used, dies and displays damagemodel, if any (if not, removes itself) "target" What to use when it dies "target2" What to use when it's repaired "target3" What to use when it's used while it's broken "paintarget" target to fire when hit (but not destroyed) "count" the amount of armor/health/ammo given (default 50) "gravity" if set to 1, this will be affected by gravity "radius" Chunk code tries to pick a good volume of chunks, but you can alter this to scale the number of spawned chunks. (default 1) (.5) is half as many chunks, (2) is twice as many chunks Damage: default is none "splashDamage" - damage to do (will make it explode on death) "splashRadius" - radius for above damage "team" - This cannot take damage from members of this team: "player" "neutral" "enemy" "material" - default is "8 - MAT_NONE" - choose from this list: 0 = MAT_METAL (grey metal) 1 = MAT_GLASS 2 = MAT_ELECTRICAL (sparks only) 3 = MAT_ELEC_METAL (METAL chunks and sparks) 4 = MAT_DRK_STONE (brown stone chunks) 5 = MAT_LT_STONE (tan stone chunks) 6 = MAT_GLASS_METAL (glass and METAL chunks) 7 = MAT_METAL2 (blue/grey metal) 8 = MAT_NONE (no chunks-DEFAULT) 9 = MAT_GREY_STONE (grey colored stone) 10 = MAT_METAL3 (METAL and METAL2 chunk combo) 11 = MAT_CRATE1 (yellow multi-colored crate chunks) 12 = MAT_GRATE1 (grate chunks--looks horrible right now) 13 = MAT_ROPE (for yavin_trial, no chunks, just wispy bits ) 14 = MAT_CRATE2 (red multi-colored crate chunks) 15 = MAT_WHITE_METAL (white angular chunks for Stu, NS_hideout ) FIXME/TODO: set size better? multiple damage models? custom explosion effect/sound? */ void SP_misc_model_breakable( gentity_t *ent ) { char damageModel[MAX_QPATH]; char chunkModel[MAX_QPATH]; char useModel[MAX_QPATH]; int len; // Chris F. requested default for misc_model_breakable to be NONE...so don't arbitrarily change this. G_SpawnInt( "material", "8", (int*)&ent->material ); G_SpawnFloat( "radius", "1", &ent->radius ); // used to scale chunk code if desired by a designer CacheChunkEffects( ent->material ); misc_model_breakable_init( ent ); len = strlen( ent->model ) - 4; strncpy( damageModel, ent->model, len ); damageModel[len] = 0; //chop extension strncpy( chunkModel, damageModel, sizeof(chunkModel)); strncpy( useModel, damageModel, sizeof(useModel)); if (ent->takedamage) { //Dead/damaged model if( !(ent->spawnflags & 8) ) { //no dmodel strcat( damageModel, "_d1.md3" ); ent->s.modelindex2 = G_ModelIndex( damageModel ); } //Chunk model strcat( chunkModel, "_c1.md3" ); ent->s.modelindex3 = G_ModelIndex( chunkModel ); } //Use model if( ent->spawnflags & 32 ) { //has umodel strcat( useModel, "_u1.md3" ); ent->sound1to2 = G_ModelIndex( useModel ); } if ( !ent->mins[0] && !ent->mins[1] && !ent->mins[2] ) { VectorSet (ent->mins, -16, -16, -16); } if ( !ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2] ) { VectorSet (ent->maxs, 16, 16, 16); } if ( ent->spawnflags & 2 ) { ent->s.eFlags |= EF_ANIM_ALLFAST; } G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); gi.linkentity (ent); if ( ent->spawnflags & 128 ) {//Can be used by the player's BUTTON_USE ent->svFlags |= SVF_PLAYER_USABLE; } if ( ent->team && ent->team[0] ) { ent->noDamageTeam = TranslateTeamName( ent->team ); if ( ent->noDamageTeam == TEAM_FREE ) { G_Error("team name %s not recognized\n", ent->team); } } ent->team = NULL; //HACK if ( ent->model && Q_stricmp( "models/map_objects/ships/tie_fighter.md3", ent->model ) == 0 ) {//run a think G_EffectIndex( "fighter_explosion2" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass1.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass4.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tiepass5.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire2.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/tie_fire3.wav" ); G_SoundIndex( "sound/weapons/tie_fighter/TIEexplode.wav" ); ent->e_ThinkFunc = thinkF_TieFighterThink; ent->nextthink = level.time + FRAMETIME; } float grav = 0; G_SpawnFloat( "gravity", "0", &grav ); if ( grav ) {//affected by gravity G_SetAngles( ent, ent->s.angles ); G_SetOrigin( ent, ent->currentOrigin ); misc_model_breakable_gravity_init( ent, qtrue ); } }
//---------------------------------------------------------- void fx_runner_link( gentity_t *ent ) { vec3_t dir; if ( ent->target ) { // try to use the target to override the orientation gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->target ); if ( !target ) { // Bah, no good, dump a warning, but continue on and use the UP vector Com_Printf( "fx_runner_link: target specified but not found: %s\n", ent->target ); Com_Printf( " -assuming UP orientation.\n" ); } else { // Our target is valid so let's override the default UP vector VectorSubtract( target->s.origin, ent->s.origin, dir ); VectorNormalize( dir ); vectoangles( dir, ent->s.angles ); } } // don't really do anything with this right now other than do a check to warn the designers if the target2 is bogus if ( ent->target2 ) { gentity_t *target = NULL; target = G_Find( target, FOFS(targetname), ent->target2 ); if ( !target ) { // Target2 is bogus, but we can still continue Com_Printf( "fx_runner_link: target2 was specified but is not valid: %s\n", ent->target2 ); } } G_SetAngles( ent, ent->s.angles ); if ( ent->spawnflags & 1 || ent->spawnflags & 2 ) // STARTOFF || ONESHOT { // We won't even consider thinking until we are used ent->nextthink = -1; } else { if ( VALIDSTRING( ent->soundSet ) == true ) { ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID ); if ( ent->s.loopSound < 0 ) { ent->s.loopSound = 0; } } // Let's get to work right now! ent->e_ThinkFunc = thinkF_fx_runner_think; ent->nextthink = level.time + 200; // wait a small bit, then start working } // make us useable if we can be targeted if ( ent->targetname ) { ent->e_UseFunc = useF_fx_runner_use; } }
//---------------------------------------------------------- void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { vec3_t org; // turn off any firing animations it may have been doing self->s.frame = self->startFrame = self->endFrame = 0; self->svFlags &= ~SVF_ANIMATING; self->health = 0; // self->s.weapon = WP_EMPLACED_GUN; // we need to be able to switch back to the old weapon self->takedamage = qfalse; self->lastEnemy = attacker; // we defer explosion so the player has time to get out if ( self->e_DieFunc ) { self->e_ThinkFunc = thinkF_emplaced_blow; self->nextthink = level.time + 3000; // don't blow for a couple of seconds return; } if ( self->activator && self->activator->client ) { if ( self->activator->NPC ) { vec3_t right; // radius damage seems to throw them, but add an extra bit to throw them away from the weapon AngleVectors( self->currentAngles, NULL, right, NULL ); VectorMA( self->activator->client->ps.velocity, 140, right, self->activator->client->ps.velocity ); self->activator->client->ps.velocity[2] = -100; // kill them self->activator->health = 0; self->activator->client->ps.stats[STAT_HEALTH] = 0; } // kill the players emplaced ammo, cheesy way to keep the gun from firing self->activator->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0; } self->e_PainFunc = painF_NULL; self->e_ThinkFunc = thinkF_NULL; if ( self->target ) { G_UseTargets( self, attacker ); } G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); // when the gun is dead, add some ugliness to it. vec3_t ugly; ugly[YAW] = 4; ugly[PITCH] = self->lastAngles[PITCH] * 0.8f + crandom() * 6; ugly[ROLL] = crandom() * 7; gi.G2API_SetBoneAnglesIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, ugly, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 0, 0 ); VectorCopy( self->currentOrigin, org ); org[2] += 20; G_PlayEffect( "emplaced/explode", org ); // create some persistent smoke by using a dynamically created fx runner gentity_t *ent = G_Spawn(); if ( ent ) { ent->delay = 200; ent->random = 100; ent->fxID = G_EffectIndex( "emplaced/dead_smoke" ); ent->e_ThinkFunc = thinkF_fx_runner_think; ent->nextthink = level.time + 50; // move up above the gun origin VectorCopy( self->currentOrigin, org ); org[2] += 35; G_SetOrigin( ent, org ); VectorCopy( org, ent->s.origin ); VectorSet( ent->s.angles, -90, 0, 0 ); // up G_SetAngles( ent, ent->s.angles ); gi.linkentity( ent ); } G_ActivateBehavior( self, BSET_DEATH ); }
//---------------------------------------------------------- void SP_emplaced_eweb( gentity_t *ent ) { char name[] = "models/map_objects/hoth/eweb_model.glm"; ent->svFlags |= SVF_PLAYER_USABLE; ent->contents = CONTENTS_BODY; if ( ent->spawnflags & EMPLACED_INACTIVE ) { ent->svFlags |= SVF_INACTIVE; } VectorSet( ent->mins, -12, -12, -24 ); VectorSet( ent->maxs, 12, 12, 24 ); ent->takedamage = qtrue; if ( ( ent->spawnflags & EWEB_INVULNERABLE )) { ent->flags |= FL_GODMODE; } ent->s.radius = 80; ent->spawnflags |= 4; // deadsolid //ent->e_ThinkFunc = thinkF_NULL; ent->e_PainFunc = painF_eweb_pain; ent->e_DieFunc = dieF_eweb_die; G_EffectIndex( "emplaced/explode" ); G_EffectIndex( "emplaced/dead_smoke" ); G_SoundIndex( "sound/weapons/eweb/eweb_aim.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_dismount.mp3" ); //G_SoundIndex( "sound/weapons/eweb/eweb_empty.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_fire.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_hitplayer.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_hitsurface.wav" ); //G_SoundIndex( "sound/weapons/eweb/eweb_load.wav" ); G_SoundIndex( "sound/weapons/eweb/eweb_mount.mp3" ); // Set up our defaults and override with custom amounts as necessary G_SpawnInt( "count", "999", &ent->count ); G_SpawnInt( "health", "250", &ent->health ); G_SpawnInt( "splashDamage", "40", &ent->splashDamage ); G_SpawnInt( "splashRadius", "100", &ent->splashRadius ); G_SpawnFloat( "delay", "200", &ent->random ); // NOTE: spawning into a different field!! G_SpawnFloat( "wait", "800", &ent->wait ); ent->max_health = ent->health; ent->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud ent->s.modelindex = G_ModelIndex( name ); ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, name, ent->s.modelindex, NULL, NULL, 0, 0 ); // Activate our tags and bones ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*cannonflash" ); //muzzle bolt ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "cannon_Xrot" ); //for placing the owner relative to rotation ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "model_root", qtrue ); ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Yrot", qtrue ); ent->upperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "cannon_Xrot", qtrue ); gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0); gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0); //gi.G2API_SetBoneAngles( &ent->ghoul2[0], "cannon_Yrot", vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL); //set the constraints for this guy as an emplaced weapon, and his constraint angles //ent->s.origin2[0] = 60.0f; //60 degrees in either direction RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); ent->s.weapon = WP_EMPLACED_GUN; G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); VectorCopy( ent->s.angles, ent->lastAngles ); // store base angles for later VectorClear( ent->pos1 ); ent->e_UseFunc = useF_eweb_use; ent->bounceCount = 1;//to distinguish it from the emplaced gun gi.linkentity (ent); }
//---------------------------------------------------------- void eweb_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod,int dFlags,int hitLoc ) { vec3_t org; // turn off any firing animations it may have been doing self->s.frame = self->startFrame = self->endFrame = 0; self->svFlags &= ~(SVF_ANIMATING|SVF_PLAYER_USABLE); self->health = 0; // self->s.weapon = WP_EMPLACED_GUN; // we need to be able to switch back to the old weapon self->takedamage = qfalse; self->lastEnemy = attacker; if ( self->activator && self->activator->client ) { if ( self->activator->NPC ) { vec3_t right; // radius damage seems to throw them, but add an extra bit to throw them away from the weapon AngleVectors( self->currentAngles, NULL, right, NULL ); VectorMA( self->activator->client->ps.velocity, 140, right, self->activator->client->ps.velocity ); self->activator->client->ps.velocity[2] = -100; // kill them self->activator->health = 0; self->activator->client->ps.stats[STAT_HEALTH] = 0; } // kill the players emplaced ammo, cheesy way to keep the gun from firing self->activator->client->ps.ammo[weaponData[WP_EMPLACED_GUN].ammoIndex] = 0; } self->e_PainFunc = painF_NULL; if ( self->target ) { G_UseTargets( self, attacker ); } G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); VectorCopy( self->currentOrigin, org ); org[2] += 20; G_PlayEffect( "emplaced/explode", org ); // Turn the top of the eweb off. #define TURN_OFF 0x00000100//G2SURFACEFLAG_NODESCENDANTS gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "eweb_damage", TURN_OFF ); // create some persistent smoke by using a dynamically created fx runner gentity_t *ent = G_Spawn(); if ( ent ) { ent->delay = 200; ent->random = 100; ent->fxID = G_EffectIndex( "emplaced/dead_smoke" ); ent->e_ThinkFunc = thinkF_fx_runner_think; ent->nextthink = level.time + 50; // move up above the gun origin VectorCopy( self->currentOrigin, org ); org[2] += 35; G_SetOrigin( ent, org ); VectorCopy( org, ent->s.origin ); VectorSet( ent->s.angles, -90, 0, 0 ); // up G_SetAngles( ent, ent->s.angles ); gi.linkentity( ent ); } G_ActivateBehavior( self, BSET_DEATH ); }
//----------------------------------------------------- void finish_spawning_turretG2( gentity_t *base ) { vec3_t fwd; int t; if ( (base->spawnflags&2) ) { base->s.angles[ROLL] += 180; base->s.origin[2] -= 22.0f; } G_SetAngles( base, base->s.angles ); AngleVectors( base->r.currentAngles, fwd, NULL, NULL ); G_SetOrigin(base, base->s.origin); base->s.eType = ET_GENERAL; if ( base->team && base->team[0] && //g_gametype.integer == GT_SIEGE && !base->teamnodmg) { base->teamnodmg = atoi(base->team); } base->team = NULL; // Set up our explosion effect for the ExplodeDeath code.... G_EffectIndex( "turret/explode" ); G_EffectIndex( "sparks/spark_exp_nosnd" ); base->use = turretG2_base_use; base->pain = TurretG2Pain; // don't start working right away base->think = turretG2_base_think; base->nextthink = level.time + FRAMETIME * 5; // this is really the pitch angle..... base->speed = 0; // respawn time defaults to 20 seconds if ( (base->spawnflags&SPF_TURRETG2_CANRESPAWN) && !base->count ) { base->count = 20000; } G_SpawnFloat( "shotspeed", "0", &base->mass ); if ( (base->spawnflags&SPF_TURRETG2_TURBO) ) { if ( !base->random ) { //error worked into projectile direction base->random = 2.0f; } if ( !base->mass ) { //misnomer: speed of projectile base->mass = 20000; } if ( !base->health ) { base->health = 2000; } // search radius if ( !base->radius ) { base->radius = 32768; } // How quickly to fire if ( !base->wait ) { base->wait = 1000;// + random() * 500; } if ( !base->splashDamage ) { base->splashDamage = 200; } if ( !base->splashRadius ) { base->splashRadius = 500; } // how much damage each shot does if ( !base->damage ) { base->damage = 500; } if ( (base->spawnflags&SPF_TURRETG2_TURBO) ) { VectorSet( base->r.maxs, 64.0f, 64.0f, 30.0f ); VectorSet( base->r.mins, -64.0f, -64.0f, -30.0f ); } //start in "off" anim TurboLaser_SetBoneAnim( base, 4, 5 ); if ( g_gametype.integer == GT_SIEGE ) { //FIXME: designer-specified? //FIXME: put on other entities, too, particularly siege objectives and bbrushes... base->s.eFlags2 |= EF2_BRACKET_ENTITY; } } else { if ( !base->random ) { //error worked into projectile direction base->random = 2.0f; } if ( !base->mass ) { //misnomer: speed of projectile base->mass = 1100; } if ( !base->health ) { base->health = 100; } // search radius if ( !base->radius ) { base->radius = 512; } // How quickly to fire if ( !base->wait ) { base->wait = 150 + random() * 55; } if ( !base->splashDamage ) { base->splashDamage = 10; } if ( !base->splashRadius ) { base->splashRadius = 25; } // how much damage each shot does if ( !base->damage ) { base->damage = 5; } if ( base->spawnflags & 2 ) { //upside-down, invert r.mins and maxe VectorSet( base->r.maxs, 10.0f, 10.0f, 30.0f ); VectorSet( base->r.mins, -10.0f, -10.0f, 0.0f ); } else { VectorSet( base->r.maxs, 10.0f, 10.0f, 0.0f ); VectorSet( base->r.mins, -10.0f, -10.0f, -30.0f ); } } //stash health off for respawn. NOTE: cannot use maxhealth because that might not be set if not showing the health bar base->genericValue6 = base->health; G_SpawnInt( "showhealth", "0", &t ); if (t) { //a non-0 maxhealth value will mean we want to show the health on the hud base->maxHealth = base->health; G_ScaleNetHealth(base); base->s.shouldtarget = qtrue; //base->s.owner = MAX_CLIENTS; //not owned by any client } if (base->s.iModelScale) { //let's scale the bbox too... float fScale = base->s.iModelScale/100.0f; VectorScale(base->r.mins, fScale, base->r.mins); VectorScale(base->r.maxs, fScale, base->r.maxs); } // Precache special FX and moving sounds if ( (base->spawnflags&SPF_TURRETG2_TURBO) ) { base->genericValue13 = G_EffectIndex( "turret/turb_muzzle_flash" ); base->genericValue14 = G_EffectIndex( "turret/turb_shot" ); base->genericValue15 = G_EffectIndex( "turret/turb_impact" ); //FIXME: Turbo Laser Cannon sounds! G_SoundIndex( "sound/vehicles/weapons/turbolaser/turn.wav" ); } else { G_SoundIndex( "sound/chars/turret/startup.wav" ); G_SoundIndex( "sound/chars/turret/shutdown.wav" ); G_SoundIndex( "sound/chars/turret/ping.wav" ); G_SoundIndex( "sound/chars/turret/move.wav" ); } base->r.contents = CONTENTS_BODY|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|CONTENTS_SHOTCLIP; //base->max_health = base->health; base->takedamage = qtrue; base->die = turretG2_die; base->material = MAT_METAL; //base->r.svFlags |= SVF_NO_TELEPORT|SVF_NONNPC_ENEMY|SVF_SELF_ANIMATING; // Register this so that we can use it for the missile effect RegisterItem( BG_FindItemForWeapon( WP_BLASTER )); // But set us as a turret so that we can be identified as a turret base->s.weapon = WP_TURRET; trap_LinkEntity( base ); }
/* ================ G_BounceItem ================ */ void G_BounceItem( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; qboolean droppedSaber = qtrue; if ( ent->item && ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER && (ent->flags&FL_DROPPED_ITEM) ) { droppedSaber = qtrue; } // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); // cut the velocity to keep from bouncing forever VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta ); if ( droppedSaber ) {//a dropped saber item //FIXME: use NPC_type (as saberType) to get proper bounce sound? WP_SaberFallSound( NULL, ent ); } // check for stop if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {//stop G_SetOrigin( ent, trace->endpos ); ent->s.groundEntityNum = trace->entityNum; if ( droppedSaber ) {//a dropped saber item //stop rotation VectorClear( ent->s.apos.trDelta ); ent->currentAngles[PITCH] = SABER_PITCH_HACK; ent->currentAngles[ROLL] = 0; if ( ent->NPC_type && ent->NPC_type[0] ) {//we have a valid saber for this saberInfo_t saber; if ( WP_SaberParseParms( ent->NPC_type, &saber ) ) { if ( (saber.saberFlags&SFL_BOLT_TO_WRIST) ) { ent->currentAngles[PITCH] = 0; } } } pitch_roll_for_slope( ent, trace->plane.normal, ent->currentAngles, qtrue ); G_SetAngles( ent, ent->currentAngles ); } return; } //bounce if ( droppedSaber ) {//a dropped saber item //change rotation VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trTime = level.time; VectorSet( ent->s.apos.trDelta, Q_irand( -300, 300 ), Q_irand( -300, 300 ), Q_irand( -300, 300 ) ); } VectorAdd( ent->currentOrigin, trace->plane.normal, ent->currentOrigin); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; }
/* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, const vec3_t origin, const vec3_t velocity, char *target ) { gentity_t *dropped; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = G_NewString(item->classname); //copy it so it can be freed safely dropped->item = item; // try using the "correct" mins/maxs first VectorSet( dropped->mins, item->mins[0], item->mins[1], item->mins[2] ); VectorSet( dropped->maxs, item->maxs[0], item->maxs[1], item->maxs[2] ); if ((!dropped->mins[0] && !dropped->mins[1] && !dropped->mins[2]) && (!dropped->maxs[0] && !dropped->maxs[1] && !dropped->maxs[2])) { VectorSet( dropped->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); VectorScale( dropped->maxs, -1, dropped->mins ); } dropped->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them if ( target && target[0] ) { dropped->target = G_NewString( target ); } else { // if not targeting something, auto-remove after 30 seconds // only if it's NOT a security or goodie key if (dropped->item->giTag != INV_SECURITY_KEY ) { dropped->e_ThinkFunc = thinkF_G_FreeEntity; dropped->nextthink = level.time + 30000; } if ( dropped->item->giType == IT_AMMO && dropped->item->giTag == AMMO_FORCE ) { dropped->nextthink = -1; dropped->e_ThinkFunc = thinkF_NULL; } } dropped->e_TouchFunc = touchF_Touch_Item; if ( item->giType == IT_WEAPON ) { // give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster if ( item->giTag != WP_BOWCASTER && item->giTag != WP_THERMAL && item->giTag != WP_TRIP_MINE && item->giTag != WP_DET_PACK ) { VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f ); G_SetAngles( dropped, dropped->s.angles ); } } G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); dropped->s.eFlags |= EF_BOUNCE_HALF; dropped->flags = FL_DROPPED_ITEM; gi.linkentity (dropped); return dropped; }
void SP_misc_model_ghoul( gentity_t *ent ) { #if 1 ent->s.modelindex = G_ModelIndex( ent->model ); gi.G2API_InitGhoul2Model(ent->ghoul2, ent->model, ent->s.modelindex, NULL_HANDLE, NULL_HANDLE, 0, 0); ent->s.radius = 50; G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); qboolean bHasScale = G_SpawnVector( "modelscale_vec", "1 1 1", ent->s.modelScale ); if ( !bHasScale ) { float temp; G_SpawnFloat( "modelscale", "0", &temp ); if ( temp != 0.0f ) { ent->s.modelScale[0] = ent->s.modelScale[1] = ent->s.modelScale[2] = temp; bHasScale = qtrue; } } if ( bHasScale ) { //scale the x axis of the bbox up. ent->maxs[0] *= ent->s.modelScale[0]; ent->mins[0] *= ent->s.modelScale[0]; //scale the y axis of the bbox up. ent->maxs[1] *= ent->s.modelScale[1]; ent->mins[1] *= ent->s.modelScale[1]; //scale the z axis of the bbox up and adjust origin accordingly ent->maxs[2] *= ent->s.modelScale[2]; float oldMins2 = ent->mins[2]; ent->mins[2] *= ent->s.modelScale[2]; ent->s.origin[2] += (oldMins2 - ent->mins[2]); } gi.linkentity (ent); #else char name1[200] = "models/players/kyle/model.glm"; ent->s.modelindex = G_ModelIndex( name1 ); gi.G2API_InitGhoul2Model(ent->ghoul2, name1, ent->s.modelindex); ent->s.radius = 150; // we found the model ok - load it's animation config temp_animFileIndex = G_ParseAnimFileSet("_humanoid", "kyle"); if ( temp_animFileIndex<0 ) { Com_Printf( S_COLOR_RED"Failed to load animation file set models/players/jedi/animation.cfg\n"); } ent->s.angles[0] = 0; ent->s.angles[1] = 90; ent->s.angles[2] = 0; ent->s.origin[2] = 20; ent->s.origin[1] = 80; // ent->s.modelScale[0] = ent->s.modelScale[1] = ent->s.modelScale[2] = 0.8f; VectorSet (ent->mins, -16, -16, -37); VectorSet (ent->maxs, 16, 16, 32); //#if _DEBUG //loadsavecrash // VectorCopy(ent->mins, ent->s.mins); // VectorCopy(ent->maxs, ent->s.maxs); //#endif ent->contents = CONTENTS_BODY; ent->clipmask = MASK_NPCSOLID; G_SetOrigin( ent, ent->s.origin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); ent->health = 1000; // ent->s.modelindex = G_ModelIndex( "models/weapons2/blaster_r/g2blaster_w.glm" ); // gi.G2API_InitGhoul2Model(ent->ghoul2, "models/weapons2/blaster_r/g2blaster_w.glm", ent->s.modelindex); // gi.G2API_AddBolt(&ent->ghoul2[0], "*weapon"); // gi.G2API_AttachG2Model(&ent->ghoul2[1],&ent->ghoul2[0], 0, 0); gi.linkentity (ent); animation_t *animations = level.knownAnimFileSets[temp_animFileIndex].animations; int anim = BOTH_STAND3; float animSpeed = 50.0f / animations[anim].frameLerp; gi.G2API_SetBoneAnim(&ent->ghoul2[0], "model_root", animations[anim].firstFrame, (animations[anim].numFrames -1 )+ animations[anim].firstFrame, BONE_ANIM_OVERRIDE_FREEZE , animSpeed, cg.time); // int test = gi.G2API_GetSurfaceRenderStatus(&ent->ghoul2[0], "l_hand"); // gi.G2API_SetSurfaceOnOff(&ent->ghoul2[0], "l_arm",0x00000100); // test = gi.G2API_GetSurfaceRenderStatus(&ent->ghoul2[0], "l_hand"); // gi.G2API_SetNewOrigin(&ent->ghoul2[0], gi.G2API_AddBolt(&ent->ghoul2[0], "rhang_tag_bone")); // ent->s.apos.trDelta[1] = 10; // ent->s.apos.trType = TR_LINEAR; ent->nextthink = level.time + 1000; ent->e_ThinkFunc = thinkF_set_MiscAnim; #endif }
// AMMO RACK!! void spawn_rack_goods( gentity_t *ent ) { float v_off = 0; gitem_t *blaster = NULL, *metal_bolts = NULL, *rockets = NULL, *it = NULL; gitem_t *am_blaster = NULL, *am_metal_bolts = NULL, *am_rockets = NULL, *am_pwr_cell = NULL; gitem_t *health = NULL; int pos = 0, ct = 0; gitem_t *itemList[4]; // allocating 4, but we only use 3. done so I don't have to validate that the array isn't full before I add another gi.unlinkentity( ent ); // If BLASTER is checked...or nothing is checked then we'll do blasters if (( ent->spawnflags & RACK_BLASTER ) || !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL ))) { if ( ent->spawnflags & RACK_WEAPONS ) { blaster = FindItemForWeapon( WP_BLASTER ); } am_blaster = FindItemForAmmo( AMMO_BLASTER ); } if (( ent->spawnflags & RACK_METAL_BOLTS )) { if ( ent->spawnflags & RACK_WEAPONS ) { metal_bolts = FindItemForWeapon( WP_REPEATER ); } am_metal_bolts = FindItemForAmmo( AMMO_METAL_BOLTS ); } if (( ent->spawnflags & RACK_ROCKETS )) { if ( ent->spawnflags & RACK_WEAPONS ) { rockets = FindItemForWeapon( WP_ROCKET_LAUNCHER ); } am_rockets = FindItemForAmmo( AMMO_ROCKETS ); } if (( ent->spawnflags & RACK_PWR_CELL )) { am_pwr_cell = FindItemForAmmo( AMMO_POWERCELL ); } if (( ent->spawnflags & RACK_HEALTH )) { health = FindItem( "item_medpak_instant" ); RegisterItem( health ); } //---------Ammo types if ( am_blaster ) { itemList[ct++] = am_blaster; } if ( am_metal_bolts ) { itemList[ct++] = am_metal_bolts; } if ( am_pwr_cell ) { itemList[ct++] = am_pwr_cell; } if ( am_rockets ) { itemList[ct++] = am_rockets; } if ( !(ent->spawnflags & RACK_NO_FILL) && ct ) //double negative..should always have at least one item on there, but just being safe { for ( ; ct < 3 ; ct++ ) { itemList[ct] = itemList[0]; // first item ALWAYS propagates to fill up the shelf } } // now actually add the items to the shelf...validate that we have a list to add if ( ct ) { for ( int i = 0; i < ct; i++ ) { GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 0.5f, (i-1)* 8, 7.0f ); } } // -----Weapon option if ( ent->spawnflags & RACK_WEAPONS ) { if ( !(ent->spawnflags & ( RACK_BLASTER | RACK_METAL_BOLTS | RACK_ROCKETS | RACK_PWR_CELL ))) { // nothing was selected, so we assume blaster pack it = blaster; } else { // if weapon is checked...and so are one or more ammo types, then pick a random weapon to display..always give weaker weapons first if ( blaster ) { it = blaster; v_off = 25.5f; } else if ( metal_bolts ) { it = metal_bolts; v_off = 27.0f; } else if ( rockets ) { it = rockets; v_off = 28.0f; } } if ( it ) { // since we may have to put up a health pack on the shelf, we should know where we randomly put // the gun so we don't put the pack on the same spot..so pick either the left or right side pos = ( random() > .5 ) ? -1 : 1; GunRackAddItem( it, ent->s.origin, ent->s.angles, crandom() * 2, ( random() * 6 + 4 ) * pos, v_off ); } } // ------Medpack if (( ent->spawnflags & RACK_HEALTH ) && health ) { if ( !pos ) { // we haven't picked a side already... pos = ( random() > .5 ) ? -1 : 1; } else { // switch to the opposite side pos *= -1; } GunRackAddItem( health, ent->s.origin, ent->s.angles, crandom() * 0.5f, ( random() * 4 + 4 ) * pos, 24 ); } ent->s.modelindex = G_ModelIndex( "models/map_objects/kejim/weaponsrung.md3" ); G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); gi.linkentity( ent ); }
//---------------------------------------------------------- void SP_emplaced_gun( gentity_t *ent ) { char name[] = "models/map_objects/imp_mine/turret_chair.glm"; ent->svFlags |= SVF_PLAYER_USABLE; ent->contents = CONTENTS_BODY;//CONTENTS_SHOTCLIP|CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP;//CONTENTS_SOLID; if ( ent->spawnflags & EMPLACED_INACTIVE ) { ent->svFlags |= SVF_INACTIVE; } VectorSet( ent->mins, -30, -30, -5 ); VectorSet( ent->maxs, 30, 30, 60 ); ent->takedamage = qtrue; if ( !( ent->spawnflags & EMPLACED_VULNERABLE )) { ent->flags |= FL_GODMODE; } ent->s.radius = 110; ent->spawnflags |= 4; // deadsolid //ent->e_ThinkFunc = thinkF_NULL; ent->e_PainFunc = painF_emplaced_gun_pain; ent->e_DieFunc = dieF_emplaced_gun_die; G_EffectIndex( "emplaced/explode" ); G_EffectIndex( "emplaced/dead_smoke" ); G_SoundIndex( "sound/weapons/emplaced/emplaced_mount.mp3" ); G_SoundIndex( "sound/weapons/emplaced/emplaced_dismount.mp3" ); G_SoundIndex( "sound/weapons/emplaced/emplaced_move_lp.wav" ); // Set up our defaults and override with custom amounts as necessary G_SpawnInt( "count", "999", &ent->count ); G_SpawnInt( "health", "250", &ent->health ); G_SpawnInt( "splashDamage", "80", &ent->splashDamage ); G_SpawnInt( "splashRadius", "128", &ent->splashRadius ); G_SpawnFloat( "delay", "200", &ent->random ); // NOTE: spawning into a different field!! G_SpawnFloat( "wait", "800", &ent->wait ); ent->max_health = ent->health; ent->dflags |= DAMAGE_CUSTOM_HUD; // dumb, but we draw a custom hud ent->s.modelindex = G_ModelIndex( name ); ent->playerModel = gi.G2API_InitGhoul2Model( ent->ghoul2, name, ent->s.modelindex, NULL, NULL, 0, 0 ); // Activate our tags and bones ent->headBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*seat" ); ent->handLBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*flash01" ); ent->handRBolt = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*flash02" ); ent->rootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "base_bone", qtrue ); ent->lowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->playerModel], "swivel_bone", qtrue ); gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->playerModel], ent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 0, 0); RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); ent->s.weapon = WP_EMPLACED_GUN; G_SetOrigin( ent, ent->s.origin ); G_SetAngles( ent, ent->s.angles ); VectorCopy( ent->s.angles, ent->lastAngles ); // store base angles for later VectorCopy( ent->s.angles, ent->pos1 ); ent->e_UseFunc = useF_emplaced_gun_use; ent->bounceCount = 0;//to distinguish it from the eweb gi.linkentity (ent); }
void ExitEmplacedWeapon( gentity_t *ent ) { // requesting to unlock from the weapon // We'll leave the gun pointed in the direction it was last facing, though we'll cut out the pitch if ( ent->client ) { // if we are the player we will have put down a brush that blocks NPCs so that we have a clear spot to get back out. //gentity_t *place = G_Find( NULL, FOFS(classname), "emp_placeholder" ); if ( ent->health > 0 ) {//he's still alive, and we have a placeholder, so put him back if ( ent->owner->nextTrain ) { // reset the players position VectorCopy( ent->owner->nextTrain->currentOrigin, ent->client->ps.origin ); //reset ent's size to normal VectorCopy( ent->owner->nextTrain->mins, ent->mins ); VectorCopy( ent->owner->nextTrain->maxs, ent->maxs ); //free the placeholder G_FreeEntity( ent->owner->nextTrain ); //re-link the ent gi.linkentity( ent ); } else if ( ent->owner->e_UseFunc == useF_eweb_use )//yeah, crappy way to check this, but... { // so give 'em a push away from us vec3_t backDir, start, end; trace_t trace; gentity_t *eweb = ent->owner; float curRadius = 0.0f; float minRadius, maxRadius; qboolean safeExit = qfalse; VectorSubtract( ent->currentOrigin, eweb->currentOrigin, backDir ); backDir[2] = 0; minRadius = VectorNormalize( backDir )-8.0f; maxRadius = (ent->maxs[0]+ent->maxs[1])*0.5f; maxRadius += (eweb->maxs[0]+eweb->maxs[1])*0.5f; maxRadius *= 1.5f; if ( minRadius >= maxRadius - 1.0f ) { maxRadius = minRadius + 8.0f; } ent->owner = NULL;//so his trace hits me for ( curRadius = minRadius; curRadius <= maxRadius; curRadius += 4.0f ) { VectorMA( ent->currentOrigin, curRadius, backDir, start ); //make sure they're not in the ground VectorCopy( start, end ); start[2] += 18; end[2] -= 18; gi.trace(&trace, start, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, (EG2_Collision)0, 0); if ( !trace.allsolid && !trace.startsolid ) { G_SetOrigin( ent, trace.endpos ); gi.linkentity( ent ); safeExit = qtrue; break; } } //Hmm... otherwise, don't allow them to get off? ent->owner = eweb; if ( !safeExit ) {//don't try again for a second ent->owner->delay = level.time + 500; return; } } } else if ( ent->health <= 0 ) { // dead, so give 'em a push out of the chair vec3_t dir; AngleVectors( ent->owner->s.angles, NULL, dir, NULL ); if ( rand() & 1 ) { VectorScale( dir, -1, dir ); } VectorMA( ent->client->ps.velocity, 75, dir, ent->client->ps.velocity ); } //don't let them move towards me for a couple frames so they don't step back into me while I'm becoming solid to them if ( ent->s.number < MAX_CLIENTS ) { if ( ent->client->ps.pm_time < 100 ) { ent->client->ps.pm_time = 100; } ent->client->ps.pm_flags |= (PMF_TIME_NOFRICTION|PMF_TIME_KNOCKBACK); } if ( !ent->owner->bounceCount ) {//not an EWeb - the overridden bone angles will remember the angle we left it at VectorCopy( ent->client->ps.viewangles, ent->owner->s.angles ); ent->owner->s.angles[PITCH] = 0; G_SetAngles( ent->owner, ent->owner->s.angles ); VectorCopy( ent->owner->s.angles, ent->owner->pos1 ); } } // Remove the emplaced gun from our inventory ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_EMPLACED_GUN ); extern void ChangeWeapon( gentity_t *ent, int newWeapon ); extern void CG_ChangeWeapon( int num ); if ( ent->health <= 0 ) {//when die, don't set weapon back on when ejected from emplaced/eweb //empty hands ent->client->ps.weapon = WP_NONE; if ( ent->NPC ) { ChangeWeapon( ent, ent->client->ps.weapon ); // should be OK actually. } else { CG_ChangeWeapon( ent->client->ps.weapon ); } if ( ent->s.number < MAX_CLIENTS ) { gi.cvar_set( "cg_thirdperson", "1" ); } } else { // when we lock or unlock from the the gun, we get our old weapon back ent->client->ps.weapon = ent->owner->s.weapon; if ( ent->NPC ) {//BTW, if a saber-using NPC ever gets off of an emplaced gun/eweb, this will not work, look at NPC_ChangeWeapon for the proper way ChangeWeapon( ent, ent->client->ps.weapon ); } else { G_RemoveWeaponModels( ent ); CG_ChangeWeapon( ent->client->ps.weapon ); if ( ent->client->ps.weapon == WP_SABER ) { WP_SaberAddG2SaberModels( ent ); } else { G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); } if ( ent->s.number < MAX_CLIENTS ) { if ( ent->client->ps.weapon == WP_SABER ) { gi.cvar_set( "cg_thirdperson", "1" ); } else if ( ent->client->ps.weapon != WP_SABER && cg_gunAutoFirst.integer ) { gi.cvar_set( "cg_thirdperson", "0" ); } } } if ( ent->client->ps.weapon == WP_SABER ) { if ( ent->owner->alt_fire ) { ent->client->ps.SaberActivate(); } else { ent->client->ps.SaberDeactivate(); } } } //set the emplaced gun/eweb's weapon back to the emplaced gun ent->owner->s.weapon = WP_EMPLACED_GUN; // gi.G2API_DetachG2Model( &ent->ghoul2[ent->playerModel] ); ent->s.eFlags &= ~EF_LOCKED_TO_WEAPON; ent->client->ps.eFlags &= ~EF_LOCKED_TO_WEAPON; ent->owner->noDamageTeam = TEAM_FREE; ent->owner->svFlags &= ~SVF_NONNPC_ENEMY; ent->owner->delay = level.time; ent->owner->activator = NULL; if ( !ent->NPC ) { // by keeping the owner, a dead npc can be pushed out of the chair without colliding with it ent->owner = NULL; } }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); if ( !g_gravity->value ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this?? ent->s.pos.trDelta[1] += crandom() * 40.0f; ent->s.pos.trDelta[2] += random() * 20.0f; } else if ( (ent->flags&FL_DROPPED_ITEM) && ent->item && ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER ) {//a dropped saber item, check below, just in case int ignore = ENTITYNUM_NONE; if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } VectorSet( origin, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2]-1 ); gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, (EG2_Collision)0, 0 ); if ( !tr.allsolid && !tr.startsolid && tr.fraction > 0.001f ) {//wha? fall.... ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } return; } // get current position EvaluateTrajectory( &ent->s.pos, level.time, origin ); if ( ent->s.apos.trType != TR_STATIONARY ) { EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); G_SetAngles( ent, ent->currentAngles ); } // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } int ignore = ENTITYNUM_NONE; if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, (EG2_Collision)0, 0 ); VectorCopy( tr.endpos, ent->currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { if ( g_gravity->value <= 0 ) { if ( ent->s.apos.trType != TR_LINEAR ) { VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trDelta[1] = Q_flrand( -300, 300 ); ent->s.apos.trDelta[0] = Q_flrand( -10, 10 ); ent->s.apos.trDelta[2] = Q_flrand( -10, 10 ); ent->s.apos.trTime = level.time; } } //friction in zero-G if ( !g_gravity->value ) { float friction = 0.975f; /*friction -= ent->mass/1000.0f; if ( friction < 0.1 ) { friction = 0.1f; } */ VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta ); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } return; } // if it is in a nodrop volume, remove it contents = gi.pointcontents( ent->currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } if ( !tr.startsolid ) { G_BounceItem( ent, &tr ); } }