//----------------------------------------------------------------------------- // Purpose: Handle a collision using our special behavior //----------------------------------------------------------------------------- void CWeaponStriderBuster::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) { // Find out what we hit. // Don't do anything special if we're already attached to a strider. CBaseEntity *pVictim = pEvent->pEntities[!index]; if ( pVictim == NULL || m_pConstraint != NULL ) { BaseClass::VPhysicsCollision( index, pEvent ); return; } // Don't attach if we're being held by the player if ( VPhysicsGetObject()->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { BaseClass::VPhysicsCollision( index, pEvent ); return; } // Save off the speed of the object m_flCollisionSpeedSqr = ( pEvent->preVelocity[ index ] ).LengthSqr(); // Break if we hit the world while going fast enough. // Launched duds detonate if they hit the world at any speed. if ( pVictim->IsWorld() && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) { m_OnShatter.FireOutput( this, this ); Shatter( pVictim ); return; } // We'll handle this later in our touch call if ( ShouldStickToEntity( pVictim ) ) return; // Determine if we should shatter CBaseEntity *pOwnerEntity = pVictim->GetOwnerEntity(); bool bVictimIsStrider = ( ( pOwnerEntity != NULL ) && FClassnameIs( pOwnerEntity, "npc_strider" ) ); // Break if we hit anything other than a strider while going fast enough. // Launched duds detonate if they hit anything other than a strider any speed. if ( ( bVictimIsStrider == false ) && ( ( m_bDud && m_bLaunched ) || m_flCollisionSpeedSqr > Square( 500 ) ) ) { m_OnShatter.FireOutput( this, this ); Shatter( pVictim ); return; } // Just bounce BaseClass::VPhysicsCollision( index, pEvent ); }
/* ================ idBrittleFracture::ClientReceiveEvent ================ */ bool idBrittleFracture::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { idVec3 point, dir; switch( event ) { case EVENT_PROJECT_DECAL: { point[0] = msg.ReadFloat(); point[1] = msg.ReadFloat(); point[2] = msg.ReadFloat(); dir[0] = msg.ReadFloat(); dir[1] = msg.ReadFloat(); dir[2] = msg.ReadFloat(); ProjectDecal( point, dir, time, NULL ); return true; } case EVENT_SHATTER: { point[0] = msg.ReadFloat(); point[1] = msg.ReadFloat(); point[2] = msg.ReadFloat(); dir[0] = msg.ReadFloat(); dir[1] = msg.ReadFloat(); dir[2] = msg.ReadFloat(); Shatter( point, dir, time ); return true; } default: { return idEntity::ClientReceiveEvent( event, time, msg ); } } return false; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponStriderBuster::Detonate( void ) { CBaseEntity *pVictim = GetOwnerEntity(); if ( !m_bDud && pVictim ) { // Kill the strider (with magic effect) CBasePlayer *pPlayer = AI_GetSinglePlayer(); CTakeDamageInfo info( pPlayer, this, RandomVector( -100.0f, 100.0f ), GetAbsOrigin(), pVictim->GetHealth(), DMG_GENERIC ); pVictim->TakeDamage( info ); gamestats->Event_WeaponHit( ToBasePlayer( pPlayer ), true, GetClassname(), info ); // Tracker 62293: There's a bug where the inflictor/attacker are reversed when calling TakeDamage above so the player never gets // credit for the strider buster kills. The code has a bunch of assumptions lower level, so it's safer to just fix it here by // crediting a kill to the player directly. gamestats->Event_PlayerKilledOther( pPlayer, pVictim, info ); } m_OnDetonate.FireOutput( this, this ); // Explode if ( !m_bDud ) { CreateDestroyedEffect(); EmitSound( "Weapon_StriderBuster.Detonate" ); } else { DispatchParticleEffect( "striderbuster_explode_dummy_core", GetAbsOrigin(), GetAbsAngles() ); EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); } // Go to bits! Shatter( pVictim ); }
/* ================ idBrittleFracture::AddForce ================ */ void idBrittleFracture::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) { //if ( id < 0 || id >= shards.Num() ) if ( id < 0) { return; } if (id >= shards.Num()) id = shards.Num() - 1; if ( shards[id]->droppedTime != -1 ) { shards[id]->physicsObj.AddForce( 0, point, force ); } //else if ( health <= 0 && !disableFracture ) else if ( !disableFracture ) { Shatter( point, force, gameLocal.time ); //StartSound( "snd_shatter", SND_CHANNEL_ANY, 0, false, NULL ); //BC Trigger any alarm things. ActivateTargets( gameLocal.GetLocalPlayer() ); } }
/* ================ idBrittleFracture::Event_Touch ================ */ void idBrittleFracture::Event_Touch( idEntity* other, trace_t* trace ) { idVec3 point, impulse; // Let the server handle this, clients dont' predict it if( common->IsClient() ) { return; } if( !IsBroken() ) { return; } if( trace->c.id < 0 || trace->c.id >= shards.Num() ) { return; } point = shards[trace->c.id]->clipModel->GetOrigin(); impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass(); Shatter( point, impulse, gameLocal.time ); }
//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->GetObjectSpacePos(relPos); #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(nullptr, absPos, ZeroVector, 30, 30); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; if (pieces[piece]->original == nullptr) return; if (flags & PF_Shatter) { Shatter(piece, absPos, unit->speed); return; } // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed; float3 explSpeed((0.5f - gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f); if (unit->pos.y - CGround::GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) explSpeed.y = (0.5f - gs->randFloat()) * 6.0f; if (baseSpeed.SqLength() > 9.0f) { const float l = baseSpeed.Length(); const float l2 = 3.0f + math::sqrt(l - 3.0f); baseSpeed *= (l2 / l); } explSpeed += baseSpeed; const float partSat = projectileHandler->GetParticleSaturation(); int newFlags = 0; newFlags |= (PF_Explode * ((flags & PF_Explode ) != 0)); newFlags |= (PF_Smoke * ((flags & PF_Smoke ) != 0) && partSat < 0.95f); newFlags |= (PF_Fire * ((flags & PF_Fire ) != 0) && partSat < 0.95f); newFlags |= (PF_NoCEGTrail * ((flags & PF_NoCEGTrail) != 0)); newFlags |= (PF_Recursive * ((flags & PF_Recursive ) != 0)); new CPieceProjectile(unit, pieces[piece], absPos, explSpeed, newFlags, 0.5f); #endif }
void CNgcSector::plat_set_shatter(bool on) { if( on && mp_geom ) { Shatter( mp_geom ); } }
/* ================ idBrittleFracture::AddForce ================ */ void idBrittleFracture::AddForce( idEntity *ent, int id, const idVec3 &point, const idVec3 &force ) { if( id < 0 || id >= shards.Num() ) { return; } if( shards[id]->droppedTime != -1 ) { shards[id]->physicsObj.AddForce( 0, point, force ); } else if( health <= 0 && !disableFracture ) { Shatter( point, force, gameLocal.time ); } }
/* ================ idBrittleFracture::Event_Touch ================ */ void idBrittleFracture::Event_Touch( idEntity *other, trace_t *trace ) { idVec3 point, impulse; if( !IsBroken() ) { return; } if( trace->c.id < 0 || trace->c.id >= shards.Num() ) { return; } point = shards[trace->c.id]->clipModel->GetOrigin(); impulse = other->GetPhysics()->GetLinearVelocity() * other->GetPhysics()->GetMass(); Shatter( point, impulse, gameLocal.time ); }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CWeaponStriderBuster::BusterDetachThink() { SetNextThink( gpGlobals->curtime + 0.1f ); trace_t tr; UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() - Vector( 0, 0, 1200), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); if( fabs(tr.startpos.z - tr.endpos.z) < 240.0f ) { SetThink(NULL); EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() ); SetHealth( 0 ); CTakeDamageInfo info; info.SetDamage( 1.0f ); info.SetAttacker( this ); info.SetInflictor( this ); Shatter(this); } }
//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->pos + unit->frontdir * relPos.z + unit->updir * relPos.y + unit->rightdir * relPos.x; #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(absPos, float3(0, 0, 0), 30, 30, NULL); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed + unit->residualImpulse * 0.5f; float sql = baseSpeed.SqLength(); if (sql > 9) { const float l = math::sqrt(sql); const float l2 = 3 + math::sqrt(l - 3); baseSpeed *= (l2 / l); } float3 speed((0.5f-gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f); if (unit->pos.y - ground->GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) { speed.y = (0.5f - gs->randFloat()) * 6.0f; } speed += baseSpeed; if (speed.SqLength() > 12*12) { speed.Normalize(); speed *= 12; } /* TODO Push this back. Don't forget to pass the team (color). */ if (flags & PF_Shatter) { Shatter(piece, absPos, speed); } else { LocalModelPiece* pieceData = pieces[piece]; if (pieceData->original != NULL) { int newflags = PF_Fall; // if they don't fall they could live forever if (flags & PF_Explode) { newflags |= PF_Explode; } //if (flags & PF_Fall) { newflags |= PF_Fall; } if ((flags & PF_Smoke) && ph->particleSaturation < 1) { newflags |= PF_Smoke; } if ((flags & PF_Fire) && ph->particleSaturation < 0.95f) { newflags |= PF_Fire; } if (flags & PF_NoCEGTrail) { newflags |= PF_NoCEGTrail; } //LOG_L(L_DEBUG, "Exploding %s as %d", script.pieceNames[piece].c_str(), dl); new CPieceProjectile(absPos, speed, pieceData, newflags,unit,0.5f); } } #endif }
//----------------------------------------------------------------------------- // Purpose: Intercept damage and decide whether or not we want to trigger // Input : &info - //----------------------------------------------------------------------------- int CWeaponStriderBuster::OnTakeDamage( const CTakeDamageInfo &info ) { // If we're attached, any damage from the player makes us trigger CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pAttacker = info.GetAttacker(); bool bInflictorIsPlayer = ( pInflictor != NULL && pInflictor->IsPlayer() ); bool bAttackerIsPlayer = ( pAttacker != NULL && pAttacker->IsPlayer() ); if ( GetParent() && GetParent()->ClassMatches( g_iszVehicle ) ) { return 0; } // Only take damage from a player, for the moment if ( striderbuster_allow_all_damage.GetBool() || ( IsAttachedToStrider() && ( bAttackerIsPlayer || bInflictorIsPlayer ) ) ) { Detonate(); return 0; } if ( pAttacker && ( pAttacker->Classify() == CLASS_COMBINE || pAttacker->Classify() == CLASS_COMBINE_HUNTER ) ) { if ( VPhysicsGetObject() && !VPhysicsGetObject()->IsMoveable() ) { return 0; } } // Hunters are able to destroy strider busters if ( hunter_hate_held_striderbusters.GetBool() || hunter_hate_thrown_striderbusters.GetBool() || hunter_hate_attached_striderbusters.GetBool() ) { if ( ( GetHealth() > 0 ) && ( pInflictor != NULL ) && FClassnameIs( pInflictor, "hunter_flechette" ) ) { // // Flechette impacts don't hurt the striderbuster unless it's attached to a strider, // but the explosions always do. This is so that held or thrown striderbusters fly // awry because of the flechette, but attached striderbusters break instantly to make // the hunters more effective at defending the strider. // if ( IsAttachedToStrider() || !( info.GetDamageType() & DMG_NEVERGIB ) ) { if( striderbuster_die_detach.GetBool() && IsAttachedToStrider() ) { // Make the buster fall off and break. m_takedamage = DAMAGE_NO; CNPC_Strider *pStrider = dynamic_cast<CNPC_Strider *>(GetOwnerEntity()); Assert( pStrider != NULL ); pStrider->StriderBusterDetached( this ); DestroyConstraint(); // Amplify some lateral force. Vector vecForce = info.GetDamageForce(); vecForce.z = 0.0f; VPhysicsGetObject()->ApplyForceCenter( vecForce * 5.0f ); SetContextThink( NULL, gpGlobals->curtime, s_pBusterPingThinkContext ); SetThink( &CWeaponStriderBuster::BusterDetachThink ); SetNextThink( gpGlobals->curtime ); m_iBusterFlags |= STRIDERBUSTER_FLAG_KNOCKED_OFF_STRIDER; return 0; } else { // Destroy the buster in place // Make sure they know it blew up prematurely. EmitSound( "Weapon_StriderBuster.Dud_Detonate" ); DispatchParticleEffect( "striderbuster_break_flechette", GetAbsOrigin(), GetAbsAngles() ); SetHealth( 0 ); Shatter( info.GetAttacker() ); return 0; } } if ( info.GetDamage() < 5 ) { bool bFirst = ( m_CarryAngles.x == 45 && m_CarryAngles.y == 0 && m_CarryAngles.z == 0); float sinTime = sin( gpGlobals->curtime ); bool bSubtractX = ( bFirst ) ? ( sinTime < 0 ) : ( m_CarryAngles.x < 45 ); m_CarryAngles.x += ( 10.0 + 10.0 * fabsf( sinTime ) + random->RandomFloat( -2.5, 2.5 ) + random->RandomFloat( -2.5, 2.5 ) ) * ( ( bSubtractX ) ? -1.0 : 1.0 ); m_CarryAngles.y = 15 * ( sin( gpGlobals->curtime ) + cos( gpGlobals->curtime * 0.5 ) ) * .5 + random->RandomFloat( -15, 15 ); m_CarryAngles.z = 7.5 * ( sin( gpGlobals->curtime ) + sin( gpGlobals->curtime * 2.0 ) ) * .5 + random->RandomFloat( -7.5, 7.5 ); } return 1; } } // Allow crushing damage if ( info.GetDamageType() & DMG_CRUSH ) return BaseClass::OnTakeDamage( info ); return 0; }
//----------------------------------------------------------------------------- // Purpose: Physics system has just told us our constraint has been broken //----------------------------------------------------------------------------- void CWeaponStriderBuster::InputConstraintBroken( inputdata_t &inputdata ) { // Shatter with no real explosion effect Shatter( NULL ); }
/* ================ idBrittleFracture::Restore ================ */ void idBrittleFracture::Restore( idRestoreGame* savefile ) { savefile->ReadInt( health ); savefile->Read( &fl, sizeof( fl ) ); LittleBitField( &fl, sizeof( fl ) ); // setttings savefile->ReadMaterial( material ); savefile->ReadMaterial( decalMaterial ); savefile->ReadFloat( decalSize ); savefile->ReadFloat( maxShardArea ); savefile->ReadFloat( maxShatterRadius ); savefile->ReadFloat( minShatterRadius ); savefile->ReadFloat( linearVelocityScale ); savefile->ReadFloat( angularVelocityScale ); savefile->ReadFloat( shardMass ); savefile->ReadFloat( density ); savefile->ReadFloat( friction ); savefile->ReadFloat( bouncyness ); savefile->ReadString( fxFracture ); // state savefile->ReadBounds( bounds ); savefile->ReadBool( disableFracture ); savefile->ReadInt( lastRenderEntityUpdate ); savefile->ReadBool( changed ); savefile->ReadModel( defaultRenderModel ); // Reset all brittle Fractures so we can re-break them if necessary fl.takedamage = true; CreateFractures( defaultRenderModel ); FindNeighbours(); int numEvents = 0; bool resolveBreaks = false; savefile->ReadInt( numEvents ); for( int i = 0; i < numEvents; i++ ) { fractureEvent_s restoredEvent; savefile->ReadInt( restoredEvent.eventType ); savefile->ReadVec3( restoredEvent.point ); savefile->ReadVec3( restoredEvent.vector ); if( restoredEvent.eventType == EVENT_PROJECT_DECAL ) { ProjectDecal( restoredEvent.point, restoredEvent.vector, gameLocal.time, NULL ); } else { Shatter( restoredEvent.point, restoredEvent.vector, gameLocal.time ); } resolveBreaks = true; } // remove any dropped shards for( int i = 0; resolveBreaks && i < shards.Num(); i++ ) { if( shards[i]->droppedTime != -1 ) { RemoveShard( i ); i--; } } renderEntity.hModel = renderModelManager->AllocModel(); renderEntity.hModel->InitEmpty( brittleFracture_SnapshotName ); renderEntity.callback = idBrittleFracture::ModelCallback; renderEntity.noShadow = true; renderEntity.noSelfShadow = true; renderEntity.noDynamicInteractions = false; savefile->ReadBool( isXraySurface ); }
//Flags as defined by the cob standard void CUnitScript::Explode(int piece, int flags) { if (!PieceExists(piece)) { ShowUnitScriptError("Invalid piecenumber for explode"); return; } #ifndef _CONSOLE const float3 relPos = GetPiecePos(piece); const float3 absPos = unit->GetObjectSpacePos(relPos); #ifdef TRACE_SYNC tracefile << "Cob explosion: "; tracefile << absPos.x << " " << absPos.y << " " << absPos.z << " " << piece << " " << flags << "\n"; #endif if (!(flags & PF_NoHeatCloud)) { // Do an explosion at the location first new CHeatCloudProjectile(NULL, absPos, ZeroVector, 30, 30); } // If this is true, no stuff should fly off if (flags & PF_NONE) return; // This means that we are going to do a full fledged piece explosion! float3 baseSpeed = unit->speed; float3 explSpeed((0.5f - gs->randFloat()) * 6.0f, 1.2f + gs->randFloat() * 5.0f, (0.5f - gs->randFloat()) * 6.0f); if (baseSpeed.SqLength() > 9) { const float l = baseSpeed.Length(); const float l2 = 3 + math::sqrt(l - 3); baseSpeed *= (l2 / l); } if (unit->pos.y - CGround::GetApproximateHeight(unit->pos.x, unit->pos.z) > 15) { explSpeed.y = (0.5f - gs->randFloat()) * 6.0f; } explSpeed += baseSpeed; // limit projectile speed to 12 elmos/frame (why?) if (false && explSpeed.SqLength() > (12.0f*12.0f)) { explSpeed = (explSpeed.Normalize() * 12.0f); } if (flags & PF_Shatter) { Shatter(piece, absPos, explSpeed); return; } if (pieces[piece]->original == NULL) return; // projectiles that don't fall could live forever int newflags = PF_Fall; const float partSat = projectileHandler->GetParticleSaturation(); if (flags & PF_Explode) { newflags |= PF_Explode; } // if (flags & PF_Fall) { newflags |= PF_Fall; } if ((flags & PF_Smoke) && partSat < 1.0f) { newflags |= PF_Smoke; } if ((flags & PF_Fire) && partSat < 0.95f) { newflags |= PF_Fire; } if (flags & PF_NoCEGTrail) { newflags |= PF_NoCEGTrail; } if (flags & PF_Recursive) { newflags |= PF_Recursive; } new CPieceProjectile(unit, pieces[piece], absPos, explSpeed, newflags, 0.5f); #endif }