LE1 Power and Talent Documentation - ME3Tweaks/LegendaryExplorer GitHub Wiki

With the release of LE1 Talent and Power Overhaul, it seemed prudent to provide an in-depth reference document outlining precisely how it was accomplished. This is not a tutorial itself but is designed to complement existing Legendary Explorer (LEX) tutorials.

Background

In LE2 and LE3, class powers can be customized to one’s desire, and custom classes can be created from existing powers with relative ease. DropTheSquid’s Bonus Bonus Powers makes this even easier by giving the player the ability to do this within the game itself. Even new powers can feasibly be created. In fact, Tajfun403 has successfully ported combat rolls and biotic dashes from LE3 to LE2 utilizing LEX tools. A quick glance at the contents of the CookedPCConsole folder of LE2 and LE3 can provide a starting point to understand how this was accomplished. Individual files for each character class can be seen, each containing stats and scripts linked to powers usable by that class. For example, the file SFXCharacterClass_Adept will contain all power scripts in addition to an object that contains references to all abilities usable by that class, listed in the order they appear on the squad screen. In contrast, LE1's CookedPCConsole lacks player class files, instead featuring a single SFXGameContent_Powers file containing only power scripts, with no visible stats or objects that could feasibly be used to assign powers to a character.

This is because information detailing classes and powers is stored differently in LE1. During the development of the original Mass Effect, Bioware used Unreal Engine 3 before it was fully developed. As a result, they fell back on familiar design habits from their in-house engines, such as storing large amounts of data in two-dimensional arrays (2DAs). These 2DAs resemble spreadsheets and are viewable in Legendary Explorer. Existing tutorials on the LEX wiki cover 2DA modification, but the actual structure and interconnectivity of these objects can seem quite complex. To demystify the system, the functionality and connectivity should be mapped starting with the location of all relevant 2DAs. These are stored in Engine.pcc within LE1’s CookedPCConsole folder.

Class and Talent 2DA System

The important 2DA objects are:

  • Characters_Character

  • Classes_BaseClass

  • Classes_ClassTalents

  • Classes_ClassSpecializations

  • Talent_TalentEffectLevels

  • GameProperty_GameProperties

  • GameProperty_GameEffects

  • Talent_BonusTalents

  • Talent_TalentGUI

  • Powers_Powers

  • Template_Template

Characters_Character

Figure 1

Figure 1

The Characters_Character 2DA contains labels corresponding to squadmates and player classes, with a BaseClassID column used to assign classes to characters (e.g., Vanguard is 5, Adept is 2). (Figure 1). The SuperSoldier class is a test class that contains all talents for development purposes.

Classes_BaseClass

Figure 2

Figure 2

The Classes_BaseClass 2DA defines information visible in-game in the Squad and Squad Select screens (Figure 2).

The IsHuman label could be better thought of as IsPlayer and can be seen marked for all player classes.

The DisplayName field contains a string reference (stringref) that specifies the class name shown during character creation and in the upper left corner of the Squad Screen, below the character's name.

The DisplayDescription field provides a description of the class at the bottom of the screen during class selection in character creation.

The next three columns indicate the class strengths, with the numerical values corresponding to the strength bars displayed at the bottom of the screen when selecting the squad before departing the Normandy. The last three columns are vestigial and not visible to players during gameplay.

Classes_ClassTalents

Figure 3

Figure 3

Figure 3 shows a small excerpt of the Classes_ClassTalent 2DA that corresponds to the Soldier class. The BaseClassID column can be seen to match the BaseClassID in the Characters_Character 2DA. The following column, TalentID will be explained further in subsequent sections.

The MaxRank column indicates the maximum number of ranks available for each talent, with most entries set to 12, corresponding to the talents upgradeable on the Squad screen.

The LevelOffset column specifies the level the character must be before that talent become available. All rows with positive LevelOffsets also include a prerequisite. These prerequisites can be seen in the PrereqTalent0 column. The PrereqRank0 column specifies how many ranks of that prerequisite are needed before the locked talent is available. Rows with negative LevelOffsets have no prerequisite talent, with -1 representing a standard amount of ranks available at Level 1 (with the amount confined by the PrereqRank0 column of the corresponding locked talent. So, talent 0 will not have 4 ranks fully available until level 2, when talent 15 can be unlocked), and -12 indicating immediate availability of all ranks at Level 1.

The LevelsPerRank column denotes the level progression per rank, which is one level per rank for all talents.

StartingRank shows the number of ranks filled in when a character of this class is spawned.

The VisualOrder column determines the display order on the talent screen, with lower numbers appearing at the top and higher numbers at the bottom. To demonstrate this, compare the non-zero numbers with the squad screen for the Soldier class in-game. The TalentID values correspond to the talents visible in the game. Talents without a VisualOrder will be explained later.

Classes_ClassSpecializations

Figure 4

Figure 4

Classes_ClassSpecialization also contains the BaseClassID column as seen in other 2DAs (Figure 4). The first two rows have a BaseClassID of 0 and thus correspond to the Soldier class.

The TalentID column from Classes_ClassTalents can be cross-referenced with the ReplaceTalentID in Classes_ClassSpecialization.

The BonusID adjacent to that column indicates the talent specialization that will be used to replace the corresponding passive talent on the Squad screen after the completion of the side mission on Luna.

Similar to the Classes_BaseClass 2DA, the DisplayName and DisplayDescription columns contain stringrefs for the names and descriptions of the Specialization Talents, such as Shock Trooper or Commando, which are displayed on the Squad screen for those talents.

Talent_TalentEffectLevels

A:

Figure 5a

B:

Figure 5b

Figure 5

Talent_TalentEffectLevels introduces new headers, including GameProperty and GameEffect, along with their corresponding Label (Figure 5). There are 12 columns representing each rank of a talent. These values can be understood better by comparing them to the rank descriptions on the squad screen.

For example, the first talent, 0 (Pistols), is displayed in Figure 5a. At level 3, Marksman is unlocked, as indicated by the GE_LWMarksman_Enable row being set to 1. This signifies that the power is ready for use and can be added to the power wheel, though an additional condition for GUI visibility is needed and will be explained later. The GE_LWMarksman_MaxDriftRed and GE_LWMarksman_MinDriftRed values are both 0.4 at rank 3. According to the description, which states “Boosts accuracy by 60%”, this value is logical, as a 40% reduction in drift corresponds to a 60% increase in accuracy. Other values are self-explanatory. “Kickback” is labeled as “Accuracy Cost” in the player-visible description, representing the degree to which weapon accuracy is reduced after using the specific power associated with this talent.

Returning to the idea of talents without a VisualOrder seen in the Class_ClassTalents 2DA, one such talent is displayed in Figure 5b. The Talent_Label of this talent indicates it is a Setup talent, which described its intended function. This talent activates various game effects that enhance the Soldier class's abilities, such as automatically applying the capability to wear light and medium armor. The PowerGive GameEffects in this talent add specific powers to the power wheel, including Immunity, Shield Boost, Overkill (Suppression), Carnage, Adrenaline Burst, Marksman, and Assassination (Critical), once these powers are enabled in their respective progressions.

Additionally, this talent grants access to the First Aid skill, which is available to all player classes. It can be used by squadmates when health is low but is not enabled by default, thus giving the player complete control over medi-gel reserves. Weapon proficiencies can also be seen within this talent. In LE1, all classes are proficient with every weapon. In OT1, proficiency improved with the corresponding talent, explaining why the sniper rifle scope would sway like Shepard had just slammed a six pack of Smirnoff and is trying to remember which way is the floor and which is the ceiling until approximately eight ranks in the talent had been unlocked.

GameProperty_GameProperties and GameProperty_GameEffects

A:

Figure 6a

B:

Figure 6b

Figure 6

Figure 6b displays an excerpt of the GameProperty_GameEffects 2DA that contains the game effects for the Throw talent, identified as talent 49 in the Talent_TalentEffectsLevel 2DA. In Talent_TalentEffectsLevel, the GameEffect column contains values that correspond to the row labels of GameProperty_GameEffects. Similarly, the GameEffect_Label column of Talent_TalentEffectsLevel corresponds to the Label column of GameProperty_GameEffects.

The Class column specifies the type of game effect, with certain game effect classes labeled with variable types like float or bool. The class names that include the word parameter vary from talent to talent and are used to store information unique to that power. For example, Throw imparts force and damage on a target, so these custom parameters are included. Thus, the power can pull that information from the effects levels 2DA.

The csid column is consistently marked as 1, and while it is required, it does not affect anything in-game.

Subsequent columns are paired as property and value. A game effect can be viewed as an object with four variables: m_attributename, m_powername, m_aspect, and m_modifier. m_attributename denotes the specific attribute controlled by the game effect. For instance, the second row has a value of m_CastingTime for its m_attributename, which means this attribute controls the base casting time for a power. The m_powername property indicates which row in the Powers_Powers 2DA should be referenced for that specific power, such as TK_Throw, which will be explained further later.

The m_aspect property specifies whether the values in the LevelX columns of the Talent_TalentEffectsLevel 2DA are base values or modifiers. The m_modifier column remains empty for base values but is present for modifiers, identifying the modifier type as a sum, float, or layer.

A sum modifier adds its value to the base value, a float acts as a percentage increase, and a layer reduces a base value by a percentage. For example, if a power deals m_Damage of 20 points with a float modifier of 0.5, the actual damage becomes 30 points, which is displayed in-game as “Increases damage of X power by 50%”.

Conversely, a layer modifier is a float value that is directly multiplied by the base value, resulting in an overall reduction, e.g., a m_CooldownTime of 45 can have a layer modifier of 0.9, which would reduce the base value by 10% to 40.5 points.

Modifier game effects are named similarly to their base counterparts, but with \_S, \_F, or \_L suffixes. These typically are not contained within the talent that unlocks the power but are instead used within passive talents such as “Nemesis”.

Resistance modifiers may seem counterintuitive, often appearing as a layer modifier that implies a reduction in resistance. Instead, this reduces the damage values applied to the character. For instance, taking ranks in Combat Armor will cause incoming damage to be multiplied by the damage resistance game effect, thus reducing the actual damage taken.

Game Properties can be conceptualized as a container of Game Effects. For example, the Throw ability has a Game Property of 17, or GP_TKThrowCoreGFX (Figure 6a), which contains Game Effects 53-64 as seen in the Talent_TalentEffectLevels 2DA. Three types of game properties are defined in the third column of GameProperty_GameProperties (Figure 6a). For talents that include powers, the relevant types are GPT_Static and GPT_Instant, where GPT stands for “Game Property Timer”.

In this context, static refers to a property that is continuously applied without a time limit and is not a one-time occurrence. This can be observed in the Property and Value sets on the right-hand side of the table, where the property m_bTicked is set to 0, indicating persistence without a time limit.

Statistics for powers are stored in static game properties, as seen in the Talent_TalentEffectsLevel 2DA, where all stats for Throw are stored in GP_TKThrowCoreGFX, a static game property. Each talent containing a power includes a static Core game property.

Modifiers are typically placed in a separate static game property that specifies the modifier's function. For example, a game effect that increases the m_Force of Throw would be stored in GP_TKThrow_ForceInc.

Every power has an associated PowerGive Game Property, which is a GPT_Instant Game Property. This type means it is executed once, typically when a character is spawned, adding the power to the power wheel. In the case of NPCs, this addition makes the ability available for AI utilization.

Powers_Powers

Figure 7

Figure 7

Powers_Powers controls the visual and functional elements that are executed when a power is activated. The power TK_Throw is shown in Figure 7 as an example (one line of the table is displayed in five parts for ease of illustration). The Label column with the power name corresponds to the m_powername value in the GameProperty_GameEffects 2DA.

The DisplayName column contains the stringref of the power name, which is generally unused in-game. Following this, the Description column shows what appears when hovering over a power in the power wheel. The power name is displayed in bold, with the rest of the description appearing beneath it, formatted by the game based upon line breaks within the stringref.

The Type column defines the type of power, which can be Target, Party, MELEE, or Cylinder. Target powers are typically hitscan and activate where the player is pointing. Party powers generally apply buffs or debuffs to the caster or the caster’s squad. MELEE powers operate within melee range. Cylinder powers are projectiles.

The Effect column specifies the effect type of the power, such as ATTACK, Disable, DEFENSE, Heal, BUFF, Suicide, or Death. ATTACK applies the power to the targeted area, Disable applies a debuff, DEFENSE restricts incoming damage, and Heal restores health. BUFF enhances the caster’s stats or alters weapon firing modes. Suicide causes the caster to die upon casting, while Death triggers the power when the caster dies.

The Icon column displays what appears in the power wheel when the power is available, with Throw’s icon being 25 (Figure 7). ImpactText displays text over the target’s health bar when hit, typically left blank but can be used to add words like “In Stasis” or “Hacked” for specific powers. The RequiredWeapon column ensures the icon appears only if the corresponding weapon is equipped, for instance, cycling between Marksman, Overkill, Carnage, and Assassination based on the weapon.

The Anim column specifies an instance path to an animation object which is triggered whenever the power is activated. The optional CoverAnim column plays a different animation when the player is in cover, useful for powers cast without breaking cover. It should be noted that animations can be freely ported between games. The animation’s rate is directly influenced by the CastingTime parameter seen in Figure 6b. Longer casting times result in the animation playing out more slowly than faster casting times.

The VFXAppearance column determines the visual effect applied when the power is cast. NoiseRelease and NoiseImpact indicate whether a sound effect plays when the power is cast or impacts the target (it is unclear whether this value actually affects anything in-game. Typically sounds are controlled directly by VFX). ImpactShape is primarily used for melee powers, defining the effect area in front of the caster. The next four columns are vestigial and unused in the vanilla game.

CasterCanMove indicates whether the character’s movement is disabled during casting. AimType can be either CameraTrace or SelectedTarget, determining whether the power hits the selected target or where the camera is pointing. A CameraTrace power can be fired over the shoulder of a SelectedTarget without hitting them. RequiredResource specifies if a resource is depleted upon using the power, such as medi-gel or omni-gel for First Aid or Mako repairs, respectively. HUDType determines whether the player or squadmate can see and cast the power from the power wheel.

The script column refers to the specific Unrealscript class controlling the power’s action upon casting. Lastly, the parameters columns match various listed parameters in the GameProperty_GameEffects 2DA and can be directly accessed from an Unrealscript function.

Talent_BonusTalents

Figure 8

Figure 8

Talent_BonusTalents is utilized when a bonus talent is selected at the character creation screen and serves as the counterpart to the Classes_ClassSpecialization 2DA. The replacement passive talent is routed through this 2DA using one of the first 11 rows to substitute the original passive class talent (Figure 8).

The rest of the rows correspond to a specific bonus talent. Each bonus talent is associated with two rows. The first row is a talent that provides the PowerGive effect, enabling the player to use the power. The second row represents the talent itself to be added to the list. The VisualOrder is set to 85, indicating that the talent should be inserted into the class talent list between talents with VisualOrder values of 80 and 90.

Talent_TalentGUI

Figure 9

Figure 9

The Talent_TalentGUI 2DA contains the interface for the Squad screen, with each row corresponding to a specific talent listed by row numbers. The DisplayName labels each talent row on the Squad screen, while the DisplayDescription appears in the box below when the player hovers over the talent name. The UnlockName and UnlockBlurb columns provide notifications such as Unlock X and X unlocked when a talent with a prerequisite is unlocked. Icons follow the same rules as those in the Powers_Powers 2DA. The NameX and UnlockBlurbX columns are used for the pop-up window which appears when a power is unlocked. The DescriptionX boxes contain stringrefs that display the text in the box below when hovering over that talent rank.

Template_Template

Figure 10

The last 2DA is Template_Template located in the package export BIOG_2DA_LevelUp_X (Figure 10). This governs which auto level up tables are assigned to which class. Examining Figure 10, the BaseClassID is once again present; however, the most important element of this 2DA is the row label. Which row each class is located within governs which Template is used when calculating talent ranks during auto level up. An excerpt of the Soldier Auto Level Up table, Template_10, is displayed in Figure 11.

Figure 11

This may be the least intuitive 2DA of the system. There are only two columns present, TalentID and Rank. There are 60 levels possible in the game, but only 48 rows in the table. Additionally, each row contains only one talent, and choosing auto level up on the Squad screen assigns talent ranks to multiple talents.

The logic that governs assignment of talent ranks per level is native but can be gleaned by comparing each table to what occurs whenever an auto level up is performed in-game.

For Classic Levels 1-5, each level unlocks three talent points to be assigned to ranks. Soldiers begin with a rank assigned in Assault Rifles (7) and Combat Armor (30). Pressing the auto level up button assigns one rank to Assault Rifles (7), one to Combat Armor (30), and one to Assault Training (35). This repeats for levels 2 and 3. At level 4, suddenly, one rank is assigned to Assault Rifles (7), and two ranks are assigned to the passive Soldier Talent. This reveals the logic undergirding the table.

When auto level up is pressed, the game locates the first row of the 2DA and ascertains if the number of ranks listed in the Rank column have been assigned to the talent listed in the TalentID column. If the number is less than what is listed in that cell, it assigns one rank to the talent if one or more ranks have previously been taken in the talent, and two ranks if no ranks have yet been assigned to the talent. It then moves on to the next row and repeats this process.

Using this knowledge, an auto level up table for a custom class can be created. The easiest method is to mirror an extant table with the list of updated talents for the custom class.

Unrealscript Components

Figure 12a
Figure 12b

Figure 12

With the network of 2DAs mapped, it is now possible to move forward into the Unrealscript components that govern power function. Reference materials are available for the construction and syntax of UnrealScript classes, functions, and variables, as well as for understanding how LEX interprets the bytecode it generates. Although there are minor differences between LEXscript and Unrealscript, the term “Unrealscript” will be used going forward to represent LEXscript. Further details on the differences of the two languages are available in the Legendary Explorer documentation on GitHub.

To begin, the BioPower and BioPowerScript classes will need to be explored. It should be noted that within the BioPower class, all functions except one are marked as “native” and lack an executable script body. This indicates that these functions are implemented in C++ and compiled directly into the executable file. Reverse-engineering these functions is a complex process, involving assembly code that would need to be deobfuscated to approximate the original source code. With an understanding of that code, modification is possible with ASI mods; however, this requires both a well-rounded knowledge of C++ and of the offsets of the native functions within the executable. Both are likely beyond the knowledge of the average modder, so these will have to remain an obstacle to work around.

Figure 12 is an image of the instance variables where information from the 2DA tables is stored within the BioPower class. There is one non-native function (not shown) that allows access to the parameters listed within the Powers_Powers 2DA. For modification or creation of powers, no adjustments to this class should be necessary. In fact, any modification to a native class is strongly discouraged. Any disruption of the visible compilation chain will create a disconnect with the compiled C++ code that backs the class, which could result in instability and game crashes.

Figure 13

Figure 13

The ubiquity of the BioComplexFloatStructAttribute enum is immediately noticeable. The enum's structure is shown in Figure 13.

Although the enum may appear complex, only two variables are significant: m_Current and m_Base. m_Base represents the value specified by the rows in the Talent_TalentEffectLevels 2DA for the talent. m_Current denotes the value after modifiers have been applied and is the value used in-game.

Figure 14

Figure 14

In the BioPowerScript class, three functions are labeled as “native”. The first two are primarily handled by the game, while the third is a utility function that returns a ground location when provided with a vector position in the level (Figure 14). The AdjustCooldown function appears to be vestigial and does not return any useful information. The remaining functions describe the power's action.

OnImpact is the parent function executed whenever a power affects a target. The game provides parameters to this function, including oCaster (the pawn that initiated the power), fCasterStability (the source of this value is unclear, and it is typically 1.0 and unused), oImpacted (the pawn hit by the ability), and nPreviouslyImpacted (which increments the achievement for power usage).

StartPhase and EndPhase are executed during casting, with StartPhase running before casting begins and EndPhase running when it concludes. These functions include a variable for an enum detailing the exact phase of casting (e.g., BIO_POWER_NOT_STARTED, BIO_POWER_CASTING, BIO_POWER_RELEASE, BIO_POWER_USING, and BIO_POWER_FINISHED), along with oCaster and fDuration (the intended duration of the power, as specified in the previously detailed 2DAs).

The CanStartPower function can interrupt casting if a certain parameter is not met; however, for most powers, this function is inactive since powers can usually be cast if they are not on cooldown.

InitializePowerScript is the very first function executed for every power, detailing anything required before casting begins. By default, this includes assigning the name of the power being used to the instance variable m_nmPower.

Figure 15

Figure 15

The BioPowerScript class extends into the BioPowerScriptDesign class within the SFXGameContent_Powers file, where all functions of this class can be observed (Figure 15). These functions serve as utilities managing the specifics of power effects. This class acts as the parent to all scripts executing various powers.

Figure 16

Figure 16

The class responsible for running the Throw power, BioThrowScript, is identified in the Power_Powers 2DA. Examining the StartPhase of that power reveals how all components integrate (Figure 16). The parameters listed in Power_Powers are visible here once again, with values that can be traced back through all relevant 2DAs. The m_ThrowForce parameter is present in both Power_Powers and GameProperties_GameEffects. Locating m_ThrowForce as a parameter in GameProperties_GameEffects identifies the game effect referenced in Talent_TalentEffectsLevels, with the rank of the power in Talent_TalentEffectsLevels determining the value of that parameter.

Figure 17

Figure 17

When examining the OnImpact function of the same power, the sequence of events occurring when the power hits an enemy is revealed (Figure 17). The initial part of this function verifies the target being hit. The function is designed to exit if the target is either dead or allied with the caster, preventing unintended interactions with allies.

Subsequently, the function determines the type of damage inflicted, which is set by default properties associated with the class and modified by elements from the talent 2DAs. The function compares the “physics level” of the impacted target with that of the power. This attribute, assigned to various actors, approximates their weight. For instance, casting Singularity with a single rank on a krogan results in no effect but upgrading Singularity to level 12 causes the krogan to be lifted due to the physics level of Singularity surpassing that of the krogan. In Throw, the physics level determines whether the target is affected by m_bIgnorePhysicsThreshold, which dictates whether the target is bodily thrown or simply withstands the attack's force.

The subsequent part of the function, present in every vanilla power available to the player, increments the achievement counter for power usage. The function then performs calculations to determine the trajectory and damage to the target. It calculates the difference between the caster and the target’s location, normalizing this into a vector that is a mathematical representation of a directional arrow. Modifications to the damage are computed based on the target's biotic resistance and momentum from the force multiplied by the direction. Finally, a function is called to apply these calculated effects.

Figure 18

Figure 18

The EffectTakeDamage function from the list of effects in BioPowerScriptDesign provides insight into the mechanics when an actor takes damage (Figure 18). Initially, the AI Controller of the caster is retrieved, to be fed into a function later. Damage is then calculated based on the proximity of the impacted actor to the point of impact, with actors further away receiving less damage than those hit directly. After calculating this distance, the game creates a game property to apply to the impacted actor, incorporating all gathered information.

Figure 19

Figure 19

However, the function that applies this property is native (Figure 19). Thus, a mod developer has to rely on the game to perform as expected; a bit of a troublesome prospect. This illustrates a recurring issue in LE1, where important processes are inaccessible to the average modder behind native barriers. It is advised to verify if vanilla objects use native functions as anticipated, as unexpected behavior may lead to game instability and difficulties in tracing crashes.

One example of this unexpected behavior is the native function SetBaseFloatValue in the BioAttributes class which deallocates memory dedicated to an array prior to garbage collection, which runs counterintuitive to its name. When garbage collection is run, the game attempts to deallocate memory that has already been cleared. This results in a heap corruption and a game crash. However, since this behavior is native, no debugging information can easily be obtained without some knowledge of native function offsets and the ability to apply this knowledge to a minidump file.

Figure 20

Figure 20

The fundamental architecture of LE1’s internal power functions has been established, but clearly, it is possible to port powers across games, so how was this accomplished? To begin, LE2’s SFXGame should be explored, starting with its version of BioPowerScript (Figure 20).

Familiar functions are clearly present, but now they’re buried under an avalanche of functions that LE1 does not have. Notably, greater complexity in power functionality is allowed via the presence of a Tick function. In Unreal Engine 3, the Tick function is typically a function reserved for Actors, enabling real-time control by executing a function every frame (for instance, 60 times per second at 60 FPS). BioWare extended this functionality to powers in LE2, allowing for complex interactions such as power projectiles and dynamic responses to in-game events, like Tactical Cloak disengaging when a weapon is fired. In contrast, LE1 powers are relatively simple, either executing an immediate action or adhering to a fixed time limit, unaffected by player or enemy actions. The absence of a Tick function in LE1 imposes limitations on the capabilities of its powers, making porting of more intricate powers from later games appear impossible. However, this was achieved through a clever workaround.

Non-actor objects lack Tick functions, but a simulation of one can be created by leveraging an Actor's Tick via Timers. Timers are used to execute a function after a specified duration, measured in seconds, and can be associated with an Actor. By configuring the timer to trigger frequently (essentially every tick) and attaching it to the caster of the power, the power script can simulate a ticking function. This allows for real-time control over the power, effectively mimicking the Tick function within LE1.

Through this method, the essential components are in place: stat storage (2DAs), scripts (extending from BioPowerScriptDesign), and precise control mechanisms (Timers). These elements enable the creation of more complex powers within LE1. However, power creation also involves animations, sounds, and visual effects, which will be addressed next.

Visual Effects and Particle Systems

Figure 21

Figure 21

In the Powers_Powers 2DA, the column labeled VFXAppearance contains an instanced path to an object (Figure 9). For vanilla powers, these objects are located in the file BIOC_Materials.pcc. Upon opening this file and locating the specified object, seven basic components of a power’s VFX appearance are identified, with six visible in Figure 21. The components are as follows:

  1. PlayerCrust: The visual effect applied to the caster upon power activation, such as the biotic aura seen during the casting of Throw.

  2. FramebufferEffect: The effect applied to the screen when the power is cast, such as the slight screen warp that occurs when biotic powers are activated.

  3. TargetCrust: The visual effect applied to the target when struck by the power.

  4. WorldImpactVisualEffect: The visual effect that occurs when the power hits a solid object.

  5. nmProjectileAttachPoint: The specific point on the caster’s body from which the power originates (e.g., the right hand, indicated by RightWrist).

  6. vfxReleaseEffect: The effect that occurs when the associated BioPowerScript reaches the EndPhase. In Figure 9, this is another type of framebuffer.

  7. vfxProjectileEffect: This component of VFXAppearance can be found within the Throw_Proj_VFX_Appearance export in the same package folder as the export in Figure 21 (Figure 22). This component represents the appearance of the projectile. While vanilla player powers do not include projectiles, biotic enemies utilize them, allowing the player the potential to dodge out of the way of the incoming attack.

Figure 22

Figure 22

It is important to note that as this exploration of VFX continues, the effects in-game will become less straightforward. Working with VFX often involves significant trial and error due to interactions with the game’s native code and the power’s stats. Consequently, not all elements listed in LEX may correspond directly to in-game effects in an intuitive manner. Therefore, VFX editing typically requires frequent backups, along with a methodical approach of hypothesis testing and verification.

The elements within the VFX template will be systematically analyzed, starting with BioCurveDrivenParameters. These parameters serve as modifiers that can be applied to the Effects Material. Two variables are typically associated with this information: the enum and the name variable. The enum functions as a label, while the name variable directly influences the in-game visuals.

Figure 23

Figure 23

Each BioCurveDrivenParameter includes specific elements. The parameter name determines which aspect of the effects material is affected, varying depending on the material. Additionally, a lookup table (LUT) of values is present. This LUT represents a baked curve of floats, each float adjusts intensity of the specific effect of the material defined via the sParameterName. It should be noted that adjusting these LUTs and time scales may not be intuitive to effects seen in-game. It is recommended to set high values and then observe the effects to determine how to best adjust each parameter (Figure 23).

The Lifetime of the effects material within the VFX template also interacts with these parameters. The combination of these elements determines the duration for which the effect remains visible in-game. This duration is further influenced by the stats of the power associated with the VFX template. The precise interaction of these elements is complex and could require a substantial amount of trial and error.

Regarding the SoundCue element, existing tutorials on importing sound into the game can be used to link aa sound directly to the specific visual effect.

Figure 24

Figure 24

Figure 25

Figure 25

Within a Prefab, a relatively simple object is observed, containing an archetype array (Figure 24). Delving further into the archetype reveals several key elements (Figure 25). These include a vector and rotator that determine the location of the archetype relative to the pawn it is attached to, a tag identifying it as an emitter, a Name corresponding to the bone on the pawn to which the archetype is attached, and a float labeled CreationTime.

Modifications to the relative location settings are generally unnecessary unless precise adjustments are required, and CreationTime can also be disregarded. The Tag, which is consistent across all archetypes, is not particularly useful for developers and is therefore best left unchanged. The primary focus should be on the bone attachment, which may be modified if necessary, and the ParticleSystemComponent.

Figure 26

Figure 26

Within the ParticleSystemComponent, various properties are present, though not all require attention (Figure 26). Properties such as OldPosition, ReplacementPrimitive, LightingChannels, and TickGroup can be safely ignored. InstanceParameters typically do not need modification but can be adjusted to set attributes like the color of the effect. However, not all changes may produce noticeable effects in-game. The focus should be on the template, where the actual ParticleSystem is stored.

Figure 27

Figure 27

In the template, several properties may be present, but the emitters list is the primary concern, as it comprises the system (Figure 27). Other properties can generally be left untouched, as they remain consistent across most systems.

When examining an emitter, a list of LODLevels and an EmitterName are observed (Figure 28). Both parameters are important: EmitterName assists in identifying an emitter for developers, while LODLevels control the detail displayed at various distances on screen. To ensure modifications are effective, all LODLevels should be modified in a block when any changes are made. Further analysis of the LODLevel reveals a complex structure, which will be explored in detail (Figure 29).

Figure 28

Figure 28

Figure 29

Figure 29

The RequiredModule is the most critical module within the list, as it contains the material used by the emitter (Figure 30). When porting particle systems between games, it is essential to address every RequiredModule because materials cloned from another game will not function correctly by default and will need to be replaced with a suitable donor material.

The primary focus should be on the material itself, which must be replaced when integrating new systems. The original material’s specific properties should be examined, particularly the boolean properties (Figure 31). Properties such as bUsedWithParticleSprites, bUsedWithBeamTrails, and bUsedWithParticleSubUV are crucial. To find a compatible material in-game, the Asset Database can be used to filter available materials based on these required Boolean properties. Once a suitable donor material is identified, the material in the RequiredModule can be replaced, followed by verification of the associated textures listed in the binary interpreter tab, which may need to be adjusted (Figure 32).

Figure 30

Figure 30

Figure 31

Figure 31

Figure 32

Figure 32

It is recommended to locate a material that matches both the boolean properties and the number of materials in the binary as closely as possible to the original material. Although an exact match may not always be feasible, the goal is to find a material that closely resembles the original. Achieving this requires a process of trial and error. The material's behavior within the particle system should be tested by repeatedly booting the game and triggering the particle system. Observing these behaviors will inform whether the selected material is suitable. Once this process is complete, attention can be redirected to the other modules.

Figure 33

Figure 33

As an example, ParticleModuleSize is displayed in Figure 33. Similar to other modules, it contains a LookupTable (LUT) with minimum and maximum values. This LUT is responsible for setting the size of the particles and adjusting them throughout the lifetime of the particle system. LEX also includes a Particle Module tab (Figure 34), this can be helpful when editing Color and ColorOverLife modules as it provides a visual into how vector values translate into an RGB color. When modifying these parameters, it is essential to adjust the LUT and the minimum and maximum values simultaneously, as the minimum and maximum settings may alter the final particle size from the intended values. Understanding how these changes will impact the particle system is challenging without observing the effects in-game. Therefore, it is advised to use a process of backing up, making educated guesses, and testing to achieve the desired outcome.

Figure 34

Figure 34

Figure 35

Figure 35

Another crucial module for defining the overall shape of certain effects is the TypeDataModule. This module can be observed in the particle system associated with the Singularity impact effect in vanilla LE1 (Figure 35). Within the TypeDataModule, two key properties are highlighted: the mesh and the bOverrideMaterial boolean (Figure 36).

Figure 36

Figure 36

The mesh is always a StaticMesh, which the game utilizes to shape the particle system’s effect. The bOverrideMaterial property enables toggling between the material specified by the RequiredModule and the material associated with the StaticMesh in the binary tab. It is highly recommended to keep this property enabled to maintain precise control over the material via the RequiredModule. This setting also allows the same mesh to be used across multiple particle systems.

User Interface and Scaleform Elements

The last component involves the integration of new icons into the UI. The basegame of LE1 includes 26 icons, among these are icons for each weapon and power. Examining the Icon column of the Powers_Powers 2DA in Figure 9 and the Icon# columns of the Talent_TalentGUI 2DA in Figure 11 reveals which specific icons are associated with each ability. For instance, icon 25 is associated with the Throw ability.

These integers are associated with frames of a sprite within a scaleform object. Scaleform objects are embedded Shockwave Flash (SWF) files within PC-Console (PCC) package files (scaleform is a middleware that allows an interface between flash objects and game engines). Specifically, the objects most associated with talents and powers are GUI_SF_CharacterRecord.CharacterRecord, GUI_SF_CharacterRecord.PC_CharacterRecord, GUI_SF_HUD.ME_HUD, GUI_SF_HUD.PC_ME_HUD, and gui_sf_utility.WindowPop.

GUI_SF_CharacterRecord.CharacterRecord and GUI_SF_CharacterRecord.PC_CharacterRecord control the squad screen. The ability icons stored in these SWFs are used in the level block UI, popping up over a block whenever a power is about to be unlocked. Which SWF is utilized depends on the type of UI displayed for the player. The former is used when the player is using a controller whereas the latter is used when the player is using a mouse and keyboard.

GUI_SF_HUD.ME_HUD and GUI_SF_HUD.PC_ME_HUD control the power wheel. The ability icons stored in these SWFs are used whenever the power wheel is pulled up on the player’s screen. As with the Character Record SWFs, which object is used is dependent on the input used by the player during gameplay.

Lastly, gui_sf_utility.WindowPop contains the icons used whenever a pop up appears on screen (e.g. when a power is upgraded on the Squad screen, the pop up that appears that provides more information).

Scaleform editing is a rather fiddly type of modding that is prone to unexpected behavior. The oft-repeated mantra of “backup, guess, and check” is even more important for this component, as any change to a scaleform object may unexpectedly break unrelated elements and require restoration to correct.

With this stated, it is advised to not use the basegame SWFs that are located in the Startup files. Modder DropTheSquid’s HUD Enhancements mod both provides a more functionally flexible HUD for end users and does not suffer from the propensity of UI elements to behave in unexpected manners whenever an element is edited (Figure 37).

Figure 37a Figure 37b

Figure 37

As seen in LEX scaleform tutorials, JPEXS, an open-source flash decompiler can be used to open exported SWF files from LEX. Once a SWF is opened, the icon library for each UI can be located. This will take the form of a sprite with frames that cycle through each icon. It should be noted that there may be multiple copies of this sprite. Each of these copies will be used in different elements of the HUD. Thus, any modification to one copy should be duplicated on each copy. For the HUD Enhancements SWF GUI_SF_HUD_Enhanced.ME_HUD, the icon sprites that are used in game are 210, 479, 912, 1220, 1335, and 1557.

Shapes and sprites can be copied from SWFs exported from other Mass Effect games. Alternatively, JPEXS supports the importation of Scalable Vector Graphic (SVG) files.

The frames of each sprite can be pulled either from shapes, which is done for every icon in vanilla LE1, or other sprites, which is done for some icons in later games. Careful importation and adjustments of sprites from later games can be done to expand the list of possible icons in each Icon Sprite. However, given that adjustment of SWFs is a very volatile process, any change to any sprite or shape should be tested in-game to ensure no existing UI elements have been adversely affected in form, function, or both.

Figure 38

Figure 38

There are a few flags which should always be set whenever an icon is added to the frame list in a sprite. placeFlagHasMatrix allows fine control of the exact dimensions and location of the icon within the frame. placeFlagHasCharacter determines the shape or sprite for the icon. placeFlagHasMove overrides the previous icon shape and replaces it with the current icon.

Figure 39

Figure 39

A sprite can contain multiple layers of shapes as seen in Figure 39. Since SVGs are, by nature, very simple objects, layering them within JPEXS allows the creation of more complex icons with fine details. LE2 and LE3 use this method frequently. Thus, the migration of a sprite between SWFs of different games involves the importation of multiple objects. To minimize the impact on the recipient SWF, each shape component of the sprite should be imported one at a time. Then, the sprite itself can be recreated using the parameters seen in the donor SWF.

Occasionally, drastic impact to UI elements is entirely unavoidable, as extending the frames of the icon sprite will shift the file offsets of all subsequent elements, creating a snowball effect which could lead to the side effects seen in Figure 40.

Figure 40

Figure 40

Power and Talent Overhaul solves this problem by keeping the basegame SWF file offsets of GUI_SF_CharacterRecord.PC_CharacterRecord in place and cloning the sprite labeled TalentHilightBlock to the end of the sprite list with the name TalentHilightBlock2. The ActionScript that references this sprite was then rerouted by editing line 2396 of the associated pcode to instead reference TalentHilightBlock2. This succeeded in adding onto the talent icon list while retaining the formatting of all elements in the Squad screen. Counterintuitively, this affects the icon list for both the mouse and keyboard Squad screen and the controller Squad screen, eliminating the need to extend this workaround to GUI_SF_CharacterRecord.CharacterRecord.

The last element, gui_sf_utility.WindowPop is by far the simplest, as the lack of colored elements prevents the possible formatting impact of file offset editing seen in other SWFs. The sprite with the icon list in this is 341.

It is essential to check how the size and position of each icon translates to its appearance in game. Depending on where the sprite is used, the icons could appear larger, smaller, or in a different position. Similar to VFX editing, scaleform modification always requires extensive testing to ensure what is seen in JPEXS translates appropriately to the in-game UI.

Conclusion

LE1’s underlying talent and power architecture may be far more complex than later games in the series; however, the modification of powers and indeed, the importation of abilities from later games is entirely feasible. Functionalities that LE1 obstinately does not have can be emulated through innovative workarounds and careful manipulation of 2DA files. Visual effects can be ported and adapted by modifying VFX templates, particle systems, and materials, requiring extensive trial and error due to the complexity of their interactions with game code. The incorporation of new icons into the user interface can be accomplished by editing scaleform objects, though this process can often lead to unintended side effects that necessitate iterative testing and adjustments. A backup-and-test methodology is essential in maintaining the stability and functionality of the game throughout all parts of the process. Hopefully, the information and strategies outlined provide a comprehensive framework for anyone considering modding powers within the game.

⚠️ **GitHub.com Fallback** ⚠️