KF2-Server-Extension/ServerExt/Classes/ExtCharacterInfo.uc

651 lines
21 KiB
Ucode
Raw Normal View History

2017-10-20 02:00:49 +00:00
// Only a helper class to hold code.
class ExtCharacterInfo extends Object
2020-11-28 19:53:57 +00:00
abstract;
2017-10-20 02:00:49 +00:00
2020-11-28 20:04:55 +00:00
static final function Object SafeLoadObject(string S, Class ObjClass)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local Object O;
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
O = FindObject(S,ObjClass);
return O!=None ? O : DynamicLoadObject(S,ObjClass);
2020-01-09 11:05:13 +00:00
}
static function InitCharacterMICs(KFCharacterInfo_Human C, KFPawn P, optional bool bMaskHead)
{
2020-11-28 19:53:57 +00:00
local int i;
2020-11-28 20:12:58 +00:00
if (P.WorldInfo.NetMode == NM_DedicatedServer)
2020-11-28 19:53:57 +00:00
{
return;
}
P.CharacterMICs.Remove(0, P.CharacterMICs.Length);
// body MIC
2020-11-28 20:04:55 +00:00
if (P.Mesh != None)
2020-11-28 19:53:57 +00:00
{
P.CharacterMICs[0] = P.Mesh.CreateAndSetMaterialInstanceConstant(C.BodyMaterialID);
}
// head MIC
2020-11-28 20:12:58 +00:00
if (P.ThirdPersonHeadMeshComponent != None)
2020-11-28 19:53:57 +00:00
{
P.CharacterMICs[1] = P.ThirdPersonHeadMeshComponent.CreateAndSetMaterialInstanceConstant(C.HeadMaterialID);
2020-11-28 20:04:55 +00:00
if (bMaskHead)
2020-11-28 19:53:57 +00:00
{
// initial mask for new head MIC (also see ResetHeadMaskParam())
P.CharacterMICs[1].SetScalarParameterValue('Scalar_Mask', 1.f);
}
}
// attachment MIC
2020-11-28 20:12:58 +00:00
for (i=0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:12:58 +00:00
if (P.ThirdPersonAttachments[i] != none)
2020-11-28 19:53:57 +00:00
{
P.CharacterMICs.AddItem(P.ThirdPersonAttachments[i].CreateAndSetMaterialInstanceConstant(0));
}
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
if (P.FirstPersonAttachments[i] != none)
{
P.CharacterMICs.AddItem(P.FirstPersonAttachments[i].CreateAndSetMaterialInstanceConstant(0));
}
}
2017-10-20 02:00:49 +00:00
}
/** Sets the pawns character mesh from it's CharacterInfo, and updates instance of player in map if there is one. */
2020-11-28 20:04:55 +00:00
static final function SetCharacterMeshFromArch(KFCharacterInfo_Human C, KFPawn KFP, optional KFPlayerReplicationInfo KFPRI)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local ExtPlayerReplicationInfo EPRI;
local int AttachmentIdx, CosmeticMeshIdx;
local bool bMaskHeadMesh, bCustom;
2020-11-28 20:04:55 +00:00
if (KFPRI == none)
2020-11-28 19:53:57 +00:00
{
`Warn("Does not have a KFPRI" @ C);
2023-05-14 03:01:17 +00:00
return;
2020-11-28 19:53:57 +00:00
}
EPRI = ExtPlayerReplicationInfo(KFPRI);
bCustom = (EPRI!=None ? EPRI.UsesCustomChar() : false);
// Body mesh & skin. Index of 255 implies use index 0 (default).
SetBodyMeshAndSkin(C,
bCustom ? EPRI.CustomCharacter.BodyMeshIndex : KFPRI.RepCustomizationInfo.BodyMeshIndex,
bCustom ? EPRI.CustomCharacter.BodySkinIndex : KFPRI.RepCustomizationInfo.BodySkinIndex,
KFP,
KFPRI);
// Head mesh & skin. Index of 255 implies use index 0 (default).
SetHeadMeshAndSkin(C,
bCustom ? EPRI.CustomCharacter.HeadMeshIndex : KFPRI.RepCustomizationInfo.HeadMeshIndex,
bCustom ? EPRI.CustomCharacter.HeadSkinIndex : KFPRI.RepCustomizationInfo.HeadSkinIndex,
KFP,
KFPRI);
// skip dedicated for purely cosmetic stuff
2020-11-28 20:04:55 +00:00
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
2020-11-28 19:53:57 +00:00
{
2023-05-14 02:49:12 +00:00
// Must clear all attachments before trying to attach new ones,
2020-11-28 19:53:57 +00:00
// otherwise we might accidentally remove things we're not supposed to
2020-11-28 20:12:58 +00:00
for (AttachmentIdx=0; AttachmentIdx < `MAX_COSMETIC_ATTACHMENTS; AttachmentIdx++)
2020-11-28 19:53:57 +00:00
{
// Clear any previous attachments from other characters
C.DetachAttachment(AttachmentIdx, KFP);
}
// Cosmetic attachment mesh & skin. Index of 255 implies don't use any attachments (default)
2020-11-28 20:12:58 +00:00
for (AttachmentIdx=0; AttachmentIdx < `MAX_COSMETIC_ATTACHMENTS; AttachmentIdx++)
2020-11-28 19:53:57 +00:00
{
CosmeticMeshIdx = bCustom ? EPRI.CustomCharacter.AttachmentMeshIndices[AttachmentIdx] : KFPRI.RepCustomizationInfo.AttachmentMeshIndices[AttachmentIdx];
2020-11-28 20:04:55 +00:00
if (CosmeticMeshIdx != `CLEARED_ATTACHMENT_INDEX && CosmeticMeshIdx != INDEX_NONE)
2020-11-28 19:53:57 +00:00
{
bMaskHeadMesh = bMaskHeadMesh || C.CosmeticVariants[CosmeticMeshIdx].bMaskHeadMesh;
// Attach all saved attachments to the character
SetAttachmentMeshAndSkin(C,
CosmeticMeshIdx,
bCustom ? EPRI.CustomCharacter.AttachmentSkinIndices[AttachmentIdx] : KFPRI.RepCustomizationInfo.AttachmentSkinIndices[AttachmentIdx],
KFP, KFPRI);
}
}
// initial mask for new MIC (also see ResetHeadMaskParam())
InitCharacterMICs(C, KFP, bMaskHeadMesh);
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
static final function SetBodyMeshAndSkin(KFCharacterInfo_Human C,
int CurrentBodyMeshIndex,
int CurrentBodySkinIndex,
2020-11-28 19:53:57 +00:00
KFPawn KFP,
2020-11-28 20:04:55 +00:00
KFPlayerReplicationInfo KFPRI)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local string CharBodyMeshName;
local SkeletalMesh CharBodyMesh;
//Always use default body on servers
if (KFP.WorldInfo.NetMode == NM_DedicatedServer)
{
CurrentBodyMeshIndex = 0;
CurrentBodySkinIndex = 0;
}
// Character Mesh
2020-11-28 20:12:58 +00:00
if (C.BodyVariants.length > 0)
2020-11-28 19:53:57 +00:00
{
// Assign a skin to the body mesh as a material override
CurrentBodyMeshIndex = (CurrentBodyMeshIndex < C.BodyVariants.length) ? CurrentBodyMeshIndex : 0;
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
if (KFPRI.StartLoadCosmeticContent(C, ECOSMETICTYPE_Body, CurrentBodyMeshIndex))
{
return;
}
// Retrieve the name of the meshes to be used from the archetype
CharBodyMeshName = C.BodyVariants[CurrentBodyMeshIndex].MeshName;
// Load the meshes
CharBodyMesh = SkeletalMesh(SafeLoadObject(CharBodyMeshName, class'SkeletalMesh'));
// Assign the body mesh to the pawn
2020-11-28 20:04:55 +00:00
if (CharBodyMesh != KFP.Mesh.SkeletalMesh)
2020-11-28 19:53:57 +00:00
{
KFP.Mesh.SetSkeletalMesh(CharBodyMesh);
KFP.OnCharacterMeshChanged();
}
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
{
SetBodySkinMaterial(C, C.BodyVariants[CurrentBodyMeshIndex], CurrentBodySkinIndex, KFP);
}
}
else
{
`warn("Character does not have a valid mesh");
}
2017-10-20 02:00:49 +00:00
}
static final function SetBodySkinMaterial(KFCharacterInfo_Human C, OutfitVariants CurrentVariant, int NewSkinIndex, KFPawn KFP)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local int i;
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
{
2020-11-28 20:12:58 +00:00
if (CurrentVariant.SkinVariations.length > 0)
2020-11-28 19:53:57 +00:00
{
// Assign a skin to the body mesh as a material override
NewSkinIndex = (NewSkinIndex < CurrentVariant.SkinVariations.length) ? NewSkinIndex : 0;
KFP.Mesh.SetMaterial(C.BodyMaterialID, CurrentVariant.SkinVariations[NewSkinIndex].Skin);
}
else
{
// Use material specified in the mesh asset
2020-11-28 20:12:58 +00:00
for (i=0; i<KFP.Mesh.GetNumElements(); i++)
2020-11-28 19:53:57 +00:00
{
KFP.Mesh.SetMaterial(i, none);
}
}
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
static final function SetHeadSkinMaterial(KFCharacterInfo_Human C, OutfitVariants CurrentVariant, byte NewSkinIndex, KFPawn KFP)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local int i;
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
{
2020-11-28 20:12:58 +00:00
if (CurrentVariant.SkinVariations.length > 0)
2020-11-28 19:53:57 +00:00
{
// Assign a skin to the body mesh as a material override
NewSkinIndex = (NewSkinIndex < CurrentVariant.SkinVariations.length) ? NewSkinIndex : 0;
KFP.ThirdPersonHeadMeshComponent.SetMaterial(C.HeadMaterialID, CurrentVariant.SkinVariations[NewSkinIndex].Skin);
}
else
{
// Use material specified in the mesh asset
2020-11-28 20:12:58 +00:00
for (i=0; i<KFP.ThirdPersonHeadMeshComponent.GetNumElements(); i++)
2020-11-28 19:53:57 +00:00
{
KFP.ThirdPersonHeadMeshComponent.SetMaterial(i, none);
}
}
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
static final function SetHeadMeshAndSkin(KFCharacterInfo_Human C,
2020-11-28 19:53:57 +00:00
byte CurrentHeadMeshIndex,
byte CurrentHeadSkinIndex,
KFPawn KFP,
2020-11-28 20:04:55 +00:00
KFPlayerReplicationInfo KFPRI)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local string CharHeadMeshName;
local SkeletalMesh CharHeadMesh;
2020-11-28 20:04:55 +00:00
if (C.HeadVariants.length > 0)
2020-11-28 19:53:57 +00:00
{
CurrentHeadMeshIndex = (CurrentHeadMeshIndex < C.HeadVariants.length) ? CurrentHeadMeshIndex : 0;
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
if (KFPRI.StartLoadCosmeticContent(C, ECOSMETICTYPE_Head, CurrentHeadMeshIndex))
{
return;
}
CharHeadMeshName = C.HeadVariants[CurrentHeadMeshIndex].MeshName;
CharHeadMesh = SkeletalMesh(DynamicLoadObject(CharHeadMeshName, class'SkeletalMesh'));
// Parent the third person head mesh to the body mesh
KFP.ThirdPersonHeadMeshComponent.SetSkeletalMesh(CharHeadMesh);
KFP.ThirdPersonHeadMeshComponent.SetScale(C.DefaultMeshScale);
KFP.ThirdPersonHeadMeshComponent.SetParentAnimComponent(KFP.Mesh);
KFP.ThirdPersonHeadMeshComponent.SetShadowParent(KFP.Mesh);
KFP.ThirdPersonHeadMeshComponent.SetLODParent(KFP.Mesh);
KFP.AttachComponent(KFP.ThirdPersonHeadMeshComponent);
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
{
SetHeadSkinMaterial(C, C.HeadVariants[CurrentHeadMeshIndex], CurrentHeadSkinIndex, KFP);
}
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
static final function SetAttachmentSkinMaterial(KFCharacterInfo_Human C,
2020-11-28 19:53:57 +00:00
int PawnAttachmentIndex,
const out AttachmentVariants CurrentVariant,
byte NewSkinIndex,
KFPawn KFP,
optional bool bIsFirstPerson)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local int i;
if (KFP.WorldInfo.NetMode != NM_DedicatedServer)
{
2020-11-28 20:12:58 +00:00
if (CurrentVariant.AttachmentItem.SkinVariations.length > 0)
2020-11-28 19:53:57 +00:00
{
// Assign a skin to the attachment mesh as a material override
2020-11-28 20:04:55 +00:00
if (NewSkinIndex < CurrentVariant.AttachmentItem.SkinVariations.length)
2020-11-28 19:53:57 +00:00
{
if (bIsFirstPerson)
{
if (KFP.FirstPersonAttachments[PawnAttachmentIndex] != none)
{
KFP.FirstPersonAttachments[PawnAttachmentIndex].SetMaterial(
CurrentVariant.AttachmentItem.SkinMaterialID,
CurrentVariant.AttachmentItem.SkinVariations[NewSkinIndex].Skin1p);
}
}
else
{
KFP.ThirdPersonAttachments[PawnAttachmentIndex].SetMaterial(
CurrentVariant.AttachmentItem.SkinMaterialID,
CurrentVariant.AttachmentItem.SkinVariations[NewSkinIndex].Skin);
}
}
else
{
`log("Out of bounds skin index for"@CurrentVariant.MeshName);
C.RemoveAttachmentMeshAndSkin(PawnAttachmentIndex, KFP);
}
}
else
{
if (bIsFirstPerson)
{
if (KFP.FirstPersonAttachments[PawnAttachmentIndex] != none)
{
for (i = 0; i < KFP.FirstPersonAttachments[PawnAttachmentIndex].GetNumElements(); i++)
{
KFP.FirstPersonAttachments[PawnAttachmentIndex].SetMaterial(i, none);
}
}
}
else
{
// Use material specified in the mesh asset
2020-11-28 20:12:58 +00:00
for (i=0; i < KFP.ThirdPersonAttachments[PawnAttachmentIndex].GetNumElements(); i++)
2020-11-28 19:53:57 +00:00
{
KFP.ThirdPersonAttachments[PawnAttachmentIndex].SetMaterial(i, none);
}
}
}
}
2017-10-20 02:00:49 +00:00
}
2020-11-28 20:04:55 +00:00
static final function SetAttachmentMeshAndSkin(KFCharacterInfo_Human C,
2020-11-28 19:53:57 +00:00
int CurrentAttachmentMeshIndex,
int CurrentAttachmentSkinIndex,
KFPawn KFP,
KFPlayerReplicationInfo KFPRI,
2020-11-28 20:04:55 +00:00
optional bool bIsFirstPerson)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local string CharAttachmentMeshName;
local name CharAttachmentSocketName;
local int AttachmentSlotIndex;
local SkeletalMeshComponent AttachmentMesh;
if (KFP.WorldInfo.NetMode == NM_DedicatedServer)
{
return;
}
// Clear any previously attachments for the same slot
//DetachConflictingAttachments(CurrentAttachmentMeshIndex, KFP, KFPRI);
// Get a slot where this attachment could fit
AttachmentSlotIndex = GetAttachmentSlotIndex(C, CurrentAttachmentMeshIndex, KFP, KFPRI);
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
if (AttachmentSlotIndex == INDEX_NONE)
{
return;
}
// Since cosmetic attachments are optional, do not choose index 0 if none is
// specified unlike the the head and body meshes
2020-11-28 20:04:55 +00:00
if (C.CosmeticVariants.Length > 0 &&
2023-05-14 03:01:17 +00:00
CurrentAttachmentMeshIndex < C.CosmeticVariants.Length)
2020-11-28 19:53:57 +00:00
{
if (KFPRI.StartLoadCosmeticContent(C, ECOSMETICTYPE_Attachment, CurrentAttachmentMeshIndex))
{
return;
}
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
// Cache values from character info
CharAttachmentMeshName = bIsFirstPerson ? C.Get1pMeshByIndex(CurrentAttachmentMeshIndex) : C.GetMeshByIndex(CurrentAttachmentMeshIndex);
CharAttachmentSocketName = bIsFirstPerson ? C.CosmeticVariants[CurrentAttachmentMeshIndex].AttachmentItem.SocketName1p : C.CosmeticVariants[CurrentAttachmentMeshIndex].AttachmentItem.SocketName;
AttachmentMesh = bIsFirstPerson ? KFP.ArmsMesh : KFP.Mesh;
// If previously attached and we could have changed outfits (e.g. local player UI) then re-validate
// required skeletal mesh socket. Must be after body mesh DLO, but before AttachComponent.
2020-11-28 20:04:55 +00:00
if (KFP.IsLocallyControlled())
2020-11-28 19:53:57 +00:00
{
2020-11-28 20:04:55 +00:00
if (CharAttachmentSocketName != '' && KFP.Mesh.GetSocketByName(CharAttachmentSocketName) == None)
2020-11-28 19:53:57 +00:00
{
C.RemoveAttachmentMeshAndSkin(AttachmentSlotIndex, KFP, KFPRI);
return;
}
}
// Set First Person Cosmetic if mesh exists for it.
2020-11-28 20:12:58 +00:00
if (CharAttachmentMeshName != "")
2020-11-28 19:53:57 +00:00
{
// Set Cosmetic Mesh
SetAttachmentMesh(C, CurrentAttachmentMeshIndex, AttachmentSlotIndex, CharAttachmentMeshName, CharAttachmentSocketName, AttachmentMesh, KFP, bIsFirstPerson);
}
else
{
// Make sure to clear out attachment if we're replacing with nothing.
2020-11-28 20:12:58 +00:00
if (bIsFirstPerson)
2020-11-28 19:53:57 +00:00
{
KFP.FirstPersonAttachments[AttachmentSlotIndex] = none;
KFP.FirstPersonAttachmentSocketNames[AttachmentSlotIndex] = '';
}
}
// Set Cosmetic Skin
SetAttachmentSkinMaterial(
C,
AttachmentSlotIndex,
C.CosmeticVariants[CurrentAttachmentMeshIndex],
CurrentAttachmentSkinIndex,
KFP,
bIsFirstPerson);
}
// Treat `CLEARED_ATTACHMENT_INDEX as special value (for client detachment)
2020-11-28 20:12:58 +00:00
if (CurrentAttachmentMeshIndex == `CLEARED_ATTACHMENT_INDEX)
2020-11-28 19:53:57 +00:00
{
C.RemoveAttachmentMeshAndSkin(AttachmentSlotIndex, KFP, KFPRI);
}
2020-01-09 11:05:13 +00:00
}
static final function SetAttachmentMesh(KFCharacterInfo_Human C, int CurrentAttachmentMeshIndex, int AttachmentSlotIndex, string CharAttachmentMeshName, name CharAttachmentSocketName, SkeletalMeshComponent PawnMesh, KFPawn KFP, bool bIsFirstPerson = false)
{
2020-11-28 19:53:57 +00:00
local StaticMeshComponent StaticAttachment;
local SkeletalMeshComponent SkeletalAttachment;
local bool bIsSkeletalAttachment;
local StaticMesh CharAttachmentStaticMesh;
local SkeletalMesh CharacterAttachmentSkelMesh;
local float MaxDrawDistance;
local SkeletalMeshSocket AttachmentSocket;
local vector AttachmentLocationRelativeToSocket, AttachmentScaleRelativeToSocket;
local rotator AttachmentRotationRelativeToSocket;
MaxDrawDistance = C.CosmeticVariants[CurrentAttachmentMeshIndex].AttachmentItem.MaxDrawDistance;
AttachmentLocationRelativeToSocket = C.CosmeticVariants[CurrentAttachmentMeshIndex].RelativeTranslation;
AttachmentRotationRelativeToSocket = C.CosmeticVariants[CurrentAttachmentMeshIndex].RelativeRotation;
AttachmentScaleRelativeToSocket = C.CosmeticVariants[CurrentAttachmentMeshIndex].RelativeScale;
bIsSkeletalAttachment = C.CosmeticVariants[CurrentAttachmentMeshIndex].AttachmentItem.bIsSkeletalAttachment;
//`log("AttachmentLocationRelativeToSocket: x="$AttachmentLocationRelativeToSocket.x@"y="$AttachmentLocationRelativeToSocket.y@"z="$AttachmentLocationRelativeToSocket.z);
// If it is a skeletal attachment, parent anim it to the body mesh
if (bIsSkeletalAttachment)
{
if (bIsFirstPerson && (SkeletalMeshComponent(KFP.FirstPersonAttachments[AttachmentSlotIndex]) != none))
{
SkeletalAttachment = SkeletalMeshComponent(KFP.FirstPersonAttachments[AttachmentSlotIndex]);
}
else if (!bIsFirstPerson && (SkeletalMeshComponent(KFP.ThirdPersonAttachments[AttachmentSlotIndex]) != none))
{
SkeletalAttachment = SkeletalMeshComponent(KFP.ThirdPersonAttachments[AttachmentSlotIndex]);
}
else
{
SkeletalAttachment = new(KFP) class'KFSkeletalMeshComponent';
if (bIsFirstPerson)
{
C.SetFirstPersonCosmeticAttachment(SkeletalAttachment);
}
SkeletalAttachment.SetActorCollision(false, false);
2020-11-28 20:12:58 +00:00
if (bIsFirstPerson)
2020-11-28 19:53:57 +00:00
{
KFP.FirstPersonAttachments[AttachmentSlotIndex] = SkeletalAttachment;
}
else
{
KFP.ThirdPersonAttachments[AttachmentSlotIndex] = SkeletalAttachment;
}
}
// Load and assign skeletal mesh
CharacterAttachmentSkelMesh = SkeletalMesh(DynamicLoadObject(CharAttachmentMeshName, class'SkeletalMesh'));
SkeletalAttachment.SetSkeletalMesh(CharacterAttachmentSkelMesh);
// Parent animation and LOD transitions to body mesh
SkeletalAttachment.SetParentAnimComponent(PawnMesh);
SkeletalAttachment.SetLODParent(PawnMesh);
SkeletalAttachment.SetScale(C.DefaultMeshScale);
SkeletalAttachment.SetCullDistance(MaxDrawDistance);
SkeletalAttachment.SetShadowParent(PawnMesh);
SkeletalAttachment.SetLightingChannels(KFP.PawnLightingChannel);
KFP.AttachComponent(SkeletalAttachment);
}
// Otherwise (if static), attach to a socket on the body mesh
else
{
if (!bIsFirstPerson && (StaticMeshComponent(KFP.ThirdPersonAttachments[AttachmentSlotIndex]) != none))
{
StaticAttachment = StaticMeshComponent(KFP.ThirdPersonAttachments[AttachmentSlotIndex]);
}
else if (bIsFirstPerson && (StaticMeshComponent(KFP.FirstPersonAttachments[AttachmentSlotIndex]) != none))
{
StaticAttachment = StaticMeshComponent(KFP.FirstPersonAttachments[AttachmentSlotIndex]);
}
else
{
StaticAttachment = new(KFP) class'StaticMeshComponent';
StaticAttachment.SetActorCollision(false, false);
2020-11-28 20:12:58 +00:00
if (bIsFirstPerson)
2020-11-28 19:53:57 +00:00
{
KFP.FirstPersonAttachments[AttachmentSlotIndex] = StaticAttachment;
}
else
{
KFP.ThirdPersonAttachments[AttachmentSlotIndex] = StaticAttachment;
}
}
// Load and assign static mesh
CharAttachmentStaticMesh = StaticMesh(DynamicLoadObject(CharAttachmentMeshName, class'StaticMesh'));
StaticAttachment.SetStaticMesh(CharAttachmentStaticMesh);
// Set properties
StaticAttachment.SetScale(C.DefaultMeshScale);
StaticAttachment.SetCullDistance(MaxDrawDistance);
StaticAttachment.SetShadowParent(KFP.Mesh);
StaticAttachment.SetLightingChannels(KFP.PawnLightingChannel);
// For static meshes, attach to given socket
AttachmentSocket = PawnMesh.GetSocketByName(CharAttachmentSocketName);
PawnMesh.AttachComponent(
StaticAttachment,
AttachmentSocket.BoneName,
AttachmentSocket.RelativeLocation + AttachmentLocationRelativeToSocket,
AttachmentSocket.RelativeRotation + AttachmentRotationRelativeToSocket,
AttachmentSocket.RelativeScale * AttachmentScaleRelativeToSocket);
}
2020-11-28 20:12:58 +00:00
if (bIsFirstPerson)
2020-11-28 19:53:57 +00:00
{
KFP.FirstPersonAttachmentSocketNames[AttachmentSlotIndex] = CharAttachmentSocketName;
}
else
{
KFP.ThirdPersonAttachmentSocketNames[AttachmentSlotIndex] = CharAttachmentSocketName;
}
2017-10-20 02:00:49 +00:00
}
/**
2023-05-14 02:49:12 +00:00
* Removes any attachments that exist in the same socket or have overriding cases
* Network: Local Player
2017-10-20 02:00:49 +00:00
*/
static final function DetachConflictingAttachments(KFCharacterInfo_Human C, int NewAttachmentMeshIndex, KFPawn KFP, optional KFPlayerReplicationInfo KFPRI, optional out array<int> out_RemovedAttachments)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local name NewAttachmentSocketName;
local int i, CurrentAttachmentIdx;
local ExtPlayerReplicationInfo EPRI;
EPRI = ExtPlayerReplicationInfo(KFPRI);
2020-11-28 20:04:55 +00:00
if (EPRI==none || !EPRI.UsesCustomChar())
2020-11-28 19:53:57 +00:00
return;
2020-11-28 20:04:55 +00:00
if (C.CosmeticVariants.length > 0 &&
2023-05-14 03:01:17 +00:00
NewAttachmentMeshIndex < C.CosmeticVariants.length)
2020-11-28 19:53:57 +00:00
{
// The socket that this attachment requires
NewAttachmentSocketName = C.CosmeticVariants[NewAttachmentMeshIndex].AttachmentItem.SocketName;
2020-11-28 20:12:58 +00:00
for (i=0; i < `MAX_COSMETIC_ATTACHMENTS; i++)
2020-11-28 19:53:57 +00:00
{
CurrentAttachmentIdx = EPRI.CustomCharacter.AttachmentMeshIndices[i];
2020-11-28 20:04:55 +00:00
if (CurrentAttachmentIdx == `CLEARED_ATTACHMENT_INDEX)
2020-11-28 19:53:57 +00:00
continue;
// Remove the object if it is taking up our desired slot
2020-11-28 20:12:58 +00:00
if (KFP.ThirdPersonAttachmentSocketNames[i] != '' &&
2020-11-28 20:04:55 +00:00
KFP.ThirdPersonAttachmentSocketNames[i] == NewAttachmentSocketName)
2020-11-28 19:53:57 +00:00
{
2023-05-14 02:49:12 +00:00
C.RemoveAttachmentMeshAndSkin(i, KFP, KFPRI);
out_RemovedAttachments.AddItem(i);
2020-11-28 19:53:57 +00:00
continue;
}
// Remove the object if it cannot exist at the same time as another equipped item
2020-11-28 20:12:58 +00:00
if (C.GetOverrideCase(CurrentAttachmentIdx, NewAttachmentMeshIndex))
2020-11-28 19:53:57 +00:00
{
C.RemoveAttachmentMeshAndSkin(i, KFP, KFPRI);
out_RemovedAttachments.AddItem(i);
2020-11-28 19:53:57 +00:00
continue;
}
// Check inverse override
2020-11-28 20:12:58 +00:00
if (C.GetOverrideCase(NewAttachmentMeshIndex, CurrentAttachmentIdx))
2020-11-28 19:53:57 +00:00
{
C.RemoveAttachmentMeshAndSkin(i, KFP, KFPRI);
out_RemovedAttachments.AddItem(i);
2020-11-28 19:53:57 +00:00
continue;
}
}
}
2017-10-20 02:00:49 +00:00
}
/** Assign an arm mesh and material to this pawn */
2020-11-28 20:04:55 +00:00
static final function SetFirstPersonArmsFromArch(KFCharacterInfo_Human C, KFPawn KFP, optional KFPlayerReplicationInfo KFPRI)
2017-10-20 02:00:49 +00:00
{
2020-11-28 19:53:57 +00:00
local ExtPlayerReplicationInfo EPRI;
local bool bCustom;
local int AttachmentIdx, CosmeticMeshIdx;
2020-11-28 20:04:55 +00:00
if (KFPRI == none)
2020-11-28 19:53:57 +00:00
{
`Warn("Does not have a KFPRI" @ C);
2023-05-14 03:01:17 +00:00
return;
2020-11-28 19:53:57 +00:00
}
EPRI = ExtPlayerReplicationInfo(KFPRI);
bCustom = (EPRI!=None ? EPRI.UsesCustomChar() : false);
2023-05-14 02:49:12 +00:00
// First person arms mesh and skin are based on body mesh & skin.
2020-11-28 19:53:57 +00:00
// Index of 255 implies use index 0 (default).
C.SetArmsMeshAndSkin(
bCustom ? EPRI.CustomCharacter.BodyMeshIndex : KFPRI.RepCustomizationInfo.BodyMeshIndex,
bCustom ? EPRI.CustomCharacter.BodySkinIndex : KFPRI.RepCustomizationInfo.BodySkinIndex,
KFP,
KFPRI);
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
for (AttachmentIdx = 0; AttachmentIdx < `MAX_COSMETIC_ATTACHMENTS; AttachmentIdx++)
{
CosmeticMeshIdx = bCustom ? EPRI.CustomCharacter.AttachmentMeshIndices[AttachmentIdx] : KFPRI.RepCustomizationInfo.AttachmentMeshIndices[AttachmentIdx];
if (CosmeticMeshIdx != `CLEARED_ATTACHMENT_INDEX && CosmeticMeshIdx != INDEX_NONE)
{
// Attach all saved attachments to the character
SetAttachmentMeshAndSkin(
C,
CosmeticMeshIdx,
bCustom ? EPRI.CustomCharacter.AttachmentSkinIndices[AttachmentIdx] : KFPRI.RepCustomizationInfo.AttachmentSkinIndices[AttachmentIdx],
KFP, KFPRI, true);
}
}
2017-10-20 02:00:49 +00:00
}
2020-01-09 11:05:13 +00:00
static function int GetAttachmentSlotIndex(
2020-11-28 19:53:57 +00:00
KFCharacterInfo_Human C,
int CurrentAttachmentMeshIndex,
KFPawn KFP,
KFPlayerReplicationInfo KFPRI)
2020-01-09 11:05:13 +00:00
{
2020-11-28 19:53:57 +00:00
local int AttachmentIdx,CosmeticMeshIdx;
local ExtPlayerReplicationInfo EPRI;
local bool bCustom;
2020-11-28 20:12:58 +00:00
if (KFPRI == None)
2020-11-28 19:53:57 +00:00
{
`warn("GetAttachmentSlotIndex - NO KFPRI");
return INDEX_NONE;
}
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
EPRI = ExtPlayerReplicationInfo(KFPRI);
bCustom = (EPRI!=None ? EPRI.UsesCustomChar() : false);
2023-05-14 02:49:12 +00:00
2020-11-28 19:53:57 +00:00
// Return the next available attachment index or the index that matches this mesh
2020-11-28 20:12:58 +00:00
for (AttachmentIdx = 0; AttachmentIdx < `MAX_COSMETIC_ATTACHMENTS; AttachmentIdx++)
2020-11-28 19:53:57 +00:00
{
CosmeticMeshIdx = bCustom ? EPRI.CustomCharacter.AttachmentMeshIndices[AttachmentIdx] : KFPRI.RepCustomizationInfo.AttachmentMeshIndices[AttachmentIdx];
if (CosmeticMeshIdx == CurrentAttachmentMeshIndex)
2020-11-28 19:53:57 +00:00
{
return AttachmentIdx;
}
}
return INDEX_NONE;
2020-01-09 11:05:13 +00:00
}
static function bool IsAttachmentAvailable(KFCharacterInfo_Human C, const out AttachmentVariants Attachment, Pawn PreviewPawn)
{
2020-11-28 20:04:55 +00:00
if (Attachment.AttachmentItem.SocketName != '' && PreviewPawn.Mesh.GetSocketByName(Attachment.AttachmentItem.SocketName) == None)
2020-11-28 19:53:57 +00:00
{
return false;
}
2020-01-09 11:05:13 +00:00
2020-11-28 19:53:57 +00:00
return true;
2023-05-14 02:49:12 +00:00
}