2023-04-13 17:30:38 +02:00

793 lines
28 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\clientfield_shared;
#using scripts\shared\util_shared;
#namespace GibClientUtils;
function autoexec main()
{
clientfield::register(
"actor",
"gib_state",
1,
(9),
"int",
&GibClientUtils::_GibHandler,
!true,
!true);
clientfield::register(
"playercorpse",
"gib_state",
1,
(9+3+3),
"int",
&GibClientUtils::_GibHandler,
!true,
!true);
gibDefinitions = struct::get_script_bundles("gibcharacterdef");
gibPieceLookup = [];
gibPieceLookup[2] = "annihilate";
gibPieceLookup[8] = "head";
gibPieceLookup[16] = "rightarm";
gibPieceLookup[32] = "leftarm";
gibPieceLookup[128] = "rightleg";
gibPieceLookup[256] = "leftleg";
processedBundles = [];
// Process each gib bundle to allow quick access to information in the future.
foreach ( definitionName, definition in gibDefinitions )
{
gibBundle = SpawnStruct();
gibBundle.gibs = [];
gibBundle.name = definitionName;
foreach ( gibPieceFlag, gibPieceName in gibPieceLookup )
{
gibStruct = SpawnStruct();
gibStruct.gibmodel = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibmodel" );
gibStruct.gibtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibtag" );
gibStruct.gibfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibfx" );
gibStruct.gibfxtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibeffecttag" );
gibStruct.gibdynentfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibdynentfx" );
gibStruct.gibsound = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibsound" );
gibBundle.gibs[ gibPieceFlag ] = gibStruct;
}
processedBundles[ definitionName ] = gibBundle;
}
// Replaces all gib character define bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "gibcharacterdef" ] = processedBundles;
// Thread that handles any corpse annihilate request.
level thread _AnnihilateCorpse();
}
function private _AnnihilateCorpse()
{
while( true )
{
level waittill( "corpse_explode", localClientNum, body, origin );
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
continue;
}
if ( IsDefined( body ) && _HasGibDef( body ) && body IsRagdoll() )
{
ClientEntGibHead( localClientNum, body );
ClientEntGibRightArm( localClientNum, body );
ClientEntGibLeftArm( localClientNum, body );
ClientEntGibRightLeg( localClientNum, body );
ClientEntGibLeftLeg( localClientNum, body );
}
// TODO(David Young 6-8-15): Human bodies are currently the only supported gibbable corpse.
// Need to add support to the gib definition to flag if a corpse can be annihilated instead.
if( IsDefined( body ) && _HasGibDef( body ) && body.archetype == "human" )
{
// Only allow human gibbing at a 50% rate.
if ( RandomInt( 100 ) >= 50 )
{
continue;
}
if ( IsDefined( origin ) && DistanceSquared( body.origin, origin ) <= ( 120 * 120 ) )
{
// Toggle hiding the ragdoll.
body.ignoreRagdoll = true;
body _GibEntity(
localClientNum,
2 |
16 |
32 |
(128+256),
true );
}
}
}
}
function private _CloneGibData( localClientNum, entity, clone )
{
clone.gib_data = SpawnStruct();
// Copy gib data.
clone.gib_data.gib_state = entity.gib_state;
clone.gib_data.gibdef = entity.gibdef;
// Copy all model data.
clone.gib_data.hatmodel = entity.hatmodel;
clone.gib_data.head = entity.head;
clone.gib_data.legdmg1 = entity.legdmg1;
clone.gib_data.legdmg2 = entity.legdmg2;
clone.gib_data.legdmg3 = entity.legdmg3;
clone.gib_data.legdmg4 = entity.legdmg4;
clone.gib_data.torsodmg1 = entity.torsodmg1;
clone.gib_data.torsodmg2 = entity.torsodmg2;
clone.gib_data.torsodmg3 = entity.torsodmg3;
clone.gib_data.torsodmg4 = entity.torsodmg4;
clone.gib_data.torsodmg5 = entity.torsodmg5;
}
function private _GetGibDef( entity )
{
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
return entity GetPlayerGibDef();
}
else if ( IsDefined( entity.gib_data ) )
{
return entity.gib_data.gibdef;
}
return entity.gibdef;
}
function private _GetGibbedState( localClientNum, entity )
{
if ( IsDefined( entity.gib_data ) && IsDefined( entity.gib_data.gib_state ) )
{
return entity.gib_data.gib_state;
}
else if ( IsDefined( entity.gib_state ) )
{
return entity.gib_state;
}
return 0;
}
function private _GetGibbedLegModel( localClientNum, entity )
{
gibState = _GetGibbedState( localClientNum, entity );
rightLegGibbed = (gibState & 128);
leftLegGibbed = (gibState & 256);
if ( rightLegGibbed && leftLegGibbed)
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg4:entity.legDmg4);
}
else if ( rightLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg2:entity.legDmg2);
}
else if ( leftLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg3:entity.legDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.legDmg1:entity.legDmg1);
}
function private _GetGibExtraModel( localClientNumm, entity, gibFlag )
{
if ( gibFlag == 4 )
return (isdefined(entity.gib_data)?entity.gib_data.hatmodel:entity.hatmodel);
else if ( gibFlag == 8 )
return (isdefined(entity.gib_data)?entity.gib_data.head:entity.head);
else
AssertMsg( "Unable to find gib model." );
}
function private _GetGibbedTorsoModel( localClientNum, entity )
{
gibState = _GetGibbedState( localClientNum, entity );
rightArmGibbed = (gibState & 16);
leftArmGibbed = (gibState & 32);
if ( rightArmGibbed && leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
// TODO(David Young 5-14-14): Currently AI's don't support both arms getting blown off.
// return GIB_TORSO_NO_ARMS_MODEL( entity );
}
else if ( rightArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
}
else if ( leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg3:entity.torsoDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg1:entity.torsoDmg1);
}
function private _GibPieceTag( localClientNum, entity, gibFlag )
{
if ( !_HasGibDef( self ) )
{
return;
}
gibBundle = struct::get_script_bundle("gibcharacterdef",_GetGibDef( entity ));
gibPiece = gibBundle.gibs[ gibFlag ];
if ( IsDefined( gibPiece ) )
{
return gibPiece.gibfxtag;
}
}
function private _GibEntity( localClientNum, gibFlags, shouldSpawnGibs )
{
entity = self;
if ( !_HasGibDef( entity ) )
{
return;
}
// Skip the toggle flag, GIB_TOGGLE_GIB_MODEL_FLAG.
currentGibFlag = 2;
gibDir = undefined;
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
// TODO(David Young 6-8-15): Add support for yaw bits for AI as well.
yaw_bits = ( ( gibFlags >> 9 ) & ( ( 1 << 3 ) - 1 ) );
yaw = getanglefrombits( yaw_bits, 3 );
gibDir = AnglesToForward( ( 0, yaw, 0 ) );
}
gibBundle = struct::get_script_bundle("gibcharacterdef",_GetGibDef( entity ));
// Handles any number of simultaneous gibbings.
while ( gibFlags >= currentGibFlag )
{
if ( gibFlags & currentGibFlag )
{
gibPiece = gibBundle.gibs[ currentGibFlag ];
if ( IsDefined( gibPiece ) )
{
if ( shouldSpawnGibs )
{
entity thread _GibPiece( localClientNum, entity, gibPiece.gibmodel, gibPiece.gibtag, gibPiece.gibdynentfx, gibDir );
}
_PlayGibFX( localClientNum, entity, gibPiece.gibfx, gibPiece.gibfxtag );
_PlayGibSound( localClientNum, entity, gibPiece.gibsound );
if ( currentGibFlag == 2 )
{
entity Hide();
entity.ignoreRagdoll = true;
}
}
_HandleGibCallbacks( localClientNum, entity, currentGibFlag );
}
currentGibFlag = currentGibFlag << 1;
}
}
function private _SetGibbed( localClientNum, entity, gibFlag )
{
gib_state = (_GetGibbedState( localClientNum, entity ) | ( gibFlag & ( ( 1 << 9 ) - 1 ) ));
if ( IsDefined( entity.gib_data ) )
{
entity.gib_data.gib_state = gib_state;
}
else
{
entity.gib_state = gib_state;
}
}
function private _GibClientEntityInternal( localClientNum, entity, gibFlag )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
if ( !IsDefined( entity ) || !_HasGibDef( entity ) )
{
return;
}
if ( entity.type !== "scriptmover" )
{
// Clientside gibbing currently only supports script models.
return;
}
if ( IsGibbed( localClientNum, entity, gibFlag ) )
{
return;
}
if ( !(_GetGibbedState( localClientNum, entity ) < 16) )
{
legModel = _GetGibbedLegModel( localClientNum, entity );
entity Detach( legModel, "" );
}
_SetGibbed( localClientNum, entity, gibFlag );
entity SetModel( _GetGibbedTorsoModel( localClientNum, entity ) );
entity Attach( _GetGibbedLegModel( localClientNum, entity ), "" );
entity _GibEntity( localClientNum, gibFlag, true );
}
function private _GibClientExtraInternal( localClientNum, entity, gibFlag )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
if ( !IsDefined( entity ) )
{
return;
}
if ( entity.type !== "scriptmover" )
{
// Clientside gibbing currently only supports script models.
return;
}
if ( IsGibbed( localClientNum, entity, gibFlag ) )
{
return;
}
gibModel = _GetGibExtraModel( localClientNum, entity, gibFlag );
if ( IsDefined( gibModel ) && entity IsAttached( gibModel, "" ) )
{
entity Detach( gibModel, "" );
}
if ( gibFlag == 8 )
{
if ( IsDefined( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5) ) )
{
entity Attach( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5), "" );
}
}
_SetGibbed( localClientNum, entity, gibFlag );
entity _GibEntity( localClientNum, gibFlag, true );
}
function private _GibHandler( localClientNum, oldValue, newValue, bNewEnt, bInitialSnap, fieldName, wasDemoJump )
{
entity = self;
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
}
else
{
if ( IsDefined( entity.maturegib ) && entity.maturegib && !util::is_mature() )
{
return;
}
if ( IsDefined( entity.restrictedgib ) && entity.restrictedgib && !IsShowGibsEnabled() )
{
return;
}
}
gibFlags = (oldValue ^ newValue);
shouldSpawnGibs = (!(newValue & 1));
// Don't use the old clientfield value for new entities.
if ( bNewEnt )
{
gibFlags = (0 ^ newValue);
}
entity _GibEntity( localClientNum, gibFlags, shouldSpawnGibs );
entity.gib_state = newValue;
}
function _GibPiece( localClientNum, entity, gibModel, gibTag, gibFx, gibDir )
{
if ( !IsDefined( gibTag ) || !IsDefined( gibModel ) )
{
return;
}
startPosition = entity GetTagOrigin( gibTag );
startAngles = entity GetTagAngles( gibTag );
endPosition = startPosition;
endAngles = startAngles;
forwardVector = undefined;
if ( !IsDefined( startPosition ) || !IsDefined( startAngles ) )
{
return false;
}
if ( IsDefined( gibDir ) )
{
startPosition = (0,0,0);
forwardVector = gibDir;
// TODO: define the scale on the server from the damage
forwardVector *= RandomFloatRange( 100.0, 500.0 );
}
else
{
// Let a frame pass to approximate the linear and angular velocity of the tag.
{wait(.016);};
if ( IsDefined( entity ) )
{
endPosition = entity GetTagOrigin( gibTag );
endAngles = entity GetTagAngles( gibTag );
}
else
{
// Entity already removed.
endPosition = startPosition + ( AnglesToForward( startAngles ) * 10 );
endAngles = startAngles;
}
if ( !IsDefined( endPosition ) || !IsDefined( endAngles ) )
{
return false;
}
forwardVector = VectorNormalize( endPosition - startPosition );
forwardVector *= RandomFloatRange( 0.6, 1.0 );
forwardVector += ( RandomFloatRange( 0, 0.2 ), RandomFloatRange( 0, 0.2 ), RandomFloatRange( 0.2, 0.7 ) );
}
if( IsDefined( entity ) )
{
gibEntity = CreateDynEntAndLaunch( localClientNum, gibModel, endPosition, endAngles, startPosition, forwardVector, gibFx, true );
if ( IsDefined( gibEntity ) )
{
SetDynEntBodyRenderOptionsPacked( gibEntity, entity GetBodyRenderOptionsPacked() );
}
}
}
function private _HandleGibCallbacks( localClientNum, entity, gibFlag )
{
if ( IsDefined( entity._gibCallbacks ) &&
IsDefined( entity._gibCallbacks[gibFlag] ) )
{
foreach ( callback in entity._gibCallbacks[gibFlag] )
{
[[callback]]( localClientNum, entity, gibFlag );
}
}
}
function private _HandleGibAnnihilate( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib_annihilate" );
GibClientUtils::ClientEntGibAnnihilate( localClientNum, entity );
}
function private _HandleGibHead( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"head\"" );
GibClientUtils::ClientEntGibHead( localClientNum, entity );
}
function private _HandleGibRightArm( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"arm_right\"" );
GibClientUtils::ClientEntGibRightArm( localClientNum, entity );
}
function private _HandleGibLeftArm( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"arm_left\"" );
GibClientUtils::ClientEntGibLeftArm( localClientNum, entity );
}
function private _HandleGibRightLeg( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"leg_right\"" );
GibClientUtils::ClientEntGibRightLeg( localClientNum, entity );
}
function private _HandleGibLeftLeg( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"leg_left\"" );
GibClientUtils::ClientEntGibLeftLeg( localClientNum, entity );
}
function private _HasGibDef( entity )
{
return ( IsDefined( entity.gib_data ) && IsDefined( entity.gib_data.gibdef ) ) ||
IsDefined( entity.gibdef ) ||
( ( entity GetPlayerGibDef() ) != "unknown" );
}
function _PlayGibFX( localClientNum, entity, fxFileName, fxTag )
{
if ( IsDefined( fxFileName ) && IsDefined( fxTag ) && entity hasDobj(localClientNum) )
{
fx = PlayFxOnTag( localClientNum, fxFileName, entity, fxTag );
if(isDefined(fx))
{
if(isDefined(entity.team))
{
SetFxTeam( localClientNum, fx, entity.team );
}
if( ( isdefined( level.SetGibFXToIgnorePause ) && level.SetGibFXToIgnorePause ) )
{
SetFXIgnorePause( localClientNum, fx, true );
}
}
return fx;
}
}
function _PlayGibSound( localClientNum, entity, soundAlias )
{
if ( IsDefined( soundAlias ) )
{
PlaySound( localClientNum, soundAlias, entity.origin );
}
}
/@
"Name: AddGibCallback( localClientNum, entity, gibFlag, callbackFunction )"
"Summary: Register a function callback that is called when the corresponding piece is gibbed."
"MandatoryArg: <num> : Client number."
"MandatoryArg: <entity> : Entity to add callbacks to."
"MandatoryArg: <num> : Gib piece to register for."
"MandatoryArg: <function> : Function to call, function is passed the localClientNum, entity, and gibFlag."
"Module: Gib"
@/
function AddGibCallback( localClientNum, entity, gibFlag, callbackFunction )
{
assert( IsFunctionPtr( callbackFunction ) );
if ( !IsDefined( entity._gibCallbacks ) )
{
entity._gibCallbacks = [];
}
if ( !IsDefined( entity._gibCallbacks[gibFlag] ) )
{
entity._gibCallbacks[gibFlag] = [];
}
gibCallbacks = entity._gibCallbacks[gibFlag];
gibCallbacks[gibCallbacks.size] = callbackFunction;
entity._gibCallbacks[gibFlag] = gibCallbacks;
}
function ClientEntGibAnnihilate( localClientNum, entity )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
// Toggle hiding the ragdoll.
entity.ignoreRagdoll = true;
entity _GibEntity(
localClientNum,
2 |
16 |
32 |
(128+256),
true );
}
function ClientEntGibHead( localClientNum, entity )
{
_GibClientExtraInternal( localClientNum, entity, 4 );
_GibClientExtraInternal( localClientNum, entity, 8 );
}
function ClientEntGibLeftArm( localClientNum, entity )
{
if ( IsGibbed( localClientNum, entity, 16 ) )
{
return;
}
_GibClientEntityInternal( localClientNum, entity, 32 );
}
function ClientEntGibRightArm( localClientNum, entity )
{
if ( IsGibbed( localClientNum, entity, 32 ) )
{
return;
}
_GibClientEntityInternal( localClientNum, entity, 16 );
}
function ClientEntGibLeftLeg( localClientNum, entity )
{
_GibClientEntityInternal( localClientNum, entity, 256 );
}
function ClientEntGibRightLeg( localClientNum, entity )
{
_GibClientEntityInternal( localClientNum, entity, 128 );
}
function CreateScriptModelOfEntity( localClientNum, entity )
{
clone = Spawn( localClientNum, entity.origin, "script_model" );
clone.angles = entity.angles;
_CloneGibData( localClientNum, entity, clone );
gibState = _GetGibbedState( localClientNum, clone );
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
// Don't display gibbed entities for non-mature clients.
gibState = 0;
}
if ( !(_GetGibbedState( localClientNum, entity ) < 16) )
{
// Attach torso or full body model.
clone SetModel( _GetGibbedTorsoModel( localClientNum, entity ) );
// Only attach separate legs if the body is damage already.
clone Attach( _GetGibbedLegModel( localClientNum, entity ), "" );
}
else
{
// Attach full body model.
clone SetModel( entity.model );
}
if ( (gibState & 8) )
{
// Attach head stump.
if ( IsDefined( (isdefined(clone.gib_data)?clone.gib_data.torsoDmg5:clone.torsoDmg5) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.torsoDmg5:clone.torsoDmg5), "" );
}
}
else
{
// Attach head.
if ( IsDefined( (isdefined(clone.gib_data)?clone.gib_data.head:clone.head) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.head:clone.head), "" );
}
if ( !(gibState & 4) && IsDefined( (isdefined(clone.gib_data)?clone.gib_data.hatmodel:clone.hatmodel) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.hatmodel:clone.hatmodel), "" );
}
}
return clone;
}
function IsGibbed( localClientNum, entity, gibFlag )
{
return (_GetGibbedState( localClientNum, entity ) & gibFlag);
}
function IsUndamaged( localClientNum, entity )
{
return _GetGibbedState( localClientNum, entity ) == 0;
}
function GibEntity( localClientNum, gibFlags )
{
self _GibEntity( localClientNum, gibFlags, true );
self.gib_state = (_GetGibbedState( localClientNum, self ) | ( gibFlags & ( ( 1 << 9 ) - 1 ) ));
}
function HandleGibNotetracks( localClientNum )
{
entity = self;
entity thread _HandleGibAnnihilate( localClientNum );
entity thread _HandleGibHead( localClientNum );
entity thread _HandleGibRightArm( localClientNum );
entity thread _HandleGibLeftArm( localClientNum );
entity thread _HandleGibRightLeg( localClientNum );
entity thread _HandleGibLeftLeg( localClientNum );
}
function PlayerGibLeftArm( localClientNum )
{
self GibEntity( localClientNum, 32 );
}
function PlayerGibRightArm( localClientNum )
{
self GibEntity( localClientNum, 16 );
}
function PlayerGibLeftLeg( localClientNum )
{
self GibEntity( localClientNum, 256 );
}
function PlayerGibRightLeg( localClientNum )
{
self GibEntity( localClientNum, 128 );
}
function PlayerGibLegs( localClientNum )
{
self GibEntity( localClientNum, 128 );
self GibEntity( localClientNum, 256 );
}
function PlayerGibTag( localClientNum, gibFlag )
{
return _GibPieceTag( localClientNum, self, gibFlag );
}