MHServerEmu Progress Report: January 2025
The first month of 2025 is almost over, and we have some MHServerEmu progress to share.
Conditions
By the end of last month we had the skeleton for the condition system working, and it was time to build upon it. Two major things that were still missing were pausing and disabling.
Pausing makes it possible for a condition with finite duration to function as an infinite condition for as long as the pause is in effect. Pausing is allowed only for conditions flagged as IsBoost
if one of the following criteria is met:
-
The owner of the condition is currently in a region flagged as
PausesBoostConditions
(e.g. hub regions). -
The owner of the condition exits the world, such as during a loading screen.
-
The global live tuning variable
eGTV_BoostTimersRunning
is set to false.
One important thing to note is that pausing a condition affects only its duration. Its effect remains applied even if it is “paused”.
Separate from pausing is disabling a condition. When a condition is disabled, it remains attached to its owner, but its effects are removed until it is enabled again. Conditions are disabled by setting the DisablePowerEffects
property on the owner, which can be done by applying another condition. This system, combined with power activations triggered by power events and procs, functions essentially as a scripting language that can affect the state of the owner in different ways. For example, various special resource behaviors, like Hulk’s Anger or Punisher’s Ammo, use this functionality to enable and disable primary resource regeneration and decay depending on the current combat state.
With these taken care of, the condition system is now in a mostly functional state.
Resources
The original plan was to merge the remaining power subsystems one by one as they were ready. However, while I was working on implementing Spirit, which is the simplest primary resource that many of the heroes use, it became apparent that on its own it will leave many other characters that rely on special behaviors in a semi-broken state. After getting some feedback from the players on our Discord server, I decided to batch these systems into a larger update of the power system, and this is what I have spent the majority of my time working on this month.
Resources in Marvel Heroes come in two flavors: primary and secondary. Each resource has an associated ManaBehaviorPrototype
that defines its behavior, such as whether it starts empty or depletes on death. Avatars are the only entities that use resources; enemies, team-ups, and other AI-controlled characters do not have any.
Primary resources are referred to internally as Endurance
, and all heroes have them. A hero can technically have multiple primary resources which are distinguished by the ManaType
parameter, but in practice the only hero that uses this is Doctor Doom. The default primary resource is Spirit, which is essentially just mana. Special primary resource behaviors are implemented via the scripting-like system of powers and conditions mentioned in the previous section. Primary resources are displayed in the UI as the “globe” on the right side of the action bar:
Secondary resources are optional, and they can represent various things, from combo points to ants, depending on the hero, with no default behavior. They can have pips, which are basically breakpoints for spending them and activating effects. Secondary resources are represented by horizontal bars above the power slots:
Implementing this system on its own was not particularly difficult, and we could have done it a long time ago. The real roadblocker until now has been getting all the other things that interact with it working, and we have made significant progress on that front as well.
Procs
Back in July of last year when we first got the power system working, I talked about how in many cases powers are actually implemented as “combo” chains. Here is a quick recap: when a more complex effect is needed, it is done by activating subsequent powers when various power events happen, such as a power’s animation reaching its contact frame. Procs are essentially an extension of this system, except their only action is activation of a specific proc power (which in turn can do more varied actions), and their triggers are not limited to stages of the power pipeline.
In total there are 74 proc triggers as of version 1.52. Some of them are more generic, like OnPowerHit
and OnGotDamaged
, while others are more specialized, such as OnSecondaryResourcePipGain
, OnSkillshotReflect
, and OnControlledEntityReleased
. Procs are assigned to entities using one of three proc properties: Proc
, ProcKeyword
, and ProcNotKeyword
. In many cases these properties come from conditions applied by passive powers. Proc properties are parameterized by their trigger, a reference to the power they activate, and an optional parameter:
-
For regular procs the parameter is a threshold value (e.g. an
OnEnduranceBelow
proc with a threshold value of50
is going to activate when the owner’s primary resource drops below 50%). -
For
ProcKeyword
andProcNotKeyword
the parameter is a reference to a keyword prototype, with the former requiring something to have the keyword, and the latter being the opposite and requiring something to not have the keyword.
The value of the property represents the trigger chance of the proc. In some cases this chance is multiplied by the OnHitProcChanceMultiplier
value taken from the prototype of the power that triggered the proc. When this multiplier is applied, the ProcChanceMultiplierBehaviorType
that is also defined in the power prototype is taken into account. Possible values include:
-
AllowProcChanceMultiplier
- applies the multiplier normally. -
IgnoreProcChanceMultiplier
- the multiplier is not applied. -
IgnoreProcChanceMultiplierUnlessZero
- the multiplier is used only to disable on hit procs completely.
The purpose of these multipliers is to limit the proc activation rate for fast hitting powers, which would trigger procs too often without them.
Here are some examples of proc properties:
-
ProcProp[OnAnyHit][CosmicItemSkyBeamAoe][0][0] = 0.05f
- This proc is activated by the
OnAnyHit
trigger, and the activation chance is 5%. The power this proc is going to activate isCosmicItemSkyBeamAoe
. This is a proc assigned by one of the cosmic affixes found on gear.
- This proc is activated by the
-
ProcKeywordProp[OnPowerHit][FuryGainProcEffect][Fury][0] = 1.0f
- This proc is activated by the
OnPowerHit
trigger, it requires the triggering power to have theFury
keyword, and the activation chance is 100%. The power this proc is going to activate isFuryGainProcEffect
. This is a proc assigned by one of Wolverine’s passive powers, and it’s his main way of restoring his primary resource.
- This proc is activated by the
The big challenge with implementing procs has been the sheer amount of code needed to handle all the various triggers. The code itself is very boilerplate and involves doing checks and filling out PowerActivationSettings
instances, but the behavior of various triggers is subtly different enough to not be able to just reuse all the code.
After a few weeks of work though it was done, and now we have a fully-featured proc system. There is just one more gear needed to get this whole mechanism moving.
Tickers
Some properties apply effects to the entities they are assigned to periodically. Most of these involve dealing damage over time (DoT), healing over time (HoT), and applying resource changes over time. For this the game uses a class called PropertyTicker
.
A ticker is essentially just a timer that applies properties at certain update intervals. What makes it special is how it interacts with conditions and procs: a proc can activate a power that will enable or disable a condition, which in turn will enable or disable its ticker. This is how resources like Hulk’s Anger works: entering and exiting combat triggers procs that turn Anger regeneration and decay on or off.
At the time of writing this the timer aspect of tickers has been implemented, and what remains is to expand the power results calculation pipeline to work with over time properties. As part of this I will also be doing a pass on how damage is calculated in general so that we can implement various damage mitigation effects. Overall I expect this to take a week or two, and after that this batch of power changes should be ready for testing in nightly builds.
Population Improvements
While I was busy with powers, Alex has also been hard at work improving some systems that needed more attention. Among them is the population system, which still had some marker-related issues that needed to be fixed.
The basic idea behind the population system is that the game has limited resources for spawning, and the PopulationManager
class distributes these resources among various requests that often exceed the available resources. One example of such resource is markers, which are spawn points hand placed by game designers on various cells. There are many examples in the game when there are fewer markers than entities that try to spawn on them, and the population system has to handle this.
One issue we had was how mission requests for markers were handled. We had some instances where markers were partially used by different missions, and as a result none of them had enough enemies to complete them. To deal with this, Alex has implemented an “all or nothing” style distribution system for missions where markers get reserved in chunks, and if there aren’t enough markers, the mission will wait until it is its time to shine. Implementing this allowed us to deal with population issues in regions like Midtown Patrol.
Another problem was related to how spawn requests were prioritized. Some requests are flagged as “critical”, which means they need to spawn before everything else. When this priority is not taken into account, unexpected semi-random behavior can occur, like missions becoming incompletable. Due to a small oversight this priority was not taken into account in some cases, and this has now been fixed.
And here is a fun fact about marker conflicts: we just found a bug related to them that existed in the original game. In the Castle Doom terminal during the fight with Doctor Doom cutscenes are supposed to be playing on phase transitions, just like they do in story mode. However, because the terminal has two conflicting missions (G09DoctorDoomDailyEndgame
and DoomFightDailyKismetControl
), the necessary hotspots that trigger the cutscenes fail to spawn at all! This bug is now fixed by disabling one of the missions via live tuning, but you can still get the authentic buggy experience if you prefer it by turning the tuning off.
Modding
In addition to polishing existing systems, in January Alex has also started some preliminary work on modding tools for modifying the client as a side project. While it is still very early, there have been some successful experiments with simple texture swaps.
The tool he is currently working on is called MH Texture Manager, and it is going to allow potential mod developers to browse and replace various textures. Here is what it looks like right now:
And here is what it is like in-game:
Please keep in mind that this is purely experimental at this stage, and the scope of the MHServerEmu project does not include creating any new custom content. You can follow Alex’s progress with modding tools on GitHub.
Back to the coding mines for us. See you all next time!