Author Topic: Playing sound from location of projectile impact?  (Read 2065 times)

So im using the SFX system that Package_Complex uses for my weapons and im having trouble implementing sounds played from locations that projectiles land.

Heres the support script:
Code: [Select]
function strStart(%str, %search)
{
    return getSubStr(%str, 0, strlen(%search)) $= %search;
}

function strEnd(%str, %search)
{
    %len1 = strlen(%str);
    %len2 = strlen(%search);

    return strlen(%len1) >= strlen(%len2) && getSubStr(%str, %len1 - %len2, %len2) $= %search;
}

function strStartCut(%str, %search)
{
    return getSubStr(%str, strlen(%search), strlen(%str));
}

function strirpos(%str, %search)
{
    %len = strlen(%search);

    for (%i = strlen(%str) - %len; %i >= 0; %i--)
    {
        if (getSubStr(%str, %i, %len) $= %search)
            return %i;
    }

    return -1;
}

function SFXEffect::onAdd(%this)
{
    %name = %this.getName();

    if (%name $= "")
    {
        error("ERROR: SFXEffect objects must have a name!");
        %this.delete();
        return;
    }

    // If this is a duplicate, delete the old one.
    %this.setName("");
    %original = nameToID(%name);
    %this.setName(%name);

    if (%original != -1 && %original.class $= "SFXEffect")
        %original.delete();

    // Go through fields to discover all layers
    // We'll have to use local variables for everything because this looks
    // through the fields of %this, and we don't want to mess it up.
    for (%i = 0; 1; %i++)
    {
        %tag = %this.getTaggedField(%i);

        if (%tag $= "")
            break;

        %field = getField(%tag, 0);
        %value = getFields(%tag, 1, getFieldCount(%tag));

        // This part is really repetitive because we can't use fields here
        // Sorry! :P
        // Technically it could be cleaned up with globals but that's a whole
        // other can of worms
        if (strStart(%field, "file"))
        {
            %field = strStartCut(%field, "file");
            %index = strirpos(%field, "_");

            if (%index != -1)
            {
                %handle = getSubStr(%field, %index + 1, strlen(%field));
                %field = getSubStr(%field, 0, %index);
            }
            else
                %handle = "";

            if (!%isLayer[%field])
            {
                %isLayer[%field] = true;
                %layer[%maxLayer++] = %field;
            }

            %sample[%field, %maxSample[%field]++] = %handle;
        }
        else if (strStart(%field, "description"))
        {
            %field = strStartCut(%field, "description");
            %index = strirpos(%field, "_");

            if (%index != -1)
            {
                %handle = getSubStr(%field, %index + 1, strlen(%field));
                %field = getSubStr(%field, 0, %index);
            }

            if (!%isLayer[%field])
            {
                %isLayer[%field] = true;
                %layer[%maxLayer++] = %field;
            }

            %sample[%field, %maxSample[%field]++] = %handle;
        }
        else if (strStart(%field, "profile"))
        {
            %field = strStartCut(%field, "profile");
            %index = strirpos(%field, "_");

            if (%index != -1)
            {
                %handle = getSubStr(%field, %index + 1, strlen(%field));
                %field = getSubStr(%field, 0, %index);
            }

            if (!%isLayer[%field])
            {
                %isLayer[%field] = true;
                %layer[%maxLayer++] = %field;
            }

            %sample[%field, %maxSample[%field]++] = %handle;
        }
        // Now that I think about it, maybe globals would be better...
        // else if (strStart(%field, "useSource2D"))
        // {
        //     %field = strStartCut(%field, "useSource2D");
        //
        //     if (!%isLayer[%field])
        //     {
        //         %isLayer[%field] = true;
        //         %layer[%maxLayer++] = %field;
        //     }
        // }
        // else if (strStart(%field, "distanceThreshold"))
        // {
        //     %field = strStartCut(%field, "distanceThreshold");
        //
        //     if (!%isLayer[%field])
        //     {
        //         %isLayer[%field] = true;
        //         %layer[%maxLayer++] = %field;
        //     }
        // }
    }

    // Thank god that's over, now we can start making a mess with fields!
    %this.layerCount = 0;

    for (%layerIndex = 1; %layerIndex <= %maxLayer; %layerIndex++)
    {
        %layerName = %layer[%layerIndex];
        %layerMaxSample = %maxSample[%layerName];

        if (%layerMaxSample < 1)
        {
            // TODO: Do this after trying to load samples so that invalid samples don't count
            warn("SFXEffect " @ %name @ " has no samples on layer " @ %layerName @ ", ignoring layer...");
            continue;
        }

        %this.layerName[%this.layerCount] = %layerName;
        %this.layerCount++;
        %this.sampleCount[%layerName] = 0;

        for (%sampleIndex = 1; %sampleIndex <= %layerMaxSample; %sampleIndex++)
        {
            %sampleName = %sample[%layerName, %sampleIndex];

            if (%this.profile[%layerName, %sampleName] $= "")
            {
                if (%this.file[%layerName, %sampleName] $= "")
                {
                    warn("SFXEffect " @ %name @ " has no file or profile for sample " @ %sampleName @ " on layer " @ %layerName @ ", ignoring sample...");
                    continue;
                }

                %fileName = %this.file[%layerName, %sampleName];
                %description = FireSFX3d;
                %preload = true;

                if (%this.description[%layerName, %sampleName] !$= "")
                    %description = %this.description[%layerName, %sampleName];

                // Should probably getSafeVariableName the %name
                %profileName = "__SFXEffect_AudioProfile_" @ %name @ "_" @ getSafeVariableName(%layerName) @ "_" @ getSafeVariableName(%sampleName);
                %profile = nameToID(%profileName);

                if (!isObject(%profile))
                {
                    %profileName = "__SFXEffect_AudioProfile_" @ getSafeVariableName(%fileName) @ "_" @ getSafeVariableName(%description.getName());
                    echo("making " @ %profileName);
                    eval("datablock AudioProfile(" @ %profileName @ "){fileName=%fileName;description=%description;preload=%preload;};");
                    %profile = nameToID(%profileName);
                }

                if (!isObject(%profile))
                {
                    warn("SFXEffect " @ %name @ " failed to create profile for sample " @ %sampleName @ " on layer " @ %layerName @ ", ignoring sample...");
                    continue;
                }

                %this.profile[%layerName, %sampleName] = %profile;
            }

            %this.sampleHandle[%layerName, %this.sampleCount[%layerName]] = %sampleName;
            %this.sampleCount[%layerName]++;
        }
    }
}

function SFXEffect::playTo(%this, %client, %position, %source)
{
    %control = %client.getControlObject();

    if (!isObject(%control))
    {
        %client.debugSFX(%this, "not playing: no control object");
        return;
    }

    %isSource = %client == %source;
    %distance = vectorDist(%position, %control.getPosition());

    for (%i = 0; %i < %this.layerCount; %i++)
    {
        %layerName = %this.layerName[%i];

        if (%this.sampleCount[%layerName] < 1)
        {
            %client.debugSFX(%this, "not playing " @ %layerName @ ": no samples");
            continue;
        }

        switch$ (%this.filterListener[%layerName])
        {
            case "source": if (!%isSource) { %client.debugSFX(%this, "not playing " @ %layerName @ ": filterListener"); continue; }
            case "other": if (%isSource) { %client.debugSFX(%this, "not playing " @ %layerName @ ": filterListener"); continue; }
        }

        if (%this.filterDistanceMin[%layerName] !$= "" && %distance < %this.filterDistanceMin[%layerName])
        {
            %client.debugSFX(%this, "not playing " @ %layerName @ ": filterDistanceMin" SPC %distance);
            continue;
        }

        if (%this.filterDistanceMax[%layerName] !$= "" && %distance > %this.filterDistanceMax[%layerName])
        {
            %client.debugSFX(%this, "not playing " @ %layerName @ ": filterDistanceMax" SPC %distance);
            continue;
        }

        %use2D = false;

        switch$ (%this.use2D[%layerName])
        {
            case "always" or "1": %use2D = true;
            case "source": %use2D = %isSource;
            case "other": %use2D = !%isSource;
            default: %use2D = false;
        }

        %sampleIndex = getRandom(%this.sampleCount[%layerName] - 1);
        %sampleHandle = %this.sampleHandle[%layerName, %sampleIndex];

        %profile = %this.profile[%layerName, %sampleHandle];

        if (!isObject(%profile))
        {
            %client.debugSFX(%this, "not playing " @ %layerName @ ": invalid sample " @ %sampleHAndle);
            continue;
        }

        %client.debugSFX(%this, "playing " @ %layerName @ ": " @ %use2D SPC %sampleHandle);

        if (%use2D)
            %client.play2D(%profile);
        else
            %client.play3D(%profile, %position);
    }
}

function SFXEffect::play(%this, %position, %source)
{
    %count = ClientGroup.getCount();

    for (%i = 0; %i < %count; %i++)
        %this.playTo(ClientGroup.getObject(%i), %position, %source);
}

function SFXEffect::playFrom(%this, %position, %object)
{
    if (!isFunction(%object.getClassName(), "getControllingClient"))
        return %this.play(%position, 0);

    return %this.play(%position, %object.getControllingClient());
}

function GameConnection::debugSFX(%this, %sfx, %text)
{
    if (%this.debugSFX || %this.debugSFX[%sfx.getName()])
        messageClient(%this, '', "\c4debugSFX (" @ %sfx.getName() @ ")\c6: " @ %text);
}

and using this guy
Code: [Select]
FireSFX.playFrom(%obj.getMuzzlePoint(%slot), %obj);I can play a set of audio from the position "MuzzlePoint"

Now i want to be able to play the SFX when a projectile explodes or collides with the ground but im not sure how to write it out so it gets the location of the projectile and plays the SFX from there.

The HE grenade in Package_Complex does this
Code: [Select]
HEGrenadeExplodeSFX.playFrom(%position, %object);
but ive tried this with my weapons plus other variations (which is basically just me plopping random variables) and ive gotten no luck.
Can someone show me how to set this up or tell me what im doing wrong?

This is what i have for the projectile that im using
Code: [Select]
function SMAWProjectile::onCollision(%this, %slot)
{
ATExplosionSFX.playFrom(%this.getPosition(%slot), %this);
}

HE grenade does
Code: [Select]
function hegrenadeProjectile::onCollision(%this,%obj,%col,%fade,%pos,%normal)
{
serverPlay3D(hegrenadeBounceSound,%obj.getTransform());
}

maybe copy that?

Thats what i was using before but i want to play this
Code: [Select]
new ScriptObject(ATExplosionSFX)
{
class = "SFXEffect";

    file["close", 1] = "Add-Ons/Weapon_ATWeapons/Explosion/ATClose1.wav";
    file["close", 2] = "Add-Ons/Weapon_ATWeapons/Explosion/ATClose2.wav";
    file["close", 3] = "Add-Ons/Weapon_ATWeapons/Explosion/ATClose3.wav";
    filterDistanceMax["close"] = 100;
    use3D["close"] = "source";

    file["far", 1] = "Add-Ons/Weapon_ATWeapons/Explosion/ATMid1.wav";
    file["far", 2] = "Add-Ons/Weapon_ATWeapons/Explosion/ATMid2.wav";
    file["far", 3] = "Add-Ons/Weapon_ATWeapons/Explosion/ATMid3.wav";
    filterDistanceMin["far"] = 100;
filterDistanceMax["far"] = 300;
    use3D["far"] = "always";

file["very_far", 1] = "Add-Ons/Weapon_ATWeapons/Explosion/ATFar1.wav";
file["very_far", 2] = "Add-Ons/Weapon_ATWeapons/Explosion/ATFar2.wav";
file["very_far", 3] = "Add-Ons/Weapon_ATWeapons/Explosion/ATFar3.wav";
    filterDistanceMin["very_far"] = 300;
filterDistanceMax["very_far"] = 2000;
    use3D["very_far"] = "always";
};
When the projectile impacts which requires me to use something similiar to this in the onCollide function
Code: [Select]
FireSFX.playFrom(%obj.getMuzzlePoint(%slot), %obj);i just dont know how to word the line of code so those sounds play aat that position... Feel me? Im not sure how to explain it.

ahm not sure what you want, but If I were trying to play a sound from where the projectile hits something, I would use the results of a trace like this to figure it out.

Code: (console.log) [Select]
Leaving [sportBallsPackage]Armor::onTrigger() - return 909
Entering [c4]ProjectileData::onCollision(907, 16663, 11073, 1, 6.387541 46.184765 0.000000, 0.000000 0.000000 1.000000)
   Entering ProjectileData::onCollision(907, 16663, 11073, 1, 6.387541 46.184765 0.000000, 0.000000 0.000000 1.000000)
      Entering getBL_IDFromObject(16663)
         Entering getBrickGroupFromObject(16663)
         Leaving getBrickGroupFromObject() - return 11084
      Leaving getBL_IDFromObject() - return 999999
   Leaving ProjectileData::onCollision() - return 1
Leaving [c4]ProjectileData::onCollision() - return
Entering ProjectileData::onExplode(907, 16663, 6.387541 46.184765 0.010000)
   Entering getBL_IDFromObject(16663)
      Entering getBrickGroupFromObject(16663)
      Leaving getBrickGroupFromObject() - return 11084
   Leaving getBL_IDFromObject() - return 999999
Leaving ProjectileData::onExplode() - return 0

And then i found the gun projectile here:
Code: (server.cs line 218) [Select]
datablock ProjectileData(gunProjectile)
{
   projectileShapeName = "./bullet.dts";
   directDamage        = 30;

So I'd do something like:
Code: (birds.cs) [Select]
deactivatepackage(gunsoundpackage);
package gunsoundpackage
{
   function gunProjectile::onCollision(%data,%proj,%a,%pos,%vec)
   {
      ATExplosionSFX.playFrom(%pos,%proj.sourceobj);
      Parent::onCollision(%data,%proj,%a,%pos,%vec);
   }
};
activatepackage(gunsoundpackage);

... not sure if %proj.sourceobj is the right field for the person who fired the bullet, but whatever.

So im basically trying to get the SFX script that Weapon_Package_Complex uses to work with projectiles exploding. I got it to work with weapons being fired but not for projectiles colliding.
Code: [Select]
package gunsoundpackage
{
   function gunProjectile::onCollision(%data,%proj,%a,%pos,%vec)
   {
      ATExplosionSFX.playFrom(%pos,%proj.sourceobj);
      Parent::onCollision(%data,%proj,%a,%pos,%vec);
   }
};
activatepackage(gunsoundpackage);
Also does this have to be a package?

No it doesn't have to be in a package.  That's just how I do things.

In this case I was messing w/the default gun, and I parented the original function and:
When parenting a function I package it up.  Otherwise if the file is executed more than once, I can get unexpected behavior, such as previous changes still being in effect, or actions being run more than once. :P

if it's your own projectile that you're just trying to make bounce and play sound, I suspect you don't need to parent or anything, just copy what the HE grenade does

I tried copying what the HE grenade does but that wasn't working either i keep getting this:
Code: [Select]
Add-Ons/Vehicle_Humvee/sfx.cs (296): Unable to find object: '' attempting to call function 'getClassName'
BackTrace: ->SMAWProjectile::onCollision->SFXEffect::playFrom
in the console

Post the code you used for the sound
It should be like two lines of code?

Post the code you used for the sound
It should be like two lines of code?
Its litterally just this line of code
Code: [Select]
FireSFX.playFrom(%obj.getMuzzlePoint(%slot), %obj);and then the support script thats posted on the topic already, they work in unison.

uh that doesnt seem right

you should attach the sfx to the projectile explosion, or package yourProjectileDatablock::onCollision and using serverPlay3D(sound, position);

uh that doesnt seem right

you should attach the sfx to the projectile explosion, or package yourProjectileDatablock::onCollision and using serverPlay3D(sound, position);
I was using serverPlay3D i might just go back to that.

I got it to work.
Code: [Select]
function SMAWProjectile::onCollision(%this, %obj, %col, %fade, %position, %normal)
{
   ATExplosionSFX.play(%position);
   Parent::onCollision(%this, %obj, %col, %fade, %position, %normal);
}

Thanks for the help guys
« Last Edit: November 03, 2017, 01:14:36 PM by Ctrooper »