PackMaster - BattleStateMachine
Dylan Torres Dylan Torres

PackMaster - BattleStateMachine

This is a script that controls the state machine of PackMaster’s turn-based combat system. The script inherits from both MonoBehaviour and a custom interface titled IBackpackSelectable. This interface is used to define when an item in the player’s backpack can be selected, allowing for interaction with the inventory system.

Before any functions or coroutines are defined, several references to the combat screen are defined for both players and enemies. Similar references between the player and enemy exist. A reference to singular prefabs called enemyPrefab and playerPrefab, respectively, determine the enemy and player GameObjects to be spawned. These GameObjects are spawned at transforms called enemyStagePosition and playerStagePosition, respectively, as the battle begins. From there, transforms called enemyStageVFX and playerStageVFX, respectively, determine where the VFX for attack animations appear to overlap the units. The last shared reference type is to a Scriptable Object called Unit, where the health, damage and gold value stats of each unit are stored. These are referenced as enemyUnit and playerUnit for their respective unit.

The enemy unit’s data does have unique references of its own, such as a GameObject array called enemyArray that stores prefabs of each enemy type to generate random encounters with a public integer called challengeThreshold. If the player’s experience level is greater than challengeThreshold, enemies with higher stats are added to the pool.

After proper references are found for the player and enemy, data for the UI is collected. A custom class called BattleHUD, which is used to customize UI cards for each unit, is referenced as enemyHUD and playerHUD, respectively. Then, a reference to the enumerator BattleStates is found and referred to as stateOfBattle. From there, actionText, saved as a TextMeshProUGUI, describes actions that occur in battle. Next, a reference to a camera script called ThugShaker is found, which controls the camera shake on the camera used to observe the battle. From there, an AudioSource reference abbreviated to aS is found, followed by VFX references for plasmaBallVFX, ringVFX and explosionVFX used for attacking, healing and enemy death, respectively.

After references are found and assigned for battle data, references to interactive UI elements are found. This includes the custom class Backpack, referred to as backpack, which displays the player’s inventory, and buttons used to attack enemies and leave battle. Additionally, references to in-game TMP Text strings that detail the enemy’s gold drops and the player’s item are found. These choices control values of the bool isSelectingGridPosition and a custom class reference to BackpackItem, titled currentlySelectedItem.

(1/7)

Read More
PackMaster - InitializeBattle()
Dylan Torres Dylan Torres

PackMaster - InitializeBattle()

BattleStateMachine is chock full of coroutine IEnumerators, the first of which is titled InitializeBattle(). First, two random int values called normalEnemyIndex and challengeEnemyIndex are created to decide what the player’s encounter will be. The playerStart GameObject is the position where the playerPrefab is instantiated atop its designated stage. Once it is instantiated, the value of playerUnit is set to its Unit component.

Once the player is properly placed, the enemy encounter is determined via Random.Range, which will pick a value based on the player’s current level. If the player’s level is under challengeThreshold, half of the enemy pool is open for random selection through normalEnemyIndex, but any value greater than it will cause the entire pool to be open for random selection through challengeEnemyIndex. Once the enemyPrefab is set, it is instantiated on enemyStart atop enemyStagePosition. As this happens, enemyUnit is set to enemyStart’s Unit component, and the action text details the enemy that was just decided.

Following the enemyUnit declaration, the attackButton remains off while isSelectingGridPosition is set to true. Then, the playerHUD and enemyHUD are established through a method called HUDSetup. This method takes a parameter of the Unit type, and from there, it customizes the health bar and level display of each unit. In the case of the player, it also displays their experience bar. Considering that no weapons are yet selected, the defaultDamageValue floats from the playerUnit and enemyUnit are set as the base value. After one second passes, the stateOfBattle updates to reflect that it is now the player’s turn.

In void PlayerTurn(), the stateOfBattle is set to BattleStates.Player_Turn to account for other state-changing methods in the code. Once it is changed, actionText will read “Choose your next move!“ This invites the player to make an inventory selection and strike the enemy with the now-active attack button.

(2/7)

Read More
PackMaster - PlayerAttack()
Dylan Torres Dylan Torres

PackMaster - PlayerAttack()

In the PlayerAttack() coroutine, the enemyUnit’s health is controlled by a bool value called enemyHasDied. This checks if the damage dealt to the enemy was enough to reduce their HP to 0. If it is, the bool returns true, but if not, the bool returns false.

As the player’s attack lands, both the enemy and player’s HP values are adjusted as necessary, actionText changes to reflect the attack that was performed, and the camera shakes to enhance the impact. The player will also heal itself for however much the healthRegenAmount value is set to based on the current inventory selection. The item’s designated SFX, which adjusts the playerUnit’s attackSFX value when the item is selected, then plays in aS. As the player attacks, a delegate event is invoked that triggers script functions subscribed to the event. Following this, plasmaBallVFX is instantiated on the enemy stage, and bool values in the player’s animator will control the attacking animation. Depending on the player’s item they have selected to use in their attack, the attack animation will vary through the adjustment of animator bool values.

After one second passes, the bool that controls attacking in the player’s animator is set to false. If the enemy has died as defined by the enemyHasDied bool or if the player has died as defined by their HP reaching a value less than or equal to zero, stateOfBattle will change to Battle_Finish, and the function that runs to set finished battle behaviors is called. Otherwise, if the enemy and player are still alive, the battle continues with the enemy’s turn.

(3/7)

Read More
PackMaster - OnAttackButtonPressed() & EnemyTurn()
Dylan Torres Dylan Torres

PackMaster - OnAttackButtonPressed() & EnemyTurn()

In public void OnAttackButtonPressed(), the manager checks if the value of stateOfBattle is not equal to Player_Turn. If it is not, the function immediately returns before setting the button to become non-interactive and beginning the PlayerAttack() coroutine.

In the EnemyTurn() coroutine, two float values are declared that control the enemy’s ability to heal itself. The first float is called healBenchmark, and it controls the odds of an enemy healing itself by a chance equal to the quotient of enemyUnit’s levelOfUnit variable divided by a value of twenty-five. The second float is called randomHealChance, and it gets a decimal value between zero and one. If enemyUnit’s currentHP is less than enemyUnit’s pointToRestore (a float value that determines when an enemy is allowed to heal itself) and if randomHealChance is less than or equal to healBenchmark, the enemy heals itself. If this check fails, the enemy attacks.

After a second passes, the player’s HP value is checked. If playerUnit’s currentHP is less than or equal to zero, the function to run the end of the battle is called. Otherwise, if playerUnit’s currentHP is over zero, it becomes the player’s turn once again.

(4/7)

Read More
PackMaster - Enemy Coroutines & Functions
Dylan Torres Dylan Torres

PackMaster - Enemy Coroutines & Functions

In the EnemyAttack() coroutine, behaviors for the enemy’s attack are defined. Similar to the enemyHasDied bool, a new bool called playerHasDied tracks if the player has taken a lethal amount of damage. The text component of actionText changes to reflect the enemy’s attack, the camera shake effect occurs for its set duration by the value of the set duration, a sound for the attack plays through aS, and plasmaBallVFX is instantiated on the playerStageVFX transform. The enemy’s attacking animation is set through the IsAttacking bool being set to true, and after a second, the same bool is set to false.

In the EnemyHeal() coroutine, a private bool called enemyCanHeal is declared as the enemyUnit runs a function called HealSelf, which controls its health regeneration. As an enemy heals itself, actionText updates to reflect the amount they healed themselves by. The enemyHUD then updates to display this new health value, and a ringVFX prefab is instantiated at the enemyStageVFX transform. After a second, the coroutine returns.

In void CollectGold(), the player gathers an amount of gold from the enemy upon their defeat. This gold is stored with the player’s current inventory in BackpackSaver, a singleton that stores the player’s items.

In void GainExperience(), the player gathers experience from the enemyUnit’s experienceYield value. Much like gold, experience is stored in the BackpackSaver singleton. If the player gains 100% experience, a function will be called to increment the player’s level and reset the bar.

(5/7)

Read More
PackMaster - FinishBattle()
Dylan Torres Dylan Torres

PackMaster - FinishBattle()

In void FinishBattle(), stateOfBattle is set to Battle_Finish, and both item selection and the attack button are disabled. This means no more turns of combat may be taken.

If the player’s currentHP is set to zero or less, the battle registers the enemy as victorious. Then, the combat screen transitions to the Game Over screen.

Otherwise, if the enemy’s HP is set to zero or less, the enemy VFX for an explosion and money are set from scripts called ExplosionHit and MoneyHit, respectively. If these values are valid, the effects play as the enemyStart object is destroyed. After the object is destroyed, the playerUnit’s animator bool “IsVictorious“ sets itself true, causing the robot to look at the screen as a victory animation. Then, actionText declares the player victorious, and a hidden button allowing the player to leave the combat screen sets itself active. The player then collects gold as outlined in the CollectGold() function, and their earnings are displayed in the itemInfoLabel text box. As gold is collected, the player’s experience is increased by the unit’s experience yield as outlined in the GainExperience() function. The private bool playerLevelsUp indicates whether or not the experience gained by the player was enough to level them up. If true, this indicates the playerUnit’s LevelUp() function has been called. Level and experience updates are then reflected in playerHUD through its HUDSetup function.

(6/7)

Read More
PackMaster - BackpackItem Select(BackpackItem bPI)
Dylan Torres Dylan Torres

PackMaster - BackpackItem Select(BackpackItem bPI)

The public BackpackItem Select method is used to allow item selection in the player’s backpack inventory, and it takes an argument of the BackpackItem bPI.

First, a check is made to determine if currentlySelectedItem is set to null. If currentlySelectedItem is null and bPI is also null, this means the player has no item selected. When this happens, their damage is reset to default, and it causes defaultAttackSound to be set as the player’s attack sound.

If currentlySelectedItem is null and bPI does NOT return null, OR if currentlySelectedItem is NOT equal to bPI, this means that an item has been selected. This will set currentlySelectedItem to the value of bPI, and playerUnit’s attack sound is set to bPI’s unique itemUseSound reference. From there, AttackCallback attack is set to handle any adjacency bonuses from items surrounding currentlySelectedItem in the backpack. For example, if there is a lighter to the left of currentlySelectedItem, a damage multiplier will be factored into the damage calculation. Important to note is that this system only supports the cardinal directions and does not account for diagonal adjacency. After bonuses from adjacent items are accounted for, playerUnit’s damage and healthRegenAmount value is set to the end product or sum of the dmg and heal bonuses. If attack.dmg is greater than zero, it displays in itemInfoLabel’s text box. Otherwise, it displays a blank string. If attack.heal is a non-zero value, a string displaying the heal value of the current selection is appended to itemInfoText’s current display.

If currentlySelectedItem is NOT null and bPI is equal to currentlySelectedItem, this means the player has tried to select the same item twice. This is handled as deselecting an item according to the game’s logic, so it proceeds to set currentlySelectedItem to null. After that, it reset’s the player’s damage and attackSound to their default values, and actionText displays that their item was deselected.

Following this check, bPI is returned to close the function.

(7/7)

Read More