- One of three Shrine locations activates each round; the next Shrine is almost always in a different location from the previous one.
- Both teams race to kill 40 Guardians at the active Shrine. First to 40 captures it.
- Capturing a Shrine summons an Arcane, Frozen, or Mortar Punisher that pushes the lane closest to that Shrine.
- The Punisher type changes every round and cannot repeat back-to-back.
Objective Timers
| Event | Duration | Seconds | Source |
|---|---|---|---|
| First Shrine Activation after a 30s warning | 3:00 | 180s | — |
| Shrine Duration | 2:30 | 150s | libMSHE_gv_mMDiabloShrinesShrineTimerDuration_C |
| Shrine Warning | 0:30 | 30s | libMSHE_gv_mMDiabloShrineWarningTimerLong_C |
Units
Punisher
- HP
- 14500
- Speed
- 3.75
Scaling
- Basic damage
- +2/min 1-10, +3/min 11-15, +5/min 16+
- Life
- +1000/min 1-3, +1100/min 4-8, +1300/min 9-13, +1600/min 14-18, +1750/min 19+
Abilities
- QPunish
MercPunisherLanerLeapLeaps to the targeted location, dealing 1 damage and stunning targets for 1 seconds at the impact location.
- WFixate
MercPunisherLanerFixateFixate on the target, becoming enraged and increasing movement speed by 100% and attack speed by 0% for 4 seconds. During this time the Punisher may not receive any orders. Stuns the Punisher for 1 seconds when the effect ends.
- RCancel Symbiote
AbathurSymbioteCancelCancels the Symbiote ability.
Weapons
| ID | Damage | Period | Range |
|---|---|---|---|
MercPunisherLanerWeapon | 160 | 1s | 1.5 |
MercPunisherLanerFixateWeapon | 160 | 0.5s | 2 |
MercPunisherLanerWeaponStructure | 160 | 1s | 1.5 |
Shrine Randomisation
Each round, one of three Shrine locations becomes active. The next Shrine is almost always in a different spot than the previous one, though same-location repeats can happen rarely. The Punisher type (Arcane, Frozen, or Mortar) never repeats back-to-back.
TextExpressionSetToken("Param/Expression/lib_MSHE_D32CCB19", "B", IntToText(libMSHE_gv_mMDiabloShrineCursedEventTotal_C));
TextTagSetText(libMSHE_gv_mMISTextTag[lp_team], TextExpressionAssemble("Param/Expression/lib_MSHE_D32CCB19"));
}
}
}
void libMSHE_gf_MMISRandomizeNextShrineandShowPreview () {
// Automatic Variable Declarations
int autoED96EF13_val;
// Implementation
if ((libCore_gv_dEBUGDebuggingEnabled == true) && (libMSHE_gv_forcedPunisherType != 0)) {
libMSHE_gv_mMISLastShrineCounter += 1;
libMSHE_gv_mMISCurrentShrine = RandomInt(1, libMSHE_gv_mMISNumberofShrinesAllowed);
libMSHE_gv_mMISBuffIndex = libMSHE_gv_forcedPunisherType;
}
else {
if ((libMSHE_gv_mMISNumberOfShrinesEvents == 0)) {
libMSHE_gv_mMISCurrentShrine = RandomInt(1, libMSHE_gv_mMISNumberofShrinesAllowed);
libMSHE_gv_mMISBuffIndex = libMSHE_gf_MMISGetWhichShrine();
}
else {
while (((libMSHE_gv_mMISLastShrineCounter < 15) && ((libMSHE_gv_mMISCurrentShrine == libMSHE_gv_mMISLastShrine) || (libMSHE_gv_mMISBuffIndex == libMSHE_gv_mMISLastBuff)))) {
libMSHE_gv_mMISLastShrineCounter += 1;
libMSHE_gv_mMISCurrentShrine = RandomInt(1, libMSHE_gv_mMISNumberofShrinesAllowed);
libMSHE_gv_mMISBuffIndex = libMSHE_gf_MMISGetWhichShrine();
}
}
}
libNtve_gf_SendActorMessageToUnit(libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine], "SetMinimapVisibility 1");
libNtve_gf_SendActorMessageToUnit(libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine], "SetMinimapVisibilityAlways 1");
autoED96EF13_val = libMSHE_gv_mMISBuffIndex;
if (autoED96EF13_val == 1) {
libNtve_gf_SendActorMessageToUnit(libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine], "MinimapTooltip DOCSTR_InactiveFrozenShrine");
}
else if (autoED96EF13_val == 2) {
// ...
int auto6F20D935_u;
unit auto6F20D935_var;
unitgroup autoE5CA2A3B_g;
int autoE5CA2A3B_u;
unit autoE5CA2A3B_var;
string auto318ED688_val;
// Variable Initialization
lv_shrine = UnitGroupEmpty();
lv_shrineType = libMSHE_ge_MMISShrineTypes_None;
// Implementation
libMSHE_gv_mMISLastShrine = lp_shrine;
libMSHE_gv_mMISLastBuff = libMSHE_gv_mMISBuffIndex;
UnitRemove(libMSHE_gv_mMISWarningUnit);
auto6F20D935_g = UnitGroup("DiabloShrine", c_playerAny, RegionEntireMap(), UnitFilter(0, 0, (1 << c_targetFilterMissile), (1 << (c_targetFilterDead - 32)) | (1 << (c_targetFilterHidden - 32))), 0);
auto6F20D935_u = UnitGroupCount(auto6F20D935_g, c_unitCountAll);
for (;; auto6F20D935_u -= 1) {
auto6F20D935_var = UnitGroupUnitFromEnd(auto6F20D935_g, auto6F20D935_u);
if (auto6F20D935_var == null) { break; }
UnitAbilityEnable(auto6F20D935_var, "CursedShrineSpawn", true);
}
autoE5CA2A3B_g = libMSHE_gv_mMISShrineGroups[lp_shrine];
autoE5CA2A3B_u = UnitGroupCount(autoE5CA2A3B_g, c_unitCountAll);
for (;; autoE5CA2A3B_u -= 1) {
// ...
}
}
// Actions
if (!runActions) {
return true;
}
UnitBehaviorRemove(libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine], "DiabloShrineWarningFX", 1);
libMSHE_gf_MMISTransmissionShrineActivating();
libMSHE_gf_MMISActivateShrine(libMSHE_gv_mMISCurrentShrine);
libMSHE_gv_mMISNumberOfShrinesEvents += 1;
libMSHE_gv_mMISLastShrineCounter = 0;
return true;
}
//--------------------------------------------------------------------------------------------------
void libMSHE_gt_MMISWarningTimerExpired_Init () {
libMSHE_gt_MMISWarningTimerExpired = TriggerCreate("libMSHE_gt_MMISWarningTimerExpired_Func");
TriggerAddEventTimer(libMSHE_gt_MMISWarningTimerExpired, libMSHE_gv_mMISShrineWarningTimer);
}
//--------------------------------------------------------------------------------------------------
// Trigger: MM IS Start Shrine Timer
//--------------------------------------------------------------------------------------------------
// ...
// Actions
if (!runActions) {
return true;
}
TimerPause(libMSHE_gv_mMISShrineTimer, true);
TimerStart(libMSHE_gv_mMISShrineWarningTimer, 1.0, false, c_timeGame);
if ((libMSHE_gv_mMISNumberOfShrinesEvents == 0)) {
libMSHE_gv_mMISCurrentShrine = RandomInt(1, libMSHE_gv_mMISNumberofShrinesAllowed);
libMSHE_gv_mMISBuffIndex = libMSHE_gf_MMISGetWhichShrine();
}
else {
while (((libMSHE_gv_mMISLastShrineCounter < 15) && ((libMSHE_gv_mMISCurrentShrine == libMSHE_gv_mMISLastShrine) || (libMSHE_gv_mMISBuffIndex == libMSHE_gv_mMISLastBuff)))) {
libMSHE_gv_mMISLastShrineCounter += 1;
libMSHE_gv_mMISCurrentShrine = RandomInt(1, libMSHE_gv_mMISNumberofShrinesAllowed);
libMSHE_gv_mMISBuffIndex = libMSHE_gf_MMISGetWhichShrine();
}
}
libMSHE_gf_MMISWarningAnnouncementShrine(libMSHE_gv_mMISCurrentShrine);
PlayerGroupRemove(libMSHE_gv_mMISShrineGroup, libMSHE_gv_mMISBuffIndex);
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_crystalFrozenIcon, PlayerGroupAll(), false);
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_crystalMortarIcon, PlayerGroupAll(), false);
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_crystalArcaneIcon, PlayerGroupAll(), false);
autoD3439B71_val = libMSHE_gv_mMISBuffIndex;
if (autoD3439B71_val == 1) {
libMSHE_gv_mMISBuff = "FrozenShrine";
40 Guardians → Punisher
Both teams race to kill 40 Guardians, also described as Skeletal Defenders, spawned by the active Shrine. The first team to 40 kills captures it and summons a Punisher.
playergroup auto1A5B44D4_g;
// Variable Initialization
// Implementation
if ((libMSHE_gv_mMISTextTag[lp_team] == c_textTagNone)) {
TextTagCreate(StringExternal("Param/Value/lib_MSHE_8C8796B4"), 24, UnitGetPosition(lp_currentShrine), 1.0, true, false, libGame_gf_PlayersOnTeamHeroes(lp_team, false));
libMSHE_gv_mMISTextTag[lp_team] = TextTagLastCreated();
}
if ((libMSHE_gv_mMISKillCount[lp_team] >= libMSHE_gv_mMDiabloShrineCursedEventTotal_C)) {
TextTagDestroy(libMSHE_gv_mMISTextTag[lp_team]);
if ((lp_enemyTeam <= libCore_gv_bALMaxTeams)) {
TextTagDestroy(libMSHE_gv_mMISTextTag[lp_enemyTeam]);
}
libMSHE_gv_mMISTextTag[lp_team] = c_textTagNone;
libMSHE_gv_mMISTextTag[lp_enemyTeam] = c_textTagNone;
}
else {
auto1A5B44D4_g = libGame_gf_PlayersOnTeamHeroes(lp_team, false);
lv_itPlayer = -1;
while (true) {
lv_itPlayer = PlayerGroupNextPlayer(auto1A5B44D4_g, lv_itPlayer);
if (lv_itPlayer<0) { break; }
TextExpressionSetToken("Param/Expression/lib_MSHE_D32CCB19", "A", IntToText(libMSHE_gv_mMISKillCount[lp_team]));
TextExpressionSetToken("Param/Expression/lib_MSHE_D32CCB19", "B", IntToText(libMSHE_gv_mMDiabloShrineCursedEventTotal_C));
TextTagSetText(libMSHE_gv_mMISTextTag[lp_team], TextExpressionAssemble("Param/Expression/lib_MSHE_D32CCB19"));
}
}
}
void libMSHE_gf_MMISRandomizeNextShrineandShowPreview () {
// Automatic Variable Declarations
int autoED96EF13_val;
// Implementation
// ...
// Implementation
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_mainPanel, libGame_gv_teams[libGame_gv_teamOrderIndex_C].lv_teamPlayerGroupNoNPCPlayers, lp_showHide);
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_mainPanel, libGame_gv_teams[libGame_gv_teamChaosIndex_C].lv_teamPlayerGroupNoNPCPlayers, lp_showHide);
DialogControlSetVisible(libMSHE_gv_mMTowerPushPanel.lv_mainPanel, libCore_gv_playerGroupFromPlayer[libCore_gv_oBSERVER_ObserverUIPlayer], lp_showHide);
}
void libMSHE_gf_MMTowerPushUISetGoblinCount (int lp_count, int lp_count2) {
// Automatic Variable Declarations
// Implementation
TextExpressionSetToken("Param/Expression/lib_MSHE_7B0D17EF", "A", IntToText(lp_count));
TextExpressionSetToken("Param/Expression/lib_MSHE_7B0D17EF", "B", IntToText(libMSHE_gv_mMDiabloShrineCursedEventTotal_C));
libNtve_gf_SetDialogItemText(libMSHE_gv_mMTowerPushPanel.lv_hauntedMinesSkullModeSkullCounterLabel[1], TextExpressionAssemble("Param/Expression/lib_MSHE_7B0D17EF"), PlayerGroupAll());
TextExpressionSetToken("Param/Expression/lib_MSHE_E44F01EB", "A", IntToText(lp_count2));
TextExpressionSetToken("Param/Expression/lib_MSHE_E44F01EB", "B", IntToText(libMSHE_gv_mMDiabloShrineCursedEventTotal_C));
libNtve_gf_SetDialogItemText(libMSHE_gv_mMTowerPushPanel.lv_hauntedMinesSkullModeSkullCounterLabel[2], TextExpressionAssemble("Param/Expression/lib_MSHE_E44F01EB"), PlayerGroupAll());
}
void libMSHE_gf_MMTowerPushUISetMode (int lp_mode) {
// Automatic Variable Declarations
int autoB3683D6E_val;
// Implementation
autoB3683D6E_val = lp_mode;
if (autoB3683D6E_val == libMSHE_ge_MMISUIModes_Idle) {
// ...
TextTagCreate(TextExpressionAssemble("Param/Expression/lib_MSHE_034712A9"), 24, UnitGetPosition(EventUnit()), 1.0, true, true, lv_textTagPlayers);
TextTagSetVelocity(TextTagLastCreated(), 1.5, 90.0);
TextTagSetTime(TextTagLastCreated(), c_textTagTimeDuration, 3.0);
TextTagSetFogVisibility(TextTagLastCreated(), c_visTypeFog);
TextExpressionSetToken("Param/Expression/lib_MSHE_A99E643F", "A", IntToText(1));
TextTagCreate(TextExpressionAssemble("Param/Expression/lib_MSHE_A99E643F"), 24, UnitGetPosition(EventUnit()), 1.0, true, true, lv_enemyTextTagPlayers);
TextTagSetVelocity(TextTagLastCreated(), 1.5, 90.0);
TextTagSetTime(TextTagLastCreated(), c_textTagTimeDuration, 3.0);
TextTagSetFogVisibility(TextTagLastCreated(), c_visTypeFog);
libMSHE_gf_MMISTextTagScoreHandler((3 - libGame_gf_TeamNumberOfPlayer(libNtve_gf_KillingPlayer())), libGame_gf_TeamNumberOfPlayer(libNtve_gf_KillingPlayer()), libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine]);
if ((libMSHE_gv_mMISKillCount[libGame_gf_TeamNumberOfPlayer(libNtve_gf_KillingPlayer())] == libMSHE_gv_mMDiabloShrineCursedEventTotal_C)) {
lv_shrine = libMSHE_gv_mMISShrines[libMSHE_gv_mMISCurrentShrine];
lv_winningTeam = libGame_gf_TeamNumberOfPlayer(libNtve_gf_KillingPlayer());
if ((UnitHasBehavior2(lv_shrine, "CombatShrine") == true)) {
lv_shrineBuff = "CombatShrineBuff";
libMSHE_gv_mMISShrineBuffText = StringExternal("Param/Value/lib_MSHE_6F731C56");
libMSHE_gf_MMISAnnouncementShrineCaptured(lv_shrine, libMSHE_ge_MMISShrineTypes_CombatShrine, lv_winningTeam);
}
if ((UnitHasBehavior2(lv_shrine, "SiegeShrine") == true)) {
lv_shrineBuff = "SiegeShrineBuff";
Punisher Lane Selection
Capturing a Shrine summons a Punisher that pushes the lane closest to that Shrine. Punishers focus on attacking enemy Heroes, but their leap can target enemy Gates as well as Heroes.
}
else {
}
lv_monster = UnitLastCreated();
libMSHE_gv_mMISPunisher = UnitLastCreated();
TriggerEnable(libMSHE_gt_PunisherDurationTracking, true);
libGame_gf_CalculateSpawnedMinionGoalPoints(lp_team, UnitGetPosition(lv_monster));
libAIAI_gf_MinionAIStartMinionAI(-1, lv_monster, libGame_gv_spawnedMinionLane, libGame_gv_spawnedMinionFirstWaypointIndex, false, true, false, lv_enemyTeam, false);
libMSHE_gv_mMDiabloShrinesPunisherPushLane = AILaneWaypointGetClosestLane(UnitGetPosition(lv_monster));
}
void libMSHE_gf_MMBOEUpdateAmbience (int lp_player) {
// Automatic Variable Declarations
// Implementation
if ((PointGetX(CameraGetTarget(lp_player)) > 124.0)) {
libMSHE_gf_MMBOEChangeAmbience("Ambience_BoE_Hell", null, PlayerGroupSingle(lp_player));
}
Timer Source
libmshe_h.galaxy — constants
const fixed libMSHE_gv_mMDiabloShrinesShrineTimerDuration_C = 150.0;
const fixed libMSHE_gv_mMDiabloShrineWarningTimerLong_C = 30.0;
libmshe.galaxy — timer start
// Actions
if (!runActions) {
return true;
}
TriggerExecute(libMSHE_gt_MMISUIHandler, true, false);
TimerStart(libMSHE_gv_mMISShrineTimer, libMSHE_gv_mMDiabloShrinesShrineTimerDuration_C, false, c_timeGame);
TimerStart(libMSHE_gv_mMISExperince, libMSHE_gv_mMDiabloShrinesExperinceTimer_C, false, c_timeGame);
TimerStart(libMSHE_gv_mMISAffixTimer, libMSHE_gv_mMISAffixTimerDuration, false, c_timeGame);
libMSHE_gv_mMDiabloShrinesShrineTimerWarning = libMSHE_gv_mMDiabloShrineWarningTimerLong_C;
CatalogFieldValueSet(c_gameCatalogBehavior, "DiabloShrineWarningFX", "Duration", libCore_gv_cOMPUTER_Neutral, IntToString(FixedToInt(libMSHE_gv_mMDiabloShrineWarningTimerLong_C)));
autoD4036543_ae = libMSHE_gv_mMISNumberofShrinesAllowed;
lv_itShrine = 1;
// ...
// Actions
if (!runActions) {
return true;
}
TimerStart(libMSHE_gv_mMISShrineTimer, libMSHE_gv_mMDiabloShrinesShrineTimerDuration_C, false, c_timeGame);
auto92397144_g = libMSHE_gv_mMISParalyzeStorage;
auto92397144_u = UnitGroupCount(auto92397144_g, c_unitCountAll);
for (;; auto92397144_u -= 1) {
lv_itUnit = UnitGroupUnitFromEnd(auto92397144_g, auto92397144_u);
if (lv_itUnit == null) { break; }
UnitRemove(lv_itUnit);
// ...
// Actions
if (!runActions) {
return true;
}
TriggerEnable(libMSHE_gt_PunisherDurationTracking, false);
TimerStart(libMSHE_gv_mMISShrineTimer, libMSHE_gv_mMDiabloShrinesShrineTimerDuration_C, false, c_timeGame);
TimerStart(libMSHE_gv_mMISObjectivePreviewTimer, libMSHE_gv_mMISObjectivePreviewDelay_C, false, c_timeGame);
if ((UnitGetOwner(EventUnit()) == libCore_gv_cOMPUTER_TeamOrder)) {
SoundtrackPlay(PlayerGroupAll(), c_soundtrackCategoryMusic, "MX_Event_Shrines_Punisher_Heaven_End", c_soundtrackCueAny, c_soundtrackIndexAny, false);
}
else {
SoundtrackPlay(PlayerGroupAll(), c_soundtrackCategoryMusic, "MX_Event_Shrines_Punisher_Hell_End", c_soundtrackCueAny, c_soundtrackIndexAny, false);
Source Files
- libmshe.galaxy
- libmshe_h.galaxy
- infernalshrines.stormmod/goaldata.xml
- infernalshrines.stormmod/unitdata.xml
- infernalshrines.stormmod/userdata.xml
- libmshd.galaxy
- libmshd_h.galaxy
- infernalshrinesdata.stormmod/abildata.xml
- infernalshrinesdata.stormmod/attachmethoddata.xml
- infernalshrinesdata.stormmod/behaviordata.xml
- infernalshrinesdata.stormmod/buttondata.xml
- infernalshrinesdata.stormmod/effectdata.xml
- infernalshrinesdata.stormmod/mapdata.xml
- infernalshrinesdata.stormmod/moverdata.xml
- infernalshrinesdata.stormmod/unitdata.xml
- infernalshrinesdata.stormmod/validatordata.xml
- infernalshrinesdata.stormmod/weapondata.xml