2h, Shield visibility and Dual-Handed Weapons

OK so as the title suggests, I’m aiming to develop how combat works in my game, in a few ways (apart from skilling). For now, I want to start with the way that my player handles weapons, especially that I want to handle 3 types of weapons:

  1. 2h weapons. For weapons that are 2 handed, I have animations to handle that, but I don’t have the code yet, and I’ll probably give this a solo try first and update the post as we go. The goal of these weapons is that they are weapons that are handled by 2 hands (so the player has to automatically unequip whatever they’re holding on their shield hands for these ones). I think everything else there is already taken care of

  2. Shield visibility. I tried following Thomas’ tutorial regarding that, but unfortunately it was just a complete mess for my game, and I reversed the changes and tried again multiple times. Sounds like now I have to get this one running from the ground up. The only add on I’d probably want in is to slow down the attack time for these, so that the player has a chance to play a shield defence animation (I can tune the timing accordingly, but I want to find a way to integrate the animation for the player putting his shield up, so that he can defend himself from incoming attacks)

  3. Dual-Handed Weapons. Again, I tried following Thomas’ suggestions for that one as well, but it didn’t work well for me. So I’ll need help with that one

So, in short, squeezing a second animation time frame between animations might be essential for me, so as my player will be available to defend himself visually as well

Can we please start with the shield visibility on this topic? It’s been bugging me for a while (and yes, I checked my Synty scale for my modular fantasy hero, and it’s alright), and no matter what I try, it just won’t show up for some reason

Let’s start with what you have already. I’m not writing another system into existence, you should be at the stage where you’re figuring some of these things out by now. If you’re not, then I’ve been doing too much in the previous sections and not getting the process explained enough.

Run me through how you’re equipping the shields. I’m assuming you have some sort of ShieldConfig similar to a WeaponConfig that spawns the shield.

Hey Brian. Apologies I forgot to update, but my laptop is currently in repairs (I had an unexpected network card crash last night). As far as I remember, yes I had a shieldconfig script on my shield that was pretty much the one Thomas had. Once it returns from repairs I’ll update you and we can discuss what I currently have, and what’s missing :slight_smile:

OK I fixed the Shield visibility issue on my own (along with some help from Thomas, which ultimately leads back to you, so thanks again :slight_smile:), now that my laptop is back from repairs, and here’s how I did it:

Here is my ‘ShieldConfig.cs’ script:

using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Stats;

namespace RPG.Combat {

    [CreateAssetMenu(fileName = "Shield", menuName = "Defence/Shield", order = 0)]
    public class ShieldConfig : EquipableItem, IModifierProvider
    {
        
        [SerializeField] Shield equippedPrefab;
        [SerializeField] bool isRightHanded;
        [SerializeField] float defenseBonus = 0;
        [SerializeField] float percentageBonus = 0;

        const string shieldName = "Shield";
        
        public Shield SpawnEquipableItem(Transform rightHand, Transform leftHand) {

            DestroyOldEquipableItem(rightHand, leftHand);

            Shield shield = null;

            if (equippedPrefab != null) {

                Transform handTransform = GetTransform(rightHand, leftHand);
                shield = Instantiate(equippedPrefab, handTransform);
                shield.gameObject.name = shieldName;

            }

            return shield;

        }

        public Transform GetTransform(Transform rightHand, Transform leftHand) {

            Transform handTransform;
            if (isRightHanded) handTransform = rightHand;
            else handTransform = leftHand;
            return handTransform;

        }

        void DestroyOldEquipableItem(Transform rightHand, Transform leftHand) {

            Transform oldWeapon = rightHand.Find(shieldName);

            if (oldWeapon == null) {
                oldWeapon = leftHand.Find(shieldName);
            }

            if (oldWeapon == null) return;

            oldWeapon.name = "DESTROYING";
            Destroy(oldWeapon.gameObject);

        }
        

        // Added Armor to 'Stats.cs'
        public IEnumerable<float> GetAdditiveModifiers(Stat stat)
        {
            if (stat == Stat.Defence) yield return defenseBonus;
        }

        public IEnumerable<float> GetPercentageModifiers(Stat stat)
        {
            if (stat == Stat.Defence) yield return percentageBonus;
        }
 
    }

}

And here is my ‘Shield.cs’ script (which I have no intentions of assigning anything to it’s Unity Event for now, but similar to ‘Weapon.cs’, I created it, just in case I change my mind down the line):

using UnityEngine;
using UnityEngine.Events;

namespace RPG.Combat {

    public class Shield : MonoBehaviour {

        [SerializeField] UnityEvent onHit;

        public void OnHit() {

            onHit.Invoke();

        }
        
    }

}

And I also added these into ‘Fighter.cs’:

// Variables:

        ShieldConfig defaultShield;
        ShieldConfig currentShield;
        LazyValue<Shield> currentEquippedShield;

// in 'void Awake()':

        currentShield = defaultShield;
        currentEquippedShield = new LazyValue<Shield>(null);

// Support functions for the Shield Equipment:

        public void EquipShield (ShieldConfig shield) {
            currentShield = shield;
            currentEquippedShield.value = AttachShield(shield);
        }

        private Shield AttachShield(ShieldConfig shield) {
            return shield.SpawnEquipableItem(rightHandTransform, leftHandTransform);
        }

        private void DestroyOffHandItem() {
            Shield offHandWeapon = leftHandTransform.GetComponentInChildren<Shield>();
            if (offHandWeapon == null) return;
            DestroyImmediate(offHandWeapon.gameObject);
        }

        

And in ‘Fighter.cs’, I updated the ‘UpdateWeapon()’ function to consider the shield as well:

    private void UpdateWeapon() {

        var weapon = equipment.GetItemInSlot(EquipLocation.Weapon) as WeaponConfig;
        var shield = equipment.GetItemInSlot(EquipLocation.Shield) as ShieldConfig;

        if (weapon == null) EquipWeapon(defaultWeapon);
        else EquipWeapon(weapon);

        if (shield != null) EquipShield(shield);
        else DestroyOffHandItem();

    }

Finally, I went to the Shield.cs Script Holder and reset the position transforms, placed y = 0.03 (so that the shield doesn’t go through the Players’ skin) and Rotated X by -90 degrees, and the shield looks perfect in the Players’ hand now (and obviously you can’t forget the Synty Scaling bug, so your hand transforms are all scaled by 100).

I still have 3 minor issues though:

  1. On my death, the shield disappears from my equipment system, but the item itself does not visibly disappear from my players’ hand for some reason… What went wrong here?

For this one, I tried solving it on my own as well, but failed. This is my attempt with ‘ShieldConfig.cs’:

        private float health;
        private Transform rightHandTransform;
        private Transform leftHandTransform;

        private void Awake() {
            health = GameObject.FindWithTag("Player").GetComponent<Health>().GetHealthPoints();
            rightHandTransform = GameObject.FindWithTag("Player").GetComponent<Fighter>().GetRightHandTransform();
            leftHandTransform = GameObject.FindWithTag("Player").GetComponent<Fighter>().GetLeftHandTransform();
        }

        private void Update() {
            if (health <= 0) DestroyOldEquipableItem(rightHandTransform, leftHandTransform);
        }

[PLEASE HELP… :slight_smile:]

  1. The bow and the shield go to the same hand, so if we put either on… they both appear on the same hand. We should solve this when we go to the “2h” transition, right?
  2. [SOLVED THIS ONE ON MY OWN] I forgot this one, so… how do we get this shield config script to display the bonuses on the shield in the tooltip? Again, I just don’t remember how (apologies)

Here is my solution for the third problem, on my own:

        public override string GetDescription()
        {

            string result = "Protection Shield";    // definition
            result += $"\n\n{GetRawDescription()}\n";   // description of the gameObject

            if ((int) defenseBonus != 0) {  // color change of bonus, based on positive or negative
                string bonus = defenseBonus > 0 ? "<color=#8888ff>bonus</color>" : "<color=#ff8888>penalty</color>";
                
                if ((int) defenseBonus > 0) result += $"\n{(int)defenseBonus} point {bonus} to Defence";
                else if ((int) defenseBonus <= 0) result += $"\n{(int)defenseBonus * -1} point {bonus} to Defence";
            }

            if ((int) percentageBonus != 0) {
                string bonus = percentageBonus > 0 ? "<color=#8888ff>bonus</color>" : "<color=#ff8888>penalty</color>";
                
                if ((int) percentageBonus > 0) result += $"\n{(int)percentageBonus} percentage {bonus} to Defence";
                else if ((int) percentageBonus <= 0) result += $"\n{(int) percentageBonus * -1} percentage {bonus} to Defence";
            }

            return result;

        }

Apart from that, we can move on to the 2h weapons issue, prior to trying out the dual weapon issue :slight_smile:

That, I’m not sure of… Add some Debugs into UpdateWeapon and DestroyOffHandWeapon to make sure they are getting called when the character dies… if not… check subscriptions…

One slight change I prefer when it comes to shields is to make their transform a different transfrom than the LeftHandTransform. This gives you a bit more flexibility, and it also allows you to do things like this:

foreach(Transform child in shieldTransform) Destroy(child.gameObject);

Also, avoid DestroyImmediate in favor of Destroy. DestroyImmediate is for things that happen within the Editor (as in custom editors), where Destroy is for things that happen while the game is running.

ShieldConfig should contain no scene related data. All it knows about the wielder of the shield is what is passed into it from calling functions. Generally speaking, it should have no other state.
ScriptableObjects also don’t get Update() methods, so that solution was doomed from the start.

MaxAcceptable should return 0 for a 2 handed or left handed weapon if you have a shield equipped.
Likewise, MaxAcceptable should return 0 for a shield if a 2 handed or left handed weapon is equipped.

After a bit of debugging, I learned that the problem was because I was indeed using ‘DestroyImmediate()’ instead of ‘Destroy()’, in my new ‘Fighter.DestroyOffHandItem()’ function. I’m not sure what big difference this makes, but it did solve the problem

Do we go under the hand transform for the shield and create an empty gameObject for that, or…? And what changes in code will I have to do to account for this?

Oh that’s something new to learn, good to know :slight_smile: (although ‘ShieldConfig.cs’ was not a scriptable object inheritor for me, so now I’m confused…)

I did not update my ‘MaxAcceptable()’ method to account for that yet, I thought this was part of the 2h system we are yet to implement. This is what my ‘MaxAcceptable()’ function currently looks like:

public int MaxAcceptable(InventoryItem item)
        {
            EquipableItem equipableItem = item as EquipableItem;
            if (equipableItem == null) return 0;
            if (!equipableItem.CanEquip(equipLocation, playerEquipment)) return 0;  // IF WE CANT EQUIP A WEAPON, BASED ON TRAITS IN IPredicateEvaluator.cs, ASSIGNED IN 'EquipableItem.cs', DONT EQUIP IT!
            if (GetItem() != null) return 0;

            return 1;
        }

What modifications can we do for this though…?

What I do is have a separate GameObject under the Hand for the LeftHandTransform and the ShieldTransform. I do the same for RightHandTransform. Then rather than searching for magic strings like Sam does in the course, I just clear the transform. It’s important that these are GameObjects that are children of the Hand bone, and not the hand bone itself, as bad things will happen if you destroy all fo the hand bone’s children.

In terms of code, you store a shieldTransform in Fighter just like right and left hand transforms. You pass the shield transform to the ShieldConfig tell it to spawn.

I still don’t understand what the point of this is though, I mean we will only have a shield in the hand that gets deleted (when item is removed) or replaced down the line, right…? (I hope I’m not coming along as defensive, I’m just baffled as of why we need to take this extra step)

By the way, I tried upgrading my ‘MaxAcceptable()’ function based on your suggestions, and although it’s not working, this is what I managed to get:

public int MaxAcceptable(InventoryItem item)
        {
            EquipableItem equipableItem = item as EquipableItem;
            if (equipableItem == null) return 0;
            if (!equipableItem.CanEquip(equipLocation, playerEquipment)) return 0;  // IF WE CANT EQUIP A WEAPON, BASED ON TRAITS IN IPredicateEvaluator.cs, ASSIGNED IN 'EquipableItem.cs', DONT EQUIP IT!
            
            // If it's a sword, or a main-hand weapon, and it's labelled '2-handed', and the player is holding a shield, don't equip the new '2-handed' item:
            if ((equipLocation == EquipLocation.Weapon) && item is WeaponConfig weaponConfig) {

                if (weaponConfig.isTwoHanded && (playerEquipment.GetItemInSlot(EquipLocation.Shield) != null)) return 0;

            }

            // If the player is trying to equip a shield, check for the weapon slot. If the player is holding a single hand weapon there, and the new weapon is 2-handed, don't equip the new 2-handed weapon, and just leave the old weapon there:
            if (equipLocation == EquipLocation.Shield) {

                EquipableItem mainHand = playerEquipment.GetItemInSlot(EquipLocation.Weapon);
                if (mainHand is WeaponConfig mainHandConfig) {
                    if (mainHandConfig.isTwoHanded) return 0;
                }

            }
            
            // If the player is holding something, don't replace it yet:
            if (GetItem() != null) return 0;

            // If none of the previous conditions are met, return true
            return 1;
        }

This actually should be working, the catch being that this will only work with drag and drop out of the box.
For click to equip, you’ll need to do these checks in your click to equip code.

A more robust way of handling this might be to add a MaxAcceptable method to the Equipment component itself which both EquipmentSlotUI and InventorySlotUI can call to get the result.

        public int MaxAcceptable(EquipLocation equipLocation, InventoryItem item)
        {
            if (item is EquipableItem equipableItem)
            {
                if (!equipableItem.CanEquip(equipLocation, this)) return 0;
                switch (equipLocation)
                {
                    case EquipLocation.Shield:
                        if (GetItemInSlot(EquipLocation.Weapon) is WeaponConfig weapon)
                        {
                            if (weapon == null) return 1;
                            if (weapon.IsLeftHanded || weapon.IsTwoHanded) return 0;
                        }
                        return 1;
                    case EquipLocation.Weapon:
                        if (item is WeaponConfig weaponConfig)
                        {
                            if (weaponConfig.IsLeftHanded || weaponConfig.IsTwoHanded)
                                return GetItemInSlot(EquipLocation.Shield) == null ? 1 : 0;
                            return 0;
                        }
                        return 1;
                    default: return 1;
                }
            }
            return 0;
        }

Then EquipmentSlotUI.MaxAcceptable() and InventorySlotUI’s right click to equip methods can check the Equipment’s MaxAcceptable() to determine eligibility.

When dealing with both weapon and shield spawning, several users have reported issues either when using the same GameObject for the LeftHandTransform and Shield spawn point. The old item is often not destroyed because for some reason the item is not being found to delete. Using an empty transform ensures you can simply do a Transform wipe. If you use a root bone, then a Transform wipe will destroy fingers and those fingers might no longer move with the character (bad!).

OK so here’s what I tried doing, although it’s still not working (check under the 4 steps of code to get a better idea of what went wrong):

  1. In WeaponConfig.cs, I have changed the Serialized variable ‘isRightHanded’ variable to public, so we can access it from other scripts:

// Old One:
[SerializeField] bool isRightHanded = true;

// New One:
public bool isRightHanded = true;
  1. In ‘Equipment.cs’, I have added the ‘MaxAcceptable()’ function you have recommended, as follows:

        public int MaxAcceptable(EquipLocation equipLocation, InventoryItem item) {

            if (item is EquipableItem equipableItem) {

                if (!equipableItem.CanEquip(equipLocation, this)) return 0;

                switch (equipLocation) {

                    case EquipLocation.Shield:
                        if(GetItemInSlot(EquipLocation.Weapon) is WeaponConfig weapon) {
                            if (weapon == null) return 1;
                            if (!weapon.isRightHanded || weapon.isTwoHanded) return 0;
                        }
                    return 1;

                    case EquipLocation.Weapon:
                        if (item is WeaponConfig weaponConfig) {
                            if (!weaponConfig.isRightHanded || weaponConfig.isTwoHanded) {
                                return GetItemInSlot(EquipLocation.Shield) == null ? 1 : 0;
                            }
                            return 0;
                        }
                        return 1;
                        
                    default: return 1;

                }

            }

            return 0;

        }
  1. In ‘EquipmentSlotUI.MaxAcceptable()’, I have added this line, to replace the ‘CanEquip(…)’ check function before it, to check for 2-handed weapons, although it seems to PARTIALLY do the job in drag-and-drop (which is expected):

            // if the weapon is 2-handed, don't wield it if the second hand is not empty:
            if (equipment.MaxAcceptable(equipLocation, item) == 0) return 0;

So now that function looks like this:

public int MaxAcceptable(InventoryItem item)
        {
            // Casting any inventory item, going to equipment, as an Equipable Item:
            EquipableItem equipableItem = item as EquipableItem;

            Equipment equipment = GameObject.FindWithTag("Player").GetComponent<Equipment>();
            
            // If the equipable Item is null, don't equip anything:
            if (equipableItem == null) return 0;

            // If you can't equip a weapon, due to level constraints or equivalent, don't equip it:
            // if (!equipableItem.CanEquip(equipLocation, playerEquipment)) return 0;

            // if the weapon is 2-handed, don't wield it if the second hand is not empty:
            if (equipment.MaxAcceptable(equipLocation, item) == 0) return 0;

            // If the player is holding something, don't replace it yet:
            if (GetItem() != null) return 0;

            // If none of the previous conditions are met, return true
            return 1;
        }
  1. in ‘InventorySlotUI.cs’, I also added this ‘MaxAcceptable()’ function check, also replacing the ‘CanEquip(…)’ function check before it, as follows:

                // Test: Check 'Equipment.MaxAcceptable()' to confirm if a weapon is 2-handed or not:
                if (equipment.MaxAcceptable(EquipLocation.Weapon, item) == 0) return;

So now the equipableItem block check looks like this:


            if (item is EquipableItem equipableItem) {

                Equipment equipment = inventory.GetComponent<Equipment>();
                EquipableItem equippedItem = equipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
                // if (!equipableItem.CanEquip(equipableItem.GetAllowedEquipLocation(), equipment)) return;    // The line solely responsible for checking for Predicate conditions, prior to being able to wield a weapon (if you're not high enough of a level, you can't wield that)
                
                // Test: Check 'Equipment.MaxAcceptable()' to confirm if a weapon is 2-handed or not:
                if (equipment.MaxAcceptable(EquipLocation.Weapon, item) == 0) return;
                
                equipment.RemoveItem(equipableItem.GetAllowedEquipLocation());
                equipment.AddItem(equipableItem.GetAllowedEquipLocation(), equipableItem);
                RemoveItems(1);

                if (equippedItem != null)
                {
                    AddItems(equippedItem, 1);
                }

            }

Now the problem is… it partially works, not a terribly robust system. What changes can I do? (I mean if you double-click it fast enough, you can still equip the weapon and break the newly implemented law, for both the swords and the shields… The shield, especially, is hesitant to go to the assigned equipment slot, unless you click on it fast enough (more than one click), regardless of whether there’s anything in the weapon slot or not, and the sword becomes more hesitant when the shield is there (I labelled it as a 2-handed sword for now), otherwise it just goes to the slot if there’s no shield)

As for the ‘Drag and Drop’ system, for now it works perfectly fine… It does all the proper checkings, but the ‘Click to Equip’ is still a mess (or so I thought… the Drag and Drop System now bothers the non ‘isTwoHanded’ variables as well unfortunately, so both systems are a total mess now :confused: - TLDR, even the drag and drop is a total mess, mainly because it treats non 2h weapons as 2h weapons as well, and this is just plain chaos tbh)

And a final request for 2h weapons. Let’s say the player has a shield and sword, and he attempts to wield a 2h-sword of some sword, can we automatically unequip whatever he’s wielding in his main hand and second hand, in exchange for the 2h sword? That would be a nice addon :slight_smile:

If we’re still working on this by the way, we might want to integrate an Inventory space checker as well into the formula, so that the player can’t equip a 2h weapon if he doesn’t have enough inventory space to actually take off the weapon and shield that the 2h weapon will be replacing… it’s honestly a bit weird seeing both the bow and shield in the same hand :sweat_smile: - I think I have an idea how to do this, but considering that the basic 2h system isn’t even working, it doesn’t make sense to try implement this just yet

This line should read:

if(equipment.MaxAcceptable(equipableItem.GetAllowedEquipLocation(), equipableItem)==0) return;

That’s a complete rewrite of what we’ve just done, if what you’re doing now isn’t working, this will be far worse.

Put in a throttle for the input… maybe something like a global bool isHandlingRightClick… when HandleRightClick starts, if isHandlingRighClick return. (This also means that if you have to abort HandleRightClick or equipping the weapon, etc, then you’ll also need to clear isHandlingRightClick)

Ok, enough is going on that I’m really not sure what is going on… Remember that I’m not using the same HandleRightClick you are…
Paste in your entire HandleRightClick() method, along with anything HandleRightClick calls within InventorySlotUI.cs

You’ve utterly confused me with this. It’s working? It’s not?

It can’t treat 2H weapons the same as 1H weapons because of how booleans work, so I’m unsure of what’s going on here…

OK, so here is my ‘EquipmentSlotUI.TryHandleRightClick()’:

// Unequipping a weapon, by clicking on it:
        public void TryHandleRightClick(Inventory inventory) {

            if (GetItem() is EquipableItem equipableItem) {
            
            EquipableItem equippedItem = playerEquipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
            
            if (inventory.HasSpaceFor(equippedItem)) {
            
            inventory.AddToFirstEmptySlot(equippedItem, 1);
            playerEquipment.RemoveItem(equipableItem.GetAllowedEquipLocation());

            }

            else {

                Debug.Log("Not enough Inventory Space");

            }

            }

        }

And here is the lengthier ‘InventorySlotUI.TryHandleRightClick()’ method:

// Equipping a Weapon by clicking on it, and banking it if the bank is open:
        public void TryHandleRightClick() {

            InventoryItem item = inventory.GetItemInSlot(index);
            int number = inventory.GetNumberInSlot(index);
            if (item == null || number < 1) return; // inventory/bank slot is empty, so do nothing

            if (!inventory.gameObject.CompareTag("Player")) {

                TransferToOtherInventory(Inventory.GetPlayerInventory(), item, 1);
                return;

            }

            var otherInventoryUI = FindObjectsOfType<InventoryUI>().FirstOrDefault(ui => ui.IsOtherInventory);  // returns other inventory, or null

            if (otherInventoryUI != null && otherInventoryUI.gameObject.activeSelf && otherInventoryUI.SelectedInventory != null) {

                Inventory otherInventory = otherInventoryUI.SelectedInventory;
                TransferToOtherInventory(otherInventory, item, 1);
                return;

            }

            if (item is EquipableItem equipableItem) {

                Equipment equipment = inventory.GetComponent<Equipment>();
                EquipableItem equippedItem = equipment.GetItemInSlot(equipableItem.GetAllowedEquipLocation());
                if (!equipableItem.CanEquip(equipableItem.GetAllowedEquipLocation(), equipment)) return;    // The line solely responsible for checking for Predicate conditions, prior to being able to wield a weapon (if you're not high enough of a level, you can't wield that)
                
                // Test: Check 'Equipment.MaxAcceptable()' to confirm if a weapon is 2-handed or not:
                // if (equipment.MaxAcceptable(EquipLocation.Weapon, item) == 0) return;
                
                equipment.RemoveItem(equipableItem.GetAllowedEquipLocation());
                equipment.AddItem(equipableItem.GetAllowedEquipLocation(), equipableItem);
                RemoveItems(1);

                if (equippedItem != null)
                {
                    AddItems(equippedItem, 1);
                }

            }

            else if (item is ActionItem actionItem) {

                ActionStore actionStore = inventory.GetComponent<ActionStore>();
                int slot = actionStore.GetFirstEmptySlot();

                if (slot > -1) {

                    actionStore.AddAction(actionItem, slot, GetNumber());
                    RemoveItems(GetNumber());

                }

            }

            else if (item is AmmunitionItem ammunitionItem && inventory.TryGetComponent(out Quiver quiver))
            {

                // If there is a "Quiver" Equipment slot, and what we clicked on is a Rangers' arrow,
                // then we shall transfer the arrow to the dedicated slot

                Debug.Log($"{ammunitionItem} is ammo, transferring to {quiver}");
                TransferToQuiver(ammunitionItem, quiver, number);

            }

        }

It’s… working… but the problem is, it works for both 1H and 2H weapons, so it’s basically not classifying them apart, which is definitely not what I want… we can just assume it’s not working

Would’ve been a nice, neat little addon, but if it’s chaos then I think we can just let this one go

Safe to say I’m a little confused? I don’t think I’ve ever seen global variables/functions before tbh

You have… it’s any variable that is not within a method… also called an instance variable…
For example:

private bool isHandingRightClick;

public void TryHandleRightClick()
{
    if(isHandlingRightClick) return;
    isHandlingRightClick=true;
    //rest of method

Of course, any time you would return within TryHandleRightClick() you would need to clear this variable

if(item==null || number < 1) 
{
    isHandlingRightClick=false;
    return;
}

The idea here is that this puts up a roadblock to spam clicking. Don’t worry about that for now, we need to get things working when a normal click situation is in progress first. Worry about breaking it with spam clicking after that.

Under the comment

//if(equpment.MaxAcceptable(EquipLocation.Weapon, item) == 0) return;

add

if(equipment.MaxAcceptable(equipableItem.GetAllowedEquipLocation(), equipableItem)==0) return;

That’s what I’m not getting… the check for two handedness should be a simple pass through of a boolean value… its TwoHanded, or it’s not… the method is written to balk at left handed weapons and 2 handed weapons.

OK so I basically just learned something, that this line:

if(equipment.MaxAcceptable(equipableItem.GetAllowedEquipLocation(), equipableItem)==0) return;

Only goes in ‘InventorySlotUI.cs’, and not in ‘EquipmentSlotUI.cs’, got it (correct me if I’m wrong). For now, it works if I’m not spam clicking

But I also noticed a few things:

  1. The shield doesn’t even have knowledge of whether it’s a 2h weapon or not in the main players’ hand. If there’s a weapon in the main weapon slot, and it doesn’t matter if it’s 2-handed or not (my bow is not set to be 2-handed… yet (for testing purposes)), it won’t go there unless I spam-click

  2. No weapon is going to the main hand if there’s a shield (again, unless I spam-click), like… at all. It’s like the shield hates anyone being in the other hand, and everyone hates the shield as well (I don’t have multiple shield gameObjects created yet, so I’m guessing if we get another one this issue may or may not escalate between shields)

I have to mention as well, I have implemented this line, in ‘InventorySlotUI.cs’:

if(equipment.MaxAcceptable(equipableItem.GetAllowedEquipLocation(), equipableItem)==0) return;

I have also introduced this global variable, in ‘InventorySlotUI.cs’:

private bool isHandlingRightClick;

This change (in ‘InventorySlotUI.TryHandleRightClick()’):

and a few lines later, I have also implemented this one:

if(item==null || number < 1) 
{
    isHandlingRightClick=false;
    return;
}

I had to double check this, because you (quite uniquely) have decided to set a bool for rightHanded instead of leftHanded, but the logic is correct.
If there is no weapon, then the return should be 1, equip shield
If the weapon is Left Handed (bow, anybody?) then it should be 0, do not equip shield
If the weapon is Two Handed, then it should be 0, do not equip shield.
If it passes both of these checks, then the weapon is right handed, equip shield.

Correct, if you are HandlingRightClick within the EquipmentSlotUI, then the only thing it will be doing is unequipping the weapon. It could care less at that point how many items can be equipped in the weapon slot, it just needs to know if there is room in the inventory.

This is why using isRightHanded as the variable instead of isLeftHanded makes me uncomfortable… I think that fundamentally, we are working at cross purposes.

Is your bow marked rightHanded?

Nope, my bow is left handed (if I remember correctly, back in the course I set my bow to left hand because I think that was the only way I can run the animation back then). What changes should I do though?

I honestly don’t know. I’m stumped, and it’s past bed time.

My testing on this end passed, so I still can’t figure out what’s wrong on this end.
Let’s put some Debugs in Equipment.MaxAcceptable

case EquipLocation.Shield:
    Debug.Log("Testing EquipLocation.Shield");
    if(GetItemInSlot(EquipLocation.Weapon) is WeaponConfig weapon)
    {
         if(weapon==null) 
         {
              Debug.Log($"No weapon is equipped, returning 1");
              return 1;
         }
         if(!weapon.isRightHanded)
         {
              Debug.Log($"{weapon} is left handed, returning 0");
              return 0;
         }
         if(weapon.isTwoHanded)
         {
              Debug.Log($"{weapon} is two handed, returning 0");
              return 0;
         }
         Debug.Log($"{weapon} is right handed, returning 1");
         return 1;

You can do similar debugs on the case EquipLocation.Weapon

OK I edited this comment to highlight something… weird that just happened (this comment is edited 4 hours after I wrote the comment below this one)

To play around with my new 2h system (check the comment below this one for more details), I created a new 2h sword. Sword is working fine, but for god knows what reason, when I try implement a 2h attack animation, the sword does NULL damage to the enemy, and I have no idea why. It plays the animation normally through the animator override controller, but it won’t deal any damage (literally NULL damage. Not even a zero, just straight up null…)

I tried fiddling with both the animator, the fighter.cs script (I reversed that), and just an animator override controller I made for that sword, but it seems like it swore an oath that if the new animation replaces the current attack animation, it won’t deal any sort of damage, and I have absolutely no idea why (and it doesn’t happen for any other weapon, just this one for some reason). What can I do to fix this?

This is what my Animator Override Controller looks like (Unarmed-Attack-L3 is what’s causing the main issue):

2h sword animator override

Privacy & Terms