#RPGB Fix - The Shapeshifting effect can now be used on NPCs

1 messages · Page 1 of 1 (latest)

forest palm
#

Allowing enemies to be turned into small animals temporarily, making them unable to deal damage to targets.
Here, we refer to the player's code for shapeshifting and replicate it for the NPC code.
Principle:

  1. Cache the model, animations, and behaviors.
  2. Instantiate the new model to transform into, set the original model's transform parameters to the new model, and hide the original model.
  3. Update the UI name and icon.
  4. Set the animation controller and Avatar.
  5. Update and reset the NPC's behaviors and animations.
  6. When the transformation ends, destroy the transformed model, show the original model, and restore the cached animations and behaviors. Update the state to remove the transformation.

Fix Steps:

  1. Add a new variable in the RPGEffect.cs code to store the NPC prefab, so it can be synchronized with the settings on the AI unit side.
    See image A.
public RPGNpc shapeshiftingNPC;

Then, also write the save data in the CopyEntryData() method below.
See image B.

original.shapeshiftingNPC = copied.shapeshiftingNPC;
  1. In the RPGBuilderEditorEffectModule.cs code, within the DrawView() method:
    See image C.
    Add the newly created variable to get the NPC prefab.
    You can search for the location of currentEntry.ranks[i].shapeshiftingAnimatorControllerCombat.
currentEntry.ranks[i].shapeshiftingNPC = (RPGNpc)
                                RPGBuilderEditorFields.DrawHorizontalObject<RPGNpc>(
                                    "NPC Prefab", "", currentEntry.ranks[i].shapeshiftingNPC);//NEW ADD

This way, there will be an additional option in the editor, allowing you to drag in NPCs from the AI units. We need to obtain the Phase and NPC Prefab from it.
(In RPGBuilder, under Combat -> Effects, create a new Effect with Type set to Shapeshifting. Between the Animator Controller Combat and Avatar in the RANKS, there will be an additional option for NPC Prefab.)
See image D.

#
  1. In the MobCombatEntity.cs code, add a new variable to get the original model and a temporary container to store the original model.
    See image F.
public GameObject OrignModel;
public GameObject tempObj;

Add a new Start() method in the MobCombatEntity.cs code to cache some data.
See image G.

protected override void Start()
        {
            if (AIEntity)
            {
                CachedBehaviorTemplate = AIEntity.BehaviorTemplate;
                CachedAnimatorController = ThisAnimator.runtimeAnimatorController;
                CachedAnimatorAvatar = ThisAnimator.avatar;
                CachedAnimatorUseRootMotion = ThisAnimator.applyRootMotion;
                CachedAnimatorUpdateMode = ThisAnimator.updateMode;
                CachedAnimatorCullingMode = ThisAnimator.cullingMode;

                OrignModel = transform.GetChild(0).gameObject;
                tempObj = null;
            }
        }

Find the EntityDeath() method and add the following:
See image H.

if (IsShapeshifted()) ResetShapeshifting();

If you want to prevent the NPC from dealing damage to targets after transforming, you need to add a check in the FixedUpdate() method:
See image i.

if (Shapeshifted)
            {
                EndAbility(0);
                ResetChanneling();
                ResetCasting();
                AIEntity.SetInCombatState(false);
            }
#

Copy the following two methods from CombatEntity.cs to MobCombatEntity.cs:

InitEffectType() method and EndStateEffect() method.

public override void InitEffectType(RPGEffect effect, int effectRank)
        {
            switch (effect.effectType)
            {
                case RPGEffect.EFFECT_TYPE.Shapeshifting:
                    InitShapeshifting(effect, effectRank);
                    break;
                case RPGEffect.EFFECT_TYPE.Flying:
                    break;
                case RPGEffect.EFFECT_TYPE.Stealth:
                    break;
                case RPGEffect.EFFECT_TYPE.Mount:
                    break;
            }
        }

public override void EndStateEffect(int stateIndex)
        {
            if (stateIndex < 0 || stateIndex >= States.Count) return;
            var state = States[stateIndex]; 
            switch (state.stateEffect.effectType)
            {
                case RPGEffect.EFFECT_TYPE.Stun:
                case RPGEffect.EFFECT_TYPE.Sleep:
                case RPGEffect.EFFECT_TYPE.Root:
                    ThisAnimator.SetBool("Stunned", false);
                    ResetStun();
                    break;
                case RPGEffect.EFFECT_TYPE.Shapeshifting:
                    if (IsShapeshifted()) ResetShapeshifting();
                    break;

                case RPGEffect.EFFECT_TYPE.Flying:
                    break;

                case RPGEffect.EFFECT_TYPE.Stealth:
                    return;
                case RPGEffect.EFFECT_TYPE.Mount:
                    break;
            }

            bool statEffect = state.stateEffect.effectType == RPGEffect.EFFECT_TYPE.Stat;

            CombatEvents.Instance.OnStateEnded(this, stateIndex);
            
            if (stateIndex < States.Count) States.RemoveAt(stateIndex);
            if (statEffect) StatCalculator.CalculateEffectsStats(this);
        }
#

Below, also copy the following two methods from PlayerCombatEntity.cs to MobCombatEntity.cs:

InitShapeshifting() method and ResetShapeshifting() method.

#
Renderer GetActiveRenderer(GameObject model)
        {
            foreach (Transform child in model.transform)
            {
                if (child.gameObject.activeInHierarchy)
                {
                    Renderer renderer = child.GetComponent<Renderer>();
                    if (renderer != null)
                    {
                        return renderer;
                    }

                    renderer = GetActiveRenderer(child.gameObject);
                    if (renderer != null)
                    {
                        return renderer;
                    }
                }
            }
            return null; 
        }
#
  1. In the CombatEntity.cs code, add a new variable to cache the AI behavior template!
    See image J.
public AIBehaviorTemplate CachedBehaviorTemplate;
  1. In the ScreenSpaceNameplates.cs code, find the RemoveState() method and fix the error.
    See image K.
 public void RemoveState(CombatEntity entity, int index)
        {
            if (index < 0 || index >= entity.GetStates().Count) return;
            foreach (var t in AllNameplates.Where(t => t.InUse).Where(t => t.Entity == entity))
                for (var x = 0; x < t.dataHolder.statesList.Count; x++)
                    if (t.dataHolder.statesList[x].stateData == entity.GetStates()[index])
                    {
                        Destroy(t.dataHolder.statesList[x].statesDisplay.gameObject);
                        t.dataHolder.statesList.RemoveAt(x);
                    }
        }
#
  1. In the AIEntity.cs code, add a check to return a default value if there are no target objects, otherwise it will cause an error.
    See image L.
public bool IsTargetTooFar(float maximumDistance)
        {
            if (ThisCombatEntity.GetTarget())
                return Vector3.Distance(transform.position, ThisCombatEntity.GetTarget().transform.position) > maximumDistance;
            else
                return false;
        }
        public bool IsTargetTooClose(float minimumDistance)
        {
            if (ThisCombatEntity.GetTarget())
                return Vector3.Distance(transform.position, ThisCombatEntity.GetTarget().transform.position) < minimumDistance;
            else
                return false;
        }
        public bool IsTargetTooClose(float distance, float modifier)
        {
            if (ThisCombatEntity.GetTarget())
            {
                return Vector3.Distance(transform.position, ThisCombatEntity.GetTarget().transform.position) <
                   distance * modifier;
            }
            else
                return false;
        }
        public bool IsTargetWithinRange(float distance)
        {
            if (ThisCombatEntity.GetTarget())
                return Vector3.Distance(transform.position, ThisCombatEntity.GetTarget().transform.position) <= distance;
            else
                return false;
        }

#

Done.

Set up the editor in the same way as for the player, but with an additional NPC prefab. It is recommended to create a separate Effect for NPCs and set a duration in seconds, so that the NPC automatically reverts to its original model after the transformation.

It is best to use a GROUND skill to apply the shapeshifting effect. Avoid using AOE (Area of Effect) or frequently cast abilities, as this can lead to bugs where the NPC's animations are constantly overridden and cannot play correctly.

Gameplay Result:

#

You can see that after the bear is transformed into a wild boar, it cannot deal damage to targets. Once it reverts back to the bear model, it can deal damage to targets again.

#

Currently, I can only implement it to this extent. I haven't found a way to prevent the NPC from attacking and fleeing. If you have any solutions, please share and discuss them. @winged mist @rugged geode

winged mist
#

dude this is great i will try this out later for sure amazing

rugged geode
#

thank you

forest palm
#

RPGB Fix - The Shapeshifting effect can now be used on NPCs

winged mist
#

@rugged geode hey Vik did you make this work if yes pls do share i am having a problem my ground abilities dos not work and if i use anything else the time on the effect dos not switch back to in base form

#

ok ground ability is working now but the set time on the effect is not switching back to the main form it keeps the hex effect forever

#

and its not set to endless ?

#

ups my bad its fixed now i have it working

#

never mind

#

O wait but there is a problem now for me if the enemy npc gets Hexed for some reason it still can hurt me

#

@forest palm would it be possible to kind of disable the Hexed or morphed NPC, right now they are still chasing us and trying to hurt us the way real Hex works its kind of making the npc wander around and not being able to attack

rugged geode
#

maybe in future