Repository: CryptoBlades/cryptoblades
File: contracts/PvpArena.sol (Solidity)

Code before:
            _matchableCharactersByTier[tier].remove(characterID);
        }

        isCharacterInArena[characterID] = false;
        isWeaponInArena[weaponID] = false;

        // setting characters, weapons and shield NFTVAR_BUSY to 0
        characters.setNftVar(characterID, 1, 0);
        weapons.setNftVar(weaponID, 1, 0);
    }

    function _getCharacterPowerRoll(Duelist memory character, uint8 opponentTrait)
        private
        view
        returns (uint24)
    {
        Fighter memory fighter = fighterByCharacter[character.ID];
        uint256 weaponID = fighter.weaponID;
        uint256 seed = randoms.getRandomSeedUsingHash(
            characters.ownerOf(character.ID),
            blockhash(block.number - 1)
        );

        int128 bonusShieldStats;
        if (fighter.useShield) {
            bonusShieldStats = _getShieldStats(character.ID).mul(int128(_shieldFactor)).div(100);
        }

        (
            ,
            int128 weaponMultFight,
            uint24 weaponBonusPower,
            uint8 weaponTrait
        ) = weapons.getFightData(weaponID, character.trait);

        int128 playerTraitBonus = getPVPTraitBonusAgainst(
            character.trait,
            weaponTrait,
            opponentTrait
        );

        uint24 playerFightPower = Common.getPlayerPower(character.basePower, (weaponMultFight.add(bonusShieldStats)), weaponBonusPower);

        uint256 playerPower = RandomUtil.plusMinus10PercentSeeded(
            playerFightPower,
            seed
        );

        return uint24(playerTraitBonus.mulu(playerPower));
    }


Review comment (bug):
i don't think the mul function is correct here.
ABDKMath64x64.sol says
/**
   * Calculate x * y rounding down.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function mul (int128 x, int128 y)

What you probably want to use is .mulu(

  /**
   * Calculate x * y rounding down, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y unsigned 256-bit integer number
   * @return unsigned 256-bit integer number
   */
  function mulu (int128 x, uint256 y)

Code after:
        }

        isCharacterInArena[characterID] = false;
        isWeaponInArena[weaponID] = false;

        // setting characters, weapons and shield NFTVAR_BUSY to 0
        characters.setNftVar(characterID, 1, 0);
        weapons.setNftVar(weaponID, 1, 0);
    }

    function _getCharacterPowerRoll(Duelist memory character, uint8 opponentTrait)
        private
        view
        returns (uint24)
    {
        Fighter memory fighter = fighterByCharacter[character.ID];
        uint256 weaponID = fighter.weaponID;
        uint256 seed = randoms.getRandomSeedUsingHash(
            characters.ownerOf(character.ID),
            blockhash(block.number - 1)
        );

        int128 bonusShieldStats;
        if (fighter.useShield) {
            bonusShieldStats = int128(int128(_shieldFactor).mulu(uint256(_getShieldStats(character.ID))).div(100));
        }

        (
            ,
            int128 weaponMultFight,
            uint24 weaponBonusPower,
            uint8 weaponTrait
        ) = weapons.getFightData(weaponID, character.trait);

        int128 playerTraitBonus = getPVPTraitBonusAgainst(
            character.trait,
            weaponTrait,
            opponentTrait
        );

        uint24 playerFightPower = Common.getPlayerPower(character.basePower, (weaponMultFight.add(bonusShieldStats)), weaponBonusPower);

        uint256 playerPower = RandomUtil.plusMinus10PercentSeeded(
            playerFightPower,
            seed
        );

        return uint24(playerTraitBonus.mulu(playerPower));
    }

    function getPVPTraitBonusAgainst(