Daggerfall Unity

For all the modders out there, I specifically built this module with the idea future modders will want to or need to modify and access it. Below is the documentation on how to do this.

Using The FPSShieldModule Namespace

In order to access my mods scripts and corresponding script objects and properties, you will need to access its namespace structure that stores all these class objects. I recommend reading this very short article on Namespaces if you are not familiar with them before moving forward:

You can access my mods namespace using: AmbidexterityModule

First, you need to download the latest mod script files that are in the Developer Package, under the files section -HERE-, and put/extract/paste them directly into your unity project.

Ensure both your mod script files and my FPSShield.cs file are placed anywhere within the "daggerfall-unity-master\Assets\Scripts" directory. This ensures the editor loads the script files directly into the base engine environment with all the base script files and objects and is required to work with my FPSShield module code. I also recommend it as a basic modding practice for all mod developers.

This is required for your unity project to pick up the mods namespace and allow you access to its class objects.


The easiest way to set it up and access it in your scripts code is through the C# "using
" keyword placed at the top of your script file, like so: using AmbidexterityModule;

Now you can access the mods FPSShield class objects and properties.

FPSShield Class Objects & Properties List:

Here is a list and description of the differing FPSShield objects and properties for the mod.

  • float attackerDamage: The last damage amount the shield blocked from the attacker.
  • float blockAngle: The block angle for the currently equipped shield.
  • float totalBlockTime: The time in seconds to complete a block raise before any modifiers are applied. default value is 2.
  • bool blockStart: true if the shield blocking has begun. False if not.
  • DaggerfallEntity enemyEntity: the last enemy to be blocked by the shield.
  • bool isBlocking: true when player is in blocking mode and can't be damaged; false when player is vulnerable to hits.
  • bool isHit: true when shield blocks a hit; false when not.
  • int shieldStates: the current state the shield is in (0 - idle | 1 - Raising | 2 - Raised | 3 - Lowering | 4 - Bash  ).
  • int shieldTypes: the current shield type equipped (0 - Buckler | 1 - Round | 2 - Kite | 3 - Tower).
  • float vulnerableTime: the amount of time the player is vulnerable before isBlocking kicks in and begins blocking state.
  • bool isRaised: True when shield if in fully raised state; false when not.
  • bool shieldEquipped: true when shield is equipped. false when not.
  • bool isBashing: True when shield is bashing. False when not.
  • bool GetEquippedShield(): Checks and resets shield type and angle based on what shield type the player has equipped on their ragdoll, and returns true if shield is equipped on player; returns false and sets shield type to 0.
  • float getAnimeTime(): returns the current amount of time covered in the current animation playing.
  • int bashDamage: Returns amount of damage bash did.
  • DaggerfallEntity bashedEnemyEntity: The enemy entity you hit with bash.
  • DaggerfallUnityItem equippedShield: The shield you have equipped.

You can access these and use them how you wish. I have not put in any object based language to protect their access. However, be aware, changing the shield state, type, isBlocking, or isHitting may cause the mod to break.

You can access them through your mods script file easily; anywhere in your code call the FPSShield class object and reference any property you want, like so:


Adding Compatibility To Your Mod:

If you find your mod touches the same CalculateAttackDamage formula as this mod, you will find that it breaks the shield module and forces the user to choose mine or your mod. I built this mod to try and make it as easy as possible to add compatibility. The easiest way to do this is what we covered above, adding the name space and using the mod objects in your custom CalculateAttackDamage formula to ensure the block and corresponding animations still work.

There are only two code changes you need to make to your mods CalculateAttackDamage to ensure my shield module runs smoothly and is compatible.

1.You need to stop special effects from coming through when the players block is active. You can find those code lines in the formula by doing a search for the below block of code:

// Apply special monster attack effects
if (hitDamage > 0)
FormulaHelper.OnMonsterHit(AIAttacker, target, hitDamage);

Once you find this code block, add a simple FPSShield.isBlocking check to the if then, like so:

//Apply special monster attack effects
//SHIELD MODULE ADDITION: If player is not blocking with shield, allow custom hit effects to apply.\\
if (hitDamage > 0 && !FPSShield.isBlocking)
FormulaHelper.OnMonsterHit(AIAttacker, target, hitDamage);

Now, when the player block is active, it will stop the special effect from going through without changing the base damage output and screwing with any other scripts that need a damage output, including my shield module.

Feel free to copy and paste my code to make it easy for you.

2. You need to negate damage and ensure the proper properties are set in my shield module script. In order to make it easy for you, I've already created a copy and pastable code block that should work, as long as you haven't changed any of the original formula object names.

copy and past the below code block at the end of your mods CalculateAttackDamage formula (right before it returns the damage as seen below) and ensure the objects references are correct. I put all object references in blue in case your compiler gives you any errors. If you setup the namespace correctly, you shouldn't have to change the class object references yourself and the compiler should not produce errors.

//checks to see if player is blocking yet and if the target is the player. If so, assign damage to attackerDamage, enemy object to enemyEntity, set isHit to true for shield mod, 0 out the damage so player doesn't take any, and assign enemyEntity for shield module.
if (FPSShield.isBlocking == true && target == GameManager.Instance.PlayerEntity && (FPSShield.shieldStates == 1 || FPSShield.shieldStates == 2) && damage != 0)
//grabs attackers sense object.
EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent<EnemySenses>();
//uses attackers sense to figure out their direction to target using their position data.
Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
toTarget.y = 0;
//grabs players main camera object and sets up player direction using the camera object.
Vector3 targetDirection2D;
Camera mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
targetDirection2D = -new Vector3(mainCamera.transform.forward.x, 0, mainCamera.transform.forward.z);
//if the attack angle is shield block angle degrees or more (player does not have them on screen) don't register the block.
if (!(Vector3.Angle(toTarget, targetDirection2D) > FPSShield.blockAngle))
FPSShield.isHit = true;
FPSShield.attackerDamage = damage;
damage = 0;
FPSShield.enemyEntity = attacker;

return damage;

Now, as long as your mod loads after mine and overrides my CalculateAttackDamage formula, it should connect and ensure my shield module still functions correctly.

You can provide the patch one of two ways.

1. You can either create a toggle mod setting that allows the player to turn on or off based of if they are using my mod or not (The benefit is it allows you to keep one mod JSON file, but it requires two separate CalculatateAttackDamage formulas). *THE EXAMPLE SCRIPT FILE CAN BE FOUND IN THE DEVELOPER PACKAGE*.

2. The other option is to provide a second JSON file that is compatible with my mod and doesn't use a toggle (The benefit is you can just use a single CalculateAttackDamage formula in the script).  *THE EXAMPLE SCRIPT FILE CAN BE FOUND IN THE DEVELOPER PACKAGE*.

Either option 1 or 2 works, but you do not need to do both.

Article information

Added on

Edited on

Written by