Replication Issue on Melee Weapons

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
Folks,

me again with YAI (yet another issue) that I have not been able to wrap my arms fully around to and could use a kick in the right direction. It involves melee combat and on-line games. What we did is something a bit different. We got away from using the trace X units in front of a player and went to an animation driven system. We created a dozen or so custom attack animations for the pawn class( one handed swings, two handed, jabs/pokes, ect). We look at your movement and the base the attack animation on the movement. The key is the weapon and how its attached. We use one standard bone in the model to attach to your hand. Then we placed 2 bones in the model of the weapon. We place one of them at the tip of the blade and the other in the hilt of a sword (for example). Then we the player "fires" the weapon, we start tracing between these two points. As soon something breaks that trace we look at the hit detection to see if it was a "wall", a player or another weapon (ie a block) then handle that hit. The system works pretty well off-line. However on-line its a different story and I am sure its how I set up the replication of the weapons. Instead of using a started weapon attachment I used this:

Code:
//==================================================================
//  The active weapon file
//==================================================================

class swordAttachmentb extends MeleeWeapon placeable;

var ShieldSparks Sparks;
var(Health) float Health;

var() int MomentumTransfer;
var() float damMomentum; // changed type to float from int -jm
var() int Damage;
var() int DamageRadius;
var() int DamageType;
var swipetrail SweepTrail;
var() float HeadShotDamageMult;
var() float HeadShotRadius;
var name ActionAttack;
var array<Actor> HitActors[16]; // this array will keep our list of hit actors.
// Sounds vars
var(MeleeSounds) array<Sound> BlockSound;
var(MeleeSounds) array<Sound> Melee_HitFlesh;
var(MeleeSounds) array<Sound> Melee_HitArmor;
var(MeleeSounds) array<Sound> Head_Shot;
var Sound HitWallSound;


//=============================================================================
// PostBeginPlay
// Set up the swipe effects
//=============================================================================
simulated function PostBeginPlay()
{
    local actor W;
    W = self;
   // log("I am here " @location);
    //Log("My instigator is " @instigator.controller.Playerreplicationinfo.PlayerName);
    if ( Level.NetMode != NM_DedicatedServer)
    {
        if (!Level.bDropDetail && (Level.DetailMode != DM_Low) )
            SweepTrail = Spawn(class 'swipetrail', self);
        if ( SweepTrail != None )
        {
            W.AttachToBone(SweepTrail, 'sweepjoint3');
            SweepTrail.mRegen =false;
        }
    }
}


//=============================================================================
// ThirdPersonEffects
// Blank out any current 3rd person effects
//=============================================================================
simulated event ThirdPersonEffects()
{
}


//=============================================================================
// Explode
// Blows up with an effect?
// Jb: Why? for like a shattered sword???? Hmm Come back and remove later?
//=============================================================================
simulated function Explode(vector HitLocation, vector HitNormal)
{                               // is this used? - JM
    if (Role == ROLE_Authority)
    {
        // HurtRadius(Damage, DamageRadius, class'ketoweapons.DamTypesever',
        // MomentumTransfer, HitLocation );
    }
    if (EffectIsRelevant(Location, false))
        Spawn(class 'ImpactDust',,, Location);  // ,rotator(Velocity));
}


//=============================================================================
// Destroyed
// Remove the swipe/sparks effect
//=============================================================================
simulated function Destroyed()
{
    if (SweepTrail != None)
        sweeptrail.Destroy();
    if (Sparks != None)
        Sparks.Destroy();
    Super.Destroyed();
}


//=============================================================================
// CheckHit
// Remove the swipe/sparks effect
//=============================================================================
function bool CheckHit(Actor A)
{
 local int i;

 // Check if this actor is valid to be struck
 if(A == Owner || A.Owner == Owner || A == self)
 {
  return false;
 }
      // Check this actor against the HitCount array
 for(i = 0; i < 16; i++)
 {
  if(HitActors[i]== A)
  { // Found this actor in the list
   return false;
  }
 }
 // The actor wasn't in the list, so add it
 for(i = 0; i < 16; i++)
 {
  if(HitActors[i] == None)
  {
   HitActors[i]= A;
   return true;
  }
 }
 return(false);
}


//=============================================================================
// ClearHitActors
// Resets the array of actors we hit
//=============================================================================
function ClearHitActors()
{
 local int i;
 for(i = 0; i < 16; i++)
  HitActors[i] = None;

}


//=============================================================================
// Timer
// This function checks to see if any object passes between the sweep joints of
// the weapon.
//
//  Jb: AdHOCK (or is that AdHACK...hehheh get it...hahaha)  fix was to move it to a timer and have the
//       fire class controlls when the timer as active and not active....hey its a bit better...
//=============================================================================
Function Timer()
{
    local vector newpos1;
    local vector newpos2;
    local coords newcoords1;
    local coords newcoords2;
    local Vector End, HitLocation, HitNormal, Start;
    local actor Other;
    local actor W;
    // local xWeaponAttachment WeaponAttachment;
    local Weapon Weapon;
    local Controller C;

      if (SweepTrail != None)
            SweepTrail.mRegen = true;

      W = self;               // say my name...
      newcoords1 = W.GetBoneCoords('sweepjoint1');    // get the location of the sword blade start
      newcoords2 = W.GetBoneCoords('sweepjoint2');    // get the location of the sword blade end
      newpos1 = newcoords1.Origin;
      newpos2 = newcoords2.Origin;
      log("sweep joints are at " @newpos1 @newpos2);
      MeleePawn(Instigator).pt1 = newcoords1.Origin;
      MeleePawn(Instigator).pt2 = newcoords2.Origin; // copy for async trace drawing

        Start = newpos1;
        End = newpos2;
        Other = Trace(HitLocation, HitNormal, End, Start, true);    // see if the sword 'line' hits anything
        C = Instigator.Controller;
        Weapon = Instigator.Weapon;

        if (Other != None && Other != W )
        {
            if (Other.IsA('MeleeWeapon'))
            {
                PlaySound(BlockSound[Rand(BlockSound.Length)], SLOT_Interact);
                if (CheckHit(Other))
                {
                   // Hit a new object here..spawn effect
                    Spawn(class 'WallSparks',,, HitLocation);
                }

                // We also should stop our swing...as we are not swining amy more.
                // Cad do this by setting the bfire tag to false, then play another
                // animation to stop

                // Jb: Stop their fire
                if (C != none)
                {
                   C.bFire = 0;
              Weapon.StopFire(0);  // now we need to stop the player from holding the firebutton down
                }
                // Reset thier animation
                ActionAttack = '2hstance';
                MeleePawn(Instigator).SetAnimAction(ActionAttack);
                SwordFire(Weapon.FireMode[0]).GotoState('');  // Stop the swing!
                // moved gotostate to the end, in case it was jumpin early. -jm
            }
            else if (Other.bWorldGeometry)
            {
                // weve struck map stuff
                PlaySound(HItWallSound, SLOT_Interact);
                if (CheckHit(Other))
                {

                   Spawn(class'HitEffect'.static.GetHitEffect(Other, Other.Location,HitNormal)
                               ,,, HitLocation, Rotator(HitNormal));

                }
                if (C != none)
                {
                   C.bFire = 0;
                 Weapon.StopFire(0);  // now we need to stop the player from holding the firebutton down
                }
                // Reset their animation
                ActionAttack = '2hstance';
                MeleePawn(Instigator).SetAnimAction(ActionAttack);
                // on the surface type (like rune did)
                SwordFire(Weapon.FireMode[0]).GotoState('');  // Stop the swing!
            }
            else if (Other.IsA('Projectile'))
            {
               // Maybe play a custom sound for projectiles here??
               // call that projeciles ProcessTouch to see if it can be refelected
               Projectile(Other).ProcessTouch(Instigator, HitLocation);
            }
            else if (!Other.bWorldGeometry && Other != Instigator && Other != Owner && Other.IsA('Pawn'))
            {
                // we hit paydirt! err, it hit the other pawn in the flesh, time to do some damage.
                // Jb: Maybe put a check for a team mate and if so stop the swing or damage?

                //Play sounds here
                if (Pawn(Other).ShieldStrength != 0.0)
                      PlaySound(Melee_HitArmor[Rand(Melee_HitArmor.Length)], SLOT_Interact);   // implies hit a target with shield/armor
                else
                      PlaySound(Melee_HitFlesh[Rand(Melee_HitFlesh.Length)], SLOT_Interact);  // implies hit a target with w/o shield/armor
                // give them damage :)
               ProcessTouch(Other, HitLocation);
            }

        }

   // else
   // {
        if (SweepTrail != None)
            SweepTrail.mRegen = false;
   // }
}


//=============================================================================
// ProcessTouch
// Call when we hit another actor to see if we should hand out damage.
//=============================================================================
simulated function ProcessTouch(Actor Other, Vector hitLocation)
{
    local Vector X, moe;
    local float dist;
    local class<DamageType> swordDamageType;

    if (Other == Instigator)
        return;
    if (Other == Owner)
        return;
    if (Other == self)
        return;

     Damage = Default.Damage + (5*Sword(Instigator.Weapon).CurrentStance);

     // if the current pawn is in berserk mode, increase their damage by 20%
     if ( Instigator.IsA('xPawn') && xPawn(Instigator).CurrentCombo != none && xPawn(Instigator).CurrentCombo.IsA('ComboBerserk'))
       Damage += Damage * 0.20;

     if (Other != None && Other.IsA('xPawn') )
     {
           X = Normal(Other.Location-Instigator.location);
           moe = (X+vect(0,0,0.5))*damMomentum;
           // collect damagetype from custom weapons
           if ( Instigator.Weapon.IsA( 'Sword' ) )
               swordDamageType = class 'DamTypeSword';
           else if ( Instigator.Weapon.IsA( 'Axe' ) )
                swordDamageType = class 'DamTypeAxe';
           else
                swordDamageType = class 'DamTypeDoubleAxe';

           if (Pawn(Other) != None)
           {
                if (Other.GetClosestBone(HitLocation, X, dist, 'head', HeadShotRadius) == 'head')
                {
                     PlaySound(Head_Shot[Rand(Head_Shot.Length)], SLOT_Interact);
                     Other.TakeDamage(Damage * HeadShotDamageMult,Instigator, HitLocation, moe, class 'Melee.DamTypeseverHead ');
                }
                else
                {
                     Other.TakeDamage(Damage, Instigator, HitLocation, moe, swordDamageType );
                }
           }
      }
      else if (Other != None && Other.bProjTarget)
      {
           Other.TakeDamage(Damage, Instigator, HitLocation, moe, swordDamageType );
           // Need a sound for a reflect/projectile hit?? Or No solid hits?
           PlaySound(HitWallSound, SLOT_Misc);
      }
}



defaultproperties
{
    Health=500;
    MomentumTransfer=30000;
    damMomentum=25000f;        
    HeadShotDamageMult=2.000000;
    HeadShotRadius=8.000000;
    Damage=15;
    DamageRadius=45;
    DrawType=DT_Mesh;
    // Temp add by Jb
    Mesh=SkeletalMesh'Chaos_melee1.OldBastardSword'
    DrawScale=0.425
    // end temp
    SurfaceType=EST_Metal;
    SoundRadius=100.000000;
    SoundVolume=255;
    TransientSoundVolume=1.200000;
    TransientSoundRadius=400.000000;
    bCollideActors=True;
    bCollideWorld=True;
    bBlockActors=True;
    bBlockPlayers=True;
    // added in these to better model the sword
    CollisionRadius=6;
    CollisionHeight=41;
    BlockSound(0)=Sound'MeleeSounds.Melee.sword_blockmelee1'
    BlockSound(1)=Sound'MeleeSounds.Melee.sword_blockmelee2'
    BlockSound(2)=Sound'MeleeSounds.Melee.sword_blockmelee3'
    BlockSound(3)=Sound'MeleeSounds.Melee.sword_blockmelee4'
    BlockSound(4)=Sound'MeleeSounds.Melee.sword_blockmelee6'
    BlockSound(5)=Sound'MeleeSounds.Melee.sword_blockmelee5'
    HitWallSound=Sound'MeleeSounds.Melee.sword_wall1'
    Melee_HitFlesh(0)=Sound'MeleeSounds.Melee.melee_hitflesh1'
    Melee_HitFlesh(1)=Sound'MeleeSounds.Melee.melee_hitflesh2'
    Melee_HitFlesh(2)=Sound'MeleeSounds.Melee.melee_hitflesh3'
    Melee_HitFlesh(3)=Sound'MeleeSounds.Melee.melee_hitflesh4'
    Melee_HitFlesh(4)=Sound'MeleeSounds.Melee.melee_hitflesh5'
    Melee_HitFlesh(5)=Sound'MeleeSounds.Melee.melee_hitflesh6'
    Melee_HitFlesh(6)=Sound'MeleeSounds.Melee.melee_hitflesh7'
    Melee_HitFlesh(7)=Sound'MeleeSounds.Melee.melee_hitflesh8'
    Melee_HitArmor(0)=Sound'MeleeSounds.Melee.melee_hitarmor1'
    Melee_HitArmor(1)=Sound'MeleeSounds.Melee.melee_hitarmor2'
    Melee_HitArmor(2)=Sound'MeleeSounds.Melee.melee_hitarmor3'
    Melee_HitArmor(3)=Sound'MeleeSounds.Melee.melee_hitarmor4'
    Melee_HitArmor(4)=Sound'MeleeSounds.Melee.melee_hitarmor5'
    Melee_HitArmor(5)=Sound'MeleeSounds.Melee.melee_hitarmor6'
    Head_Shot(0)=Sound'MeleeSounds.Melee.melee_headshot1'
    Head_Shot(1)=Sound'MeleeSounds.Melee.melee_headshot2'
    Head_Shot(2)=Sound'MeleeSounds.Melee.melee_headshot3'
    Head_Shot(3)=Sound'MeleeSounds.Melee.melee_headshot4'
    Head_Shot(4)=Sound'MeleeSounds.Melee.melee_headshot5'
}

and its partent:

[code]
//============================================================
// base class for the weapon "attachments"
//============================================================
class MeleeWeapon extends Actor;

defaultproperties
{

    bCollideActors=True;
    bCollideWorld=True;
    bBlockActors=True;
    bBlockPlayers = true;
    bPathColliding = False;

    //Replication stuff
    bGameRelevant=True;
    bAlwaysRelevant=True
   //bReplicateMovement=false
}


No real reason why I chose to make this a parent of Actor. What should I have used for the parent of the MeleeWeapon Class? So in on-line games it does not do any damage. And some time the sword will attach it self to the players hands. It depends on the bReplicateMovement var. If I leave it in as false, then my sword attachment spawns but its is never attached to my hand. Thus I walk around with out a weapon. If I remove it (so its set to true via the Actor class setting) then my weapon gets attached. However when I log the location of the trace joints they never move. So I can not hit anything. Now with some work I was able to play on-line with the bots. I noticed that when the swords did not attach (ie bReplicateMovement=false) I would see these swords lying at their spawns. If I walked up to a bots sword and that bot swung, then I DID TAKE damage. So the method will work. Once I can figure out what I did wrong. I guess I don't understand how it can attach the weapon and yet not have the sword joints be updated. Any thoughts???
 
Last edited:

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
Daid303,

ok that makes a lot of senses. I made that change but I still do not get any updates to the postion of the sword when I log its location:

Code:
      log("my postion is " @self.location);
      log("sweep joints are at " @newpos1 @newpos2);

These never change in value even though the sword is in my hand and moving. Now in an off-line game these change. Any ideas what I did wrong?
 

RegularX

Master of Dagoth Lies
Feb 2, 2000
1,215
0
0
Chicago, IL
What if you keep InventoryAttachment as the parent but change "bFastAttachmentReplication" to be false? It might be setting the Actor mostly correct, but I wonder if that var isn't telling the native replication to not worry about sending the location/rotation over the wire.
 

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
RegX,

thanks for the reply. Last night I tried a few things and one of them was as you suggested as well as the bOnlyDirtyReplication. Still no luck. At this point i have no idea why I can see the sword in the hands of my players yet logs show it never moves eventhough I go through the level hopping like a bunny. I would expect if the log is showing it not moving then it would not be attached. Confusing :(
 

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
Yeap its set to some non-zero value that I am almost sure its the spawn point of my player (or close to it).

I tried a rather nasty hack of settibg its location every tick to my instigators location. Well I got the logs to show that it moved. However it detached from my hand. I did not figure on a way to reattach it so dropped that idea. Thanks again!
 

RegularX

Master of Dagoth Lies
Feb 2, 2000
1,215
0
0
Chicago, IL
This is totally a side note - but is it me, or are semicolons suddenly OK in default props? didn't that use to hose the whole section?
 

RegularX

Master of Dagoth Lies
Feb 2, 2000
1,215
0
0
Chicago, IL
Doing a bit reading, it seems like Role_DumbProxy would be a better choice unless the Owner is acting like a base. ROLE_SimulatedProxy (in inventory attachment) won't replication location if there's no base.

If it is the base, try keeping SimulatedProxy and setting bUpdateSimulatedPosition=True ... (at this point I'm just trying to decipher Actor's replication rules for location)
 

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
Update:

Erik from Epic replied with this:

> Bone positions are not replicated directly, what's more they're not
> updated at all if the base mesh is not rendered - like on a server or
> even on a client PC when the mesh is outside the field of view.
> Except that is, if any game code explicitly requests a bone position
> (GetBonePosition/Rotation etc) which causes the bones to be updated
> internally.
>
> - Erik

For me I logged the bone postion of the right hand. That showed up and had real data on server. It also moved. So now that I know I can use the location of the righthand on the server, it should be a simple matter of calculating those sweep joints with some vector math.
 

jb

New Member
May 22, 2000
278
0
0
www.planetunreal.com
Found a hint burried in the UTMod List.

Basically what I had to do was get the bone cord of the players hands. Move the Attachment class to that location. Set the rotation of the attachment class to that of the right hand. Then reattach. Just 6 lines of code is all it took :)
 
Last edited: