The master key is implementing in BaseMutator a good version of function "ReplaceWith", there is the problem and there I solved it, years ago. And some of those MH2 types are tempted to keep old weapons owned by monsters and... this doesn't happen all time. Aside, human player can use old weapons anyway but there will be a few errors in client due to null states called in Weapon vs TournamentWeapon if server has a weapon tweaker.
Another way would be directly adjusting function in Engine.u package and conforming it with V451 or whatever for preventing mismatch. "ReplaceWith" is a very poorly coded thing. My advice for all MH admins is to change that mess by overwriting or rewriting Engine.u or... XCGE replacement (there more other things can be solved).
The next Move for multiple replacements in cascade was another controller...
Okay, see this:
Code: Select all
function bool CheckReplacement( Actor Other, out byte bSuperRelevant)
{
local Inventory Inv;
local int i;
local Pawn P;
bSuperRelevant = 1;
if ( Weapon(Other) != None && Weapon(Other).Instigator != None && Weapon(Other).Instigator.IsA('Pawn') )
{
P = Weapon(Other).Instigator;
Other.bAlwaysRelevant = False;
if ( P.Health > 0 && P.PlayerReplicationInfo == None )
spawn (class'PawnControl',P,,P.Location); //Wait a bit to see what's the deal
}
....
And PawnControl will react a bit later - "code's patience is the key, like I said elsewhere".
Code: Select all
class PawnControl expands Info;
var Pawn P;
var float Patience;
event PrebeginPlay()
{
}
event PostBeginPlay()
{
Patience=0.15; //wait a bit to see what's the deal
}
function FinishWaiting()
{
local Weapon W;
if ( Pawn(Owner) != None && Pawn(Owner).Health > 0 )
P = Pawn(Owner);
if ( P == None || P.bDeleteMe || P.Health <= 0 )
{
Destroy();
GoTo JL48;
}
if ( P != None && P.Weapon == None )
{
Foreach RadiusActors (class'Weapon',W,100,P.Location)
{
if ( W != None && W.Instigator == P )
{
W.BecomeItem();
P.AddInventory(W);
W.GiveAmmo(P);
}
}
P.SwitchToBestWeapon();
P.bIsPlayer = False;
}
JL48:
}
Auto State CheckAndGo
{
Begin:
Sleep(Patience);
DoneWaiting:
FinishWaiting();
LifeSpan=0.1;
}
Instigator is held in replacement - ReplaceWith is rewritten. In other hand Instigator check can be removed and taken a RespawnTime=0 or another factor initiated in replacement.
Code: Select all
function bool ReplaceWith(actor Other, string aClassName) //reworking this for different reasons
{
local Actor A;
local class<Actor> aClass;
local Pawn P;
local Weapon W;
local Rotator DropRot;
local bool bMarked;
local float ORadius, OHeight;
if ( Other.IsA('Inventory') && (Other.Location == vect(0,0,0)) )
return false;
ORadius = Other.CollisionRadius;
OHeight = Other.CollisionHeight;
Other.SetCollisionSize(1,1);
aClass = class<Actor>(DynamicLoadObject(aClassName, class'Class', True)); //Parameter True works in XC servers - remove it out of XC...
if ( aClass != None )
{
aClass.Default.bCollideWhenPlacing = False;
A = Spawn(aClass,Other.Owner,Other.tag,Other.Location+vect(0,0,1),Other.Rotation);
}
else
{
log("Replacement Warning... unable to load"@aClassName@".",'XC_MonsterHunt');
}
if ( Other.IsA('Inventory') && A != None )
{
if ( Inventory(Other).MyMarker != None )
{
if ( Inventory(A) != None )
{
Inventory(Other).MyMarker.markedItem = Inventory(A);
Inventory(A).SetCollisionSize(1,1);
Inventory(A).MyMarker = Inventory(Other).MyMarker;
A.SetLocation(Other.Location+vect(0,0,1));
bMarked = True;
Inventory(A).bRotatingPickup = Inventory(Other).bRotatingPickup;
Inventory(A).RespawnTime = Inventory(Other).RespawnTime;
}
Inventory(Other).MyMarker = None;
}
else if ( A.IsA('Inventory') && Inventory(Other).Instigator != None
&& Inventory(Other).Instigator.bIsPawn && Vsize(A.Location-Other.Instigator.Location) <= A.CollisionRadius )
{
Inventory(A).Respawntime = 0.0;
Inventory(A).bHeldItem = True;
if (Inventory(A).IsA('Weapon'))
{
W = Weapon(Inventory(A));
W.PickupMessageClass = Class'Botpack.PickupMessagePlus';
if ( Other.Instigator.Health > 0 )
{
P = Other.Instigator;
W.Instigator = P;
if ( P.FindInventoryType(aClass) == None )
{
if (!P.bIsPlayer)
P.bIsPlayer=True;
W.Touch(P);
W.bAlwaysRelevant = False;
if ( P.PlayerReplicationInfo == None )
P.bIsPlayer = False;
}
}
}
}
else
{
if ( !bMarked && Other.Owner == None )
{
if (!Level.bStartUp)
{
Inventory(A).RemoteRole = ROLE_DumbProxy;
Inventory(A).bCollideWorld = True;
DropRot = Other.Rotation;
DropRot.Pitch = Rand(32768);
DropRot.Yaw = Rand(65536);
Inventory(A).Velocity = vector(DropRot) * RandRange(200,300);
Inventory(A).Velocity.Z = RandRange(250,350);
Inventory(A).SetPhysics(PHYS_Falling);
Inventory(A).GotoState('Pickup', 'Dropped');
}
if ( bDeadBabuin )
Inventory(A).RespawnTime = 0.00;
else
Inventory(A).RespawnTime = Inventory(Other).RespawnTime;
}
else
{
if ( Other.Owner != None && Other.Owner.bIsPawn )
{
log ("Detected a spawn "$Other$" with Owner "$Other.Owner$". Inventory will be set.");
P = Pawn(Other.Owner);
if ( Inventory(A).IsA('Weapon'))
{
W = Weapon(Inventory(A));
W.ReSpawnTime = 0.0;
W.BecomeItem();
W.Instigator = P;
P.AddInventory(W);
W.GiveAmmo(P);
W.WeaponSet(P);
W.bAlwaysRelevant = False;
}
}
}
}
}
else
Other.SetCollisionSize(ORadius,OHeight);
if ( A != None )
{
if ( Inventory(A) != None && Inventory(A).MyMarker != None )
A.SetCollisionSize(ORadius,OHeight);
A.event = Other.event;
A.tag = Other.tag;
A.RotationRate = Other.RotationRate;
A.bFixedRotationDir = Other.bFixedRotationDir;
if ( Other.Base != None )
A.SetBase(Other.Base);
// log ( Other$" is now "$A );
return true;
}
return false;
}
Code can be a bit simplified with those "A" checks... but they work in this state for years.
bDeadBabuin bool variable makes difference between weapon coming from dead pawns or from WoodenBoxes - set by game controller with function "DiscardInventory" rewritten exactly for this reason. Weapons dropped by Pawns are not respawning while those from various decorations are re-spawning if you look at TounamentWeapons not replaced and configured this way, in this case everything should have a copy reacting closer to old item.
Code: Select all
function DiscardInventory( Pawn Other )
{
local actor dropped;
local inventory Inv;
local weapon weap;
local float speed;
if ( EMonsterBase(BaseMutator) != None )
EMonsterBase(BaseMutator).bDeadBabuin = True;
if( Other.DropWhenKilled != None )
{
dropped = Spawn(Other.DropWhenKilled,,,Other.Location);
Inv = Inventory(dropped);
if ( Inv != None )
{
Inv.RespawnTime = 0.0; //don't respawn
Inv.BecomePickup();
}
if ( dropped != None )
{
dropped.RemoteRole = ROLE_DumbProxy;
dropped.SetPhysics(PHYS_Falling);
dropped.bCollideWorld = true;
dropped.Velocity = Other.Velocity + VRand() * 280;
}
if ( Inv != None )
Inv.GotoState('PickUp', 'Dropped');
}
if( (Other.Weapon!=None) && (Other.Weapon.Class != Level.Game.BaseMutator.MutatedDefaultWeapon())
&& Other.Weapon.bCanThrow )
{
speed = VSize(Other.Velocity);
weap = Other.Weapon;
if (speed != 0)
weap.Velocity = Normal(Other.Velocity/speed + 0.5 * VRand()) * (speed + 280);
else {
weap.Velocity.X = 0;
weap.Velocity.Y = 0;
weap.Velocity.Z = 0;
}
Other.TossWeapon();
if ( weap.PickupAmmoCount == 0 )
weap.PickupAmmoCount = 1;
}
Other.Weapon = None;
Other.SelectedItem = None;
for( Inv = Other.Inventory; Inv != None; Inv = Inv.Inventory )
Inv.Destroy();
if ( EMonsterBase(BaseMutator) != None )
EMonsterBase(BaseMutator).bDeadBabuin = False;
}