Custom Hat Player NPC Classes (v1)

#CustomActor

It allows us to set up startup expression and animation, custom skin and hat class, weapon visibility, and text above the head. Bow Kid and Hat Kid variants included! (it only doesn't support costumes for now)

mcu8_NPC_Player.uc

[RAW] [Download]

/**
 *
 * Copyright 2012-2015 Gears for Breakfast ApS. All Rights Reserved.
 */
 
 class mcu8_NPC_Player extends Hat_NPC
    dependson(Hat_ExpressionComponent)
    abstract;
 
var Array<Inventory> MyInventory;
var transient PlayerController PlayerOwner;
var transient bool HasUpdatedVisualsOnce;
var Hat_TextRenderComponent TextRenderComponent;
var MeshComponent PreviewMeshes[2];
 
var(CustomNPC_Visuals) bool GiveWeapon;
var(CustomNPC_Visuals) class<Hat_Weapon> WeaponClass;
var(CustomNPC_Visuals) bool WeaponInitiallyVisible;
var(CustomNPC_Visuals) class<Hat_Ability_Trigger> HatClass;
var(CustomNPC_Visuals) class<Hat_CosmeticItemQualityInfo> HatQualityInfo;
var(CustomNPC_Visuals) class<Hat_Collectible_Skin> MySkinClass;
var(CustomNPC_Visuals) string DisplayName;
var(CustomNPC_Visuals) Color DisplayColor;
 
var(CustomNPC_Animations) Name InitialAnimation;
var(CustomNPC_Animations) bool Loop;
var(CustomNPC_Animations) bool Restart;
var(CustomNPC_Animations) float PlayRate;
var(CustomNPC_Animations) float AnimationDuration;
 
var(CustomNPC_Expressions) EExpressionType InitialExpression;
var(CustomNPC_Expressions) bool MakeDefault;
var(CustomNPC_Expressions) float Duration;
 
var(Cosmetics) bool DisableUpperBody;
var(Cosmetics) bool DisableLegs;
var(Cosmetics) bool DisableHats;
var(Cosmetics) bool DisableMasks; // If we don't want to cover the player's face in this cinematic, turn this on
 
var bool IsLowQuality; // Sets IsLowQuality on its cosmeticitems
var transient protected class<Hat_Collectible_Skin> CurrentSkinClass;
 
function OnEnabled()
{
    Super.OnEnabled();
    UpdateVisuals();
}
 
simulated event PostBeginPlay()
{
    Super.PostBeginPlay();
    if (!HasUpdatedVisualsOnce)
        UpdateVisuals();
}
 
simulated event Destroyed()
{
    DiscardInventory();
    Super.Destroyed();
}
 
function PlayAnimationMy(SkeletalMeshComponent c, bool IsPlay)
{
    local float dur;
    local AnimNodeSlot slot;
 
    if (c.AnimTreeTemplate != None)
    {
        foreach c.AllAnimNodes(class'AnimNodeSlot', slot)
        {
            if (InitialAnimation == '' || !IsPlay)
                slot.StopCustomAnim(0.1);
            else
            {
                slot.PlayCustomAnim(InitialAnimation, PlayRate, 0.1, 0.1, Loop);
            }
            break;
        }
        return;
    }
 
    dur = 0.0;
    if (PlayRate > 0 && AnimationDuration > 0) dur = AnimationDuration / PlayRate;
    c.PlayAnim(InitialAnimation, dur, Loop, Restart);
}
 
function SetExpression(Actor Act, EExpressionType Exp, optional float d)
{
    local Hat_ExpressionComponent Cmp;
 
    Cmp = GetActorExpression(Act);
    if (Cmp == None)
    {
        /*
        Cmp = new(Act) class'Hat_ExpressionComponent_HatKid';
        Act.AttachComponent(Cmp);
        Cmp.Init();
        if (Hat_SkeletalMeshActor(Act) != None) Hat_SkeletalMeshActor(Act).ExpressionComponent = Cmp;
        */
        return;
    }
 
    if (MakeDefault)
    {
        Cmp.SetDefaultExpression(Exp);
    }
    else
    {
        Cmp.SetExpression(Exp, d);
    }
 
    if (Exp == EExpressionType_Ignore)
    {
        Expression.Reset();
        Expression.Init();
    }
}
 
static function Hat_ExpressionComponent GetActorExpression(Actor Act)
{
    local Hat_ExpressionComponent Cmp;
 
    foreach Act.ComponentList(class'Hat_ExpressionComponent', Cmp)
        return Cmp;
 
    return None;
}
 
function UpdateVisuals()
{
    local int i;
 
    for (i = 0; i < 2; i++)
    {
        if (PreviewMeshes[i] == None) continue;
        DetachComponent(PreviewMeshes[i]);
        PreviewMeshes[i] = None;
    }
 
    AttachComponent(Expression);
    Expression.Reset();
    Expression.Init();
 
    AddDefaultInventory();
    HasUpdatedVisualsOnce = true;
 
    PlayAnimationMy(SkeletalMeshComponent, true);
 
    If (Expression != None) {
        SetExpression(Self, InitialExpression, Duration);
    }
 
    TextRenderComponent.Text = DisplayName;
    TextRenderComponent.TextColor = DisplayColor;
    TextRenderComponent.SetHidden(DisplayName == "");
    ReattachComponent(TextRenderComponent);
}
 
function AddDefaultInventory()
{
    DiscardInventory();
 
    CreateInventoryForPlayerTypeNPC();
    CreateInventory(HatClass, HatQualityInfo);
 
    GiveUmbrella();
    ShowWeapon(WeaponInitiallyVisible);
 
    if (MySkinClass != None)
    {        
        InitMaterialInstances();
        CurrentSkinClass = MySkinClass;
        CurrentSkinClass.static.Apply(self);
    }
}
 
function CreateInventoryForPlayerTypeNPC();
 
function Array<MeshComponent> GetMyMaterialMeshComponents(optional bool attachments)
{
    local Array<MeshComponent> OutArray;
    local Inventory Inv;
    local MeshComponent Mesh;
 
    OutArray = Super.GetMyMaterialMeshComponents(attachments);
 
    foreach MyInventory(Inv)
    {
        Mesh = MeshComponent(Inv.DroppedPickupMesh);
 
        if (Mesh != None)
        {
            OutArray.AddItem(Mesh);
        }
    }
 
    return OutArray;
}
 
simulated expensive function InitMaterialInstances()
{
    local Inventory Inv;
    local MeshComponent Mesh;
 
    Super.InitMaterialInstances();
 
    foreach MyInventory(Inv)
    {
        Mesh = MeshComponent(Inv.DroppedPickupMesh);
 
        if (Mesh != None)
        {
            InitMaterialInstancesMesh(Mesh);
 
            if (Inv.IsA('Hat_CosmeticItem'))
                Hat_CosmeticItem(Inv).OnInitInventoryMaterialInstancesQualityInfo(Mesh, Hat_CosmeticItem(Inv).MyItemQualityInfo);
        }
    }
 
    // We have to re-initialize expression any time after
    if (Expression != None)
    {
        Expression.Reset();
        Expression.Init();
    }
}
 
function GiveLoadoutItem(Hat_BackpackItem InvClass)
{
    if (class<Inventory>(InvClass.BackpackClass) == None) return;
    CreateInventory(class<Inventory>(InvClass.BackpackClass), InvClass.IsA('Hat_LoadoutBackpackItem') ? Hat_LoadoutBackpackItem(InvClass).ItemQualityInfo : None);
}
 
function Inventory CreateInventory(class<Inventory> c, optional class<Hat_CosmeticItemQualityInfo> ItemQualityInfo)
{
    local Inventory    Inv;
    local Hat_CosmeticItem cos;
    local Name SpawnTag;
 
    // We have a custom tag set, so likely we're set to a specific cubemap. Our items should be too
    SpawnTag = '';
    if (InStr(Locs(string(Tag)), "hat_npc_player") == INDEX_NONE)
    {
        SpawnTag = Tag;
    }
 
    Inv = Spawn(c, None, SpawnTag,,,,true);
    if (Inv == None) return None;
 
    MyInventory.AddItem(Inv);
    cos = Hat_CosmeticItem(Inv);
    if (cos != None)
    {
        // MEKU TO-DO: Work out why Instigator is set?
        cos.Instigator = None;
        cos.ActorInstigator = None;
 
        if (ItemQualityInfo != None)
            cos.UpdateItemQuality(ItemQualityInfo);
 
        cos.ActorInstigator = self;
        cos.IsLowQuality = IsLowQuality;
        if (Hat_Ability(Inv) != None)
        {
            Hat_Ability(Inv).UpdateArrayIndex();
        }
        if (!cos.AttachToOwner(self, cos.MyItemQualityInfo))
        {
            `if(`notdefined(FINAL_RELEASE))
            `broadcast("AttachToOwner failed: " $ c);
            `endif
        }
        cos.SetOcclusionHidden(true);
        if (cos.DroppedPickupMesh != None)
            cos.DroppedPickupMesh.SetDepthPriorityGroup(SkeletalMeshComponent.DepthPriorityGroup);
        if (!IsLowQuality && cos.DroppedPickupMesh != None && SkeletalMeshComponent(cos.DroppedPickupMesh) != None)
            SkeletalMeshComponent(cos.DroppedPickupMesh).SetHasPhysicsAssetInstance(SkeletalMeshComponent.bHasPhysicsAssetInstance);
        if (!IsLowQuality && cos.OccludedMesh != None && SkeletalMeshComponent(cos.OccludedMesh) != None)
            SkeletalMeshComponent(cos.OccludedMesh).SetHasPhysicsAssetInstance(SkeletalMeshComponent.bHasPhysicsAssetInstance);
    }
    else if (Inv.IsA('Hat_Weapon'))
    {
        if (Inv.DroppedPickupMesh != None)
            Hat_Weapon(Inv).AttachMeshToOwner(self, Inv.DroppedPickupMesh);
    }
    return Inv;
}
 
function DiscardInventory()
{
    local int i;
    for (i = MyInventory.Length-1; i >= 0; i--)
    {
        MyInventory[i].ItemRemovedFromInvManager();
        MyInventory[i].Destroy();
    }
    MyInventory.Length = 0;
 
    SkeletalMeshComponent.SetSectionGroup('');
 
    // We have to un-apply the skin by applying the default skin (which resets parameters)
    if (CurrentSkinClass != None)
    {
        //`broadcast("Removed: " $ CurrentSkinClass);
        CurrentSkinClass.static.Removed(self);
        CurrentSkinClass = None;
 
 
        class'Hat_Collectible_Skin'.static.Apply(self);
 
        if (Expression != None)
        {
            Expression.Reset();
            Expression.Init();
        }
    }
}
 
simulated function ShowWeapon(bool b)
{
    local int i;
    for (i = 0; i < MyInventory.Length; i++)
    {
        if (!MyInventory[i].IsA('Hat_Weapon')) continue;
            MyInventory[i].SetHidden(!b);
        if (MyInventory[i].IsA('Hat_Weapon_Umbrella'))
            Hat_Weapon_Umbrella(MyInventory[i]).TakeOut();
    }
}
 
function bool GiveUmbrella()
{
    local Hat_BackpackItem bi;
    if (!GiveWeapon) return false;
    bi = new class'Hat_BackpackItem';
    bi.BackpackClass = WeaponClass;
    GiveLoadoutItem(bi);
    return true;
}
 
function bool IsTooFarAway()
{
    return false;
}
 
defaultproperties
{    
    Begin Object Class=Hat_TextRenderComponent Name=TextRenderComponent0
        Translation=(Z=115)
        Size = 0.8f
        TextLimit = 50
        HiddenGame=false
        TextColor=(R=127,G=187,B=213)
        CastShadow = false
        //Rotation=(Yaw=-16384)
    End Object
    TextRenderComponent = TextRenderComponent0;
    Components.Add(TextRenderComponent0)
 
    TickOptimize = TickOptimize_None;
 
    bBlockActors = false;
    bCollideWorld = true;
    bCollideActors = true;
    BlockRigidBody = false;
    bPathColliding = false;
    bCanBeDamaged = true;
 
    GiveWeapon = false;
    WeaponInitiallyVisible = false;
    WeaponClass = class'Hat_Weapon_Umbrella';
    HatClass = class'Hat_Ability_Help';
    HatQualityInfo = None;
 
    InitialAnimation = "Idle"
    Loop = true;
    Restart = false;
    PlayRate = 1.0;
    AnimationDuration = 0.0;
 
    DisplayColor=(R=127,G=187,B=213)
 
    Duration = 0;
}
mcu8_NPC_Player_BowKid.uc

[RAW] [Download]

class mcu8_NPC_Player_BowKid extends mcu8_NPC_Player
    placeable;
 
defaultproperties
{
    Begin Object Name=SkeletalMeshComponent0
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_Coop.models.bowkid_head_skm'
        PhysicsAsset=PhysicsAsset'HatinTime_Characters_CoPartner.Physics.CoPartner_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        AnimTreeTemplate=AnimTree'HatInTime_Characters.AnimTree.MatineeAnimTree'
        Animations=None
        bNoSelfShadow=true
        bUseTickOptimization = false;
    End Object
 
    Begin Object Class=SkeletalMeshComponent Name=PreviewMesh0
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_Coop.models.bowkid_body_skm'
        PhysicsAsset=PhysicsAsset'HatInTime_Characters_Coop.Physics.bowkid_body_skm_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        bCacheAnimSequenceNodes=FALSE
        ShadowParent = SkeletalMeshComponent0
        ParentAnimComponent=SkeletalMeshComponent0
        LightEnvironment=MyLightEnvironment
        CanBlockCamera=false
        MaxDrawDistance = 7000;
        bDisableFaceFX = true;
        AlwaysLoadOnClient=false
        AlwaysLoadOnServer=false
    End Object
    Components.Add(PreviewMesh0)
    PreviewMeshes(0) = PreviewMesh0;
 
    Begin Object Class=SkeletalMeshComponent Name=PreviewMesh1
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_Coop.models.bowkid_legs_skm'
        PhysicsAsset=PhysicsAsset'HatInTime_Characters_HatKid.Physics.HatKidLegs_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        bCacheAnimSequenceNodes=FALSE
        ShadowParent = SkeletalMeshComponent0
        ParentAnimComponent=SkeletalMeshComponent0
        LightEnvironment=MyLightEnvironment
        CanBlockCamera=false
        MaxDrawDistance = 7000;
        bDisableFaceFX = true;
        AlwaysLoadOnClient=false
        AlwaysLoadOnServer=false
    End Object
    Components.Add(PreviewMesh1)
    PreviewMeshes(1) = PreviewMesh1;
 
    Begin Object Class=Hat_ExpressionComponent_BowKid Name=hExpression
 
    End Object
    Components.Add(hExpression);
    Expression = hExpression;
 
    HatQualityInfo = class'Hat_CosmeticItemQualityInfo_Help_Bow';
}
 
function CreateInventoryForPlayerTypeNPC()
{
    CreateInventory(class'Hat_CosmeticItem_BowKidUpperBody', None);
    CreateInventory(class'Hat_CosmeticItem_BowKidLegs', None);
}
 
mcu8_NPC_Player_HatKid.uc

[RAW] [Download]

class mcu8_NPC_Player_HatKid extends mcu8_NPC_Player
    placeable;
 
defaultproperties
{
    Begin Object Name=SkeletalMeshComponent0
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_HatKid.models.HatKidHead'
        PhysicsAsset=PhysicsAsset'HatInTime_Characters_HatKid.Physics.HatKidHead_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        AnimTreeTemplate=AnimTree'HatInTime_Characters.AnimTree.MatineeAnimTree'
        Animations=None
        bNoSelfShadow=true
        bUseTickOptimization = false;
    End Object
 
    Begin Object Class=SkeletalMeshComponent Name=PreviewMesh0
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_HatKid.models.HatKidBody'
        PhysicsAsset=PhysicsAsset'HatInTime_Characters_HatKid.Physics.HatKidBody_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        bCacheAnimSequenceNodes=FALSE
        ShadowParent = SkeletalMeshComponent0
        ParentAnimComponent=SkeletalMeshComponent0
        LightEnvironment=MyLightEnvironment
        CanBlockCamera=false
        MaxDrawDistance = 7000;
        bDisableFaceFX = true;
        AlwaysLoadOnClient=false
        AlwaysLoadOnServer=false
    End Object
    Components.Add(PreviewMesh0)
    PreviewMeshes(0) = PreviewMesh0;
 
    Begin Object Class=SkeletalMeshComponent Name=PreviewMesh1
        SkeletalMesh=SkeletalMesh'HatInTime_Characters_HatKid.models.HatKidLegs'
        PhysicsAsset=PhysicsAsset'HatInTime_Characters_HatKid.Physics.HatKidLegs_Physics'
        AnimSets(0)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Anims'
        AnimSets(1)=AnimSet'HatInTime_Characters_HatKid.AnimSet.HatKidV2_Attack_Anims'
        AnimSets(2)=AnimSet'HatInTime_Characters_HatKid2.AnimSet.HatKidV2_Cruise'
        AnimSets(3)=AnimSet'HatInTime_Characters_HatKid3.AnimSet.HatKidV2_Metro'
        bCacheAnimSequenceNodes=FALSE
        ShadowParent = SkeletalMeshComponent0
        ParentAnimComponent=SkeletalMeshComponent0
        LightEnvironment=MyLightEnvironment
        CanBlockCamera=false
        MaxDrawDistance = 7000;
        bDisableFaceFX = true;
        AlwaysLoadOnClient=false
        AlwaysLoadOnServer=false
    End Object
    Components.Add(PreviewMesh1)
    PreviewMeshes(1) = PreviewMesh1;
 
    Begin Object Class=Hat_ExpressionComponent_HatKid Name=hExpression
 
    End Object
    Components.Add(hExpression);
    Expression = hExpression;
}
 
function CreateInventoryForPlayerTypeNPC()
{
    CreateInventory(class'Hat_CosmeticItem_HatKidUpperBody', None);
    CreateInventory(class'Hat_CosmeticItem_HatKidLegs', None);
}